All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/30] Trivial MIPI CCS support
@ 2020-11-24  9:31 Sakari Ailus
  2020-11-24  9:31 ` [PATCH 01/30] ccs: Add MIPI CCS compatible strings Sakari Ailus
                   ` (29 more replies)
  0 siblings, 30 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:31 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

Hello everyone,

Here's a set of patches that turn the existing SMIA driver into a MIPI CCS
driver while maintaining SMIA support. A number of bugs in the existing
code are fixed in this set, too.

The changes at this point are primarily focused on dealing with new
mandatory driver features related to PLL configuration (as CCS allows for
much more variation there) and things such as integer conversion from
U16.U16 format instead of float. There are some other new features as well
such as digital gain and support for getting device specific analogue gain
coefficients.

A new feature in CCS is CCS static data which makes it possible to obtain
sensor's capabilities and limits from a file chosen based on sensor
identification. CCS static data is used also for storing MSR registers so
supporting new, CCS compliant devices requires no driver changes.

Note that the library as well as the register definitions are dual
licensed under GNU GPL v2 OR BSD 3-clause licenses for use outside the
Linux kernel.

Also DT bindings are updated accordingly and converted to YAML format.

More information on MIPI CCS can be found here:

<URL:https://www.mipi.org/specifications/camera-command-set>

Comments are welcome.

since the big, big patchset (v2):

- Split into more easily reviewable chunks (this is the second of maybe
  three). The cover page describes the entire big set. This set contains
  new DT compatible strings, CCS ACPI ID, rudimentary support for CCS
  (without PLL changes most sensors need, that's for later), including CCS
  static data.

- Fix SPDX tags. Some were left accidentally with BSD-3-Clause license
  only.

- Remove WARN_ON() from snprintf(), but return an error instead.

- Free loaded static data on error as well.

- Add descriptions to ccs-data-defs.h and ccs-data.h to document the
  difference between the two.

- Make the delay after I²C transfer error a range.

- Make better use of kernel support functions.

- Depends on earlier pull request here:

  <URL:https://patchwork.linuxtv.org/project/linux-media/patch/20201123231950.GD4351@valkosipuli.retiisi.org.uk/>

Sakari Ailus (30):
  ccs: Add MIPI CCS compatible strings
  ccs: Add device compatible identifiers for telling SMIA and CCS apart
  ccs: Add CCS ACPI device ID
  ccs: Remove the I²C ID table
  ccs: Remove remaining support for platform data
  ccs: Make hwcfg part of the device specific struct
  ccs: Fix obtaining bus information from firmware
  ccs: Add CCS static data parser library
  ccs: Combine revision number major and minor into one
  ccs: Read CCS static data from firmware binaries
  ccs: Stop reading arrays after the first zero
  ccs: The functions to get compose or crop rectangle never return NULL
  ccs: Replace somewhat harsh internal checks based on BUG with WARN_ON
  ccs: Refactor register reading a little
  ccs: Make real to integer number conversion optional
  ccs: Move limit value real to integer conversion from read to access
    time
  ccs: Read ireal numbers correctly
  smiapp-pll: Rename as ccs-pll
  ccs-pll: Fix MODULE_LICENSE
  ccs: Change my e-mail address
  ccs: Allow range in between I²C retries
  ccs: Add support for manufacturer regs from sensor and module files
  ccs: Use static data read-only registers
  ccs: Clean up runtime PM usage
  ccs: Wrap long lines, unwrap short ones
  ccs: Use longer pre-I²C sleep for CCS compliant devices
  ccs: Remove unnecessary delays from power-up sequence
  dt-bindings: mipi,ccs: Don't mention vana voltage
  dt-bindings: mipi,ccs: Add vcore and vio supplies
  ccs: Use all regulators

 .../bindings/media/i2c/mipi-ccs.yaml          |  11 +-
 MAINTAINERS                                   |   4 +-
 drivers/media/i2c/Kconfig                     |   2 +-
 drivers/media/i2c/Makefile                    |   2 +-
 drivers/media/i2c/{smiapp-pll.c => ccs-pll.c} |  66 +-
 drivers/media/i2c/{smiapp-pll.h => ccs-pll.h} |  42 +-
 drivers/media/i2c/ccs/Kconfig                 |   2 +-
 drivers/media/i2c/ccs/Makefile                |   2 +-
 drivers/media/i2c/ccs/ccs-core.c              | 396 +++++---
 drivers/media/i2c/ccs/ccs-data-defs.h         | 219 ++++
 drivers/media/i2c/ccs/ccs-data.c              | 944 ++++++++++++++++++
 drivers/media/i2c/ccs/ccs-data.h              | 120 +++
 drivers/media/i2c/ccs/ccs-quirk.c             |  10 +-
 drivers/media/i2c/ccs/ccs-quirk.h             |   2 +-
 drivers/media/i2c/ccs/ccs-reg-access.c        | 227 ++++-
 drivers/media/i2c/ccs/ccs-reg-access.h        |   6 +-
 drivers/media/i2c/ccs/ccs.h                   |  24 +-
 17 files changed, 1805 insertions(+), 274 deletions(-)
 rename drivers/media/i2c/{smiapp-pll.c => ccs-pll.c} (89%)
 rename drivers/media/i2c/{smiapp-pll.h => ccs-pll.h} (66%)
 create mode 100644 drivers/media/i2c/ccs/ccs-data-defs.h
 create mode 100644 drivers/media/i2c/ccs/ccs-data.c
 create mode 100644 drivers/media/i2c/ccs/ccs-data.h

-- 
2.27.0

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH 01/30] ccs: Add MIPI CCS compatible strings
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
@ 2020-11-24  9:31 ` Sakari Ailus
  2020-11-24  9:31 ` [PATCH 02/30] ccs: Add device compatible identifiers for telling SMIA and CCS apart Sakari Ailus
                   ` (28 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:31 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

Add "mipi-ccs-1.0" and "mipi-ccs-1.1" compatible strings to the CCS
driver.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-core.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 69e7990c65f3..64bad5b678a3 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -3236,6 +3236,9 @@ static int ccs_remove(struct i2c_client *client)
 }
 
 static const struct of_device_id ccs_of_table[] = {
+	{ .compatible = "mipi-ccs-1.1" },
+	{ .compatible = "mipi-ccs-1.0" },
+	{ .compatible = "mipi-ccs" },
 	{ .compatible = "nokia,smia" },
 	{ },
 };
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 02/30] ccs: Add device compatible identifiers for telling SMIA and CCS apart
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
  2020-11-24  9:31 ` [PATCH 01/30] ccs: Add MIPI CCS compatible strings Sakari Ailus
@ 2020-11-24  9:31 ` Sakari Ailus
  2020-11-24  9:31 ` [PATCH 03/30] ccs: Add CCS ACPI device ID Sakari Ailus
                   ` (27 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:31 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

Add device data specific to DT compatible ID to tell SMIA and CCS devices
apart already in power-up.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-core.c | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 64bad5b678a3..1d365da570a6 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -58,6 +58,12 @@ static const struct ccs_module_ident ccs_module_idents[] = {
 	CCS_IDENT_LQ(0x10, 0x4241, -1, "imx125es", &smiapp_imx125es_quirk),
 };
 
+#define CCS_DEVICE_FLAG_IS_SMIA		BIT(0)
+
+struct ccs_device {
+	unsigned char flags;
+};
+
 /*
  *
  * Dynamic Capability Identification
@@ -3235,11 +3241,17 @@ static int ccs_remove(struct i2c_client *client)
 	return 0;
 }
 
+static const struct ccs_device smia_device = {
+	.flags = CCS_DEVICE_FLAG_IS_SMIA,
+};
+
+static const struct ccs_device ccs_device = {};
+
 static const struct of_device_id ccs_of_table[] = {
-	{ .compatible = "mipi-ccs-1.1" },
-	{ .compatible = "mipi-ccs-1.0" },
-	{ .compatible = "mipi-ccs" },
-	{ .compatible = "nokia,smia" },
+	{ .compatible = "mipi-ccs-1.1", .data = &ccs_device },
+	{ .compatible = "mipi-ccs-1.0", .data = &ccs_device },
+	{ .compatible = "mipi-ccs", .data = &ccs_device },
+	{ .compatible = "nokia,smia", .data = &smia_device },
 	{ },
 };
 MODULE_DEVICE_TABLE(of, ccs_of_table);
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 03/30] ccs: Add CCS ACPI device ID
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
  2020-11-24  9:31 ` [PATCH 01/30] ccs: Add MIPI CCS compatible strings Sakari Ailus
  2020-11-24  9:31 ` [PATCH 02/30] ccs: Add device compatible identifiers for telling SMIA and CCS apart Sakari Ailus
@ 2020-11-24  9:31 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 04/30] ccs: Remove the I²C ID table Sakari Ailus
                   ` (26 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:31 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

The CCS compliant sensors use device ID "MIPI0200". Use this id for ACPI
device matching.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-core.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 1d365da570a6..bb712d71b8c8 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -3247,6 +3247,12 @@ static const struct ccs_device smia_device = {
 
 static const struct ccs_device ccs_device = {};
 
+static const struct acpi_device_id ccs_acpi_table[] = {
+	{ .id = "MIPI0200", .driver_data = (unsigned long)&ccs_device },
+	{ },
+};
+MODULE_DEVICE_TABLE(acpi, ccs_acpi_table);
+
 static const struct of_device_id ccs_of_table[] = {
 	{ .compatible = "mipi-ccs-1.1", .data = &ccs_device },
 	{ .compatible = "mipi-ccs-1.0", .data = &ccs_device },
@@ -3269,6 +3275,7 @@ static const struct dev_pm_ops ccs_pm_ops = {
 
 static struct i2c_driver ccs_i2c_driver = {
 	.driver	= {
+		.acpi_match_table = ccs_acpi_table,
 		.of_match_table = ccs_of_table,
 		.name = CCS_NAME,
 		.pm = &ccs_pm_ops,
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 04/30] ccs: Remove the I²C ID table
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (2 preceding siblings ...)
  2020-11-24  9:31 ` [PATCH 03/30] ccs: Add CCS ACPI device ID Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 05/30] ccs: Remove remaining support for platform data Sakari Ailus
                   ` (25 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

The I²C ID table is no longer needed; remove it.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-core.c | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index bb712d71b8c8..bb3759e2534c 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -3262,12 +3262,6 @@ static const struct of_device_id ccs_of_table[] = {
 };
 MODULE_DEVICE_TABLE(of, ccs_of_table);
 
-static const struct i2c_device_id ccs_id_table[] = {
-	{ SMIAPP_NAME, 0 },
-	{ },
-};
-MODULE_DEVICE_TABLE(i2c, ccs_id_table);
-
 static const struct dev_pm_ops ccs_pm_ops = {
 	SET_SYSTEM_SLEEP_PM_OPS(ccs_suspend, ccs_resume)
 	SET_RUNTIME_PM_OPS(ccs_power_off, ccs_power_on, NULL)
@@ -3282,7 +3276,6 @@ static struct i2c_driver ccs_i2c_driver = {
 	},
 	.probe_new = ccs_probe,
 	.remove	= ccs_remove,
-	.id_table = ccs_id_table,
 };
 
 static int ccs_module_init(void)
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 05/30] ccs: Remove remaining support for platform data
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (3 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 04/30] ccs: Remove the I²C ID table Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 06/30] ccs: Make hwcfg part of the device specific struct Sakari Ailus
                   ` (24 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

No need to support platform data; remove support for conveying hardware
configuration that way.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-core.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index bb3759e2534c..af79d80ebb59 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -2860,9 +2860,6 @@ static struct ccs_hwconfig *ccs_get_hwconfig(struct device *dev)
 	int i;
 	int rval;
 
-	if (!fwnode)
-		return dev->platform_data;
-
 	ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
 	if (!ep)
 		return NULL;
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 06/30] ccs: Make hwcfg part of the device specific struct
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (4 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 05/30] ccs: Remove remaining support for platform data Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 07/30] ccs: Fix obtaining bus information from firmware Sakari Ailus
                   ` (23 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

There's no need to allocate the hardware configuration struct separately.
Put it in struct ccs_sensor.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-core.c  | 94 ++++++++++++++++---------------
 drivers/media/i2c/ccs/ccs-quirk.c |  4 +-
 drivers/media/i2c/ccs/ccs.h       |  2 +-
 3 files changed, 51 insertions(+), 49 deletions(-)

diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index af79d80ebb59..dcc71c8fe075 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -809,7 +809,7 @@ static int ccs_init_late_controls(struct ccs_sensor *sensor)
 	sensor->link_freq = v4l2_ctrl_new_int_menu(
 		&sensor->src->ctrl_handler, &ccs_ctrl_ops,
 		V4L2_CID_LINK_FREQ, __fls(*valid_link_freqs),
-		__ffs(*valid_link_freqs), sensor->hwcfg->op_sys_clock);
+		__ffs(*valid_link_freqs), sensor->hwcfg.op_sys_clock);
 
 	return sensor->src->ctrl_handler.error;
 }
@@ -922,8 +922,8 @@ static int ccs_get_mbus_formats(struct ccs_sensor *sensor)
 
 		pll->bits_per_pixel = f->compressed;
 
-		for (j = 0; sensor->hwcfg->op_sys_clock[j]; j++) {
-			pll->link_freq = sensor->hwcfg->op_sys_clock[j];
+		for (j = 0; sensor->hwcfg.op_sys_clock[j]; j++) {
+			pll->link_freq = sensor->hwcfg.op_sys_clock[j];
 
 			rval = ccs_pll_try(sensor, pll);
 			dev_dbg(&client->dev, "link freq %u Hz, bpp %u %s\n",
@@ -1123,21 +1123,21 @@ static int ccs_change_cci_addr(struct ccs_sensor *sensor)
 	int rval;
 	u32 val;
 
-	client->addr = sensor->hwcfg->i2c_addr_dfl;
+	client->addr = sensor->hwcfg.i2c_addr_dfl;
 
 	rval = ccs_write(sensor, CCI_ADDRESS_CTRL,
-			 sensor->hwcfg->i2c_addr_alt << 1);
+			 sensor->hwcfg.i2c_addr_alt << 1);
 	if (rval)
 		return rval;
 
-	client->addr = sensor->hwcfg->i2c_addr_alt;
+	client->addr = sensor->hwcfg.i2c_addr_alt;
 
 	/* verify addr change went ok */
 	rval = ccs_read(sensor, CCI_ADDRESS_CTRL, &val);
 	if (rval)
 		return rval;
 
-	if (val != sensor->hwcfg->i2c_addr_alt << 1)
+	if (val != sensor->hwcfg.i2c_addr_alt << 1)
 		return -ENODEV;
 
 	return 0;
@@ -1151,13 +1151,13 @@ static int ccs_change_cci_addr(struct ccs_sensor *sensor)
 static int ccs_setup_flash_strobe(struct ccs_sensor *sensor)
 {
 	struct ccs_flash_strobe_parms *strobe_setup;
-	unsigned int ext_freq = sensor->hwcfg->ext_clk;
+	unsigned int ext_freq = sensor->hwcfg.ext_clk;
 	u32 tmp;
 	u32 strobe_adjustment;
 	u32 strobe_width_high_rs;
 	int rval;
 
-	strobe_setup = sensor->hwcfg->strobe_setup;
+	strobe_setup = sensor->hwcfg.strobe_setup;
 
 	/*
 	 * How to calculate registers related to strobe length. Please
@@ -1265,7 +1265,7 @@ static int ccs_setup_flash_strobe(struct ccs_sensor *sensor)
 	rval = ccs_write(sensor, FLASH_TRIGGER_RS, strobe_setup->trigger);
 
 out:
-	sensor->hwcfg->strobe_setup->trigger = 0;
+	sensor->hwcfg.strobe_setup->trigger = 0;
 
 	return rval;
 }
@@ -1304,7 +1304,7 @@ static int ccs_power_on(struct device *dev)
 	gpiod_set_value(sensor->reset, 0);
 	gpiod_set_value(sensor->xshutdown, 1);
 
-	sleep = SMIAPP_RESET_DELAY(sensor->hwcfg->ext_clk);
+	sleep = SMIAPP_RESET_DELAY(sensor->hwcfg.ext_clk);
 	usleep_range(sleep, sleep);
 
 	/*
@@ -1318,7 +1318,7 @@ static int ccs_power_on(struct device *dev)
 	 * is found.
 	 */
 
-	if (sensor->hwcfg->i2c_addr_alt) {
+	if (sensor->hwcfg.i2c_addr_alt) {
 		rval = ccs_change_cci_addr(sensor);
 		if (rval) {
 			dev_err(dev, "cci address change error\n");
@@ -1332,7 +1332,7 @@ static int ccs_power_on(struct device *dev)
 		goto out_cci_addr_fail;
 	}
 
-	if (sensor->hwcfg->i2c_addr_alt) {
+	if (sensor->hwcfg.i2c_addr_alt) {
 		rval = ccs_change_cci_addr(sensor);
 		if (rval) {
 			dev_err(dev, "cci address change error\n");
@@ -1348,13 +1348,13 @@ static int ccs_power_on(struct device *dev)
 	}
 
 	rval = ccs_write(sensor, EXTCLK_FREQUENCY_MHZ,
-			 sensor->hwcfg->ext_clk / (1000000 / (1 << 8)));
+			 sensor->hwcfg.ext_clk / (1000000 / (1 << 8)));
 	if (rval) {
 		dev_err(dev, "extclk frequency set failed\n");
 		goto out_cci_addr_fail;
 	}
 
-	rval = ccs_write(sensor, CSI_LANE_MODE, sensor->hwcfg->lanes - 1);
+	rval = ccs_write(sensor, CSI_LANE_MODE, sensor->hwcfg.lanes - 1);
 	if (rval) {
 		dev_err(dev, "csi lane mode set failed\n");
 		goto out_cci_addr_fail;
@@ -1368,7 +1368,7 @@ static int ccs_power_on(struct device *dev)
 	}
 
 	rval = ccs_write(sensor, CSI_SIGNALING_MODE,
-			 sensor->hwcfg->csi_signalling_mode);
+			 sensor->hwcfg.csi_signalling_mode);
 	if (rval) {
 		dev_err(dev, "csi signalling mode set failed\n");
 		goto out_cci_addr_fail;
@@ -1412,7 +1412,7 @@ static int ccs_power_off(struct device *dev)
 	 * really see a power off and next time the cci address change
 	 * will fail. So do a soft reset explicitly here.
 	 */
-	if (sensor->hwcfg->i2c_addr_alt)
+	if (sensor->hwcfg.i2c_addr_alt)
 		ccs_write(sensor, SOFTWARE_RESET, CCS_SOFTWARE_RESET_ON);
 
 	gpiod_set_value(sensor->reset, 1);
@@ -1551,8 +1551,8 @@ static int ccs_start_streaming(struct ccs_sensor *sensor)
 	if (CCS_LIM(sensor, FLASH_MODE_CAPABILITY) &
 	    (CCS_FLASH_MODE_CAPABILITY_SINGLE_STROBE |
 	     SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE) &&
-	    sensor->hwcfg->strobe_setup != NULL &&
-	    sensor->hwcfg->strobe_setup->trigger != 0) {
+	    sensor->hwcfg.strobe_setup != NULL &&
+	    sensor->hwcfg.strobe_setup->trigger != 0) {
 		rval = ccs_setup_flash_strobe(sensor);
 		if (rval)
 			goto out;
@@ -2850,9 +2850,9 @@ static int __maybe_unused ccs_resume(struct device *dev)
 	return rval;
 }
 
-static struct ccs_hwconfig *ccs_get_hwconfig(struct device *dev)
+static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev)
 {
-	struct ccs_hwconfig *hwcfg;
+	struct ccs_hwconfig *hwcfg = &sensor->hwcfg;
 	struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 };
 	struct fwnode_handle *ep;
 	struct fwnode_handle *fwnode = dev_fwnode(dev);
@@ -2862,7 +2862,7 @@ static struct ccs_hwconfig *ccs_get_hwconfig(struct device *dev)
 
 	ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
 	if (!ep)
-		return NULL;
+		return -ENODEV;
 
 	bus_cfg.bus_type = V4L2_MBUS_CSI2_DPHY;
 	rval = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
@@ -2874,10 +2874,6 @@ static struct ccs_hwconfig *ccs_get_hwconfig(struct device *dev)
 	if (rval)
 		goto out_err;
 
-	hwcfg = devm_kzalloc(dev, sizeof(*hwcfg), GFP_KERNEL);
-	if (!hwcfg)
-		goto out_err;
-
 	switch (bus_cfg.bus_type) {
 	case V4L2_MBUS_CSI2_DPHY:
 		hwcfg->csi_signalling_mode = CCS_CSI_SIGNALING_MODE_CSI_2_DPHY;
@@ -2891,6 +2887,7 @@ static struct ccs_hwconfig *ccs_get_hwconfig(struct device *dev)
 		break;
 	default:
 		dev_err(dev, "unsupported bus %u\n", bus_cfg.bus_type);
+		rval = -EINVAL;
 		goto out_err;
 	}
 
@@ -2907,6 +2904,7 @@ static struct ccs_hwconfig *ccs_get_hwconfig(struct device *dev)
 			break;
 		default:
 			dev_err(dev, "invalid rotation %u\n", rotation);
+			rval = -EINVAL;
 			goto out_err;
 		}
 	}
@@ -2921,14 +2919,17 @@ static struct ccs_hwconfig *ccs_get_hwconfig(struct device *dev)
 
 	if (!bus_cfg.nr_of_link_frequencies) {
 		dev_warn(dev, "no link frequencies defined\n");
+		rval = -EINVAL;
 		goto out_err;
 	}
 
 	hwcfg->op_sys_clock = devm_kcalloc(
 		dev, bus_cfg.nr_of_link_frequencies + 1 /* guardian */,
 		sizeof(*hwcfg->op_sys_clock), GFP_KERNEL);
-	if (!hwcfg->op_sys_clock)
+	if (!hwcfg->op_sys_clock) {
+		rval = -ENOMEM;
 		goto out_err;
+	}
 
 	for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) {
 		hwcfg->op_sys_clock[i] = bus_cfg.link_frequencies[i];
@@ -2937,29 +2938,30 @@ static struct ccs_hwconfig *ccs_get_hwconfig(struct device *dev)
 
 	v4l2_fwnode_endpoint_free(&bus_cfg);
 	fwnode_handle_put(ep);
-	return hwcfg;
+
+	return 0;
 
 out_err:
 	v4l2_fwnode_endpoint_free(&bus_cfg);
 	fwnode_handle_put(ep);
-	return NULL;
+
+	return rval;
 }
 
 static int ccs_probe(struct i2c_client *client)
 {
 	struct ccs_sensor *sensor;
-	struct ccs_hwconfig *hwcfg = ccs_get_hwconfig(&client->dev);
 	unsigned int i;
 	int rval;
 
-	if (hwcfg == NULL)
-		return -ENODEV;
-
 	sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
 	if (sensor == NULL)
 		return -ENOMEM;
 
-	sensor->hwcfg = hwcfg;
+	rval = ccs_get_hwconfig(sensor, &client->dev);
+	if (rval)
+		return rval;
+
 	sensor->src = &sensor->ssds[sensor->ssds_used];
 
 	v4l2_i2c_subdev_init(&sensor->src->sd, client, &ccs_ops);
@@ -2982,33 +2984,33 @@ static int ccs_probe(struct i2c_client *client)
 	}
 
 	if (sensor->ext_clk) {
-		if (sensor->hwcfg->ext_clk) {
+		if (sensor->hwcfg.ext_clk) {
 			unsigned long rate;
 
 			rval = clk_set_rate(sensor->ext_clk,
-					    sensor->hwcfg->ext_clk);
+					    sensor->hwcfg.ext_clk);
 			if (rval < 0) {
 				dev_err(&client->dev,
 					"unable to set clock freq to %u\n",
-					sensor->hwcfg->ext_clk);
+					sensor->hwcfg.ext_clk);
 				return rval;
 			}
 
 			rate = clk_get_rate(sensor->ext_clk);
-			if (rate != sensor->hwcfg->ext_clk) {
+			if (rate != sensor->hwcfg.ext_clk) {
 				dev_err(&client->dev,
 					"can't set clock freq, asked for %u but got %lu\n",
-					sensor->hwcfg->ext_clk, rate);
+					sensor->hwcfg.ext_clk, rate);
 				return rval;
 			}
 		} else {
-			sensor->hwcfg->ext_clk = clk_get_rate(sensor->ext_clk);
+			sensor->hwcfg.ext_clk = clk_get_rate(sensor->ext_clk);
 			dev_dbg(&client->dev, "obtained clock freq %u\n",
-				sensor->hwcfg->ext_clk);
+				sensor->hwcfg.ext_clk);
 		}
-	} else if (sensor->hwcfg->ext_clk) {
+	} else if (sensor->hwcfg.ext_clk) {
 		dev_dbg(&client->dev, "assuming clock freq %u\n",
-			sensor->hwcfg->ext_clk);
+			sensor->hwcfg.ext_clk);
 	} else {
 		dev_err(&client->dev, "unable to obtain clock freq\n");
 		return -EINVAL;
@@ -3061,7 +3063,7 @@ static int ccs_probe(struct i2c_client *client)
 	 *
 	 * Rotation also changes the bayer pattern.
 	 */
-	if (sensor->hwcfg->module_board_orient ==
+	if (sensor->hwcfg.module_board_orient ==
 	    CCS_MODULE_BOARD_ORIENT_180)
 		sensor->hvflip_inv_mask =
 			CCS_IMAGE_ORIENTATION_HORIZONTAL_MIRROR |
@@ -3133,8 +3135,8 @@ static int ccs_probe(struct i2c_client *client)
 
 	/* prepare PLL configuration input values */
 	sensor->pll.bus_type = SMIAPP_PLL_BUS_TYPE_CSI2;
-	sensor->pll.csi2.lanes = sensor->hwcfg->lanes;
-	sensor->pll.ext_clk_freq_hz = sensor->hwcfg->ext_clk;
+	sensor->pll.csi2.lanes = sensor->hwcfg.lanes;
+	sensor->pll.ext_clk_freq_hz = sensor->hwcfg.ext_clk;
 	sensor->pll.scale_n = CCS_LIM(sensor, SCALER_N_MIN);
 
 	ccs_create_subdev(sensor, sensor->scaler, " scaler", 2,
diff --git a/drivers/media/i2c/ccs/ccs-quirk.c b/drivers/media/i2c/ccs/ccs-quirk.c
index 5a24da1d7aa9..facec28f8447 100644
--- a/drivers/media/i2c/ccs/ccs-quirk.c
+++ b/drivers/media/i2c/ccs/ccs-quirk.c
@@ -152,13 +152,13 @@ static int jt8ev1_post_poweron(struct ccs_sensor *sensor)
 	if (rval < 0)
 		return rval;
 
-	switch (sensor->hwcfg->ext_clk) {
+	switch (sensor->hwcfg.ext_clk) {
 	case 9600000:
 		return ccs_write_addr_8s(sensor, regs_96,
 				       ARRAY_SIZE(regs_96));
 	default:
 		dev_warn(&client->dev, "no MSRs for %d Hz ext_clk\n",
-			 sensor->hwcfg->ext_clk);
+			 sensor->hwcfg.ext_clk);
 		return 0;
 	}
 }
diff --git a/drivers/media/i2c/ccs/ccs.h b/drivers/media/i2c/ccs/ccs.h
index bfe39e02f5e9..2d1e8339f663 100644
--- a/drivers/media/i2c/ccs/ccs.h
+++ b/drivers/media/i2c/ccs/ccs.h
@@ -215,7 +215,7 @@ struct ccs_sensor {
 	struct ccs_subdev *binner;
 	struct ccs_subdev *scaler;
 	struct ccs_subdev *pixel_array;
-	struct ccs_hwconfig *hwcfg;
+	struct ccs_hwconfig hwcfg;
 	struct regulator *vana;
 	struct clk *ext_clk;
 	struct gpio_desc *xshutdown;
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 07/30] ccs: Fix obtaining bus information from firmware
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (5 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 06/30] ccs: Make hwcfg part of the device specific struct Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 08/30] ccs: Add CCS static data parser library Sakari Ailus
                   ` (22 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

Let v4l2_fwnode_endpoint_alloc_parse to figure out the type of the data
bus. As the old bindings did not require the "bus-type" property, we need
to rely on guessing between CSI-2 D-PHY and CCP2. Setting the type to
CSI-2 D-PHY will parse just that and succeed even if no data-lanes are
set.

Also add a comment on the matter to the driver to avoid breaking this in
the future.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-core.c | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index dcc71c8fe075..6fb546ca08f3 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -2853,7 +2853,7 @@ static int __maybe_unused ccs_resume(struct device *dev)
 static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev)
 {
 	struct ccs_hwconfig *hwcfg = &sensor->hwcfg;
-	struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 };
+	struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = V4L2_MBUS_UNKNOWN };
 	struct fwnode_handle *ep;
 	struct fwnode_handle *fwnode = dev_fwnode(dev);
 	u32 rotation;
@@ -2864,13 +2864,11 @@ static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev)
 	if (!ep)
 		return -ENODEV;
 
-	bus_cfg.bus_type = V4L2_MBUS_CSI2_DPHY;
+	/*
+	 * Note that we do need to rely on detecting the bus type between CSI-2
+	 * D-PHY and CCP2 as the old bindings did not require it.
+	 */
 	rval = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
-	if (rval == -ENXIO) {
-		bus_cfg = (struct v4l2_fwnode_endpoint)
-			{ .bus_type = V4L2_MBUS_CCP2 };
-		rval = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
-	}
 	if (rval)
 		goto out_err;
 
@@ -2879,6 +2877,7 @@ static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev)
 		hwcfg->csi_signalling_mode = CCS_CSI_SIGNALING_MODE_CSI_2_DPHY;
 		hwcfg->lanes = bus_cfg.bus.mipi_csi2.num_data_lanes;
 		break;
+	case V4L2_MBUS_CSI1:
 	case V4L2_MBUS_CCP2:
 		hwcfg->csi_signalling_mode = (bus_cfg.bus.mipi_csi1.strobe) ?
 		SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_STROBE :
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 08/30] ccs: Add CCS static data parser library
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (6 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 07/30] ccs: Fix obtaining bus information from firmware Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 09/30] ccs: Combine revision number major and minor into one Sakari Ailus
                   ` (21 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

Add a parser library for parsing the CCS static data format.

The library may be also compiled in user space as the format has uses also
in the user space. Therefore it is dual licensed under the 3-clause BSD
license as well.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/Makefile        |   2 +-
 drivers/media/i2c/ccs/ccs-data-defs.h | 219 ++++++
 drivers/media/i2c/ccs/ccs-data.c      | 944 ++++++++++++++++++++++++++
 drivers/media/i2c/ccs/ccs-data.h      | 120 ++++
 4 files changed, 1284 insertions(+), 1 deletion(-)
 create mode 100644 drivers/media/i2c/ccs/ccs-data-defs.h
 create mode 100644 drivers/media/i2c/ccs/ccs-data.c
 create mode 100644 drivers/media/i2c/ccs/ccs-data.h

diff --git a/drivers/media/i2c/ccs/Makefile b/drivers/media/i2c/ccs/Makefile
index 08dd4e948fb0..44601ba8cd53 100644
--- a/drivers/media/i2c/ccs/Makefile
+++ b/drivers/media/i2c/ccs/Makefile
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 ccs-objs			+= ccs-core.o ccs-reg-access.o \
-				   ccs-quirk.o ccs-limits.o
+				   ccs-quirk.o ccs-limits.o ccs-data.o
 obj-$(CONFIG_VIDEO_CCS)		+= ccs.o
 
 ccflags-y += -I $(srctree)/drivers/media/i2c
diff --git a/drivers/media/i2c/ccs/ccs-data-defs.h b/drivers/media/i2c/ccs/ccs-data-defs.h
new file mode 100644
index 000000000000..db556f3e78d8
--- /dev/null
+++ b/drivers/media/i2c/ccs/ccs-data-defs.h
@@ -0,0 +1,219 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * CCS static data binary format definitions
+ *
+ * Copyright 2019--2020 Intel Corporation
+ */
+
+#ifndef __CCS_DATA_DEFS_H__
+#define __CCS_DATA_DEFS_H__
+
+#include "ccs-data.h"
+
+#define CCS_STATIC_DATA_VERSION	0
+
+enum __ccs_data_length_specifier_id {
+	CCS_DATA_LENGTH_SPECIFIER_1 = 0,
+	CCS_DATA_LENGTH_SPECIFIER_2 = 1,
+	CCS_DATA_LENGTH_SPECIFIER_3 = 2
+};
+#define CCS_DATA_LENGTH_SPECIFIER_SIZE_SHIFT	6
+
+struct __ccs_data_length_specifier {
+	uint8_t length;
+} __attribute__((packed));
+
+struct __ccs_data_length_specifier2 {
+	uint8_t length[2];
+} __attribute__((packed));
+
+struct __ccs_data_length_specifier3 {
+	uint8_t length[3];
+} __attribute__((packed));
+
+struct __ccs_data_block {
+	uint8_t id;
+	struct __ccs_data_length_specifier length;
+} __attribute__((packed));
+
+#define CCS_DATA_BLOCK_HEADER_ID_VERSION_SHIFT	5
+
+struct __ccs_data_block3 {
+	uint8_t id;
+	struct __ccs_data_length_specifier2 length;
+} __attribute__((packed));
+
+struct __ccs_data_block4 {
+	uint8_t id;
+	struct __ccs_data_length_specifier3 length;
+} __attribute__((packed));
+
+enum __ccs_data_block_id {
+	CCS_DATA_BLOCK_ID_DUMMY	= 1,
+	CCS_DATA_BLOCK_ID_DATA_VERSION = 2,
+	CCS_DATA_BLOCK_ID_SENSOR_READ_ONLY_REGS = 3,
+	CCS_DATA_BLOCK_ID_MODULE_READ_ONLY_REGS = 4,
+	CCS_DATA_BLOCK_ID_SENSOR_MANUFACTURER_REGS = 5,
+	CCS_DATA_BLOCK_ID_MODULE_MANUFACTURER_REGS = 6,
+	CCS_DATA_BLOCK_ID_SENSOR_RULE_BASED_BLOCK = 32,
+	CCS_DATA_BLOCK_ID_MODULE_RULE_BASED_BLOCK = 33,
+	CCS_DATA_BLOCK_ID_SENSOR_PDAF_PIXEL_LOCATION = 36,
+	CCS_DATA_BLOCK_ID_MODULE_PDAF_PIXEL_LOCATION = 37,
+	CCS_DATA_BLOCK_ID_LICENSE = 40,
+	CCS_DATA_BLOCK_ID_END = 127,
+};
+
+struct __ccs_data_block_version {
+	uint8_t static_data_version_major[2];
+	uint8_t static_data_version_minor[2];
+	uint8_t year[2];
+	uint8_t month;
+	uint8_t day;
+} __attribute__((packed));
+
+struct __ccs_data_block_regs {
+	uint8_t reg_len;
+} __attribute__((packed));
+
+#define CCS_DATA_BLOCK_REGS_ADDR_MASK		0x07
+#define CCS_DATA_BLOCK_REGS_LEN_SHIFT		3
+#define CCS_DATA_BLOCK_REGS_LEN_MASK		0x38
+#define CCS_DATA_BLOCK_REGS_SEL_SHIFT		6
+enum ccs_data_block_regs_sel {
+	CCS_DATA_BLOCK_REGS_SEL_REGS = 0,
+	CCS_DATA_BLOCK_REGS_SEL_REGS2 = 1,
+	CCS_DATA_BLOCK_REGS_SEL_REGS3 = 2,
+};
+
+struct __ccs_data_block_regs2 {
+	uint8_t reg_len;
+	uint8_t addr;
+} __attribute__((packed));
+
+#define CCS_DATA_BLOCK_REGS_2_ADDR_MASK		0x01
+#define CCS_DATA_BLOCK_REGS_2_LEN_SHIFT		1
+#define CCS_DATA_BLOCK_REGS_2_LEN_MASK		0x3e
+
+struct __ccs_data_block_regs3 {
+	uint8_t reg_len;
+	uint8_t addr[2];
+} __attribute__((packed));
+
+#define CCS_DATA_BLOCK_REGS_3_LEN_MASK		0x3f
+
+enum __ccs_data_ffd_pixelcode {
+	CCS_DATA_BLOCK_FFD_PIXELCODE_EMBEDDED = 1,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_DUMMY = 2,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_BLACK = 3,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_DARK = 4,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_VISIBLE = 5,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_MS_0 = 8,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_MS_1 = 9,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_MS_2 = 10,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_MS_3 = 11,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_MS_4 = 12,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_MS_5 = 13,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_MS_6 = 14,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_TOP_OB = 16,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_BOTTOM_OB = 17,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_LEFT_OB = 18,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_RIGHT_OB = 19,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_TOP_LEFT_OB = 20,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_TOP_RIGHT_OB = 21,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_BOTTOM_LEFT_OB = 22,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_BOTTOM_RIGHT_OB = 23,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_TOTAL = 24,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_TOP_PDAF = 32,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_BOTTOM_PDAF = 33,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_LEFT_PDAF = 34,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_RIGHT_PDAF = 35,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_TOP_LEFT_PDAF = 36,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_TOP_RIGHT_PDAF = 37,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_BOTTOM_LEFT_PDAF = 38,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_BOTTOM_RIGHT_PDAF = 39,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_SEPARATED_PDAF = 40,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_ORIGINAL_ORDER_PDAF = 41,
+	CCS_DATA_BLOCK_FFD_PIXELCODE_VENDOR_PDAF = 41,
+};
+
+struct __ccs_data_block_ffd_entry {
+	uint8_t pixelcode;
+	uint8_t reserved;
+	uint8_t value[2];
+} __attribute__((packed));
+
+struct __ccs_data_block_ffd {
+	uint8_t num_column_descs;
+	uint8_t num_row_descs;
+} __attribute__((packed));
+
+enum __ccs_data_block_rule_id {
+	CCS_DATA_BLOCK_RULE_ID_IF = 1,
+	CCS_DATA_BLOCK_RULE_ID_READ_ONLY_REGS = 2,
+	CCS_DATA_BLOCK_RULE_ID_FFD = 3,
+	CCS_DATA_BLOCK_RULE_ID_MSR = 4,
+	CCS_DATA_BLOCK_RULE_ID_PDAF_READOUT = 5,
+};
+
+struct __ccs_data_block_rule_if {
+	uint8_t addr[2];
+	uint8_t value;
+	uint8_t mask;
+} __attribute__((packed));
+
+enum __ccs_data_block_pdaf_readout_order{
+	CCS_DATA_BLOCK_PDAF_READOUT_ORDER_ORIGINAL = 1,
+	CCS_DATA_BLOCK_PDAF_READOUT_ORDER_SEPARATE_WITHIN_LINE = 2,
+	CCS_DATA_BLOCK_PDAF_READOUT_ORDER_SEPARATE_TYPES_SEPARATE_LINES = 3,
+};
+
+struct __ccs_data_block_pdaf_readout {
+	uint8_t pdaf_readout_info_reserved;
+	uint8_t pdaf_readout_info_order;
+} __attribute__((packed));
+
+struct __ccs_data_block_pdaf_pix_loc_block_desc {
+	uint8_t block_type_id;
+	uint8_t repeat_x[2];
+} __attribute__((packed));
+
+struct __ccs_data_block_pdaf_pix_loc_block_desc_group {
+	uint8_t num_block_descs[2];
+	uint8_t repeat_y;
+} __attribute__((packed));
+
+enum __ccs_data_block_pdaf_pix_loc_pixel_type {
+	CCS_DATA_PDAF_PIXEL_TYPE_LEFT_SEPARATED = 0,
+	CCS_DATA_PDAF_PIXEL_TYPE_RIGHT_SEPARATED = 1,
+	CCS_DATA_PDAF_PIXEL_TYPE_TOP_SEPARATED = 2,
+	CCS_DATA_PDAF_PIXEL_TYPE_BOTTOM_SEPARATED = 3,
+	CCS_DATA_PDAF_PIXEL_TYPE_LEFT_SIDE_BY_SIDE = 4,
+	CCS_DATA_PDAF_PIXEL_TYPE_RIGHT_SIDE_BY_SIDE = 5,
+	CCS_DATA_PDAF_PIXEL_TYPE_TOP_SIDE_BY_SIDE = 6,
+	CCS_DATA_PDAF_PIXEL_TYPE_BOTTOM_SIDE_BY_SIDE = 7,
+	CCS_DATA_PDAF_PIXEL_TYPE_TOP_LEFT = 8,
+	CCS_DATA_PDAF_PIXEL_TYPE_TOP_RIGHT = 9,
+	CCS_DATA_PDAF_PIXEL_TYPE_BOTTOM_LEFT = 10,
+	CCS_DATA_PDAF_PIXEL_TYPE_BOTTOM_RIGHT = 11,
+};
+
+struct __ccs_data_block_pdaf_pix_loc_pixel_desc {
+	uint8_t pixel_type;
+	uint8_t small_offset_x;
+	uint8_t small_offset_y;
+} __attribute__((packed));
+
+struct __ccs_data_block_pdaf_pix_loc {
+	uint8_t main_offset_x[2];
+	uint8_t main_offset_y[2];
+	uint8_t global_pdaf_type;
+	uint8_t block_width;
+	uint8_t block_height;
+	uint8_t num_block_desc_groups[2];
+} __attribute__((packed));
+
+struct __ccs_data_block_end {
+	uint8_t crc[4];
+} __attribute__((packed));
+
+#endif /* __CCS_DATA_DEFS_H__ */
diff --git a/drivers/media/i2c/ccs/ccs-data.c b/drivers/media/i2c/ccs/ccs-data.c
new file mode 100644
index 000000000000..de6473600a3d
--- /dev/null
+++ b/drivers/media/i2c/ccs/ccs-data.c
@@ -0,0 +1,944 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * CCS static data binary parser library
+ *
+ * Copyright 2019--2020 Intel Corporation
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/limits.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "ccs-data-defs.h"
+
+struct bin_container {
+	void *base;
+	void *now;
+	void *end;
+	size_t size;
+};
+
+static void *bin_alloc(struct bin_container *bin, size_t len)
+{
+	void *ptr;
+
+	len = ALIGN(len, 8);
+
+	if (bin->end - bin->now < len)
+		return NULL;
+
+	ptr = bin->now;
+	bin->now += len;
+
+	return ptr;
+}
+
+static void bin_reserve(struct bin_container *bin, size_t len)
+{
+	bin->size += ALIGN(len, 8);
+}
+
+static int bin_backing_alloc(struct bin_container *bin)
+{
+	bin->base = bin->now = kvzalloc(bin->size, GFP_KERNEL);
+	if (!bin->base)
+		return -ENOMEM;
+
+	bin->end = bin->base + bin->size;
+
+	return 0;
+}
+
+#define is_contained(var, endp)				\
+	(sizeof(*var) <= (endp) - (void *)(var))
+#define has_headroom(ptr, headroom, endp)	\
+	((headroom) <= (endp) - (void *)(ptr))
+#define is_contained_with_headroom(var, headroom, endp)		\
+	(sizeof(*var) + (headroom) <= (endp) - (void *)(var))
+
+static int
+ccs_data_parse_length_specifier(const struct __ccs_data_length_specifier *__len,
+				size_t *__hlen, size_t *__plen,
+				const void *endp)
+{
+	size_t hlen, plen;
+
+	if (!is_contained(__len, endp))
+		return -ENODATA;
+
+	switch (__len->length >> CCS_DATA_LENGTH_SPECIFIER_SIZE_SHIFT) {
+	case CCS_DATA_LENGTH_SPECIFIER_1:
+		hlen = sizeof(*__len);
+		plen = __len->length &
+			((1 << CCS_DATA_LENGTH_SPECIFIER_SIZE_SHIFT) - 1);
+		break;
+	case CCS_DATA_LENGTH_SPECIFIER_2: {
+		struct __ccs_data_length_specifier2 *__len2 = (void *)__len;
+
+		if (!is_contained(__len2, endp))
+			return -ENODATA;
+
+		hlen = sizeof(*__len2);
+		plen = ((size_t)
+			(__len2->length[0] &
+			 ((1 << CCS_DATA_LENGTH_SPECIFIER_SIZE_SHIFT) - 1))
+			<< 8) + __len2->length[1];
+		break;
+	}
+	case CCS_DATA_LENGTH_SPECIFIER_3: {
+		struct __ccs_data_length_specifier3 *__len3 = (void *)__len;
+
+		if (!is_contained(__len3, endp))
+			return -ENODATA;
+
+		hlen = sizeof(*__len3);
+		plen = ((size_t)
+			(__len3->length[0] &
+			 ((1 << CCS_DATA_LENGTH_SPECIFIER_SIZE_SHIFT) - 1))
+			<< 16) + (__len3->length[0] << 8) + __len3->length[1];
+		break;
+	}
+	default:
+		return -EINVAL;
+	}
+
+	if (!has_headroom(__len, hlen + plen, endp))
+		return -ENODATA;
+
+	*__hlen = hlen;
+	*__plen = plen;
+
+	return 0;
+}
+
+static uint8_t
+ccs_data_parse_format_version(const struct __ccs_data_block *block)
+{
+	return block->id >> CCS_DATA_BLOCK_HEADER_ID_VERSION_SHIFT;
+}
+
+static uint8_t ccs_data_parse_block_id(const struct __ccs_data_block *block,
+				       bool is_first)
+{
+	if (!is_first)
+		return block->id;
+
+	return block->id & ((1 << CCS_DATA_BLOCK_HEADER_ID_VERSION_SHIFT) - 1);
+}
+
+static int ccs_data_parse_version(struct bin_container *bin,
+				  struct ccs_data_container *ccsdata,
+				  const void *payload, const void *endp)
+{
+	const struct __ccs_data_block_version *v = payload;
+	struct ccs_data_block_version *vv;
+
+	if (v + 1 != endp)
+		return -ENODATA;
+
+	if (!bin->base) {
+		bin_reserve(bin, sizeof(*ccsdata->version));
+		return 0;
+	}
+
+	ccsdata->version = bin_alloc(bin, sizeof(*ccsdata->version));
+	if (!ccsdata->version)
+		return -ENOMEM;
+
+	vv = ccsdata->version;
+	vv->version_major = ((uint16_t)v->static_data_version_minor[0] << 8) +
+		v->static_data_version_major[1];
+	vv->version_minor = ((uint16_t)v->static_data_version_minor[0] << 8) +
+		v->static_data_version_major[1];
+	vv->date_year =  ((uint16_t)v->year[0] << 8) + v->year[1];
+	vv->date_month = v->month;
+	vv->date_day = v->day;
+
+	return 0;
+}
+
+static void print_ccs_data_version(struct device *dev,
+				   struct ccs_data_block_version *v)
+{
+	dev_dbg(dev,
+		"static data version %4.4x.%4.4x, date %4.4u-%2.2u-%2.2u\n",
+		v->version_major, v->version_minor,
+		v->date_year, v->date_month, v->date_day);
+}
+
+static int ccs_data_block_parse_header(const struct __ccs_data_block *block,
+				       bool is_first, unsigned int *__block_id,
+				       const void **payload,
+				       const struct __ccs_data_block **next_block,
+				       const void *endp, struct device *dev,
+				       bool verbose)
+{
+	size_t plen, hlen;
+	uint8_t block_id;
+	int rval;
+
+	if (!is_contained(block, endp))
+		return -ENODATA;
+
+	rval = ccs_data_parse_length_specifier(&block->length, &hlen, &plen,
+					       endp);
+	if (rval < 0)
+		return rval;
+
+	block_id = ccs_data_parse_block_id(block, is_first);
+
+	if (verbose)
+		dev_dbg(dev,
+			"Block ID 0x%2.2x, header length %zu, payload length %zu\n",
+			block_id, hlen, plen);
+
+	if (!has_headroom(&block->length, hlen + plen, endp))
+		return -ENODATA;
+
+	if (__block_id)
+		*__block_id = block_id;
+
+	if (payload)
+		*payload = (void *)&block->length + hlen;
+
+	if (next_block)
+		*next_block = (void *)&block->length + hlen + plen;
+
+	return 0;
+}
+
+static int ccs_data_parse_regs(struct bin_container *bin,
+			       struct ccs_reg **__regs,
+			       size_t *__num_regs, const void *payload,
+			       const void *endp, struct device *dev)
+{
+	struct ccs_reg *regs_base, *regs;
+	size_t num_regs = 0;
+	uint16_t addr = 0;
+
+	if (bin->base && __regs) {
+		regs = regs_base = bin_alloc(bin, sizeof(*regs) * *__num_regs);
+		if (!regs)
+			return -ENOMEM;
+	}
+
+	while (payload < endp && num_regs < INT_MAX) {
+		const struct __ccs_data_block_regs *r = payload;
+		size_t len;
+		const void *data;
+
+		if (!is_contained(r, endp))
+			return -ENODATA;
+
+		switch (r->reg_len >> CCS_DATA_BLOCK_REGS_SEL_SHIFT) {
+		case CCS_DATA_BLOCK_REGS_SEL_REGS:
+			addr += r->reg_len & CCS_DATA_BLOCK_REGS_ADDR_MASK;
+			len = ((r->reg_len & CCS_DATA_BLOCK_REGS_LEN_MASK)
+			       >> CCS_DATA_BLOCK_REGS_LEN_SHIFT) + 1;
+
+			if (!is_contained_with_headroom(r, len, endp))
+				return -ENODATA;
+
+			data = r + 1;
+			break;
+		case CCS_DATA_BLOCK_REGS_SEL_REGS2: {
+			const struct __ccs_data_block_regs2 *r2 = payload;
+
+			if (!is_contained(r2, endp))
+				return -ENODATA;
+
+			addr += ((uint16_t)(r2->reg_len &
+				 CCS_DATA_BLOCK_REGS_2_ADDR_MASK) << 8)
+				+ r2->addr;
+			len = ((r2->reg_len & CCS_DATA_BLOCK_REGS_2_LEN_MASK)
+			       >> CCS_DATA_BLOCK_REGS_2_LEN_SHIFT) + 1;
+
+			if (!is_contained_with_headroom(r2, len, endp))
+				return -ENODATA;
+
+			data = r2 + 1;
+			break;
+		}
+		case CCS_DATA_BLOCK_REGS_SEL_REGS3: {
+			const struct __ccs_data_block_regs3 *r3 = payload;
+
+			if (!is_contained(r3, endp))
+				return -ENODATA;
+
+			addr = ((uint16_t)r3->addr[0] << 8) + r3->addr[1];
+			len = (r3->reg_len & CCS_DATA_BLOCK_REGS_3_LEN_MASK) + 1;
+
+			if (!is_contained_with_headroom(r3, len, endp))
+				return -ENODATA;
+
+			data = r3 + 1;
+			break;
+		}
+		default:
+			return -EINVAL;
+		}
+
+		num_regs++;
+
+		if (!bin->base) {
+			bin_reserve(bin, len);
+		} else if (__regs) {
+			regs->addr = addr;
+			regs->len = len;
+			regs->value = bin_alloc(bin, len);
+			if (!regs->value)
+				return -ENOMEM;
+
+			memcpy(regs->value, data, len);
+			regs++;
+		}
+
+		addr += len;
+		payload = data + len;
+	}
+
+	if (!bin->base)
+		bin_reserve(bin, sizeof(*regs) * num_regs);
+
+	if (__num_regs)
+		*__num_regs = num_regs;
+
+	if (bin->base && __regs)
+		*__regs = regs_base;
+
+	return 0;
+}
+
+static int ccs_data_parse_reg_rules(struct bin_container *bin,
+				    struct ccs_reg **__regs,
+				    size_t *__num_regs,
+				    const void *payload,
+				    const void *endp, struct device *dev)
+{
+	int rval;
+
+	if (!bin->base)
+		return ccs_data_parse_regs(bin, NULL, NULL, payload, endp, dev);
+
+	rval = ccs_data_parse_regs(bin, NULL, __num_regs, payload, endp, dev);
+	if (rval)
+		return rval;
+
+	return ccs_data_parse_regs(bin, __regs, __num_regs, payload, endp,
+				   dev);
+}
+
+static void assign_ffd_entry(struct ccs_frame_format_desc *desc,
+			     const struct __ccs_data_block_ffd_entry *ent)
+{
+	desc->pixelcode = ent->pixelcode;
+	desc->value = ((uint16_t)ent->value[0] << 8) + ent->value[1];
+}
+
+static int ccs_data_parse_ffd(struct bin_container *bin,
+			      struct ccs_frame_format_descs **ffd,
+			      const void *payload,
+			      const void *endp, struct device *dev)
+{
+	const struct __ccs_data_block_ffd *__ffd = payload;
+	const struct __ccs_data_block_ffd_entry *__entry;
+	unsigned int i;
+
+	if (!is_contained(__ffd, endp))
+		return -ENODATA;
+
+	if ((void *)__ffd + sizeof(*__ffd) +
+	    ((uint32_t)__ffd->num_column_descs +
+	     (uint32_t)__ffd->num_row_descs) *
+	    sizeof(struct __ccs_data_block_ffd_entry) != endp)
+		return -ENODATA;
+
+	if (!bin->base) {
+		bin_reserve(bin, sizeof(**ffd));
+		bin_reserve(bin, __ffd->num_column_descs *
+			    sizeof(struct ccs_frame_format_desc));
+		bin_reserve(bin, __ffd->num_row_descs *
+			    sizeof(struct ccs_frame_format_desc));
+
+		return 0;
+	}
+
+	*ffd = bin_alloc(bin, sizeof(**ffd));
+	if (!*ffd)
+		return -ENOMEM;
+
+	(*ffd)->num_column_descs = __ffd->num_column_descs;
+	(*ffd)->num_row_descs = __ffd->num_row_descs;
+	__entry = (void *)(__ffd + 1);
+
+	(*ffd)->column_descs = bin_alloc(bin, __ffd->num_column_descs *
+					 sizeof(*(*ffd)->column_descs));
+	if (!(*ffd)->column_descs)
+		return -ENOMEM;
+
+	for (i = 0; i < __ffd->num_column_descs; i++, __entry++)
+		assign_ffd_entry(&(*ffd)->column_descs[i], __entry);
+
+	(*ffd)->row_descs = bin_alloc(bin, __ffd->num_row_descs *
+				      sizeof(*(*ffd)->row_descs));
+	if (!(*ffd)->row_descs)
+		return -ENOMEM;
+
+	for (i = 0; i < __ffd->num_row_descs; i++, __entry++)
+		assign_ffd_entry(&(*ffd)->row_descs[i], __entry);
+
+	if (__entry != endp)
+		return -EPROTO;
+
+	return 0;
+}
+
+static int ccs_data_parse_pdaf_readout(struct bin_container *bin,
+				       struct ccs_pdaf_readout **pdaf_readout,
+				       const void *payload,
+				       const void *endp, struct device *dev)
+{
+	const struct __ccs_data_block_pdaf_readout *__pdaf = payload;
+
+	if (!is_contained(__pdaf, endp))
+		return -ENODATA;
+
+	if (!bin->base) {
+		bin_reserve(bin, sizeof(**pdaf_readout));
+	} else {
+		*pdaf_readout = bin_alloc(bin, sizeof(**pdaf_readout));
+		if (!*pdaf_readout)
+			return -ENOMEM;
+
+		(*pdaf_readout)->pdaf_readout_info_order =
+			__pdaf->pdaf_readout_info_order;
+	}
+
+	return ccs_data_parse_ffd(bin, !bin->base ? NULL : &(*pdaf_readout)->ffd,
+				  __pdaf + 1, endp, dev);
+}
+
+static int ccs_data_parse_rules(struct bin_container *bin,
+				struct ccs_rule **__rules,
+				size_t *__num_rules, const void *payload,
+				const void *endp, struct device *dev)
+{
+	struct ccs_rule *rules_base, *rules = NULL, *next_rule;
+	size_t num_rules = 0;
+	const void *__next_rule = payload;
+	int rval;
+
+	if (bin->base) {
+		rules_base = next_rule =
+			bin_alloc(bin, sizeof(*rules) * *__num_rules);
+		if (!rules_base)
+			return -ENOMEM;
+	}
+
+	while (__next_rule < endp) {
+		size_t rule_hlen, rule_plen, rule_plen2;
+		const uint8_t *__rule_type;
+		const void *rule_payload;
+
+		/* Size of a single rule */
+		rval = ccs_data_parse_length_specifier(__next_rule, &rule_hlen,
+						       &rule_plen, endp);
+
+		if (rval < 0)
+			return rval;
+
+		__rule_type = __next_rule + rule_hlen;
+
+		if (!is_contained(__rule_type, endp))
+			return -ENODATA;
+
+		rule_payload = __rule_type + 1;
+		rule_plen2 = rule_plen - sizeof(*__rule_type);
+
+		switch (*__rule_type) {
+		case CCS_DATA_BLOCK_RULE_ID_IF: {
+			const struct __ccs_data_block_rule_if *__if_rules =
+				rule_payload;
+			const size_t __num_if_rules =
+				rule_plen2 / sizeof(*__if_rules);
+			struct ccs_if_rule *if_rule;
+
+			if (!has_headroom(__if_rules,
+					  sizeof(*__if_rules) * __num_if_rules,
+					  rule_payload + rule_plen2))
+				return -ENODATA;
+
+			/* Also check there is no extra data */
+			if (__if_rules + __num_if_rules !=
+			    rule_payload + rule_plen2)
+				return -EINVAL;
+
+			if (!bin->base) {
+				bin_reserve(bin,
+					    sizeof(*if_rule) *
+					    __num_if_rules);
+				num_rules++;
+			} else {
+				unsigned int i;
+
+				rules = next_rule;
+				next_rule++;
+
+				if_rule = bin_alloc(bin,
+						    sizeof(*if_rule) *
+						    __num_if_rules);
+				if (!if_rule)
+					return -ENOMEM;
+
+				for (i = 0; i < __num_if_rules; i++) {
+					if_rule[i].addr =
+						((uint16_t)__if_rules[i].addr[0]
+						 << 8) +
+						__if_rules[i].addr[1];
+					if_rule[i].value = __if_rules[i].value;
+					if_rule[i].mask = __if_rules[i].mask;
+				}
+
+				rules->if_rules = if_rule;
+				rules->num_if_rules = __num_if_rules;
+			}
+			break;
+		}
+		case CCS_DATA_BLOCK_RULE_ID_READ_ONLY_REGS:
+			rval = ccs_data_parse_reg_rules(bin, &rules->read_only_regs,
+							&rules->num_read_only_regs,
+							rule_payload,
+							rule_payload + rule_plen2,
+							dev);
+			if (rval)
+				return rval;
+			break;
+		case CCS_DATA_BLOCK_RULE_ID_FFD:
+			rval = ccs_data_parse_ffd(bin, &rules->frame_format,
+						  rule_payload,
+						  rule_payload + rule_plen2,
+						  dev);
+			if (rval)
+				return rval;
+			break;
+		case CCS_DATA_BLOCK_RULE_ID_MSR:
+			rval = ccs_data_parse_reg_rules(bin,
+							&rules->manufacturer_regs,
+							&rules->num_manufacturer_regs,
+							rule_payload,
+							rule_payload + rule_plen2,
+							dev);
+			if (rval)
+				return rval;
+			break;
+		case CCS_DATA_BLOCK_RULE_ID_PDAF_READOUT:
+			rval = ccs_data_parse_pdaf_readout(bin,
+							   &rules->pdaf_readout,
+							   rule_payload,
+							   rule_payload + rule_plen2,
+							   dev);
+			if (rval)
+				return rval;
+			break;
+		default:
+			dev_dbg(dev,
+				"Don't know how to handle rule type %u!\n",
+				*__rule_type);
+			return -EINVAL;
+		}
+		__next_rule = __next_rule + rule_hlen + rule_plen;
+	}
+
+	if (!bin->base) {
+		bin_reserve(bin, sizeof(*rules) * num_rules);
+		*__num_rules = num_rules;
+	} else {
+		*__rules = rules_base;
+	}
+
+	return 0;
+}
+
+static int ccs_data_parse_pdaf(struct bin_container *bin, struct ccs_pdaf_pix_loc **pdaf,
+			       const void *payload, const void *endp,
+			       struct device *dev)
+{
+	const struct __ccs_data_block_pdaf_pix_loc *__pdaf = payload;
+	const struct __ccs_data_block_pdaf_pix_loc_block_desc_group *__bdesc_group;
+	const struct __ccs_data_block_pdaf_pix_loc_pixel_desc *__pixel_desc;
+	unsigned int i;
+	uint16_t num_block_desc_groups;
+	uint8_t max_block_type_id = 0;
+	const uint8_t *__num_pixel_descs;
+
+	if (!is_contained(__pdaf, endp))
+		return -ENODATA;
+
+	if (bin->base) {
+		*pdaf = bin_alloc(bin, sizeof(**pdaf));
+		if (!*pdaf)
+			return -ENOMEM;
+	} else {
+		bin_reserve(bin, sizeof(**pdaf));
+	}
+
+	num_block_desc_groups =
+		((uint16_t)__pdaf->num_block_desc_groups[0] << 8) +
+		__pdaf->num_block_desc_groups[1];
+
+	if (bin->base) {
+		(*pdaf)->main_offset_x =
+			((uint16_t)__pdaf->main_offset_x[0] << 8) +
+			__pdaf->main_offset_x[1];
+		(*pdaf)->main_offset_y =
+			((uint16_t)__pdaf->main_offset_y[0] << 8) +
+			__pdaf->main_offset_y[1];
+		(*pdaf)->global_pdaf_type = __pdaf->global_pdaf_type;
+		(*pdaf)->block_width = __pdaf->block_width;
+		(*pdaf)->block_height = __pdaf->block_height;
+		(*pdaf)->num_block_desc_groups = num_block_desc_groups;
+	}
+
+	__bdesc_group = (const void *)(__pdaf + 1);
+
+	if (bin->base) {
+		(*pdaf)->block_desc_groups =
+			bin_alloc(bin,
+				  sizeof(struct ccs_pdaf_pix_loc_block_desc_group) *
+				  num_block_desc_groups);
+		if (!(*pdaf)->block_desc_groups)
+			return -ENOMEM;
+	} else {
+		bin_reserve(bin, sizeof(struct ccs_pdaf_pix_loc_block_desc_group) *
+			    num_block_desc_groups);
+	}
+
+	for (i = 0; i < num_block_desc_groups; i++) {
+		const struct __ccs_data_block_pdaf_pix_loc_block_desc *__bdesc;
+		uint16_t num_block_descs;
+		unsigned int j;
+
+		if (!is_contained(__bdesc_group, endp))
+			return -ENODATA;
+
+		num_block_descs =
+			((uint16_t)__bdesc_group->num_block_descs[0] << 8) +
+			__bdesc_group->num_block_descs[1];
+
+		if (bin->base) {
+			(*pdaf)->block_desc_groups[i].repeat_y =
+				__bdesc_group->repeat_y;
+			(*pdaf)->block_desc_groups[i].num_block_descs =
+				num_block_descs;
+		}
+
+		__bdesc = (const void *)(__bdesc_group + 1);
+
+		if (bin->base) {
+			(*pdaf)->block_desc_groups[i].block_descs =
+				bin_alloc(bin,
+					  sizeof(struct ccs_pdaf_pix_loc_block_desc) *
+					  num_block_descs);
+			if (!(*pdaf)->block_desc_groups[i].block_descs)
+				return -ENOMEM;
+		} else {
+			bin_reserve(bin, sizeof(struct ccs_pdaf_pix_loc_block_desc) *
+				    num_block_descs);
+		}
+
+		for (j = 0; j < num_block_descs; j++, __bdesc++) {
+			struct ccs_pdaf_pix_loc_block_desc *bdesc;
+
+			if (!is_contained(__bdesc, endp))
+				return -ENODATA;
+
+			if (max_block_type_id <= __bdesc->block_type_id)
+				max_block_type_id = __bdesc->block_type_id + 1;
+
+			if (!bin->base)
+				continue;
+
+			bdesc = &(*pdaf)->block_desc_groups[i].block_descs[j];
+
+			bdesc->repeat_x = ((uint16_t)__bdesc->repeat_x[0] << 8)
+				+ __bdesc->repeat_x[1];
+
+			if (__bdesc->block_type_id >= num_block_descs)
+				return -EINVAL;
+
+			bdesc->block_type_id = __bdesc->block_type_id;
+		}
+
+		__bdesc_group = (const void *)__bdesc;
+	}
+
+	__num_pixel_descs = (const void *)__bdesc_group;
+
+	if (bin->base) {
+		(*pdaf)->pixel_desc_groups =
+			bin_alloc(bin,
+				  sizeof(struct ccs_pdaf_pix_loc_pixel_desc_group) *
+				  max_block_type_id);
+		if (!(*pdaf)->pixel_desc_groups)
+			return -ENOMEM;
+		(*pdaf)->num_pixel_desc_grups = max_block_type_id;
+	} else {
+		bin_reserve(bin, sizeof(struct ccs_pdaf_pix_loc_pixel_desc_group) *
+			    max_block_type_id);
+	}
+
+	for (i = 0; i < max_block_type_id; i++) {
+		struct ccs_pdaf_pix_loc_pixel_desc_group *pdgroup;
+		unsigned int j;
+
+		if (!is_contained(__num_pixel_descs, endp))
+			return -ENODATA;
+
+		if (bin->base) {
+			pdgroup = &(*pdaf)->pixel_desc_groups[i];
+			pdgroup->descs =
+				bin_alloc(bin,
+					  sizeof(struct ccs_pdaf_pix_loc_pixel_desc) *
+					  *__num_pixel_descs);
+			if (!pdgroup->descs)
+				return -ENOMEM;
+			pdgroup->num_descs = *__num_pixel_descs;
+		} else {
+			bin_reserve(bin, sizeof(struct ccs_pdaf_pix_loc_pixel_desc) *
+				    *__num_pixel_descs);
+		}
+
+		__pixel_desc = (const void *)(__num_pixel_descs + 1);
+
+		for (j = 0; j < *__num_pixel_descs; j++, __pixel_desc++) {
+			struct ccs_pdaf_pix_loc_pixel_desc *pdesc;
+
+			if (!is_contained(__pixel_desc, endp))
+				return -ENODATA;
+
+			if (!bin->base)
+				continue;
+
+			pdesc = &pdgroup->descs[j];
+			pdesc->pixel_type = __pixel_desc->pixel_type;
+			pdesc->small_offset_x = __pixel_desc->small_offset_x;
+			pdesc->small_offset_y = __pixel_desc->small_offset_y;
+		}
+
+		__num_pixel_descs = (const void *)(__pixel_desc + 1);
+	}
+
+	return 0;
+}
+
+static int ccs_data_parse_license(struct bin_container *bin,
+				  char **__license,
+				  size_t *__license_length,
+				  const void *payload, const void *endp)
+{
+	size_t size = endp - payload;
+	char *license;
+
+	if (!bin->base) {
+		bin_reserve(bin, size);
+		return 0;
+	}
+
+	license = bin_alloc(bin, size);
+	if (!license)
+		return -ENOMEM;
+
+	memcpy(license, payload, size);
+
+	*__license = license;
+	*__license_length = size;
+
+	return 0;
+}
+
+static int ccs_data_parse_end(bool *end, const void *payload, const void *endp,
+			      struct device *dev)
+{
+	const struct __ccs_data_block_end *__end = payload;
+
+	if (__end + 1 != endp) {
+		dev_dbg(dev, "Invalid end block length %u\n",
+			(unsigned int)(endp - payload));
+		return -ENODATA;
+	}
+
+	*end = true;
+
+	return 0;
+}
+
+static int __ccs_data_parse(struct bin_container *bin,
+			    struct ccs_data_container *ccsdata,
+			    const void *data, size_t len, struct device *dev,
+			    bool verbose)
+{
+	const struct __ccs_data_block *block = data;
+	const struct __ccs_data_block *endp = data + len;
+	unsigned int version;
+	bool is_first = true;
+	int rval;
+
+	version = ccs_data_parse_format_version(block);
+	if (version != CCS_STATIC_DATA_VERSION) {
+		dev_dbg(dev, "Don't know how to handle version %u\n", version);
+		return -EINVAL;
+	}
+
+	if (verbose)
+		dev_dbg(dev, "Parsing CCS static data version %u\n", version);
+
+	if (!bin->base)
+		*ccsdata = (struct ccs_data_container){ 0 };
+
+	while (block < endp) {
+		const struct __ccs_data_block *next_block;
+		unsigned int block_id;
+		const void *payload;
+
+		rval = ccs_data_block_parse_header(block, is_first, &block_id,
+						   &payload, &next_block, endp,
+						   dev,
+						   bin->base ? false : verbose);
+
+		if (rval < 0)
+			return rval;
+
+		switch (block_id) {
+		case CCS_DATA_BLOCK_ID_DUMMY:
+			break;
+		case CCS_DATA_BLOCK_ID_DATA_VERSION:
+			rval = ccs_data_parse_version(bin, ccsdata, payload,
+						      next_block);
+			if (rval < 0)
+				return rval;
+			break;
+		case CCS_DATA_BLOCK_ID_SENSOR_READ_ONLY_REGS:
+			rval = ccs_data_parse_regs(
+				bin, &ccsdata->sensor_read_only_regs,
+				&ccsdata->num_sensor_read_only_regs, payload,
+				next_block, dev);
+			if (rval < 0)
+				return rval;
+			break;
+		case CCS_DATA_BLOCK_ID_SENSOR_MANUFACTURER_REGS:
+			rval = ccs_data_parse_regs(
+				bin, &ccsdata->sensor_manufacturer_regs,
+				&ccsdata->num_sensor_manufacturer_regs, payload,
+				next_block, dev);
+			if (rval < 0)
+				return rval;
+			break;
+		case CCS_DATA_BLOCK_ID_MODULE_READ_ONLY_REGS:
+			rval = ccs_data_parse_regs(
+				bin, &ccsdata->module_read_only_regs,
+				&ccsdata->num_module_read_only_regs, payload,
+				next_block, dev);
+			if (rval < 0)
+				return rval;
+			break;
+		case CCS_DATA_BLOCK_ID_MODULE_MANUFACTURER_REGS:
+			rval = ccs_data_parse_regs(
+				bin, &ccsdata->module_manufacturer_regs,
+				&ccsdata->num_module_manufacturer_regs, payload,
+				next_block, dev);
+			if (rval < 0)
+				return rval;
+			break;
+		case CCS_DATA_BLOCK_ID_SENSOR_PDAF_PIXEL_LOCATION:
+			rval = ccs_data_parse_pdaf(bin, &ccsdata->sensor_pdaf,
+						   payload, next_block, dev);
+			if (rval < 0)
+				return rval;
+			break;
+		case CCS_DATA_BLOCK_ID_MODULE_PDAF_PIXEL_LOCATION:
+			rval = ccs_data_parse_pdaf(bin, &ccsdata->module_pdaf,
+						   payload, next_block, dev);
+			if (rval < 0)
+				return rval;
+			break;
+		case CCS_DATA_BLOCK_ID_SENSOR_RULE_BASED_BLOCK:
+			rval = ccs_data_parse_rules(
+				bin, &ccsdata->sensor_rules,
+				&ccsdata->num_sensor_rules, payload, next_block,
+				dev);
+			if (rval < 0)
+				return rval;
+			break;
+		case CCS_DATA_BLOCK_ID_MODULE_RULE_BASED_BLOCK:
+			rval = ccs_data_parse_rules(
+				bin, &ccsdata->module_rules,
+				&ccsdata->num_module_rules, payload, next_block,
+				dev);
+			if (rval < 0)
+				return rval;
+			break;
+		case CCS_DATA_BLOCK_ID_LICENSE:
+			rval = ccs_data_parse_license(bin, &ccsdata->license,
+						      &ccsdata->license_length,
+						      payload, next_block);
+			if (rval < 0)
+				return rval;
+			break;
+		case CCS_DATA_BLOCK_ID_END:
+			rval = ccs_data_parse_end(&ccsdata->end, payload,
+						  next_block, dev);
+			if (rval < 0)
+				return rval;
+			break;
+		default:
+			dev_dbg(dev, "WARNING: not handling block ID 0x%2.2x\n",
+				block_id);
+		}
+
+		block = next_block;
+		is_first = false;
+	}
+
+	return 0;
+}
+
+int ccs_data_parse(struct ccs_data_container *ccsdata, const void *data,
+		   size_t len, struct device *dev, bool verbose)
+{
+	struct bin_container bin = { 0 };
+	int rval;
+
+	rval = __ccs_data_parse(&bin, ccsdata, data, len, dev, verbose);
+	if (rval)
+		return rval;
+
+	rval = bin_backing_alloc(&bin);
+	if (rval)
+		return rval;
+
+	rval = __ccs_data_parse(&bin, ccsdata, data, len, dev, false);
+	if (rval)
+		goto out_free;
+
+	if (verbose && ccsdata->version)
+		print_ccs_data_version(dev, ccsdata->version);
+
+	if (bin.now != bin.end) {
+		rval = -EPROTO;
+		dev_dbg(dev, "parsing mismatch; base %p; now %p; end %p\n",
+			bin.base, bin.now, bin.end);
+		goto out_free;
+	}
+
+	ccsdata->backing = bin.base;
+
+	return 0;
+
+out_free:
+	kvfree(bin.base);
+
+	return rval;
+}
diff --git a/drivers/media/i2c/ccs/ccs-data.h b/drivers/media/i2c/ccs/ccs-data.h
new file mode 100644
index 000000000000..323201a15bf7
--- /dev/null
+++ b/drivers/media/i2c/ccs/ccs-data.h
@@ -0,0 +1,120 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * CCS static data in-memory data structure definitions
+ *
+ * Copyright 2019--2020 Intel Corporation
+ */
+
+#ifndef __CCS_DATA_H__
+#define __CCS_DATA_H__
+
+#include <linux/types.h>
+
+struct ccs_data_block_version {
+	uint16_t version_major;
+	uint16_t version_minor;
+	uint16_t date_year;
+	uint8_t date_month;
+	uint8_t date_day;
+};
+
+struct ccs_reg {
+	uint16_t addr;
+	uint16_t len;
+	uint8_t *value;
+};
+
+struct ccs_if_rule {
+	uint16_t addr;
+	uint8_t value;
+	uint8_t mask;
+};
+
+struct ccs_frame_format_desc {
+	uint8_t pixelcode;
+	uint16_t value;
+};
+
+struct ccs_frame_format_descs {
+	uint8_t num_column_descs;
+	uint8_t num_row_descs;
+	struct ccs_frame_format_desc *column_descs;
+	struct ccs_frame_format_desc *row_descs;
+};
+
+struct ccs_pdaf_readout {
+	uint8_t pdaf_readout_info_order;
+	struct ccs_frame_format_descs *ffd;
+};
+
+struct ccs_rule {
+	size_t num_if_rules;
+	struct ccs_if_rule *if_rules;
+	size_t num_read_only_regs;
+	struct ccs_reg *read_only_regs;
+	size_t num_manufacturer_regs;
+	struct ccs_reg *manufacturer_regs;
+	struct ccs_frame_format_descs *frame_format;
+	struct ccs_pdaf_readout *pdaf_readout;
+};
+
+struct ccs_pdaf_pix_loc_block_desc {
+	uint8_t block_type_id;
+	uint16_t repeat_x;
+};
+
+struct ccs_pdaf_pix_loc_block_desc_group {
+	uint8_t repeat_y;
+	uint16_t num_block_descs;
+	struct ccs_pdaf_pix_loc_block_desc *block_descs;
+};
+
+struct ccs_pdaf_pix_loc_pixel_desc {
+	uint8_t pixel_type;
+	uint8_t small_offset_x;
+	uint8_t small_offset_y;
+};
+
+struct ccs_pdaf_pix_loc_pixel_desc_group {
+	uint8_t num_descs;
+	struct ccs_pdaf_pix_loc_pixel_desc *descs;
+};
+
+struct ccs_pdaf_pix_loc {
+	uint16_t main_offset_x;
+	uint16_t main_offset_y;
+	uint8_t global_pdaf_type;
+	uint8_t block_width;
+	uint8_t block_height;
+	uint16_t num_block_desc_groups;
+	struct ccs_pdaf_pix_loc_block_desc_group *block_desc_groups;
+	uint8_t num_pixel_desc_grups;
+	struct ccs_pdaf_pix_loc_pixel_desc_group *pixel_desc_groups;
+};
+
+struct ccs_data_container {
+	struct ccs_data_block_version *version;
+	size_t num_sensor_read_only_regs;
+	struct ccs_reg *sensor_read_only_regs;
+	size_t num_sensor_manufacturer_regs;
+	struct ccs_reg *sensor_manufacturer_regs;
+	size_t num_sensor_rules;
+	struct ccs_rule *sensor_rules;
+	size_t num_module_read_only_regs;
+	struct ccs_reg *module_read_only_regs;
+	size_t num_module_manufacturer_regs;
+	struct ccs_reg *module_manufacturer_regs;
+	size_t num_module_rules;
+	struct ccs_rule *module_rules;
+	struct ccs_pdaf_pix_loc *sensor_pdaf;
+	struct ccs_pdaf_pix_loc *module_pdaf;
+	size_t license_length;
+	char *license;
+	bool end;
+	void *backing;
+};
+
+int ccs_data_parse(struct ccs_data_container *ccsdata, const void *data,
+		   size_t len, struct device *dev, bool verbose);
+
+#endif /* __CCS_DATA_H__ */
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 09/30] ccs: Combine revision number major and minor into one
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (7 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 08/30] ccs: Add CCS static data parser library Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 10/30] ccs: Read CCS static data from firmware binaries Sakari Ailus
                   ` (20 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

The module revision number major and minor are both 8 bits while the
sensor revision number is 16 bits. Combine the module revision into one
number.

This also adds printing the lowest 8 bits of the module version through
the sysfs attribute.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-core.c  | 25 ++++++++++++++-----------
 drivers/media/i2c/ccs/ccs-quirk.c |  2 +-
 drivers/media/i2c/ccs/ccs.h       |  3 +--
 3 files changed, 16 insertions(+), 14 deletions(-)

diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 6fb546ca08f3..17287a8f539c 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -2427,11 +2427,11 @@ ccs_sysfs_ident_read(struct device *dev, struct device_attribute *attr,
 	if (minfo->mipi_manufacturer_id)
 		return snprintf(buf, PAGE_SIZE, "%4.4x%4.4x%2.2x\n",
 				minfo->mipi_manufacturer_id, minfo->model_id,
-				minfo->revision_number_major) + 1;
+				minfo->revision_number) + 1;
 	else
 		return snprintf(buf, PAGE_SIZE, "%2.2x%4.4x%2.2x\n",
 				minfo->smia_manufacturer_id, minfo->model_id,
-				minfo->revision_number_major) + 1;
+				minfo->revision_number) + 1;
 }
 
 static DEVICE_ATTR(ident, S_IRUGO, ccs_sysfs_ident_read, NULL);
@@ -2445,6 +2445,7 @@ static int ccs_identify_module(struct ccs_sensor *sensor)
 	struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
 	struct ccs_module_info *minfo = &sensor->minfo;
 	unsigned int i;
+	u32 rev;
 	int rval = 0;
 
 	/* Module info */
@@ -2460,11 +2461,13 @@ static int ccs_identify_module(struct ccs_sensor *sensor)
 	if (!rval)
 		rval = ccs_read_addr_8only(sensor,
 					   CCS_R_MODULE_REVISION_NUMBER_MAJOR,
-					   &minfo->revision_number_major);
-	if (!rval)
+					   &rev);
+	if (!rval) {
 		rval = ccs_read_addr_8only(sensor,
 					   CCS_R_MODULE_REVISION_NUMBER_MINOR,
-					   &minfo->revision_number_minor);
+					   &minfo->revision_number);
+		minfo->revision_number |= rev << 8;
+	}
 	if (!rval)
 		rval = ccs_read_addr_8only(sensor, CCS_R_MODULE_DATE_YEAR,
 					   &minfo->module_year);
@@ -2519,9 +2522,9 @@ static int ccs_identify_module(struct ccs_sensor *sensor)
 			minfo->smia_manufacturer_id, minfo->model_id);
 
 	dev_dbg(&client->dev,
-		"module revision 0x%2.2x-0x%2.2x date %2.2d-%2.2d-%2.2d\n",
-		minfo->revision_number_major, minfo->revision_number_minor,
-		minfo->module_year, minfo->module_month, minfo->module_day);
+		"module revision 0x%4.4x date %2.2d-%2.2d-%2.2d\n",
+		minfo->revision_number, minfo->module_year, minfo->module_month,
+		minfo->module_day);
 
 	if (minfo->sensor_mipi_manufacturer_id)
 		dev_dbg(&client->dev, "MIPI CCS sensor 0x%4.4x-0x%4.4x\n",
@@ -2559,7 +2562,7 @@ static int ccs_identify_module(struct ccs_sensor *sensor)
 		minfo->smia_manufacturer_id =
 			minfo->sensor_smia_manufacturer_id;
 		minfo->model_id = minfo->sensor_model_id;
-		minfo->revision_number_major = minfo->sensor_revision_number;
+		minfo->revision_number = minfo->sensor_revision_number;
 	}
 
 	for (i = 0; i < ARRAY_SIZE(ccs_module_idents); i++) {
@@ -2576,11 +2579,11 @@ static int ccs_identify_module(struct ccs_sensor *sensor)
 		if (ccs_module_idents[i].flags
 		    & CCS_MODULE_IDENT_FLAG_REV_LE) {
 			if (ccs_module_idents[i].revision_number_major
-			    < minfo->revision_number_major)
+			    < (minfo->revision_number >> 8))
 				continue;
 		} else {
 			if (ccs_module_idents[i].revision_number_major
-			    != minfo->revision_number_major)
+			    != (minfo->revision_number >> 8))
 				continue;
 		}
 
diff --git a/drivers/media/i2c/ccs/ccs-quirk.c b/drivers/media/i2c/ccs/ccs-quirk.c
index facec28f8447..07c5733b4244 100644
--- a/drivers/media/i2c/ccs/ccs-quirk.c
+++ b/drivers/media/i2c/ccs/ccs-quirk.c
@@ -35,7 +35,7 @@ static int ccs_write_addr_8s(struct ccs_sensor *sensor,
 
 static int jt8ew9_limits(struct ccs_sensor *sensor)
 {
-	if (sensor->minfo.revision_number_major < 0x03)
+	if (sensor->minfo.revision_number < 0x0300)
 		sensor->frame_skip = 1;
 
 	/* Below 24 gain doesn't have effect at all, */
diff --git a/drivers/media/i2c/ccs/ccs.h b/drivers/media/i2c/ccs/ccs.h
index 2d1e8339f663..ad2ff5a74424 100644
--- a/drivers/media/i2c/ccs/ccs.h
+++ b/drivers/media/i2c/ccs/ccs.h
@@ -107,8 +107,7 @@ struct ccs_module_info {
 	u32 smia_manufacturer_id;
 	u32 mipi_manufacturer_id;
 	u32 model_id;
-	u32 revision_number_major;
-	u32 revision_number_minor;
+	u32 revision_number;
 
 	u32 module_year;
 	u32 module_month;
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 10/30] ccs: Read CCS static data from firmware binaries
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (8 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 09/30] ccs: Combine revision number major and minor into one Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 11/30] ccs: Stop reading arrays after the first zero Sakari Ailus
                   ` (19 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

Read the CCS static data for sensors and modules. The files are expected
to be found in "ccs" directory.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-core.c | 47 +++++++++++++++++++++++++++++++-
 drivers/media/i2c/ccs/ccs.h      |  2 ++
 2 files changed, 48 insertions(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 17287a8f539c..be27b002a772 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -16,6 +16,7 @@
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/device.h>
+#include <linux/firmware.h>
 #include <linux/gpio.h>
 #include <linux/gpio/consumer.h>
 #include <linux/module.h>
@@ -2953,6 +2954,8 @@ static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev)
 static int ccs_probe(struct i2c_client *client)
 {
 	struct ccs_sensor *sensor;
+	const struct firmware *fw;
+	char filename[40];
 	unsigned int i;
 	int rval;
 
@@ -3042,9 +3045,43 @@ static int ccs_probe(struct i2c_client *client)
 		goto out_power_off;
 	}
 
+	rval = snprintf(filename, sizeof(filename),
+			"ccs/ccs-sensor-%4.4x-%4.4x-%4.4x.fw",
+			sensor->minfo.sensor_mipi_manufacturer_id,
+			sensor->minfo.sensor_model_id,
+			sensor->minfo.sensor_revision_number);
+	if (rval >= sizeof(filename)) {
+		rval = -ENOMEM;
+		goto out_power_off;
+	}
+
+	rval = request_firmware(&fw, filename, &client->dev);
+	if (!rval) {
+		ccs_data_parse(&sensor->sdata, fw->data, fw->size, &client->dev,
+			       true);
+		release_firmware(fw);
+	}
+
+	rval = snprintf(filename, sizeof(filename),
+			"ccs/ccs-module-%4.4x-%4.4x-%4.4x.fw",
+			sensor->minfo.mipi_manufacturer_id,
+			sensor->minfo.model_id,
+			sensor->minfo.revision_number);
+	if (rval >= sizeof(filename)) {
+		rval = -ENOMEM;
+		goto out_release_sdata;
+	}
+
+	rval = request_firmware(&fw, filename, &client->dev);
+	if (!rval) {
+		ccs_data_parse(&sensor->mdata, fw->data, fw->size, &client->dev,
+			       true);
+		release_firmware(fw);
+	}
+
 	rval = ccs_read_all_limits(sensor);
 	if (rval)
-		goto out_power_off;
+		goto out_release_mdata;
 
 	rval = ccs_read_frame_fmt(sensor);
 	if (rval) {
@@ -3208,6 +3245,12 @@ static int ccs_probe(struct i2c_client *client)
 out_cleanup:
 	ccs_cleanup(sensor);
 
+out_release_mdata:
+	kvfree(sensor->mdata.backing);
+
+out_release_sdata:
+	kvfree(sensor->sdata.backing);
+
 out_free_ccs_limits:
 	kfree(sensor->ccs_limits);
 
@@ -3238,6 +3281,8 @@ static int ccs_remove(struct i2c_client *client)
 	ccs_cleanup(sensor);
 	mutex_destroy(&sensor->mutex);
 	kfree(sensor->ccs_limits);
+	kvfree(sensor->sdata.backing);
+	kvfree(sensor->mdata.backing);
 
 	return 0;
 }
diff --git a/drivers/media/i2c/ccs/ccs.h b/drivers/media/i2c/ccs/ccs.h
index ad2ff5a74424..cbcd93b519da 100644
--- a/drivers/media/i2c/ccs/ccs.h
+++ b/drivers/media/i2c/ccs/ccs.h
@@ -16,6 +16,7 @@
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-subdev.h>
 
+#include "ccs-data.h"
 #include "ccs-quirk.h"
 #include "ccs-regs.h"
 #include "ccs-reg-access.h"
@@ -227,6 +228,7 @@ struct ccs_sensor {
 	const struct ccs_csi_data_format *internal_csi_format;
 	u32 default_mbus_frame_fmts;
 	int default_pixel_order;
+	struct ccs_data_container sdata, mdata;
 
 	u8 binning_horizontal;
 	u8 binning_vertical;
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 11/30] ccs: Stop reading arrays after the first zero
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (9 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 10/30] ccs: Read CCS static data from firmware binaries Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 12/30] ccs: The functions to get compose or crop rectangle never return NULL Sakari Ailus
                   ` (18 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

The register arrays have a certain size but not all the entries will be
relevant. In practice reading can be stopped after encountering a zero
value in the array. Do that to avoid extra reads.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-core.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index be27b002a772..dec248fe7cc1 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -199,6 +199,9 @@ static int ccs_read_all_limits(struct ccs_sensor *sensor)
 				goto out_err;
 			}
 
+			if (!val && j)
+				break;
+
 			ccs_assign_limit(ptr, width, val);
 
 			dev_dbg(&client->dev, "0x%8.8x \"%s\" = %u, 0x%x\n",
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 12/30] ccs: The functions to get compose or crop rectangle never return NULL
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (10 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 11/30] ccs: Stop reading arrays after the first zero Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 13/30] ccs: Replace somewhat harsh internal checks based on BUG with WARN_ON Sakari Ailus
                   ` (17 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

The NULL check is not needed as the functions do not return NULL. Remove
the check (and BUG).

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-core.c | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index dec248fe7cc1..25b4c84524ff 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -1766,16 +1766,12 @@ static void ccs_get_crop_compose(struct v4l2_subdev *subdev,
 			*comps = &ssd->compose;
 	} else {
 		if (crops) {
-			for (i = 0; i < subdev->entity.num_pads; i++) {
+			for (i = 0; i < subdev->entity.num_pads; i++)
 				crops[i] = v4l2_subdev_get_try_crop(subdev, cfg, i);
-				BUG_ON(!crops[i]);
-			}
 		}
-		if (comps) {
+		if (comps)
 			*comps = v4l2_subdev_get_try_compose(subdev, cfg,
 							     CCS_PAD_SINK);
-			BUG_ON(!*comps);
-		}
 	}
 }
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 13/30] ccs: Replace somewhat harsh internal checks based on BUG with WARN_ON
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (11 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 12/30] ccs: The functions to get compose or crop rectangle never return NULL Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 14/30] ccs: Refactor register reading a little Sakari Ailus
                   ` (16 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

If an internal driver error was encountered, BUG was issued. Instead, do
less harsh WARN_ON_ONCE and try to manage with the consequences.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-core.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 25b4c84524ff..70b4d2180971 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -546,6 +546,10 @@ static void ccs_update_mbus_formats(struct ccs_sensor *sensor)
 		to_csi_format_idx(sensor->internal_csi_format) & ~3;
 	unsigned int pixel_order = ccs_pixel_order(sensor);
 
+	if (WARN_ON_ONCE(max(internal_csi_format_idx, csi_format_idx) +
+			 pixel_order >= ARRAY_SIZE(ccs_csi_data_formats)))
+		return;
+
 	sensor->mbus_frame_fmts =
 		sensor->default_mbus_frame_fmts << pixel_order;
 	sensor->csi_format =
@@ -554,9 +558,6 @@ static void ccs_update_mbus_formats(struct ccs_sensor *sensor)
 		&ccs_csi_data_formats[internal_csi_format_idx
 					 + pixel_order];
 
-	BUG_ON(max(internal_csi_format_idx, csi_format_idx) + pixel_order
-	       >= ARRAY_SIZE(ccs_csi_data_formats));
-
 	dev_dbg(&client->dev, "new pixel order %s\n",
 		pixel_order_str[pixel_order]);
 }
@@ -1806,7 +1807,7 @@ static void ccs_propagate(struct v4l2_subdev *subdev,
 		*crops[CCS_PAD_SRC] = *comp;
 		break;
 	default:
-		BUG();
+		WARN_ON_ONCE(1);
 	}
 }
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 14/30] ccs: Refactor register reading a little
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (12 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 13/30] ccs: Replace somewhat harsh internal checks based on BUG with WARN_ON Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 15/30] ccs: Make real to integer number conversion optional Sakari Ailus
                   ` (15 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

Rework quirk and 8-bit only access functions with a single function that
takes arguments. This is later extensible to support yet more flags.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-reg-access.c | 37 ++++++++++++--------------
 1 file changed, 17 insertions(+), 20 deletions(-)

diff --git a/drivers/media/i2c/ccs/ccs-reg-access.c b/drivers/media/i2c/ccs/ccs-reg-access.c
index a8e9a235bfb3..abec746f3c93 100644
--- a/drivers/media/i2c/ccs/ccs-reg-access.c
+++ b/drivers/media/i2c/ccs/ccs-reg-access.c
@@ -168,39 +168,36 @@ static int __ccs_read_addr(struct ccs_sensor *sensor, u32 reg, u32 *val,
 	return 0;
 }
 
-int ccs_read_addr_no_quirk(struct ccs_sensor *sensor, u32 reg, u32 *val)
-{
-	return __ccs_read_addr(
-		sensor, reg, val,
-		ccs_needs_quirk(sensor, CCS_QUIRK_FLAG_8BIT_READ_ONLY));
-}
-
-static int ccs_read_addr_quirk(struct ccs_sensor *sensor, u32 reg, u32 *val,
-			       bool force8)
+static int ccs_read_addr_raw(struct ccs_sensor *sensor, u32 reg, u32 *val,
+			     bool force8, bool quirk)
 {
 	int rval;
 
-	*val = 0;
-	rval = ccs_call_quirk(sensor, reg_access, false, &reg, val);
-	if (rval == -ENOIOCTLCMD)
-		return 0;
-	if (rval < 0)
-		return rval;
+	if (quirk) {
+		*val = 0;
+		rval = ccs_call_quirk(sensor, reg_access, false, &reg, val);
+		if (rval == -ENOIOCTLCMD)
+			return 0;
+		if (rval < 0)
+			return rval;
 
-	if (force8)
-		return __ccs_read_addr(sensor, reg, val, true);
+		if (force8)
+			return __ccs_read_addr(sensor, reg, val, true);
+	}
 
-	return ccs_read_addr_no_quirk(sensor, reg, val);
+	return __ccs_read_addr(sensor, reg, val,
+			       ccs_needs_quirk(sensor,
+					       CCS_QUIRK_FLAG_8BIT_READ_ONLY));
 }
 
 int ccs_read_addr(struct ccs_sensor *sensor, u32 reg, u32 *val)
 {
-	return ccs_read_addr_quirk(sensor, reg, val, false);
+	return ccs_read_addr_raw(sensor, reg, val, false, true);
 }
 
 int ccs_read_addr_8only(struct ccs_sensor *sensor, u32 reg, u32 *val)
 {
-	return ccs_read_addr_quirk(sensor, reg, val, true);
+	return ccs_read_addr_raw(sensor, reg, val, true, true);
 }
 
 int ccs_write_addr_no_quirk(struct ccs_sensor *sensor, u32 reg, u32 val)
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 15/30] ccs: Make real to integer number conversion optional
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (13 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 14/30] ccs: Refactor register reading a little Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 16/30] ccs: Move limit value real to integer conversion from read to access time Sakari Ailus
                   ` (14 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

The limit values will be raw soon, and the conversion takes place later
on. Prepare for that.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-reg-access.c | 35 +++++++++++++++++++-------
 drivers/media/i2c/ccs/ccs-reg-access.h |  2 ++
 2 files changed, 28 insertions(+), 9 deletions(-)

diff --git a/drivers/media/i2c/ccs/ccs-reg-access.c b/drivers/media/i2c/ccs/ccs-reg-access.c
index abec746f3c93..fe6112cba6be 100644
--- a/drivers/media/i2c/ccs/ccs-reg-access.c
+++ b/drivers/media/i2c/ccs/ccs-reg-access.c
@@ -143,14 +143,23 @@ unsigned int ccs_reg_width(u32 reg)
 	return sizeof(uint8_t);
 }
 
+u32 ccs_reg_conv(struct ccs_sensor *sensor, u32 reg, u32 val)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+
+	if (reg & CCS_FL_FLOAT_IREAL)
+		val = float_to_u32_mul_1000000(client, val);
+
+	return val;
+}
+
 /*
  * Read a 8/16/32-bit i2c register.  The value is returned in 'val'.
  * Returns zero if successful, or non-zero otherwise.
  */
 static int __ccs_read_addr(struct ccs_sensor *sensor, u32 reg, u32 *val,
-			   bool only8)
+			   bool only8, bool conv)
 {
-	struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
 	unsigned int len = ccs_reg_width(reg);
 	int rval;
 
@@ -162,14 +171,16 @@ static int __ccs_read_addr(struct ccs_sensor *sensor, u32 reg, u32 *val,
 	if (rval < 0)
 		return rval;
 
-	if (reg & CCS_FL_FLOAT_IREAL)
-		*val = float_to_u32_mul_1000000(client, *val);
+	if (!conv)
+		return 0;
+
+	*val = ccs_reg_conv(sensor, reg, *val);
 
 	return 0;
 }
 
 static int ccs_read_addr_raw(struct ccs_sensor *sensor, u32 reg, u32 *val,
-			     bool force8, bool quirk)
+			     bool force8, bool quirk, bool conv)
 {
 	int rval;
 
@@ -182,22 +193,28 @@ static int ccs_read_addr_raw(struct ccs_sensor *sensor, u32 reg, u32 *val,
 			return rval;
 
 		if (force8)
-			return __ccs_read_addr(sensor, reg, val, true);
+			return __ccs_read_addr(sensor, reg, val, true, conv);
 	}
 
 	return __ccs_read_addr(sensor, reg, val,
 			       ccs_needs_quirk(sensor,
-					       CCS_QUIRK_FLAG_8BIT_READ_ONLY));
+					       CCS_QUIRK_FLAG_8BIT_READ_ONLY),
+			       conv);
 }
 
 int ccs_read_addr(struct ccs_sensor *sensor, u32 reg, u32 *val)
 {
-	return ccs_read_addr_raw(sensor, reg, val, false, true);
+	return ccs_read_addr_raw(sensor, reg, val, false, true, true);
 }
 
 int ccs_read_addr_8only(struct ccs_sensor *sensor, u32 reg, u32 *val)
 {
-	return ccs_read_addr_raw(sensor, reg, val, true, true);
+	return ccs_read_addr_raw(sensor, reg, val, true, true, true);
+}
+
+int ccs_read_addr_noconv(struct ccs_sensor *sensor, u32 reg, u32 *val)
+{
+	return ccs_read_addr_raw(sensor, reg, val, false, true, false);
 }
 
 int ccs_write_addr_no_quirk(struct ccs_sensor *sensor, u32 reg, u32 val)
diff --git a/drivers/media/i2c/ccs/ccs-reg-access.h b/drivers/media/i2c/ccs/ccs-reg-access.h
index 9fdf5659ed09..5f6ff9c57698 100644
--- a/drivers/media/i2c/ccs/ccs-reg-access.h
+++ b/drivers/media/i2c/ccs/ccs-reg-access.h
@@ -24,10 +24,12 @@ struct ccs_sensor;
 int ccs_read_addr_no_quirk(struct ccs_sensor *sensor, u32 reg, u32 *val);
 int ccs_read_addr(struct ccs_sensor *sensor, u32 reg, u32 *val);
 int ccs_read_addr_8only(struct ccs_sensor *sensor, u32 reg, u32 *val);
+int ccs_read_addr_noconv(struct ccs_sensor *sensor, u32 reg, u32 *val);
 int ccs_write_addr_no_quirk(struct ccs_sensor *sensor, u32 reg, u32 val);
 int ccs_write_addr(struct ccs_sensor *sensor, u32 reg, u32 val);
 
 unsigned int ccs_reg_width(u32 reg);
+u32 ccs_reg_conv(struct ccs_sensor *sensor, u32 reg, u32 val);
 
 #define ccs_read(sensor, reg_name, val) \
 	ccs_read_addr(sensor, CCS_R_##reg_name, val)
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 16/30] ccs: Move limit value real to integer conversion from read to access time
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (14 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 15/30] ccs: Make real to integer number conversion optional Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 17/30] ccs: Read ireal numbers correctly Sakari Ailus
                   ` (13 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

Instead of converting the limit values at register read time, do that at
access time instead.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-core.c | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 70b4d2180971..57efc34fc67d 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -130,6 +130,7 @@ static u32 ccs_get_limit(struct ccs_sensor *sensor,
 			 unsigned int limit, unsigned int offset)
 {
 	void *ptr;
+	u32 val;
 	int ret;
 
 	ret = ccs_limit_ptr(sensor, limit, offset, &ptr);
@@ -138,16 +139,20 @@ static u32 ccs_get_limit(struct ccs_sensor *sensor,
 
 	switch (ccs_reg_width(ccs_limits[ccs_limit_offsets[limit].info].reg)) {
 	case sizeof(u8):
-		return *(u8 *)ptr;
+		val = *(u8 *)ptr;
+		break;
 	case sizeof(u16):
-		return *(u16 *)ptr;
+		val = *(u16 *)ptr;
+		break;
 	case sizeof(u32):
-		return *(u32 *)ptr;
+		val = *(u32 *)ptr;
+		break;
+	default:
+		WARN_ON(1);
+		return 0;
 	}
 
-	WARN_ON(1);
-
-	return 0;
+	return ccs_reg_conv(sensor, ccs_limits[limit].reg, val);
 }
 
 #define CCS_LIM(sensor, limit) \
@@ -188,7 +193,7 @@ static int ccs_read_all_limits(struct ccs_sensor *sensor)
 		     j++, reg += width, ptr += width) {
 			u32 val;
 
-			ret = ccs_read_addr(sensor, reg, &val);
+			ret = ccs_read_addr_noconv(sensor, reg, &val);
 			if (ret)
 				goto out_err;
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 17/30] ccs: Read ireal numbers correctly
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (15 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 16/30] ccs: Move limit value real to integer conversion from read to access time Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 18/30] smiapp-pll: Rename as ccs-pll Sakari Ailus
                   ` (12 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

Some limit values are available in q16.q16 format, referred to as 32-bit
unsigned ireal in CCS. Read these correctly.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-core.c       | 10 ++--------
 drivers/media/i2c/ccs/ccs-reg-access.c | 23 +++++++++++++++++++++--
 drivers/media/i2c/ccs/ccs.h            |  9 +++++++++
 3 files changed, 32 insertions(+), 10 deletions(-)

diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 57efc34fc67d..074b246538d2 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -126,8 +126,8 @@ void ccs_replace_limit(struct ccs_sensor *sensor,
 	ccs_assign_limit(ptr, ccs_reg_width(linfo->reg), val);
 }
 
-static u32 ccs_get_limit(struct ccs_sensor *sensor,
-			 unsigned int limit, unsigned int offset)
+u32 ccs_get_limit(struct ccs_sensor *sensor, unsigned int limit,
+		  unsigned int offset)
 {
 	void *ptr;
 	u32 val;
@@ -155,12 +155,6 @@ static u32 ccs_get_limit(struct ccs_sensor *sensor,
 	return ccs_reg_conv(sensor, ccs_limits[limit].reg, val);
 }
 
-#define CCS_LIM(sensor, limit) \
-	ccs_get_limit(sensor, CCS_L_##limit, 0)
-
-#define CCS_LIM_AT(sensor, limit, offset)	\
-	ccs_get_limit(sensor, CCS_L_##limit, CCS_L_##limit##_OFFSET(offset))
-
 static int ccs_read_all_limits(struct ccs_sensor *sensor)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
diff --git a/drivers/media/i2c/ccs/ccs-reg-access.c b/drivers/media/i2c/ccs/ccs-reg-access.c
index fe6112cba6be..91ccbca11577 100644
--- a/drivers/media/i2c/ccs/ccs-reg-access.c
+++ b/drivers/media/i2c/ccs/ccs-reg-access.c
@@ -15,6 +15,7 @@
 #include <linux/i2c.h>
 
 #include "ccs.h"
+#include "ccs-limits.h"
 
 static uint32_t float_to_u32_mul_1000000(struct i2c_client *client,
 					 uint32_t phloat)
@@ -143,12 +144,30 @@ unsigned int ccs_reg_width(u32 reg)
 	return sizeof(uint8_t);
 }
 
+static u32 ireal32_to_u32_mul_1000000(struct i2c_client *client, u32 val)
+{
+	if (val >> 10 > U32_MAX / 15625) {
+		dev_warn(&client->dev, "value %u overflows!\n", val);
+		return U32_MAX;
+	}
+
+	return ((val >> 10) * 15625) +
+		(val & GENMASK(9, 0)) * 15625 / 1024;
+}
+
 u32 ccs_reg_conv(struct ccs_sensor *sensor, u32 reg, u32 val)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
 
-	if (reg & CCS_FL_FLOAT_IREAL)
-		val = float_to_u32_mul_1000000(client, val);
+	if (reg & CCS_FL_FLOAT_IREAL) {
+		if (CCS_LIM(sensor, CLOCK_CAPA_TYPE_CAPABILITY) &
+		    CCS_CLOCK_CAPA_TYPE_CAPABILITY_IREAL)
+			val = ireal32_to_u32_mul_1000000(client, val);
+		else
+			val = float_to_u32_mul_1000000(client, val);
+	} else if (reg & CCS_FL_IREAL) {
+		val = ireal32_to_u32_mul_1000000(client, val);
+	}
 
 	return val;
 }
diff --git a/drivers/media/i2c/ccs/ccs.h b/drivers/media/i2c/ccs/ccs.h
index cbcd93b519da..f60d1801c469 100644
--- a/drivers/media/i2c/ccs/ccs.h
+++ b/drivers/media/i2c/ccs/ccs.h
@@ -17,6 +17,7 @@
 #include <media/v4l2-subdev.h>
 
 #include "ccs-data.h"
+#include "ccs-limits.h"
 #include "ccs-quirk.h"
 #include "ccs-regs.h"
 #include "ccs-reg-access.h"
@@ -50,6 +51,12 @@
 #define CCS_DFL_I2C_ADDR	(0x20 >> 1) /* Default I2C Address */
 #define CCS_ALT_I2C_ADDR	(0x6e >> 1) /* Alternate I2C Address */
 
+#define CCS_LIM(sensor, limit) \
+	ccs_get_limit(sensor, CCS_L_##limit, 0)
+
+#define CCS_LIM_AT(sensor, limit, offset)	\
+	ccs_get_limit(sensor, CCS_L_##limit, CCS_L_##limit##_OFFSET(offset))
+
 /*
  * Sometimes due to board layout considerations the camera module can be
  * mounted rotated. The typical rotation used is 180 degrees which can be
@@ -277,5 +284,7 @@ struct ccs_sensor {
 
 void ccs_replace_limit(struct ccs_sensor *sensor,
 		       unsigned int limit, unsigned int offset, u32 val);
+u32 ccs_get_limit(struct ccs_sensor *sensor, unsigned int limit,
+		  unsigned int offset);
 
 #endif /* __CCS_H__ */
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 18/30] smiapp-pll: Rename as ccs-pll
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (16 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 17/30] ccs: Read ireal numbers correctly Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 19/30] ccs-pll: Fix MODULE_LICENSE Sakari Ailus
                   ` (11 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

MIPI CCS replaces SMIA and SMIA++ as the current standard. CCS brings new
features while existing functionality will be supported. Rename the
smiapp-pll as ccs-pll accordingly.

Also add Intel copyright to the files.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 MAINTAINERS                                   |  4 +-
 drivers/media/i2c/Kconfig                     |  2 +-
 drivers/media/i2c/Makefile                    |  2 +-
 drivers/media/i2c/{smiapp-pll.c => ccs-pll.c} | 60 +++++++++----------
 drivers/media/i2c/{smiapp-pll.h => ccs-pll.h} | 40 ++++++-------
 drivers/media/i2c/ccs/Kconfig                 |  2 +-
 drivers/media/i2c/ccs/ccs-core.c              | 18 +++---
 drivers/media/i2c/ccs/ccs-quirk.c             |  2 +-
 drivers/media/i2c/ccs/ccs.h                   |  4 +-
 9 files changed, 66 insertions(+), 68 deletions(-)
 rename drivers/media/i2c/{smiapp-pll.c => ccs-pll.c} (90%)
 rename drivers/media/i2c/{smiapp-pll.h => ccs-pll.h} (68%)

diff --git a/MAINTAINERS b/MAINTAINERS
index d6892e1abdc5..252c9e86fccc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11668,8 +11668,8 @@ L:	linux-media@vger.kernel.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml
 F:	drivers/media/i2c/ccs/
-F:	drivers/media/i2c/smiapp-pll.c
-F:	drivers/media/i2c/smiapp-pll.h
+F:	drivers/media/i2c/ccs-pll.c
+F:	drivers/media/i2c/ccs-pll.h
 F:	include/uapi/linux/smiapp.h
 
 MIPS
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 41a8b6189259..4b6fcf13d527 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -722,7 +722,7 @@ menu "Camera sensor devices"
 config VIDEO_APTINA_PLL
 	tristate
 
-config VIDEO_SMIAPP_PLL
+config VIDEO_CCS_PLL
 	tristate
 
 config VIDEO_HI556
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index cb0be09e38bd..c716bac3ed4b 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -104,7 +104,7 @@ obj-$(CONFIG_VIDEO_S5C73M3)	+= s5c73m3/
 obj-$(CONFIG_VIDEO_ADP1653)	+= adp1653.o
 obj-$(CONFIG_VIDEO_LM3560)	+= lm3560.o
 obj-$(CONFIG_VIDEO_LM3646)	+= lm3646.o
-obj-$(CONFIG_VIDEO_SMIAPP_PLL)	+= smiapp-pll.o
+obj-$(CONFIG_VIDEO_CCS_PLL)	+= ccs-pll.o
 obj-$(CONFIG_VIDEO_AK881X)		+= ak881x.o
 obj-$(CONFIG_VIDEO_IR_I2C)  += ir-kbd-i2c.o
 obj-$(CONFIG_VIDEO_I2C)		+= video-i2c.o
diff --git a/drivers/media/i2c/smiapp-pll.c b/drivers/media/i2c/ccs-pll.c
similarity index 90%
rename from drivers/media/i2c/smiapp-pll.c
rename to drivers/media/i2c/ccs-pll.c
index 690abe8cbdb2..d2f0f7375f5c 100644
--- a/drivers/media/i2c/smiapp-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -1,9 +1,10 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * drivers/media/i2c/smiapp-pll.c
+ * drivers/media/i2c/ccs-pll.c
  *
- * Generic driver for SMIA/SMIA++ compliant camera modules
+ * Generic MIPI CCS/SMIA/SMIA++ PLL calculator
  *
+ * Copyright (C) 2020 Intel Corporation
  * Copyright (C) 2011--2012 Nokia Corporation
  * Contact: Sakari Ailus <sakari.ailus@iki.fi>
  */
@@ -13,7 +14,7 @@
 #include <linux/lcm.h>
 #include <linux/module.h>
 
-#include "smiapp-pll.h"
+#include "ccs-pll.h"
 
 /* Return an even number or one. */
 static inline uint32_t clk_div_even(uint32_t a)
@@ -50,11 +51,11 @@ static int bounds_check(struct device *dev, uint32_t val,
 	return -EINVAL;
 }
 
-static void print_pll(struct device *dev, struct smiapp_pll *pll)
+static void print_pll(struct device *dev, struct ccs_pll *pll)
 {
 	dev_dbg(dev, "pre_pll_clk_div\t%u\n",  pll->pre_pll_clk_div);
 	dev_dbg(dev, "pll_multiplier \t%u\n",  pll->pll_multiplier);
-	if (!(pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS)) {
+	if (!(pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS)) {
 		dev_dbg(dev, "op_sys_clk_div \t%u\n", pll->op.sys_clk_div);
 		dev_dbg(dev, "op_pix_clk_div \t%u\n", pll->op.pix_clk_div);
 	}
@@ -64,7 +65,7 @@ static void print_pll(struct device *dev, struct smiapp_pll *pll)
 	dev_dbg(dev, "ext_clk_freq_hz \t%u\n", pll->ext_clk_freq_hz);
 	dev_dbg(dev, "pll_ip_clk_freq_hz \t%u\n", pll->pll_ip_clk_freq_hz);
 	dev_dbg(dev, "pll_op_clk_freq_hz \t%u\n", pll->pll_op_clk_freq_hz);
-	if (!(pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS)) {
+	if (!(pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS)) {
 		dev_dbg(dev, "op_sys_clk_freq_hz \t%u\n",
 			pll->op.sys_clk_freq_hz);
 		dev_dbg(dev, "op_pix_clk_freq_hz \t%u\n",
@@ -75,10 +76,9 @@ static void print_pll(struct device *dev, struct smiapp_pll *pll)
 }
 
 static int check_all_bounds(struct device *dev,
-			    const struct smiapp_pll_limits *limits,
-			    const struct smiapp_pll_branch_limits *op_limits,
-			    struct smiapp_pll *pll,
-			    struct smiapp_pll_branch *op_pll)
+			    const struct ccs_pll_limits *limits,
+			    const struct ccs_pll_branch_limits *op_limits,
+			    struct ccs_pll *pll, struct ccs_pll_branch *op_pll)
 {
 	int rval;
 
@@ -118,7 +118,7 @@ static int check_all_bounds(struct device *dev,
 	 * If there are no OP clocks, the VT clocks are contained in
 	 * the OP clock struct.
 	 */
-	if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS)
+	if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS)
 		return rval;
 
 	if (!rval)
@@ -148,11 +148,11 @@ static int check_all_bounds(struct device *dev,
  *
  * @return Zero on success, error code on error.
  */
-static int __smiapp_pll_calculate(
-	struct device *dev, const struct smiapp_pll_limits *limits,
-	const struct smiapp_pll_branch_limits *op_limits,
-	struct smiapp_pll *pll, struct smiapp_pll_branch *op_pll, uint32_t mul,
-	uint32_t div, uint32_t lane_op_clock_ratio)
+static int
+__ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *limits,
+		    const struct ccs_pll_branch_limits *op_limits,
+		    struct ccs_pll *pll, struct ccs_pll_branch *op_pll,
+		    uint32_t mul, uint32_t div, uint32_t lane_op_clock_ratio)
 {
 	uint32_t sys_div;
 	uint32_t best_pix_div = INT_MAX >> 1;
@@ -252,7 +252,7 @@ static int __smiapp_pll_calculate(
 	op_pll->pix_clk_freq_hz =
 		op_pll->sys_clk_freq_hz / op_pll->pix_clk_div;
 
-	if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) {
+	if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) {
 		/* No OP clocks --- VT clocks are used instead. */
 		goto out_skip_vt_calc;
 	}
@@ -383,12 +383,11 @@ static int __smiapp_pll_calculate(
 	return check_all_bounds(dev, limits, op_limits, pll, op_pll);
 }
 
-int smiapp_pll_calculate(struct device *dev,
-			 const struct smiapp_pll_limits *limits,
-			 struct smiapp_pll *pll)
+int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *limits,
+		      struct ccs_pll *pll)
 {
-	const struct smiapp_pll_branch_limits *op_limits = &limits->op;
-	struct smiapp_pll_branch *op_pll = &pll->op;
+	const struct ccs_pll_branch_limits *op_limits = &limits->op;
+	struct ccs_pll_branch *op_pll = &pll->op;
 	uint16_t min_pre_pll_clk_div;
 	uint16_t max_pre_pll_clk_div;
 	uint32_t lane_op_clock_ratio;
@@ -396,7 +395,7 @@ int smiapp_pll_calculate(struct device *dev,
 	unsigned int i;
 	int rval = -EINVAL;
 
-	if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) {
+	if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) {
 		/*
 		 * If there's no OP PLL at all, use the VT values
 		 * instead. The OP values are ignored for the rest of
@@ -406,7 +405,7 @@ int smiapp_pll_calculate(struct device *dev,
 		op_pll = &pll->vt;
 	}
 
-	if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE)
+	if (pll->flags & CCS_PLL_FLAG_OP_PIX_CLOCK_PER_LANE)
 		lane_op_clock_ratio = pll->csi2.lanes;
 	else
 		lane_op_clock_ratio = 1;
@@ -416,12 +415,12 @@ int smiapp_pll_calculate(struct device *dev,
 		pll->binning_vertical);
 
 	switch (pll->bus_type) {
-	case SMIAPP_PLL_BUS_TYPE_CSI2:
+	case CCS_PLL_BUS_TYPE_CSI2:
 		/* CSI transfers 2 bits per clock per lane; thus times 2 */
 		pll->pll_op_clk_freq_hz = pll->link_freq * 2
 			* (pll->csi2.lanes / lane_op_clock_ratio);
 		break;
-	case SMIAPP_PLL_BUS_TYPE_PARALLEL:
+	case CCS_PLL_BUS_TYPE_PARALLEL:
 		pll->pll_op_clk_freq_hz = pll->link_freq * pll->bits_per_pixel
 			/ DIV_ROUND_UP(pll->bits_per_pixel,
 				       pll->parallel.bus_width);
@@ -461,9 +460,8 @@ int smiapp_pll_calculate(struct device *dev,
 	for (pll->pre_pll_clk_div = min_pre_pll_clk_div;
 	     pll->pre_pll_clk_div <= max_pre_pll_clk_div;
 	     pll->pre_pll_clk_div += 2 - (pll->pre_pll_clk_div & 1)) {
-		rval = __smiapp_pll_calculate(dev, limits, op_limits, pll,
-					      op_pll, mul, div,
-					      lane_op_clock_ratio);
+		rval = __ccs_pll_calculate(dev, limits, op_limits, pll, op_pll,
+					   mul, div, lane_op_clock_ratio);
 		if (rval)
 			continue;
 
@@ -475,8 +473,8 @@ int smiapp_pll_calculate(struct device *dev,
 
 	return rval;
 }
-EXPORT_SYMBOL_GPL(smiapp_pll_calculate);
+EXPORT_SYMBOL_GPL(ccs_pll_calculate);
 
 MODULE_AUTHOR("Sakari Ailus <sakari.ailus@iki.fi>");
-MODULE_DESCRIPTION("Generic SMIA/SMIA++ PLL calculator");
+MODULE_DESCRIPTION("Generic MIPI CCS/SMIA/SMIA++ PLL calculator");
 MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/smiapp-pll.h b/drivers/media/i2c/ccs-pll.h
similarity index 68%
rename from drivers/media/i2c/smiapp-pll.h
rename to drivers/media/i2c/ccs-pll.h
index bd6902f54539..88d641ee3fa1 100644
--- a/drivers/media/i2c/smiapp-pll.h
+++ b/drivers/media/i2c/ccs-pll.h
@@ -1,32 +1,33 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * drivers/media/i2c/smiapp-pll.h
+ * drivers/media/i2c/ccs-pll.h
  *
- * Generic driver for SMIA/SMIA++ compliant camera modules
+ * Generic MIPI CCS/SMIA/SMIA++ PLL calculator
  *
+ * Copyright (C) 2020 Intel Corporation
  * Copyright (C) 2012 Nokia Corporation
  * Contact: Sakari Ailus <sakari.ailus@iki.fi>
  */
 
-#ifndef SMIAPP_PLL_H
-#define SMIAPP_PLL_H
+#ifndef CCS_PLL_H
+#define CCS_PLL_H
 
 /* CSI-2 or CCP-2 */
-#define SMIAPP_PLL_BUS_TYPE_CSI2				0x00
-#define SMIAPP_PLL_BUS_TYPE_PARALLEL				0x01
+#define CCS_PLL_BUS_TYPE_CSI2				0x00
+#define CCS_PLL_BUS_TYPE_PARALLEL				0x01
 
 /* op pix clock is for all lanes in total normally */
-#define SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE			(1 << 0)
-#define SMIAPP_PLL_FLAG_NO_OP_CLOCKS				(1 << 1)
+#define CCS_PLL_FLAG_OP_PIX_CLOCK_PER_LANE			(1 << 0)
+#define CCS_PLL_FLAG_NO_OP_CLOCKS				(1 << 1)
 
-struct smiapp_pll_branch {
+struct ccs_pll_branch {
 	uint16_t sys_clk_div;
 	uint16_t pix_clk_div;
 	uint32_t sys_clk_freq_hz;
 	uint32_t pix_clk_freq_hz;
 };
 
-struct smiapp_pll {
+struct ccs_pll {
 	/* input values */
 	uint8_t bus_type;
 	union {
@@ -51,14 +52,14 @@ struct smiapp_pll {
 	uint16_t pll_multiplier;
 	uint32_t pll_ip_clk_freq_hz;
 	uint32_t pll_op_clk_freq_hz;
-	struct smiapp_pll_branch vt;
-	struct smiapp_pll_branch op;
+	struct ccs_pll_branch vt;
+	struct ccs_pll_branch op;
 
 	uint32_t pixel_rate_csi;
 	uint32_t pixel_rate_pixel_array;
 };
 
-struct smiapp_pll_branch_limits {
+struct ccs_pll_branch_limits {
 	uint16_t min_sys_clk_div;
 	uint16_t max_sys_clk_div;
 	uint32_t min_sys_clk_freq_hz;
@@ -69,7 +70,7 @@ struct smiapp_pll_branch_limits {
 	uint32_t max_pix_clk_freq_hz;
 };
 
-struct smiapp_pll_limits {
+struct ccs_pll_limits {
 	/* Strict PLL limits */
 	uint32_t min_ext_clk_freq_hz;
 	uint32_t max_ext_clk_freq_hz;
@@ -82,8 +83,8 @@ struct smiapp_pll_limits {
 	uint32_t min_pll_op_freq_hz;
 	uint32_t max_pll_op_freq_hz;
 
-	struct smiapp_pll_branch_limits vt;
-	struct smiapp_pll_branch_limits op;
+	struct ccs_pll_branch_limits vt;
+	struct ccs_pll_branch_limits op;
 
 	/* Other relevant limits */
 	uint32_t min_line_length_pck_bin;
@@ -92,8 +93,7 @@ struct smiapp_pll_limits {
 
 struct device;
 
-int smiapp_pll_calculate(struct device *dev,
-			 const struct smiapp_pll_limits *limits,
-			 struct smiapp_pll *pll);
+int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *limits,
+		      struct ccs_pll *pll);
 
-#endif /* SMIAPP_PLL_H */
+#endif /* CCS_PLL_H */
diff --git a/drivers/media/i2c/ccs/Kconfig b/drivers/media/i2c/ccs/Kconfig
index b4f8b10da420..59f35b33ddc1 100644
--- a/drivers/media/i2c/ccs/Kconfig
+++ b/drivers/media/i2c/ccs/Kconfig
@@ -4,7 +4,7 @@ config VIDEO_CCS
 	depends on I2C && VIDEO_V4L2 && HAVE_CLK
 	select MEDIA_CONTROLLER
 	select VIDEO_V4L2_SUBDEV_API
-	select VIDEO_SMIAPP_PLL
+	select VIDEO_CCS_PLL
 	select V4L2_FWNODE
 	help
 	  This is a generic driver for MIPI CCS, SMIA++ and SMIA compliant
diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 074b246538d2..6c8528e6ac96 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -363,7 +363,7 @@ static int ccs_read_frame_fmt(struct ccs_sensor *sensor)
 
 static int ccs_pll_configure(struct ccs_sensor *sensor)
 {
-	struct smiapp_pll *pll = &sensor->pll;
+	struct ccs_pll *pll = &sensor->pll;
 	int rval;
 
 	rval = ccs_write(sensor, VT_PIX_CLK_DIV, pll->vt.pix_clk_div);
@@ -386,7 +386,7 @@ static int ccs_pll_configure(struct ccs_sensor *sensor)
 	rval = ccs_write(sensor, REQUESTED_LINK_RATE,
 			 DIV_ROUND_UP(pll->op.sys_clk_freq_hz,
 				      1000000 / 256 / 256));
-	if (rval < 0 || sensor->pll.flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS)
+	if (rval < 0 || sensor->pll.flags & CCS_PLL_FLAG_NO_OP_CLOCKS)
 		return rval;
 
 	rval = ccs_write(sensor, OP_PIX_CLK_DIV, pll->op.pix_clk_div);
@@ -396,10 +396,10 @@ static int ccs_pll_configure(struct ccs_sensor *sensor)
 	return ccs_write(sensor, OP_SYS_CLK_DIV, pll->op.sys_clk_div);
 }
 
-static int ccs_pll_try(struct ccs_sensor *sensor, struct smiapp_pll *pll)
+static int ccs_pll_try(struct ccs_sensor *sensor, struct ccs_pll *pll)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
-	struct smiapp_pll_limits lim = {
+	struct ccs_pll_limits lim = {
 		.min_pre_pll_clk_div = CCS_LIM(sensor, MIN_PRE_PLL_CLK_DIV),
 		.max_pre_pll_clk_div = CCS_LIM(sensor, MAX_PRE_PLL_CLK_DIV),
 		.min_pll_ip_freq_hz = CCS_LIM(sensor, MIN_PLL_IP_CLK_FREQ_MHZ),
@@ -431,12 +431,12 @@ static int ccs_pll_try(struct ccs_sensor *sensor, struct smiapp_pll *pll)
 		.min_line_length_pck = CCS_LIM(sensor, MIN_LINE_LENGTH_PCK),
 	};
 
-	return smiapp_pll_calculate(&client->dev, &lim, pll);
+	return ccs_pll_calculate(&client->dev, &lim, pll);
 }
 
 static int ccs_pll_update(struct ccs_sensor *sensor)
 {
-	struct smiapp_pll *pll = &sensor->pll;
+	struct ccs_pll *pll = &sensor->pll;
 	int rval;
 
 	pll->binning_horizontal = sensor->binning_horizontal;
@@ -829,7 +829,7 @@ static void ccs_free_controls(struct ccs_sensor *sensor)
 static int ccs_get_mbus_formats(struct ccs_sensor *sensor)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
-	struct smiapp_pll *pll = &sensor->pll;
+	struct ccs_pll *pll = &sensor->pll;
 	u8 compressed_max_bpp = 0;
 	unsigned int type, n;
 	unsigned int i, pixel_order;
@@ -3155,7 +3155,7 @@ static int ccs_probe(struct i2c_client *client)
 	    !CCS_LIM(sensor, MIN_OP_PIX_CLK_DIV) ||
 	    !CCS_LIM(sensor, MAX_OP_PIX_CLK_DIV)) {
 		/* No OP clock branch */
-		sensor->pll.flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS;
+		sensor->pll.flags |= CCS_PLL_FLAG_NO_OP_CLOCKS;
 	} else if (CCS_LIM(sensor, SCALING_CAPABILITY)
 		   != CCS_SCALING_CAPABILITY_NONE ||
 		   CCS_LIM(sensor, DIGITAL_CROP_CAPABILITY)
@@ -3172,7 +3172,7 @@ static int ccs_probe(struct i2c_client *client)
 	sensor->scale_m = CCS_LIM(sensor, SCALER_N_MIN);
 
 	/* prepare PLL configuration input values */
-	sensor->pll.bus_type = SMIAPP_PLL_BUS_TYPE_CSI2;
+	sensor->pll.bus_type = CCS_PLL_BUS_TYPE_CSI2;
 	sensor->pll.csi2.lanes = sensor->hwcfg.lanes;
 	sensor->pll.ext_clk_freq_hz = sensor->hwcfg.ext_clk;
 	sensor->pll.scale_n = CCS_LIM(sensor, SCALER_N_MIN);
diff --git a/drivers/media/i2c/ccs/ccs-quirk.c b/drivers/media/i2c/ccs/ccs-quirk.c
index 07c5733b4244..8b4fa60044b2 100644
--- a/drivers/media/i2c/ccs/ccs-quirk.c
+++ b/drivers/media/i2c/ccs/ccs-quirk.c
@@ -190,7 +190,7 @@ static int jt8ev1_post_streamoff(struct ccs_sensor *sensor)
 
 static int jt8ev1_init(struct ccs_sensor *sensor)
 {
-	sensor->pll.flags |= SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE;
+	sensor->pll.flags |= CCS_PLL_FLAG_OP_PIX_CLOCK_PER_LANE;
 
 	return 0;
 }
diff --git a/drivers/media/i2c/ccs/ccs.h b/drivers/media/i2c/ccs/ccs.h
index f60d1801c469..c8a9f4ee093e 100644
--- a/drivers/media/i2c/ccs/ccs.h
+++ b/drivers/media/i2c/ccs/ccs.h
@@ -21,7 +21,7 @@
 #include "ccs-quirk.h"
 #include "ccs-regs.h"
 #include "ccs-reg-access.h"
-#include "../smiapp-pll.h"
+#include "../ccs-pll.h"
 #include "smiapp-reg-defs.h"
 
 /*
@@ -256,7 +256,7 @@ struct ccs_sensor {
 
 	struct ccs_module_info minfo;
 
-	struct smiapp_pll pll;
+	struct ccs_pll pll;
 
 	/* Is a default format supported for a given BPP? */
 	unsigned long *valid_link_freqs;
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 19/30] ccs-pll: Fix MODULE_LICENSE
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (17 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 18/30] smiapp-pll: Rename as ccs-pll Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 20/30] ccs: Change my e-mail address Sakari Ailus
                   ` (10 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

Change MODULE_LICENSE to "GPL v2" as indicated by the SPDX tag.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index d2f0f7375f5c..58f5fe7062ae 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -477,4 +477,4 @@ EXPORT_SYMBOL_GPL(ccs_pll_calculate);
 
 MODULE_AUTHOR("Sakari Ailus <sakari.ailus@iki.fi>");
 MODULE_DESCRIPTION("Generic MIPI CCS/SMIA/SMIA++ PLL calculator");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 20/30] ccs: Change my e-mail address
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (18 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 19/30] ccs-pll: Fix MODULE_LICENSE Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 21/30] ccs: Allow range in between I²C retries Sakari Ailus
                   ` (9 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

Use my @linux.intel.com e-mail address in the CCS driver.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c            | 4 ++--
 drivers/media/i2c/ccs-pll.h            | 2 +-
 drivers/media/i2c/ccs/ccs-core.c       | 6 +++---
 drivers/media/i2c/ccs/ccs-quirk.c      | 2 +-
 drivers/media/i2c/ccs/ccs-quirk.h      | 2 +-
 drivers/media/i2c/ccs/ccs-reg-access.c | 2 +-
 drivers/media/i2c/ccs/ccs-reg-access.h | 2 +-
 drivers/media/i2c/ccs/ccs.h            | 2 +-
 8 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index 58f5fe7062ae..0d57bac1599a 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -6,7 +6,7 @@
  *
  * Copyright (C) 2020 Intel Corporation
  * Copyright (C) 2011--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@iki.fi>
+ * Contact: Sakari Ailus <sakari.ailus@linux.intel.com>
  */
 
 #include <linux/device.h>
@@ -475,6 +475,6 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *limits,
 }
 EXPORT_SYMBOL_GPL(ccs_pll_calculate);
 
-MODULE_AUTHOR("Sakari Ailus <sakari.ailus@iki.fi>");
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
 MODULE_DESCRIPTION("Generic MIPI CCS/SMIA/SMIA++ PLL calculator");
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/ccs-pll.h b/drivers/media/i2c/ccs-pll.h
index 88d641ee3fa1..07f7f9e8a1cc 100644
--- a/drivers/media/i2c/ccs-pll.h
+++ b/drivers/media/i2c/ccs-pll.h
@@ -6,7 +6,7 @@
  *
  * Copyright (C) 2020 Intel Corporation
  * Copyright (C) 2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@iki.fi>
+ * Contact: Sakari Ailus <sakari.ailus@linux.intel.com>
  */
 
 #ifndef CCS_PLL_H
diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 6c8528e6ac96..c53911b1c78b 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -6,7 +6,7 @@
  *
  * Copyright (C) 2020 Intel Corporation
  * Copyright (C) 2010--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@iki.fi>
+ * Contact: Sakari Ailus <sakari.ailus@linux.intel.com>
  *
  * Based on smiapp driver by Vimarsh Zutshi
  * Based on jt8ev1.c by Vimarsh Zutshi
@@ -1168,7 +1168,7 @@ static int ccs_setup_flash_strobe(struct ccs_sensor *sensor)
 	 * do not change, or if you do at least know what you're
 	 * doing. :-)
 	 *
-	 * Sakari Ailus <sakari.ailus@iki.fi> 2010-10-25
+	 * Sakari Ailus <sakari.ailus@linux.intel.com> 2010-10-25
 	 *
 	 * flash_strobe_length [us] / 10^6 = (tFlash_strobe_width_ctrl
 	 *	/ EXTCLK freq [Hz]) * flash_strobe_adjustment
@@ -3357,7 +3357,7 @@ static void ccs_module_cleanup(void)
 module_init(ccs_module_init);
 module_exit(ccs_module_cleanup);
 
-MODULE_AUTHOR("Sakari Ailus <sakari.ailus@iki.fi>");
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
 MODULE_DESCRIPTION("Generic MIPI CCS/SMIA/SMIA++ camera sensor driver");
 MODULE_LICENSE("GPL v2");
 MODULE_ALIAS("smiapp");
diff --git a/drivers/media/i2c/ccs/ccs-quirk.c b/drivers/media/i2c/ccs/ccs-quirk.c
index 8b4fa60044b2..4fe8c6f70579 100644
--- a/drivers/media/i2c/ccs/ccs-quirk.c
+++ b/drivers/media/i2c/ccs/ccs-quirk.c
@@ -6,7 +6,7 @@
  *
  * Copyright (C) 2020 Intel Corporation
  * Copyright (C) 2011--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@iki.fi>
+ * Contact: Sakari Ailus <sakari.ailus@linux.intel.com>
  */
 
 #include <linux/delay.h>
diff --git a/drivers/media/i2c/ccs/ccs-quirk.h b/drivers/media/i2c/ccs/ccs-quirk.h
index 3e7779e2fc4b..6b4ec4beaba0 100644
--- a/drivers/media/i2c/ccs/ccs-quirk.h
+++ b/drivers/media/i2c/ccs/ccs-quirk.h
@@ -6,7 +6,7 @@
  *
  * Copyright (C) 2020 Intel Corporation
  * Copyright (C) 2011--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@iki.fi>
+ * Contact: Sakari Ailus <sakari.ailus@linux.intel.com>
  */
 
 #ifndef __CCS_QUIRK__
diff --git a/drivers/media/i2c/ccs/ccs-reg-access.c b/drivers/media/i2c/ccs/ccs-reg-access.c
index 91ccbca11577..aad2727570ec 100644
--- a/drivers/media/i2c/ccs/ccs-reg-access.c
+++ b/drivers/media/i2c/ccs/ccs-reg-access.c
@@ -6,7 +6,7 @@
  *
  * Copyright (C) 2020 Intel Corporation
  * Copyright (C) 2011--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@iki.fi>
+ * Contact: Sakari Ailus <sakari.ailus@linux.intel.com>
  */
 
 #include <asm/unaligned.h>
diff --git a/drivers/media/i2c/ccs/ccs-reg-access.h b/drivers/media/i2c/ccs/ccs-reg-access.h
index 5f6ff9c57698..cfad2e520fe2 100644
--- a/drivers/media/i2c/ccs/ccs-reg-access.h
+++ b/drivers/media/i2c/ccs/ccs-reg-access.h
@@ -6,7 +6,7 @@
  *
  * Copyright (C) 2020 Intel Corporation
  * Copyright (C) 2011--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@iki.fi>
+ * Contact: Sakari Ailus <sakari.ailus@linux.intel.com>
  */
 
 #ifndef SMIAPP_REGS_H
diff --git a/drivers/media/i2c/ccs/ccs.h b/drivers/media/i2c/ccs/ccs.h
index c8a9f4ee093e..6b07e4143ff0 100644
--- a/drivers/media/i2c/ccs/ccs.h
+++ b/drivers/media/i2c/ccs/ccs.h
@@ -6,7 +6,7 @@
  *
  * Copyright (C) 2020 Intel Corporation
  * Copyright (C) 2010--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@iki.fi>
+ * Contact: Sakari Ailus <sakari.ailus@linux.intel.com>
  */
 
 #ifndef __CCS_H__
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 21/30] ccs: Allow range in between I²C retries
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (19 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 20/30] ccs: Change my e-mail address Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 22/30] ccs: Add support for manufacturer regs from sensor and module files Sakari Ailus
                   ` (8 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

Make the delay between I²C access retries a range between 1 and 2 ms. Also
make the number of retries 10 instead of 5, in order not to reduce the
total amount of time.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-reg-access.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/media/i2c/ccs/ccs-reg-access.c b/drivers/media/i2c/ccs/ccs-reg-access.c
index aad2727570ec..79efed5e0dad 100644
--- a/drivers/media/i2c/ccs/ccs-reg-access.c
+++ b/drivers/media/i2c/ccs/ccs-reg-access.c
@@ -256,7 +256,7 @@ int ccs_write_addr_no_quirk(struct ccs_sensor *sensor, u32 reg, u32 val)
 	put_unaligned_be16(CCS_REG_ADDR(reg), data);
 	put_unaligned_be32(val << (8 * (sizeof(val) - len)), data + 2);
 
-	for (retries = 0; retries < 5; retries++) {
+	for (retries = 0; retries < 10; retries++) {
 		/*
 		 * Due to unknown reason sensor stops responding. This
 		 * loop is a temporaty solution until the root cause
@@ -271,7 +271,7 @@ int ccs_write_addr_no_quirk(struct ccs_sensor *sensor, u32 reg, u32 val)
 			return 0;
 		}
 
-		usleep_range(2000, 2000);
+		usleep_range(1000, 2000);
 	}
 
 	dev_err(&client->dev,
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 22/30] ccs: Add support for manufacturer regs from sensor and module files
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (20 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 21/30] ccs: Allow range in between I²C retries Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 23/30] ccs: Use static data read-only registers Sakari Ailus
                   ` (7 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

Write manufacturer specific registers (MSRs) from file to the sensor on
sensor power-on.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-core.c       | 23 +++++++
 drivers/media/i2c/ccs/ccs-reg-access.c | 94 ++++++++++++++++++++------
 drivers/media/i2c/ccs/ccs-reg-access.h |  2 +
 3 files changed, 97 insertions(+), 22 deletions(-)

diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index c53911b1c78b..5e01f22608d7 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -1278,6 +1278,21 @@ static int ccs_setup_flash_strobe(struct ccs_sensor *sensor)
  * Power management
  */
 
+static int ccs_write_msr_regs(struct ccs_sensor *sensor)
+{
+	int rval;
+
+	rval = ccs_write_data_regs(sensor,
+				   sensor->sdata.sensor_manufacturer_regs,
+				   sensor->sdata.num_sensor_manufacturer_regs);
+	if (rval)
+		return rval;
+
+	return ccs_write_data_regs(sensor,
+				   sensor->mdata.module_manufacturer_regs,
+				   sensor->mdata.num_module_manufacturer_regs);
+}
+
 static int ccs_power_on(struct device *dev)
 {
 	struct v4l2_subdev *subdev = dev_get_drvdata(dev);
@@ -1383,6 +1398,10 @@ static int ccs_power_on(struct device *dev)
 	if (rval < 0)
 		goto out_cci_addr_fail;
 
+	rval = ccs_write_msr_regs(sensor);
+	if (rval)
+		goto out_cci_addr_fail;
+
 	rval = ccs_call_quirk(sensor, post_poweron);
 	if (rval) {
 		dev_err(dev, "post_poweron quirks failed\n");
@@ -3220,6 +3239,10 @@ static int ccs_probe(struct i2c_client *client)
 	if (rval < 0)
 		goto out_media_entity_cleanup;
 
+	rval = ccs_write_msr_regs(sensor);
+	if (rval)
+		goto out_media_entity_cleanup;
+
 	pm_runtime_set_active(&client->dev);
 	pm_runtime_get_noresume(&client->dev);
 	pm_runtime_enable(&client->dev);
diff --git a/drivers/media/i2c/ccs/ccs-reg-access.c b/drivers/media/i2c/ccs/ccs-reg-access.c
index 79efed5e0dad..918bc98c226f 100644
--- a/drivers/media/i2c/ccs/ccs-reg-access.c
+++ b/drivers/media/i2c/ccs/ccs-reg-access.c
@@ -236,12 +236,38 @@ int ccs_read_addr_noconv(struct ccs_sensor *sensor, u32 reg, u32 *val)
 	return ccs_read_addr_raw(sensor, reg, val, false, true, false);
 }
 
+static int ccs_write_retry(struct i2c_client *client, struct i2c_msg *msg)
+{
+	unsigned int retries;
+	int r;
+
+	for (retries = 0; retries < 10; retries++) {
+		/*
+		 * Due to unknown reason sensor stops responding. This
+		 * loop is a temporaty solution until the root cause
+		 * is found.
+		 */
+		r = i2c_transfer(client->adapter, msg, 1);
+		if (r != 1) {
+			usleep_range(1000, 2000);
+			continue;
+		}
+
+		if (retries)
+			dev_err(&client->dev,
+				"sensor i2c stall encountered. retries: %d\n",
+				retries);
+		return 0;
+	}
+
+	return r;
+}
+
 int ccs_write_addr_no_quirk(struct ccs_sensor *sensor, u32 reg, u32 val)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
 	struct i2c_msg msg;
 	unsigned char data[6];
-	unsigned int retries;
 	unsigned int len = ccs_reg_width(reg);
 	int r;
 
@@ -256,27 +282,11 @@ int ccs_write_addr_no_quirk(struct ccs_sensor *sensor, u32 reg, u32 val)
 	put_unaligned_be16(CCS_REG_ADDR(reg), data);
 	put_unaligned_be32(val << (8 * (sizeof(val) - len)), data + 2);
 
-	for (retries = 0; retries < 10; retries++) {
-		/*
-		 * Due to unknown reason sensor stops responding. This
-		 * loop is a temporaty solution until the root cause
-		 * is found.
-		 */
-		r = i2c_transfer(client->adapter, &msg, 1);
-		if (r == 1) {
-			if (retries)
-				dev_err(&client->dev,
-					"sensor i2c stall encountered. retries: %d\n",
-					retries);
-			return 0;
-		}
-
-		usleep_range(1000, 2000);
-	}
-
-	dev_err(&client->dev,
-		"wrote 0x%x to offset 0x%x error %d\n", val,
-		CCS_REG_ADDR(reg), r);
+	r = ccs_write_retry(client, &msg);
+	if (r)
+		dev_err(&client->dev,
+			"wrote 0x%x to offset 0x%x error %d\n", val,
+			CCS_REG_ADDR(reg), r);
 
 	return r;
 }
@@ -297,3 +307,43 @@ int ccs_write_addr(struct ccs_sensor *sensor, u32 reg, u32 val)
 
 	return ccs_write_addr_no_quirk(sensor, reg, val);
 }
+
+#define MAX_WRITE_LEN	32U
+
+int ccs_write_data_regs(struct ccs_sensor *sensor, struct ccs_reg *regs,
+			size_t num_regs)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+	unsigned char buf[2 + MAX_WRITE_LEN];
+	struct i2c_msg msg = {
+		.addr = client->addr,
+		.buf = buf,
+	};
+	size_t i;
+
+	for (i = 0; i < num_regs; i++, regs++) {
+		unsigned char *regdata = regs->value;
+		unsigned int j;
+
+		for (j = 0; j < regs->len;
+		     j += msg.len - 2, regdata += msg.len - 2) {
+			int rval;
+
+			msg.len = min(regs->len - j, MAX_WRITE_LEN);
+
+			put_unaligned_be16(regs->addr + j, buf);
+			memcpy(buf + 2, regdata, msg.len);
+			msg.len += 2;
+
+			rval = ccs_write_retry(client, &msg);
+			if (rval) {
+				dev_err(&client->dev,
+					"error writing %u octets to address 0x%4.4x\n",
+					msg.len, regs->addr + j);
+				return rval;
+			}
+		}
+	}
+
+	return 0;
+}
diff --git a/drivers/media/i2c/ccs/ccs-reg-access.h b/drivers/media/i2c/ccs/ccs-reg-access.h
index cfad2e520fe2..78c43f92d99a 100644
--- a/drivers/media/i2c/ccs/ccs-reg-access.h
+++ b/drivers/media/i2c/ccs/ccs-reg-access.h
@@ -27,6 +27,8 @@ int ccs_read_addr_8only(struct ccs_sensor *sensor, u32 reg, u32 *val);
 int ccs_read_addr_noconv(struct ccs_sensor *sensor, u32 reg, u32 *val);
 int ccs_write_addr_no_quirk(struct ccs_sensor *sensor, u32 reg, u32 val);
 int ccs_write_addr(struct ccs_sensor *sensor, u32 reg, u32 val);
+int ccs_write_data_regs(struct ccs_sensor *sensor, struct ccs_reg *regs,
+			size_t num_regs);
 
 unsigned int ccs_reg_width(u32 reg);
 u32 ccs_reg_conv(struct ccs_sensor *sensor, u32 reg, u32 val);
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 23/30] ccs: Use static data read-only registers
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (21 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 22/30] ccs: Add support for manufacturer regs from sensor and module files Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 24/30] ccs: Clean up runtime PM usage Sakari Ailus
                   ` (6 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

Access read-only registers from CCS static data.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-reg-access.c | 64 ++++++++++++++++++++++++--
 1 file changed, 60 insertions(+), 4 deletions(-)

diff --git a/drivers/media/i2c/ccs/ccs-reg-access.c b/drivers/media/i2c/ccs/ccs-reg-access.c
index 918bc98c226f..3de863e3bf26 100644
--- a/drivers/media/i2c/ccs/ccs-reg-access.c
+++ b/drivers/media/i2c/ccs/ccs-reg-access.c
@@ -198,11 +198,67 @@ static int __ccs_read_addr(struct ccs_sensor *sensor, u32 reg, u32 *val,
 	return 0;
 }
 
+static int __ccs_read_data(struct ccs_reg *regs, size_t num_regs,
+			   u32 reg, u32 *val)
+{
+	unsigned int width = ccs_reg_width(reg);
+	size_t i;
+
+	for (i = 0; i < num_regs; i++, regs++) {
+		uint8_t *data;
+
+		if (regs->addr + regs->len < CCS_REG_ADDR(reg) + width)
+			continue;
+
+		if (regs->addr > CCS_REG_ADDR(reg))
+			break;
+
+		data = &regs->value[CCS_REG_ADDR(reg) - regs->addr];
+
+		switch (width) {
+		case sizeof(uint8_t):
+			*val = *data;
+			break;
+		case sizeof(uint16_t):
+			*val = get_unaligned_be16(data);
+			break;
+		case sizeof(uint32_t):
+			*val = get_unaligned_be32(data);
+			break;
+		default:
+			WARN_ON(1);
+			return -EINVAL;
+		}
+
+		return 0;
+	}
+
+	return -ENOENT;
+}
+
+static int ccs_read_data(struct ccs_sensor *sensor, u32 reg, u32 *val)
+{
+	if (!__ccs_read_data(sensor->sdata.sensor_read_only_regs,
+			     sensor->sdata.num_sensor_read_only_regs,
+			     reg, val))
+		return 0;
+
+	return __ccs_read_data(sensor->mdata.module_read_only_regs,
+			       sensor->mdata.num_module_read_only_regs,
+			       reg, val);
+}
+
 static int ccs_read_addr_raw(struct ccs_sensor *sensor, u32 reg, u32 *val,
-			     bool force8, bool quirk, bool conv)
+			     bool force8, bool quirk, bool conv, bool data)
 {
 	int rval;
 
+	if (data) {
+		rval = ccs_read_data(sensor, reg, val);
+		if (!rval)
+			return 0;
+	}
+
 	if (quirk) {
 		*val = 0;
 		rval = ccs_call_quirk(sensor, reg_access, false, &reg, val);
@@ -223,17 +279,17 @@ static int ccs_read_addr_raw(struct ccs_sensor *sensor, u32 reg, u32 *val,
 
 int ccs_read_addr(struct ccs_sensor *sensor, u32 reg, u32 *val)
 {
-	return ccs_read_addr_raw(sensor, reg, val, false, true, true);
+	return ccs_read_addr_raw(sensor, reg, val, false, true, true, true);
 }
 
 int ccs_read_addr_8only(struct ccs_sensor *sensor, u32 reg, u32 *val)
 {
-	return ccs_read_addr_raw(sensor, reg, val, true, true, true);
+	return ccs_read_addr_raw(sensor, reg, val, true, true, true, true);
 }
 
 int ccs_read_addr_noconv(struct ccs_sensor *sensor, u32 reg, u32 *val)
 {
-	return ccs_read_addr_raw(sensor, reg, val, false, true, false);
+	return ccs_read_addr_raw(sensor, reg, val, false, true, false, true);
 }
 
 static int ccs_write_retry(struct i2c_client *client, struct i2c_msg *msg)
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 24/30] ccs: Clean up runtime PM usage
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (22 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 23/30] ccs: Use static data read-only registers Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 25/30] ccs: Wrap long lines, unwrap short ones Sakari Ailus
                   ` (5 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

If pm_runtime_get_sync() fails, there's no need to set the device active
again. Also, in the same case to return the usage_count to zero,
pm_runtime_put_noidle() is enough.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-core.c | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 5e01f22608d7..c3023570a620 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -1625,8 +1625,6 @@ static int ccs_pm_get_init(struct ccs_sensor *sensor)
 
 	rval = pm_runtime_get_sync(&client->dev);
 	if (rval < 0) {
-		if (rval != -EBUSY && rval != -EAGAIN)
-			pm_runtime_set_active(&client->dev);
 		pm_runtime_put_noidle(&client->dev);
 
 		return rval;
@@ -2842,9 +2840,8 @@ static int __maybe_unused ccs_suspend(struct device *dev)
 
 	rval = pm_runtime_get_sync(dev);
 	if (rval < 0) {
-		if (rval != -EBUSY && rval != -EAGAIN)
-			pm_runtime_set_active(&client->dev);
-		pm_runtime_put(dev);
+		pm_runtime_put_noidle(dev);
+
 		return -EAGAIN;
 	}
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 25/30] ccs: Wrap long lines, unwrap short ones
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (23 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 24/30] ccs: Clean up runtime PM usage Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 26/30] ccs: Use longer pre-I²C sleep for CCS compliant devices Sakari Ailus
                   ` (4 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

Over the years (and renaming) some lines that may well be wrapped ended up
being over 80 characters, likewise there are shorter lines that can be
merged. Do that.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-core.c | 45 +++++++++++++-------------------
 1 file changed, 18 insertions(+), 27 deletions(-)

diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index c3023570a620..863295b8fb5c 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -657,8 +657,7 @@ static int ccs_set_ctrl(struct v4l2_ctrl *ctrl)
 		break;
 	case V4L2_CID_HBLANK:
 		rval = ccs_write(sensor, LINE_LENGTH_PCK,
-				 sensor->pixel_array->crop[
-					 CCS_PA_PAD_SRC].width
+				 sensor->pixel_array->crop[CCS_PA_PAD_SRC].width
 				 + ctrl->val);
 
 		break;
@@ -989,15 +988,13 @@ static void ccs_update_blanking(struct ccs_sensor *sensor)
 
 	min = max_t(int,
 		    CCS_LIM(sensor, MIN_FRAME_BLANKING_LINES),
-		    min_fll -
-		    sensor->pixel_array->crop[CCS_PA_PAD_SRC].height);
+		    min_fll - sensor->pixel_array->crop[CCS_PA_PAD_SRC].height);
 	max = max_fll -	sensor->pixel_array->crop[CCS_PA_PAD_SRC].height;
 
 	__v4l2_ctrl_modify_range(vblank, min, max, vblank->step, min);
 
 	min = max_t(int,
-		    min_llp -
-		    sensor->pixel_array->crop[CCS_PA_PAD_SRC].width,
+		    min_llp - sensor->pixel_array->crop[CCS_PA_PAD_SRC].width,
 		    min_lbp);
 	max = max_llp - sensor->pixel_array->crop[CCS_PA_PAD_SRC].width;
 
@@ -1784,7 +1781,8 @@ static void ccs_get_crop_compose(struct v4l2_subdev *subdev,
 	} else {
 		if (crops) {
 			for (i = 0; i < subdev->entity.num_pads; i++)
-				crops[i] = v4l2_subdev_get_try_crop(subdev, cfg, i);
+				crops[i] = v4l2_subdev_get_try_crop(subdev,
+								    cfg, i);
 		}
 		if (comps)
 			*comps = v4l2_subdev_get_try_compose(subdev, cfg,
@@ -1809,8 +1807,7 @@ static void ccs_propagate(struct v4l2_subdev *subdev,
 		comp->height = crops[CCS_PAD_SINK]->height;
 		if (which == V4L2_SUBDEV_FORMAT_ACTIVE) {
 			if (ssd == sensor->scaler) {
-				sensor->scale_m =
-					CCS_LIM(sensor, SCALER_N_MIN);
+				sensor->scale_m = CCS_LIM(sensor, SCALER_N_MIN);
 				sensor->scaling_mode =
 					CCS_SCALING_MODE_NO_SCALING;
 			} else if (ssd == sensor->binner) {
@@ -2236,9 +2233,11 @@ static int ccs_set_crop(struct v4l2_subdev *subdev,
 		if (sel->pad == ssd->sink_pad) {
 			_r.left = 0;
 			_r.top = 0;
-			_r.width = v4l2_subdev_get_try_format(subdev, cfg, sel->pad)
+			_r.width = v4l2_subdev_get_try_format(subdev, cfg,
+							      sel->pad)
 				->width;
-			_r.height = v4l2_subdev_get_try_format(subdev, cfg, sel->pad)
+			_r.height = v4l2_subdev_get_try_format(subdev, cfg,
+							       sel->pad)
 				->height;
 			src_size = &_r;
 		} else {
@@ -2356,11 +2355,9 @@ static int ccs_set_selection(struct v4l2_subdev *subdev,
 	sel->r.width = CCS_ALIGN_DIM(sel->r.width, sel->flags);
 	sel->r.height =	CCS_ALIGN_DIM(sel->r.height, sel->flags);
 
-	sel->r.width = max_t(unsigned int,
-			     CCS_LIM(sensor, MIN_X_OUTPUT_SIZE),
+	sel->r.width = max_t(unsigned int, CCS_LIM(sensor, MIN_X_OUTPUT_SIZE),
 			     sel->r.width);
-	sel->r.height = max_t(unsigned int,
-			      CCS_LIM(sensor, MIN_Y_OUTPUT_SIZE),
+	sel->r.height = max_t(unsigned int, CCS_LIM(sensor, MIN_Y_OUTPUT_SIZE),
 			      sel->r.height);
 
 	switch (sel->target) {
@@ -2613,8 +2610,7 @@ static int ccs_identify_module(struct ccs_sensor *sensor)
 		dev_warn(&client->dev,
 			 "no quirks for this module; let's hope it's fully compliant\n");
 
-	dev_dbg(&client->dev, "the sensor is called %s\n",
-		minfo->name);
+	dev_dbg(&client->dev, "the sensor is called %s\n", minfo->name);
 
 	return 0;
 }
@@ -2634,19 +2630,15 @@ static int ccs_register_subdev(struct ccs_sensor *sensor,
 	if (!sink_ssd)
 		return 0;
 
-	rval = media_entity_pads_init(&ssd->sd.entity,
-				      ssd->npads, ssd->pads);
+	rval = media_entity_pads_init(&ssd->sd.entity, ssd->npads, ssd->pads);
 	if (rval) {
-		dev_err(&client->dev,
-			"media_entity_pads_init failed\n");
+		dev_err(&client->dev, "media_entity_pads_init failed\n");
 		return rval;
 	}
 
-	rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev,
-					   &ssd->sd);
+	rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev, &ssd->sd);
 	if (rval) {
-		dev_err(&client->dev,
-			"v4l2_device_register_subdev failed\n");
+		dev_err(&client->dev, "v4l2_device_register_subdev failed\n");
 		return rval;
 	}
 
@@ -2654,8 +2646,7 @@ static int ccs_register_subdev(struct ccs_sensor *sensor,
 				     &sink_ssd->sd.entity, sink_pad,
 				     link_flags);
 	if (rval) {
-		dev_err(&client->dev,
-			"media_create_pad_link failed\n");
+		dev_err(&client->dev, "media_create_pad_link failed\n");
 		v4l2_device_unregister_subdev(&ssd->sd);
 		return rval;
 	}
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 26/30] ccs: Use longer pre-I²C sleep for CCS compliant devices
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (24 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 25/30] ccs: Wrap long lines, unwrap short ones Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 27/30] ccs: Remove unnecessary delays from power-up sequence Sakari Ailus
                   ` (3 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

Longer idle period is required on I²C bus before the first transaction
after lifting xshutdown.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-core.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 863295b8fb5c..5014aa0d7969 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -1300,6 +1300,7 @@ static int ccs_power_on(struct device *dev)
 	 */
 	struct ccs_sensor *sensor =
 		container_of(ssd, struct ccs_sensor, ssds[0]);
+	const struct ccs_device *ccsdev = device_get_match_data(dev);
 	unsigned int sleep;
 	int rval;
 
@@ -1320,7 +1321,11 @@ static int ccs_power_on(struct device *dev)
 	gpiod_set_value(sensor->reset, 0);
 	gpiod_set_value(sensor->xshutdown, 1);
 
-	sleep = SMIAPP_RESET_DELAY(sensor->hwcfg.ext_clk);
+	if (ccsdev->flags & CCS_DEVICE_FLAG_IS_SMIA)
+		sleep = SMIAPP_RESET_DELAY(sensor->hwcfg.ext_clk);
+	else
+		sleep = 5000;
+
 	usleep_range(sleep, sleep);
 
 	/*
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 27/30] ccs: Remove unnecessary delays from power-up sequence
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (25 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 26/30] ccs: Use longer pre-I²C sleep for CCS compliant devices Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 28/30] dt-bindings: mipi,ccs: Don't mention vana voltage Sakari Ailus
                   ` (2 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

SMIA nor CCS need these delays; remove them.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-core.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 5014aa0d7969..89dc09587211 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -1309,14 +1309,12 @@ static int ccs_power_on(struct device *dev)
 		dev_err(dev, "failed to enable vana regulator\n");
 		return rval;
 	}
-	usleep_range(1000, 1000);
 
 	rval = clk_prepare_enable(sensor->ext_clk);
 	if (rval < 0) {
 		dev_dbg(dev, "failed to enable xclk\n");
 		goto out_xclk_fail;
 	}
-	usleep_range(1000, 1000);
 
 	gpiod_set_value(sensor->reset, 0);
 	gpiod_set_value(sensor->xshutdown, 1);
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 28/30] dt-bindings: mipi,ccs: Don't mention vana voltage
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (26 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 27/30] ccs: Remove unnecessary delays from power-up sequence Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 29/30] dt-bindings: mipi,ccs: Add vcore and vio supplies Sakari Ailus
  2020-11-24  9:32 ` [PATCH 30/30] ccs: Use all regulators Sakari Ailus
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

It was mentioned vana voltage is typically 2,8 volts. This is truly sensor
dependent, and nowadays 2,8 volts is a lot.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml b/Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml
index 1d90767a6196..51426a950414 100644
--- a/Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml
@@ -37,8 +37,7 @@ properties:
     maxItems: 1
 
   vana-supply:
-    description: Analogue voltage supply (VANA), typically 2,8 volts (sensor
-      dependent).
+    description: Analogue voltage supply (VANA), sensor dependent.
     maxItems: 1
 
   clocks:
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 29/30] dt-bindings: mipi,ccs: Add vcore and vio supplies
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (27 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 28/30] dt-bindings: mipi,ccs: Don't mention vana voltage Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  2020-11-24  9:32 ` [PATCH 30/30] ccs: Use all regulators Sakari Ailus
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

Vcore and vio supplies are also part of the spec and used by many sensors.
Do not specify the voltages as they are generally sensor dependent.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml b/Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml
index 51426a950414..d94bd67ccea1 100644
--- a/Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml
@@ -40,6 +40,14 @@ properties:
     description: Analogue voltage supply (VANA), sensor dependent.
     maxItems: 1
 
+  vcore-supply:
+    description: Core voltage supply (VCore), sensor dependent.
+    maxItems: 1
+
+  vio-supply:
+    description: I/O voltage supply (VIO), sensor dependent.
+    maxItems: 1
+
   clocks:
     description: External clock to the sensor.
     maxItems: 1
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 30/30] ccs: Use all regulators
  2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
                   ` (28 preceding siblings ...)
  2020-11-24  9:32 ` [PATCH 29/30] dt-bindings: mipi,ccs: Add vcore and vio supplies Sakari Ailus
@ 2020-11-24  9:32 ` Sakari Ailus
  29 siblings, 0 replies; 31+ messages in thread
From: Sakari Ailus @ 2020-11-24  9:32 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, mchehab

Use regulators vio and vcore besides vana. The regulators were always
there but on many boards they've been hard wired. Control them explicitly
now.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-core.c | 30 +++++++++++++++++++++++-------
 drivers/media/i2c/ccs/ccs.h      |  2 +-
 2 files changed, 24 insertions(+), 8 deletions(-)

diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 89dc09587211..4447ca367a84 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -65,6 +65,8 @@ struct ccs_device {
 	unsigned char flags;
 };
 
+static const char * const ccs_regulators[] = { "vcore", "vio", "vana" };
+
 /*
  *
  * Dynamic Capability Identification
@@ -1304,7 +1306,8 @@ static int ccs_power_on(struct device *dev)
 	unsigned int sleep;
 	int rval;
 
-	rval = regulator_enable(sensor->vana);
+	rval = regulator_bulk_enable(ARRAY_SIZE(ccs_regulators),
+				     sensor->regulators);
 	if (rval) {
 		dev_err(dev, "failed to enable vana regulator\n");
 		return rval;
@@ -1416,7 +1419,8 @@ static int ccs_power_on(struct device *dev)
 	clk_disable_unprepare(sensor->ext_clk);
 
 out_xclk_fail:
-	regulator_disable(sensor->vana);
+	regulator_bulk_disable(ARRAY_SIZE(ccs_regulators),
+			       sensor->regulators);
 
 	return rval;
 }
@@ -1442,7 +1446,8 @@ static int ccs_power_off(struct device *dev)
 	gpiod_set_value(sensor->xshutdown, 0);
 	clk_disable_unprepare(sensor->ext_clk);
 	usleep_range(5000, 5000);
-	regulator_disable(sensor->vana);
+	regulator_bulk_disable(ARRAY_SIZE(ccs_regulators),
+			       sensor->regulators);
 	sensor->streaming = false;
 
 	return 0;
@@ -2981,10 +2986,21 @@ static int ccs_probe(struct i2c_client *client)
 	v4l2_i2c_subdev_init(&sensor->src->sd, client, &ccs_ops);
 	sensor->src->sd.internal_ops = &ccs_internal_src_ops;
 
-	sensor->vana = devm_regulator_get(&client->dev, "vana");
-	if (IS_ERR(sensor->vana)) {
-		dev_err(&client->dev, "could not get regulator for vana\n");
-		return PTR_ERR(sensor->vana);
+	sensor->regulators = devm_kcalloc(&client->dev,
+					  ARRAY_SIZE(ccs_regulators),
+					  sizeof(*sensor->regulators),
+					  GFP_KERNEL);
+	if (!sensor->regulators)
+		return -ENOMEM;
+
+	for (i = 0; i < ARRAY_SIZE(ccs_regulators); i++)
+		sensor->regulators[i].supply = ccs_regulators[i];
+
+	rval = devm_regulator_bulk_get(&client->dev, ARRAY_SIZE(ccs_regulators),
+				       sensor->regulators);
+	if (rval) {
+		dev_err(&client->dev, "could not get regulators\n");
+		return rval;
 	}
 
 	sensor->ext_clk = devm_clk_get(&client->dev, NULL);
diff --git a/drivers/media/i2c/ccs/ccs.h b/drivers/media/i2c/ccs/ccs.h
index 6b07e4143ff0..356b87c33405 100644
--- a/drivers/media/i2c/ccs/ccs.h
+++ b/drivers/media/i2c/ccs/ccs.h
@@ -223,7 +223,7 @@ struct ccs_sensor {
 	struct ccs_subdev *scaler;
 	struct ccs_subdev *pixel_array;
 	struct ccs_hwconfig hwcfg;
-	struct regulator *vana;
+	struct regulator_bulk_data *regulators;
 	struct clk *ext_clk;
 	struct gpio_desc *xshutdown;
 	struct gpio_desc *reset;
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2020-11-24  9:38 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-24  9:31 [PATCH 00/30] Trivial MIPI CCS support Sakari Ailus
2020-11-24  9:31 ` [PATCH 01/30] ccs: Add MIPI CCS compatible strings Sakari Ailus
2020-11-24  9:31 ` [PATCH 02/30] ccs: Add device compatible identifiers for telling SMIA and CCS apart Sakari Ailus
2020-11-24  9:31 ` [PATCH 03/30] ccs: Add CCS ACPI device ID Sakari Ailus
2020-11-24  9:32 ` [PATCH 04/30] ccs: Remove the I²C ID table Sakari Ailus
2020-11-24  9:32 ` [PATCH 05/30] ccs: Remove remaining support for platform data Sakari Ailus
2020-11-24  9:32 ` [PATCH 06/30] ccs: Make hwcfg part of the device specific struct Sakari Ailus
2020-11-24  9:32 ` [PATCH 07/30] ccs: Fix obtaining bus information from firmware Sakari Ailus
2020-11-24  9:32 ` [PATCH 08/30] ccs: Add CCS static data parser library Sakari Ailus
2020-11-24  9:32 ` [PATCH 09/30] ccs: Combine revision number major and minor into one Sakari Ailus
2020-11-24  9:32 ` [PATCH 10/30] ccs: Read CCS static data from firmware binaries Sakari Ailus
2020-11-24  9:32 ` [PATCH 11/30] ccs: Stop reading arrays after the first zero Sakari Ailus
2020-11-24  9:32 ` [PATCH 12/30] ccs: The functions to get compose or crop rectangle never return NULL Sakari Ailus
2020-11-24  9:32 ` [PATCH 13/30] ccs: Replace somewhat harsh internal checks based on BUG with WARN_ON Sakari Ailus
2020-11-24  9:32 ` [PATCH 14/30] ccs: Refactor register reading a little Sakari Ailus
2020-11-24  9:32 ` [PATCH 15/30] ccs: Make real to integer number conversion optional Sakari Ailus
2020-11-24  9:32 ` [PATCH 16/30] ccs: Move limit value real to integer conversion from read to access time Sakari Ailus
2020-11-24  9:32 ` [PATCH 17/30] ccs: Read ireal numbers correctly Sakari Ailus
2020-11-24  9:32 ` [PATCH 18/30] smiapp-pll: Rename as ccs-pll Sakari Ailus
2020-11-24  9:32 ` [PATCH 19/30] ccs-pll: Fix MODULE_LICENSE Sakari Ailus
2020-11-24  9:32 ` [PATCH 20/30] ccs: Change my e-mail address Sakari Ailus
2020-11-24  9:32 ` [PATCH 21/30] ccs: Allow range in between I²C retries Sakari Ailus
2020-11-24  9:32 ` [PATCH 22/30] ccs: Add support for manufacturer regs from sensor and module files Sakari Ailus
2020-11-24  9:32 ` [PATCH 23/30] ccs: Use static data read-only registers Sakari Ailus
2020-11-24  9:32 ` [PATCH 24/30] ccs: Clean up runtime PM usage Sakari Ailus
2020-11-24  9:32 ` [PATCH 25/30] ccs: Wrap long lines, unwrap short ones Sakari Ailus
2020-11-24  9:32 ` [PATCH 26/30] ccs: Use longer pre-I²C sleep for CCS compliant devices Sakari Ailus
2020-11-24  9:32 ` [PATCH 27/30] ccs: Remove unnecessary delays from power-up sequence Sakari Ailus
2020-11-24  9:32 ` [PATCH 28/30] dt-bindings: mipi,ccs: Don't mention vana voltage Sakari Ailus
2020-11-24  9:32 ` [PATCH 29/30] dt-bindings: mipi,ccs: Add vcore and vio supplies Sakari Ailus
2020-11-24  9:32 ` [PATCH 30/30] ccs: Use all regulators Sakari Ailus

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.