* [PATCH 0/3] media: Add MIPI CCI register access helper functions
@ 2023-06-06 16:58 Hans de Goede
2023-06-06 16:58 ` [PATCH 1/3] " Hans de Goede
` (2 more replies)
0 siblings, 3 replies; 38+ messages in thread
From: Hans de Goede @ 2023-06-06 16:58 UTC (permalink / raw)
To: Laurent Pinchart, Sakari Ailus
Cc: Hans de Goede, Mauro Carvalho Chehab, Andy Shevchenko,
linux-media
Hi Laurent, Sakari, et al.,
The CSI2 specification specifies a standard method to access camera sensor
registers called "Camera Control Interface (CCI)".
Currently a lot of Linux camera sensor drivers all have their own custom
helpers for this, often copy and pasted from other drivers.
This adds a set of generic helpers for this so that all sensor drivers can
switch to a single common implementation.
This is based on / the result of our previous discussion on this here:
Link: https://lore.kernel.org/linux-media/59aefa7f-7bf9-6736-6040-39551329cd0a@redhat.com/
Patch 2/3 is an example of how these helpers can be used and 3/3 removes
the now no longer necessary ov_16bit_addr_reg_helpers.h which was
the previous attempt to add common CCI access helpers. As such
patch 2/3 and 3/3 are more of a RFC really. Please focus on reviewing
patch 1/3.
Regards,
Hans
Hans de Goede (3):
media: Add MIPI CCI register access helper functions
media: atomisp: ov2680: Convert to new CCI register access helpers
media: Remove ov_16bit_addr_reg_helpers.h
Documentation/driver-api/media/v4l2-cci.rst | 5 +
Documentation/driver-api/media/v4l2-core.rst | 1 +
drivers/media/v4l2-core/Kconfig | 5 +
drivers/media/v4l2-core/Makefile | 1 +
drivers/media/v4l2-core/v4l2-cci.c | 142 +++++++++++
drivers/staging/media/atomisp/i2c/Kconfig | 1 +
.../media/atomisp/i2c/atomisp-ov2680.c | 233 ++++++++----------
drivers/staging/media/atomisp/i2c/ov2680.h | 73 +-----
include/media/ov_16bit_addr_reg_helpers.h | 92 -------
include/media/v4l2-cci.h | 109 ++++++++
10 files changed, 367 insertions(+), 295 deletions(-)
create mode 100644 Documentation/driver-api/media/v4l2-cci.rst
create mode 100644 drivers/media/v4l2-core/v4l2-cci.c
delete mode 100644 include/media/ov_16bit_addr_reg_helpers.h
create mode 100644 include/media/v4l2-cci.h
--
2.40.1
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-06 16:58 [PATCH 0/3] media: Add MIPI CCI register access helper functions Hans de Goede
@ 2023-06-06 16:58 ` Hans de Goede
2023-06-06 20:43 ` Andy Shevchenko
` (2 more replies)
2023-06-06 16:58 ` [PATCH 2/3] media: atomisp: ov2680: Convert to new CCI register access helpers Hans de Goede
2023-06-06 16:58 ` [PATCH 3/3] media: Remove ov_16bit_addr_reg_helpers.h Hans de Goede
2 siblings, 3 replies; 38+ messages in thread
From: Hans de Goede @ 2023-06-06 16:58 UTC (permalink / raw)
To: Laurent Pinchart, Sakari Ailus
Cc: Hans de Goede, Mauro Carvalho Chehab, Andy Shevchenko,
linux-media
The CSI2 specification specifies a standard method to access camera sensor
registers called "Camera Control Interface (CCI)".
This uses either 8 or 16 bit (big-endian wire order) register addresses
and supports 8, 16, 24 or 32 bit (big-endian wire order) register widths.
Currently a lot of Linux camera sensor drivers all have their own custom
helpers for this, often copy and pasted from other drivers.
Add a set of generic helpers for this so that all sensor drivers can
switch to a single common implementation.
These helpers take an extra optional "int *err" function parameter,
this can be used to chain a bunch of register accesses together with
only a single error check at the end, rather then needing to error
check each individual register access. The first failing call will
set the contents of err to a non 0 value and all other calls will
then become no-ops.
Link: https://lore.kernel.org/linux-media/59aefa7f-7bf9-6736-6040-39551329cd0a@redhat.com/
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
Documentation/driver-api/media/v4l2-cci.rst | 5 +
Documentation/driver-api/media/v4l2-core.rst | 1 +
drivers/media/v4l2-core/Kconfig | 5 +
drivers/media/v4l2-core/Makefile | 1 +
drivers/media/v4l2-core/v4l2-cci.c | 142 +++++++++++++++++++
include/media/v4l2-cci.h | 109 ++++++++++++++
6 files changed, 263 insertions(+)
create mode 100644 Documentation/driver-api/media/v4l2-cci.rst
create mode 100644 drivers/media/v4l2-core/v4l2-cci.c
create mode 100644 include/media/v4l2-cci.h
diff --git a/Documentation/driver-api/media/v4l2-cci.rst b/Documentation/driver-api/media/v4l2-cci.rst
new file mode 100644
index 000000000000..dd297a40ed20
--- /dev/null
+++ b/Documentation/driver-api/media/v4l2-cci.rst
@@ -0,0 +1,5 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+V4L2 CCI kAPI
+^^^^^^^^^^^^^
+.. kernel-doc:: include/media/v4l2-cci.h
diff --git a/Documentation/driver-api/media/v4l2-core.rst b/Documentation/driver-api/media/v4l2-core.rst
index 1a8c4a5f256b..239045ecc8f4 100644
--- a/Documentation/driver-api/media/v4l2-core.rst
+++ b/Documentation/driver-api/media/v4l2-core.rst
@@ -22,6 +22,7 @@ Video4Linux devices
v4l2-mem2mem
v4l2-async
v4l2-fwnode
+ v4l2-cci
v4l2-rect
v4l2-tuner
v4l2-common
diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
index 348559bc2468..523ba243261d 100644
--- a/drivers/media/v4l2-core/Kconfig
+++ b/drivers/media/v4l2-core/Kconfig
@@ -74,6 +74,11 @@ config V4L2_FWNODE
config V4L2_ASYNC
tristate
+config V4L2_CCI
+ tristate
+ depends on I2C
+ select REGMAP_I2C
+
# Used by drivers that need Videobuf modules
config VIDEOBUF_GEN
tristate
diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
index 41d91bd10cf2..be2551705755 100644
--- a/drivers/media/v4l2-core/Makefile
+++ b/drivers/media/v4l2-core/Makefile
@@ -25,6 +25,7 @@ videodev-$(CONFIG_VIDEO_V4L2_I2C) += v4l2-i2c.o
# (e. g. LC_ALL=C sort Makefile)
obj-$(CONFIG_V4L2_ASYNC) += v4l2-async.o
+obj-$(CONFIG_V4L2_CCI) += v4l2-cci.o
obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash-led-class.o
obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o
obj-$(CONFIG_V4L2_H264) += v4l2-h264.o
diff --git a/drivers/media/v4l2-core/v4l2-cci.c b/drivers/media/v4l2-core/v4l2-cci.c
new file mode 100644
index 000000000000..21207d137dbe
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-cci.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MIPI Camera Control Interface (CCI) register access helpers.
+ *
+ * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
+ */
+
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include <media/v4l2-cci.h>
+
+int cci_read(struct regmap *map, u32 reg, u32 *val, int *err)
+{
+ int i, len, ret;
+ u8 buf[4];
+
+ if (err && *err)
+ return *err;
+
+ /* Set len to register width in bytes */
+ len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
+ reg &= CCI_REG_ADDR_MASK;
+
+ ret = regmap_bulk_read(map, reg, buf, len);
+ if (ret) {
+ dev_err(regmap_get_device(map), "Error reading reg 0x%4x: %d\n", reg, ret);
+ if (err)
+ *err = ret;
+
+ return ret;
+ }
+
+ *val = 0;
+ for (i = 0; i < len; i++) {
+ *val <<= 8;
+ *val |= buf[i];
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cci_read);
+
+int cci_write(struct regmap *map, u32 reg, u32 val, int *err)
+{
+ int i, len, ret;
+ u8 buf[4];
+
+ if (err && *err)
+ return *err;
+
+ /* Set len to register width in bytes */
+ len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
+ reg &= CCI_REG_ADDR_MASK;
+
+ for (i = 0; i < len; i++) {
+ buf[len - i - 1] = val & 0xff;
+ val >>= 8;
+ }
+
+ ret = regmap_bulk_write(map, reg, buf, len);
+ if (ret) {
+ dev_err(regmap_get_device(map), "Error writing reg 0x%4x: %d\n", reg, ret);
+ if (err)
+ *err = ret;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cci_write);
+
+int cci_update_bits(struct regmap *map, u32 reg, u32 mask, u32 val, int *err)
+{
+ int width, ret;
+ u32 readval;
+
+ if (err && *err)
+ return *err;
+
+ /*
+ * For single byte updates use regmap_update_bits(), this uses
+ * the regmap-lock to protect against other read-modify-writes racing.
+ */
+ width = (reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT;
+ if (width == cci_reg_8) {
+ reg &= CCI_REG_ADDR_MASK;
+ ret = regmap_update_bits(map, reg, mask, val);
+ if (ret) {
+ dev_err(regmap_get_device(map), "Error updating reg 0x%4x: %d\n", reg, ret);
+ if (err)
+ *err = ret;
+ }
+
+ return ret;
+ }
+
+ ret = cci_read(map, reg, &readval, err);
+ if (ret)
+ return ret;
+
+ val = (readval & ~mask) | (val & mask);
+
+ return cci_write(map, reg, val, err);
+}
+EXPORT_SYMBOL_GPL(cci_update_bits);
+
+int cci_multi_reg_write(struct regmap *map, const struct reg_sequence *regs, int num_regs, int *err)
+{
+ int i, ret;
+
+ if (err && *err)
+ return *err;
+
+ for (i = 0; i < num_regs; i++) {
+ ret = cci_write(map, regs[i].reg, regs[i].def, err);
+ if (ret)
+ return ret;
+
+ if (regs[i].delay_us)
+ fsleep(regs[i].delay_us);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cci_multi_reg_write);
+
+struct regmap *cci_regmap_init_i2c(struct i2c_client *client, int reg_addr_bits)
+{
+ struct regmap_config config = {
+ .reg_bits = reg_addr_bits,
+ .val_bits = 8,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ };
+
+ return devm_regmap_init_i2c(client, &config);
+}
+EXPORT_SYMBOL_GPL(cci_regmap_init_i2c);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
diff --git a/include/media/v4l2-cci.h b/include/media/v4l2-cci.h
new file mode 100644
index 000000000000..69b8a7c4a013
--- /dev/null
+++ b/include/media/v4l2-cci.h
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * MIPI Camera Control Interface (CCI) register access helpers.
+ *
+ * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
+ */
+#ifndef _V4L2_CCI_H
+#define _V4L2_CCI_H
+
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+/*
+ * Note cci_reg_8 deliberately is 0, not 1, so that raw
+ * (not wrapped in a CCI_REG*() macro) register addresses
+ * do 8 bit wide accesses. This allows unchanged use of register
+ * initialization lists of raw address, value pairs which only
+ * do 8 bit width accesses. Which makes porting drivers easier.
+ */
+enum cci_reg_type {
+ cci_reg_8 = 0,
+ cci_reg_16,
+ cci_reg_24,
+ cci_reg_32,
+};
+
+/*
+ * Macros to define register address with the register width encoded
+ * into the higher bits. CCI_REG8() is a no-op so its use is optional.
+ */
+#define CCI_REG_ADDR_MASK GENMASK(15, 0)
+#define CCI_REG_WIDTH_SHIFT 16
+#define CCI_REG_WIDTH_MASK GENMASK(17, 16)
+
+#define CCI_REG8(x) ((cci_reg_8 << CCI_REG_WIDTH_SHIFT) | (x))
+#define CCI_REG16(x) ((cci_reg_16 << CCI_REG_WIDTH_SHIFT) | (x))
+#define CCI_REG24(x) ((cci_reg_24 << CCI_REG_WIDTH_SHIFT) | (x))
+#define CCI_REG32(x) ((cci_reg_32 << CCI_REG_WIDTH_SHIFT) | (x))
+
+/**
+ * cci_read() - Read a value from a single CCI register
+ *
+ * @map: Register map to write to
+ * @reg: Register address to write, use CCI_REG#() macros to encode reg width
+ * @val: Pointer to store read value
+ * @err: optional pointer to store errors, if a previous error is set the write will be skipped
+ *
+ * Return: %0 on success or a negative error code on failure.
+ */
+int cci_read(struct regmap *map, u32 reg, u32 *val, int *err);
+
+/**
+ * cci_write() - Write a value to a single CCI register
+ *
+ * @map: Register map to write to
+ * @reg: Register address to write, use CCI_REG#() macros to encode reg width
+ * @val: Value to be written
+ * @err: optional pointer to store errors, if a previous error is set the write will be skipped
+ *
+ * Return: %0 on success or a negative error code on failure.
+ */
+int cci_write(struct regmap *map, u32 reg, u32 val, int *err);
+
+/**
+ * cci_update_bits() - Perform a read/modify/write cycle on a single CCI register
+ *
+ * @map: Register map to write to
+ * @reg: Register address to write, use CCI_REG#() macros to encode reg width
+ * @mask: Bitmask to change
+ * @val: New value for bitmask
+ * @err: optional pointer to store errors, if a previous error is set the update will be skipped
+ *
+ * For 8 bit width registers this is guaranteed to be atomic wrt other
+ * cci_*() register access functions. For multi-byte width registers
+ * atomicity is NOT guaranteed.
+ *
+ * Return: %0 on success or a negative error code on failure.
+ */
+int cci_update_bits(struct regmap *map, u32 reg, u32 mask, u32 val, int *err);
+
+/**
+ * cci_multi_reg_write() - Write multiple registers to the device
+ *
+ * @map: Register map to write to
+ * @regs: Array of structures containing register-address, value pairs to be written
+ * register-addresses use CCI_REG#() macros to encode reg width
+ * @num_regs: Number of registers to write
+ * @err: optional pointer to store errors, if a previous error is set the update will be skipped
+ *
+ * Write multiple registers to the device where the set of register, value
+ * pairs are supplied in any order, possibly not all in a single range.
+ *
+ * Return: %0 on success or a negative error code on failure.
+ */
+int cci_multi_reg_write(struct regmap *map, const struct reg_sequence *regs, int num_regs, int *err);
+
+/**
+ * cci_regmap_init_i2c() - Create regmap to use with cci_*() register access functions
+ *
+ * @client: i2c_client to create the regmap for
+ * @reg_addr_bits: register address width to use (8 or 16)
+ *
+ * Note the memory for the created regmap is devm() managed, tied to the client.
+ *
+ * Return: %0 on success or a negative error code on failure.
+ */
+struct regmap *cci_regmap_init_i2c(struct i2c_client *client, int reg_addr_bits);
+
+#endif
--
2.40.1
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH 2/3] media: atomisp: ov2680: Convert to new CCI register access helpers
2023-06-06 16:58 [PATCH 0/3] media: Add MIPI CCI register access helper functions Hans de Goede
2023-06-06 16:58 ` [PATCH 1/3] " Hans de Goede
@ 2023-06-06 16:58 ` Hans de Goede
2023-06-06 20:53 ` Andy Shevchenko
2023-06-07 16:05 ` Laurent Pinchart
2023-06-06 16:58 ` [PATCH 3/3] media: Remove ov_16bit_addr_reg_helpers.h Hans de Goede
2 siblings, 2 replies; 38+ messages in thread
From: Hans de Goede @ 2023-06-06 16:58 UTC (permalink / raw)
To: Laurent Pinchart, Sakari Ailus
Cc: Hans de Goede, Mauro Carvalho Chehab, Andy Shevchenko,
linux-media
Use the new comon CCI register access helpers to replace the private
register access helpers in the ov2680 driver.
While at it also switch to using the same register address defines
as the standard drivers/media/i2c/ov2680.c driver to make merging
the 2 drivers simpler.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/staging/media/atomisp/i2c/Kconfig | 1 +
.../media/atomisp/i2c/atomisp-ov2680.c | 233 ++++++++----------
drivers/staging/media/atomisp/i2c/ov2680.h | 73 +-----
3 files changed, 104 insertions(+), 203 deletions(-)
diff --git a/drivers/staging/media/atomisp/i2c/Kconfig b/drivers/staging/media/atomisp/i2c/Kconfig
index 16b6b808d4a7..e353b7fdbff0 100644
--- a/drivers/staging/media/atomisp/i2c/Kconfig
+++ b/drivers/staging/media/atomisp/i2c/Kconfig
@@ -53,6 +53,7 @@ config VIDEO_ATOMISP_OV2680
tristate "Omnivision OV2680 sensor support"
depends on ACPI
depends on I2C && VIDEO_DEV
+ select V4L2_CCI
help
This is a Video4Linux2 sensor-level driver for the Omnivision
OV2680 raw camera.
diff --git a/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c b/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c
index 77070bbd0157..3cc56090677c 100644
--- a/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c
+++ b/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c
@@ -25,11 +25,47 @@
#include <linux/pm_runtime.h>
#include <linux/types.h>
-#include <media/ov_16bit_addr_reg_helpers.h>
#include <media/v4l2-device.h>
#include "ov2680.h"
+#define OV2680_CHIP_ID 0x2680
+
+#define OV2680_REG_STREAM_CTRL CCI_REG8(0x0100)
+#define OV2680_REG_SOFT_RESET CCI_REG8(0x0103)
+
+#define OV2680_REG_CHIP_ID CCI_REG16(0x300a)
+#define OV2680_REG_SC_CMMN_SUB_ID CCI_REG8(0x302a)
+
+#define OV2680_REG_EXPOSURE_PK CCI_REG24(0x3500)
+#define OV2680_REG_R_MANUAL CCI_REG8(0x3503)
+#define OV2680_REG_GAIN_PK CCI_REG16(0x350a)
+
+#define OV2680_REG_SENSOR_CTRL_0A CCI_REG8(0x370a)
+
+#define OV2680_REG_HORIZONTAL_START CCI_REG16(0x3800)
+#define OV2680_REG_VERTICAL_START CCI_REG16(0x3802)
+#define OV2680_REG_HORIZONTAL_END CCI_REG16(0x3804)
+#define OV2680_REG_VERTICAL_END CCI_REG16(0x3806)
+#define OV2680_REG_HORIZONTAL_OUTPUT_SIZE CCI_REG16(0x3808)
+#define OV2680_REG_VERTICAL_OUTPUT_SIZE CCI_REG16(0x380a)
+#define OV2680_REG_TIMING_HTS CCI_REG16(0x380c)
+#define OV2680_REG_TIMING_VTS CCI_REG16(0x380e)
+#define OV2680_REG_ISP_X_WIN CCI_REG16(0x3810)
+#define OV2680_REG_ISP_Y_WIN CCI_REG16(0x3812)
+#define OV2680_REG_X_INC CCI_REG8(0x3814)
+#define OV2680_REG_Y_INC CCI_REG8(0x3815)
+#define OV2680_REG_FORMAT1 CCI_REG8(0x3820)
+#define OV2680_REG_FORMAT2 CCI_REG8(0x3821)
+
+#define OV2680_REG_ISP_CTRL00 CCI_REG8(0x5080)
+
+#define OV2680_REG_X_WIN CCI_REG16(0x5704)
+#define OV2680_REG_Y_WIN CCI_REG16(0x5706)
+
+#define OV2680_FRAME_RATE 30
+#define OV2680_INTEGRATION_TIME_MARGIN 8
+
static const struct v4l2_rect ov2680_default_crop = {
.left = OV2680_ACTIVE_START_LEFT,
.top = OV2680_ACTIVE_START_TOP,
@@ -37,21 +73,6 @@ static const struct v4l2_rect ov2680_default_crop = {
.height = OV2680_ACTIVE_HEIGHT,
};
-static int ov2680_write_reg_array(struct i2c_client *client,
- const struct ov2680_reg *reglist)
-{
- const struct ov2680_reg *next = reglist;
- int ret;
-
- for (; next->reg != 0; next++) {
- ret = ov_write_reg8(client, next->reg, next->val);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
static void ov2680_set_bayer_order(struct ov2680_dev *sensor, struct v4l2_mbus_framefmt *fmt)
{
static const int ov2680_hv_flip_bayer_order[] = {
@@ -78,7 +99,8 @@ static int ov2680_set_vflip(struct ov2680_dev *sensor, s32 val)
if (sensor->is_streaming)
return -EBUSY;
- ret = ov_update_reg(sensor->client, OV2680_REG_FORMAT1, BIT(2), val ? BIT(2) : 0);
+ ret = cci_update_bits(sensor->regmap, OV2680_REG_FORMAT1, BIT(2),
+ val ? BIT(2) : 0, NULL);
if (ret < 0)
return ret;
@@ -93,7 +115,8 @@ static int ov2680_set_hflip(struct ov2680_dev *sensor, s32 val)
if (sensor->is_streaming)
return -EBUSY;
- ret = ov_update_reg(sensor->client, OV2680_REG_FORMAT2, BIT(2), val ? BIT(2) : 0);
+ ret = cci_update_bits(sensor->regmap, OV2680_REG_FORMAT2, BIT(2),
+ val ? BIT(2) : 0, NULL);
if (ret < 0)
return ret;
@@ -103,30 +126,25 @@ static int ov2680_set_hflip(struct ov2680_dev *sensor, s32 val)
static int ov2680_exposure_set(struct ov2680_dev *sensor, u32 exp)
{
- return ov_write_reg24(sensor->client, OV2680_REG_EXPOSURE_PK_HIGH, exp << 4);
+ return cci_write(sensor->regmap, OV2680_REG_EXPOSURE_PK, exp << 4, NULL);
}
static int ov2680_gain_set(struct ov2680_dev *sensor, u32 gain)
{
- return ov_write_reg16(sensor->client, OV2680_REG_GAIN_PK, gain);
+ return cci_write(sensor->regmap, OV2680_REG_GAIN_PK, gain, NULL);
}
static int ov2680_test_pattern_set(struct ov2680_dev *sensor, int value)
{
- int ret;
+ int ret = 0;
if (!value)
- return ov_update_reg(sensor->client, OV2680_REG_ISP_CTRL00, BIT(7), 0);
+ return cci_update_bits(sensor->regmap, OV2680_REG_ISP_CTRL00, BIT(7), 0, NULL);
- ret = ov_update_reg(sensor->client, OV2680_REG_ISP_CTRL00, 0x03, value - 1);
- if (ret < 0)
- return ret;
+ cci_update_bits(sensor->regmap, OV2680_REG_ISP_CTRL00, 0x03, value - 1, &ret);
+ cci_update_bits(sensor->regmap, OV2680_REG_ISP_CTRL00, BIT(7), BIT(7), &ret);
- ret = ov_update_reg(sensor->client, OV2680_REG_ISP_CTRL00, BIT(7), BIT(7));
- if (ret < 0)
- return ret;
-
- return 0;
+ return ret;
}
static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl)
@@ -171,15 +189,16 @@ static const struct v4l2_ctrl_ops ov2680_ctrl_ops = {
static int ov2680_init_registers(struct v4l2_subdev *sd)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret;
+ struct ov2680_dev *sensor = to_ov2680_sensor(sd);
+ int ret = 0;
- ret = ov_write_reg8(client, OV2680_SW_RESET, 0x01);
+ cci_write(sensor->regmap, OV2680_REG_SOFT_RESET, 0x01, &ret);
/* Wait for sensor reset */
usleep_range(1000, 2000);
- ret |= ov2680_write_reg_array(client, ov2680_global_setting);
+ cci_multi_reg_write(sensor->regmap, ov2680_global_setting,
+ ARRAY_SIZE(ov2680_global_setting), &ret);
return ret;
}
@@ -247,9 +266,8 @@ static void ov2680_calc_mode(struct ov2680_dev *sensor)
static int ov2680_set_mode(struct ov2680_dev *sensor)
{
- struct i2c_client *client = sensor->client;
u8 sensor_ctrl_0a, inc, fmt1, fmt2;
- int ret;
+ int ret = 0;
if (sensor->mode.binning) {
sensor_ctrl_0a = 0x23;
@@ -263,77 +281,27 @@ static int ov2680_set_mode(struct ov2680_dev *sensor)
fmt2 = 0x00;
}
- ret = ov_write_reg8(client, OV2680_REG_SENSOR_CTRL_0A, sensor_ctrl_0a);
- if (ret)
- return ret;
+ cci_write(sensor->regmap, OV2680_REG_SENSOR_CTRL_0A, sensor_ctrl_0a, &ret);
+ cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_START, sensor->mode.h_start, &ret);
+ cci_write(sensor->regmap, OV2680_REG_VERTICAL_START, sensor->mode.v_start, &ret);
+ cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_END, sensor->mode.h_end, &ret);
+ cci_write(sensor->regmap, OV2680_REG_VERTICAL_END, sensor->mode.v_end, &ret);
+ cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_OUTPUT_SIZE,
+ sensor->mode.h_output_size, &ret);
+ cci_write(sensor->regmap, OV2680_REG_VERTICAL_OUTPUT_SIZE,
+ sensor->mode.v_output_size, &ret);
+ cci_write(sensor->regmap, OV2680_REG_TIMING_HTS, sensor->mode.hts, &ret);
+ cci_write(sensor->regmap, OV2680_REG_TIMING_VTS, sensor->mode.vts, &ret);
+ cci_write(sensor->regmap, OV2680_REG_ISP_X_WIN, 0, &ret);
+ cci_write(sensor->regmap, OV2680_REG_ISP_Y_WIN, 0, &ret);
+ cci_write(sensor->regmap, OV2680_REG_X_INC, inc, &ret);
+ cci_write(sensor->regmap, OV2680_REG_Y_INC, inc, &ret);
+ cci_write(sensor->regmap, OV2680_REG_X_WIN, sensor->mode.h_output_size, &ret);
+ cci_write(sensor->regmap, OV2680_REG_Y_WIN, sensor->mode.v_output_size, &ret);
+ cci_write(sensor->regmap, OV2680_REG_FORMAT1, fmt1, &ret);
+ cci_write(sensor->regmap, OV2680_REG_FORMAT2, fmt2, &ret);
- ret = ov_write_reg16(client, OV2680_HORIZONTAL_START_H, sensor->mode.h_start);
- if (ret)
- return ret;
-
- ret = ov_write_reg16(client, OV2680_VERTICAL_START_H, sensor->mode.v_start);
- if (ret)
- return ret;
-
- ret = ov_write_reg16(client, OV2680_HORIZONTAL_END_H, sensor->mode.h_end);
- if (ret)
- return ret;
-
- ret = ov_write_reg16(client, OV2680_VERTICAL_END_H, sensor->mode.v_end);
- if (ret)
- return ret;
-
- ret = ov_write_reg16(client, OV2680_HORIZONTAL_OUTPUT_SIZE_H,
- sensor->mode.h_output_size);
- if (ret)
- return ret;
-
- ret = ov_write_reg16(client, OV2680_VERTICAL_OUTPUT_SIZE_H,
- sensor->mode.v_output_size);
- if (ret)
- return ret;
-
- ret = ov_write_reg16(client, OV2680_HTS, sensor->mode.hts);
- if (ret)
- return ret;
-
- ret = ov_write_reg16(client, OV2680_VTS, sensor->mode.vts);
- if (ret)
- return ret;
-
- ret = ov_write_reg16(client, OV2680_ISP_X_WIN, 0);
- if (ret)
- return ret;
-
- ret = ov_write_reg16(client, OV2680_ISP_Y_WIN, 0);
- if (ret)
- return ret;
-
- ret = ov_write_reg8(client, OV2680_X_INC, inc);
- if (ret)
- return ret;
-
- ret = ov_write_reg8(client, OV2680_Y_INC, inc);
- if (ret)
- return ret;
-
- ret = ov_write_reg16(client, OV2680_X_WIN, sensor->mode.h_output_size);
- if (ret)
- return ret;
-
- ret = ov_write_reg16(client, OV2680_Y_WIN, sensor->mode.v_output_size);
- if (ret)
- return ret;
-
- ret = ov_write_reg8(client, OV2680_REG_FORMAT1, fmt1);
- if (ret)
- return ret;
-
- ret = ov_write_reg8(client, OV2680_REG_FORMAT2, fmt2);
- if (ret)
- return ret;
-
- return 0;
+ return ret;
}
static int ov2680_set_fmt(struct v4l2_subdev *sd,
@@ -478,35 +446,25 @@ static int ov2680_init_cfg(struct v4l2_subdev *sd,
return ov2680_set_fmt(sd, sd_state, &fmt);
}
-static int ov2680_detect(struct i2c_client *client)
+static int ov2680_detect(struct ov2680_dev *sensor)
{
- struct i2c_adapter *adapter = client->adapter;
- u32 high, low;
- int ret;
- u16 id;
- u8 revision;
+ u32 chip_id, rev;
+ int ret = 0;
- if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
- return -ENODEV;
-
- ret = ov_read_reg8(client, OV2680_SC_CMMN_CHIP_ID_H, &high);
- if (ret) {
- dev_err(&client->dev, "sensor_id_high = 0x%x\n", high);
- return -ENODEV;
- }
- ret = ov_read_reg8(client, OV2680_SC_CMMN_CHIP_ID_L, &low);
- id = ((((u16)high) << 8) | (u16)low);
-
- if (id != OV2680_ID) {
- dev_err(&client->dev, "sensor ID error 0x%x\n", id);
+ cci_read(sensor->regmap, OV2680_REG_CHIP_ID, &chip_id, &ret);
+ cci_read(sensor->regmap, OV2680_REG_SC_CMMN_SUB_ID, &rev, &ret);
+ if (ret < 0) {
+ dev_err(sensor->dev, "failed to read chip id\n");
return -ENODEV;
}
- ret = ov_read_reg8(client, OV2680_SC_CMMN_SUB_ID, &high);
- revision = (u8)high & 0x0f;
+ if (chip_id != OV2680_CHIP_ID) {
+ dev_err(sensor->dev, "chip id: 0x%04x does not match expected 0x%04x\n",
+ chip_id, OV2680_CHIP_ID);
+ return -ENODEV;
+ }
- dev_info(&client->dev, "sensor_revision id = 0x%x, rev= %d\n",
- id, revision);
+ dev_info(sensor->dev, "sensor_revision id = 0x%x, rev= %d\n", chip_id, rev & 0x0f);
return 0;
}
@@ -538,11 +496,11 @@ static int ov2680_s_stream(struct v4l2_subdev *sd, int enable)
if (ret)
goto error_power_down;
- ret = ov_write_reg8(client, OV2680_SW_STREAM, OV2680_START_STREAMING);
+ ret = cci_write(sensor->regmap, OV2680_REG_STREAM_CTRL, 1, NULL);
if (ret)
goto error_power_down;
} else {
- ov_write_reg8(client, OV2680_SW_STREAM, OV2680_STOP_STREAMING);
+ cci_write(sensor->regmap, OV2680_REG_STREAM_CTRL, 0, NULL);
pm_runtime_put(sensor->sd.dev);
}
@@ -563,6 +521,7 @@ static int ov2680_s_stream(struct v4l2_subdev *sd, int enable)
static int ov2680_s_config(struct v4l2_subdev *sd)
{
+ struct ov2680_dev *sensor = to_ov2680_sensor(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret;
@@ -573,7 +532,7 @@ static int ov2680_s_config(struct v4l2_subdev *sd)
}
/* config & detect sensor */
- ret = ov2680_detect(client);
+ ret = ov2680_detect(sensor);
if (ret)
dev_err(&client->dev, "ov2680_detect err s_config.\n");
@@ -586,7 +545,7 @@ static int ov2680_g_frame_interval(struct v4l2_subdev *sd,
struct v4l2_subdev_frame_interval *interval)
{
interval->interval.numerator = 1;
- interval->interval.denominator = OV2680_FPS;
+ interval->interval.denominator = OV2680_FRAME_RATE;
return 0;
}
@@ -638,7 +597,7 @@ static int ov2680_enum_frame_interval(struct v4l2_subdev *sd,
return -EINVAL;
fie->interval.numerator = 1;
- fie->interval.denominator = OV2680_FPS;
+ fie->interval.denominator = OV2680_FRAME_RATE;
return 0;
}
@@ -738,9 +697,13 @@ static int ov2680_probe(struct i2c_client *client)
if (!sensor)
return -ENOMEM;
+ sensor->regmap = cci_regmap_init_i2c(client, 16);
+ if (IS_ERR(sensor->regmap))
+ return PTR_ERR(sensor->regmap);
+
mutex_init(&sensor->lock);
- sensor->client = client;
+ sensor->dev = &client->dev;
v4l2_i2c_subdev_init(&sensor->sd, client, &ov2680_ops);
/*
diff --git a/drivers/staging/media/atomisp/i2c/ov2680.h b/drivers/staging/media/atomisp/i2c/ov2680.h
index d032af245674..d547846c2d13 100644
--- a/drivers/staging/media/atomisp/i2c/ov2680.h
+++ b/drivers/staging/media/atomisp/i2c/ov2680.h
@@ -24,6 +24,7 @@
#include <linux/delay.h>
#include <linux/videodev2.h>
#include <linux/spinlock.h>
+#include <media/v4l2-cci.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
@@ -44,75 +45,12 @@
/* 1704 * 1294 * 30fps = 66MHz pixel clock */
#define OV2680_PIXELS_PER_LINE 1704
#define OV2680_LINES_PER_FRAME 1294
-#define OV2680_FPS 30
+
#define OV2680_SKIP_FRAMES 3
/* If possible send 16 extra rows / lines to the ISP as padding */
#define OV2680_END_MARGIN 16
-#define OV2680_FOCAL_LENGTH_NUM 334 /*3.34mm*/
-
-#define OV2680_INTEGRATION_TIME_MARGIN 8
-#define OV2680_ID 0x2680
-
-/*
- * OV2680 System control registers
- */
-#define OV2680_SW_SLEEP 0x0100
-#define OV2680_SW_RESET 0x0103
-#define OV2680_SW_STREAM 0x0100
-
-#define OV2680_SC_CMMN_CHIP_ID_H 0x300A
-#define OV2680_SC_CMMN_CHIP_ID_L 0x300B
-#define OV2680_SC_CMMN_SCCB_ID 0x302B /* 0x300C*/
-#define OV2680_SC_CMMN_SUB_ID 0x302A /* process, version*/
-
-#define OV2680_GROUP_ACCESS 0x3208 /*Bit[7:4] Group control, Bit[3:0] Group ID*/
-
-#define OV2680_REG_EXPOSURE_PK_HIGH 0x3500
-#define OV2680_REG_GAIN_PK 0x350a
-
-#define OV2680_REG_SENSOR_CTRL_0A 0x370a
-
-#define OV2680_HORIZONTAL_START_H 0x3800 /* Bit[11:8] */
-#define OV2680_HORIZONTAL_START_L 0x3801 /* Bit[7:0] */
-#define OV2680_VERTICAL_START_H 0x3802 /* Bit[11:8] */
-#define OV2680_VERTICAL_START_L 0x3803 /* Bit[7:0] */
-#define OV2680_HORIZONTAL_END_H 0x3804 /* Bit[11:8] */
-#define OV2680_HORIZONTAL_END_L 0x3805 /* Bit[7:0] */
-#define OV2680_VERTICAL_END_H 0x3806 /* Bit[11:8] */
-#define OV2680_VERTICAL_END_L 0x3807 /* Bit[7:0] */
-#define OV2680_HORIZONTAL_OUTPUT_SIZE_H 0x3808 /* Bit[11:8] */
-#define OV2680_HORIZONTAL_OUTPUT_SIZE_L 0x3809 /* Bit[7:0] */
-#define OV2680_VERTICAL_OUTPUT_SIZE_H 0x380a /* Bit[11:8] */
-#define OV2680_VERTICAL_OUTPUT_SIZE_L 0x380b /* Bit[7:0] */
-#define OV2680_HTS 0x380c
-#define OV2680_VTS 0x380e
-#define OV2680_ISP_X_WIN 0x3810
-#define OV2680_ISP_Y_WIN 0x3812
-#define OV2680_X_INC 0x3814
-#define OV2680_Y_INC 0x3815
-
-#define OV2680_FRAME_OFF_NUM 0x4202
-
-/*Flip/Mirror*/
-#define OV2680_REG_FORMAT1 0x3820
-#define OV2680_REG_FORMAT2 0x3821
-
-#define OV2680_MWB_RED_GAIN_H 0x5004/*0x3400*/
-#define OV2680_MWB_GREEN_GAIN_H 0x5006/*0x3402*/
-#define OV2680_MWB_BLUE_GAIN_H 0x5008/*0x3404*/
-#define OV2680_MWB_GAIN_MAX 0x0fff
-
-#define OV2680_REG_ISP_CTRL00 0x5080
-
-#define OV2680_X_WIN 0x5704
-#define OV2680_Y_WIN 0x5706
-#define OV2680_WIN_CONTROL 0x5708
-
-#define OV2680_START_STREAMING 0x01
-#define OV2680_STOP_STREAMING 0x00
-
/*
* ov2680 device structure.
*/
@@ -121,7 +59,8 @@ struct ov2680_dev {
struct media_pad pad;
/* Protect against concurrent changes to controls */
struct mutex lock;
- struct i2c_client *client;
+ struct device *dev;
+ struct regmap *regmap;
struct gpio_desc *powerdown;
struct fwnode_handle *ep_fwnode;
bool is_streaming;
@@ -173,7 +112,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
return &sensor->sd;
}
-static struct ov2680_reg const ov2680_global_setting[] = {
+static const struct reg_sequence ov2680_global_setting[] = {
/* MIPI PHY, 0x10 -> 0x1c enable bp_c_hs_en_lat and bp_d_hs_en_lat */
{0x3016, 0x1c},
@@ -242,8 +181,6 @@ static struct ov2680_reg const ov2680_global_setting[] = {
/* DPC THRE RATIO 0x04 (4) -> 0x00 (0) */
{0x5792, 0x00},
-
- {}
};
#endif
--
2.40.1
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH 3/3] media: Remove ov_16bit_addr_reg_helpers.h
2023-06-06 16:58 [PATCH 0/3] media: Add MIPI CCI register access helper functions Hans de Goede
2023-06-06 16:58 ` [PATCH 1/3] " Hans de Goede
2023-06-06 16:58 ` [PATCH 2/3] media: atomisp: ov2680: Convert to new CCI register access helpers Hans de Goede
@ 2023-06-06 16:58 ` Hans de Goede
2023-06-07 15:57 ` Laurent Pinchart
2 siblings, 1 reply; 38+ messages in thread
From: Hans de Goede @ 2023-06-06 16:58 UTC (permalink / raw)
To: Laurent Pinchart, Sakari Ailus
Cc: Hans de Goede, Mauro Carvalho Chehab, Andy Shevchenko,
linux-media
The helpers in this header are not used anywhere anymore,
they have been superseded by the new CCI register access helpers.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
include/media/ov_16bit_addr_reg_helpers.h | 92 -----------------------
1 file changed, 92 deletions(-)
delete mode 100644 include/media/ov_16bit_addr_reg_helpers.h
diff --git a/include/media/ov_16bit_addr_reg_helpers.h b/include/media/ov_16bit_addr_reg_helpers.h
deleted file mode 100644
index 1c60a50bd795..000000000000
--- a/include/media/ov_16bit_addr_reg_helpers.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * I2C register access helpers for Omnivision OVxxxx image sensors which expect
- * a 16 bit register address in big-endian format and which have 1-3 byte
- * wide registers, in big-endian format (for the higher width registers).
- *
- * Based on the register helpers from drivers/media/i2c/ov2680.c which is:
- * Copyright (C) 2018 Linaro Ltd
- */
-#ifndef __OV_16BIT_ADDR_REG_HELPERS_H
-#define __OV_16BIT_ADDR_REG_HELPERS_H
-
-#include <asm/unaligned.h>
-#include <linux/dev_printk.h>
-#include <linux/i2c.h>
-
-static inline int ov_read_reg(struct i2c_client *client, u16 reg,
- unsigned int len, u32 *val)
-{
- u8 addr_buf[2], data_buf[4] = { };
- struct i2c_msg msgs[2];
- int ret;
-
- if (len > 4)
- return -EINVAL;
-
- put_unaligned_be16(reg, addr_buf);
-
- msgs[0].addr = client->addr;
- msgs[0].flags = 0;
- msgs[0].len = ARRAY_SIZE(addr_buf);
- msgs[0].buf = addr_buf;
-
- msgs[1].addr = client->addr;
- msgs[1].flags = I2C_M_RD;
- msgs[1].len = len;
- msgs[1].buf = &data_buf[4 - len];
-
- ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
- if (ret != ARRAY_SIZE(msgs)) {
- dev_err(&client->dev, "read error: reg=0x%4x: %d\n", reg, ret);
- return -EIO;
- }
-
- *val = get_unaligned_be32(data_buf);
-
- return 0;
-}
-
-#define ov_read_reg8(s, r, v) ov_read_reg(s, r, 1, v)
-#define ov_read_reg16(s, r, v) ov_read_reg(s, r, 2, v)
-#define ov_read_reg24(s, r, v) ov_read_reg(s, r, 3, v)
-
-static inline int ov_write_reg(struct i2c_client *client, u16 reg,
- unsigned int len, u32 val)
-{
- u8 buf[6];
- int ret;
-
- if (len > 4)
- return -EINVAL;
-
- put_unaligned_be16(reg, buf);
- put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
- ret = i2c_master_send(client, buf, len + 2);
- if (ret != len + 2) {
- dev_err(&client->dev, "write error: reg=0x%4x: %d\n", reg, ret);
- return -EIO;
- }
-
- return 0;
-}
-
-#define ov_write_reg8(s, r, v) ov_write_reg(s, r, 1, v)
-#define ov_write_reg16(s, r, v) ov_write_reg(s, r, 2, v)
-#define ov_write_reg24(s, r, v) ov_write_reg(s, r, 3, v)
-
-static inline int ov_update_reg(struct i2c_client *client, u16 reg, u8 mask, u8 val)
-{
- u32 readval;
- int ret;
-
- ret = ov_read_reg8(client, reg, &readval);
- if (ret < 0)
- return ret;
-
- val = (readval & ~mask) | (val & mask);
-
- return ov_write_reg8(client, reg, val);
-}
-
-#endif
--
2.40.1
^ permalink raw reply related [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-06 16:58 ` [PATCH 1/3] " Hans de Goede
@ 2023-06-06 20:43 ` Andy Shevchenko
2023-06-07 8:40 ` Hans de Goede
2023-06-07 7:50 ` Sakari Ailus
2023-06-07 18:18 ` Laurent Pinchart
2 siblings, 1 reply; 38+ messages in thread
From: Andy Shevchenko @ 2023-06-06 20:43 UTC (permalink / raw)
To: Hans de Goede
Cc: Laurent Pinchart, Sakari Ailus, Mauro Carvalho Chehab,
Andy Shevchenko, linux-media
On Tue, Jun 6, 2023 at 7:58 PM Hans de Goede <hdegoede@redhat.com> wrote:
>
> The CSI2 specification specifies a standard method to access camera sensor
> registers called "Camera Control Interface (CCI)".
>
> This uses either 8 or 16 bit (big-endian wire order) register addresses
> and supports 8, 16, 24 or 32 bit (big-endian wire order) register widths.
>
> Currently a lot of Linux camera sensor drivers all have their own custom
> helpers for this, often copy and pasted from other drivers.
>
> Add a set of generic helpers for this so that all sensor drivers can
> switch to a single common implementation.
>
> These helpers take an extra optional "int *err" function parameter,
> this can be used to chain a bunch of register accesses together with
> only a single error check at the end, rather then needing to error
> check each individual register access. The first failing call will
> set the contents of err to a non 0 value and all other calls will
> then become no-ops.
...
> +#include <linux/delay.h>
> +#include <linux/dev_printk.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
+ types.h
> +#include <media/v4l2-cci.h>
> +int cci_read(struct regmap *map, u32 reg, u32 *val, int *err)
> +{
> + int i, len, ret;
> + u8 buf[4];
> +
> + if (err && *err)
> + return *err;
> +
> + /* Set len to register width in bytes */
> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
> + reg &= CCI_REG_ADDR_MASK;
> +
> + ret = regmap_bulk_read(map, reg, buf, len);
> + if (ret) {
> + dev_err(regmap_get_device(map), "Error reading reg 0x%4x: %d\n", reg, ret);
> + if (err)
> + *err = ret;
> +
> + return ret;
> + }
> +
> + *val = 0;
> + for (i = 0; i < len; i++) {
> + *val <<= 8;
> + *val |= buf[i];
> + }
I really prefer to see put_unaligned() here depending on the length.
Note, that on some CPUs it might be one assembly instruction or even
none, depending on how the result is going to be used.
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(cci_read);
Can we have it namespaced?
> +int cci_write(struct regmap *map, u32 reg, u32 val, int *err)
> +{
> + int i, len, ret;
> + u8 buf[4];
> +
> + if (err && *err)
> + return *err;
> +
> + /* Set len to register width in bytes */
> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
> + reg &= CCI_REG_ADDR_MASK;
> +
> + for (i = 0; i < len; i++) {
> + buf[len - i - 1] = val & 0xff;
> + val >>= 8;
> + }
> +
> + ret = regmap_bulk_write(map, reg, buf, len);
> + if (ret) {
> + dev_err(regmap_get_device(map), "Error writing reg 0x%4x: %d\n", reg, ret);
> + if (err)
> + *err = ret;
> + }
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(cci_write);
Same comments as per above function.
...
> + if (regs[i].delay_us)
I'm wondering why fsleep() doesn't have this check? Or does it?
> + fsleep(regs[i].delay_us);
...
> +struct regmap *cci_regmap_init_i2c(struct i2c_client *client, int reg_addr_bits)
> +{
> + struct regmap_config config = {
> + .reg_bits = reg_addr_bits,
> + .val_bits = 8,
> + .reg_format_endian = REGMAP_ENDIAN_BIG,
Is the lock required?
If so, how is it helpful?
Can we move this outside as static const?
> + };
> +
> + return devm_regmap_init_i2c(client, &config);
> +}
...
> +#ifndef _V4L2_CCI_H
> +#define _V4L2_CCI_H
+ bits.h
> +#include <linux/regmap.h>
Not used, rather requires forward declarations of
struct regmap
struct reg_sequence
Also note missing i2c_client forward declaration.
> +#include <linux/types.h>
> +
> +/*
> + * Note cci_reg_8 deliberately is 0, not 1, so that raw
> + * (not wrapped in a CCI_REG*() macro) register addresses
> + * do 8 bit wide accesses. This allows unchanged use of register
> + * initialization lists of raw address, value pairs which only
> + * do 8 bit width accesses. Which makes porting drivers easier.
> + */
> +enum cci_reg_type {
> + cci_reg_8 = 0,
But this is guaranteed by the C standard... See also below.
> + cci_reg_16,
But this one becomes 1, so the above comment doesn't clarify why it's
okay to have it 1 and not 2.
> + cci_reg_24,
> + cci_reg_32,
> +};
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 2/3] media: atomisp: ov2680: Convert to new CCI register access helpers
2023-06-06 16:58 ` [PATCH 2/3] media: atomisp: ov2680: Convert to new CCI register access helpers Hans de Goede
@ 2023-06-06 20:53 ` Andy Shevchenko
2023-06-07 8:53 ` Hans de Goede
2023-06-07 16:05 ` Laurent Pinchart
1 sibling, 1 reply; 38+ messages in thread
From: Andy Shevchenko @ 2023-06-06 20:53 UTC (permalink / raw)
To: Hans de Goede
Cc: Laurent Pinchart, Sakari Ailus, Mauro Carvalho Chehab,
Andy Shevchenko, linux-media
On Tue, Jun 6, 2023 at 7:58 PM Hans de Goede <hdegoede@redhat.com> wrote:
>
> Use the new comon CCI register access helpers to replace the private
> register access helpers in the ov2680 driver.
>
> While at it also switch to using the same register address defines
> as the standard drivers/media/i2c/ov2680.c driver to make merging
> the 2 drivers simpler.
...
> + cci_write(sensor->regmap, OV2680_REG_SENSOR_CTRL_0A, sensor_ctrl_0a, &ret);
> + cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_START, sensor->mode.h_start, &ret);
> + cci_write(sensor->regmap, OV2680_REG_VERTICAL_START, sensor->mode.v_start, &ret);
> + cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_END, sensor->mode.h_end, &ret);
> + cci_write(sensor->regmap, OV2680_REG_VERTICAL_END, sensor->mode.v_end, &ret);
> + cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_OUTPUT_SIZE,
> + sensor->mode.h_output_size, &ret);
> + cci_write(sensor->regmap, OV2680_REG_VERTICAL_OUTPUT_SIZE,
> + sensor->mode.v_output_size, &ret);
> + cci_write(sensor->regmap, OV2680_REG_TIMING_HTS, sensor->mode.hts, &ret);
> + cci_write(sensor->regmap, OV2680_REG_TIMING_VTS, sensor->mode.vts, &ret);
> + cci_write(sensor->regmap, OV2680_REG_ISP_X_WIN, 0, &ret);
> + cci_write(sensor->regmap, OV2680_REG_ISP_Y_WIN, 0, &ret);
> + cci_write(sensor->regmap, OV2680_REG_X_INC, inc, &ret);
> + cci_write(sensor->regmap, OV2680_REG_Y_INC, inc, &ret);
> + cci_write(sensor->regmap, OV2680_REG_X_WIN, sensor->mode.h_output_size, &ret);
> + cci_write(sensor->regmap, OV2680_REG_Y_WIN, sensor->mode.v_output_size, &ret);
> + cci_write(sensor->regmap, OV2680_REG_FORMAT1, fmt1, &ret);
> + cci_write(sensor->regmap, OV2680_REG_FORMAT2, fmt2, &ret);
I know that &ret thingy was discussed before and Laurent is keen to
have this, but has anybody actually tested how bad or not at all the
code generation becomes?
...
> + struct device *dev;
> + struct regmap *regmap;
Isn't the same device associated with regmap? If so, one of them
probably duplicates the other.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-06 16:58 ` [PATCH 1/3] " Hans de Goede
2023-06-06 20:43 ` Andy Shevchenko
@ 2023-06-07 7:50 ` Sakari Ailus
2023-06-07 8:46 ` Hans de Goede
2023-06-07 18:18 ` Laurent Pinchart
2 siblings, 1 reply; 38+ messages in thread
From: Sakari Ailus @ 2023-06-07 7:50 UTC (permalink / raw)
To: Hans de Goede
Cc: Laurent Pinchart, Mauro Carvalho Chehab, Andy Shevchenko,
linux-media
Hi Hans,
Thank you for the patchset.
On Tue, Jun 06, 2023 at 06:58:06PM +0200, Hans de Goede wrote:
> The CSI2 specification specifies a standard method to access camera sensor
> registers called "Camera Control Interface (CCI)".
>
> This uses either 8 or 16 bit (big-endian wire order) register addresses
> and supports 8, 16, 24 or 32 bit (big-endian wire order) register widths.
>
> Currently a lot of Linux camera sensor drivers all have their own custom
> helpers for this, often copy and pasted from other drivers.
>
> Add a set of generic helpers for this so that all sensor drivers can
> switch to a single common implementation.
>
> These helpers take an extra optional "int *err" function parameter,
> this can be used to chain a bunch of register accesses together with
> only a single error check at the end, rather then needing to error
> check each individual register access. The first failing call will
> set the contents of err to a non 0 value and all other calls will
> then become no-ops.
>
> Link: https://lore.kernel.org/linux-media/59aefa7f-7bf9-6736-6040-39551329cd0a@redhat.com/
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> ---
> Documentation/driver-api/media/v4l2-cci.rst | 5 +
> Documentation/driver-api/media/v4l2-core.rst | 1 +
> drivers/media/v4l2-core/Kconfig | 5 +
> drivers/media/v4l2-core/Makefile | 1 +
> drivers/media/v4l2-core/v4l2-cci.c | 142 +++++++++++++++++++
> include/media/v4l2-cci.h | 109 ++++++++++++++
> 6 files changed, 263 insertions(+)
> create mode 100644 Documentation/driver-api/media/v4l2-cci.rst
> create mode 100644 drivers/media/v4l2-core/v4l2-cci.c
> create mode 100644 include/media/v4l2-cci.h
>
> diff --git a/Documentation/driver-api/media/v4l2-cci.rst b/Documentation/driver-api/media/v4l2-cci.rst
> new file mode 100644
> index 000000000000..dd297a40ed20
> --- /dev/null
> +++ b/Documentation/driver-api/media/v4l2-cci.rst
> @@ -0,0 +1,5 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +V4L2 CCI kAPI
> +^^^^^^^^^^^^^
> +.. kernel-doc:: include/media/v4l2-cci.h
> diff --git a/Documentation/driver-api/media/v4l2-core.rst b/Documentation/driver-api/media/v4l2-core.rst
> index 1a8c4a5f256b..239045ecc8f4 100644
> --- a/Documentation/driver-api/media/v4l2-core.rst
> +++ b/Documentation/driver-api/media/v4l2-core.rst
> @@ -22,6 +22,7 @@ Video4Linux devices
> v4l2-mem2mem
> v4l2-async
> v4l2-fwnode
> + v4l2-cci
> v4l2-rect
> v4l2-tuner
> v4l2-common
> diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
> index 348559bc2468..523ba243261d 100644
> --- a/drivers/media/v4l2-core/Kconfig
> +++ b/drivers/media/v4l2-core/Kconfig
> @@ -74,6 +74,11 @@ config V4L2_FWNODE
> config V4L2_ASYNC
> tristate
>
> +config V4L2_CCI
> + tristate
> + depends on I2C
> + select REGMAP_I2C
> +
> # Used by drivers that need Videobuf modules
> config VIDEOBUF_GEN
> tristate
> diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
> index 41d91bd10cf2..be2551705755 100644
> --- a/drivers/media/v4l2-core/Makefile
> +++ b/drivers/media/v4l2-core/Makefile
> @@ -25,6 +25,7 @@ videodev-$(CONFIG_VIDEO_V4L2_I2C) += v4l2-i2c.o
> # (e. g. LC_ALL=C sort Makefile)
>
> obj-$(CONFIG_V4L2_ASYNC) += v4l2-async.o
> +obj-$(CONFIG_V4L2_CCI) += v4l2-cci.o
> obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash-led-class.o
> obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o
> obj-$(CONFIG_V4L2_H264) += v4l2-h264.o
> diff --git a/drivers/media/v4l2-core/v4l2-cci.c b/drivers/media/v4l2-core/v4l2-cci.c
> new file mode 100644
> index 000000000000..21207d137dbe
> --- /dev/null
> +++ b/drivers/media/v4l2-core/v4l2-cci.c
> @@ -0,0 +1,142 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * MIPI Camera Control Interface (CCI) register access helpers.
> + *
> + * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/dev_printk.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +
> +#include <media/v4l2-cci.h>
> +
> +int cci_read(struct regmap *map, u32 reg, u32 *val, int *err)
> +{
> + int i, len, ret;
Could i and len be unsigned?
> + u8 buf[4];
> +
> + if (err && *err)
> + return *err;
> +
> + /* Set len to register width in bytes */
> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
> + reg &= CCI_REG_ADDR_MASK;
> +
> + ret = regmap_bulk_read(map, reg, buf, len);
> + if (ret) {
> + dev_err(regmap_get_device(map), "Error reading reg 0x%4x: %d\n", reg, ret);
> + if (err)
> + *err = ret;
> +
> + return ret;
> + }
> +
> + *val = 0;
> + for (i = 0; i < len; i++) {
> + *val <<= 8;
> + *val |= buf[i];
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(cci_read);
> +
> +int cci_write(struct regmap *map, u32 reg, u32 val, int *err)
> +{
> + int i, len, ret;
Same here.
> + u8 buf[4];
> +
> + if (err && *err)
> + return *err;
> +
> + /* Set len to register width in bytes */
> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
> + reg &= CCI_REG_ADDR_MASK;
> +
> + for (i = 0; i < len; i++) {
> + buf[len - i - 1] = val & 0xff;
> + val >>= 8;
> + }
> +
> + ret = regmap_bulk_write(map, reg, buf, len);
> + if (ret) {
> + dev_err(regmap_get_device(map), "Error writing reg 0x%4x: %d\n", reg, ret);
> + if (err)
> + *err = ret;
> + }
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(cci_write);
> +
> +int cci_update_bits(struct regmap *map, u32 reg, u32 mask, u32 val, int *err)
> +{
> + int width, ret;
> + u32 readval;
> +
> + if (err && *err)
> + return *err;
> +
> + /*
> + * For single byte updates use regmap_update_bits(), this uses
> + * the regmap-lock to protect against other read-modify-writes racing.
> + */
> + width = (reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT;
> + if (width == cci_reg_8) {
> + reg &= CCI_REG_ADDR_MASK;
> + ret = regmap_update_bits(map, reg, mask, val);
> + if (ret) {
> + dev_err(regmap_get_device(map), "Error updating reg 0x%4x: %d\n", reg, ret);
> + if (err)
> + *err = ret;
> + }
> +
> + return ret;
> + }
> +
> + ret = cci_read(map, reg, &readval, err);
> + if (ret)
> + return ret;
> +
> + val = (readval & ~mask) | (val & mask);
> +
> + return cci_write(map, reg, val, err);
> +}
> +EXPORT_SYMBOL_GPL(cci_update_bits);
> +
> +int cci_multi_reg_write(struct regmap *map, const struct reg_sequence *regs, int num_regs, int *err)
> +{
> + int i, ret;
> +
> + if (err && *err)
> + return *err;
> +
> + for (i = 0; i < num_regs; i++) {
> + ret = cci_write(map, regs[i].reg, regs[i].def, err);
> + if (ret)
> + return ret;
> +
> + if (regs[i].delay_us)
> + fsleep(regs[i].delay_us);
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(cci_multi_reg_write);
> +
> +struct regmap *cci_regmap_init_i2c(struct i2c_client *client, int reg_addr_bits)
> +{
> + struct regmap_config config = {
> + .reg_bits = reg_addr_bits,
> + .val_bits = 8,
> + .reg_format_endian = REGMAP_ENDIAN_BIG,
> + };
> +
> + return devm_regmap_init_i2c(client, &config);
> +}
> +EXPORT_SYMBOL_GPL(cci_regmap_init_i2c);
Bulk write functions would be nice, too: CCI does not limit access to
register-like targets.
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
> diff --git a/include/media/v4l2-cci.h b/include/media/v4l2-cci.h
> new file mode 100644
> index 000000000000..69b8a7c4a013
> --- /dev/null
> +++ b/include/media/v4l2-cci.h
> @@ -0,0 +1,109 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * MIPI Camera Control Interface (CCI) register access helpers.
> + *
> + * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
> + */
> +#ifndef _V4L2_CCI_H
> +#define _V4L2_CCI_H
> +
> +#include <linux/regmap.h>
> +#include <linux/types.h>
> +
> +/*
> + * Note cci_reg_8 deliberately is 0, not 1, so that raw
> + * (not wrapped in a CCI_REG*() macro) register addresses
> + * do 8 bit wide accesses. This allows unchanged use of register
> + * initialization lists of raw address, value pairs which only
> + * do 8 bit width accesses. Which makes porting drivers easier.
> + */
> +enum cci_reg_type {
> + cci_reg_8 = 0,
> + cci_reg_16,
> + cci_reg_24,
> + cci_reg_32,
> +};
> +
> +/*
> + * Macros to define register address with the register width encoded
> + * into the higher bits. CCI_REG8() is a no-op so its use is optional.
> + */
> +#define CCI_REG_ADDR_MASK GENMASK(15, 0)
> +#define CCI_REG_WIDTH_SHIFT 16
> +#define CCI_REG_WIDTH_MASK GENMASK(17, 16)
> +
> +#define CCI_REG8(x) ((cci_reg_8 << CCI_REG_WIDTH_SHIFT) | (x))
> +#define CCI_REG16(x) ((cci_reg_16 << CCI_REG_WIDTH_SHIFT) | (x))
> +#define CCI_REG24(x) ((cci_reg_24 << CCI_REG_WIDTH_SHIFT) | (x))
> +#define CCI_REG32(x) ((cci_reg_32 << CCI_REG_WIDTH_SHIFT) | (x))
I'd drop enum ccsi_reg_type and just define the values here using plain
numbers. How you test which width you have changes a little but not
necessarily for worse. Up to you.
> +
> +/**
> + * cci_read() - Read a value from a single CCI register
> + *
> + * @map: Register map to write to
> + * @reg: Register address to write, use CCI_REG#() macros to encode reg width
> + * @val: Pointer to store read value
> + * @err: optional pointer to store errors, if a previous error is set the write will be skipped
> + *
> + * Return: %0 on success or a negative error code on failure.
> + */
> +int cci_read(struct regmap *map, u32 reg, u32 *val, int *err);
> +
> +/**
> + * cci_write() - Write a value to a single CCI register
> + *
> + * @map: Register map to write to
> + * @reg: Register address to write, use CCI_REG#() macros to encode reg width
> + * @val: Value to be written
> + * @err: optional pointer to store errors, if a previous error is set the write will be skipped
> + *
> + * Return: %0 on success or a negative error code on failure.
> + */
> +int cci_write(struct regmap *map, u32 reg, u32 val, int *err);
> +
> +/**
> + * cci_update_bits() - Perform a read/modify/write cycle on a single CCI register
> + *
> + * @map: Register map to write to
> + * @reg: Register address to write, use CCI_REG#() macros to encode reg width
> + * @mask: Bitmask to change
> + * @val: New value for bitmask
> + * @err: optional pointer to store errors, if a previous error is set the update will be skipped
> + *
> + * For 8 bit width registers this is guaranteed to be atomic wrt other
> + * cci_*() register access functions. For multi-byte width registers
> + * atomicity is NOT guaranteed.
> + *
> + * Return: %0 on success or a negative error code on failure.
> + */
> +int cci_update_bits(struct regmap *map, u32 reg, u32 mask, u32 val, int *err);
> +
> +/**
> + * cci_multi_reg_write() - Write multiple registers to the device
> + *
> + * @map: Register map to write to
> + * @regs: Array of structures containing register-address, value pairs to be written
> + * register-addresses use CCI_REG#() macros to encode reg width
> + * @num_regs: Number of registers to write
> + * @err: optional pointer to store errors, if a previous error is set the update will be skipped
> + *
> + * Write multiple registers to the device where the set of register, value
> + * pairs are supplied in any order, possibly not all in a single range.
> + *
> + * Return: %0 on success or a negative error code on failure.
> + */
> +int cci_multi_reg_write(struct regmap *map, const struct reg_sequence *regs, int num_regs, int *err);
> +
> +/**
> + * cci_regmap_init_i2c() - Create regmap to use with cci_*() register access functions
> + *
> + * @client: i2c_client to create the regmap for
> + * @reg_addr_bits: register address width to use (8 or 16)
> + *
> + * Note the memory for the created regmap is devm() managed, tied to the client.
> + *
> + * Return: %0 on success or a negative error code on failure.
> + */
> +struct regmap *cci_regmap_init_i2c(struct i2c_client *client, int reg_addr_bits);
> +
> +#endif
--
Kind regards,
Sakari Ailus
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-06 20:43 ` Andy Shevchenko
@ 2023-06-07 8:40 ` Hans de Goede
2023-06-07 12:01 ` Sakari Ailus
0 siblings, 1 reply; 38+ messages in thread
From: Hans de Goede @ 2023-06-07 8:40 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Laurent Pinchart, Sakari Ailus, Mauro Carvalho Chehab,
Andy Shevchenko, linux-media
Hi,
On 6/6/23 22:43, Andy Shevchenko wrote:
> On Tue, Jun 6, 2023 at 7:58 PM Hans de Goede <hdegoede@redhat.com> wrote:
>>
>> The CSI2 specification specifies a standard method to access camera sensor
>> registers called "Camera Control Interface (CCI)".
>>
>> This uses either 8 or 16 bit (big-endian wire order) register addresses
>> and supports 8, 16, 24 or 32 bit (big-endian wire order) register widths.
>>
>> Currently a lot of Linux camera sensor drivers all have their own custom
>> helpers for this, often copy and pasted from other drivers.
>>
>> Add a set of generic helpers for this so that all sensor drivers can
>> switch to a single common implementation.
>>
>> These helpers take an extra optional "int *err" function parameter,
>> this can be used to chain a bunch of register accesses together with
>> only a single error check at the end, rather then needing to error
>> check each individual register access. The first failing call will
>> set the contents of err to a non 0 value and all other calls will
>> then become no-ops.
>
> ...
>
>> +#include <linux/delay.h>
>> +#include <linux/dev_printk.h>
>> +#include <linux/module.h>
>> +#include <linux/regmap.h>
>
> + types.h
>
>> +#include <media/v4l2-cci.h>
>
>> +int cci_read(struct regmap *map, u32 reg, u32 *val, int *err)
>> +{
>> + int i, len, ret;
>> + u8 buf[4];
>> +
>> + if (err && *err)
>> + return *err;
>> +
>> + /* Set len to register width in bytes */
>> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
>> + reg &= CCI_REG_ADDR_MASK;
>> +
>> + ret = regmap_bulk_read(map, reg, buf, len);
>> + if (ret) {
>> + dev_err(regmap_get_device(map), "Error reading reg 0x%4x: %d\n", reg, ret);
>> + if (err)
>> + *err = ret;
>> +
>> + return ret;
>> + }
>> +
>> + *val = 0;
>> + for (i = 0; i < len; i++) {
>> + *val <<= 8;
>> + *val |= buf[i];
>> + }
>
> I really prefer to see put_unaligned() here depending on the length.
> Note, that on some CPUs it might be one assembly instruction or even
> none, depending on how the result is going to be used.
Ok, so you mean changing it to something like this:
switch (len)
case 1:
*val = buf[0];
break;
case 2:
*val = get_unaligned_be16(buf);
break;
case 3:
*val = __get_unaligned_be24(buf);
break;
case 4:
*val = get_unaligned_be32(buf);
break;
}
?
>
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(cci_read);
>
> Can we have it namespaced?
I'm not sure if having just these 5 symbols in their own namespace is worth it. SO far the media subsystem is not using module/symbol namespacing at all.
Sakari, Laurent, any opinions on this ?
>> +int cci_write(struct regmap *map, u32 reg, u32 val, int *err)
>> +{
>> + int i, len, ret;
>> + u8 buf[4];
>> +
>> + if (err && *err)
>> + return *err;
>> +
>> + /* Set len to register width in bytes */
>> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
>> + reg &= CCI_REG_ADDR_MASK;
>> +
>> + for (i = 0; i < len; i++) {
>> + buf[len - i - 1] = val & 0xff;
>> + val >>= 8;
>> + }
>> +
>> + ret = regmap_bulk_write(map, reg, buf, len);
>> + if (ret) {
>> + dev_err(regmap_get_device(map), "Error writing reg 0x%4x: %d\n", reg, ret);
>> + if (err)
>> + *err = ret;
>> + }
>> +
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(cci_write);
>
> Same comments as per above function.
>
> ...
>
>> + if (regs[i].delay_us)
>
> I'm wondering why fsleep() doesn't have this check? Or does it?
>
>> + fsleep(regs[i].delay_us);
>
> ...
>
>> +struct regmap *cci_regmap_init_i2c(struct i2c_client *client, int reg_addr_bits)
>> +{
>> + struct regmap_config config = {
>> + .reg_bits = reg_addr_bits,
>> + .val_bits = 8,
>> + .reg_format_endian = REGMAP_ENDIAN_BIG,
>
> Is the lock required?
> If so, how is it helpful?
Interesting questions sensor drivers typically already do
their own locking.
So I guess we could indeed tell regmap to skip locking here.
Sakari, Laurent any opinion on this ?
> Can we move this outside as static const?
No, because reg_bits is not const.
>> + };
>> +
>> + return devm_regmap_init_i2c(client, &config);
>> +}
>
> ...
>
>> +#ifndef _V4L2_CCI_H
>> +#define _V4L2_CCI_H
>
> + bits.h
>
>> +#include <linux/regmap.h>
>
> Not used, rather requires forward declarations of
>
> struct regmap
> struct reg_sequence
Ack, I'll change this for the next version.
> Also note missing i2c_client forward declaration.
That was also taken care of by regmap.h.
>
>> +#include <linux/types.h>
>> +
>> +/*
>> + * Note cci_reg_8 deliberately is 0, not 1, so that raw
>> + * (not wrapped in a CCI_REG*() macro) register addresses
>> + * do 8 bit wide accesses. This allows unchanged use of register
>> + * initialization lists of raw address, value pairs which only
>> + * do 8 bit width accesses. Which makes porting drivers easier.
>> + */
>> +enum cci_reg_type {
>> + cci_reg_8 = 0,
>
> But this is guaranteed by the C standard... See also below.
>
>> + cci_reg_16,
>
> But this one becomes 1, so the above comment doesn't clarify why it's
> okay to have it 1 and not 2.
Basically the idea is that the enum value is the reg-width in bytes - 1
where the - 1 is there so that cci_reg_8 = 0 .
Regards,
Hans
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-07 7:50 ` Sakari Ailus
@ 2023-06-07 8:46 ` Hans de Goede
2023-06-07 11:41 ` Sakari Ailus
0 siblings, 1 reply; 38+ messages in thread
From: Hans de Goede @ 2023-06-07 8:46 UTC (permalink / raw)
To: Sakari Ailus
Cc: Laurent Pinchart, Mauro Carvalho Chehab, Andy Shevchenko,
linux-media
Hi Sakari,
On 6/7/23 09:50, Sakari Ailus wrote:
> Hi Hans,
>
> Thank you for the patchset.
>
> On Tue, Jun 06, 2023 at 06:58:06PM +0200, Hans de Goede wrote:
>> The CSI2 specification specifies a standard method to access camera sensor
>> registers called "Camera Control Interface (CCI)".
>>
>> This uses either 8 or 16 bit (big-endian wire order) register addresses
>> and supports 8, 16, 24 or 32 bit (big-endian wire order) register widths.
>>
>> Currently a lot of Linux camera sensor drivers all have their own custom
>> helpers for this, often copy and pasted from other drivers.
>>
>> Add a set of generic helpers for this so that all sensor drivers can
>> switch to a single common implementation.
>>
>> These helpers take an extra optional "int *err" function parameter,
>> this can be used to chain a bunch of register accesses together with
>> only a single error check at the end, rather then needing to error
>> check each individual register access. The first failing call will
>> set the contents of err to a non 0 value and all other calls will
>> then become no-ops.
>>
>> Link: https://lore.kernel.org/linux-media/59aefa7f-7bf9-6736-6040-39551329cd0a@redhat.com/
>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>> ---
>> Documentation/driver-api/media/v4l2-cci.rst | 5 +
>> Documentation/driver-api/media/v4l2-core.rst | 1 +
>> drivers/media/v4l2-core/Kconfig | 5 +
>> drivers/media/v4l2-core/Makefile | 1 +
>> drivers/media/v4l2-core/v4l2-cci.c | 142 +++++++++++++++++++
>> include/media/v4l2-cci.h | 109 ++++++++++++++
>> 6 files changed, 263 insertions(+)
>> create mode 100644 Documentation/driver-api/media/v4l2-cci.rst
>> create mode 100644 drivers/media/v4l2-core/v4l2-cci.c
>> create mode 100644 include/media/v4l2-cci.h
>>
>> diff --git a/Documentation/driver-api/media/v4l2-cci.rst b/Documentation/driver-api/media/v4l2-cci.rst
>> new file mode 100644
>> index 000000000000..dd297a40ed20
>> --- /dev/null
>> +++ b/Documentation/driver-api/media/v4l2-cci.rst
>> @@ -0,0 +1,5 @@
>> +.. SPDX-License-Identifier: GPL-2.0
>> +
>> +V4L2 CCI kAPI
>> +^^^^^^^^^^^^^
>> +.. kernel-doc:: include/media/v4l2-cci.h
>> diff --git a/Documentation/driver-api/media/v4l2-core.rst b/Documentation/driver-api/media/v4l2-core.rst
>> index 1a8c4a5f256b..239045ecc8f4 100644
>> --- a/Documentation/driver-api/media/v4l2-core.rst
>> +++ b/Documentation/driver-api/media/v4l2-core.rst
>> @@ -22,6 +22,7 @@ Video4Linux devices
>> v4l2-mem2mem
>> v4l2-async
>> v4l2-fwnode
>> + v4l2-cci
>> v4l2-rect
>> v4l2-tuner
>> v4l2-common
>> diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
>> index 348559bc2468..523ba243261d 100644
>> --- a/drivers/media/v4l2-core/Kconfig
>> +++ b/drivers/media/v4l2-core/Kconfig
>> @@ -74,6 +74,11 @@ config V4L2_FWNODE
>> config V4L2_ASYNC
>> tristate
>>
>> +config V4L2_CCI
>> + tristate
>> + depends on I2C
>> + select REGMAP_I2C
>> +
>> # Used by drivers that need Videobuf modules
>> config VIDEOBUF_GEN
>> tristate
>> diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
>> index 41d91bd10cf2..be2551705755 100644
>> --- a/drivers/media/v4l2-core/Makefile
>> +++ b/drivers/media/v4l2-core/Makefile
>> @@ -25,6 +25,7 @@ videodev-$(CONFIG_VIDEO_V4L2_I2C) += v4l2-i2c.o
>> # (e. g. LC_ALL=C sort Makefile)
>>
>> obj-$(CONFIG_V4L2_ASYNC) += v4l2-async.o
>> +obj-$(CONFIG_V4L2_CCI) += v4l2-cci.o
>> obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash-led-class.o
>> obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o
>> obj-$(CONFIG_V4L2_H264) += v4l2-h264.o
>> diff --git a/drivers/media/v4l2-core/v4l2-cci.c b/drivers/media/v4l2-core/v4l2-cci.c
>> new file mode 100644
>> index 000000000000..21207d137dbe
>> --- /dev/null
>> +++ b/drivers/media/v4l2-core/v4l2-cci.c
>> @@ -0,0 +1,142 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * MIPI Camera Control Interface (CCI) register access helpers.
>> + *
>> + * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
>> + */
>> +
>> +#include <linux/delay.h>
>> +#include <linux/dev_printk.h>
>> +#include <linux/module.h>
>> +#include <linux/regmap.h>
>> +
>> +#include <media/v4l2-cci.h>
>> +
>> +int cci_read(struct regmap *map, u32 reg, u32 *val, int *err)
>> +{
>> + int i, len, ret;
>
> Could i and len be unsigned?
Andy suggested replacing the for-loop below with:
switch (len)
case 1:
*val = buf[0];
break;
case 2:
*val = get_unaligned_be16(buf);
break;
case 3:
*val = __get_unaligned_be24(buf);
break;
case 4:
*val = get_unaligned_be32(buf);
break;
}
Then i goes away. What do you think about doing it like
this instead ?
>
>> + u8 buf[4];
>> +
>> + if (err && *err)
>> + return *err;
>> +
>> + /* Set len to register width in bytes */
>> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
>> + reg &= CCI_REG_ADDR_MASK;
>> +
>> + ret = regmap_bulk_read(map, reg, buf, len);
>> + if (ret) {
>> + dev_err(regmap_get_device(map), "Error reading reg 0x%4x: %d\n", reg, ret);
>> + if (err)
>> + *err = ret;
>> +
>> + return ret;
>> + }
>> +
>> + *val = 0;
>> + for (i = 0; i < len; i++) {
>> + *val <<= 8;
>> + *val |= buf[i];
>> + }
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(cci_read);
>> +
>> +int cci_write(struct regmap *map, u32 reg, u32 val, int *err)
>> +{
>> + int i, len, ret;
>
> Same here.
>
>> + u8 buf[4];
>> +
>> + if (err && *err)
>> + return *err;
>> +
>> + /* Set len to register width in bytes */
>> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
>> + reg &= CCI_REG_ADDR_MASK;
>> +
>> + for (i = 0; i < len; i++) {
>> + buf[len - i - 1] = val & 0xff;
>> + val >>= 8;
>> + }
>> +
>> + ret = regmap_bulk_write(map, reg, buf, len);
>> + if (ret) {
>> + dev_err(regmap_get_device(map), "Error writing reg 0x%4x: %d\n", reg, ret);
>> + if (err)
>> + *err = ret;
>> + }
>> +
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(cci_write);
>> +
>> +int cci_update_bits(struct regmap *map, u32 reg, u32 mask, u32 val, int *err)
>> +{
>> + int width, ret;
>> + u32 readval;
>> +
>> + if (err && *err)
>> + return *err;
>> +
>> + /*
>> + * For single byte updates use regmap_update_bits(), this uses
>> + * the regmap-lock to protect against other read-modify-writes racing.
>> + */
>> + width = (reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT;
>> + if (width == cci_reg_8) {
>> + reg &= CCI_REG_ADDR_MASK;
>> + ret = regmap_update_bits(map, reg, mask, val);
>> + if (ret) {
>> + dev_err(regmap_get_device(map), "Error updating reg 0x%4x: %d\n", reg, ret);
>> + if (err)
>> + *err = ret;
>> + }
>> +
>> + return ret;
>> + }
>> +
>> + ret = cci_read(map, reg, &readval, err);
>> + if (ret)
>> + return ret;
>> +
>> + val = (readval & ~mask) | (val & mask);
>> +
>> + return cci_write(map, reg, val, err);
>> +}
>> +EXPORT_SYMBOL_GPL(cci_update_bits);
>> +
>> +int cci_multi_reg_write(struct regmap *map, const struct reg_sequence *regs, int num_regs, int *err)
>> +{
>> + int i, ret;
>> +
>> + if (err && *err)
>> + return *err;
>> +
>> + for (i = 0; i < num_regs; i++) {
>> + ret = cci_write(map, regs[i].reg, regs[i].def, err);
>> + if (ret)
>> + return ret;
>> +
>> + if (regs[i].delay_us)
>> + fsleep(regs[i].delay_us);
>> + }
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(cci_multi_reg_write);
>> +
>> +struct regmap *cci_regmap_init_i2c(struct i2c_client *client, int reg_addr_bits)
>> +{
>> + struct regmap_config config = {
>> + .reg_bits = reg_addr_bits,
>> + .val_bits = 8,
>> + .reg_format_endian = REGMAP_ENDIAN_BIG,
>> + };
>> +
>> + return devm_regmap_init_i2c(client, &config);
>> +}
>> +EXPORT_SYMBOL_GPL(cci_regmap_init_i2c);
>
> Bulk write functions would be nice, too: CCI does not limit access to
> register-like targets.
For bulk writing encoding the register width into the address
makes no sense, so we would need to specify in the documentation
that only raw register addresses are accepted and that the write
is always done in bytes.
At which point we are basically adding a 1:1 wrapper around
regmap_bulk_write(). So I think it would be better for sensor
drivers which need this to just use regmap_bulk_write()
directly.
Regards,
Hans
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 2/3] media: atomisp: ov2680: Convert to new CCI register access helpers
2023-06-06 20:53 ` Andy Shevchenko
@ 2023-06-07 8:53 ` Hans de Goede
2023-06-07 15:51 ` Laurent Pinchart
0 siblings, 1 reply; 38+ messages in thread
From: Hans de Goede @ 2023-06-07 8:53 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Laurent Pinchart, Sakari Ailus, Mauro Carvalho Chehab,
Andy Shevchenko, linux-media
Hi,
On 6/6/23 22:53, Andy Shevchenko wrote:
> On Tue, Jun 6, 2023 at 7:58 PM Hans de Goede <hdegoede@redhat.com> wrote:
>>
>> Use the new comon CCI register access helpers to replace the private
>> register access helpers in the ov2680 driver.
>>
>> While at it also switch to using the same register address defines
>> as the standard drivers/media/i2c/ov2680.c driver to make merging
>> the 2 drivers simpler.
>
> ...
>
>> + cci_write(sensor->regmap, OV2680_REG_SENSOR_CTRL_0A, sensor_ctrl_0a, &ret);
>> + cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_START, sensor->mode.h_start, &ret);
>> + cci_write(sensor->regmap, OV2680_REG_VERTICAL_START, sensor->mode.v_start, &ret);
>> + cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_END, sensor->mode.h_end, &ret);
>> + cci_write(sensor->regmap, OV2680_REG_VERTICAL_END, sensor->mode.v_end, &ret);
>> + cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_OUTPUT_SIZE,
>> + sensor->mode.h_output_size, &ret);
>> + cci_write(sensor->regmap, OV2680_REG_VERTICAL_OUTPUT_SIZE,
>> + sensor->mode.v_output_size, &ret);
>> + cci_write(sensor->regmap, OV2680_REG_TIMING_HTS, sensor->mode.hts, &ret);
>> + cci_write(sensor->regmap, OV2680_REG_TIMING_VTS, sensor->mode.vts, &ret);
>> + cci_write(sensor->regmap, OV2680_REG_ISP_X_WIN, 0, &ret);
>> + cci_write(sensor->regmap, OV2680_REG_ISP_Y_WIN, 0, &ret);
>> + cci_write(sensor->regmap, OV2680_REG_X_INC, inc, &ret);
>> + cci_write(sensor->regmap, OV2680_REG_Y_INC, inc, &ret);
>> + cci_write(sensor->regmap, OV2680_REG_X_WIN, sensor->mode.h_output_size, &ret);
>> + cci_write(sensor->regmap, OV2680_REG_Y_WIN, sensor->mode.v_output_size, &ret);
>> + cci_write(sensor->regmap, OV2680_REG_FORMAT1, fmt1, &ret);
>> + cci_write(sensor->regmap, OV2680_REG_FORMAT2, fmt2, &ret);
>
> I know that &ret thingy was discussed before and Laurent is keen to
> have this, but has anybody actually tested how bad or not at all the
> code generation becomes?
The cci_write function is in another module, so it won't be inlined and as such I don't see how the code generation can become bad. We loose all the if (ret) return ret; checks here, so the code should become smaller.
Or are you worried about having to pass the 1 extra parameter ?
>
> ...
>
>> + struct device *dev;
>> + struct regmap *regmap;
>
> Isn't the same device associated with regmap? If so, one of them
> probably duplicates the other.
You are right, but the entire atomisp-ov2680.c file is going away real soon now. I plan to post a series to get drivers/media/i2c/ov2680.c ready to replace it later today.
So I'm not even sure if this patch should be merged, as I mentioned in the cover letter this one is mostly here to illustrate use of the new helpers.
I also wrote this patch to make porting recent atomisp-ov2680.c changes over to drivers/media/i2c/ov2680.c easier. Part of the series to get drivers/media/i2c/ov2680.c into shape is converting it to the new CCI helpers so that I could then easily copy over bits from the also converted atomisp-ov2680.c.
So it might be interesting to still merge this so that the latest state of atomisp-ov2680.c is easier to compare to drivers/media/i2c/ov2680.c if the need arises.
Regards,
Hans
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-07 8:46 ` Hans de Goede
@ 2023-06-07 11:41 ` Sakari Ailus
0 siblings, 0 replies; 38+ messages in thread
From: Sakari Ailus @ 2023-06-07 11:41 UTC (permalink / raw)
To: Hans de Goede
Cc: Laurent Pinchart, Mauro Carvalho Chehab, Andy Shevchenko,
linux-media
Hi Hans,
On Wed, Jun 07, 2023 at 10:46:58AM +0200, Hans de Goede wrote:
> Hi Sakari,
>
> On 6/7/23 09:50, Sakari Ailus wrote:
> > Hi Hans,
> >
> > Thank you for the patchset.
> >
> > On Tue, Jun 06, 2023 at 06:58:06PM +0200, Hans de Goede wrote:
> >> The CSI2 specification specifies a standard method to access camera sensor
> >> registers called "Camera Control Interface (CCI)".
> >>
> >> This uses either 8 or 16 bit (big-endian wire order) register addresses
> >> and supports 8, 16, 24 or 32 bit (big-endian wire order) register widths.
> >>
> >> Currently a lot of Linux camera sensor drivers all have their own custom
> >> helpers for this, often copy and pasted from other drivers.
> >>
> >> Add a set of generic helpers for this so that all sensor drivers can
> >> switch to a single common implementation.
> >>
> >> These helpers take an extra optional "int *err" function parameter,
> >> this can be used to chain a bunch of register accesses together with
> >> only a single error check at the end, rather then needing to error
> >> check each individual register access. The first failing call will
> >> set the contents of err to a non 0 value and all other calls will
> >> then become no-ops.
> >>
> >> Link: https://lore.kernel.org/linux-media/59aefa7f-7bf9-6736-6040-39551329cd0a@redhat.com/
> >> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> >> ---
> >> Documentation/driver-api/media/v4l2-cci.rst | 5 +
> >> Documentation/driver-api/media/v4l2-core.rst | 1 +
> >> drivers/media/v4l2-core/Kconfig | 5 +
> >> drivers/media/v4l2-core/Makefile | 1 +
> >> drivers/media/v4l2-core/v4l2-cci.c | 142 +++++++++++++++++++
> >> include/media/v4l2-cci.h | 109 ++++++++++++++
> >> 6 files changed, 263 insertions(+)
> >> create mode 100644 Documentation/driver-api/media/v4l2-cci.rst
> >> create mode 100644 drivers/media/v4l2-core/v4l2-cci.c
> >> create mode 100644 include/media/v4l2-cci.h
> >>
> >> diff --git a/Documentation/driver-api/media/v4l2-cci.rst b/Documentation/driver-api/media/v4l2-cci.rst
> >> new file mode 100644
> >> index 000000000000..dd297a40ed20
> >> --- /dev/null
> >> +++ b/Documentation/driver-api/media/v4l2-cci.rst
> >> @@ -0,0 +1,5 @@
> >> +.. SPDX-License-Identifier: GPL-2.0
> >> +
> >> +V4L2 CCI kAPI
> >> +^^^^^^^^^^^^^
> >> +.. kernel-doc:: include/media/v4l2-cci.h
> >> diff --git a/Documentation/driver-api/media/v4l2-core.rst b/Documentation/driver-api/media/v4l2-core.rst
> >> index 1a8c4a5f256b..239045ecc8f4 100644
> >> --- a/Documentation/driver-api/media/v4l2-core.rst
> >> +++ b/Documentation/driver-api/media/v4l2-core.rst
> >> @@ -22,6 +22,7 @@ Video4Linux devices
> >> v4l2-mem2mem
> >> v4l2-async
> >> v4l2-fwnode
> >> + v4l2-cci
> >> v4l2-rect
> >> v4l2-tuner
> >> v4l2-common
> >> diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
> >> index 348559bc2468..523ba243261d 100644
> >> --- a/drivers/media/v4l2-core/Kconfig
> >> +++ b/drivers/media/v4l2-core/Kconfig
> >> @@ -74,6 +74,11 @@ config V4L2_FWNODE
> >> config V4L2_ASYNC
> >> tristate
> >>
> >> +config V4L2_CCI
> >> + tristate
> >> + depends on I2C
> >> + select REGMAP_I2C
> >> +
> >> # Used by drivers that need Videobuf modules
> >> config VIDEOBUF_GEN
> >> tristate
> >> diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
> >> index 41d91bd10cf2..be2551705755 100644
> >> --- a/drivers/media/v4l2-core/Makefile
> >> +++ b/drivers/media/v4l2-core/Makefile
> >> @@ -25,6 +25,7 @@ videodev-$(CONFIG_VIDEO_V4L2_I2C) += v4l2-i2c.o
> >> # (e. g. LC_ALL=C sort Makefile)
> >>
> >> obj-$(CONFIG_V4L2_ASYNC) += v4l2-async.o
> >> +obj-$(CONFIG_V4L2_CCI) += v4l2-cci.o
> >> obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash-led-class.o
> >> obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o
> >> obj-$(CONFIG_V4L2_H264) += v4l2-h264.o
> >> diff --git a/drivers/media/v4l2-core/v4l2-cci.c b/drivers/media/v4l2-core/v4l2-cci.c
> >> new file mode 100644
> >> index 000000000000..21207d137dbe
> >> --- /dev/null
> >> +++ b/drivers/media/v4l2-core/v4l2-cci.c
> >> @@ -0,0 +1,142 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * MIPI Camera Control Interface (CCI) register access helpers.
> >> + *
> >> + * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
> >> + */
> >> +
> >> +#include <linux/delay.h>
> >> +#include <linux/dev_printk.h>
> >> +#include <linux/module.h>
> >> +#include <linux/regmap.h>
> >> +
> >> +#include <media/v4l2-cci.h>
> >> +
> >> +int cci_read(struct regmap *map, u32 reg, u32 *val, int *err)
> >> +{
> >> + int i, len, ret;
> >
> > Could i and len be unsigned?
>
> Andy suggested replacing the for-loop below with:
>
> switch (len)
> case 1:
> *val = buf[0];
> break;
> case 2:
> *val = get_unaligned_be16(buf);
> break;
> case 3:
> *val = __get_unaligned_be24(buf);
> break;
> case 4:
> *val = get_unaligned_be32(buf);
> break;
> }
>
> Then i goes away. What do you think about doing it like
> this instead ?
I'll reply to discussion there.
>
> >
> >> + u8 buf[4];
> >> +
> >> + if (err && *err)
> >> + return *err;
> >> +
> >> + /* Set len to register width in bytes */
> >> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
> >> + reg &= CCI_REG_ADDR_MASK;
> >> +
> >> + ret = regmap_bulk_read(map, reg, buf, len);
> >> + if (ret) {
> >> + dev_err(regmap_get_device(map), "Error reading reg 0x%4x: %d\n", reg, ret);
> >> + if (err)
> >> + *err = ret;
> >> +
> >> + return ret;
> >> + }
> >> +
> >> + *val = 0;
> >> + for (i = 0; i < len; i++) {
> >> + *val <<= 8;
> >> + *val |= buf[i];
> >> + }
> >> +
> >> + return 0;
> >> +}
> >> +EXPORT_SYMBOL_GPL(cci_read);
> >> +
> >> +int cci_write(struct regmap *map, u32 reg, u32 val, int *err)
> >> +{
> >> + int i, len, ret;
> >
> > Same here.
> >
> >> + u8 buf[4];
> >> +
> >> + if (err && *err)
> >> + return *err;
> >> +
> >> + /* Set len to register width in bytes */
> >> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
> >> + reg &= CCI_REG_ADDR_MASK;
> >> +
> >> + for (i = 0; i < len; i++) {
> >> + buf[len - i - 1] = val & 0xff;
> >> + val >>= 8;
> >> + }
> >> +
> >> + ret = regmap_bulk_write(map, reg, buf, len);
> >> + if (ret) {
> >> + dev_err(regmap_get_device(map), "Error writing reg 0x%4x: %d\n", reg, ret);
> >> + if (err)
> >> + *err = ret;
> >> + }
> >> +
> >> + return ret;
> >> +}
> >> +EXPORT_SYMBOL_GPL(cci_write);
> >> +
> >> +int cci_update_bits(struct regmap *map, u32 reg, u32 mask, u32 val, int *err)
> >> +{
> >> + int width, ret;
> >> + u32 readval;
> >> +
> >> + if (err && *err)
> >> + return *err;
> >> +
> >> + /*
> >> + * For single byte updates use regmap_update_bits(), this uses
> >> + * the regmap-lock to protect against other read-modify-writes racing.
> >> + */
> >> + width = (reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT;
> >> + if (width == cci_reg_8) {
> >> + reg &= CCI_REG_ADDR_MASK;
> >> + ret = regmap_update_bits(map, reg, mask, val);
> >> + if (ret) {
> >> + dev_err(regmap_get_device(map), "Error updating reg 0x%4x: %d\n", reg, ret);
> >> + if (err)
> >> + *err = ret;
> >> + }
> >> +
> >> + return ret;
> >> + }
> >> +
> >> + ret = cci_read(map, reg, &readval, err);
> >> + if (ret)
> >> + return ret;
> >> +
> >> + val = (readval & ~mask) | (val & mask);
> >> +
> >> + return cci_write(map, reg, val, err);
> >> +}
> >> +EXPORT_SYMBOL_GPL(cci_update_bits);
> >> +
> >> +int cci_multi_reg_write(struct regmap *map, const struct reg_sequence *regs, int num_regs, int *err)
> >> +{
> >> + int i, ret;
^
I should be unsigned here. As well as num_regs.
> >> +
> >> + if (err && *err)
> >> + return *err;
> >> +
> >> + for (i = 0; i < num_regs; i++) {
> >> + ret = cci_write(map, regs[i].reg, regs[i].def, err);
> >> + if (ret)
> >> + return ret;
> >> +
> >> + if (regs[i].delay_us)
> >> + fsleep(regs[i].delay_us);
> >> + }
> >> +
> >> + return 0;
> >> +}
> >> +EXPORT_SYMBOL_GPL(cci_multi_reg_write);
> >> +
> >> +struct regmap *cci_regmap_init_i2c(struct i2c_client *client, int reg_addr_bits)
> >> +{
> >> + struct regmap_config config = {
> >> + .reg_bits = reg_addr_bits,
> >> + .val_bits = 8,
> >> + .reg_format_endian = REGMAP_ENDIAN_BIG,
> >> + };
> >> +
> >> + return devm_regmap_init_i2c(client, &config);
> >> +}
> >> +EXPORT_SYMBOL_GPL(cci_regmap_init_i2c);
> >
> > Bulk write functions would be nice, too: CCI does not limit access to
> > register-like targets.
>
> For bulk writing encoding the register width into the address
> makes no sense, so we would need to specify in the documentation
> that only raw register addresses are accepted and that the write
> is always done in bytes.
>
> At which point we are basically adding a 1:1 wrapper around
> regmap_bulk_write(). So I think it would be better for sensor
> drivers which need this to just use regmap_bulk_write()
> directly.
Ah, good point. The first argument is indeed the regmap map.
--
Kind regards,
Sakari Ailus
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-07 8:40 ` Hans de Goede
@ 2023-06-07 12:01 ` Sakari Ailus
2023-06-07 14:55 ` Laurent Pinchart
2023-06-07 15:40 ` Andy Shevchenko
0 siblings, 2 replies; 38+ messages in thread
From: Sakari Ailus @ 2023-06-07 12:01 UTC (permalink / raw)
To: Hans de Goede
Cc: Andy Shevchenko, Laurent Pinchart, Mauro Carvalho Chehab,
Andy Shevchenko, linux-media
Hi Hans,
On Wed, Jun 07, 2023 at 10:40:34AM +0200, Hans de Goede wrote:
> Hi,
>
> On 6/6/23 22:43, Andy Shevchenko wrote:
> > On Tue, Jun 6, 2023 at 7:58 PM Hans de Goede <hdegoede@redhat.com> wrote:
> >>
> >> The CSI2 specification specifies a standard method to access camera sensor
> >> registers called "Camera Control Interface (CCI)".
> >>
> >> This uses either 8 or 16 bit (big-endian wire order) register addresses
> >> and supports 8, 16, 24 or 32 bit (big-endian wire order) register widths.
> >>
> >> Currently a lot of Linux camera sensor drivers all have their own custom
> >> helpers for this, often copy and pasted from other drivers.
> >>
> >> Add a set of generic helpers for this so that all sensor drivers can
> >> switch to a single common implementation.
> >>
> >> These helpers take an extra optional "int *err" function parameter,
> >> this can be used to chain a bunch of register accesses together with
> >> only a single error check at the end, rather then needing to error
> >> check each individual register access. The first failing call will
> >> set the contents of err to a non 0 value and all other calls will
> >> then become no-ops.
> >
> > ...
> >
> >> +#include <linux/delay.h>
> >> +#include <linux/dev_printk.h>
> >> +#include <linux/module.h>
> >> +#include <linux/regmap.h>
> >
> > + types.h
> >
> >> +#include <media/v4l2-cci.h>
> >
> >> +int cci_read(struct regmap *map, u32 reg, u32 *val, int *err)
> >> +{
> >> + int i, len, ret;
> >> + u8 buf[4];
> >> +
> >> + if (err && *err)
> >> + return *err;
> >> +
> >> + /* Set len to register width in bytes */
> >> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
> >> + reg &= CCI_REG_ADDR_MASK;
> >> +
> >> + ret = regmap_bulk_read(map, reg, buf, len);
> >> + if (ret) {
> >> + dev_err(regmap_get_device(map), "Error reading reg 0x%4x: %d\n", reg, ret);
> >> + if (err)
> >> + *err = ret;
> >> +
> >> + return ret;
> >> + }
> >> +
> >> + *val = 0;
> >> + for (i = 0; i < len; i++) {
> >> + *val <<= 8;
> >> + *val |= buf[i];
> >> + }
> >
> > I really prefer to see put_unaligned() here depending on the length.
> > Note, that on some CPUs it might be one assembly instruction or even
> > none, depending on how the result is going to be used.
>
> Ok, so you mean changing it to something like this:
>
> switch (len)
> case 1:
> *val = buf[0];
> break;
> case 2:
> *val = get_unaligned_be16(buf);
> break;
> case 3:
> *val = __get_unaligned_be24(buf);
> break;
> case 4:
> *val = get_unaligned_be32(buf);
> break;
> }
I think the loop looks nicer but I'm fine with this as well.
>
> ?
>
>
>
> >
> >> + return 0;
> >> +}
> >> +EXPORT_SYMBOL_GPL(cci_read);
> >
> > Can we have it namespaced?
>
> I'm not sure if having just these 5 symbols in their own namespace is worth it. SO far the media subsystem is not using module/symbol namespacing at all.
>
> Sakari, Laurent, any opinions on this ?
Regmap nor V4L2 use it so I wouldn't use it here either.
>
>
>
> >> +int cci_write(struct regmap *map, u32 reg, u32 val, int *err)
> >> +{
> >> + int i, len, ret;
> >> + u8 buf[4];
> >> +
> >> + if (err && *err)
> >> + return *err;
> >> +
> >> + /* Set len to register width in bytes */
> >> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
> >> + reg &= CCI_REG_ADDR_MASK;
> >> +
> >> + for (i = 0; i < len; i++) {
> >> + buf[len - i - 1] = val & 0xff;
> >> + val >>= 8;
> >> + }
> >> +
> >> + ret = regmap_bulk_write(map, reg, buf, len);
> >> + if (ret) {
> >> + dev_err(regmap_get_device(map), "Error writing reg 0x%4x: %d\n", reg, ret);
> >> + if (err)
> >> + *err = ret;
> >> + }
> >> +
> >> + return ret;
> >> +}
> >> +EXPORT_SYMBOL_GPL(cci_write);
> >
> > Same comments as per above function.
> >
> > ...
> >
> >> + if (regs[i].delay_us)
> >
> > I'm wondering why fsleep() doesn't have this check? Or does it?
> >
> >> + fsleep(regs[i].delay_us);
> >
> > ...
> >
> >> +struct regmap *cci_regmap_init_i2c(struct i2c_client *client, int reg_addr_bits)
> >> +{
> >> + struct regmap_config config = {
> >> + .reg_bits = reg_addr_bits,
> >> + .val_bits = 8,
> >> + .reg_format_endian = REGMAP_ENDIAN_BIG,
> >
> > Is the lock required?
> > If so, how is it helpful?
>
> Interesting questions sensor drivers typically already do
> their own locking.
>
> So I guess we could indeed tell regmap to skip locking here.
>
> Sakari, Laurent any opinion on this ?
There are loops here so it won't be atomic in any case.
Generally drivers indeed already take care of this. I don't think we need
locking on this level.
>
> > Can we move this outside as static const?
>
> No, because reg_bits is not const.
>
>
>
> >> + };
> >> +
> >> + return devm_regmap_init_i2c(client, &config);
> >> +}
> >
> > ...
> >
> >> +#ifndef _V4L2_CCI_H
> >> +#define _V4L2_CCI_H
> >
> > + bits.h
> >
> >> +#include <linux/regmap.h>
> >
> > Not used, rather requires forward declarations of
> >
> > struct regmap
> > struct reg_sequence
>
> Ack, I'll change this for the next version.
>
> > Also note missing i2c_client forward declaration.
>
> That was also taken care of by regmap.h.
>
> >
> >> +#include <linux/types.h>
> >> +
> >> +/*
> >> + * Note cci_reg_8 deliberately is 0, not 1, so that raw
> >> + * (not wrapped in a CCI_REG*() macro) register addresses
> >> + * do 8 bit wide accesses. This allows unchanged use of register
> >> + * initialization lists of raw address, value pairs which only
> >> + * do 8 bit width accesses. Which makes porting drivers easier.
> >> + */
> >> +enum cci_reg_type {
> >> + cci_reg_8 = 0,
> >
> > But this is guaranteed by the C standard... See also below.
> >
> >> + cci_reg_16,
> >
> > But this one becomes 1, so the above comment doesn't clarify why it's
> > okay to have it 1 and not 2.
>
> Basically the idea is that the enum value is the reg-width in bytes - 1
> where the - 1 is there so that cci_reg_8 = 0 .
I'm fine with the comment.
--
Kind regards,
Sakari Ailus
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-07 12:01 ` Sakari Ailus
@ 2023-06-07 14:55 ` Laurent Pinchart
2023-06-07 15:40 ` Andy Shevchenko
1 sibling, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2023-06-07 14:55 UTC (permalink / raw)
To: Sakari Ailus
Cc: Hans de Goede, Andy Shevchenko, Mauro Carvalho Chehab,
Andy Shevchenko, linux-media
Hello,
On Wed, Jun 07, 2023 at 12:01:10PM +0000, Sakari Ailus wrote:
> On Wed, Jun 07, 2023 at 10:40:34AM +0200, Hans de Goede wrote:
> > On 6/6/23 22:43, Andy Shevchenko wrote:
> > > On Tue, Jun 6, 2023 at 7:58 PM Hans de Goede wrote:
> > >>
> > >> The CSI2 specification specifies a standard method to access camera sensor
> > >> registers called "Camera Control Interface (CCI)".
> > >>
> > >> This uses either 8 or 16 bit (big-endian wire order) register addresses
> > >> and supports 8, 16, 24 or 32 bit (big-endian wire order) register widths.
> > >>
> > >> Currently a lot of Linux camera sensor drivers all have their own custom
> > >> helpers for this, often copy and pasted from other drivers.
> > >>
> > >> Add a set of generic helpers for this so that all sensor drivers can
> > >> switch to a single common implementation.
> > >>
> > >> These helpers take an extra optional "int *err" function parameter,
> > >> this can be used to chain a bunch of register accesses together with
> > >> only a single error check at the end, rather then needing to error
> > >> check each individual register access. The first failing call will
> > >> set the contents of err to a non 0 value and all other calls will
> > >> then become no-ops.
> > >
> > > ...
> > >
> > >> +#include <linux/delay.h>
> > >> +#include <linux/dev_printk.h>
> > >> +#include <linux/module.h>
> > >> +#include <linux/regmap.h>
> > >
> > > + types.h
> > >
> > >> +#include <media/v4l2-cci.h>
> > >
> > >> +int cci_read(struct regmap *map, u32 reg, u32 *val, int *err)
> > >> +{
> > >> + int i, len, ret;
> > >> + u8 buf[4];
> > >> +
> > >> + if (err && *err)
> > >> + return *err;
> > >> +
> > >> + /* Set len to register width in bytes */
> > >> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
> > >> + reg &= CCI_REG_ADDR_MASK;
> > >> +
> > >> + ret = regmap_bulk_read(map, reg, buf, len);
> > >> + if (ret) {
> > >> + dev_err(regmap_get_device(map), "Error reading reg 0x%4x: %d\n", reg, ret);
> > >> + if (err)
> > >> + *err = ret;
> > >> +
> > >> + return ret;
> > >> + }
> > >> +
> > >> + *val = 0;
> > >> + for (i = 0; i < len; i++) {
> > >> + *val <<= 8;
> > >> + *val |= buf[i];
> > >> + }
> > >
> > > I really prefer to see put_unaligned() here depending on the length.
> > > Note, that on some CPUs it might be one assembly instruction or even
> > > none, depending on how the result is going to be used.
> >
> > Ok, so you mean changing it to something like this:
> >
> > switch (len)
> > case 1:
> > *val = buf[0];
> > break;
> > case 2:
> > *val = get_unaligned_be16(buf);
> > break;
> > case 3:
> > *val = __get_unaligned_be24(buf);
> > break;
> > case 4:
> > *val = get_unaligned_be32(buf);
> > break;
> > }
>
> I think the loop looks nicer but I'm fine with this as well.
>
> > ?
> >
> > >> + return 0;
> > >> +}
> > >> +EXPORT_SYMBOL_GPL(cci_read);
> > >
> > > Can we have it namespaced?
> >
> > I'm not sure if having just these 5 symbols in their own namespace
> > is worth it. SO far the media subsystem is not using module/symbol
> > namespacing at all.
> >
> > Sakari, Laurent, any opinions on this ?
>
> Regmap nor V4L2 use it so I wouldn't use it here either.
Ditto.
> > >> +int cci_write(struct regmap *map, u32 reg, u32 val, int *err)
> > >> +{
> > >> + int i, len, ret;
> > >> + u8 buf[4];
> > >> +
> > >> + if (err && *err)
> > >> + return *err;
> > >> +
> > >> + /* Set len to register width in bytes */
> > >> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
> > >> + reg &= CCI_REG_ADDR_MASK;
> > >> +
> > >> + for (i = 0; i < len; i++) {
> > >> + buf[len - i - 1] = val & 0xff;
> > >> + val >>= 8;
> > >> + }
> > >> +
> > >> + ret = regmap_bulk_write(map, reg, buf, len);
> > >> + if (ret) {
> > >> + dev_err(regmap_get_device(map), "Error writing reg 0x%4x: %d\n", reg, ret);
> > >> + if (err)
> > >> + *err = ret;
> > >> + }
> > >> +
> > >> + return ret;
> > >> +}
> > >> +EXPORT_SYMBOL_GPL(cci_write);
> > >
> > > Same comments as per above function.
> > >
> > > ...
> > >
> > >> + if (regs[i].delay_us)
> > >
> > > I'm wondering why fsleep() doesn't have this check? Or does it?
> > >
> > >> + fsleep(regs[i].delay_us);
> > >
> > > ...
> > >
> > >> +struct regmap *cci_regmap_init_i2c(struct i2c_client *client, int reg_addr_bits)
> > >> +{
> > >> + struct regmap_config config = {
> > >> + .reg_bits = reg_addr_bits,
> > >> + .val_bits = 8,
> > >> + .reg_format_endian = REGMAP_ENDIAN_BIG,
> > >
> > > Is the lock required?
> > > If so, how is it helpful?
> >
> > Interesting questions sensor drivers typically already do
> > their own locking.
> >
> > So I guess we could indeed tell regmap to skip locking here.
> >
> > Sakari, Laurent any opinion on this ?
>
> There are loops here so it won't be atomic in any case.
>
> Generally drivers indeed already take care of this. I don't think we need
> locking on this level.
Agreed.
> > > Can we move this outside as static const?
> >
> > No, because reg_bits is not const.
> >
> > >> + };
> > >> +
> > >> + return devm_regmap_init_i2c(client, &config);
> > >> +}
> > >
> > > ...
> > >
> > >> +#ifndef _V4L2_CCI_H
> > >> +#define _V4L2_CCI_H
> > >
> > > + bits.h
> > >
> > >> +#include <linux/regmap.h>
> > >
> > > Not used, rather requires forward declarations of
> > >
> > > struct regmap
> > > struct reg_sequence
> >
> > Ack, I'll change this for the next version.
> >
> > > Also note missing i2c_client forward declaration.
> >
> > That was also taken care of by regmap.h.
> >
> > >> +#include <linux/types.h>
> > >> +
> > >> +/*
> > >> + * Note cci_reg_8 deliberately is 0, not 1, so that raw
> > >> + * (not wrapped in a CCI_REG*() macro) register addresses
> > >> + * do 8 bit wide accesses. This allows unchanged use of register
> > >> + * initialization lists of raw address, value pairs which only
> > >> + * do 8 bit width accesses. Which makes porting drivers easier.
> > >> + */
> > >> +enum cci_reg_type {
> > >> + cci_reg_8 = 0,
> > >
> > > But this is guaranteed by the C standard... See also below.
> > >
> > >> + cci_reg_16,
> > >
> > > But this one becomes 1, so the above comment doesn't clarify why it's
> > > okay to have it 1 and not 2.
> >
> > Basically the idea is that the enum value is the reg-width in bytes - 1
> > where the - 1 is there so that cci_reg_8 = 0 .
>
> I'm fine with the comment.
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-07 12:01 ` Sakari Ailus
2023-06-07 14:55 ` Laurent Pinchart
@ 2023-06-07 15:40 ` Andy Shevchenko
2023-06-07 15:58 ` Hans de Goede
1 sibling, 1 reply; 38+ messages in thread
From: Andy Shevchenko @ 2023-06-07 15:40 UTC (permalink / raw)
To: Sakari Ailus
Cc: Hans de Goede, Laurent Pinchart, Mauro Carvalho Chehab,
Andy Shevchenko, linux-media
On Wed, Jun 7, 2023 at 3:01 PM Sakari Ailus
<sakari.ailus@linux.intel.com> wrote:
> On Wed, Jun 07, 2023 at 10:40:34AM +0200, Hans de Goede wrote:
> > On 6/6/23 22:43, Andy Shevchenko wrote:
> > > On Tue, Jun 6, 2023 at 7:58 PM Hans de Goede <hdegoede@redhat.com> wrote:
...
> > >> +int cci_read(struct regmap *map, u32 reg, u32 *val, int *err)
> > >> +{
> > >> + int i, len, ret;
> > >> + u8 buf[4];
> > >> + *val = 0;
> > >> + for (i = 0; i < len; i++) {
> > >> + *val <<= 8;
> > >> + *val |= buf[i];
> > >> + }
> > >
> > > I really prefer to see put_unaligned() here depending on the length.
> > > Note, that on some CPUs it might be one assembly instruction or even
> > > none, depending on how the result is going to be used.
> >
> > Ok, so you mean changing it to something like this:
> >
> > switch (len)
> > case 1:
> > *val = buf[0];
> > break;
> > case 2:
> > *val = get_unaligned_be16(buf);
> > break;
> > case 3:
> > *val = __get_unaligned_be24(buf);
__without double underscore prefix
> > break;
> > case 4:
> > *val = get_unaligned_be32(buf);
> > break;
> > }
>
> I think the loop looks nicer but I'm fine with this as well.
>
> > ?
But the loop hides what's going on there. And I believe code
generation would be worse with a loop.
Also note, that in case of switch-case we don't write to the pointed
memory several times, which I think is also the win.
> > >> + return 0;
> > >> +}
...
> > >> +int cci_write(struct regmap *map, u32 reg, u32 val, int *err)
> > >> +{
> > >> + int i, len, ret;
> > >> + u8 buf[4];
> > >> +
> > >> + if (err && *err)
> > >> + return *err;
> > >> +
> > >> + /* Set len to register width in bytes */
> > >> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
> > >> + reg &= CCI_REG_ADDR_MASK;
> > >> +
> > >> + for (i = 0; i < len; i++) {
> > >> + buf[len - i - 1] = val & 0xff;
> > >> + val >>= 8;
> > >> + }
Similar way here.
> > >> +
> > >> + ret = regmap_bulk_write(map, reg, buf, len);
> > >> + if (ret) {
> > >> + dev_err(regmap_get_device(map), "Error writing reg 0x%4x: %d\n", reg, ret);
> > >> + if (err)
> > >> + *err = ret;
> > >> + }
> > >> +
> > >> + return ret;
> > >> +}
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 2/3] media: atomisp: ov2680: Convert to new CCI register access helpers
2023-06-07 8:53 ` Hans de Goede
@ 2023-06-07 15:51 ` Laurent Pinchart
2023-06-07 15:59 ` Hans de Goede
0 siblings, 1 reply; 38+ messages in thread
From: Laurent Pinchart @ 2023-06-07 15:51 UTC (permalink / raw)
To: Hans de Goede
Cc: Andy Shevchenko, Sakari Ailus, Mauro Carvalho Chehab,
Andy Shevchenko, linux-media
Hi Hans,
On Wed, Jun 07, 2023 at 10:53:54AM +0200, Hans de Goede wrote:
> On 6/6/23 22:53, Andy Shevchenko wrote:
> > On Tue, Jun 6, 2023 at 7:58 PM Hans de Goede wrote:
> >>
> >> Use the new comon CCI register access helpers to replace the private
> >> register access helpers in the ov2680 driver.
> >>
> >> While at it also switch to using the same register address defines
> >> as the standard drivers/media/i2c/ov2680.c driver to make merging
> >> the 2 drivers simpler.
> >
> > ...
> >
> >> + cci_write(sensor->regmap, OV2680_REG_SENSOR_CTRL_0A, sensor_ctrl_0a, &ret);
> >> + cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_START, sensor->mode.h_start, &ret);
> >> + cci_write(sensor->regmap, OV2680_REG_VERTICAL_START, sensor->mode.v_start, &ret);
> >> + cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_END, sensor->mode.h_end, &ret);
> >> + cci_write(sensor->regmap, OV2680_REG_VERTICAL_END, sensor->mode.v_end, &ret);
> >> + cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_OUTPUT_SIZE,
> >> + sensor->mode.h_output_size, &ret);
> >> + cci_write(sensor->regmap, OV2680_REG_VERTICAL_OUTPUT_SIZE,
> >> + sensor->mode.v_output_size, &ret);
> >> + cci_write(sensor->regmap, OV2680_REG_TIMING_HTS, sensor->mode.hts, &ret);
> >> + cci_write(sensor->regmap, OV2680_REG_TIMING_VTS, sensor->mode.vts, &ret);
> >> + cci_write(sensor->regmap, OV2680_REG_ISP_X_WIN, 0, &ret);
> >> + cci_write(sensor->regmap, OV2680_REG_ISP_Y_WIN, 0, &ret);
> >> + cci_write(sensor->regmap, OV2680_REG_X_INC, inc, &ret);
> >> + cci_write(sensor->regmap, OV2680_REG_Y_INC, inc, &ret);
> >> + cci_write(sensor->regmap, OV2680_REG_X_WIN, sensor->mode.h_output_size, &ret);
> >> + cci_write(sensor->regmap, OV2680_REG_Y_WIN, sensor->mode.v_output_size, &ret);
> >> + cci_write(sensor->regmap, OV2680_REG_FORMAT1, fmt1, &ret);
> >> + cci_write(sensor->regmap, OV2680_REG_FORMAT2, fmt2, &ret);
> >
> > I know that &ret thingy was discussed before and Laurent is keen to
> > have this, but has anybody actually tested how bad or not at all the
> > code generation becomes?
>
> The cci_write function is in another module, so it won't be inlined
> and as such I don't see how the code generation can become bad. We
> loose all the if (ret) return ret; checks here, so the code should
> become smaller.
>
> Or are you worried about having to pass the 1 extra parameter ?
>
> > ...
> >
> >> + struct device *dev;
> >> + struct regmap *regmap;
> >
> > Isn't the same device associated with regmap? If so, one of them
> > probably duplicates the other.
>
> You are right, but the entire atomisp-ov2680.c file is going away real
> soon now. I plan to post a series to get drivers/media/i2c/ov2680.c
> ready to replace it later today.
>
> So I'm not even sure if this patch should be merged, as I mentioned in
> the cover letter this one is mostly here to illustrate use of the new
> helpers.
How about porting drivers/media/i2c/imx290.c ? That's a real-life
example that can be merged, which is good to serve as an example
showcasing the API usage in mainline. It will also help ensuring that
these helpers are a good fit for drivers that already encode the
register width in the macros.
> I also wrote this patch to make porting recent atomisp-ov2680.c
> changes over to drivers/media/i2c/ov2680.c easier. Part of the series
> to get drivers/media/i2c/ov2680.c into shape is converting it to the
> new CCI helpers so that I could then easily copy over bits from the
> also converted atomisp-ov2680.c.
>
> So it might be interesting to still merge this so that the latest
> state of atomisp-ov2680.c is easier to compare to
> drivers/media/i2c/ov2680.c if the need arises.
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 3/3] media: Remove ov_16bit_addr_reg_helpers.h
2023-06-06 16:58 ` [PATCH 3/3] media: Remove ov_16bit_addr_reg_helpers.h Hans de Goede
@ 2023-06-07 15:57 ` Laurent Pinchart
0 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2023-06-07 15:57 UTC (permalink / raw)
To: Hans de Goede
Cc: Sakari Ailus, Mauro Carvalho Chehab, Andy Shevchenko, linux-media
Hi Hans,
Thank you for the patch.
On Tue, Jun 06, 2023 at 06:58:08PM +0200, Hans de Goede wrote:
> The helpers in this header are not used anywhere anymore,
> they have been superseded by the new CCI register access helpers.
>
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
I'm happy to see a nicer API taking over :-)
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
> include/media/ov_16bit_addr_reg_helpers.h | 92 -----------------------
> 1 file changed, 92 deletions(-)
> delete mode 100644 include/media/ov_16bit_addr_reg_helpers.h
>
> diff --git a/include/media/ov_16bit_addr_reg_helpers.h b/include/media/ov_16bit_addr_reg_helpers.h
> deleted file mode 100644
> index 1c60a50bd795..000000000000
> --- a/include/media/ov_16bit_addr_reg_helpers.h
> +++ /dev/null
> @@ -1,92 +0,0 @@
> -/* SPDX-License-Identifier: GPL-2.0 */
> -/*
> - * I2C register access helpers for Omnivision OVxxxx image sensors which expect
> - * a 16 bit register address in big-endian format and which have 1-3 byte
> - * wide registers, in big-endian format (for the higher width registers).
> - *
> - * Based on the register helpers from drivers/media/i2c/ov2680.c which is:
> - * Copyright (C) 2018 Linaro Ltd
> - */
> -#ifndef __OV_16BIT_ADDR_REG_HELPERS_H
> -#define __OV_16BIT_ADDR_REG_HELPERS_H
> -
> -#include <asm/unaligned.h>
> -#include <linux/dev_printk.h>
> -#include <linux/i2c.h>
> -
> -static inline int ov_read_reg(struct i2c_client *client, u16 reg,
> - unsigned int len, u32 *val)
> -{
> - u8 addr_buf[2], data_buf[4] = { };
> - struct i2c_msg msgs[2];
> - int ret;
> -
> - if (len > 4)
> - return -EINVAL;
> -
> - put_unaligned_be16(reg, addr_buf);
> -
> - msgs[0].addr = client->addr;
> - msgs[0].flags = 0;
> - msgs[0].len = ARRAY_SIZE(addr_buf);
> - msgs[0].buf = addr_buf;
> -
> - msgs[1].addr = client->addr;
> - msgs[1].flags = I2C_M_RD;
> - msgs[1].len = len;
> - msgs[1].buf = &data_buf[4 - len];
> -
> - ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
> - if (ret != ARRAY_SIZE(msgs)) {
> - dev_err(&client->dev, "read error: reg=0x%4x: %d\n", reg, ret);
> - return -EIO;
> - }
> -
> - *val = get_unaligned_be32(data_buf);
> -
> - return 0;
> -}
> -
> -#define ov_read_reg8(s, r, v) ov_read_reg(s, r, 1, v)
> -#define ov_read_reg16(s, r, v) ov_read_reg(s, r, 2, v)
> -#define ov_read_reg24(s, r, v) ov_read_reg(s, r, 3, v)
> -
> -static inline int ov_write_reg(struct i2c_client *client, u16 reg,
> - unsigned int len, u32 val)
> -{
> - u8 buf[6];
> - int ret;
> -
> - if (len > 4)
> - return -EINVAL;
> -
> - put_unaligned_be16(reg, buf);
> - put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
> - ret = i2c_master_send(client, buf, len + 2);
> - if (ret != len + 2) {
> - dev_err(&client->dev, "write error: reg=0x%4x: %d\n", reg, ret);
> - return -EIO;
> - }
> -
> - return 0;
> -}
> -
> -#define ov_write_reg8(s, r, v) ov_write_reg(s, r, 1, v)
> -#define ov_write_reg16(s, r, v) ov_write_reg(s, r, 2, v)
> -#define ov_write_reg24(s, r, v) ov_write_reg(s, r, 3, v)
> -
> -static inline int ov_update_reg(struct i2c_client *client, u16 reg, u8 mask, u8 val)
> -{
> - u32 readval;
> - int ret;
> -
> - ret = ov_read_reg8(client, reg, &readval);
> - if (ret < 0)
> - return ret;
> -
> - val = (readval & ~mask) | (val & mask);
> -
> - return ov_write_reg8(client, reg, val);
> -}
> -
> -#endif
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-07 15:40 ` Andy Shevchenko
@ 2023-06-07 15:58 ` Hans de Goede
2023-06-07 16:14 ` Andy Shevchenko
0 siblings, 1 reply; 38+ messages in thread
From: Hans de Goede @ 2023-06-07 15:58 UTC (permalink / raw)
To: Andy Shevchenko, Sakari Ailus
Cc: Laurent Pinchart, Mauro Carvalho Chehab, Andy Shevchenko,
linux-media
Hi,
On 6/7/23 17:40, Andy Shevchenko wrote:
> On Wed, Jun 7, 2023 at 3:01 PM Sakari Ailus
> <sakari.ailus@linux.intel.com> wrote:
>> On Wed, Jun 07, 2023 at 10:40:34AM +0200, Hans de Goede wrote:
>>> On 6/6/23 22:43, Andy Shevchenko wrote:
>>>> On Tue, Jun 6, 2023 at 7:58 PM Hans de Goede <hdegoede@redhat.com> wrote:
>
> ...
>
>>>>> +int cci_read(struct regmap *map, u32 reg, u32 *val, int *err)
>>>>> +{
>>>>> + int i, len, ret;
>>>>> + u8 buf[4];
>
>>>>> + *val = 0;
>>>>> + for (i = 0; i < len; i++) {
>>>>> + *val <<= 8;
>>>>> + *val |= buf[i];
>>>>> + }
>>>>
>>>> I really prefer to see put_unaligned() here depending on the length.
>>>> Note, that on some CPUs it might be one assembly instruction or even
>>>> none, depending on how the result is going to be used.
>>>
>>> Ok, so you mean changing it to something like this:
>>>
>>> switch (len)
>>> case 1:
>>> *val = buf[0];
>>> break;
>>> case 2:
>>> *val = get_unaligned_be16(buf);
>>> break;
>>> case 3:
>>> *val = __get_unaligned_be24(buf);
>
> __without double underscore prefix
include/asm-generic/unaligned.h
defines __get_unaligned_be24() and not get_unaligned_be24(), I guess because 24bit is not a standard register width.
>
>>> break;
>>> case 4:
>>> *val = get_unaligned_be32(buf);
>>> break;
>>> }
>>
>> I think the loop looks nicer but I'm fine with this as well.
>>
>>> ?
>
> But the loop hides what's going on there. And I believe code
> generation would be worse with a loop.
> Also note, that in case of switch-case we don't write to the pointed
> memory several times, which I think is also the win.
I understand, unless someone objects I'll move to the switch-case
approach for v2.
Regards,
Hans
>
>>>>> + return 0;
>>>>> +}
>
> ...
>
>>>>> +int cci_write(struct regmap *map, u32 reg, u32 val, int *err)
>>>>> +{
>>>>> + int i, len, ret;
>>>>> + u8 buf[4];
>>>>> +
>>>>> + if (err && *err)
>>>>> + return *err;
>>>>> +
>>>>> + /* Set len to register width in bytes */
>>>>> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
>>>>> + reg &= CCI_REG_ADDR_MASK;
>>>>> +
>>>>> + for (i = 0; i < len; i++) {
>>>>> + buf[len - i - 1] = val & 0xff;
>>>>> + val >>= 8;
>>>>> + }
>
> Similar way here.
>
>>>>> +
>>>>> + ret = regmap_bulk_write(map, reg, buf, len);
>>>>> + if (ret) {
>>>>> + dev_err(regmap_get_device(map), "Error writing reg 0x%4x: %d\n", reg, ret);
>>>>> + if (err)
>>>>> + *err = ret;
>>>>> + }
>>>>> +
>>>>> + return ret;
>>>>> +}
>
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 2/3] media: atomisp: ov2680: Convert to new CCI register access helpers
2023-06-07 15:51 ` Laurent Pinchart
@ 2023-06-07 15:59 ` Hans de Goede
2023-06-07 16:07 ` Laurent Pinchart
0 siblings, 1 reply; 38+ messages in thread
From: Hans de Goede @ 2023-06-07 15:59 UTC (permalink / raw)
To: Laurent Pinchart
Cc: Andy Shevchenko, Sakari Ailus, Mauro Carvalho Chehab,
Andy Shevchenko, linux-media
Hi Laurent,
On 6/7/23 17:51, Laurent Pinchart wrote:
> Hi Hans,
>
> On Wed, Jun 07, 2023 at 10:53:54AM +0200, Hans de Goede wrote:
>> On 6/6/23 22:53, Andy Shevchenko wrote:
>>> On Tue, Jun 6, 2023 at 7:58 PM Hans de Goede wrote:
>>>>
>>>> Use the new comon CCI register access helpers to replace the private
>>>> register access helpers in the ov2680 driver.
>>>>
>>>> While at it also switch to using the same register address defines
>>>> as the standard drivers/media/i2c/ov2680.c driver to make merging
>>>> the 2 drivers simpler.
>>>
>>> ...
>>>
>>>> + cci_write(sensor->regmap, OV2680_REG_SENSOR_CTRL_0A, sensor_ctrl_0a, &ret);
>>>> + cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_START, sensor->mode.h_start, &ret);
>>>> + cci_write(sensor->regmap, OV2680_REG_VERTICAL_START, sensor->mode.v_start, &ret);
>>>> + cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_END, sensor->mode.h_end, &ret);
>>>> + cci_write(sensor->regmap, OV2680_REG_VERTICAL_END, sensor->mode.v_end, &ret);
>>>> + cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_OUTPUT_SIZE,
>>>> + sensor->mode.h_output_size, &ret);
>>>> + cci_write(sensor->regmap, OV2680_REG_VERTICAL_OUTPUT_SIZE,
>>>> + sensor->mode.v_output_size, &ret);
>>>> + cci_write(sensor->regmap, OV2680_REG_TIMING_HTS, sensor->mode.hts, &ret);
>>>> + cci_write(sensor->regmap, OV2680_REG_TIMING_VTS, sensor->mode.vts, &ret);
>>>> + cci_write(sensor->regmap, OV2680_REG_ISP_X_WIN, 0, &ret);
>>>> + cci_write(sensor->regmap, OV2680_REG_ISP_Y_WIN, 0, &ret);
>>>> + cci_write(sensor->regmap, OV2680_REG_X_INC, inc, &ret);
>>>> + cci_write(sensor->regmap, OV2680_REG_Y_INC, inc, &ret);
>>>> + cci_write(sensor->regmap, OV2680_REG_X_WIN, sensor->mode.h_output_size, &ret);
>>>> + cci_write(sensor->regmap, OV2680_REG_Y_WIN, sensor->mode.v_output_size, &ret);
>>>> + cci_write(sensor->regmap, OV2680_REG_FORMAT1, fmt1, &ret);
>>>> + cci_write(sensor->regmap, OV2680_REG_FORMAT2, fmt2, &ret);
>>>
>>> I know that &ret thingy was discussed before and Laurent is keen to
>>> have this, but has anybody actually tested how bad or not at all the
>>> code generation becomes?
>>
>> The cci_write function is in another module, so it won't be inlined
>> and as such I don't see how the code generation can become bad. We
>> loose all the if (ret) return ret; checks here, so the code should
>> become smaller.
>>
>> Or are you worried about having to pass the 1 extra parameter ?
>>
>>> ...
>>>
>>>> + struct device *dev;
>>>> + struct regmap *regmap;
>>>
>>> Isn't the same device associated with regmap? If so, one of them
>>> probably duplicates the other.
>>
>> You are right, but the entire atomisp-ov2680.c file is going away real
>> soon now. I plan to post a series to get drivers/media/i2c/ov2680.c
>> ready to replace it later today.
>>
>> So I'm not even sure if this patch should be merged, as I mentioned in
>> the cover letter this one is mostly here to illustrate use of the new
>> helpers.
>
> How about porting drivers/media/i2c/imx290.c ? That's a real-life
> example that can be merged, which is good to serve as an example
> showcasing the API usage in mainline. It will also help ensuring that
> these helpers are a good fit for drivers that already encode the
> register width in the macros.
I prefer to port over drivers which I can actually test,
at least for now.
I already have converting ov5693.c (which also already has macros
to encode to width) on my TODO list. I'll convert that for v2
of the series.
And I also have a conversion of the "main" drivers/media/i2c/ov2680.c
ready.
I'll post that conversion as part of my big main ov2680 changes series
which I'll post in a couple of minutes (just need to write
a cover letter and then its ready).
Regards,
Hans
>> I also wrote this patch to make porting recent atomisp-ov2680.c
>> changes over to drivers/media/i2c/ov2680.c easier. Part of the series
>> to get drivers/media/i2c/ov2680.c into shape is converting it to the
>> new CCI helpers so that I could then easily copy over bits from the
>> also converted atomisp-ov2680.c.
>>
>> So it might be interesting to still merge this so that the latest
>> state of atomisp-ov2680.c is easier to compare to
>> drivers/media/i2c/ov2680.c if the need arises.
>
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 2/3] media: atomisp: ov2680: Convert to new CCI register access helpers
2023-06-06 16:58 ` [PATCH 2/3] media: atomisp: ov2680: Convert to new CCI register access helpers Hans de Goede
2023-06-06 20:53 ` Andy Shevchenko
@ 2023-06-07 16:05 ` Laurent Pinchart
2023-06-07 16:18 ` Hans de Goede
1 sibling, 1 reply; 38+ messages in thread
From: Laurent Pinchart @ 2023-06-07 16:05 UTC (permalink / raw)
To: Hans de Goede
Cc: Sakari Ailus, Mauro Carvalho Chehab, Andy Shevchenko, linux-media
Hi Hans,
Thank you for the patch.
On Tue, Jun 06, 2023 at 06:58:07PM +0200, Hans de Goede wrote:
> Use the new comon CCI register access helpers to replace the private
> register access helpers in the ov2680 driver.
>
> While at it also switch to using the same register address defines
> as the standard drivers/media/i2c/ov2680.c driver to make merging
> the 2 drivers simpler.
>
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> ---
> drivers/staging/media/atomisp/i2c/Kconfig | 1 +
> .../media/atomisp/i2c/atomisp-ov2680.c | 233 ++++++++----------
> drivers/staging/media/atomisp/i2c/ov2680.h | 73 +-----
> 3 files changed, 104 insertions(+), 203 deletions(-)
>
> diff --git a/drivers/staging/media/atomisp/i2c/Kconfig b/drivers/staging/media/atomisp/i2c/Kconfig
> index 16b6b808d4a7..e353b7fdbff0 100644
> --- a/drivers/staging/media/atomisp/i2c/Kconfig
> +++ b/drivers/staging/media/atomisp/i2c/Kconfig
> @@ -53,6 +53,7 @@ config VIDEO_ATOMISP_OV2680
> tristate "Omnivision OV2680 sensor support"
> depends on ACPI
> depends on I2C && VIDEO_DEV
> + select V4L2_CCI
> help
> This is a Video4Linux2 sensor-level driver for the Omnivision
> OV2680 raw camera.
> diff --git a/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c b/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c
> index 77070bbd0157..3cc56090677c 100644
> --- a/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c
> +++ b/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c
> @@ -25,11 +25,47 @@
> #include <linux/pm_runtime.h>
> #include <linux/types.h>
>
> -#include <media/ov_16bit_addr_reg_helpers.h>
> #include <media/v4l2-device.h>
>
> #include "ov2680.h"
>
> +#define OV2680_CHIP_ID 0x2680
> +
> +#define OV2680_REG_STREAM_CTRL CCI_REG8(0x0100)
> +#define OV2680_REG_SOFT_RESET CCI_REG8(0x0103)
> +
> +#define OV2680_REG_CHIP_ID CCI_REG16(0x300a)
> +#define OV2680_REG_SC_CMMN_SUB_ID CCI_REG8(0x302a)
> +
> +#define OV2680_REG_EXPOSURE_PK CCI_REG24(0x3500)
> +#define OV2680_REG_R_MANUAL CCI_REG8(0x3503)
> +#define OV2680_REG_GAIN_PK CCI_REG16(0x350a)
> +
> +#define OV2680_REG_SENSOR_CTRL_0A CCI_REG8(0x370a)
> +
> +#define OV2680_REG_HORIZONTAL_START CCI_REG16(0x3800)
> +#define OV2680_REG_VERTICAL_START CCI_REG16(0x3802)
> +#define OV2680_REG_HORIZONTAL_END CCI_REG16(0x3804)
> +#define OV2680_REG_VERTICAL_END CCI_REG16(0x3806)
> +#define OV2680_REG_HORIZONTAL_OUTPUT_SIZE CCI_REG16(0x3808)
> +#define OV2680_REG_VERTICAL_OUTPUT_SIZE CCI_REG16(0x380a)
> +#define OV2680_REG_TIMING_HTS CCI_REG16(0x380c)
> +#define OV2680_REG_TIMING_VTS CCI_REG16(0x380e)
> +#define OV2680_REG_ISP_X_WIN CCI_REG16(0x3810)
> +#define OV2680_REG_ISP_Y_WIN CCI_REG16(0x3812)
> +#define OV2680_REG_X_INC CCI_REG8(0x3814)
> +#define OV2680_REG_Y_INC CCI_REG8(0x3815)
> +#define OV2680_REG_FORMAT1 CCI_REG8(0x3820)
> +#define OV2680_REG_FORMAT2 CCI_REG8(0x3821)
> +
> +#define OV2680_REG_ISP_CTRL00 CCI_REG8(0x5080)
> +
> +#define OV2680_REG_X_WIN CCI_REG16(0x5704)
> +#define OV2680_REG_Y_WIN CCI_REG16(0x5706)
> +
> +#define OV2680_FRAME_RATE 30
> +#define OV2680_INTEGRATION_TIME_MARGIN 8
> +
> static const struct v4l2_rect ov2680_default_crop = {
> .left = OV2680_ACTIVE_START_LEFT,
> .top = OV2680_ACTIVE_START_TOP,
> @@ -37,21 +73,6 @@ static const struct v4l2_rect ov2680_default_crop = {
> .height = OV2680_ACTIVE_HEIGHT,
> };
>
> -static int ov2680_write_reg_array(struct i2c_client *client,
> - const struct ov2680_reg *reglist)
> -{
> - const struct ov2680_reg *next = reglist;
> - int ret;
> -
> - for (; next->reg != 0; next++) {
> - ret = ov_write_reg8(client, next->reg, next->val);
> - if (ret)
> - return ret;
> - }
> -
> - return 0;
> -}
> -
> static void ov2680_set_bayer_order(struct ov2680_dev *sensor, struct v4l2_mbus_framefmt *fmt)
> {
> static const int ov2680_hv_flip_bayer_order[] = {
> @@ -78,7 +99,8 @@ static int ov2680_set_vflip(struct ov2680_dev *sensor, s32 val)
> if (sensor->is_streaming)
> return -EBUSY;
>
> - ret = ov_update_reg(sensor->client, OV2680_REG_FORMAT1, BIT(2), val ? BIT(2) : 0);
> + ret = cci_update_bits(sensor->regmap, OV2680_REG_FORMAT1, BIT(2),
> + val ? BIT(2) : 0, NULL);
> if (ret < 0)
> return ret;
>
> @@ -93,7 +115,8 @@ static int ov2680_set_hflip(struct ov2680_dev *sensor, s32 val)
> if (sensor->is_streaming)
> return -EBUSY;
>
> - ret = ov_update_reg(sensor->client, OV2680_REG_FORMAT2, BIT(2), val ? BIT(2) : 0);
> + ret = cci_update_bits(sensor->regmap, OV2680_REG_FORMAT2, BIT(2),
> + val ? BIT(2) : 0, NULL);
> if (ret < 0)
> return ret;
>
> @@ -103,30 +126,25 @@ static int ov2680_set_hflip(struct ov2680_dev *sensor, s32 val)
>
> static int ov2680_exposure_set(struct ov2680_dev *sensor, u32 exp)
> {
> - return ov_write_reg24(sensor->client, OV2680_REG_EXPOSURE_PK_HIGH, exp << 4);
> + return cci_write(sensor->regmap, OV2680_REG_EXPOSURE_PK, exp << 4, NULL);
> }
>
> static int ov2680_gain_set(struct ov2680_dev *sensor, u32 gain)
> {
> - return ov_write_reg16(sensor->client, OV2680_REG_GAIN_PK, gain);
> + return cci_write(sensor->regmap, OV2680_REG_GAIN_PK, gain, NULL);
> }
>
> static int ov2680_test_pattern_set(struct ov2680_dev *sensor, int value)
> {
> - int ret;
> + int ret = 0;
>
> if (!value)
> - return ov_update_reg(sensor->client, OV2680_REG_ISP_CTRL00, BIT(7), 0);
> + return cci_update_bits(sensor->regmap, OV2680_REG_ISP_CTRL00, BIT(7), 0, NULL);
>
> - ret = ov_update_reg(sensor->client, OV2680_REG_ISP_CTRL00, 0x03, value - 1);
> - if (ret < 0)
> - return ret;
> + cci_update_bits(sensor->regmap, OV2680_REG_ISP_CTRL00, 0x03, value - 1, &ret);
> + cci_update_bits(sensor->regmap, OV2680_REG_ISP_CTRL00, BIT(7), BIT(7), &ret);
>
> - ret = ov_update_reg(sensor->client, OV2680_REG_ISP_CTRL00, BIT(7), BIT(7));
> - if (ret < 0)
> - return ret;
> -
> - return 0;
> + return ret;
> }
>
> static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl)
> @@ -171,15 +189,16 @@ static const struct v4l2_ctrl_ops ov2680_ctrl_ops = {
>
> static int ov2680_init_registers(struct v4l2_subdev *sd)
> {
> - struct i2c_client *client = v4l2_get_subdevdata(sd);
> - int ret;
> + struct ov2680_dev *sensor = to_ov2680_sensor(sd);
> + int ret = 0;
>
> - ret = ov_write_reg8(client, OV2680_SW_RESET, 0x01);
> + cci_write(sensor->regmap, OV2680_REG_SOFT_RESET, 0x01, &ret);
>
> /* Wait for sensor reset */
> usleep_range(1000, 2000);
>
> - ret |= ov2680_write_reg_array(client, ov2680_global_setting);
> + cci_multi_reg_write(sensor->regmap, ov2680_global_setting,
> + ARRAY_SIZE(ov2680_global_setting), &ret);
>
> return ret;
> }
> @@ -247,9 +266,8 @@ static void ov2680_calc_mode(struct ov2680_dev *sensor)
>
> static int ov2680_set_mode(struct ov2680_dev *sensor)
> {
> - struct i2c_client *client = sensor->client;
> u8 sensor_ctrl_0a, inc, fmt1, fmt2;
> - int ret;
> + int ret = 0;
>
> if (sensor->mode.binning) {
> sensor_ctrl_0a = 0x23;
> @@ -263,77 +281,27 @@ static int ov2680_set_mode(struct ov2680_dev *sensor)
> fmt2 = 0x00;
> }
>
> - ret = ov_write_reg8(client, OV2680_REG_SENSOR_CTRL_0A, sensor_ctrl_0a);
> - if (ret)
> - return ret;
> + cci_write(sensor->regmap, OV2680_REG_SENSOR_CTRL_0A, sensor_ctrl_0a, &ret);
> + cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_START, sensor->mode.h_start, &ret);
> + cci_write(sensor->regmap, OV2680_REG_VERTICAL_START, sensor->mode.v_start, &ret);
> + cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_END, sensor->mode.h_end, &ret);
> + cci_write(sensor->regmap, OV2680_REG_VERTICAL_END, sensor->mode.v_end, &ret);
> + cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_OUTPUT_SIZE,
> + sensor->mode.h_output_size, &ret);
> + cci_write(sensor->regmap, OV2680_REG_VERTICAL_OUTPUT_SIZE,
> + sensor->mode.v_output_size, &ret);
> + cci_write(sensor->regmap, OV2680_REG_TIMING_HTS, sensor->mode.hts, &ret);
> + cci_write(sensor->regmap, OV2680_REG_TIMING_VTS, sensor->mode.vts, &ret);
> + cci_write(sensor->regmap, OV2680_REG_ISP_X_WIN, 0, &ret);
> + cci_write(sensor->regmap, OV2680_REG_ISP_Y_WIN, 0, &ret);
> + cci_write(sensor->regmap, OV2680_REG_X_INC, inc, &ret);
> + cci_write(sensor->regmap, OV2680_REG_Y_INC, inc, &ret);
> + cci_write(sensor->regmap, OV2680_REG_X_WIN, sensor->mode.h_output_size, &ret);
> + cci_write(sensor->regmap, OV2680_REG_Y_WIN, sensor->mode.v_output_size, &ret);
> + cci_write(sensor->regmap, OV2680_REG_FORMAT1, fmt1, &ret);
> + cci_write(sensor->regmap, OV2680_REG_FORMAT2, fmt2, &ret);
>
> - ret = ov_write_reg16(client, OV2680_HORIZONTAL_START_H, sensor->mode.h_start);
> - if (ret)
> - return ret;
> -
> - ret = ov_write_reg16(client, OV2680_VERTICAL_START_H, sensor->mode.v_start);
> - if (ret)
> - return ret;
> -
> - ret = ov_write_reg16(client, OV2680_HORIZONTAL_END_H, sensor->mode.h_end);
> - if (ret)
> - return ret;
> -
> - ret = ov_write_reg16(client, OV2680_VERTICAL_END_H, sensor->mode.v_end);
> - if (ret)
> - return ret;
> -
> - ret = ov_write_reg16(client, OV2680_HORIZONTAL_OUTPUT_SIZE_H,
> - sensor->mode.h_output_size);
> - if (ret)
> - return ret;
> -
> - ret = ov_write_reg16(client, OV2680_VERTICAL_OUTPUT_SIZE_H,
> - sensor->mode.v_output_size);
> - if (ret)
> - return ret;
> -
> - ret = ov_write_reg16(client, OV2680_HTS, sensor->mode.hts);
> - if (ret)
> - return ret;
> -
> - ret = ov_write_reg16(client, OV2680_VTS, sensor->mode.vts);
> - if (ret)
> - return ret;
> -
> - ret = ov_write_reg16(client, OV2680_ISP_X_WIN, 0);
> - if (ret)
> - return ret;
> -
> - ret = ov_write_reg16(client, OV2680_ISP_Y_WIN, 0);
> - if (ret)
> - return ret;
> -
> - ret = ov_write_reg8(client, OV2680_X_INC, inc);
> - if (ret)
> - return ret;
> -
> - ret = ov_write_reg8(client, OV2680_Y_INC, inc);
> - if (ret)
> - return ret;
> -
> - ret = ov_write_reg16(client, OV2680_X_WIN, sensor->mode.h_output_size);
> - if (ret)
> - return ret;
> -
> - ret = ov_write_reg16(client, OV2680_Y_WIN, sensor->mode.v_output_size);
> - if (ret)
> - return ret;
> -
> - ret = ov_write_reg8(client, OV2680_REG_FORMAT1, fmt1);
> - if (ret)
> - return ret;
> -
> - ret = ov_write_reg8(client, OV2680_REG_FORMAT2, fmt2);
> - if (ret)
> - return ret;
> -
> - return 0;
> + return ret;
> }
>
> static int ov2680_set_fmt(struct v4l2_subdev *sd,
> @@ -478,35 +446,25 @@ static int ov2680_init_cfg(struct v4l2_subdev *sd,
> return ov2680_set_fmt(sd, sd_state, &fmt);
> }
>
> -static int ov2680_detect(struct i2c_client *client)
> +static int ov2680_detect(struct ov2680_dev *sensor)
> {
> - struct i2c_adapter *adapter = client->adapter;
> - u32 high, low;
> - int ret;
> - u16 id;
> - u8 revision;
> + u32 chip_id, rev;
> + int ret = 0;
>
> - if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
> - return -ENODEV;
> -
> - ret = ov_read_reg8(client, OV2680_SC_CMMN_CHIP_ID_H, &high);
> - if (ret) {
> - dev_err(&client->dev, "sensor_id_high = 0x%x\n", high);
> - return -ENODEV;
> - }
> - ret = ov_read_reg8(client, OV2680_SC_CMMN_CHIP_ID_L, &low);
> - id = ((((u16)high) << 8) | (u16)low);
> -
> - if (id != OV2680_ID) {
> - dev_err(&client->dev, "sensor ID error 0x%x\n", id);
> + cci_read(sensor->regmap, OV2680_REG_CHIP_ID, &chip_id, &ret);
> + cci_read(sensor->regmap, OV2680_REG_SC_CMMN_SUB_ID, &rev, &ret);
> + if (ret < 0) {
> + dev_err(sensor->dev, "failed to read chip id\n");
> return -ENODEV;
> }
>
> - ret = ov_read_reg8(client, OV2680_SC_CMMN_SUB_ID, &high);
> - revision = (u8)high & 0x0f;
> + if (chip_id != OV2680_CHIP_ID) {
> + dev_err(sensor->dev, "chip id: 0x%04x does not match expected 0x%04x\n",
> + chip_id, OV2680_CHIP_ID);
> + return -ENODEV;
> + }
>
> - dev_info(&client->dev, "sensor_revision id = 0x%x, rev= %d\n",
> - id, revision);
> + dev_info(sensor->dev, "sensor_revision id = 0x%x, rev= %d\n", chip_id, rev & 0x0f);
>
> return 0;
> }
> @@ -538,11 +496,11 @@ static int ov2680_s_stream(struct v4l2_subdev *sd, int enable)
> if (ret)
> goto error_power_down;
>
> - ret = ov_write_reg8(client, OV2680_SW_STREAM, OV2680_START_STREAMING);
> + ret = cci_write(sensor->regmap, OV2680_REG_STREAM_CTRL, 1, NULL);
> if (ret)
> goto error_power_down;
> } else {
> - ov_write_reg8(client, OV2680_SW_STREAM, OV2680_STOP_STREAMING);
> + cci_write(sensor->regmap, OV2680_REG_STREAM_CTRL, 0, NULL);
> pm_runtime_put(sensor->sd.dev);
> }
>
> @@ -563,6 +521,7 @@ static int ov2680_s_stream(struct v4l2_subdev *sd, int enable)
>
> static int ov2680_s_config(struct v4l2_subdev *sd)
> {
> + struct ov2680_dev *sensor = to_ov2680_sensor(sd);
> struct i2c_client *client = v4l2_get_subdevdata(sd);
> int ret;
>
> @@ -573,7 +532,7 @@ static int ov2680_s_config(struct v4l2_subdev *sd)
> }
>
> /* config & detect sensor */
> - ret = ov2680_detect(client);
> + ret = ov2680_detect(sensor);
> if (ret)
> dev_err(&client->dev, "ov2680_detect err s_config.\n");
>
> @@ -586,7 +545,7 @@ static int ov2680_g_frame_interval(struct v4l2_subdev *sd,
> struct v4l2_subdev_frame_interval *interval)
> {
> interval->interval.numerator = 1;
> - interval->interval.denominator = OV2680_FPS;
> + interval->interval.denominator = OV2680_FRAME_RATE;
> return 0;
> }
>
> @@ -638,7 +597,7 @@ static int ov2680_enum_frame_interval(struct v4l2_subdev *sd,
> return -EINVAL;
>
> fie->interval.numerator = 1;
> - fie->interval.denominator = OV2680_FPS;
> + fie->interval.denominator = OV2680_FRAME_RATE;
> return 0;
> }
>
> @@ -738,9 +697,13 @@ static int ov2680_probe(struct i2c_client *client)
> if (!sensor)
> return -ENOMEM;
>
> + sensor->regmap = cci_regmap_init_i2c(client, 16);
> + if (IS_ERR(sensor->regmap))
> + return PTR_ERR(sensor->regmap);
> +
> mutex_init(&sensor->lock);
>
> - sensor->client = client;
> + sensor->dev = &client->dev;
> v4l2_i2c_subdev_init(&sensor->sd, client, &ov2680_ops);
>
> /*
> diff --git a/drivers/staging/media/atomisp/i2c/ov2680.h b/drivers/staging/media/atomisp/i2c/ov2680.h
> index d032af245674..d547846c2d13 100644
> --- a/drivers/staging/media/atomisp/i2c/ov2680.h
> +++ b/drivers/staging/media/atomisp/i2c/ov2680.h
> @@ -24,6 +24,7 @@
> #include <linux/delay.h>
> #include <linux/videodev2.h>
> #include <linux/spinlock.h>
> +#include <media/v4l2-cci.h>
> #include <media/v4l2-subdev.h>
> #include <media/v4l2-device.h>
> #include <media/v4l2-ctrls.h>
> @@ -44,75 +45,12 @@
> /* 1704 * 1294 * 30fps = 66MHz pixel clock */
> #define OV2680_PIXELS_PER_LINE 1704
> #define OV2680_LINES_PER_FRAME 1294
> -#define OV2680_FPS 30
> +
> #define OV2680_SKIP_FRAMES 3
>
> /* If possible send 16 extra rows / lines to the ISP as padding */
> #define OV2680_END_MARGIN 16
>
> -#define OV2680_FOCAL_LENGTH_NUM 334 /*3.34mm*/
> -
> -#define OV2680_INTEGRATION_TIME_MARGIN 8
> -#define OV2680_ID 0x2680
> -
> -/*
> - * OV2680 System control registers
> - */
> -#define OV2680_SW_SLEEP 0x0100
> -#define OV2680_SW_RESET 0x0103
> -#define OV2680_SW_STREAM 0x0100
> -
> -#define OV2680_SC_CMMN_CHIP_ID_H 0x300A
> -#define OV2680_SC_CMMN_CHIP_ID_L 0x300B
> -#define OV2680_SC_CMMN_SCCB_ID 0x302B /* 0x300C*/
> -#define OV2680_SC_CMMN_SUB_ID 0x302A /* process, version*/
> -
> -#define OV2680_GROUP_ACCESS 0x3208 /*Bit[7:4] Group control, Bit[3:0] Group ID*/
> -
> -#define OV2680_REG_EXPOSURE_PK_HIGH 0x3500
> -#define OV2680_REG_GAIN_PK 0x350a
> -
> -#define OV2680_REG_SENSOR_CTRL_0A 0x370a
> -
> -#define OV2680_HORIZONTAL_START_H 0x3800 /* Bit[11:8] */
> -#define OV2680_HORIZONTAL_START_L 0x3801 /* Bit[7:0] */
> -#define OV2680_VERTICAL_START_H 0x3802 /* Bit[11:8] */
> -#define OV2680_VERTICAL_START_L 0x3803 /* Bit[7:0] */
> -#define OV2680_HORIZONTAL_END_H 0x3804 /* Bit[11:8] */
> -#define OV2680_HORIZONTAL_END_L 0x3805 /* Bit[7:0] */
> -#define OV2680_VERTICAL_END_H 0x3806 /* Bit[11:8] */
> -#define OV2680_VERTICAL_END_L 0x3807 /* Bit[7:0] */
> -#define OV2680_HORIZONTAL_OUTPUT_SIZE_H 0x3808 /* Bit[11:8] */
> -#define OV2680_HORIZONTAL_OUTPUT_SIZE_L 0x3809 /* Bit[7:0] */
> -#define OV2680_VERTICAL_OUTPUT_SIZE_H 0x380a /* Bit[11:8] */
> -#define OV2680_VERTICAL_OUTPUT_SIZE_L 0x380b /* Bit[7:0] */
> -#define OV2680_HTS 0x380c
> -#define OV2680_VTS 0x380e
> -#define OV2680_ISP_X_WIN 0x3810
> -#define OV2680_ISP_Y_WIN 0x3812
> -#define OV2680_X_INC 0x3814
> -#define OV2680_Y_INC 0x3815
> -
> -#define OV2680_FRAME_OFF_NUM 0x4202
> -
> -/*Flip/Mirror*/
> -#define OV2680_REG_FORMAT1 0x3820
> -#define OV2680_REG_FORMAT2 0x3821
> -
> -#define OV2680_MWB_RED_GAIN_H 0x5004/*0x3400*/
> -#define OV2680_MWB_GREEN_GAIN_H 0x5006/*0x3402*/
> -#define OV2680_MWB_BLUE_GAIN_H 0x5008/*0x3404*/
> -#define OV2680_MWB_GAIN_MAX 0x0fff
> -
> -#define OV2680_REG_ISP_CTRL00 0x5080
> -
> -#define OV2680_X_WIN 0x5704
> -#define OV2680_Y_WIN 0x5706
> -#define OV2680_WIN_CONTROL 0x5708
> -
> -#define OV2680_START_STREAMING 0x01
> -#define OV2680_STOP_STREAMING 0x00
> -
> /*
> * ov2680 device structure.
> */
> @@ -121,7 +59,8 @@ struct ov2680_dev {
> struct media_pad pad;
> /* Protect against concurrent changes to controls */
> struct mutex lock;
> - struct i2c_client *client;
> + struct device *dev;
> + struct regmap *regmap;
> struct gpio_desc *powerdown;
> struct fwnode_handle *ep_fwnode;
> bool is_streaming;
> @@ -173,7 +112,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
> return &sensor->sd;
> }
>
> -static struct ov2680_reg const ov2680_global_setting[] = {
> +static const struct reg_sequence ov2680_global_setting[] = {
Maybe you could move this to the .c file and also move the v4l2_cci.h
inclusion to the .c file ? Or this can be done on top, as I assume
you'll merge the .h and .c files at some point anyway.
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> /* MIPI PHY, 0x10 -> 0x1c enable bp_c_hs_en_lat and bp_d_hs_en_lat */
> {0x3016, 0x1c},
>
> @@ -242,8 +181,6 @@ static struct ov2680_reg const ov2680_global_setting[] = {
>
> /* DPC THRE RATIO 0x04 (4) -> 0x00 (0) */
> {0x5792, 0x00},
> -
> - {}
> };
>
> #endif
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 2/3] media: atomisp: ov2680: Convert to new CCI register access helpers
2023-06-07 15:59 ` Hans de Goede
@ 2023-06-07 16:07 ` Laurent Pinchart
0 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2023-06-07 16:07 UTC (permalink / raw)
To: Hans de Goede
Cc: Andy Shevchenko, Sakari Ailus, Mauro Carvalho Chehab,
Andy Shevchenko, linux-media
Hi Hans,
On Wed, Jun 07, 2023 at 05:59:08PM +0200, Hans de Goede wrote:
> On 6/7/23 17:51, Laurent Pinchart wrote:
> > On Wed, Jun 07, 2023 at 10:53:54AM +0200, Hans de Goede wrote:
> >> On 6/6/23 22:53, Andy Shevchenko wrote:
> >>> On Tue, Jun 6, 2023 at 7:58 PM Hans de Goede wrote:
> >>>>
> >>>> Use the new comon CCI register access helpers to replace the private
> >>>> register access helpers in the ov2680 driver.
> >>>>
> >>>> While at it also switch to using the same register address defines
> >>>> as the standard drivers/media/i2c/ov2680.c driver to make merging
> >>>> the 2 drivers simpler.
> >>>
> >>> ...
> >>>
> >>>> + cci_write(sensor->regmap, OV2680_REG_SENSOR_CTRL_0A, sensor_ctrl_0a, &ret);
> >>>> + cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_START, sensor->mode.h_start, &ret);
> >>>> + cci_write(sensor->regmap, OV2680_REG_VERTICAL_START, sensor->mode.v_start, &ret);
> >>>> + cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_END, sensor->mode.h_end, &ret);
> >>>> + cci_write(sensor->regmap, OV2680_REG_VERTICAL_END, sensor->mode.v_end, &ret);
> >>>> + cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_OUTPUT_SIZE,
> >>>> + sensor->mode.h_output_size, &ret);
> >>>> + cci_write(sensor->regmap, OV2680_REG_VERTICAL_OUTPUT_SIZE,
> >>>> + sensor->mode.v_output_size, &ret);
> >>>> + cci_write(sensor->regmap, OV2680_REG_TIMING_HTS, sensor->mode.hts, &ret);
> >>>> + cci_write(sensor->regmap, OV2680_REG_TIMING_VTS, sensor->mode.vts, &ret);
> >>>> + cci_write(sensor->regmap, OV2680_REG_ISP_X_WIN, 0, &ret);
> >>>> + cci_write(sensor->regmap, OV2680_REG_ISP_Y_WIN, 0, &ret);
> >>>> + cci_write(sensor->regmap, OV2680_REG_X_INC, inc, &ret);
> >>>> + cci_write(sensor->regmap, OV2680_REG_Y_INC, inc, &ret);
> >>>> + cci_write(sensor->regmap, OV2680_REG_X_WIN, sensor->mode.h_output_size, &ret);
> >>>> + cci_write(sensor->regmap, OV2680_REG_Y_WIN, sensor->mode.v_output_size, &ret);
> >>>> + cci_write(sensor->regmap, OV2680_REG_FORMAT1, fmt1, &ret);
> >>>> + cci_write(sensor->regmap, OV2680_REG_FORMAT2, fmt2, &ret);
> >>>
> >>> I know that &ret thingy was discussed before and Laurent is keen to
> >>> have this, but has anybody actually tested how bad or not at all the
> >>> code generation becomes?
> >>
> >> The cci_write function is in another module, so it won't be inlined
> >> and as such I don't see how the code generation can become bad. We
> >> loose all the if (ret) return ret; checks here, so the code should
> >> become smaller.
> >>
> >> Or are you worried about having to pass the 1 extra parameter ?
> >>
> >>> ...
> >>>
> >>>> + struct device *dev;
> >>>> + struct regmap *regmap;
> >>>
> >>> Isn't the same device associated with regmap? If so, one of them
> >>> probably duplicates the other.
> >>
> >> You are right, but the entire atomisp-ov2680.c file is going away real
> >> soon now. I plan to post a series to get drivers/media/i2c/ov2680.c
> >> ready to replace it later today.
> >>
> >> So I'm not even sure if this patch should be merged, as I mentioned in
> >> the cover letter this one is mostly here to illustrate use of the new
> >> helpers.
> >
> > How about porting drivers/media/i2c/imx290.c ? That's a real-life
> > example that can be merged, which is good to serve as an example
> > showcasing the API usage in mainline. It will also help ensuring that
> > these helpers are a good fit for drivers that already encode the
> > register width in the macros.
>
> I prefer to port over drivers which I can actually test,
> at least for now.
I can test it for you if you want :-)
> I already have converting ov5693.c (which also already has macros
> to encode to width) on my TODO list. I'll convert that for v2
> of the series.
>
> And I also have a conversion of the "main" drivers/media/i2c/ov2680.c
> ready.
>
> I'll post that conversion as part of my big main ov2680 changes series
> which I'll post in a couple of minutes (just need to write
> a cover letter and then its ready).
>
> >> I also wrote this patch to make porting recent atomisp-ov2680.c
> >> changes over to drivers/media/i2c/ov2680.c easier. Part of the series
> >> to get drivers/media/i2c/ov2680.c into shape is converting it to the
> >> new CCI helpers so that I could then easily copy over bits from the
> >> also converted atomisp-ov2680.c.
> >>
> >> So it might be interesting to still merge this so that the latest
> >> state of atomisp-ov2680.c is easier to compare to
> >> drivers/media/i2c/ov2680.c if the need arises.
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-07 15:58 ` Hans de Goede
@ 2023-06-07 16:14 ` Andy Shevchenko
2023-06-07 16:20 ` Hans de Goede
0 siblings, 1 reply; 38+ messages in thread
From: Andy Shevchenko @ 2023-06-07 16:14 UTC (permalink / raw)
To: Hans de Goede
Cc: Sakari Ailus, Laurent Pinchart, Mauro Carvalho Chehab,
Andy Shevchenko, linux-media
On Wed, Jun 7, 2023 at 6:58 PM Hans de Goede <hdegoede@redhat.com> wrote:
> On 6/7/23 17:40, Andy Shevchenko wrote:
> > On Wed, Jun 7, 2023 at 3:01 PM Sakari Ailus
> > <sakari.ailus@linux.intel.com> wrote:
> >> On Wed, Jun 07, 2023 at 10:40:34AM +0200, Hans de Goede wrote:
> >>> On 6/6/23 22:43, Andy Shevchenko wrote:
...
> >>> *val = __get_unaligned_be24(buf);
> >
> > __without double underscore prefix
>
> include/asm-generic/unaligned.h
>
> defines __get_unaligned_be24() and not get_unaligned_be24(), I guess because 24bit is not a standard register width.
Strange. Do you have some custom patches in the area?
https://elixir.bootlin.com/linux/v6.4-rc5/source/include/asm-generic/unaligned.h#L112
https://elixir.bootlin.com/linux/v6.4-rc5/source/include/asm-generic/unaligned.h#L90
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 2/3] media: atomisp: ov2680: Convert to new CCI register access helpers
2023-06-07 16:05 ` Laurent Pinchart
@ 2023-06-07 16:18 ` Hans de Goede
0 siblings, 0 replies; 38+ messages in thread
From: Hans de Goede @ 2023-06-07 16:18 UTC (permalink / raw)
To: Laurent Pinchart
Cc: Sakari Ailus, Mauro Carvalho Chehab, Andy Shevchenko, linux-media
Hi,
On 6/7/23 18:05, Laurent Pinchart wrote:
<snip>
>> -static struct ov2680_reg const ov2680_global_setting[] = {
>> +static const struct reg_sequence ov2680_global_setting[] = {
>
> Maybe you could move this to the .c file and also move the v4l2_cci.h
> inclusion to the .c file ? Or this can be done on top, as I assume
> you'll merge the .h and .c files at some point anyway.
Actually since there already is a (quite old, somewhat broken)
driver/media/i2c/ov2680.c and since the atomisp code can work
with standard v4l2 sensor drivers now I have been porting all
the recent work don on atomisp-ov2680.c to driver/media/i2c/ov2680.c
(to avoid causing regressions for potential users of that).
As I mentioned elsewhere in the thread:
"""
So I'm not even sure if this patch should be merged, as I mentioned in the cover letter this one is mostly here to illustrate use of the new helpers.
I also wrote this patch to make porting recent atomisp-ov2680.c changes over to drivers/media/i2c/ov2680.c easier. Part of the series to get drivers/media/i2c/ov2680.c into shape is converting it to the new CCI helpers so that I could then easily copy over bits from the also converted atomisp-ov2680.c.
So it might be interesting to still merge this so that the latest state of atomisp-ov2680.c is easier to compare to drivers/media/i2c/ov2680.c if the need arises.
"""
So rather then merging the .h into .c the next patch I expect to
write for the atomisp-ov2680.c driver is a patch to drop it from
the kernel since it will be superseded by a proper standard
v4l2 sensor driver then :)
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Thank you.
Regards,
Hans
>
>> /* MIPI PHY, 0x10 -> 0x1c enable bp_c_hs_en_lat and bp_d_hs_en_lat */
>> {0x3016, 0x1c},
>>
>> @@ -242,8 +181,6 @@ static struct ov2680_reg const ov2680_global_setting[] = {
>>
>> /* DPC THRE RATIO 0x04 (4) -> 0x00 (0) */
>> {0x5792, 0x00},
>> -
>> - {}
>> };
>>
>> #endif
>
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-07 16:14 ` Andy Shevchenko
@ 2023-06-07 16:20 ` Hans de Goede
2023-06-07 18:03 ` Andy Shevchenko
0 siblings, 1 reply; 38+ messages in thread
From: Hans de Goede @ 2023-06-07 16:20 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Sakari Ailus, Laurent Pinchart, Mauro Carvalho Chehab,
Andy Shevchenko, linux-media
Hi,
On 6/7/23 18:14, Andy Shevchenko wrote:
> On Wed, Jun 7, 2023 at 6:58 PM Hans de Goede <hdegoede@redhat.com> wrote:
>> On 6/7/23 17:40, Andy Shevchenko wrote:
>>> On Wed, Jun 7, 2023 at 3:01 PM Sakari Ailus
>>> <sakari.ailus@linux.intel.com> wrote:
>>>> On Wed, Jun 07, 2023 at 10:40:34AM +0200, Hans de Goede wrote:
>>>>> On 6/6/23 22:43, Andy Shevchenko wrote:
>
> ...
>
>>>>> *val = __get_unaligned_be24(buf);
>>>
>>> __without double underscore prefix
>>
>> include/asm-generic/unaligned.h
>>
>> defines __get_unaligned_be24() and not get_unaligned_be24(), I guess because 24bit is not a standard register width.
>
> Strange. Do you have some custom patches in the area?
>
> https://elixir.bootlin.com/linux/v6.4-rc5/source/include/asm-generic/unaligned.h#L112
> https://elixir.bootlin.com/linux/v6.4-rc5/source/include/asm-generic/unaligned.h#L90
No I do not have any custom patches in that area; and the wrapper you
point to is right there...
I somehow missed it, sorry.
So I will drop the __ as requested when adding the switch-case implementation for v2.
Regards,
Hans
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-07 16:20 ` Hans de Goede
@ 2023-06-07 18:03 ` Andy Shevchenko
0 siblings, 0 replies; 38+ messages in thread
From: Andy Shevchenko @ 2023-06-07 18:03 UTC (permalink / raw)
To: Hans de Goede
Cc: Sakari Ailus, Laurent Pinchart, Mauro Carvalho Chehab,
linux-media
On Wed, Jun 07, 2023 at 06:20:08PM +0200, Hans de Goede wrote:
> On 6/7/23 18:14, Andy Shevchenko wrote:
> > On Wed, Jun 7, 2023 at 6:58 PM Hans de Goede <hdegoede@redhat.com> wrote:
> >> On 6/7/23 17:40, Andy Shevchenko wrote:
> >>> On Wed, Jun 7, 2023 at 3:01 PM Sakari Ailus
> >>> <sakari.ailus@linux.intel.com> wrote:
> >>>> On Wed, Jun 07, 2023 at 10:40:34AM +0200, Hans de Goede wrote:
> >>>>> On 6/6/23 22:43, Andy Shevchenko wrote:
...
> >>>>> *val = __get_unaligned_be24(buf);
> >>>
> >>> __without double underscore prefix
> >>
> >> include/asm-generic/unaligned.h
> >>
> >> defines __get_unaligned_be24() and not get_unaligned_be24(), I guess because 24bit is not a standard register width.
> >
> > Strange. Do you have some custom patches in the area?
> >
> > https://elixir.bootlin.com/linux/v6.4-rc5/source/include/asm-generic/unaligned.h#L112
> > https://elixir.bootlin.com/linux/v6.4-rc5/source/include/asm-generic/unaligned.h#L90
>
> No I do not have any custom patches in that area; and the wrapper you
> point to is right there...
>
> I somehow missed it, sorry.
No problem.
> So I will drop the __ as requested when adding the switch-case implementation for v2.
Thank you!
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-06 16:58 ` [PATCH 1/3] " Hans de Goede
2023-06-06 20:43 ` Andy Shevchenko
2023-06-07 7:50 ` Sakari Ailus
@ 2023-06-07 18:18 ` Laurent Pinchart
2023-06-07 19:01 ` Hans de Goede
2 siblings, 1 reply; 38+ messages in thread
From: Laurent Pinchart @ 2023-06-07 18:18 UTC (permalink / raw)
To: Hans de Goede
Cc: Sakari Ailus, Mauro Carvalho Chehab, Andy Shevchenko, linux-media
Hi Hans,
Thank you for the patch.
On Tue, Jun 06, 2023 at 06:58:06PM +0200, Hans de Goede wrote:
> The CSI2 specification specifies a standard method to access camera sensor
> registers called "Camera Control Interface (CCI)".
>
> This uses either 8 or 16 bit (big-endian wire order) register addresses
> and supports 8, 16, 24 or 32 bit (big-endian wire order) register widths.
I think there are some sensors that also have 64-bit registers, but we
can deal with that later.
> Currently a lot of Linux camera sensor drivers all have their own custom
> helpers for this, often copy and pasted from other drivers.
>
> Add a set of generic helpers for this so that all sensor drivers can
> switch to a single common implementation.
>
> These helpers take an extra optional "int *err" function parameter,
> this can be used to chain a bunch of register accesses together with
> only a single error check at the end, rather then needing to error
> check each individual register access. The first failing call will
> set the contents of err to a non 0 value and all other calls will
> then become no-ops.
>
> Link: https://lore.kernel.org/linux-media/59aefa7f-7bf9-6736-6040-39551329cd0a@redhat.com/
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> ---
> Documentation/driver-api/media/v4l2-cci.rst | 5 +
> Documentation/driver-api/media/v4l2-core.rst | 1 +
> drivers/media/v4l2-core/Kconfig | 5 +
> drivers/media/v4l2-core/Makefile | 1 +
> drivers/media/v4l2-core/v4l2-cci.c | 142 +++++++++++++++++++
> include/media/v4l2-cci.h | 109 ++++++++++++++
> 6 files changed, 263 insertions(+)
> create mode 100644 Documentation/driver-api/media/v4l2-cci.rst
> create mode 100644 drivers/media/v4l2-core/v4l2-cci.c
> create mode 100644 include/media/v4l2-cci.h
>
> diff --git a/Documentation/driver-api/media/v4l2-cci.rst b/Documentation/driver-api/media/v4l2-cci.rst
> new file mode 100644
> index 000000000000..dd297a40ed20
> --- /dev/null
> +++ b/Documentation/driver-api/media/v4l2-cci.rst
> @@ -0,0 +1,5 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +V4L2 CCI kAPI
> +^^^^^^^^^^^^^
> +.. kernel-doc:: include/media/v4l2-cci.h
> diff --git a/Documentation/driver-api/media/v4l2-core.rst b/Documentation/driver-api/media/v4l2-core.rst
> index 1a8c4a5f256b..239045ecc8f4 100644
> --- a/Documentation/driver-api/media/v4l2-core.rst
> +++ b/Documentation/driver-api/media/v4l2-core.rst
> @@ -22,6 +22,7 @@ Video4Linux devices
> v4l2-mem2mem
> v4l2-async
> v4l2-fwnode
> + v4l2-cci
> v4l2-rect
> v4l2-tuner
> v4l2-common
> diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
> index 348559bc2468..523ba243261d 100644
> --- a/drivers/media/v4l2-core/Kconfig
> +++ b/drivers/media/v4l2-core/Kconfig
> @@ -74,6 +74,11 @@ config V4L2_FWNODE
> config V4L2_ASYNC
> tristate
>
> +config V4L2_CCI
> + tristate
> + depends on I2C
> + select REGMAP_I2C
> +
> # Used by drivers that need Videobuf modules
> config VIDEOBUF_GEN
> tristate
> diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
> index 41d91bd10cf2..be2551705755 100644
> --- a/drivers/media/v4l2-core/Makefile
> +++ b/drivers/media/v4l2-core/Makefile
> @@ -25,6 +25,7 @@ videodev-$(CONFIG_VIDEO_V4L2_I2C) += v4l2-i2c.o
> # (e. g. LC_ALL=C sort Makefile)
>
> obj-$(CONFIG_V4L2_ASYNC) += v4l2-async.o
> +obj-$(CONFIG_V4L2_CCI) += v4l2-cci.o
> obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash-led-class.o
> obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o
> obj-$(CONFIG_V4L2_H264) += v4l2-h264.o
> diff --git a/drivers/media/v4l2-core/v4l2-cci.c b/drivers/media/v4l2-core/v4l2-cci.c
> new file mode 100644
> index 000000000000..21207d137dbe
> --- /dev/null
> +++ b/drivers/media/v4l2-core/v4l2-cci.c
> @@ -0,0 +1,142 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * MIPI Camera Control Interface (CCI) register access helpers.
> + *
> + * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/dev_printk.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +
> +#include <media/v4l2-cci.h>
> +
> +int cci_read(struct regmap *map, u32 reg, u32 *val, int *err)
> +{
> + int i, len, ret;
> + u8 buf[4];
> +
> + if (err && *err)
> + return *err;
> +
> + /* Set len to register width in bytes */
> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
> + reg &= CCI_REG_ADDR_MASK;
> +
> + ret = regmap_bulk_read(map, reg, buf, len);
> + if (ret) {
> + dev_err(regmap_get_device(map), "Error reading reg 0x%4x: %d\n", reg, ret);
> + if (err)
> + *err = ret;
> +
> + return ret;
> + }
> +
> + *val = 0;
> + for (i = 0; i < len; i++) {
> + *val <<= 8;
> + *val |= buf[i];
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(cci_read);
> +
> +int cci_write(struct regmap *map, u32 reg, u32 val, int *err)
> +{
> + int i, len, ret;
> + u8 buf[4];
> +
> + if (err && *err)
> + return *err;
> +
> + /* Set len to register width in bytes */
> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
> + reg &= CCI_REG_ADDR_MASK;
> +
> + for (i = 0; i < len; i++) {
> + buf[len - i - 1] = val & 0xff;
> + val >>= 8;
> + }
> +
> + ret = regmap_bulk_write(map, reg, buf, len);
> + if (ret) {
> + dev_err(regmap_get_device(map), "Error writing reg 0x%4x: %d\n", reg, ret);
> + if (err)
> + *err = ret;
> + }
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(cci_write);
> +
> +int cci_update_bits(struct regmap *map, u32 reg, u32 mask, u32 val, int *err)
> +{
> + int width, ret;
> + u32 readval;
> +
> + if (err && *err)
> + return *err;
> +
> + /*
> + * For single byte updates use regmap_update_bits(), this uses
> + * the regmap-lock to protect against other read-modify-writes racing.
> + */
> + width = (reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT;
> + if (width == cci_reg_8) {
> + reg &= CCI_REG_ADDR_MASK;
> + ret = regmap_update_bits(map, reg, mask, val);
> + if (ret) {
> + dev_err(regmap_get_device(map), "Error updating reg 0x%4x: %d\n", reg, ret);
> + if (err)
> + *err = ret;
> + }
> +
> + return ret;
> + }
> +
> + ret = cci_read(map, reg, &readval, err);
> + if (ret)
> + return ret;
> +
> + val = (readval & ~mask) | (val & mask);
> +
> + return cci_write(map, reg, val, err);
Unless I'm mistaken, the regmap cache isn't used. This makes update
operations fairly costly due to the read. Could that be improved ?
> +}
> +EXPORT_SYMBOL_GPL(cci_update_bits);
> +
> +int cci_multi_reg_write(struct regmap *map, const struct reg_sequence *regs, int num_regs, int *err)
> +{
> + int i, ret;
> +
> + if (err && *err)
> + return *err;
> +
> + for (i = 0; i < num_regs; i++) {
> + ret = cci_write(map, regs[i].reg, regs[i].def, err);
> + if (ret)
> + return ret;
> +
> + if (regs[i].delay_us)
> + fsleep(regs[i].delay_us);
Do you have an immediate need for this ? If not, I'd drop support for
the delay, and add it later when and if needed. It will be easier to
discuss the API and use cases with a real user.
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(cci_multi_reg_write);
> +
> +struct regmap *cci_regmap_init_i2c(struct i2c_client *client, int reg_addr_bits)
> +{
> + struct regmap_config config = {
> + .reg_bits = reg_addr_bits,
> + .val_bits = 8,
> + .reg_format_endian = REGMAP_ENDIAN_BIG,
> + };
> +
> + return devm_regmap_init_i2c(client, &config);
> +}
> +EXPORT_SYMBOL_GPL(cci_regmap_init_i2c);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
> diff --git a/include/media/v4l2-cci.h b/include/media/v4l2-cci.h
> new file mode 100644
> index 000000000000..69b8a7c4a013
> --- /dev/null
> +++ b/include/media/v4l2-cci.h
> @@ -0,0 +1,109 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * MIPI Camera Control Interface (CCI) register access helpers.
> + *
> + * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
> + */
> +#ifndef _V4L2_CCI_H
> +#define _V4L2_CCI_H
> +
> +#include <linux/regmap.h>
> +#include <linux/types.h>
> +
> +/*
> + * Note cci_reg_8 deliberately is 0, not 1, so that raw
> + * (not wrapped in a CCI_REG*() macro) register addresses
> + * do 8 bit wide accesses. This allows unchanged use of register
> + * initialization lists of raw address, value pairs which only
> + * do 8 bit width accesses. Which makes porting drivers easier.
It does, but at the same time, it prevents catching errors caused by
incorrect register macros. I'm tempted to consider that catching those
errors is more important.
> + */
> +enum cci_reg_type {
> + cci_reg_8 = 0,
> + cci_reg_16,
> + cci_reg_24,
> + cci_reg_32,
> +};
> +
> +/*
> + * Macros to define register address with the register width encoded
> + * into the higher bits. CCI_REG8() is a no-op so its use is optional.
Even if it's a no-op I'd prefer making its use mandatory. It makes
driver code more explicit, and eases catching issues during review.
> + */
> +#define CCI_REG_ADDR_MASK GENMASK(15, 0)
> +#define CCI_REG_WIDTH_SHIFT 16
> +#define CCI_REG_WIDTH_MASK GENMASK(17, 16)
> +
> +#define CCI_REG8(x) ((cci_reg_8 << CCI_REG_WIDTH_SHIFT) | (x))
> +#define CCI_REG16(x) ((cci_reg_16 << CCI_REG_WIDTH_SHIFT) | (x))
> +#define CCI_REG24(x) ((cci_reg_24 << CCI_REG_WIDTH_SHIFT) | (x))
> +#define CCI_REG32(x) ((cci_reg_32 << CCI_REG_WIDTH_SHIFT) | (x))
> +
> +/**
> + * cci_read() - Read a value from a single CCI register
> + *
> + * @map: Register map to write to
s/write to/read from/ ?
> + * @reg: Register address to write, use CCI_REG#() macros to encode reg width
Same.
> + * @val: Pointer to store read value
> + * @err: optional pointer to store errors, if a previous error is set the write will be skipped
Line wrap ?
> + *
> + * Return: %0 on success or a negative error code on failure.
> + */
> +int cci_read(struct regmap *map, u32 reg, u32 *val, int *err);
> +
> +/**
> + * cci_write() - Write a value to a single CCI register
> + *
> + * @map: Register map to write to
> + * @reg: Register address to write, use CCI_REG#() macros to encode reg width
> + * @val: Value to be written
> + * @err: optional pointer to store errors, if a previous error is set the write will be skipped
> + *
> + * Return: %0 on success or a negative error code on failure.
> + */
> +int cci_write(struct regmap *map, u32 reg, u32 val, int *err);
> +
> +/**
> + * cci_update_bits() - Perform a read/modify/write cycle on a single CCI register
> + *
> + * @map: Register map to write to
> + * @reg: Register address to write, use CCI_REG#() macros to encode reg width
> + * @mask: Bitmask to change
> + * @val: New value for bitmask
> + * @err: optional pointer to store errors, if a previous error is set the update will be skipped
> + *
> + * For 8 bit width registers this is guaranteed to be atomic wrt other
> + * cci_*() register access functions. For multi-byte width registers
> + * atomicity is NOT guaranteed.
> + *
> + * Return: %0 on success or a negative error code on failure.
> + */
> +int cci_update_bits(struct regmap *map, u32 reg, u32 mask, u32 val, int *err);
> +
> +/**
> + * cci_multi_reg_write() - Write multiple registers to the device
> + *
> + * @map: Register map to write to
> + * @regs: Array of structures containing register-address, value pairs to be written
> + * register-addresses use CCI_REG#() macros to encode reg width
> + * @num_regs: Number of registers to write
> + * @err: optional pointer to store errors, if a previous error is set the update will be skipped
> + *
> + * Write multiple registers to the device where the set of register, value
> + * pairs are supplied in any order, possibly not all in a single range.
> + *
> + * Return: %0 on success or a negative error code on failure.
> + */
> +int cci_multi_reg_write(struct regmap *map, const struct reg_sequence *regs, int num_regs, int *err);
> +
> +/**
> + * cci_regmap_init_i2c() - Create regmap to use with cci_*() register access functions
> + *
> + * @client: i2c_client to create the regmap for
> + * @reg_addr_bits: register address width to use (8 or 16)
> + *
> + * Note the memory for the created regmap is devm() managed, tied to the client.
> + *
> + * Return: %0 on success or a negative error code on failure.
> + */
> +struct regmap *cci_regmap_init_i2c(struct i2c_client *client, int reg_addr_bits);
> +
> +#endif
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-07 18:18 ` Laurent Pinchart
@ 2023-06-07 19:01 ` Hans de Goede
2023-06-07 20:07 ` Andy Shevchenko
2023-06-08 10:27 ` Laurent Pinchart
0 siblings, 2 replies; 38+ messages in thread
From: Hans de Goede @ 2023-06-07 19:01 UTC (permalink / raw)
To: Laurent Pinchart
Cc: Sakari Ailus, Mauro Carvalho Chehab, Andy Shevchenko, linux-media
Hi Laurent,
On 6/7/23 20:18, Laurent Pinchart wrote:
> Hi Hans,
>
> Thank you for the patch.
>
> On Tue, Jun 06, 2023 at 06:58:06PM +0200, Hans de Goede wrote:
>> The CSI2 specification specifies a standard method to access camera sensor
>> registers called "Camera Control Interface (CCI)".
>>
>> This uses either 8 or 16 bit (big-endian wire order) register addresses
>> and supports 8, 16, 24 or 32 bit (big-endian wire order) register widths.
>
> I think there are some sensors that also have 64-bit registers, but we
> can deal with that later.
>
>> Currently a lot of Linux camera sensor drivers all have their own custom
>> helpers for this, often copy and pasted from other drivers.
>>
>> Add a set of generic helpers for this so that all sensor drivers can
>> switch to a single common implementation.
>>
>> These helpers take an extra optional "int *err" function parameter,
>> this can be used to chain a bunch of register accesses together with
>> only a single error check at the end, rather then needing to error
>> check each individual register access. The first failing call will
>> set the contents of err to a non 0 value and all other calls will
>> then become no-ops.
>>
>> Link: https://lore.kernel.org/linux-media/59aefa7f-7bf9-6736-6040-39551329cd0a@redhat.com/
>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>> ---
>> Documentation/driver-api/media/v4l2-cci.rst | 5 +
>> Documentation/driver-api/media/v4l2-core.rst | 1 +
>> drivers/media/v4l2-core/Kconfig | 5 +
>> drivers/media/v4l2-core/Makefile | 1 +
>> drivers/media/v4l2-core/v4l2-cci.c | 142 +++++++++++++++++++
>> include/media/v4l2-cci.h | 109 ++++++++++++++
>> 6 files changed, 263 insertions(+)
>> create mode 100644 Documentation/driver-api/media/v4l2-cci.rst
>> create mode 100644 drivers/media/v4l2-core/v4l2-cci.c
>> create mode 100644 include/media/v4l2-cci.h
>>
>> diff --git a/Documentation/driver-api/media/v4l2-cci.rst b/Documentation/driver-api/media/v4l2-cci.rst
>> new file mode 100644
>> index 000000000000..dd297a40ed20
>> --- /dev/null
>> +++ b/Documentation/driver-api/media/v4l2-cci.rst
>> @@ -0,0 +1,5 @@
>> +.. SPDX-License-Identifier: GPL-2.0
>> +
>> +V4L2 CCI kAPI
>> +^^^^^^^^^^^^^
>> +.. kernel-doc:: include/media/v4l2-cci.h
>> diff --git a/Documentation/driver-api/media/v4l2-core.rst b/Documentation/driver-api/media/v4l2-core.rst
>> index 1a8c4a5f256b..239045ecc8f4 100644
>> --- a/Documentation/driver-api/media/v4l2-core.rst
>> +++ b/Documentation/driver-api/media/v4l2-core.rst
>> @@ -22,6 +22,7 @@ Video4Linux devices
>> v4l2-mem2mem
>> v4l2-async
>> v4l2-fwnode
>> + v4l2-cci
>> v4l2-rect
>> v4l2-tuner
>> v4l2-common
>> diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
>> index 348559bc2468..523ba243261d 100644
>> --- a/drivers/media/v4l2-core/Kconfig
>> +++ b/drivers/media/v4l2-core/Kconfig
>> @@ -74,6 +74,11 @@ config V4L2_FWNODE
>> config V4L2_ASYNC
>> tristate
>>
>> +config V4L2_CCI
>> + tristate
>> + depends on I2C
>> + select REGMAP_I2C
>> +
>> # Used by drivers that need Videobuf modules
>> config VIDEOBUF_GEN
>> tristate
>> diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
>> index 41d91bd10cf2..be2551705755 100644
>> --- a/drivers/media/v4l2-core/Makefile
>> +++ b/drivers/media/v4l2-core/Makefile
>> @@ -25,6 +25,7 @@ videodev-$(CONFIG_VIDEO_V4L2_I2C) += v4l2-i2c.o
>> # (e. g. LC_ALL=C sort Makefile)
>>
>> obj-$(CONFIG_V4L2_ASYNC) += v4l2-async.o
>> +obj-$(CONFIG_V4L2_CCI) += v4l2-cci.o
>> obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash-led-class.o
>> obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o
>> obj-$(CONFIG_V4L2_H264) += v4l2-h264.o
>> diff --git a/drivers/media/v4l2-core/v4l2-cci.c b/drivers/media/v4l2-core/v4l2-cci.c
>> new file mode 100644
>> index 000000000000..21207d137dbe
>> --- /dev/null
>> +++ b/drivers/media/v4l2-core/v4l2-cci.c
>> @@ -0,0 +1,142 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * MIPI Camera Control Interface (CCI) register access helpers.
>> + *
>> + * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
>> + */
>> +
>> +#include <linux/delay.h>
>> +#include <linux/dev_printk.h>
>> +#include <linux/module.h>
>> +#include <linux/regmap.h>
>> +
>> +#include <media/v4l2-cci.h>
>> +
>> +int cci_read(struct regmap *map, u32 reg, u32 *val, int *err)
>> +{
>> + int i, len, ret;
>> + u8 buf[4];
>> +
>> + if (err && *err)
>> + return *err;
>> +
>> + /* Set len to register width in bytes */
>> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
>> + reg &= CCI_REG_ADDR_MASK;
>> +
>> + ret = regmap_bulk_read(map, reg, buf, len);
>> + if (ret) {
>> + dev_err(regmap_get_device(map), "Error reading reg 0x%4x: %d\n", reg, ret);
>> + if (err)
>> + *err = ret;
>> +
>> + return ret;
>> + }
>> +
>> + *val = 0;
>> + for (i = 0; i < len; i++) {
>> + *val <<= 8;
>> + *val |= buf[i];
>> + }
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(cci_read);
>> +
>> +int cci_write(struct regmap *map, u32 reg, u32 val, int *err)
>> +{
>> + int i, len, ret;
>> + u8 buf[4];
>> +
>> + if (err && *err)
>> + return *err;
>> +
>> + /* Set len to register width in bytes */
>> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
>> + reg &= CCI_REG_ADDR_MASK;
>> +
>> + for (i = 0; i < len; i++) {
>> + buf[len - i - 1] = val & 0xff;
>> + val >>= 8;
>> + }
>> +
>> + ret = regmap_bulk_write(map, reg, buf, len);
>> + if (ret) {
>> + dev_err(regmap_get_device(map), "Error writing reg 0x%4x: %d\n", reg, ret);
>> + if (err)
>> + *err = ret;
>> + }
>> +
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(cci_write);
>> +
>> +int cci_update_bits(struct regmap *map, u32 reg, u32 mask, u32 val, int *err)
>> +{
>> + int width, ret;
>> + u32 readval;
>> +
>> + if (err && *err)
>> + return *err;
>> +
>> + /*
>> + * For single byte updates use regmap_update_bits(), this uses
>> + * the regmap-lock to protect against other read-modify-writes racing.
>> + */
>> + width = (reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT;
>> + if (width == cci_reg_8) {
>> + reg &= CCI_REG_ADDR_MASK;
>> + ret = regmap_update_bits(map, reg, mask, val);
>> + if (ret) {
>> + dev_err(regmap_get_device(map), "Error updating reg 0x%4x: %d\n", reg, ret);
>> + if (err)
>> + *err = ret;
>> + }
>> +
>> + return ret;
>> + }
>> +
>> + ret = cci_read(map, reg, &readval, err);
>> + if (ret)
>> + return ret;
>> +
>> + val = (readval & ~mask) | (val & mask);
>> +
>> + return cci_write(map, reg, val, err);
>
> Unless I'm mistaken, the regmap cache isn't used. This makes update
> operations fairly costly due to the read. Could that be improved ?
The problem is that some registers may be volatile,
think e.g. expsoure on a sensor where auto-exposure is supported.
So normally drivers which want to use regmap caching, also
provide a whole bunch of tables describing the registers
(lists of volatile + list of writable + list of readable
registers).
So enabling caching is not trivial. I think that it would be best
for drivers which want that to supply their own regmap_config config
and directly call devm_regmap_init_i2c() if they then use
the resulting regmaps with the existing cci_* helpers then caching
will be used automatically.
>
>> +}
>> +EXPORT_SYMBOL_GPL(cci_update_bits);
>> +
>> +int cci_multi_reg_write(struct regmap *map, const struct reg_sequence *regs, int num_regs, int *err)
>> +{
>> + int i, ret;
>> +
>> + if (err && *err)
>> + return *err;
>> +
>> + for (i = 0; i < num_regs; i++) {
>> + ret = cci_write(map, regs[i].reg, regs[i].def, err);
>> + if (ret)
>> + return ret;
>> +
>> + if (regs[i].delay_us)
>> + fsleep(regs[i].delay_us);
>
> Do you have an immediate need for this ? If not, I'd drop support for
> the delay, and add it later when and if needed. It will be easier to
> discuss the API and use cases with a real user.
This is a 1:1 mirror of regmap_multi_reg_write() note this uses
the existing struct reg_sequence delay_us field and the:
if (regs[i].delay_us)
fsleep(regs[i].delay_us);
is copied from the implementation of regmap_multi_reg_write()
>
>> + }
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(cci_multi_reg_write);
>> +
>> +struct regmap *cci_regmap_init_i2c(struct i2c_client *client, int reg_addr_bits)
>> +{
>> + struct regmap_config config = {
>> + .reg_bits = reg_addr_bits,
>> + .val_bits = 8,
>> + .reg_format_endian = REGMAP_ENDIAN_BIG,
>> + };
>> +
>> + return devm_regmap_init_i2c(client, &config);
>> +}
>> +EXPORT_SYMBOL_GPL(cci_regmap_init_i2c);
>> +
>> +MODULE_LICENSE("GPL");
>> +MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
>> diff --git a/include/media/v4l2-cci.h b/include/media/v4l2-cci.h
>> new file mode 100644
>> index 000000000000..69b8a7c4a013
>> --- /dev/null
>> +++ b/include/media/v4l2-cci.h
>> @@ -0,0 +1,109 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * MIPI Camera Control Interface (CCI) register access helpers.
>> + *
>> + * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
>> + */
>> +#ifndef _V4L2_CCI_H
>> +#define _V4L2_CCI_H
>> +
>> +#include <linux/regmap.h>
>> +#include <linux/types.h>
>> +
>> +/*
>> + * Note cci_reg_8 deliberately is 0, not 1, so that raw
>> + * (not wrapped in a CCI_REG*() macro) register addresses
>> + * do 8 bit wide accesses. This allows unchanged use of register
>> + * initialization lists of raw address, value pairs which only
>> + * do 8 bit width accesses. Which makes porting drivers easier.
>
> It does, but at the same time, it prevents catching errors caused by
> incorrect register macros. I'm tempted to consider that catching those
> errors is more important.
>
>> + */
>> +enum cci_reg_type {
>> + cci_reg_8 = 0,
>> + cci_reg_16,
>> + cci_reg_24,
>> + cci_reg_32,
>> +};
>> +
>> +/*
>> + * Macros to define register address with the register width encoded
>> + * into the higher bits. CCI_REG8() is a no-op so its use is optional.
>
> Even if it's a no-op I'd prefer making its use mandatory. It makes
> driver code more explicit, and eases catching issues during review.
The problem is that almost all sensor drivers contain long list
of register-address, -val pairs which they send to their own custom
regmap_multi_reg_write()
See e.g. the drivers/media/i2c/imx219.c (to stick with the imx
theme from your imx290 request) this has a lot of quite long
struct imx219_reg arrays with raw initializers.
Often some or all of these registers in such list are
undocumented (if we have access to a datasheet at all),
so we simply don't know the register width.
So arguably adding CCI_REG8(x) around all the addresses
here is wrong, since this suggests we know the register
width.
With the current proposal to have 0 mean both unset and 8bit
width this kinda register lists just work and converting
the driver becomes just a matter of replacing e.g.
imx219_write_regs() with cci_multi_reg_write().
Where as otherwise we would need to add CCI_REG8(x)
around the addresses which:
a) Suggests we actually know the register width which
we often do not know at all
b) causes a ton of needless churn
so I would very much prefer to keep this as as and
allow unmarked register addresses.
As for the CCI_REG8(x) being useful as an annotation
during review you are of course free to enforce its
use during review. And note that I did use it for
all the OV2680_REG_FOO defines in both ov2680 conversions.
I do agree enforcing its use makes sense for individual
register address defines. The reason to make it optional
and the place where I want it to be optional is for
the array of raw register-addr + initializer-val pairs
case.
>> + */
>> +#define CCI_REG_ADDR_MASK GENMASK(15, 0)
>> +#define CCI_REG_WIDTH_SHIFT 16
>> +#define CCI_REG_WIDTH_MASK GENMASK(17, 16)
>> +
>> +#define CCI_REG8(x) ((cci_reg_8 << CCI_REG_WIDTH_SHIFT) | (x))
>> +#define CCI_REG16(x) ((cci_reg_16 << CCI_REG_WIDTH_SHIFT) | (x))
>> +#define CCI_REG24(x) ((cci_reg_24 << CCI_REG_WIDTH_SHIFT) | (x))
>> +#define CCI_REG32(x) ((cci_reg_32 << CCI_REG_WIDTH_SHIFT) | (x))
>> +
>> +/**
>> + * cci_read() - Read a value from a single CCI register
>> + *
>> + * @map: Register map to write to
>
> s/write to/read from/ ?
Ack, will fix for v2.
>> + * @reg: Register address to write, use CCI_REG#() macros to encode reg width
>
> Same.
Ack.
>> + * @val: Pointer to store read value
>> + * @err: optional pointer to store errors, if a previous error is set the write will be skipped
>
> Line wrap ?
Ack.
Regards,
Hans
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-07 19:01 ` Hans de Goede
@ 2023-06-07 20:07 ` Andy Shevchenko
2023-06-08 8:33 ` Hans de Goede
2023-06-08 8:33 ` Sakari Ailus
2023-06-08 10:27 ` Laurent Pinchart
1 sibling, 2 replies; 38+ messages in thread
From: Andy Shevchenko @ 2023-06-07 20:07 UTC (permalink / raw)
To: Hans de Goede
Cc: Laurent Pinchart, Sakari Ailus, Mauro Carvalho Chehab,
linux-media
On Wed, Jun 07, 2023 at 09:01:40PM +0200, Hans de Goede wrote:
> On 6/7/23 20:18, Laurent Pinchart wrote:
> > On Tue, Jun 06, 2023 at 06:58:06PM +0200, Hans de Goede wrote:
...
> >> + if (regs[i].delay_us)
> >> + fsleep(regs[i].delay_us);
> >
> > Do you have an immediate need for this ? If not, I'd drop support for
> > the delay, and add it later when and if needed. It will be easier to
> > discuss the API and use cases with a real user.
>
> This is a 1:1 mirror of regmap_multi_reg_write() note this uses
> the existing struct reg_sequence delay_us field and the:
>
> if (regs[i].delay_us)
> fsleep(regs[i].delay_us);
>
> is copied from the implementation of regmap_multi_reg_write()
Reading this I'm wondering if we can actually implement a regmap-cci inside
drivers/base/regmap and use it. It might be that this is impossible, but can
save us from repeating existing code I think.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-07 20:07 ` Andy Shevchenko
@ 2023-06-08 8:33 ` Hans de Goede
2023-06-08 8:33 ` Sakari Ailus
1 sibling, 0 replies; 38+ messages in thread
From: Hans de Goede @ 2023-06-08 8:33 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Laurent Pinchart, Sakari Ailus, Mauro Carvalho Chehab,
linux-media
Hi Andy,
On 6/7/23 22:07, Andy Shevchenko wrote:
> On Wed, Jun 07, 2023 at 09:01:40PM +0200, Hans de Goede wrote:
>> On 6/7/23 20:18, Laurent Pinchart wrote:
>>> On Tue, Jun 06, 2023 at 06:58:06PM +0200, Hans de Goede wrote:
>
> ...
>
>>>> + if (regs[i].delay_us)
>>>> + fsleep(regs[i].delay_us);
>>>
>>> Do you have an immediate need for this ? If not, I'd drop support for
>>> the delay, and add it later when and if needed. It will be easier to
>>> discuss the API and use cases with a real user.
>>
>> This is a 1:1 mirror of regmap_multi_reg_write() note this uses
>> the existing struct reg_sequence delay_us field and the:
>>
>> if (regs[i].delay_us)
>> fsleep(regs[i].delay_us);
>>
>> is copied from the implementation of regmap_multi_reg_write()
>
> Reading this I'm wondering if we can actually implement a regmap-cci inside
> drivers/base/regmap and use it. It might be that this is impossible, but can
> save us from repeating existing code I think.
Someone (you I believe?) already suggested this when discussing replacing
the ov_16bit_addr_reg_helpers.h . This is not possible because regmap
assumes a fixed register width for the device and this assumption is
all over the place in regmap.
The whole purpose of the CCI helpers is to provide a thin layer on top
to deal with different register widths while delegating everything else
to regmap.
Regards,
Hans
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-07 20:07 ` Andy Shevchenko
2023-06-08 8:33 ` Hans de Goede
@ 2023-06-08 8:33 ` Sakari Ailus
2023-06-08 10:24 ` Andy Shevchenko
1 sibling, 1 reply; 38+ messages in thread
From: Sakari Ailus @ 2023-06-08 8:33 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Hans de Goede, Laurent Pinchart, Mauro Carvalho Chehab,
linux-media
Hi Andy,
On Wed, Jun 07, 2023 at 11:07:28PM +0300, Andy Shevchenko wrote:
> On Wed, Jun 07, 2023 at 09:01:40PM +0200, Hans de Goede wrote:
> > On 6/7/23 20:18, Laurent Pinchart wrote:
> > > On Tue, Jun 06, 2023 at 06:58:06PM +0200, Hans de Goede wrote:
>
> ...
>
> > >> + if (regs[i].delay_us)
> > >> + fsleep(regs[i].delay_us);
> > >
> > > Do you have an immediate need for this ? If not, I'd drop support for
> > > the delay, and add it later when and if needed. It will be easier to
> > > discuss the API and use cases with a real user.
> >
> > This is a 1:1 mirror of regmap_multi_reg_write() note this uses
> > the existing struct reg_sequence delay_us field and the:
> >
> > if (regs[i].delay_us)
> > fsleep(regs[i].delay_us);
> >
> > is copied from the implementation of regmap_multi_reg_write()
>
> Reading this I'm wondering if we can actually implement a regmap-cci inside
> drivers/base/regmap and use it. It might be that this is impossible, but can
> save us from repeating existing code I think.
I very much prefer this set over trying to bury equivalent functionality
in regmap. CCI is quite unlike what regmap is intended for, due to
its variable width registers and that CCI isn't a bus itself (but on top of
the bus specification itself).
--
Kind regards,
Sakari Ailus
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-08 8:33 ` Sakari Ailus
@ 2023-06-08 10:24 ` Andy Shevchenko
0 siblings, 0 replies; 38+ messages in thread
From: Andy Shevchenko @ 2023-06-08 10:24 UTC (permalink / raw)
To: Sakari Ailus
Cc: Andy Shevchenko, Hans de Goede, Laurent Pinchart,
Mauro Carvalho Chehab, linux-media
On Thu, Jun 8, 2023 at 11:33 AM Sakari Ailus
<sakari.ailus@linux.intel.com> wrote:
> On Wed, Jun 07, 2023 at 11:07:28PM +0300, Andy Shevchenko wrote:
> > On Wed, Jun 07, 2023 at 09:01:40PM +0200, Hans de Goede wrote:
> > > On 6/7/23 20:18, Laurent Pinchart wrote:
> > > > On Tue, Jun 06, 2023 at 06:58:06PM +0200, Hans de Goede wrote:
...
> > > if (regs[i].delay_us)
> > > fsleep(regs[i].delay_us);
> > >
> > > is copied from the implementation of regmap_multi_reg_write()
> >
> > Reading this I'm wondering if we can actually implement a regmap-cci inside
> > drivers/base/regmap and use it. It might be that this is impossible, but can
> > save us from repeating existing code I think.
>
> I very much prefer this set over trying to bury equivalent functionality
> in regmap. CCI is quite unlike what regmap is intended for, due to
> its variable width registers and that CCI isn't a bus itself (but on top of
> the bus specification itself).
Oh, okay, no objections!
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-07 19:01 ` Hans de Goede
2023-06-07 20:07 ` Andy Shevchenko
@ 2023-06-08 10:27 ` Laurent Pinchart
2023-06-08 11:01 ` Sakari Ailus
2023-06-12 13:48 ` Hans de Goede
1 sibling, 2 replies; 38+ messages in thread
From: Laurent Pinchart @ 2023-06-08 10:27 UTC (permalink / raw)
To: Hans de Goede
Cc: Sakari Ailus, Mauro Carvalho Chehab, Andy Shevchenko, linux-media
Hi Hans,
On Wed, Jun 07, 2023 at 09:01:40PM +0200, Hans de Goede wrote:
> On 6/7/23 20:18, Laurent Pinchart wrote:
> > On Tue, Jun 06, 2023 at 06:58:06PM +0200, Hans de Goede wrote:
> >> The CSI2 specification specifies a standard method to access camera sensor
> >> registers called "Camera Control Interface (CCI)".
> >>
> >> This uses either 8 or 16 bit (big-endian wire order) register addresses
> >> and supports 8, 16, 24 or 32 bit (big-endian wire order) register widths.
> >
> > I think there are some sensors that also have 64-bit registers, but we
> > can deal with that later.
> >
> >> Currently a lot of Linux camera sensor drivers all have their own custom
> >> helpers for this, often copy and pasted from other drivers.
> >>
> >> Add a set of generic helpers for this so that all sensor drivers can
> >> switch to a single common implementation.
> >>
> >> These helpers take an extra optional "int *err" function parameter,
> >> this can be used to chain a bunch of register accesses together with
> >> only a single error check at the end, rather then needing to error
> >> check each individual register access. The first failing call will
> >> set the contents of err to a non 0 value and all other calls will
> >> then become no-ops.
> >>
> >> Link: https://lore.kernel.org/linux-media/59aefa7f-7bf9-6736-6040-39551329cd0a@redhat.com/
> >> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> >> ---
> >> Documentation/driver-api/media/v4l2-cci.rst | 5 +
> >> Documentation/driver-api/media/v4l2-core.rst | 1 +
> >> drivers/media/v4l2-core/Kconfig | 5 +
> >> drivers/media/v4l2-core/Makefile | 1 +
> >> drivers/media/v4l2-core/v4l2-cci.c | 142 +++++++++++++++++++
> >> include/media/v4l2-cci.h | 109 ++++++++++++++
> >> 6 files changed, 263 insertions(+)
> >> create mode 100644 Documentation/driver-api/media/v4l2-cci.rst
> >> create mode 100644 drivers/media/v4l2-core/v4l2-cci.c
> >> create mode 100644 include/media/v4l2-cci.h
> >>
> >> diff --git a/Documentation/driver-api/media/v4l2-cci.rst b/Documentation/driver-api/media/v4l2-cci.rst
> >> new file mode 100644
> >> index 000000000000..dd297a40ed20
> >> --- /dev/null
> >> +++ b/Documentation/driver-api/media/v4l2-cci.rst
> >> @@ -0,0 +1,5 @@
> >> +.. SPDX-License-Identifier: GPL-2.0
> >> +
> >> +V4L2 CCI kAPI
> >> +^^^^^^^^^^^^^
> >> +.. kernel-doc:: include/media/v4l2-cci.h
> >> diff --git a/Documentation/driver-api/media/v4l2-core.rst b/Documentation/driver-api/media/v4l2-core.rst
> >> index 1a8c4a5f256b..239045ecc8f4 100644
> >> --- a/Documentation/driver-api/media/v4l2-core.rst
> >> +++ b/Documentation/driver-api/media/v4l2-core.rst
> >> @@ -22,6 +22,7 @@ Video4Linux devices
> >> v4l2-mem2mem
> >> v4l2-async
> >> v4l2-fwnode
> >> + v4l2-cci
> >> v4l2-rect
> >> v4l2-tuner
> >> v4l2-common
> >> diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
> >> index 348559bc2468..523ba243261d 100644
> >> --- a/drivers/media/v4l2-core/Kconfig
> >> +++ b/drivers/media/v4l2-core/Kconfig
> >> @@ -74,6 +74,11 @@ config V4L2_FWNODE
> >> config V4L2_ASYNC
> >> tristate
> >>
> >> +config V4L2_CCI
> >> + tristate
> >> + depends on I2C
> >> + select REGMAP_I2C
> >> +
> >> # Used by drivers that need Videobuf modules
> >> config VIDEOBUF_GEN
> >> tristate
> >> diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
> >> index 41d91bd10cf2..be2551705755 100644
> >> --- a/drivers/media/v4l2-core/Makefile
> >> +++ b/drivers/media/v4l2-core/Makefile
> >> @@ -25,6 +25,7 @@ videodev-$(CONFIG_VIDEO_V4L2_I2C) += v4l2-i2c.o
> >> # (e. g. LC_ALL=C sort Makefile)
> >>
> >> obj-$(CONFIG_V4L2_ASYNC) += v4l2-async.o
> >> +obj-$(CONFIG_V4L2_CCI) += v4l2-cci.o
> >> obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash-led-class.o
> >> obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o
> >> obj-$(CONFIG_V4L2_H264) += v4l2-h264.o
> >> diff --git a/drivers/media/v4l2-core/v4l2-cci.c b/drivers/media/v4l2-core/v4l2-cci.c
> >> new file mode 100644
> >> index 000000000000..21207d137dbe
> >> --- /dev/null
> >> +++ b/drivers/media/v4l2-core/v4l2-cci.c
> >> @@ -0,0 +1,142 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * MIPI Camera Control Interface (CCI) register access helpers.
> >> + *
> >> + * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
> >> + */
> >> +
> >> +#include <linux/delay.h>
> >> +#include <linux/dev_printk.h>
> >> +#include <linux/module.h>
> >> +#include <linux/regmap.h>
> >> +
> >> +#include <media/v4l2-cci.h>
> >> +
> >> +int cci_read(struct regmap *map, u32 reg, u32 *val, int *err)
> >> +{
> >> + int i, len, ret;
> >> + u8 buf[4];
> >> +
> >> + if (err && *err)
> >> + return *err;
> >> +
> >> + /* Set len to register width in bytes */
> >> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
> >> + reg &= CCI_REG_ADDR_MASK;
> >> +
> >> + ret = regmap_bulk_read(map, reg, buf, len);
> >> + if (ret) {
> >> + dev_err(regmap_get_device(map), "Error reading reg 0x%4x: %d\n", reg, ret);
> >> + if (err)
> >> + *err = ret;
> >> +
> >> + return ret;
> >> + }
> >> +
> >> + *val = 0;
> >> + for (i = 0; i < len; i++) {
> >> + *val <<= 8;
> >> + *val |= buf[i];
> >> + }
> >> +
> >> + return 0;
> >> +}
> >> +EXPORT_SYMBOL_GPL(cci_read);
> >> +
> >> +int cci_write(struct regmap *map, u32 reg, u32 val, int *err)
> >> +{
> >> + int i, len, ret;
> >> + u8 buf[4];
> >> +
> >> + if (err && *err)
> >> + return *err;
> >> +
> >> + /* Set len to register width in bytes */
> >> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
> >> + reg &= CCI_REG_ADDR_MASK;
> >> +
> >> + for (i = 0; i < len; i++) {
> >> + buf[len - i - 1] = val & 0xff;
> >> + val >>= 8;
> >> + }
> >> +
> >> + ret = regmap_bulk_write(map, reg, buf, len);
> >> + if (ret) {
> >> + dev_err(regmap_get_device(map), "Error writing reg 0x%4x: %d\n", reg, ret);
> >> + if (err)
> >> + *err = ret;
> >> + }
> >> +
> >> + return ret;
> >> +}
> >> +EXPORT_SYMBOL_GPL(cci_write);
> >> +
> >> +int cci_update_bits(struct regmap *map, u32 reg, u32 mask, u32 val, int *err)
> >> +{
> >> + int width, ret;
> >> + u32 readval;
> >> +
> >> + if (err && *err)
> >> + return *err;
> >> +
> >> + /*
> >> + * For single byte updates use regmap_update_bits(), this uses
> >> + * the regmap-lock to protect against other read-modify-writes racing.
> >> + */
> >> + width = (reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT;
> >> + if (width == cci_reg_8) {
> >> + reg &= CCI_REG_ADDR_MASK;
> >> + ret = regmap_update_bits(map, reg, mask, val);
> >> + if (ret) {
> >> + dev_err(regmap_get_device(map), "Error updating reg 0x%4x: %d\n", reg, ret);
> >> + if (err)
> >> + *err = ret;
> >> + }
> >> +
> >> + return ret;
> >> + }
> >> +
> >> + ret = cci_read(map, reg, &readval, err);
> >> + if (ret)
> >> + return ret;
> >> +
> >> + val = (readval & ~mask) | (val & mask);
> >> +
> >> + return cci_write(map, reg, val, err);
> >
> > Unless I'm mistaken, the regmap cache isn't used. This makes update
> > operations fairly costly due to the read. Could that be improved ?
>
> The problem is that some registers may be volatile,
> think e.g. expsoure on a sensor where auto-exposure is supported.
>
> So normally drivers which want to use regmap caching, also
> provide a whole bunch of tables describing the registers
> (lists of volatile + list of writable + list of readable
> registers).
>
> So enabling caching is not trivial. I think that it would be best
> for drivers which want that to supply their own regmap_config config
> and directly call devm_regmap_init_i2c() if they then use
> the resulting regmaps with the existing cci_* helpers then caching
> will be used automatically.
Would there be a way to use the cache for update operations (as I think
we can consider that registers used in those operations won't be
volatile), and bypass it for standalone reads ?
> >> +}
> >> +EXPORT_SYMBOL_GPL(cci_update_bits);
> >> +
> >> +int cci_multi_reg_write(struct regmap *map, const struct reg_sequence *regs, int num_regs, int *err)
> >> +{
> >> + int i, ret;
> >> +
> >> + if (err && *err)
> >> + return *err;
> >> +
> >> + for (i = 0; i < num_regs; i++) {
> >> + ret = cci_write(map, regs[i].reg, regs[i].def, err);
> >> + if (ret)
> >> + return ret;
> >> +
> >> + if (regs[i].delay_us)
> >> + fsleep(regs[i].delay_us);
> >
> > Do you have an immediate need for this ? If not, I'd drop support for
> > the delay, and add it later when and if needed. It will be easier to
> > discuss the API and use cases with a real user.
>
> This is a 1:1 mirror of regmap_multi_reg_write() note this uses
> the existing struct reg_sequence delay_us field and the:
>
> if (regs[i].delay_us)
> fsleep(regs[i].delay_us);
>
> is copied from the implementation of regmap_multi_reg_write()
The reason why I don't like it much as that such delays are often hacks
hidden in the middle of register arrays that should in many cases be
handled differently. I was hoping that, by not supporting them yet,
we'll have an easier time to get drivers right. Maybe I'm wrong.
> >> + }
> >> +
> >> + return 0;
> >> +}
> >> +EXPORT_SYMBOL_GPL(cci_multi_reg_write);
> >> +
> >> +struct regmap *cci_regmap_init_i2c(struct i2c_client *client, int reg_addr_bits)
> >> +{
> >> + struct regmap_config config = {
> >> + .reg_bits = reg_addr_bits,
> >> + .val_bits = 8,
> >> + .reg_format_endian = REGMAP_ENDIAN_BIG,
> >> + };
> >> +
> >> + return devm_regmap_init_i2c(client, &config);
> >> +}
> >> +EXPORT_SYMBOL_GPL(cci_regmap_init_i2c);
> >> +
> >> +MODULE_LICENSE("GPL");
> >> +MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
> >> diff --git a/include/media/v4l2-cci.h b/include/media/v4l2-cci.h
> >> new file mode 100644
> >> index 000000000000..69b8a7c4a013
> >> --- /dev/null
> >> +++ b/include/media/v4l2-cci.h
> >> @@ -0,0 +1,109 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 */
> >> +/*
> >> + * MIPI Camera Control Interface (CCI) register access helpers.
> >> + *
> >> + * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
> >> + */
> >> +#ifndef _V4L2_CCI_H
> >> +#define _V4L2_CCI_H
> >> +
> >> +#include <linux/regmap.h>
> >> +#include <linux/types.h>
> >> +
> >> +/*
> >> + * Note cci_reg_8 deliberately is 0, not 1, so that raw
> >> + * (not wrapped in a CCI_REG*() macro) register addresses
> >> + * do 8 bit wide accesses. This allows unchanged use of register
> >> + * initialization lists of raw address, value pairs which only
> >> + * do 8 bit width accesses. Which makes porting drivers easier.
> >
> > It does, but at the same time, it prevents catching errors caused by
> > incorrect register macros. I'm tempted to consider that catching those
> > errors is more important.
> >
> >> + */
> >> +enum cci_reg_type {
> >> + cci_reg_8 = 0,
> >> + cci_reg_16,
> >> + cci_reg_24,
> >> + cci_reg_32,
> >> +};
> >> +
> >> +/*
> >> + * Macros to define register address with the register width encoded
> >> + * into the higher bits. CCI_REG8() is a no-op so its use is optional.
> >
> > Even if it's a no-op I'd prefer making its use mandatory. It makes
> > driver code more explicit, and eases catching issues during review.
>
> The problem is that almost all sensor drivers contain long list
> of register-address, -val pairs which they send to their own custom
> regmap_multi_reg_write()
>
> See e.g. the drivers/media/i2c/imx219.c (to stick with the imx
> theme from your imx290 request) this has a lot of quite long
> struct imx219_reg arrays with raw initializers.
>
> Often some or all of these registers in such list are
> undocumented (if we have access to a datasheet at all),
> so we simply don't know the register width.
>
> So arguably adding CCI_REG8(x) around all the addresses
> here is wrong, since this suggests we know the register
> width.
>
> With the current proposal to have 0 mean both unset and 8bit
> width this kinda register lists just work and converting
> the driver becomes just a matter of replacing e.g.
> imx219_write_regs() with cci_multi_reg_write().
>
> Where as otherwise we would need to add CCI_REG8(x)
> around the addresses which:
>
> a) Suggests we actually know the register width which
> we often do not know at all
>
> b) causes a ton of needless churn
>
> so I would very much prefer to keep this as as and
> allow unmarked register addresses.
>
> As for the CCI_REG8(x) being useful as an annotation
> during review you are of course free to enforce its
> use during review. And note that I did use it for
> all the OV2680_REG_FOO defines in both ov2680 conversions.
>
> I do agree enforcing its use makes sense for individual
> register address defines. The reason to make it optional
> and the place where I want it to be optional is for
> the array of raw register-addr + initializer-val pairs
> case.
For register arrays, I'm fine with that. For register macros, I don't
want to see
#define MY_WELL_DEFINED_8B_REG 0x1234
For those I want drivers to use CCI_REG8(). It seems we're on the same
page :-)
> >> + */
> >> +#define CCI_REG_ADDR_MASK GENMASK(15, 0)
> >> +#define CCI_REG_WIDTH_SHIFT 16
> >> +#define CCI_REG_WIDTH_MASK GENMASK(17, 16)
> >> +
> >> +#define CCI_REG8(x) ((cci_reg_8 << CCI_REG_WIDTH_SHIFT) | (x))
> >> +#define CCI_REG16(x) ((cci_reg_16 << CCI_REG_WIDTH_SHIFT) | (x))
> >> +#define CCI_REG24(x) ((cci_reg_24 << CCI_REG_WIDTH_SHIFT) | (x))
> >> +#define CCI_REG32(x) ((cci_reg_32 << CCI_REG_WIDTH_SHIFT) | (x))
> >> +
> >> +/**
> >> + * cci_read() - Read a value from a single CCI register
> >> + *
> >> + * @map: Register map to write to
> >
> > s/write to/read from/ ?
>
> Ack, will fix for v2.
>
> >> + * @reg: Register address to write, use CCI_REG#() macros to encode reg width
> >
> > Same.
>
> Ack.
>
> >> + * @val: Pointer to store read value
> >> + * @err: optional pointer to store errors, if a previous error is set the write will be skipped
> >
> > Line wrap ?
>
> Ack.
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-08 10:27 ` Laurent Pinchart
@ 2023-06-08 11:01 ` Sakari Ailus
2023-06-12 15:03 ` Laurent Pinchart
2023-06-12 13:48 ` Hans de Goede
1 sibling, 1 reply; 38+ messages in thread
From: Sakari Ailus @ 2023-06-08 11:01 UTC (permalink / raw)
To: Laurent Pinchart
Cc: Hans de Goede, Mauro Carvalho Chehab, Andy Shevchenko,
linux-media
Hi Laurent,
On Thu, Jun 08, 2023 at 01:27:25PM +0300, Laurent Pinchart wrote:
> Hi Hans,
>
> On Wed, Jun 07, 2023 at 09:01:40PM +0200, Hans de Goede wrote:
> > On 6/7/23 20:18, Laurent Pinchart wrote:
> > > On Tue, Jun 06, 2023 at 06:58:06PM +0200, Hans de Goede wrote:
> > >> The CSI2 specification specifies a standard method to access camera sensor
> > >> registers called "Camera Control Interface (CCI)".
> > >>
> > >> This uses either 8 or 16 bit (big-endian wire order) register addresses
> > >> and supports 8, 16, 24 or 32 bit (big-endian wire order) register widths.
> > >
> > > I think there are some sensors that also have 64-bit registers, but we
> > > can deal with that later.
> > >
> > >> Currently a lot of Linux camera sensor drivers all have their own custom
> > >> helpers for this, often copy and pasted from other drivers.
> > >>
> > >> Add a set of generic helpers for this so that all sensor drivers can
> > >> switch to a single common implementation.
> > >>
> > >> These helpers take an extra optional "int *err" function parameter,
> > >> this can be used to chain a bunch of register accesses together with
> > >> only a single error check at the end, rather then needing to error
> > >> check each individual register access. The first failing call will
> > >> set the contents of err to a non 0 value and all other calls will
> > >> then become no-ops.
> > >>
> > >> Link: https://lore.kernel.org/linux-media/59aefa7f-7bf9-6736-6040-39551329cd0a@redhat.com/
> > >> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> > >> ---
> > >> Documentation/driver-api/media/v4l2-cci.rst | 5 +
> > >> Documentation/driver-api/media/v4l2-core.rst | 1 +
> > >> drivers/media/v4l2-core/Kconfig | 5 +
> > >> drivers/media/v4l2-core/Makefile | 1 +
> > >> drivers/media/v4l2-core/v4l2-cci.c | 142 +++++++++++++++++++
> > >> include/media/v4l2-cci.h | 109 ++++++++++++++
> > >> 6 files changed, 263 insertions(+)
> > >> create mode 100644 Documentation/driver-api/media/v4l2-cci.rst
> > >> create mode 100644 drivers/media/v4l2-core/v4l2-cci.c
> > >> create mode 100644 include/media/v4l2-cci.h
> > >>
> > >> diff --git a/Documentation/driver-api/media/v4l2-cci.rst b/Documentation/driver-api/media/v4l2-cci.rst
> > >> new file mode 100644
> > >> index 000000000000..dd297a40ed20
> > >> --- /dev/null
> > >> +++ b/Documentation/driver-api/media/v4l2-cci.rst
> > >> @@ -0,0 +1,5 @@
> > >> +.. SPDX-License-Identifier: GPL-2.0
> > >> +
> > >> +V4L2 CCI kAPI
> > >> +^^^^^^^^^^^^^
> > >> +.. kernel-doc:: include/media/v4l2-cci.h
> > >> diff --git a/Documentation/driver-api/media/v4l2-core.rst b/Documentation/driver-api/media/v4l2-core.rst
> > >> index 1a8c4a5f256b..239045ecc8f4 100644
> > >> --- a/Documentation/driver-api/media/v4l2-core.rst
> > >> +++ b/Documentation/driver-api/media/v4l2-core.rst
> > >> @@ -22,6 +22,7 @@ Video4Linux devices
> > >> v4l2-mem2mem
> > >> v4l2-async
> > >> v4l2-fwnode
> > >> + v4l2-cci
> > >> v4l2-rect
> > >> v4l2-tuner
> > >> v4l2-common
> > >> diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
> > >> index 348559bc2468..523ba243261d 100644
> > >> --- a/drivers/media/v4l2-core/Kconfig
> > >> +++ b/drivers/media/v4l2-core/Kconfig
> > >> @@ -74,6 +74,11 @@ config V4L2_FWNODE
> > >> config V4L2_ASYNC
> > >> tristate
> > >>
> > >> +config V4L2_CCI
> > >> + tristate
> > >> + depends on I2C
> > >> + select REGMAP_I2C
> > >> +
> > >> # Used by drivers that need Videobuf modules
> > >> config VIDEOBUF_GEN
> > >> tristate
> > >> diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
> > >> index 41d91bd10cf2..be2551705755 100644
> > >> --- a/drivers/media/v4l2-core/Makefile
> > >> +++ b/drivers/media/v4l2-core/Makefile
> > >> @@ -25,6 +25,7 @@ videodev-$(CONFIG_VIDEO_V4L2_I2C) += v4l2-i2c.o
> > >> # (e. g. LC_ALL=C sort Makefile)
> > >>
> > >> obj-$(CONFIG_V4L2_ASYNC) += v4l2-async.o
> > >> +obj-$(CONFIG_V4L2_CCI) += v4l2-cci.o
> > >> obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash-led-class.o
> > >> obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o
> > >> obj-$(CONFIG_V4L2_H264) += v4l2-h264.o
> > >> diff --git a/drivers/media/v4l2-core/v4l2-cci.c b/drivers/media/v4l2-core/v4l2-cci.c
> > >> new file mode 100644
> > >> index 000000000000..21207d137dbe
> > >> --- /dev/null
> > >> +++ b/drivers/media/v4l2-core/v4l2-cci.c
> > >> @@ -0,0 +1,142 @@
> > >> +// SPDX-License-Identifier: GPL-2.0
> > >> +/*
> > >> + * MIPI Camera Control Interface (CCI) register access helpers.
> > >> + *
> > >> + * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
> > >> + */
> > >> +
> > >> +#include <linux/delay.h>
> > >> +#include <linux/dev_printk.h>
> > >> +#include <linux/module.h>
> > >> +#include <linux/regmap.h>
> > >> +
> > >> +#include <media/v4l2-cci.h>
> > >> +
> > >> +int cci_read(struct regmap *map, u32 reg, u32 *val, int *err)
> > >> +{
> > >> + int i, len, ret;
> > >> + u8 buf[4];
> > >> +
> > >> + if (err && *err)
> > >> + return *err;
> > >> +
> > >> + /* Set len to register width in bytes */
> > >> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
> > >> + reg &= CCI_REG_ADDR_MASK;
> > >> +
> > >> + ret = regmap_bulk_read(map, reg, buf, len);
> > >> + if (ret) {
> > >> + dev_err(regmap_get_device(map), "Error reading reg 0x%4x: %d\n", reg, ret);
> > >> + if (err)
> > >> + *err = ret;
> > >> +
> > >> + return ret;
> > >> + }
> > >> +
> > >> + *val = 0;
> > >> + for (i = 0; i < len; i++) {
> > >> + *val <<= 8;
> > >> + *val |= buf[i];
> > >> + }
> > >> +
> > >> + return 0;
> > >> +}
> > >> +EXPORT_SYMBOL_GPL(cci_read);
> > >> +
> > >> +int cci_write(struct regmap *map, u32 reg, u32 val, int *err)
> > >> +{
> > >> + int i, len, ret;
> > >> + u8 buf[4];
> > >> +
> > >> + if (err && *err)
> > >> + return *err;
> > >> +
> > >> + /* Set len to register width in bytes */
> > >> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
> > >> + reg &= CCI_REG_ADDR_MASK;
> > >> +
> > >> + for (i = 0; i < len; i++) {
> > >> + buf[len - i - 1] = val & 0xff;
> > >> + val >>= 8;
> > >> + }
> > >> +
> > >> + ret = regmap_bulk_write(map, reg, buf, len);
> > >> + if (ret) {
> > >> + dev_err(regmap_get_device(map), "Error writing reg 0x%4x: %d\n", reg, ret);
> > >> + if (err)
> > >> + *err = ret;
> > >> + }
> > >> +
> > >> + return ret;
> > >> +}
> > >> +EXPORT_SYMBOL_GPL(cci_write);
> > >> +
> > >> +int cci_update_bits(struct regmap *map, u32 reg, u32 mask, u32 val, int *err)
> > >> +{
> > >> + int width, ret;
> > >> + u32 readval;
> > >> +
> > >> + if (err && *err)
> > >> + return *err;
> > >> +
> > >> + /*
> > >> + * For single byte updates use regmap_update_bits(), this uses
> > >> + * the regmap-lock to protect against other read-modify-writes racing.
> > >> + */
> > >> + width = (reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT;
> > >> + if (width == cci_reg_8) {
> > >> + reg &= CCI_REG_ADDR_MASK;
> > >> + ret = regmap_update_bits(map, reg, mask, val);
> > >> + if (ret) {
> > >> + dev_err(regmap_get_device(map), "Error updating reg 0x%4x: %d\n", reg, ret);
> > >> + if (err)
> > >> + *err = ret;
> > >> + }
> > >> +
> > >> + return ret;
> > >> + }
> > >> +
> > >> + ret = cci_read(map, reg, &readval, err);
> > >> + if (ret)
> > >> + return ret;
> > >> +
> > >> + val = (readval & ~mask) | (val & mask);
> > >> +
> > >> + return cci_write(map, reg, val, err);
> > >
> > > Unless I'm mistaken, the regmap cache isn't used. This makes update
> > > operations fairly costly due to the read. Could that be improved ?
> >
> > The problem is that some registers may be volatile,
> > think e.g. expsoure on a sensor where auto-exposure is supported.
> >
> > So normally drivers which want to use regmap caching, also
> > provide a whole bunch of tables describing the registers
> > (lists of volatile + list of writable + list of readable
> > registers).
> >
> > So enabling caching is not trivial. I think that it would be best
> > for drivers which want that to supply their own regmap_config config
> > and directly call devm_regmap_init_i2c() if they then use
> > the resulting regmaps with the existing cci_* helpers then caching
> > will be used automatically.
>
> Would there be a way to use the cache for update operations (as I think
> we can consider that registers used in those operations won't be
> volatile), and bypass it for standalone reads ?
Could we rely on regmap on this? It provides a way to tell which registers
are volatile. Very few of these drivers would get any benefit from caching
anyway (or even use update_bits()).
>
> > >> +}
> > >> +EXPORT_SYMBOL_GPL(cci_update_bits);
> > >> +
> > >> +int cci_multi_reg_write(struct regmap *map, const struct reg_sequence *regs, int num_regs, int *err)
> > >> +{
> > >> + int i, ret;
> > >> +
> > >> + if (err && *err)
> > >> + return *err;
> > >> +
> > >> + for (i = 0; i < num_regs; i++) {
> > >> + ret = cci_write(map, regs[i].reg, regs[i].def, err);
> > >> + if (ret)
> > >> + return ret;
> > >> +
> > >> + if (regs[i].delay_us)
> > >> + fsleep(regs[i].delay_us);
> > >
> > > Do you have an immediate need for this ? If not, I'd drop support for
> > > the delay, and add it later when and if needed. It will be easier to
> > > discuss the API and use cases with a real user.
> >
> > This is a 1:1 mirror of regmap_multi_reg_write() note this uses
> > the existing struct reg_sequence delay_us field and the:
> >
> > if (regs[i].delay_us)
> > fsleep(regs[i].delay_us);
> >
> > is copied from the implementation of regmap_multi_reg_write()
>
> The reason why I don't like it much as that such delays are often hacks
> hidden in the middle of register arrays that should in many cases be
> handled differently. I was hoping that, by not supporting them yet,
> we'll have an easier time to get drivers right. Maybe I'm wrong.
It's not uncommon for a sensor to require e.g. a given amount of time to
recover from software reset. Then again, embedding software in a register
list can hardly be described as a good practice. There maybe other such
cases, too.
But the field already exists in the struct. I don't object acting based on
its contents as such.
>
> > >> + }
> > >> +
> > >> + return 0;
> > >> +}
> > >> +EXPORT_SYMBOL_GPL(cci_multi_reg_write);
> > >> +
> > >> +struct regmap *cci_regmap_init_i2c(struct i2c_client *client, int reg_addr_bits)
> > >> +{
> > >> + struct regmap_config config = {
> > >> + .reg_bits = reg_addr_bits,
> > >> + .val_bits = 8,
> > >> + .reg_format_endian = REGMAP_ENDIAN_BIG,
> > >> + };
> > >> +
> > >> + return devm_regmap_init_i2c(client, &config);
> > >> +}
> > >> +EXPORT_SYMBOL_GPL(cci_regmap_init_i2c);
> > >> +
> > >> +MODULE_LICENSE("GPL");
> > >> +MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
> > >> diff --git a/include/media/v4l2-cci.h b/include/media/v4l2-cci.h
> > >> new file mode 100644
> > >> index 000000000000..69b8a7c4a013
> > >> --- /dev/null
> > >> +++ b/include/media/v4l2-cci.h
> > >> @@ -0,0 +1,109 @@
> > >> +/* SPDX-License-Identifier: GPL-2.0 */
> > >> +/*
> > >> + * MIPI Camera Control Interface (CCI) register access helpers.
> > >> + *
> > >> + * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
> > >> + */
> > >> +#ifndef _V4L2_CCI_H
> > >> +#define _V4L2_CCI_H
> > >> +
> > >> +#include <linux/regmap.h>
> > >> +#include <linux/types.h>
> > >> +
> > >> +/*
> > >> + * Note cci_reg_8 deliberately is 0, not 1, so that raw
> > >> + * (not wrapped in a CCI_REG*() macro) register addresses
> > >> + * do 8 bit wide accesses. This allows unchanged use of register
> > >> + * initialization lists of raw address, value pairs which only
> > >> + * do 8 bit width accesses. Which makes porting drivers easier.
> > >
> > > It does, but at the same time, it prevents catching errors caused by
> > > incorrect register macros. I'm tempted to consider that catching those
> > > errors is more important.
> > >
> > >> + */
> > >> +enum cci_reg_type {
> > >> + cci_reg_8 = 0,
> > >> + cci_reg_16,
> > >> + cci_reg_24,
> > >> + cci_reg_32,
> > >> +};
> > >> +
> > >> +/*
> > >> + * Macros to define register address with the register width encoded
> > >> + * into the higher bits. CCI_REG8() is a no-op so its use is optional.
> > >
> > > Even if it's a no-op I'd prefer making its use mandatory. It makes
> > > driver code more explicit, and eases catching issues during review.
> >
> > The problem is that almost all sensor drivers contain long list
> > of register-address, -val pairs which they send to their own custom
> > regmap_multi_reg_write()
> >
> > See e.g. the drivers/media/i2c/imx219.c (to stick with the imx
> > theme from your imx290 request) this has a lot of quite long
> > struct imx219_reg arrays with raw initializers.
> >
> > Often some or all of these registers in such list are
> > undocumented (if we have access to a datasheet at all),
> > so we simply don't know the register width.
> >
> > So arguably adding CCI_REG8(x) around all the addresses
> > here is wrong, since this suggests we know the register
> > width.
> >
> > With the current proposal to have 0 mean both unset and 8bit
> > width this kinda register lists just work and converting
> > the driver becomes just a matter of replacing e.g.
> > imx219_write_regs() with cci_multi_reg_write().
> >
> > Where as otherwise we would need to add CCI_REG8(x)
> > around the addresses which:
> >
> > a) Suggests we actually know the register width which
> > we often do not know at all
> >
> > b) causes a ton of needless churn
> >
> > so I would very much prefer to keep this as as and
> > allow unmarked register addresses.
> >
> > As for the CCI_REG8(x) being useful as an annotation
> > during review you are of course free to enforce its
> > use during review. And note that I did use it for
> > all the OV2680_REG_FOO defines in both ov2680 conversions.
> >
> > I do agree enforcing its use makes sense for individual
> > register address defines. The reason to make it optional
> > and the place where I want it to be optional is for
> > the array of raw register-addr + initializer-val pairs
> > case.
>
> For register arrays, I'm fine with that. For register macros, I don't
> want to see
>
> #define MY_WELL_DEFINED_8B_REG 0x1234
>
> For those I want drivers to use CCI_REG8(). It seems we're on the same
> page :-)
For a register list based sensor driver, I don't really mind even missing
this in the register lists. Using that macro doesn't help when the problem
really is a very long list of random-looking numbers.
Better drivers should of course use the macro.
--
Kind regards,
Sakari Ailus
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-08 10:27 ` Laurent Pinchart
2023-06-08 11:01 ` Sakari Ailus
@ 2023-06-12 13:48 ` Hans de Goede
2023-06-12 15:16 ` Laurent Pinchart
1 sibling, 1 reply; 38+ messages in thread
From: Hans de Goede @ 2023-06-12 13:48 UTC (permalink / raw)
To: Laurent Pinchart
Cc: Sakari Ailus, Mauro Carvalho Chehab, Andy Shevchenko, linux-media
Hi Laurent,
On 6/8/23 12:27, Laurent Pinchart wrote:
> Hi Hans,
>
> On Wed, Jun 07, 2023 at 09:01:40PM +0200, Hans de Goede wrote:
>> On 6/7/23 20:18, Laurent Pinchart wrote:
<snip>
>>>> +int cci_update_bits(struct regmap *map, u32 reg, u32 mask, u32 val, int *err)
>>>> +{
>>>> + int width, ret;
>>>> + u32 readval;
>>>> +
>>>> + if (err && *err)
>>>> + return *err;
>>>> +
>>>> + /*
>>>> + * For single byte updates use regmap_update_bits(), this uses
>>>> + * the regmap-lock to protect against other read-modify-writes racing.
>>>> + */
>>>> + width = (reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT;
>>>> + if (width == cci_reg_8) {
>>>> + reg &= CCI_REG_ADDR_MASK;
>>>> + ret = regmap_update_bits(map, reg, mask, val);
>>>> + if (ret) {
>>>> + dev_err(regmap_get_device(map), "Error updating reg 0x%4x: %d\n", reg, ret);
>>>> + if (err)
>>>> + *err = ret;
>>>> + }
>>>> +
>>>> + return ret;
>>>> + }
>>>> +
>>>> + ret = cci_read(map, reg, &readval, err);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + val = (readval & ~mask) | (val & mask);
>>>> +
>>>> + return cci_write(map, reg, val, err);
>>>
>>> Unless I'm mistaken, the regmap cache isn't used. This makes update
>>> operations fairly costly due to the read. Could that be improved ?
>>
>> The problem is that some registers may be volatile,
>> think e.g. expsoure on a sensor where auto-exposure is supported.
>>
>> So normally drivers which want to use regmap caching, also
>> provide a whole bunch of tables describing the registers
>> (lists of volatile + list of writable + list of readable
>> registers).
>>
>> So enabling caching is not trivial. I think that it would be best
>> for drivers which want that to supply their own regmap_config config
>> and directly call devm_regmap_init_i2c() if they then use
>> the resulting regmaps with the existing cci_* helpers then caching
>> will be used automatically.
>
> Would there be a way to use the cache for update operations (as I think
> we can consider that registers used in those operations won't be
> volatile), and bypass it for standalone reads ?
There is not really a nice way to only use the cache for update operations.
I guess we could do something hacky like tell regmap to create a cache,
then set the cache-bypass flag and drop the cache-bypass flag during
update ops. But that really is abusing the regmap API.
Generally speaking update operations don't happen that often though,
so IMHO hacking to get this cached is not worth it.
>
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(cci_update_bits);
>>>> +
>>>> +int cci_multi_reg_write(struct regmap *map, const struct reg_sequence *regs, int num_regs, int *err)
>>>> +{
>>>> + int i, ret;
>>>> +
>>>> + if (err && *err)
>>>> + return *err;
>>>> +
>>>> + for (i = 0; i < num_regs; i++) {
>>>> + ret = cci_write(map, regs[i].reg, regs[i].def, err);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + if (regs[i].delay_us)
>>>> + fsleep(regs[i].delay_us);
>>>
>>> Do you have an immediate need for this ? If not, I'd drop support for
>>> the delay, and add it later when and if needed. It will be easier to
>>> discuss the API and use cases with a real user.
>>
>> This is a 1:1 mirror of regmap_multi_reg_write() note this uses
>> the existing struct reg_sequence delay_us field and the:
>>
>> if (regs[i].delay_us)
>> fsleep(regs[i].delay_us);
>>
>> is copied from the implementation of regmap_multi_reg_write()
>
> The reason why I don't like it much as that such delays are often hacks
> hidden in the middle of register arrays that should in many cases be
> handled differently. I was hoping that, by not supporting them yet,
> we'll have an easier time to get drivers right. Maybe I'm wrong.
I understand, but having this is more or less a downside of
the choice to mirror the regmap API as close as possible.
As Sakari said, having the field there just to ignore it
seems like a bad idea.
I think that this being abused is something to watch for during
review, rather then enforcing it not being used in the CCI code.
>
>>>> + }
>>>> +
>>>> + return 0;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(cci_multi_reg_write);
>>>> +
>>>> +struct regmap *cci_regmap_init_i2c(struct i2c_client *client, int reg_addr_bits)
>>>> +{
>>>> + struct regmap_config config = {
>>>> + .reg_bits = reg_addr_bits,
>>>> + .val_bits = 8,
>>>> + .reg_format_endian = REGMAP_ENDIAN_BIG,
>>>> + };
>>>> +
>>>> + return devm_regmap_init_i2c(client, &config);
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(cci_regmap_init_i2c);
>>>> +
>>>> +MODULE_LICENSE("GPL");
>>>> +MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
>>>> diff --git a/include/media/v4l2-cci.h b/include/media/v4l2-cci.h
>>>> new file mode 100644
>>>> index 000000000000..69b8a7c4a013
>>>> --- /dev/null
>>>> +++ b/include/media/v4l2-cci.h
>>>> @@ -0,0 +1,109 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * MIPI Camera Control Interface (CCI) register access helpers.
>>>> + *
>>>> + * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
>>>> + */
>>>> +#ifndef _V4L2_CCI_H
>>>> +#define _V4L2_CCI_H
>>>> +
>>>> +#include <linux/regmap.h>
>>>> +#include <linux/types.h>
>>>> +
>>>> +/*
>>>> + * Note cci_reg_8 deliberately is 0, not 1, so that raw
>>>> + * (not wrapped in a CCI_REG*() macro) register addresses
>>>> + * do 8 bit wide accesses. This allows unchanged use of register
>>>> + * initialization lists of raw address, value pairs which only
>>>> + * do 8 bit width accesses. Which makes porting drivers easier.
>>>
>>> It does, but at the same time, it prevents catching errors caused by
>>> incorrect register macros. I'm tempted to consider that catching those
>>> errors is more important.
>>>
>>>> + */
>>>> +enum cci_reg_type {
>>>> + cci_reg_8 = 0,
>>>> + cci_reg_16,
>>>> + cci_reg_24,
>>>> + cci_reg_32,
>>>> +};
>>>> +
>>>> +/*
>>>> + * Macros to define register address with the register width encoded
>>>> + * into the higher bits. CCI_REG8() is a no-op so its use is optional.
>>>
>>> Even if it's a no-op I'd prefer making its use mandatory. It makes
>>> driver code more explicit, and eases catching issues during review.
>>
>> The problem is that almost all sensor drivers contain long list
>> of register-address, -val pairs which they send to their own custom
>> regmap_multi_reg_write()
>>
>> See e.g. the drivers/media/i2c/imx219.c (to stick with the imx
>> theme from your imx290 request) this has a lot of quite long
>> struct imx219_reg arrays with raw initializers.
>>
>> Often some or all of these registers in such list are
>> undocumented (if we have access to a datasheet at all),
>> so we simply don't know the register width.
>>
>> So arguably adding CCI_REG8(x) around all the addresses
>> here is wrong, since this suggests we know the register
>> width.
>>
>> With the current proposal to have 0 mean both unset and 8bit
>> width this kinda register lists just work and converting
>> the driver becomes just a matter of replacing e.g.
>> imx219_write_regs() with cci_multi_reg_write().
>>
>> Where as otherwise we would need to add CCI_REG8(x)
>> around the addresses which:
>>
>> a) Suggests we actually know the register width which
>> we often do not know at all
>>
>> b) causes a ton of needless churn
>>
>> so I would very much prefer to keep this as as and
>> allow unmarked register addresses.
>>
>> As for the CCI_REG8(x) being useful as an annotation
>> during review you are of course free to enforce its
>> use during review. And note that I did use it for
>> all the OV2680_REG_FOO defines in both ov2680 conversions.
>>
>> I do agree enforcing its use makes sense for individual
>> register address defines. The reason to make it optional
>> and the place where I want it to be optional is for
>> the array of raw register-addr + initializer-val pairs
>> case.
>
> For register arrays, I'm fine with that. For register macros, I don't
> want to see
>
> #define MY_WELL_DEFINED_8B_REG 0x1234
>
> For those I want drivers to use CCI_REG8(). It seems we're on the same
> page :-)
Right, but if we want cci_multi_reg_write() to work with register
arrays without the CCI_REG8() macros then the code needs to stay
as is and we cannot enforce use of the macro by erroring out
if it is not used.
Regards,
Hans
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-08 11:01 ` Sakari Ailus
@ 2023-06-12 15:03 ` Laurent Pinchart
2023-06-13 9:57 ` Sakari Ailus
0 siblings, 1 reply; 38+ messages in thread
From: Laurent Pinchart @ 2023-06-12 15:03 UTC (permalink / raw)
To: Sakari Ailus
Cc: Hans de Goede, Mauro Carvalho Chehab, Andy Shevchenko,
linux-media
Hi Sakari,
On Thu, Jun 08, 2023 at 11:01:29AM +0000, Sakari Ailus wrote:
> On Thu, Jun 08, 2023 at 01:27:25PM +0300, Laurent Pinchart wrote:
> > On Wed, Jun 07, 2023 at 09:01:40PM +0200, Hans de Goede wrote:
> > > On 6/7/23 20:18, Laurent Pinchart wrote:
> > > > On Tue, Jun 06, 2023 at 06:58:06PM +0200, Hans de Goede wrote:
> > > >> The CSI2 specification specifies a standard method to access camera sensor
> > > >> registers called "Camera Control Interface (CCI)".
> > > >>
> > > >> This uses either 8 or 16 bit (big-endian wire order) register addresses
> > > >> and supports 8, 16, 24 or 32 bit (big-endian wire order) register widths.
> > > >
> > > > I think there are some sensors that also have 64-bit registers, but we
> > > > can deal with that later.
> > > >
> > > >> Currently a lot of Linux camera sensor drivers all have their own custom
> > > >> helpers for this, often copy and pasted from other drivers.
> > > >>
> > > >> Add a set of generic helpers for this so that all sensor drivers can
> > > >> switch to a single common implementation.
> > > >>
> > > >> These helpers take an extra optional "int *err" function parameter,
> > > >> this can be used to chain a bunch of register accesses together with
> > > >> only a single error check at the end, rather then needing to error
> > > >> check each individual register access. The first failing call will
> > > >> set the contents of err to a non 0 value and all other calls will
> > > >> then become no-ops.
> > > >>
> > > >> Link: https://lore.kernel.org/linux-media/59aefa7f-7bf9-6736-6040-39551329cd0a@redhat.com/
> > > >> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> > > >> ---
> > > >> Documentation/driver-api/media/v4l2-cci.rst | 5 +
> > > >> Documentation/driver-api/media/v4l2-core.rst | 1 +
> > > >> drivers/media/v4l2-core/Kconfig | 5 +
> > > >> drivers/media/v4l2-core/Makefile | 1 +
> > > >> drivers/media/v4l2-core/v4l2-cci.c | 142 +++++++++++++++++++
> > > >> include/media/v4l2-cci.h | 109 ++++++++++++++
> > > >> 6 files changed, 263 insertions(+)
> > > >> create mode 100644 Documentation/driver-api/media/v4l2-cci.rst
> > > >> create mode 100644 drivers/media/v4l2-core/v4l2-cci.c
> > > >> create mode 100644 include/media/v4l2-cci.h
> > > >>
> > > >> diff --git a/Documentation/driver-api/media/v4l2-cci.rst b/Documentation/driver-api/media/v4l2-cci.rst
> > > >> new file mode 100644
> > > >> index 000000000000..dd297a40ed20
> > > >> --- /dev/null
> > > >> +++ b/Documentation/driver-api/media/v4l2-cci.rst
> > > >> @@ -0,0 +1,5 @@
> > > >> +.. SPDX-License-Identifier: GPL-2.0
> > > >> +
> > > >> +V4L2 CCI kAPI
> > > >> +^^^^^^^^^^^^^
> > > >> +.. kernel-doc:: include/media/v4l2-cci.h
> > > >> diff --git a/Documentation/driver-api/media/v4l2-core.rst b/Documentation/driver-api/media/v4l2-core.rst
> > > >> index 1a8c4a5f256b..239045ecc8f4 100644
> > > >> --- a/Documentation/driver-api/media/v4l2-core.rst
> > > >> +++ b/Documentation/driver-api/media/v4l2-core.rst
> > > >> @@ -22,6 +22,7 @@ Video4Linux devices
> > > >> v4l2-mem2mem
> > > >> v4l2-async
> > > >> v4l2-fwnode
> > > >> + v4l2-cci
> > > >> v4l2-rect
> > > >> v4l2-tuner
> > > >> v4l2-common
> > > >> diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
> > > >> index 348559bc2468..523ba243261d 100644
> > > >> --- a/drivers/media/v4l2-core/Kconfig
> > > >> +++ b/drivers/media/v4l2-core/Kconfig
> > > >> @@ -74,6 +74,11 @@ config V4L2_FWNODE
> > > >> config V4L2_ASYNC
> > > >> tristate
> > > >>
> > > >> +config V4L2_CCI
> > > >> + tristate
> > > >> + depends on I2C
> > > >> + select REGMAP_I2C
> > > >> +
> > > >> # Used by drivers that need Videobuf modules
> > > >> config VIDEOBUF_GEN
> > > >> tristate
> > > >> diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
> > > >> index 41d91bd10cf2..be2551705755 100644
> > > >> --- a/drivers/media/v4l2-core/Makefile
> > > >> +++ b/drivers/media/v4l2-core/Makefile
> > > >> @@ -25,6 +25,7 @@ videodev-$(CONFIG_VIDEO_V4L2_I2C) += v4l2-i2c.o
> > > >> # (e. g. LC_ALL=C sort Makefile)
> > > >>
> > > >> obj-$(CONFIG_V4L2_ASYNC) += v4l2-async.o
> > > >> +obj-$(CONFIG_V4L2_CCI) += v4l2-cci.o
> > > >> obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash-led-class.o
> > > >> obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o
> > > >> obj-$(CONFIG_V4L2_H264) += v4l2-h264.o
> > > >> diff --git a/drivers/media/v4l2-core/v4l2-cci.c b/drivers/media/v4l2-core/v4l2-cci.c
> > > >> new file mode 100644
> > > >> index 000000000000..21207d137dbe
> > > >> --- /dev/null
> > > >> +++ b/drivers/media/v4l2-core/v4l2-cci.c
> > > >> @@ -0,0 +1,142 @@
> > > >> +// SPDX-License-Identifier: GPL-2.0
> > > >> +/*
> > > >> + * MIPI Camera Control Interface (CCI) register access helpers.
> > > >> + *
> > > >> + * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
> > > >> + */
> > > >> +
> > > >> +#include <linux/delay.h>
> > > >> +#include <linux/dev_printk.h>
> > > >> +#include <linux/module.h>
> > > >> +#include <linux/regmap.h>
> > > >> +
> > > >> +#include <media/v4l2-cci.h>
> > > >> +
> > > >> +int cci_read(struct regmap *map, u32 reg, u32 *val, int *err)
> > > >> +{
> > > >> + int i, len, ret;
> > > >> + u8 buf[4];
> > > >> +
> > > >> + if (err && *err)
> > > >> + return *err;
> > > >> +
> > > >> + /* Set len to register width in bytes */
> > > >> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
> > > >> + reg &= CCI_REG_ADDR_MASK;
> > > >> +
> > > >> + ret = regmap_bulk_read(map, reg, buf, len);
> > > >> + if (ret) {
> > > >> + dev_err(regmap_get_device(map), "Error reading reg 0x%4x: %d\n", reg, ret);
> > > >> + if (err)
> > > >> + *err = ret;
> > > >> +
> > > >> + return ret;
> > > >> + }
> > > >> +
> > > >> + *val = 0;
> > > >> + for (i = 0; i < len; i++) {
> > > >> + *val <<= 8;
> > > >> + *val |= buf[i];
> > > >> + }
> > > >> +
> > > >> + return 0;
> > > >> +}
> > > >> +EXPORT_SYMBOL_GPL(cci_read);
> > > >> +
> > > >> +int cci_write(struct regmap *map, u32 reg, u32 val, int *err)
> > > >> +{
> > > >> + int i, len, ret;
> > > >> + u8 buf[4];
> > > >> +
> > > >> + if (err && *err)
> > > >> + return *err;
> > > >> +
> > > >> + /* Set len to register width in bytes */
> > > >> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
> > > >> + reg &= CCI_REG_ADDR_MASK;
> > > >> +
> > > >> + for (i = 0; i < len; i++) {
> > > >> + buf[len - i - 1] = val & 0xff;
> > > >> + val >>= 8;
> > > >> + }
> > > >> +
> > > >> + ret = regmap_bulk_write(map, reg, buf, len);
> > > >> + if (ret) {
> > > >> + dev_err(regmap_get_device(map), "Error writing reg 0x%4x: %d\n", reg, ret);
> > > >> + if (err)
> > > >> + *err = ret;
> > > >> + }
> > > >> +
> > > >> + return ret;
> > > >> +}
> > > >> +EXPORT_SYMBOL_GPL(cci_write);
> > > >> +
> > > >> +int cci_update_bits(struct regmap *map, u32 reg, u32 mask, u32 val, int *err)
> > > >> +{
> > > >> + int width, ret;
> > > >> + u32 readval;
> > > >> +
> > > >> + if (err && *err)
> > > >> + return *err;
> > > >> +
> > > >> + /*
> > > >> + * For single byte updates use regmap_update_bits(), this uses
> > > >> + * the regmap-lock to protect against other read-modify-writes racing.
> > > >> + */
> > > >> + width = (reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT;
> > > >> + if (width == cci_reg_8) {
> > > >> + reg &= CCI_REG_ADDR_MASK;
> > > >> + ret = regmap_update_bits(map, reg, mask, val);
> > > >> + if (ret) {
> > > >> + dev_err(regmap_get_device(map), "Error updating reg 0x%4x: %d\n", reg, ret);
> > > >> + if (err)
> > > >> + *err = ret;
> > > >> + }
> > > >> +
> > > >> + return ret;
> > > >> + }
> > > >> +
> > > >> + ret = cci_read(map, reg, &readval, err);
> > > >> + if (ret)
> > > >> + return ret;
> > > >> +
> > > >> + val = (readval & ~mask) | (val & mask);
> > > >> +
> > > >> + return cci_write(map, reg, val, err);
> > > >
> > > > Unless I'm mistaken, the regmap cache isn't used. This makes update
> > > > operations fairly costly due to the read. Could that be improved ?
> > >
> > > The problem is that some registers may be volatile,
> > > think e.g. expsoure on a sensor where auto-exposure is supported.
> > >
> > > So normally drivers which want to use regmap caching, also
> > > provide a whole bunch of tables describing the registers
> > > (lists of volatile + list of writable + list of readable
> > > registers).
> > >
> > > So enabling caching is not trivial. I think that it would be best
> > > for drivers which want that to supply their own regmap_config config
> > > and directly call devm_regmap_init_i2c() if they then use
> > > the resulting regmaps with the existing cci_* helpers then caching
> > > will be used automatically.
> >
> > Would there be a way to use the cache for update operations (as I think
> > we can consider that registers used in those operations won't be
> > volatile), and bypass it for standalone reads ?
>
> Could we rely on regmap on this? It provides a way to tell which registers
> are volatile. Very few of these drivers would get any benefit from caching
> anyway (or even use update_bits()).
Yes we could I suppose. I've never been a big fan of that part of the
regmap API as it's cumbersome to use when dealing with devices that have
lots of registers, like camera sensors usually do. Volatile registers
tend to be scattered around the address space, making the volatile_table
lookup inefficient, and the volatile_reg function wouldn't be great
either. I could live with that I suppose, but given that I think we can
expect read-modify-write operations to never operate on a volatile
register, and plain read operations to nearly always do (with the
exception of read-only version registers that are read once at probe
time only), it would be a bit of a shame to rely on the less efficient
regmap volatile support.
> > > >> +}
> > > >> +EXPORT_SYMBOL_GPL(cci_update_bits);
> > > >> +
> > > >> +int cci_multi_reg_write(struct regmap *map, const struct reg_sequence *regs, int num_regs, int *err)
> > > >> +{
> > > >> + int i, ret;
> > > >> +
> > > >> + if (err && *err)
> > > >> + return *err;
> > > >> +
> > > >> + for (i = 0; i < num_regs; i++) {
> > > >> + ret = cci_write(map, regs[i].reg, regs[i].def, err);
> > > >> + if (ret)
> > > >> + return ret;
> > > >> +
> > > >> + if (regs[i].delay_us)
> > > >> + fsleep(regs[i].delay_us);
> > > >
> > > > Do you have an immediate need for this ? If not, I'd drop support for
> > > > the delay, and add it later when and if needed. It will be easier to
> > > > discuss the API and use cases with a real user.
> > >
> > > This is a 1:1 mirror of regmap_multi_reg_write() note this uses
> > > the existing struct reg_sequence delay_us field and the:
> > >
> > > if (regs[i].delay_us)
> > > fsleep(regs[i].delay_us);
> > >
> > > is copied from the implementation of regmap_multi_reg_write()
> >
> > The reason why I don't like it much as that such delays are often hacks
> > hidden in the middle of register arrays that should in many cases be
> > handled differently. I was hoping that, by not supporting them yet,
> > we'll have an easier time to get drivers right. Maybe I'm wrong.
>
> It's not uncommon for a sensor to require e.g. a given amount of time to
> recover from software reset. Then again, embedding software in a register
> list can hardly be described as a good practice. There maybe other such
> cases, too.
That's exactly my concern, I'd like to avoid giving an easy option for
people to embed reset actions in a large registers table :-) I don't
object to the feature if we find valid use cases for it.
> But the field already exists in the struct. I don't object acting based on
> its contents as such.
>
> > > >> + }
> > > >> +
> > > >> + return 0;
> > > >> +}
> > > >> +EXPORT_SYMBOL_GPL(cci_multi_reg_write);
> > > >> +
> > > >> +struct regmap *cci_regmap_init_i2c(struct i2c_client *client, int reg_addr_bits)
> > > >> +{
> > > >> + struct regmap_config config = {
> > > >> + .reg_bits = reg_addr_bits,
> > > >> + .val_bits = 8,
> > > >> + .reg_format_endian = REGMAP_ENDIAN_BIG,
> > > >> + };
> > > >> +
> > > >> + return devm_regmap_init_i2c(client, &config);
> > > >> +}
> > > >> +EXPORT_SYMBOL_GPL(cci_regmap_init_i2c);
> > > >> +
> > > >> +MODULE_LICENSE("GPL");
> > > >> +MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
> > > >> diff --git a/include/media/v4l2-cci.h b/include/media/v4l2-cci.h
> > > >> new file mode 100644
> > > >> index 000000000000..69b8a7c4a013
> > > >> --- /dev/null
> > > >> +++ b/include/media/v4l2-cci.h
> > > >> @@ -0,0 +1,109 @@
> > > >> +/* SPDX-License-Identifier: GPL-2.0 */
> > > >> +/*
> > > >> + * MIPI Camera Control Interface (CCI) register access helpers.
> > > >> + *
> > > >> + * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
> > > >> + */
> > > >> +#ifndef _V4L2_CCI_H
> > > >> +#define _V4L2_CCI_H
> > > >> +
> > > >> +#include <linux/regmap.h>
> > > >> +#include <linux/types.h>
> > > >> +
> > > >> +/*
> > > >> + * Note cci_reg_8 deliberately is 0, not 1, so that raw
> > > >> + * (not wrapped in a CCI_REG*() macro) register addresses
> > > >> + * do 8 bit wide accesses. This allows unchanged use of register
> > > >> + * initialization lists of raw address, value pairs which only
> > > >> + * do 8 bit width accesses. Which makes porting drivers easier.
> > > >
> > > > It does, but at the same time, it prevents catching errors caused by
> > > > incorrect register macros. I'm tempted to consider that catching those
> > > > errors is more important.
> > > >
> > > >> + */
> > > >> +enum cci_reg_type {
> > > >> + cci_reg_8 = 0,
> > > >> + cci_reg_16,
> > > >> + cci_reg_24,
> > > >> + cci_reg_32,
> > > >> +};
> > > >> +
> > > >> +/*
> > > >> + * Macros to define register address with the register width encoded
> > > >> + * into the higher bits. CCI_REG8() is a no-op so its use is optional.
> > > >
> > > > Even if it's a no-op I'd prefer making its use mandatory. It makes
> > > > driver code more explicit, and eases catching issues during review.
> > >
> > > The problem is that almost all sensor drivers contain long list
> > > of register-address, -val pairs which they send to their own custom
> > > regmap_multi_reg_write()
> > >
> > > See e.g. the drivers/media/i2c/imx219.c (to stick with the imx
> > > theme from your imx290 request) this has a lot of quite long
> > > struct imx219_reg arrays with raw initializers.
> > >
> > > Often some or all of these registers in such list are
> > > undocumented (if we have access to a datasheet at all),
> > > so we simply don't know the register width.
> > >
> > > So arguably adding CCI_REG8(x) around all the addresses
> > > here is wrong, since this suggests we know the register
> > > width.
> > >
> > > With the current proposal to have 0 mean both unset and 8bit
> > > width this kinda register lists just work and converting
> > > the driver becomes just a matter of replacing e.g.
> > > imx219_write_regs() with cci_multi_reg_write().
> > >
> > > Where as otherwise we would need to add CCI_REG8(x)
> > > around the addresses which:
> > >
> > > a) Suggests we actually know the register width which
> > > we often do not know at all
> > >
> > > b) causes a ton of needless churn
> > >
> > > so I would very much prefer to keep this as as and
> > > allow unmarked register addresses.
> > >
> > > As for the CCI_REG8(x) being useful as an annotation
> > > during review you are of course free to enforce its
> > > use during review. And note that I did use it for
> > > all the OV2680_REG_FOO defines in both ov2680 conversions.
> > >
> > > I do agree enforcing its use makes sense for individual
> > > register address defines. The reason to make it optional
> > > and the place where I want it to be optional is for
> > > the array of raw register-addr + initializer-val pairs
> > > case.
> >
> > For register arrays, I'm fine with that. For register macros, I don't
> > want to see
> >
> > #define MY_WELL_DEFINED_8B_REG 0x1234
> >
> > For those I want drivers to use CCI_REG8(). It seems we're on the same
> > page :-)
>
> For a register list based sensor driver, I don't really mind even missing
> this in the register lists. Using that macro doesn't help when the problem
> really is a very long list of random-looking numbers.
I think we agree here, the place where I want to see the macros being
used without exceptions is when accessing individual registers.
> Better drivers should of course use the macro.
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-12 13:48 ` Hans de Goede
@ 2023-06-12 15:16 ` Laurent Pinchart
2023-06-12 15:33 ` Hans de Goede
0 siblings, 1 reply; 38+ messages in thread
From: Laurent Pinchart @ 2023-06-12 15:16 UTC (permalink / raw)
To: Hans de Goede
Cc: Sakari Ailus, Mauro Carvalho Chehab, Andy Shevchenko, linux-media
Hi Hans,
On Mon, Jun 12, 2023 at 03:48:01PM +0200, Hans de Goede wrote:
> On 6/8/23 12:27, Laurent Pinchart wrote:
> > On Wed, Jun 07, 2023 at 09:01:40PM +0200, Hans de Goede wrote:
> >> On 6/7/23 20:18, Laurent Pinchart wrote:
>
> <snip>
>
> >>>> +int cci_update_bits(struct regmap *map, u32 reg, u32 mask, u32 val, int *err)
> >>>> +{
> >>>> + int width, ret;
> >>>> + u32 readval;
> >>>> +
> >>>> + if (err && *err)
> >>>> + return *err;
> >>>> +
> >>>> + /*
> >>>> + * For single byte updates use regmap_update_bits(), this uses
> >>>> + * the regmap-lock to protect against other read-modify-writes racing.
> >>>> + */
> >>>> + width = (reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT;
> >>>> + if (width == cci_reg_8) {
> >>>> + reg &= CCI_REG_ADDR_MASK;
> >>>> + ret = regmap_update_bits(map, reg, mask, val);
> >>>> + if (ret) {
> >>>> + dev_err(regmap_get_device(map), "Error updating reg 0x%4x: %d\n", reg, ret);
> >>>> + if (err)
> >>>> + *err = ret;
> >>>> + }
> >>>> +
> >>>> + return ret;
> >>>> + }
> >>>> +
> >>>> + ret = cci_read(map, reg, &readval, err);
> >>>> + if (ret)
> >>>> + return ret;
> >>>> +
> >>>> + val = (readval & ~mask) | (val & mask);
> >>>> +
> >>>> + return cci_write(map, reg, val, err);
> >>>
> >>> Unless I'm mistaken, the regmap cache isn't used. This makes update
> >>> operations fairly costly due to the read. Could that be improved ?
> >>
> >> The problem is that some registers may be volatile,
> >> think e.g. expsoure on a sensor where auto-exposure is supported.
> >>
> >> So normally drivers which want to use regmap caching, also
> >> provide a whole bunch of tables describing the registers
> >> (lists of volatile + list of writable + list of readable
> >> registers).
> >>
> >> So enabling caching is not trivial. I think that it would be best
> >> for drivers which want that to supply their own regmap_config config
> >> and directly call devm_regmap_init_i2c() if they then use
> >> the resulting regmaps with the existing cci_* helpers then caching
> >> will be used automatically.
> >
> > Would there be a way to use the cache for update operations (as I think
> > we can consider that registers used in those operations won't be
> > volatile), and bypass it for standalone reads ?
>
> There is not really a nice way to only use the cache for update operations.
>
> I guess we could do something hacky like tell regmap to create a cache,
> then set the cache-bypass flag and drop the cache-bypass flag during
> update ops. But that really is abusing the regmap API.
>
> Generally speaking update operations don't happen that often though,
> so IMHO hacking to get this cached is not worth it.
I2C reads are slow, so even if they're not very common, it would be nice
to avoid them. We can start without any caching and improve it later,
I'm fine with that.
> >>>> +}
> >>>> +EXPORT_SYMBOL_GPL(cci_update_bits);
> >>>> +
> >>>> +int cci_multi_reg_write(struct regmap *map, const struct reg_sequence *regs, int num_regs, int *err)
> >>>> +{
> >>>> + int i, ret;
> >>>> +
> >>>> + if (err && *err)
> >>>> + return *err;
> >>>> +
> >>>> + for (i = 0; i < num_regs; i++) {
> >>>> + ret = cci_write(map, regs[i].reg, regs[i].def, err);
> >>>> + if (ret)
> >>>> + return ret;
> >>>> +
> >>>> + if (regs[i].delay_us)
> >>>> + fsleep(regs[i].delay_us);
> >>>
> >>> Do you have an immediate need for this ? If not, I'd drop support for
> >>> the delay, and add it later when and if needed. It will be easier to
> >>> discuss the API and use cases with a real user.
> >>
> >> This is a 1:1 mirror of regmap_multi_reg_write() note this uses
> >> the existing struct reg_sequence delay_us field and the:
> >>
> >> if (regs[i].delay_us)
> >> fsleep(regs[i].delay_us);
> >>
> >> is copied from the implementation of regmap_multi_reg_write()
> >
> > The reason why I don't like it much as that such delays are often hacks
> > hidden in the middle of register arrays that should in many cases be
> > handled differently. I was hoping that, by not supporting them yet,
> > we'll have an easier time to get drivers right. Maybe I'm wrong.
>
> I understand, but having this is more or less a downside of
> the choice to mirror the regmap API as close as possible.
>
> As Sakari said, having the field there just to ignore it
> seems like a bad idea.
I'm not sure to agree here, if we see no valid use case for that field,
why would it be a bad idea to ignore it ? That shouldn't affect anyone.
> I think that this being abused is something to watch for during
> review, rather then enforcing it not being used in the CCI code.
>
> >>>> + }
> >>>> +
> >>>> + return 0;
> >>>> +}
> >>>> +EXPORT_SYMBOL_GPL(cci_multi_reg_write);
> >>>> +
> >>>> +struct regmap *cci_regmap_init_i2c(struct i2c_client *client, int reg_addr_bits)
> >>>> +{
> >>>> + struct regmap_config config = {
> >>>> + .reg_bits = reg_addr_bits,
> >>>> + .val_bits = 8,
> >>>> + .reg_format_endian = REGMAP_ENDIAN_BIG,
> >>>> + };
> >>>> +
> >>>> + return devm_regmap_init_i2c(client, &config);
> >>>> +}
> >>>> +EXPORT_SYMBOL_GPL(cci_regmap_init_i2c);
> >>>> +
> >>>> +MODULE_LICENSE("GPL");
> >>>> +MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
> >>>> diff --git a/include/media/v4l2-cci.h b/include/media/v4l2-cci.h
> >>>> new file mode 100644
> >>>> index 000000000000..69b8a7c4a013
> >>>> --- /dev/null
> >>>> +++ b/include/media/v4l2-cci.h
> >>>> @@ -0,0 +1,109 @@
> >>>> +/* SPDX-License-Identifier: GPL-2.0 */
> >>>> +/*
> >>>> + * MIPI Camera Control Interface (CCI) register access helpers.
> >>>> + *
> >>>> + * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
> >>>> + */
> >>>> +#ifndef _V4L2_CCI_H
> >>>> +#define _V4L2_CCI_H
> >>>> +
> >>>> +#include <linux/regmap.h>
> >>>> +#include <linux/types.h>
> >>>> +
> >>>> +/*
> >>>> + * Note cci_reg_8 deliberately is 0, not 1, so that raw
> >>>> + * (not wrapped in a CCI_REG*() macro) register addresses
> >>>> + * do 8 bit wide accesses. This allows unchanged use of register
> >>>> + * initialization lists of raw address, value pairs which only
> >>>> + * do 8 bit width accesses. Which makes porting drivers easier.
> >>>
> >>> It does, but at the same time, it prevents catching errors caused by
> >>> incorrect register macros. I'm tempted to consider that catching those
> >>> errors is more important.
> >>>
> >>>> + */
> >>>> +enum cci_reg_type {
> >>>> + cci_reg_8 = 0,
> >>>> + cci_reg_16,
> >>>> + cci_reg_24,
> >>>> + cci_reg_32,
> >>>> +};
> >>>> +
> >>>> +/*
> >>>> + * Macros to define register address with the register width encoded
> >>>> + * into the higher bits. CCI_REG8() is a no-op so its use is optional.
> >>>
> >>> Even if it's a no-op I'd prefer making its use mandatory. It makes
> >>> driver code more explicit, and eases catching issues during review.
> >>
> >> The problem is that almost all sensor drivers contain long list
> >> of register-address, -val pairs which they send to their own custom
> >> regmap_multi_reg_write()
> >>
> >> See e.g. the drivers/media/i2c/imx219.c (to stick with the imx
> >> theme from your imx290 request) this has a lot of quite long
> >> struct imx219_reg arrays with raw initializers.
> >>
> >> Often some or all of these registers in such list are
> >> undocumented (if we have access to a datasheet at all),
> >> so we simply don't know the register width.
> >>
> >> So arguably adding CCI_REG8(x) around all the addresses
> >> here is wrong, since this suggests we know the register
> >> width.
> >>
> >> With the current proposal to have 0 mean both unset and 8bit
> >> width this kinda register lists just work and converting
> >> the driver becomes just a matter of replacing e.g.
> >> imx219_write_regs() with cci_multi_reg_write().
> >>
> >> Where as otherwise we would need to add CCI_REG8(x)
> >> around the addresses which:
> >>
> >> a) Suggests we actually know the register width which
> >> we often do not know at all
> >>
> >> b) causes a ton of needless churn
> >>
> >> so I would very much prefer to keep this as as and
> >> allow unmarked register addresses.
> >>
> >> As for the CCI_REG8(x) being useful as an annotation
> >> during review you are of course free to enforce its
> >> use during review. And note that I did use it for
> >> all the OV2680_REG_FOO defines in both ov2680 conversions.
> >>
> >> I do agree enforcing its use makes sense for individual
> >> register address defines. The reason to make it optional
> >> and the place where I want it to be optional is for
> >> the array of raw register-addr + initializer-val pairs
> >> case.
> >
> > For register arrays, I'm fine with that. For register macros, I don't
> > want to see
> >
> > #define MY_WELL_DEFINED_8B_REG 0x1234
> >
> > For those I want drivers to use CCI_REG8(). It seems we're on the same
> > page :-)
>
> Right, but if we want cci_multi_reg_write() to work with register
> arrays without the CCI_REG8() macros then the code needs to stay
> as is and we cannot enforce use of the macro by erroring out
> if it is not used.
Could we have an internal __cci_reg_write() that doesn't check the size,
and a cci_reg_write() wrapper that does ?
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-12 15:16 ` Laurent Pinchart
@ 2023-06-12 15:33 ` Hans de Goede
2023-06-12 16:02 ` Laurent Pinchart
0 siblings, 1 reply; 38+ messages in thread
From: Hans de Goede @ 2023-06-12 15:33 UTC (permalink / raw)
To: Laurent Pinchart
Cc: Sakari Ailus, Mauro Carvalho Chehab, Andy Shevchenko, linux-media
Hi,
On 6/12/23 17:16, Laurent Pinchart wrote:
> Hi Hans,
>
> On Mon, Jun 12, 2023 at 03:48:01PM +0200, Hans de Goede wrote:
>> On 6/8/23 12:27, Laurent Pinchart wrote:
>>> On Wed, Jun 07, 2023 at 09:01:40PM +0200, Hans de Goede wrote:
>>>> On 6/7/23 20:18, Laurent Pinchart wrote:
>>
>> <snip>
>>
>>>>>> +int cci_update_bits(struct regmap *map, u32 reg, u32 mask, u32 val, int *err)
>>>>>> +{
>>>>>> + int width, ret;
>>>>>> + u32 readval;
>>>>>> +
>>>>>> + if (err && *err)
>>>>>> + return *err;
>>>>>> +
>>>>>> + /*
>>>>>> + * For single byte updates use regmap_update_bits(), this uses
>>>>>> + * the regmap-lock to protect against other read-modify-writes racing.
>>>>>> + */
>>>>>> + width = (reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT;
>>>>>> + if (width == cci_reg_8) {
>>>>>> + reg &= CCI_REG_ADDR_MASK;
>>>>>> + ret = regmap_update_bits(map, reg, mask, val);
>>>>>> + if (ret) {
>>>>>> + dev_err(regmap_get_device(map), "Error updating reg 0x%4x: %d\n", reg, ret);
>>>>>> + if (err)
>>>>>> + *err = ret;
>>>>>> + }
>>>>>> +
>>>>>> + return ret;
>>>>>> + }
>>>>>> +
>>>>>> + ret = cci_read(map, reg, &readval, err);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> +
>>>>>> + val = (readval & ~mask) | (val & mask);
>>>>>> +
>>>>>> + return cci_write(map, reg, val, err);
>>>>>
>>>>> Unless I'm mistaken, the regmap cache isn't used. This makes update
>>>>> operations fairly costly due to the read. Could that be improved ?
>>>>
>>>> The problem is that some registers may be volatile,
>>>> think e.g. expsoure on a sensor where auto-exposure is supported.
>>>>
>>>> So normally drivers which want to use regmap caching, also
>>>> provide a whole bunch of tables describing the registers
>>>> (lists of volatile + list of writable + list of readable
>>>> registers).
>>>>
>>>> So enabling caching is not trivial. I think that it would be best
>>>> for drivers which want that to supply their own regmap_config config
>>>> and directly call devm_regmap_init_i2c() if they then use
>>>> the resulting regmaps with the existing cci_* helpers then caching
>>>> will be used automatically.
>>>
>>> Would there be a way to use the cache for update operations (as I think
>>> we can consider that registers used in those operations won't be
>>> volatile), and bypass it for standalone reads ?
>>
>> There is not really a nice way to only use the cache for update operations.
>>
>> I guess we could do something hacky like tell regmap to create a cache,
>> then set the cache-bypass flag and drop the cache-bypass flag during
>> update ops. But that really is abusing the regmap API.
>>
>> Generally speaking update operations don't happen that often though,
>> so IMHO hacking to get this cached is not worth it.
>
> I2C reads are slow, so even if they're not very common, it would be nice
> to avoid them. We can start without any caching and improve it later,
> I'm fine with that.
>
>>>>>> +}
>>>>>> +EXPORT_SYMBOL_GPL(cci_update_bits);
>>>>>> +
>>>>>> +int cci_multi_reg_write(struct regmap *map, const struct reg_sequence *regs, int num_regs, int *err)
>>>>>> +{
>>>>>> + int i, ret;
>>>>>> +
>>>>>> + if (err && *err)
>>>>>> + return *err;
>>>>>> +
>>>>>> + for (i = 0; i < num_regs; i++) {
>>>>>> + ret = cci_write(map, regs[i].reg, regs[i].def, err);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> +
>>>>>> + if (regs[i].delay_us)
>>>>>> + fsleep(regs[i].delay_us);
>>>>>
>>>>> Do you have an immediate need for this ? If not, I'd drop support for
>>>>> the delay, and add it later when and if needed. It will be easier to
>>>>> discuss the API and use cases with a real user.
>>>>
>>>> This is a 1:1 mirror of regmap_multi_reg_write() note this uses
>>>> the existing struct reg_sequence delay_us field and the:
>>>>
>>>> if (regs[i].delay_us)
>>>> fsleep(regs[i].delay_us);
>>>>
>>>> is copied from the implementation of regmap_multi_reg_write()
>>>
>>> The reason why I don't like it much as that such delays are often hacks
>>> hidden in the middle of register arrays that should in many cases be
>>> handled differently. I was hoping that, by not supporting them yet,
>>> we'll have an easier time to get drivers right. Maybe I'm wrong.
>>
>> I understand, but having this is more or less a downside of
>> the choice to mirror the regmap API as close as possible.
>>
>> As Sakari said, having the field there just to ignore it
>> seems like a bad idea.
>
> I'm not sure to agree here, if we see no valid use case for that field,
> why would it be a bad idea to ignore it ? That shouldn't affect anyone.
I'm fine with dropping the fsleep() here, but IMHO it should
be replaced with a warning if it is missing do we want dev_warn
or WARN_ON() here ? Since setting it would be considered
a driver bug I guess WARN_ON() ?
>
>> I think that this being abused is something to watch for during
>> review, rather then enforcing it not being used in the CCI code.
>>
>>>>>> + }
>>>>>> +
>>>>>> + return 0;
>>>>>> +}
>>>>>> +EXPORT_SYMBOL_GPL(cci_multi_reg_write);
>>>>>> +
>>>>>> +struct regmap *cci_regmap_init_i2c(struct i2c_client *client, int reg_addr_bits)
>>>>>> +{
>>>>>> + struct regmap_config config = {
>>>>>> + .reg_bits = reg_addr_bits,
>>>>>> + .val_bits = 8,
>>>>>> + .reg_format_endian = REGMAP_ENDIAN_BIG,
>>>>>> + };
>>>>>> +
>>>>>> + return devm_regmap_init_i2c(client, &config);
>>>>>> +}
>>>>>> +EXPORT_SYMBOL_GPL(cci_regmap_init_i2c);
>>>>>> +
>>>>>> +MODULE_LICENSE("GPL");
>>>>>> +MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
>>>>>> diff --git a/include/media/v4l2-cci.h b/include/media/v4l2-cci.h
>>>>>> new file mode 100644
>>>>>> index 000000000000..69b8a7c4a013
>>>>>> --- /dev/null
>>>>>> +++ b/include/media/v4l2-cci.h
>>>>>> @@ -0,0 +1,109 @@
>>>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>>>> +/*
>>>>>> + * MIPI Camera Control Interface (CCI) register access helpers.
>>>>>> + *
>>>>>> + * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
>>>>>> + */
>>>>>> +#ifndef _V4L2_CCI_H
>>>>>> +#define _V4L2_CCI_H
>>>>>> +
>>>>>> +#include <linux/regmap.h>
>>>>>> +#include <linux/types.h>
>>>>>> +
>>>>>> +/*
>>>>>> + * Note cci_reg_8 deliberately is 0, not 1, so that raw
>>>>>> + * (not wrapped in a CCI_REG*() macro) register addresses
>>>>>> + * do 8 bit wide accesses. This allows unchanged use of register
>>>>>> + * initialization lists of raw address, value pairs which only
>>>>>> + * do 8 bit width accesses. Which makes porting drivers easier.
>>>>>
>>>>> It does, but at the same time, it prevents catching errors caused by
>>>>> incorrect register macros. I'm tempted to consider that catching those
>>>>> errors is more important.
>>>>>
>>>>>> + */
>>>>>> +enum cci_reg_type {
>>>>>> + cci_reg_8 = 0,
>>>>>> + cci_reg_16,
>>>>>> + cci_reg_24,
>>>>>> + cci_reg_32,
>>>>>> +};
>>>>>> +
>>>>>> +/*
>>>>>> + * Macros to define register address with the register width encoded
>>>>>> + * into the higher bits. CCI_REG8() is a no-op so its use is optional.
>>>>>
>>>>> Even if it's a no-op I'd prefer making its use mandatory. It makes
>>>>> driver code more explicit, and eases catching issues during review.
>>>>
>>>> The problem is that almost all sensor drivers contain long list
>>>> of register-address, -val pairs which they send to their own custom
>>>> regmap_multi_reg_write()
>>>>
>>>> See e.g. the drivers/media/i2c/imx219.c (to stick with the imx
>>>> theme from your imx290 request) this has a lot of quite long
>>>> struct imx219_reg arrays with raw initializers.
>>>>
>>>> Often some or all of these registers in such list are
>>>> undocumented (if we have access to a datasheet at all),
>>>> so we simply don't know the register width.
>>>>
>>>> So arguably adding CCI_REG8(x) around all the addresses
>>>> here is wrong, since this suggests we know the register
>>>> width.
>>>>
>>>> With the current proposal to have 0 mean both unset and 8bit
>>>> width this kinda register lists just work and converting
>>>> the driver becomes just a matter of replacing e.g.
>>>> imx219_write_regs() with cci_multi_reg_write().
>>>>
>>>> Where as otherwise we would need to add CCI_REG8(x)
>>>> around the addresses which:
>>>>
>>>> a) Suggests we actually know the register width which
>>>> we often do not know at all
>>>>
>>>> b) causes a ton of needless churn
>>>>
>>>> so I would very much prefer to keep this as as and
>>>> allow unmarked register addresses.
>>>>
>>>> As for the CCI_REG8(x) being useful as an annotation
>>>> during review you are of course free to enforce its
>>>> use during review. And note that I did use it for
>>>> all the OV2680_REG_FOO defines in both ov2680 conversions.
>>>>
>>>> I do agree enforcing its use makes sense for individual
>>>> register address defines. The reason to make it optional
>>>> and the place where I want it to be optional is for
>>>> the array of raw register-addr + initializer-val pairs
>>>> case.
>>>
>>> For register arrays, I'm fine with that. For register macros, I don't
>>> want to see
>>>
>>> #define MY_WELL_DEFINED_8B_REG 0x1234
>>>
>>> For those I want drivers to use CCI_REG8(). It seems we're on the same
>>> page :-)
>>
>> Right, but if we want cci_multi_reg_write() to work with register
>> arrays without the CCI_REG8() macros then the code needs to stay
>> as is and we cannot enforce use of the macro by erroring out
>> if it is not used.
>
> Could we have an internal __cci_reg_write() that doesn't check the size,
> and a cci_reg_write() wrapper that does ?
And then make cci_multi_reg_write() use the non checking version I presume?
Yes that is possible / should work.
Or alternatively we could make drivers which use raw register init lists
(with the reg-width macros) directly call regmap_multi_reg_write() since
all register writes are expected to be 8 bit wide in that case directly
calling regmap_multi_reg_write() should work fine too.
And then we can have all the cci_ prefixed versions always check
that a reg-width has been specified, which would be more consistent.
Regards,
Hans
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-12 15:33 ` Hans de Goede
@ 2023-06-12 16:02 ` Laurent Pinchart
0 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2023-06-12 16:02 UTC (permalink / raw)
To: Hans de Goede
Cc: Sakari Ailus, Mauro Carvalho Chehab, Andy Shevchenko, linux-media
Hi Hans,
On Mon, Jun 12, 2023 at 05:33:40PM +0200, Hans de Goede wrote:
> On 6/12/23 17:16, Laurent Pinchart wrote:
> > On Mon, Jun 12, 2023 at 03:48:01PM +0200, Hans de Goede wrote:
> >> On 6/8/23 12:27, Laurent Pinchart wrote:
> >>> On Wed, Jun 07, 2023 at 09:01:40PM +0200, Hans de Goede wrote:
> >>>> On 6/7/23 20:18, Laurent Pinchart wrote:
> >>
> >> <snip>
> >>
> >>>>>> +int cci_update_bits(struct regmap *map, u32 reg, u32 mask, u32 val, int *err)
> >>>>>> +{
> >>>>>> + int width, ret;
> >>>>>> + u32 readval;
> >>>>>> +
> >>>>>> + if (err && *err)
> >>>>>> + return *err;
> >>>>>> +
> >>>>>> + /*
> >>>>>> + * For single byte updates use regmap_update_bits(), this uses
> >>>>>> + * the regmap-lock to protect against other read-modify-writes racing.
> >>>>>> + */
> >>>>>> + width = (reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT;
> >>>>>> + if (width == cci_reg_8) {
> >>>>>> + reg &= CCI_REG_ADDR_MASK;
> >>>>>> + ret = regmap_update_bits(map, reg, mask, val);
> >>>>>> + if (ret) {
> >>>>>> + dev_err(regmap_get_device(map), "Error updating reg 0x%4x: %d\n", reg, ret);
> >>>>>> + if (err)
> >>>>>> + *err = ret;
> >>>>>> + }
> >>>>>> +
> >>>>>> + return ret;
> >>>>>> + }
> >>>>>> +
> >>>>>> + ret = cci_read(map, reg, &readval, err);
> >>>>>> + if (ret)
> >>>>>> + return ret;
> >>>>>> +
> >>>>>> + val = (readval & ~mask) | (val & mask);
> >>>>>> +
> >>>>>> + return cci_write(map, reg, val, err);
> >>>>>
> >>>>> Unless I'm mistaken, the regmap cache isn't used. This makes update
> >>>>> operations fairly costly due to the read. Could that be improved ?
> >>>>
> >>>> The problem is that some registers may be volatile,
> >>>> think e.g. expsoure on a sensor where auto-exposure is supported.
> >>>>
> >>>> So normally drivers which want to use regmap caching, also
> >>>> provide a whole bunch of tables describing the registers
> >>>> (lists of volatile + list of writable + list of readable
> >>>> registers).
> >>>>
> >>>> So enabling caching is not trivial. I think that it would be best
> >>>> for drivers which want that to supply their own regmap_config config
> >>>> and directly call devm_regmap_init_i2c() if they then use
> >>>> the resulting regmaps with the existing cci_* helpers then caching
> >>>> will be used automatically.
> >>>
> >>> Would there be a way to use the cache for update operations (as I think
> >>> we can consider that registers used in those operations won't be
> >>> volatile), and bypass it for standalone reads ?
> >>
> >> There is not really a nice way to only use the cache for update operations.
> >>
> >> I guess we could do something hacky like tell regmap to create a cache,
> >> then set the cache-bypass flag and drop the cache-bypass flag during
> >> update ops. But that really is abusing the regmap API.
> >>
> >> Generally speaking update operations don't happen that often though,
> >> so IMHO hacking to get this cached is not worth it.
> >
> > I2C reads are slow, so even if they're not very common, it would be nice
> > to avoid them. We can start without any caching and improve it later,
> > I'm fine with that.
> >
> >>>>>> +}
> >>>>>> +EXPORT_SYMBOL_GPL(cci_update_bits);
> >>>>>> +
> >>>>>> +int cci_multi_reg_write(struct regmap *map, const struct reg_sequence *regs, int num_regs, int *err)
> >>>>>> +{
> >>>>>> + int i, ret;
> >>>>>> +
> >>>>>> + if (err && *err)
> >>>>>> + return *err;
> >>>>>> +
> >>>>>> + for (i = 0; i < num_regs; i++) {
> >>>>>> + ret = cci_write(map, regs[i].reg, regs[i].def, err);
> >>>>>> + if (ret)
> >>>>>> + return ret;
> >>>>>> +
> >>>>>> + if (regs[i].delay_us)
> >>>>>> + fsleep(regs[i].delay_us);
> >>>>>
> >>>>> Do you have an immediate need for this ? If not, I'd drop support for
> >>>>> the delay, and add it later when and if needed. It will be easier to
> >>>>> discuss the API and use cases with a real user.
> >>>>
> >>>> This is a 1:1 mirror of regmap_multi_reg_write() note this uses
> >>>> the existing struct reg_sequence delay_us field and the:
> >>>>
> >>>> if (regs[i].delay_us)
> >>>> fsleep(regs[i].delay_us);
> >>>>
> >>>> is copied from the implementation of regmap_multi_reg_write()
> >>>
> >>> The reason why I don't like it much as that such delays are often hacks
> >>> hidden in the middle of register arrays that should in many cases be
> >>> handled differently. I was hoping that, by not supporting them yet,
> >>> we'll have an easier time to get drivers right. Maybe I'm wrong.
> >>
> >> I understand, but having this is more or less a downside of
> >> the choice to mirror the regmap API as close as possible.
> >>
> >> As Sakari said, having the field there just to ignore it
> >> seems like a bad idea.
> >
> > I'm not sure to agree here, if we see no valid use case for that field,
> > why would it be a bad idea to ignore it ? That shouldn't affect anyone.
>
> I'm fine with dropping the fsleep() here, but IMHO it should
> be replaced with a warning if it is missing do we want dev_warn
> or WARN_ON() here ? Since setting it would be considered
> a driver bug I guess WARN_ON() ?
Sounds good to me.
> >> I think that this being abused is something to watch for during
> >> review, rather then enforcing it not being used in the CCI code.
> >>
> >>>>>> + }
> >>>>>> +
> >>>>>> + return 0;
> >>>>>> +}
> >>>>>> +EXPORT_SYMBOL_GPL(cci_multi_reg_write);
> >>>>>> +
> >>>>>> +struct regmap *cci_regmap_init_i2c(struct i2c_client *client, int reg_addr_bits)
> >>>>>> +{
> >>>>>> + struct regmap_config config = {
> >>>>>> + .reg_bits = reg_addr_bits,
> >>>>>> + .val_bits = 8,
> >>>>>> + .reg_format_endian = REGMAP_ENDIAN_BIG,
> >>>>>> + };
> >>>>>> +
> >>>>>> + return devm_regmap_init_i2c(client, &config);
> >>>>>> +}
> >>>>>> +EXPORT_SYMBOL_GPL(cci_regmap_init_i2c);
> >>>>>> +
> >>>>>> +MODULE_LICENSE("GPL");
> >>>>>> +MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
> >>>>>> diff --git a/include/media/v4l2-cci.h b/include/media/v4l2-cci.h
> >>>>>> new file mode 100644
> >>>>>> index 000000000000..69b8a7c4a013
> >>>>>> --- /dev/null
> >>>>>> +++ b/include/media/v4l2-cci.h
> >>>>>> @@ -0,0 +1,109 @@
> >>>>>> +/* SPDX-License-Identifier: GPL-2.0 */
> >>>>>> +/*
> >>>>>> + * MIPI Camera Control Interface (CCI) register access helpers.
> >>>>>> + *
> >>>>>> + * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
> >>>>>> + */
> >>>>>> +#ifndef _V4L2_CCI_H
> >>>>>> +#define _V4L2_CCI_H
> >>>>>> +
> >>>>>> +#include <linux/regmap.h>
> >>>>>> +#include <linux/types.h>
> >>>>>> +
> >>>>>> +/*
> >>>>>> + * Note cci_reg_8 deliberately is 0, not 1, so that raw
> >>>>>> + * (not wrapped in a CCI_REG*() macro) register addresses
> >>>>>> + * do 8 bit wide accesses. This allows unchanged use of register
> >>>>>> + * initialization lists of raw address, value pairs which only
> >>>>>> + * do 8 bit width accesses. Which makes porting drivers easier.
> >>>>>
> >>>>> It does, but at the same time, it prevents catching errors caused by
> >>>>> incorrect register macros. I'm tempted to consider that catching those
> >>>>> errors is more important.
> >>>>>
> >>>>>> + */
> >>>>>> +enum cci_reg_type {
> >>>>>> + cci_reg_8 = 0,
> >>>>>> + cci_reg_16,
> >>>>>> + cci_reg_24,
> >>>>>> + cci_reg_32,
> >>>>>> +};
> >>>>>> +
> >>>>>> +/*
> >>>>>> + * Macros to define register address with the register width encoded
> >>>>>> + * into the higher bits. CCI_REG8() is a no-op so its use is optional.
> >>>>>
> >>>>> Even if it's a no-op I'd prefer making its use mandatory. It makes
> >>>>> driver code more explicit, and eases catching issues during review.
> >>>>
> >>>> The problem is that almost all sensor drivers contain long list
> >>>> of register-address, -val pairs which they send to their own custom
> >>>> regmap_multi_reg_write()
> >>>>
> >>>> See e.g. the drivers/media/i2c/imx219.c (to stick with the imx
> >>>> theme from your imx290 request) this has a lot of quite long
> >>>> struct imx219_reg arrays with raw initializers.
> >>>>
> >>>> Often some or all of these registers in such list are
> >>>> undocumented (if we have access to a datasheet at all),
> >>>> so we simply don't know the register width.
> >>>>
> >>>> So arguably adding CCI_REG8(x) around all the addresses
> >>>> here is wrong, since this suggests we know the register
> >>>> width.
> >>>>
> >>>> With the current proposal to have 0 mean both unset and 8bit
> >>>> width this kinda register lists just work and converting
> >>>> the driver becomes just a matter of replacing e.g.
> >>>> imx219_write_regs() with cci_multi_reg_write().
> >>>>
> >>>> Where as otherwise we would need to add CCI_REG8(x)
> >>>> around the addresses which:
> >>>>
> >>>> a) Suggests we actually know the register width which
> >>>> we often do not know at all
> >>>>
> >>>> b) causes a ton of needless churn
> >>>>
> >>>> so I would very much prefer to keep this as as and
> >>>> allow unmarked register addresses.
> >>>>
> >>>> As for the CCI_REG8(x) being useful as an annotation
> >>>> during review you are of course free to enforce its
> >>>> use during review. And note that I did use it for
> >>>> all the OV2680_REG_FOO defines in both ov2680 conversions.
> >>>>
> >>>> I do agree enforcing its use makes sense for individual
> >>>> register address defines. The reason to make it optional
> >>>> and the place where I want it to be optional is for
> >>>> the array of raw register-addr + initializer-val pairs
> >>>> case.
> >>>
> >>> For register arrays, I'm fine with that. For register macros, I don't
> >>> want to see
> >>>
> >>> #define MY_WELL_DEFINED_8B_REG 0x1234
> >>>
> >>> For those I want drivers to use CCI_REG8(). It seems we're on the same
> >>> page :-)
> >>
> >> Right, but if we want cci_multi_reg_write() to work with register
> >> arrays without the CCI_REG8() macros then the code needs to stay
> >> as is and we cannot enforce use of the macro by erroring out
> >> if it is not used.
> >
> > Could we have an internal __cci_reg_write() that doesn't check the size,
> > and a cci_reg_write() wrapper that does ?
>
> And then make cci_multi_reg_write() use the non checking version I presume?
> Yes that is possible / should work.
>
> Or alternatively we could make drivers which use raw register init lists
> (with the reg-width macros) directly call regmap_multi_reg_write() since
> all register writes are expected to be 8 bit wide in that case directly
> calling regmap_multi_reg_write() should work fine too.
>
> And then we can have all the cci_ prefixed versions always check
> that a reg-width has been specified, which would be more consistent.
Sounds good to me too :-)
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] media: Add MIPI CCI register access helper functions
2023-06-12 15:03 ` Laurent Pinchart
@ 2023-06-13 9:57 ` Sakari Ailus
0 siblings, 0 replies; 38+ messages in thread
From: Sakari Ailus @ 2023-06-13 9:57 UTC (permalink / raw)
To: Laurent Pinchart
Cc: Hans de Goede, Mauro Carvalho Chehab, Andy Shevchenko,
linux-media
Hi Laurent,
On Mon, Jun 12, 2023 at 06:03:15PM +0300, Laurent Pinchart wrote:
> Hi Sakari,
>
> On Thu, Jun 08, 2023 at 11:01:29AM +0000, Sakari Ailus wrote:
> > On Thu, Jun 08, 2023 at 01:27:25PM +0300, Laurent Pinchart wrote:
> > > On Wed, Jun 07, 2023 at 09:01:40PM +0200, Hans de Goede wrote:
> > > > On 6/7/23 20:18, Laurent Pinchart wrote:
> > > > > On Tue, Jun 06, 2023 at 06:58:06PM +0200, Hans de Goede wrote:
> > > > >> The CSI2 specification specifies a standard method to access camera sensor
> > > > >> registers called "Camera Control Interface (CCI)".
> > > > >>
> > > > >> This uses either 8 or 16 bit (big-endian wire order) register addresses
> > > > >> and supports 8, 16, 24 or 32 bit (big-endian wire order) register widths.
> > > > >
> > > > > I think there are some sensors that also have 64-bit registers, but we
> > > > > can deal with that later.
> > > > >
> > > > >> Currently a lot of Linux camera sensor drivers all have their own custom
> > > > >> helpers for this, often copy and pasted from other drivers.
> > > > >>
> > > > >> Add a set of generic helpers for this so that all sensor drivers can
> > > > >> switch to a single common implementation.
> > > > >>
> > > > >> These helpers take an extra optional "int *err" function parameter,
> > > > >> this can be used to chain a bunch of register accesses together with
> > > > >> only a single error check at the end, rather then needing to error
> > > > >> check each individual register access. The first failing call will
> > > > >> set the contents of err to a non 0 value and all other calls will
> > > > >> then become no-ops.
> > > > >>
> > > > >> Link: https://lore.kernel.org/linux-media/59aefa7f-7bf9-6736-6040-39551329cd0a@redhat.com/
> > > > >> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> > > > >> ---
> > > > >> Documentation/driver-api/media/v4l2-cci.rst | 5 +
> > > > >> Documentation/driver-api/media/v4l2-core.rst | 1 +
> > > > >> drivers/media/v4l2-core/Kconfig | 5 +
> > > > >> drivers/media/v4l2-core/Makefile | 1 +
> > > > >> drivers/media/v4l2-core/v4l2-cci.c | 142 +++++++++++++++++++
> > > > >> include/media/v4l2-cci.h | 109 ++++++++++++++
> > > > >> 6 files changed, 263 insertions(+)
> > > > >> create mode 100644 Documentation/driver-api/media/v4l2-cci.rst
> > > > >> create mode 100644 drivers/media/v4l2-core/v4l2-cci.c
> > > > >> create mode 100644 include/media/v4l2-cci.h
> > > > >>
> > > > >> diff --git a/Documentation/driver-api/media/v4l2-cci.rst b/Documentation/driver-api/media/v4l2-cci.rst
> > > > >> new file mode 100644
> > > > >> index 000000000000..dd297a40ed20
> > > > >> --- /dev/null
> > > > >> +++ b/Documentation/driver-api/media/v4l2-cci.rst
> > > > >> @@ -0,0 +1,5 @@
> > > > >> +.. SPDX-License-Identifier: GPL-2.0
> > > > >> +
> > > > >> +V4L2 CCI kAPI
> > > > >> +^^^^^^^^^^^^^
> > > > >> +.. kernel-doc:: include/media/v4l2-cci.h
> > > > >> diff --git a/Documentation/driver-api/media/v4l2-core.rst b/Documentation/driver-api/media/v4l2-core.rst
> > > > >> index 1a8c4a5f256b..239045ecc8f4 100644
> > > > >> --- a/Documentation/driver-api/media/v4l2-core.rst
> > > > >> +++ b/Documentation/driver-api/media/v4l2-core.rst
> > > > >> @@ -22,6 +22,7 @@ Video4Linux devices
> > > > >> v4l2-mem2mem
> > > > >> v4l2-async
> > > > >> v4l2-fwnode
> > > > >> + v4l2-cci
> > > > >> v4l2-rect
> > > > >> v4l2-tuner
> > > > >> v4l2-common
> > > > >> diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
> > > > >> index 348559bc2468..523ba243261d 100644
> > > > >> --- a/drivers/media/v4l2-core/Kconfig
> > > > >> +++ b/drivers/media/v4l2-core/Kconfig
> > > > >> @@ -74,6 +74,11 @@ config V4L2_FWNODE
> > > > >> config V4L2_ASYNC
> > > > >> tristate
> > > > >>
> > > > >> +config V4L2_CCI
> > > > >> + tristate
> > > > >> + depends on I2C
> > > > >> + select REGMAP_I2C
> > > > >> +
> > > > >> # Used by drivers that need Videobuf modules
> > > > >> config VIDEOBUF_GEN
> > > > >> tristate
> > > > >> diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
> > > > >> index 41d91bd10cf2..be2551705755 100644
> > > > >> --- a/drivers/media/v4l2-core/Makefile
> > > > >> +++ b/drivers/media/v4l2-core/Makefile
> > > > >> @@ -25,6 +25,7 @@ videodev-$(CONFIG_VIDEO_V4L2_I2C) += v4l2-i2c.o
> > > > >> # (e. g. LC_ALL=C sort Makefile)
> > > > >>
> > > > >> obj-$(CONFIG_V4L2_ASYNC) += v4l2-async.o
> > > > >> +obj-$(CONFIG_V4L2_CCI) += v4l2-cci.o
> > > > >> obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash-led-class.o
> > > > >> obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o
> > > > >> obj-$(CONFIG_V4L2_H264) += v4l2-h264.o
> > > > >> diff --git a/drivers/media/v4l2-core/v4l2-cci.c b/drivers/media/v4l2-core/v4l2-cci.c
> > > > >> new file mode 100644
> > > > >> index 000000000000..21207d137dbe
> > > > >> --- /dev/null
> > > > >> +++ b/drivers/media/v4l2-core/v4l2-cci.c
> > > > >> @@ -0,0 +1,142 @@
> > > > >> +// SPDX-License-Identifier: GPL-2.0
> > > > >> +/*
> > > > >> + * MIPI Camera Control Interface (CCI) register access helpers.
> > > > >> + *
> > > > >> + * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
> > > > >> + */
> > > > >> +
> > > > >> +#include <linux/delay.h>
> > > > >> +#include <linux/dev_printk.h>
> > > > >> +#include <linux/module.h>
> > > > >> +#include <linux/regmap.h>
> > > > >> +
> > > > >> +#include <media/v4l2-cci.h>
> > > > >> +
> > > > >> +int cci_read(struct regmap *map, u32 reg, u32 *val, int *err)
> > > > >> +{
> > > > >> + int i, len, ret;
> > > > >> + u8 buf[4];
> > > > >> +
> > > > >> + if (err && *err)
> > > > >> + return *err;
> > > > >> +
> > > > >> + /* Set len to register width in bytes */
> > > > >> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
> > > > >> + reg &= CCI_REG_ADDR_MASK;
> > > > >> +
> > > > >> + ret = regmap_bulk_read(map, reg, buf, len);
> > > > >> + if (ret) {
> > > > >> + dev_err(regmap_get_device(map), "Error reading reg 0x%4x: %d\n", reg, ret);
> > > > >> + if (err)
> > > > >> + *err = ret;
> > > > >> +
> > > > >> + return ret;
> > > > >> + }
> > > > >> +
> > > > >> + *val = 0;
> > > > >> + for (i = 0; i < len; i++) {
> > > > >> + *val <<= 8;
> > > > >> + *val |= buf[i];
> > > > >> + }
> > > > >> +
> > > > >> + return 0;
> > > > >> +}
> > > > >> +EXPORT_SYMBOL_GPL(cci_read);
> > > > >> +
> > > > >> +int cci_write(struct regmap *map, u32 reg, u32 val, int *err)
> > > > >> +{
> > > > >> + int i, len, ret;
> > > > >> + u8 buf[4];
> > > > >> +
> > > > >> + if (err && *err)
> > > > >> + return *err;
> > > > >> +
> > > > >> + /* Set len to register width in bytes */
> > > > >> + len = ((reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT) + 1;
> > > > >> + reg &= CCI_REG_ADDR_MASK;
> > > > >> +
> > > > >> + for (i = 0; i < len; i++) {
> > > > >> + buf[len - i - 1] = val & 0xff;
> > > > >> + val >>= 8;
> > > > >> + }
> > > > >> +
> > > > >> + ret = regmap_bulk_write(map, reg, buf, len);
> > > > >> + if (ret) {
> > > > >> + dev_err(regmap_get_device(map), "Error writing reg 0x%4x: %d\n", reg, ret);
> > > > >> + if (err)
> > > > >> + *err = ret;
> > > > >> + }
> > > > >> +
> > > > >> + return ret;
> > > > >> +}
> > > > >> +EXPORT_SYMBOL_GPL(cci_write);
> > > > >> +
> > > > >> +int cci_update_bits(struct regmap *map, u32 reg, u32 mask, u32 val, int *err)
> > > > >> +{
> > > > >> + int width, ret;
> > > > >> + u32 readval;
> > > > >> +
> > > > >> + if (err && *err)
> > > > >> + return *err;
> > > > >> +
> > > > >> + /*
> > > > >> + * For single byte updates use regmap_update_bits(), this uses
> > > > >> + * the regmap-lock to protect against other read-modify-writes racing.
> > > > >> + */
> > > > >> + width = (reg & CCI_REG_WIDTH_MASK) >> CCI_REG_WIDTH_SHIFT;
> > > > >> + if (width == cci_reg_8) {
> > > > >> + reg &= CCI_REG_ADDR_MASK;
> > > > >> + ret = regmap_update_bits(map, reg, mask, val);
> > > > >> + if (ret) {
> > > > >> + dev_err(regmap_get_device(map), "Error updating reg 0x%4x: %d\n", reg, ret);
> > > > >> + if (err)
> > > > >> + *err = ret;
> > > > >> + }
> > > > >> +
> > > > >> + return ret;
> > > > >> + }
> > > > >> +
> > > > >> + ret = cci_read(map, reg, &readval, err);
> > > > >> + if (ret)
> > > > >> + return ret;
> > > > >> +
> > > > >> + val = (readval & ~mask) | (val & mask);
> > > > >> +
> > > > >> + return cci_write(map, reg, val, err);
> > > > >
> > > > > Unless I'm mistaken, the regmap cache isn't used. This makes update
> > > > > operations fairly costly due to the read. Could that be improved ?
> > > >
> > > > The problem is that some registers may be volatile,
> > > > think e.g. expsoure on a sensor where auto-exposure is supported.
> > > >
> > > > So normally drivers which want to use regmap caching, also
> > > > provide a whole bunch of tables describing the registers
> > > > (lists of volatile + list of writable + list of readable
> > > > registers).
> > > >
> > > > So enabling caching is not trivial. I think that it would be best
> > > > for drivers which want that to supply their own regmap_config config
> > > > and directly call devm_regmap_init_i2c() if they then use
> > > > the resulting regmaps with the existing cci_* helpers then caching
> > > > will be used automatically.
> > >
> > > Would there be a way to use the cache for update operations (as I think
> > > we can consider that registers used in those operations won't be
> > > volatile), and bypass it for standalone reads ?
> >
> > Could we rely on regmap on this? It provides a way to tell which registers
> > are volatile. Very few of these drivers would get any benefit from caching
> > anyway (or even use update_bits()).
>
> Yes we could I suppose. I've never been a big fan of that part of the
> regmap API as it's cumbersome to use when dealing with devices that have
> lots of registers, like camera sensors usually do. Volatile registers
> tend to be scattered around the address space, making the volatile_table
> lookup inefficient, and the volatile_reg function wouldn't be great
> either. I could live with that I suppose, but given that I think we can
You can still have a switch there. And for larger registers all octets need
to be included.
> expect read-modify-write operations to never operate on a volatile
> register, and plain read operations to nearly always do (with the
> exception of read-only version registers that are read once at probe
> time only), it would be a bit of a shame to rely on the less efficient
> regmap volatile support.
The driver would need to enable register cache and set up the volatile
registers. It is nearly always easier to just write the full value of that
register instead. So almost always if someone feels they need this, they're
doing something wrong. That's why I'm not too concerned that this is
difficult to set up.
>
> > > > >> +}
> > > > >> +EXPORT_SYMBOL_GPL(cci_update_bits);
> > > > >> +
> > > > >> +int cci_multi_reg_write(struct regmap *map, const struct reg_sequence *regs, int num_regs, int *err)
> > > > >> +{
> > > > >> + int i, ret;
> > > > >> +
> > > > >> + if (err && *err)
> > > > >> + return *err;
> > > > >> +
> > > > >> + for (i = 0; i < num_regs; i++) {
> > > > >> + ret = cci_write(map, regs[i].reg, regs[i].def, err);
> > > > >> + if (ret)
> > > > >> + return ret;
> > > > >> +
> > > > >> + if (regs[i].delay_us)
> > > > >> + fsleep(regs[i].delay_us);
> > > > >
> > > > > Do you have an immediate need for this ? If not, I'd drop support for
> > > > > the delay, and add it later when and if needed. It will be easier to
> > > > > discuss the API and use cases with a real user.
> > > >
> > > > This is a 1:1 mirror of regmap_multi_reg_write() note this uses
> > > > the existing struct reg_sequence delay_us field and the:
> > > >
> > > > if (regs[i].delay_us)
> > > > fsleep(regs[i].delay_us);
> > > >
> > > > is copied from the implementation of regmap_multi_reg_write()
> > >
> > > The reason why I don't like it much as that such delays are often hacks
> > > hidden in the middle of register arrays that should in many cases be
> > > handled differently. I was hoping that, by not supporting them yet,
> > > we'll have an easier time to get drivers right. Maybe I'm wrong.
> >
> > It's not uncommon for a sensor to require e.g. a given amount of time to
> > recover from software reset. Then again, embedding software in a register
> > list can hardly be described as a good practice. There maybe other such
> > cases, too.
>
> That's exactly my concern, I'd like to avoid giving an easy option for
> people to embed reset actions in a large registers table :-) I don't
> object to the feature if we find valid use cases for it.
>
> > But the field already exists in the struct. I don't object acting based on
> > its contents as such.
> >
> > > > >> + }
> > > > >> +
> > > > >> + return 0;
> > > > >> +}
> > > > >> +EXPORT_SYMBOL_GPL(cci_multi_reg_write);
> > > > >> +
> > > > >> +struct regmap *cci_regmap_init_i2c(struct i2c_client *client, int reg_addr_bits)
> > > > >> +{
> > > > >> + struct regmap_config config = {
> > > > >> + .reg_bits = reg_addr_bits,
> > > > >> + .val_bits = 8,
> > > > >> + .reg_format_endian = REGMAP_ENDIAN_BIG,
> > > > >> + };
> > > > >> +
> > > > >> + return devm_regmap_init_i2c(client, &config);
> > > > >> +}
> > > > >> +EXPORT_SYMBOL_GPL(cci_regmap_init_i2c);
> > > > >> +
> > > > >> +MODULE_LICENSE("GPL");
> > > > >> +MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
> > > > >> diff --git a/include/media/v4l2-cci.h b/include/media/v4l2-cci.h
> > > > >> new file mode 100644
> > > > >> index 000000000000..69b8a7c4a013
> > > > >> --- /dev/null
> > > > >> +++ b/include/media/v4l2-cci.h
> > > > >> @@ -0,0 +1,109 @@
> > > > >> +/* SPDX-License-Identifier: GPL-2.0 */
> > > > >> +/*
> > > > >> + * MIPI Camera Control Interface (CCI) register access helpers.
> > > > >> + *
> > > > >> + * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
> > > > >> + */
> > > > >> +#ifndef _V4L2_CCI_H
> > > > >> +#define _V4L2_CCI_H
> > > > >> +
> > > > >> +#include <linux/regmap.h>
> > > > >> +#include <linux/types.h>
> > > > >> +
> > > > >> +/*
> > > > >> + * Note cci_reg_8 deliberately is 0, not 1, so that raw
> > > > >> + * (not wrapped in a CCI_REG*() macro) register addresses
> > > > >> + * do 8 bit wide accesses. This allows unchanged use of register
> > > > >> + * initialization lists of raw address, value pairs which only
> > > > >> + * do 8 bit width accesses. Which makes porting drivers easier.
> > > > >
> > > > > It does, but at the same time, it prevents catching errors caused by
> > > > > incorrect register macros. I'm tempted to consider that catching those
> > > > > errors is more important.
> > > > >
> > > > >> + */
> > > > >> +enum cci_reg_type {
> > > > >> + cci_reg_8 = 0,
> > > > >> + cci_reg_16,
> > > > >> + cci_reg_24,
> > > > >> + cci_reg_32,
> > > > >> +};
> > > > >> +
> > > > >> +/*
> > > > >> + * Macros to define register address with the register width encoded
> > > > >> + * into the higher bits. CCI_REG8() is a no-op so its use is optional.
> > > > >
> > > > > Even if it's a no-op I'd prefer making its use mandatory. It makes
> > > > > driver code more explicit, and eases catching issues during review.
> > > >
> > > > The problem is that almost all sensor drivers contain long list
> > > > of register-address, -val pairs which they send to their own custom
> > > > regmap_multi_reg_write()
> > > >
> > > > See e.g. the drivers/media/i2c/imx219.c (to stick with the imx
> > > > theme from your imx290 request) this has a lot of quite long
> > > > struct imx219_reg arrays with raw initializers.
> > > >
> > > > Often some or all of these registers in such list are
> > > > undocumented (if we have access to a datasheet at all),
> > > > so we simply don't know the register width.
> > > >
> > > > So arguably adding CCI_REG8(x) around all the addresses
> > > > here is wrong, since this suggests we know the register
> > > > width.
> > > >
> > > > With the current proposal to have 0 mean both unset and 8bit
> > > > width this kinda register lists just work and converting
> > > > the driver becomes just a matter of replacing e.g.
> > > > imx219_write_regs() with cci_multi_reg_write().
> > > >
> > > > Where as otherwise we would need to add CCI_REG8(x)
> > > > around the addresses which:
> > > >
> > > > a) Suggests we actually know the register width which
> > > > we often do not know at all
> > > >
> > > > b) causes a ton of needless churn
> > > >
> > > > so I would very much prefer to keep this as as and
> > > > allow unmarked register addresses.
> > > >
> > > > As for the CCI_REG8(x) being useful as an annotation
> > > > during review you are of course free to enforce its
> > > > use during review. And note that I did use it for
> > > > all the OV2680_REG_FOO defines in both ov2680 conversions.
> > > >
> > > > I do agree enforcing its use makes sense for individual
> > > > register address defines. The reason to make it optional
> > > > and the place where I want it to be optional is for
> > > > the array of raw register-addr + initializer-val pairs
> > > > case.
> > >
> > > For register arrays, I'm fine with that. For register macros, I don't
> > > want to see
> > >
> > > #define MY_WELL_DEFINED_8B_REG 0x1234
> > >
> > > For those I want drivers to use CCI_REG8(). It seems we're on the same
> > > page :-)
> >
> > For a register list based sensor driver, I don't really mind even missing
> > this in the register lists. Using that macro doesn't help when the problem
> > really is a very long list of random-looking numbers.
>
> I think we agree here, the place where I want to see the macros being
> used without exceptions is when accessing individual registers.
I believe we do, yes.
--
Kind regards,
Sakari Ailus
^ permalink raw reply [flat|nested] 38+ messages in thread
end of thread, other threads:[~2023-06-13 9:59 UTC | newest]
Thread overview: 38+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-06-06 16:58 [PATCH 0/3] media: Add MIPI CCI register access helper functions Hans de Goede
2023-06-06 16:58 ` [PATCH 1/3] " Hans de Goede
2023-06-06 20:43 ` Andy Shevchenko
2023-06-07 8:40 ` Hans de Goede
2023-06-07 12:01 ` Sakari Ailus
2023-06-07 14:55 ` Laurent Pinchart
2023-06-07 15:40 ` Andy Shevchenko
2023-06-07 15:58 ` Hans de Goede
2023-06-07 16:14 ` Andy Shevchenko
2023-06-07 16:20 ` Hans de Goede
2023-06-07 18:03 ` Andy Shevchenko
2023-06-07 7:50 ` Sakari Ailus
2023-06-07 8:46 ` Hans de Goede
2023-06-07 11:41 ` Sakari Ailus
2023-06-07 18:18 ` Laurent Pinchart
2023-06-07 19:01 ` Hans de Goede
2023-06-07 20:07 ` Andy Shevchenko
2023-06-08 8:33 ` Hans de Goede
2023-06-08 8:33 ` Sakari Ailus
2023-06-08 10:24 ` Andy Shevchenko
2023-06-08 10:27 ` Laurent Pinchart
2023-06-08 11:01 ` Sakari Ailus
2023-06-12 15:03 ` Laurent Pinchart
2023-06-13 9:57 ` Sakari Ailus
2023-06-12 13:48 ` Hans de Goede
2023-06-12 15:16 ` Laurent Pinchart
2023-06-12 15:33 ` Hans de Goede
2023-06-12 16:02 ` Laurent Pinchart
2023-06-06 16:58 ` [PATCH 2/3] media: atomisp: ov2680: Convert to new CCI register access helpers Hans de Goede
2023-06-06 20:53 ` Andy Shevchenko
2023-06-07 8:53 ` Hans de Goede
2023-06-07 15:51 ` Laurent Pinchart
2023-06-07 15:59 ` Hans de Goede
2023-06-07 16:07 ` Laurent Pinchart
2023-06-07 16:05 ` Laurent Pinchart
2023-06-07 16:18 ` Hans de Goede
2023-06-06 16:58 ` [PATCH 3/3] media: Remove ov_16bit_addr_reg_helpers.h Hans de Goede
2023-06-07 15:57 ` Laurent Pinchart
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox