summaryrefslogtreecommitdiffstats
path: root/drivers/media/i2c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-02-26 20:47:26 +0100
committerLinus Torvalds <torvalds@linux-foundation.org>2023-02-26 20:47:26 +0100
commit4b8c673b761e74add4fd185d806ac16c9b40158f (patch)
treeb7589b0844fc3306e8886228d4290eaff26456c6 /drivers/media/i2c
parentDocumentation: simplify and clarify DCO contribution example language (diff)
parentmedia: imx-mipi-csis: Check csis_fmt validity before use (diff)
downloadlinux-4b8c673b761e74add4fd185d806ac16c9b40158f.tar.xz
linux-4b8c673b761e74add4fd185d806ac16c9b40158f.zip
Merge tag 'media/v6.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab: - Removal of several VB1-only deprecated drivers: cpia2, fsl-viu, meye, stkwebcam, tm6000, vpfe_capture and zr364xx - saa7146 recovered from staging/deprecated. We opted to give ti a chance, and, instead of deprecating it, the intention is to write patches migrating it from VB1 to VB2. - av7110 returned from staging/deprecated/ to staging/ as we're not planning on dropping it any time soon - media controller API has gained experimental support for G_ROUTING and streams API. No drivers use it right now. We're planning to add one after -rc1, giving some time to experience the API and eventually have changes during the next development cycle - New sensor drivers: imx296, imx415, ov8858 - Atomisp had lots of changes, specially on its sensor's interface, making atomisp sensor drivers closer to normal sensor drivers - media controller kAPI has gained some helpers to traverse pipelines - uvcvideo now better support power line control - lots of bug fixes, cleanups and driver improvements * tag 'media/v6.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (296 commits) media: imx-mipi-csis: Check csis_fmt validity before use media: v4l2-subdev.c: clear stream field media: v4l2-ctrls-api.c: move ctrl->is_new = 1 to the correct line media: Revert "media: saa7146: deprecate hexium_gemini/orion, mxb and ttpci" media: Revert "media: av7110: move to staging/media/deprecated/saa7146" media: imx-pxp: convert to regmap media: imx-pxp: Use non-threaded IRQ media: imx-pxp: Introduce pxp_read() and pxp_write() wrappers media: imx-pxp: Implement frame size enumeration media: imx-pxp: Pass pixel format value to find_format() media: imx-pxp: Add media controller support media: imx-pxp: Don't set bus_info manually in .querycap() media: imx-pxp: Sort headers alphabetically media: imx-pxp: add support for i.MX7D media: imx-pxp: make data_path_ctrl0 platform dependent media: imx-pxp: disable LUT block media: imx-pxp: explicitly disable unused blocks media: imx-pxp: extract helper function to setup data path media: imx-pxp: detect PXP version media: dt-bindings: media: fsl-pxp: convert to yaml ...
Diffstat (limited to 'drivers/media/i2c')
-rw-r--r--drivers/media/i2c/Kconfig40
-rw-r--r--drivers/media/i2c/Makefile3
-rw-r--r--drivers/media/i2c/adv7180.c6
-rw-r--r--drivers/media/i2c/adv7604.c6
-rw-r--r--drivers/media/i2c/ak7375.c38
-rw-r--r--drivers/media/i2c/cs53l32a.c6
-rw-r--r--drivers/media/i2c/imx219.c311
-rw-r--r--drivers/media/i2c/imx290.c1019
-rw-r--r--drivers/media/i2c/imx296.c1172
-rw-r--r--drivers/media/i2c/imx415.c1300
-rw-r--r--drivers/media/i2c/ir-kbd-i2c.c5
-rw-r--r--drivers/media/i2c/max9286.c463
-rw-r--r--drivers/media/i2c/msp3400-driver.c5
-rw-r--r--drivers/media/i2c/mt9p031.c6
-rw-r--r--drivers/media/i2c/mt9v032.c6
-rw-r--r--drivers/media/i2c/ov2740.c4
-rw-r--r--drivers/media/i2c/ov5640.c86
-rw-r--r--drivers/media/i2c/ov5670.c312
-rw-r--r--drivers/media/i2c/ov5675.c198
-rw-r--r--drivers/media/i2c/ov7670.c8
-rw-r--r--drivers/media/i2c/ov772x.c3
-rw-r--r--drivers/media/i2c/ov8858.c2008
-rw-r--r--drivers/media/i2c/ov9282.c9
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-core.c22
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c1
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3.h1
-rw-r--r--drivers/media/i2c/saa7115.c6
-rw-r--r--drivers/media/i2c/saa7127.c6
-rw-r--r--drivers/media/i2c/st-vgxy61.c4
-rw-r--r--drivers/media/i2c/tc358746.c9
-rw-r--r--drivers/media/i2c/tda1997x.c6
-rw-r--r--drivers/media/i2c/tvaudio.c5
-rw-r--r--drivers/media/i2c/tvp514x.c6
-rw-r--r--drivers/media/i2c/video-i2c.c6
34 files changed, 6218 insertions, 868 deletions
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 833241897d63..c3d5952ca27e 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -162,6 +162,19 @@ config VIDEO_IMX290
To compile this driver as a module, choose M here: the
module will be called imx290.
+config VIDEO_IMX296
+ tristate "Sony IMX296 sensor support"
+ depends on I2C && VIDEO_DEV
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ select VIDEO_V4L2_SUBDEV_API
+ help
+ This is a Video4Linux2 sensor driver for the Sony
+ IMX296 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called imx296.
+
config VIDEO_IMX319
tristate "Sony IMX319 sensor support"
depends on I2C && VIDEO_DEV
@@ -228,6 +241,20 @@ config VIDEO_IMX412
To compile this driver as a module, choose M here: the
module will be called imx412.
+config VIDEO_IMX415
+ tristate "Sony IMX415 sensor support"
+ depends on OF_GPIO
+ depends on I2C && VIDEO_DEV
+ select VIDEO_V4L2_SUBDEV_API
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ help
+ This is a Video4Linux2 sensor driver for the Sony
+ IMX415 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called imx415.
+
config VIDEO_MAX9271_LIB
tristate
@@ -645,6 +672,19 @@ config VIDEO_OV8856
To compile this driver as a module, choose M here: the
module will be called ov8856.
+config VIDEO_OV8858
+ tristate "OmniVision OV8858 sensor support"
+ depends on I2C && PM && VIDEO_DEV
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ select V4L2_FWNODE
+ help
+ This is a Video4Linux2 sensor driver for OmniVision
+ OV8858 camera sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ov8858.
+
config VIDEO_OV8865
tristate "OmniVision OV8865 sensor support"
depends on I2C && PM && VIDEO_DEV
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 4d6c052bb5a7..4f5e9d9cee85 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -43,11 +43,13 @@ obj-$(CONFIG_VIDEO_IMX219) += imx219.o
obj-$(CONFIG_VIDEO_IMX258) += imx258.o
obj-$(CONFIG_VIDEO_IMX274) += imx274.o
obj-$(CONFIG_VIDEO_IMX290) += imx290.o
+obj-$(CONFIG_VIDEO_IMX296) += imx296.o
obj-$(CONFIG_VIDEO_IMX319) += imx319.o
obj-$(CONFIG_VIDEO_IMX334) += imx334.o
obj-$(CONFIG_VIDEO_IMX335) += imx335.o
obj-$(CONFIG_VIDEO_IMX355) += imx355.o
obj-$(CONFIG_VIDEO_IMX412) += imx412.o
+obj-$(CONFIG_VIDEO_IMX415) += imx415.o
obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
obj-$(CONFIG_VIDEO_ISL7998X) += isl7998x.o
obj-$(CONFIG_VIDEO_KS0127) += ks0127.o
@@ -96,6 +98,7 @@ obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
obj-$(CONFIG_VIDEO_OV772X) += ov772x.o
obj-$(CONFIG_VIDEO_OV7740) += ov7740.o
obj-$(CONFIG_VIDEO_OV8856) += ov8856.o
+obj-$(CONFIG_VIDEO_OV8858) += ov8858.o
obj-$(CONFIG_VIDEO_OV8865) += ov8865.o
obj-$(CONFIG_VIDEO_OV9282) += ov9282.o
obj-$(CONFIG_VIDEO_OV9640) += ov9640.o
diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c
index 216fe396973f..a22402b7acff 100644
--- a/drivers/media/i2c/adv7180.c
+++ b/drivers/media/i2c/adv7180.c
@@ -1393,9 +1393,9 @@ out_unlock:
return ret;
}
-static int adv7180_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adv7180_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct device_node *np = client->dev.of_node;
struct adv7180_state *state;
struct v4l2_subdev *sd;
@@ -1610,7 +1610,7 @@ static struct i2c_driver adv7180_driver = {
.pm = ADV7180_PM_OPS,
.of_match_table = of_match_ptr(adv7180_of_id),
},
- .probe = adv7180_probe,
+ .probe_new = adv7180_probe,
.remove = adv7180_remove,
.id_table = adv7180_id,
};
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index bda0c547ce44..9d218962d7c8 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -3401,9 +3401,9 @@ static void adv76xx_reset(struct adv76xx_state *state)
}
}
-static int adv76xx_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adv76xx_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
static const struct v4l2_dv_timings cea640x480 =
V4L2_DV_BT_CEA_640X480P59_94;
struct adv76xx_state *state;
@@ -3686,7 +3686,7 @@ static struct i2c_driver adv76xx_driver = {
.name = "adv7604",
.of_match_table = of_match_ptr(adv76xx_of_id),
},
- .probe = adv76xx_probe,
+ .probe_new = adv76xx_probe,
.remove = adv76xx_remove,
.id_table = adv76xx_i2c_id,
};
diff --git a/drivers/media/i2c/ak7375.c b/drivers/media/i2c/ak7375.c
index 1af9f698eecf..e7cec45bc271 100644
--- a/drivers/media/i2c/ak7375.c
+++ b/drivers/media/i2c/ak7375.c
@@ -6,6 +6,7 @@
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
@@ -23,17 +24,29 @@
*/
#define AK7375_CTRL_STEPS 64
#define AK7375_CTRL_DELAY_US 1000
+/*
+ * The vcm may take up 10 ms (tDELAY) to power on and start taking
+ * I2C messages. Based on AK7371 datasheet.
+ */
+#define AK7375_POWER_DELAY_US 10000
#define AK7375_REG_POSITION 0x0
#define AK7375_REG_CONT 0x2
#define AK7375_MODE_ACTIVE 0x0
#define AK7375_MODE_STANDBY 0x40
+static const char * const ak7375_supply_names[] = {
+ "vdd",
+ "vio",
+};
+
/* ak7375 device structure */
struct ak7375_device {
struct v4l2_ctrl_handler ctrls_vcm;
struct v4l2_subdev sd;
struct v4l2_ctrl *focus;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(ak7375_supply_names)];
+
/* active or standby mode */
bool active;
};
@@ -133,12 +146,24 @@ static int ak7375_probe(struct i2c_client *client)
{
struct ak7375_device *ak7375_dev;
int ret;
+ unsigned int i;
ak7375_dev = devm_kzalloc(&client->dev, sizeof(*ak7375_dev),
GFP_KERNEL);
if (!ak7375_dev)
return -ENOMEM;
+ for (i = 0; i < ARRAY_SIZE(ak7375_supply_names); i++)
+ ak7375_dev->supplies[i].supply = ak7375_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&client->dev,
+ ARRAY_SIZE(ak7375_supply_names),
+ ak7375_dev->supplies);
+ if (ret) {
+ dev_err_probe(&client->dev, ret, "Failed to get regulators\n");
+ return ret;
+ }
+
v4l2_i2c_subdev_init(&ak7375_dev->sd, client, &ak7375_ops);
ak7375_dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
ak7375_dev->sd.internal_ops = &ak7375_int_ops;
@@ -208,6 +233,11 @@ static int __maybe_unused ak7375_vcm_suspend(struct device *dev)
if (ret)
dev_err(dev, "%s I2C failure: %d\n", __func__, ret);
+ ret = regulator_bulk_disable(ARRAY_SIZE(ak7375_supply_names),
+ ak7375_dev->supplies);
+ if (ret)
+ return ret;
+
ak7375_dev->active = false;
return 0;
@@ -228,6 +258,14 @@ static int __maybe_unused ak7375_vcm_resume(struct device *dev)
if (ak7375_dev->active)
return 0;
+ ret = regulator_bulk_enable(ARRAY_SIZE(ak7375_supply_names),
+ ak7375_dev->supplies);
+ if (ret)
+ return ret;
+
+ /* Wait for vcm to become ready */
+ usleep_range(AK7375_POWER_DELAY_US, AK7375_POWER_DELAY_US + 500);
+
ret = ak7375_i2c_write(ak7375_dev, AK7375_REG_CONT,
AK7375_MODE_ACTIVE, 1);
if (ret) {
diff --git a/drivers/media/i2c/cs53l32a.c b/drivers/media/i2c/cs53l32a.c
index 9461589aea30..670f89de32d4 100644
--- a/drivers/media/i2c/cs53l32a.c
+++ b/drivers/media/i2c/cs53l32a.c
@@ -128,9 +128,9 @@ static const struct v4l2_subdev_ops cs53l32a_ops = {
* concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
*/
-static int cs53l32a_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int cs53l32a_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct cs53l32a_state *state;
struct v4l2_subdev *sd;
int i;
@@ -209,7 +209,7 @@ static struct i2c_driver cs53l32a_driver = {
.driver = {
.name = "cs53l32a",
},
- .probe = cs53l32a_probe,
+ .probe_new = cs53l32a_probe,
.remove = cs53l32a_remove,
.id_table = cs53l32a_id,
};
diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c
index 77bd79a5954e..f9471c9e3a74 100644
--- a/drivers/media/i2c/imx219.c
+++ b/drivers/media/i2c/imx219.c
@@ -42,10 +42,16 @@
/* External clock frequency is 24.0M */
#define IMX219_XCLK_FREQ 24000000
-/* Pixel rate is fixed at 182.4M for all the modes */
+/* Pixel rate is fixed for all the modes */
#define IMX219_PIXEL_RATE 182400000
+#define IMX219_PIXEL_RATE_4LANE 280800000
#define IMX219_DEFAULT_LINK_FREQ 456000000
+#define IMX219_DEFAULT_LINK_FREQ_4LANE 363000000
+
+#define IMX219_REG_CSI_LANE_MODE 0x0114
+#define IMX219_CSI_2_LANE_MODE 0x01
+#define IMX219_CSI_4_LANE_MODE 0x03
/* V_TIMING internal */
#define IMX219_REG_VTS 0x0160
@@ -89,6 +95,12 @@
#define IMX219_REG_ORIENTATION 0x0172
+/* Binning Mode */
+#define IMX219_REG_BINNING_MODE 0x0174
+#define IMX219_BINNING_NONE 0x0000
+#define IMX219_BINNING_2X2 0x0101
+#define IMX219_BINNING_2X2_ANALOG 0x0303
+
/* Test Pattern Control */
#define IMX219_REG_TEST_PATTERN 0x0600
#define IMX219_TEST_PATTERN_DISABLE 0
@@ -143,25 +155,66 @@ struct imx219_mode {
/* Default register values */
struct imx219_reg_list reg_list;
+
+ /* 2x2 binning is used */
+ bool binning;
};
-/*
- * Register sets lifted off the i2C interface from the Raspberry Pi firmware
- * driver.
- * 3280x2464 = mode 2, 1920x1080 = mode 1, 1640x1232 = mode 4, 640x480 = mode 7.
- */
-static const struct imx219_reg mode_3280x2464_regs[] = {
- {0x0100, 0x00},
+static const struct imx219_reg imx219_common_regs[] = {
+ {0x0100, 0x00}, /* Mode Select */
+
+ /* To Access Addresses 3000-5fff, send the following commands */
{0x30eb, 0x0c},
{0x30eb, 0x05},
{0x300a, 0xff},
{0x300b, 0xff},
{0x30eb, 0x05},
{0x30eb, 0x09},
- {0x0114, 0x01},
- {0x0128, 0x00},
- {0x012a, 0x18},
+
+ /* PLL Clock Table */
+ {0x0301, 0x05}, /* VTPXCK_DIV */
+ {0x0303, 0x01}, /* VTSYSCK_DIV */
+ {0x0304, 0x03}, /* PREPLLCK_VT_DIV 0x03 = AUTO set */
+ {0x0305, 0x03}, /* PREPLLCK_OP_DIV 0x03 = AUTO set */
+ {0x0306, 0x00}, /* PLL_VT_MPY */
+ {0x0307, 0x39},
+ {0x030b, 0x01}, /* OP_SYS_CLK_DIV */
+ {0x030c, 0x00}, /* PLL_OP_MPY */
+ {0x030d, 0x72},
+
+ /* Undocumented registers */
+ {0x455e, 0x00},
+ {0x471e, 0x4b},
+ {0x4767, 0x0f},
+ {0x4750, 0x14},
+ {0x4540, 0x00},
+ {0x47b4, 0x14},
+ {0x4713, 0x30},
+ {0x478b, 0x10},
+ {0x478f, 0x10},
+ {0x4793, 0x10},
+ {0x4797, 0x0e},
+ {0x479b, 0x0e},
+
+ /* Frame Bank Register Group "A" */
+ {0x0162, 0x0d}, /* Line_Length_A */
+ {0x0163, 0x78},
+ {0x0170, 0x01}, /* X_ODD_INC_A */
+ {0x0171, 0x01}, /* Y_ODD_INC_A */
+
+ /* Output setup registers */
+ {0x0114, 0x01}, /* CSI 2-Lane Mode */
+ {0x0128, 0x00}, /* DPHY Auto Mode */
+ {0x012a, 0x18}, /* EXCK_Freq */
{0x012b, 0x00},
+};
+
+/*
+ * Register sets lifted off the i2C interface from the Raspberry Pi firmware
+ * driver.
+ * 3280x2464 = mode 2, 1920x1080 = mode 1, 1640x1232 = mode 4, 640x480 = mode 7.
+ */
+static const struct imx219_reg mode_3280x2464_regs[] = {
{0x0164, 0x00},
{0x0165, 0x00},
{0x0166, 0x0c},
@@ -174,53 +227,13 @@ static const struct imx219_reg mode_3280x2464_regs[] = {
{0x016d, 0xd0},
{0x016e, 0x09},
{0x016f, 0xa0},
- {0x0170, 0x01},
- {0x0171, 0x01},
- {0x0174, 0x00},
- {0x0175, 0x00},
- {0x0301, 0x05},
- {0x0303, 0x01},
- {0x0304, 0x03},
- {0x0305, 0x03},
- {0x0306, 0x00},
- {0x0307, 0x39},
- {0x030b, 0x01},
- {0x030c, 0x00},
- {0x030d, 0x72},
{0x0624, 0x0c},
{0x0625, 0xd0},
{0x0626, 0x09},
{0x0627, 0xa0},
- {0x455e, 0x00},
- {0x471e, 0x4b},
- {0x4767, 0x0f},
- {0x4750, 0x14},
- {0x4540, 0x00},
- {0x47b4, 0x14},
- {0x4713, 0x30},
- {0x478b, 0x10},
- {0x478f, 0x10},
- {0x4793, 0x10},
- {0x4797, 0x0e},
- {0x479b, 0x0e},
- {0x0162, 0x0d},
- {0x0163, 0x78},
};
static const struct imx219_reg mode_1920_1080_regs[] = {
- {0x0100, 0x00},
- {0x30eb, 0x05},
- {0x30eb, 0x0c},
- {0x300a, 0xff},
- {0x300b, 0xff},
- {0x30eb, 0x05},
- {0x30eb, 0x09},
- {0x0114, 0x01},
- {0x0128, 0x00},
- {0x012a, 0x18},
- {0x012b, 0x00},
- {0x0162, 0x0d},
- {0x0163, 0x78},
{0x0164, 0x02},
{0x0165, 0xa8},
{0x0166, 0x0a},
@@ -233,49 +246,13 @@ static const struct imx219_reg mode_1920_1080_regs[] = {
{0x016d, 0x80},
{0x016e, 0x04},
{0x016f, 0x38},
- {0x0170, 0x01},
- {0x0171, 0x01},
- {0x0174, 0x00},
- {0x0175, 0x00},
- {0x0301, 0x05},
- {0x0303, 0x01},
- {0x0304, 0x03},
- {0x0305, 0x03},
- {0x0306, 0x00},
- {0x0307, 0x39},
- {0x030b, 0x01},
- {0x030c, 0x00},
- {0x030d, 0x72},
{0x0624, 0x07},
{0x0625, 0x80},
{0x0626, 0x04},
{0x0627, 0x38},
- {0x455e, 0x00},
- {0x471e, 0x4b},
- {0x4767, 0x0f},
- {0x4750, 0x14},
- {0x4540, 0x00},
- {0x47b4, 0x14},
- {0x4713, 0x30},
- {0x478b, 0x10},
- {0x478f, 0x10},
- {0x4793, 0x10},
- {0x4797, 0x0e},
- {0x479b, 0x0e},
};
static const struct imx219_reg mode_1640_1232_regs[] = {
- {0x0100, 0x00},
- {0x30eb, 0x0c},
- {0x30eb, 0x05},
- {0x300a, 0xff},
- {0x300b, 0xff},
- {0x30eb, 0x05},
- {0x30eb, 0x09},
- {0x0114, 0x01},
- {0x0128, 0x00},
- {0x012a, 0x18},
- {0x012b, 0x00},
{0x0164, 0x00},
{0x0165, 0x00},
{0x0166, 0x0c},
@@ -288,53 +265,13 @@ static const struct imx219_reg mode_1640_1232_regs[] = {
{0x016d, 0x68},
{0x016e, 0x04},
{0x016f, 0xd0},
- {0x0170, 0x01},
- {0x0171, 0x01},
- {0x0174, 0x01},
- {0x0175, 0x01},
- {0x0301, 0x05},
- {0x0303, 0x01},
- {0x0304, 0x03},
- {0x0305, 0x03},
- {0x0306, 0x00},
- {0x0307, 0x39},
- {0x030b, 0x01},
- {0x030c, 0x00},
- {0x030d, 0x72},
{0x0624, 0x06},
{0x0625, 0x68},
{0x0626, 0x04},
{0x0627, 0xd0},
- {0x455e, 0x00},
- {0x471e, 0x4b},
- {0x4767, 0x0f},
- {0x4750, 0x14},
- {0x4540, 0x00},
- {0x47b4, 0x14},
- {0x4713, 0x30},
- {0x478b, 0x10},
- {0x478f, 0x10},
- {0x4793, 0x10},
- {0x4797, 0x0e},
- {0x479b, 0x0e},
- {0x0162, 0x0d},
- {0x0163, 0x78},
};
static const struct imx219_reg mode_640_480_regs[] = {
- {0x0100, 0x00},
- {0x30eb, 0x05},
- {0x30eb, 0x0c},
- {0x300a, 0xff},
- {0x300b, 0xff},
- {0x30eb, 0x05},
- {0x30eb, 0x09},
- {0x0114, 0x01},
- {0x0128, 0x00},
- {0x012a, 0x18},
- {0x012b, 0x00},
- {0x0162, 0x0d},
- {0x0163, 0x78},
{0x0164, 0x03},
{0x0165, 0xe8},
{0x0166, 0x08},
@@ -347,35 +284,10 @@ static const struct imx219_reg mode_640_480_regs[] = {
{0x016d, 0x80},
{0x016e, 0x01},
{0x016f, 0xe0},
- {0x0170, 0x01},
- {0x0171, 0x01},
- {0x0174, 0x03},
- {0x0175, 0x03},
- {0x0301, 0x05},
- {0x0303, 0x01},
- {0x0304, 0x03},
- {0x0305, 0x03},
- {0x0306, 0x00},
- {0x0307, 0x39},
- {0x030b, 0x01},
- {0x030c, 0x00},
- {0x030d, 0x72},
{0x0624, 0x06},
{0x0625, 0x68},
{0x0626, 0x04},
{0x0627, 0xd0},
- {0x455e, 0x00},
- {0x471e, 0x4b},
- {0x4767, 0x0f},
- {0x4750, 0x14},
- {0x4540, 0x00},
- {0x47b4, 0x14},
- {0x4713, 0x30},
- {0x478b, 0x10},
- {0x478f, 0x10},
- {0x4793, 0x10},
- {0x4797, 0x0e},
- {0x479b, 0x0e},
};
static const struct imx219_reg raw8_framefmt_regs[] = {
@@ -394,6 +306,10 @@ static const s64 imx219_link_freq_menu[] = {
IMX219_DEFAULT_LINK_FREQ,
};
+static const s64 imx219_link_freq_4lane_menu[] = {
+ IMX219_DEFAULT_LINK_FREQ_4LANE,
+};
+
static const char * const imx219_test_pattern_menu[] = {
"Disabled",
"Color Bars",
@@ -485,6 +401,7 @@ static const struct imx219_mode supported_modes[] = {
.num_of_regs = ARRAY_SIZE(mode_3280x2464_regs),
.regs = mode_3280x2464_regs,
},
+ .binning = false,
},
{
/* 1080P 30fps cropped */
@@ -501,6 +418,7 @@ static const struct imx219_mode supported_modes[] = {
.num_of_regs = ARRAY_SIZE(mode_1920_1080_regs),
.regs = mode_1920_1080_regs,
},
+ .binning = false,
},
{
/* 2x2 binned 30fps mode */
@@ -517,6 +435,7 @@ static const struct imx219_mode supported_modes[] = {
.num_of_regs = ARRAY_SIZE(mode_1640_1232_regs),
.regs = mode_1640_1232_regs,
},
+ .binning = true,
},
{
/* 640x480 30fps mode */
@@ -533,6 +452,7 @@ static const struct imx219_mode supported_modes[] = {
.num_of_regs = ARRAY_SIZE(mode_640_480_regs),
.regs = mode_640_480_regs,
},
+ .binning = true,
},
};
@@ -569,6 +489,9 @@ struct imx219 {
/* Streaming on/off */
bool streaming;
+
+ /* Two or Four lanes */
+ u8 lanes;
};
static inline struct imx219 *to_imx219(struct v4l2_subdev *_sd)
@@ -979,6 +902,35 @@ static int imx219_set_framefmt(struct imx219 *imx219)
return -EINVAL;
}
+static int imx219_set_binning(struct imx219 *imx219)
+{
+ if (!imx219->mode->binning) {
+ return imx219_write_reg(imx219, IMX219_REG_BINNING_MODE,
+ IMX219_REG_VALUE_16BIT,
+ IMX219_BINNING_NONE);
+ }
+
+ switch (imx219->fmt.code) {
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ return imx219_write_reg(imx219, IMX219_REG_BINNING_MODE,
+ IMX219_REG_VALUE_16BIT,
+ IMX219_BINNING_2X2_ANALOG);
+
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ return imx219_write_reg(imx219, IMX219_REG_BINNING_MODE,
+ IMX219_REG_VALUE_16BIT,
+ IMX219_BINNING_2X2);
+ }
+
+ return -EINVAL;
+}
+
static const struct v4l2_rect *
__imx219_get_pad_crop(struct imx219 *imx219,
struct v4l2_subdev_state *sd_state,
@@ -1031,6 +983,13 @@ static int imx219_get_selection(struct v4l2_subdev *sd,
return -EINVAL;
}
+static int imx219_configure_lanes(struct imx219 *imx219)
+{
+ return imx219_write_reg(imx219, IMX219_REG_CSI_LANE_MODE,
+ IMX219_REG_VALUE_08BIT, (imx219->lanes == 2) ?
+ IMX219_CSI_2_LANE_MODE : IMX219_CSI_4_LANE_MODE);
+};
+
static int imx219_start_streaming(struct imx219 *imx219)
{
struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
@@ -1041,6 +1000,20 @@ static int imx219_start_streaming(struct imx219 *imx219)
if (ret < 0)
return ret;
+ /* Send all registers that are common to all modes */
+ ret = imx219_write_regs(imx219, imx219_common_regs, ARRAY_SIZE(imx219_common_regs));
+ if (ret) {
+ dev_err(&client->dev, "%s failed to send mfg header\n", __func__);
+ goto err_rpm_put;
+ }
+
+ /* Configure two or four Lane mode */
+ ret = imx219_configure_lanes(imx219);
+ if (ret) {
+ dev_err(&client->dev, "%s failed to configure lanes\n", __func__);
+ goto err_rpm_put;
+ }
+
/* Apply default values of current mode */
reg_list = &imx219->mode->reg_list;
ret = imx219_write_regs(imx219, reg_list->regs, reg_list->num_of_regs);
@@ -1056,6 +1029,13 @@ static int imx219_start_streaming(struct imx219 *imx219)
goto err_rpm_put;
}
+ ret = imx219_set_binning(imx219);
+ if (ret) {
+ dev_err(&client->dev, "%s failed to set binning: %d\n",
+ __func__, ret);
+ goto err_rpm_put;
+ }
+
/* Apply customized values from user */
ret = __v4l2_ctrl_handler_setup(imx219->sd.ctrl_handler);
if (ret)
@@ -1272,6 +1252,11 @@ static const struct v4l2_subdev_internal_ops imx219_internal_ops = {
.open = imx219_open,
};
+static unsigned long imx219_get_pixel_rate(struct imx219 *imx219)
+{
+ return (imx219->lanes == 2) ? IMX219_PIXEL_RATE : IMX219_PIXEL_RATE_4LANE;
+}
+
/* Initialize control handlers */
static int imx219_init_controls(struct imx219 *imx219)
{
@@ -1293,15 +1278,16 @@ static int imx219_init_controls(struct imx219 *imx219)
/* By default, PIXEL_RATE is read only */
imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
V4L2_CID_PIXEL_RATE,
- IMX219_PIXEL_RATE,
- IMX219_PIXEL_RATE, 1,
- IMX219_PIXEL_RATE);
+ imx219_get_pixel_rate(imx219),
+ imx219_get_pixel_rate(imx219), 1,
+ imx219_get_pixel_rate(imx219));
imx219->link_freq =
v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx219_ctrl_ops,
V4L2_CID_LINK_FREQ,
ARRAY_SIZE(imx219_link_freq_menu) - 1, 0,
- imx219_link_freq_menu);
+ (imx219->lanes == 2) ? imx219_link_freq_menu :
+ imx219_link_freq_4lane_menu);
if (imx219->link_freq)
imx219->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
@@ -1396,7 +1382,7 @@ static void imx219_free_controls(struct imx219 *imx219)
mutex_destroy(&imx219->mutex);
}
-static int imx219_check_hwcfg(struct device *dev)
+static int imx219_check_hwcfg(struct device *dev, struct imx219 *imx219)
{
struct fwnode_handle *endpoint;
struct v4l2_fwnode_endpoint ep_cfg = {
@@ -1416,10 +1402,12 @@ static int imx219_check_hwcfg(struct device *dev)
}
/* Check the number of MIPI CSI2 data lanes */
- if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) {
- dev_err(dev, "only 2 data lanes are currently supported\n");
+ if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2 &&
+ ep_cfg.bus.mipi_csi2.num_data_lanes != 4) {
+ dev_err(dev, "only 2 or 4 data lanes are currently supported\n");
goto error_out;
}
+ imx219->lanes = ep_cfg.bus.mipi_csi2.num_data_lanes;
/* Check the link frequency set in device tree */
if (!ep_cfg.nr_of_link_frequencies) {
@@ -1428,7 +1416,8 @@ static int imx219_check_hwcfg(struct device *dev)
}
if (ep_cfg.nr_of_link_frequencies != 1 ||
- ep_cfg.link_frequencies[0] != IMX219_DEFAULT_LINK_FREQ) {
+ (ep_cfg.link_frequencies[0] != ((imx219->lanes == 2) ?
+ IMX219_DEFAULT_LINK_FREQ : IMX219_DEFAULT_LINK_FREQ_4LANE))) {
dev_err(dev, "Link frequency not supported: %lld\n",
ep_cfg.link_frequencies[0]);
goto error_out;
@@ -1456,7 +1445,7 @@ static int imx219_probe(struct i2c_client *client)
v4l2_i2c_subdev_init(&imx219->sd, client, &imx219_subdev_ops);
/* Check the hardware configuration in device tree */
- if (imx219_check_hwcfg(dev))
+ if (imx219_check_hwcfg(dev, imx219))
return -EINVAL;
/* Get system clock (xclk) */
diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index 218ded13fd80..49d6c8bdec41 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -152,13 +152,10 @@
#define IMX290_PIXEL_ARRAY_RECORDING_WIDTH 1920
#define IMX290_PIXEL_ARRAY_RECORDING_HEIGHT 1080
-static const char * const imx290_supply_name[] = {
- "vdda",
- "vddd",
- "vdddo",
-};
+/* Equivalent value for 16bpp */
+#define IMX290_BLACK_LEVEL_DEFAULT 3840
-#define IMX290_NUM_SUPPLIES ARRAY_SIZE(imx290_supply_name)
+#define IMX290_NUM_SUPPLIES 3
struct imx290_regval {
u32 reg;
@@ -180,11 +177,10 @@ struct imx290 {
struct clk *xclk;
struct regmap *regmap;
u8 nlanes;
- u8 bpp;
struct v4l2_subdev sd;
struct media_pad pad;
- struct v4l2_mbus_framefmt current_format;
+
const struct imx290_mode *current_mode;
struct regulator_bulk_data supplies[IMX290_NUM_SUPPLIES];
@@ -195,35 +191,16 @@ struct imx290 {
struct v4l2_ctrl *pixel_rate;
struct v4l2_ctrl *hblank;
struct v4l2_ctrl *vblank;
-
- struct mutex lock;
-};
-
-struct imx290_pixfmt {
- u32 code;
- u8 bpp;
-};
-
-static const struct imx290_pixfmt imx290_formats[] = {
- { MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
- { MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
};
-static const struct regmap_config imx290_regmap_config = {
- .reg_bits = 16,
- .val_bits = 8,
-};
+static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
+{
+ return container_of(_sd, struct imx290, sd);
+}
-static const char * const imx290_test_pattern_menu[] = {
- "Disabled",
- "Sequence Pattern 1",
- "Horizontal Color-bar Chart",
- "Vertical Color-bar Chart",
- "Sequence Pattern 2",
- "Gradation Pattern 1",
- "Gradation Pattern 2",
- "000/555h Toggle Pattern",
-};
+/* -----------------------------------------------------------------------------
+ * Modes and formats
+ */
static const struct imx290_regval imx290_global_init_settings[] = {
{ IMX290_CTRL_07, IMX290_WINMODE_1080P },
@@ -338,7 +315,6 @@ static const struct imx290_regval imx290_10bit_settings[] = {
{ IMX290_ADBIT2, IMX290_ADBIT2_10BIT },
{ IMX290_ADBIT3, IMX290_ADBIT3_10BIT },
{ IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW10 },
- { IMX290_BLKLEVEL, 60 },
};
static const struct imx290_regval imx290_12bit_settings[] = {
@@ -348,7 +324,6 @@ static const struct imx290_regval imx290_12bit_settings[] = {
{ IMX290_ADBIT2, IMX290_ADBIT2_12BIT },
{ IMX290_ADBIT3, IMX290_ADBIT3_12BIT },
{ IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW12 },
- { IMX290_BLKLEVEL, 240 },
};
/* supported link frequencies */
@@ -438,11 +413,45 @@ static inline int imx290_modes_num(const struct imx290 *imx290)
return ARRAY_SIZE(imx290_modes_4lanes);
}
-static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
+struct imx290_format_info {
+ u32 code;
+ u8 bpp;
+ const struct imx290_regval *regs;
+ unsigned int num_regs;
+};
+
+static const struct imx290_format_info imx290_formats[] = {
+ {
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .bpp = 10,
+ .regs = imx290_10bit_settings,
+ .num_regs = ARRAY_SIZE(imx290_10bit_settings),
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .bpp = 12,
+ .regs = imx290_12bit_settings,
+ .num_regs = ARRAY_SIZE(imx290_12bit_settings),
+ }
+};
+
+static const struct imx290_format_info *imx290_format_info(u32 code)
{
- return container_of(_sd, struct imx290, sd);
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(imx290_formats); ++i) {
+ const struct imx290_format_info *info = &imx290_formats[i];
+
+ if (info->code == code)
+ return info;
+ }
+
+ return NULL;
}
+/* -----------------------------------------------------------------------------
+ * Register access
+ */
+
static int __always_unused imx290_read(struct imx290 *imx290, u32 addr, u32 *value)
{
u8 data[3] = { 0, 0, 0 };
@@ -501,28 +510,82 @@ static int imx290_set_register_array(struct imx290 *imx290,
return 0;
}
-/* Stop streaming */
-static int imx290_stop_streaming(struct imx290 *imx290)
+static int imx290_set_data_lanes(struct imx290 *imx290)
{
int ret = 0;
+ u32 frsel;
- imx290_write(imx290, IMX290_STANDBY, 0x01, &ret);
+ switch (imx290->nlanes) {
+ case 2:
+ default:
+ frsel = 0x02;
+ break;
+ case 4:
+ frsel = 0x01;
+ break;
+ }
- msleep(30);
+ imx290_write(imx290, IMX290_PHY_LANE_NUM, imx290->nlanes - 1, &ret);
+ imx290_write(imx290, IMX290_CSI_LANE_MODE, imx290->nlanes - 1, &ret);
+ imx290_write(imx290, IMX290_FR_FDG_SEL, frsel, &ret);
- return imx290_write(imx290, IMX290_XMSTA, 0x01, &ret);
+ return ret;
+}
+
+static int imx290_set_black_level(struct imx290 *imx290,
+ const struct v4l2_mbus_framefmt *format,
+ unsigned int black_level, int *err)
+{
+ unsigned int bpp = imx290_format_info(format->code)->bpp;
+
+ return imx290_write(imx290, IMX290_BLKLEVEL,
+ black_level >> (16 - bpp), err);
+}
+
+static int imx290_setup_format(struct imx290 *imx290,
+ const struct v4l2_mbus_framefmt *format)
+{
+ const struct imx290_format_info *info;
+ int ret;
+
+ info = imx290_format_info(format->code);
+
+ ret = imx290_set_register_array(imx290, info->regs, info->num_regs);
+ if (ret < 0) {
+ dev_err(imx290->dev, "Could not set format registers\n");
+ return ret;
+ }
+
+ return imx290_set_black_level(imx290, format,
+ IMX290_BLACK_LEVEL_DEFAULT, &ret);
}
+/* ----------------------------------------------------------------------------
+ * Controls
+ */
+
static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
{
struct imx290 *imx290 = container_of(ctrl->handler,
struct imx290, ctrls);
+ const struct v4l2_mbus_framefmt *format;
+ struct v4l2_subdev_state *state;
int ret = 0;
+ /*
+ * Return immediately for controls that don't need to be applied to the
+ * device.
+ */
+ if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
+ return 0;
+
/* V4L2 controls values will be applied only when power is already up */
if (!pm_runtime_get_if_in_use(imx290->dev))
return 0;
+ state = v4l2_subdev_get_locked_active_state(&imx290->sd);
+ format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
+
switch (ctrl->id) {
case V4L2_CID_ANALOGUE_GAIN:
ret = imx290_write(imx290, IMX290_GAIN, ctrl->val, NULL);
@@ -535,7 +598,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_TEST_PATTERN:
if (ctrl->val) {
- imx290_write(imx290, IMX290_BLKLEVEL, 0, &ret);
+ imx290_set_black_level(imx290, format, 0, &ret);
usleep_range(10000, 11000);
imx290_write(imx290, IMX290_PGCTRL,
(u8)(IMX290_PGCTRL_REGEN |
@@ -544,20 +607,18 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
} else {
imx290_write(imx290, IMX290_PGCTRL, 0x00, &ret);
usleep_range(10000, 11000);
- if (imx290->bpp == 10)
- imx290_write(imx290, IMX290_BLKLEVEL, 0x3c,
- &ret);
- else /* 12 bits per pixel */
- imx290_write(imx290, IMX290_BLKLEVEL, 0xf0,
- &ret);
+ imx290_set_black_level(imx290, format,
+ IMX290_BLACK_LEVEL_DEFAULT, &ret);
}
break;
+
default:
ret = -EINVAL;
break;
}
- pm_runtime_put(imx290->dev);
+ pm_runtime_mark_last_busy(imx290->dev);
+ pm_runtime_put_autosuspend(imx290->dev);
return ret;
}
@@ -566,14 +627,217 @@ static const struct v4l2_ctrl_ops imx290_ctrl_ops = {
.s_ctrl = imx290_set_ctrl,
};
-static struct v4l2_mbus_framefmt *
-imx290_get_pad_format(struct imx290 *imx290, struct v4l2_subdev_state *state,
- u32 which)
+static const char * const imx290_test_pattern_menu[] = {
+ "Disabled",
+ "Sequence Pattern 1",
+ "Horizontal Color-bar Chart",
+ "Vertical Color-bar Chart",
+ "Sequence Pattern 2",
+ "Gradation Pattern 1",
+ "Gradation Pattern 2",
+ "000/555h Toggle Pattern",
+};
+
+static void imx290_ctrl_update(struct imx290 *imx290,
+ const struct v4l2_mbus_framefmt *format,
+ const struct imx290_mode *mode)
{
- if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
- return &imx290->current_format;
- else
- return v4l2_subdev_get_try_format(&imx290->sd, state, 0);
+ unsigned int hblank = mode->hmax - mode->width;
+ unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
+ s64 link_freq = imx290_link_freqs_ptr(imx290)[mode->link_freq_index];
+ u64 pixel_rate;
+
+ /* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */
+ pixel_rate = link_freq * 2 * imx290->nlanes;
+ do_div(pixel_rate, imx290_format_info(format->code)->bpp);
+
+ __v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
+ __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, pixel_rate);
+
+ __v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 1, hblank);
+ __v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank);
+}
+
+static int imx290_ctrl_init(struct imx290 *imx290)
+{
+ struct v4l2_fwnode_device_properties props;
+ int ret;
+
+ ret = v4l2_fwnode_device_parse(imx290->dev, &props);
+ if (ret < 0)
+ return ret;
+
+ v4l2_ctrl_handler_init(&imx290->ctrls, 9);
+
+ /*
+ * The sensor has an analog gain and a digital gain, both controlled
+ * through a single gain value, expressed in 0.3dB increments. Values
+ * from 0.0dB (0) to 30.0dB (100) apply analog gain only, higher values
+ * up to 72.0dB (240) add further digital gain. Limit the range to
+ * analog gain only, support for digital gain can be added separately
+ * if needed.
+ *
+ * The IMX327 and IMX462 are largely compatible with the IMX290, but
+ * have an analog gain range of 0.0dB to 29.4dB and 42dB of digital
+ * gain. When support for those sensors gets added to the driver, the
+ * gain control should be adjusted accordingly.
+ */
+ v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN, 0, 100, 1, 0);
+
+ v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
+ V4L2_CID_EXPOSURE, 1, IMX290_VMAX_DEFAULT - 2, 1,
+ IMX290_VMAX_DEFAULT - 2);
+
+ /*
+ * Set the link frequency, pixel rate, horizontal blanking and vertical
+ * blanking to hardcoded values, they will be updated by
+ * imx290_ctrl_update().
+ */
+ imx290->link_freq =
+ v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ imx290_link_freqs_num(imx290) - 1, 0,
+ imx290_link_freqs_ptr(imx290));
+ if (imx290->link_freq)
+ imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
+ V4L2_CID_PIXEL_RATE,
+ 1, INT_MAX, 1, 1);
+
+ v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(imx290_test_pattern_menu) - 1,
+ 0, 0, imx290_test_pattern_menu);
+
+ imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
+ V4L2_CID_HBLANK, 1, 1, 1, 1);
+ if (imx290->hblank)
+ imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
+ V4L2_CID_VBLANK, 1, 1, 1, 1);
+ if (imx290->vblank)
+ imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops,
+ &props);
+
+ imx290->sd.ctrl_handler = &imx290->ctrls;
+
+ if (imx290->ctrls.error) {
+ ret = imx290->ctrls.error;
+ v4l2_ctrl_handler_free(&imx290->ctrls);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------------
+ * Subdev operations
+ */
+
+/* Start streaming */
+static int imx290_start_streaming(struct imx290 *imx290,
+ struct v4l2_subdev_state *state)
+{
+ const struct v4l2_mbus_framefmt *format;
+ int ret;
+
+ /* Set init register settings */
+ ret = imx290_set_register_array(imx290, imx290_global_init_settings,
+ ARRAY_SIZE(
+ imx290_global_init_settings));
+ if (ret < 0) {
+ dev_err(imx290->dev, "Could not set init registers\n");
+ return ret;
+ }
+
+ /* Set data lane count */
+ ret = imx290_set_data_lanes(imx290);
+ if (ret < 0) {
+ dev_err(imx290->dev, "Could not set data lanes\n");
+ return ret;
+ }
+
+ /* Apply the register values related to current frame format */
+ format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
+ ret = imx290_setup_format(imx290, format);
+ if (ret < 0) {
+ dev_err(imx290->dev, "Could not set frame format\n");
+ return ret;
+ }
+
+ /* Apply default values of current mode */
+ ret = imx290_set_register_array(imx290, imx290->current_mode->data,
+ imx290->current_mode->data_size);
+ if (ret < 0) {
+ dev_err(imx290->dev, "Could not set current mode\n");
+ return ret;
+ }
+
+ ret = imx290_write(imx290, IMX290_HMAX, imx290->current_mode->hmax,
+ NULL);
+ if (ret)
+ return ret;
+
+ /* Apply customized values from user */
+ ret = __v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler);
+ if (ret) {
+ dev_err(imx290->dev, "Could not sync v4l2 controls\n");
+ return ret;
+ }
+
+ imx290_write(imx290, IMX290_STANDBY, 0x00, &ret);
+
+ msleep(30);
+
+ /* Start streaming */
+ return imx290_write(imx290, IMX290_XMSTA, 0x00, &ret);
+}
+
+/* Stop streaming */
+static int imx290_stop_streaming(struct imx290 *imx290)
+{
+ int ret = 0;
+
+ imx290_write(imx290, IMX290_STANDBY, 0x01, &ret);
+
+ msleep(30);
+
+ return imx290_write(imx290, IMX290_XMSTA, 0x01, &ret);
+}
+
+static int imx290_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct imx290 *imx290 = to_imx290(sd);
+ struct v4l2_subdev_state *state;
+ int ret = 0;
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ if (enable) {
+ ret = pm_runtime_resume_and_get(imx290->dev);
+ if (ret < 0)
+ goto unlock;
+
+ ret = imx290_start_streaming(imx290, state);
+ if (ret) {
+ dev_err(imx290->dev, "Start stream failed\n");
+ pm_runtime_put_sync(imx290->dev);
+ goto unlock;
+ }
+ } else {
+ imx290_stop_streaming(imx290);
+ pm_runtime_mark_last_busy(imx290->dev);
+ pm_runtime_put_autosuspend(imx290->dev);
+ }
+
+unlock:
+ v4l2_subdev_unlock_state(state);
+ return ret;
}
static int imx290_enum_mbus_code(struct v4l2_subdev *sd,
@@ -595,8 +859,7 @@ static int imx290_enum_frame_size(struct v4l2_subdev *sd,
const struct imx290 *imx290 = to_imx290(sd);
const struct imx290_mode *imx290_modes = imx290_modes_ptr(imx290);
- if ((fse->code != imx290_formats[0].code) &&
- (fse->code != imx290_formats[1].code))
+ if (!imx290_format_info(fse->code))
return -EINVAL;
if (fse->index >= imx290_modes_num(imx290))
@@ -610,47 +873,6 @@ static int imx290_enum_frame_size(struct v4l2_subdev *sd,
return 0;
}
-static int imx290_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *fmt)
-{
- struct imx290 *imx290 = to_imx290(sd);
- struct v4l2_mbus_framefmt *framefmt;
-
- mutex_lock(&imx290->lock);
-
- framefmt = imx290_get_pad_format(imx290, sd_state, fmt->which);
- fmt->format = *framefmt;
-
- mutex_unlock(&imx290->lock);
-
- return 0;
-}
-
-static inline u8 imx290_get_link_freq_index(struct imx290 *imx290)
-{
- return imx290->current_mode->link_freq_index;
-}
-
-static s64 imx290_get_link_freq(struct imx290 *imx290)
-{
- u8 index = imx290_get_link_freq_index(imx290);
-
- return *(imx290_link_freqs_ptr(imx290) + index);
-}
-
-static u64 imx290_calc_pixel_rate(struct imx290 *imx290)
-{
- s64 link_freq = imx290_get_link_freq(imx290);
- u8 nlanes = imx290->nlanes;
- u64 pixel_rate;
-
- /* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */
- pixel_rate = link_freq * 2 * nlanes;
- do_div(pixel_rate, imx290->bpp);
- return pixel_rate;
-}
-
static int imx290_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
@@ -658,9 +880,6 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
struct imx290 *imx290 = to_imx290(sd);
const struct imx290_mode *mode;
struct v4l2_mbus_framefmt *format;
- unsigned int i;
-
- mutex_lock(&imx290->lock);
mode = v4l2_find_nearest_size(imx290_modes_ptr(imx290),
imx290_modes_num(imx290), width, height,
@@ -669,48 +888,21 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
fmt->format.width = mode->width;
fmt->format.height = mode->height;
- for (i = 0; i < ARRAY_SIZE(imx290_formats); i++)
- if (imx290_formats[i].code == fmt->format.code)
- break;
-
- if (i >= ARRAY_SIZE(imx290_formats))
- i = 0;
+ if (!imx290_format_info(fmt->format.code))
+ fmt->format.code = imx290_formats[0].code;
- fmt->format.code = imx290_formats[i].code;
fmt->format.field = V4L2_FIELD_NONE;
- format = imx290_get_pad_format(imx290, sd_state, fmt->which);
+ format = v4l2_subdev_get_pad_format(sd, sd_state, 0);
if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
imx290->current_mode = mode;
- imx290->bpp = imx290_formats[i].bpp;
-
- if (imx290->link_freq)
- __v4l2_ctrl_s_ctrl(imx290->link_freq,
- imx290_get_link_freq_index(imx290));
- if (imx290->pixel_rate)
- __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate,
- imx290_calc_pixel_rate(imx290));
-
- if (imx290->hblank) {
- unsigned int hblank = mode->hmax - mode->width;
-
- __v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank,
- 1, hblank);
- }
-
- if (imx290->vblank) {
- unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
- __v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank,
- 1, vblank);
- }
+ imx290_ctrl_update(imx290, &fmt->format, mode);
}
*format = fmt->format;
- mutex_unlock(&imx290->lock);
-
return 0;
}
@@ -718,14 +910,11 @@ static int imx290_get_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
- struct imx290 *imx290 = to_imx290(sd);
struct v4l2_mbus_framefmt *format;
switch (sel->target) {
case V4L2_SEL_TGT_CROP: {
- format = imx290_get_pad_format(imx290, sd_state, sel->which);
-
- mutex_lock(&imx290->lock);
+ format = v4l2_subdev_get_pad_format(sd, sd_state, 0);
sel->r.top = IMX920_PIXEL_ARRAY_MARGIN_TOP
+ (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT - format->height) / 2;
@@ -734,7 +923,6 @@ static int imx290_get_selection(struct v4l2_subdev *sd,
sel->r.width = format->width;
sel->r.height = format->height;
- mutex_unlock(&imx290->lock);
return 0;
}
@@ -763,179 +951,116 @@ static int imx290_get_selection(struct v4l2_subdev *sd,
static int imx290_entity_init_cfg(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state)
{
- struct v4l2_subdev_format fmt = { 0 };
-
- fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
- fmt.format.width = 1920;
- fmt.format.height = 1080;
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ .format = {
+ .width = 1920,
+ .height = 1080,
+ },
+ };
imx290_set_fmt(subdev, sd_state, &fmt);
return 0;
}
-static int imx290_write_current_format(struct imx290 *imx290)
-{
- int ret;
+static const struct v4l2_subdev_video_ops imx290_video_ops = {
+ .s_stream = imx290_set_stream,
+};
- switch (imx290->current_format.code) {
- case MEDIA_BUS_FMT_SRGGB10_1X10:
- ret = imx290_set_register_array(imx290, imx290_10bit_settings,
- ARRAY_SIZE(
- imx290_10bit_settings));
- if (ret < 0) {
- dev_err(imx290->dev, "Could not set format registers\n");
- return ret;
- }
- break;
- case MEDIA_BUS_FMT_SRGGB12_1X12:
- ret = imx290_set_register_array(imx290, imx290_12bit_settings,
- ARRAY_SIZE(
- imx290_12bit_settings));
- if (ret < 0) {
- dev_err(imx290->dev, "Could not set format registers\n");
- return ret;
- }
- break;
- default:
- dev_err(imx290->dev, "Unknown pixel format\n");
- return -EINVAL;
- }
+static const struct v4l2_subdev_pad_ops imx290_pad_ops = {
+ .init_cfg = imx290_entity_init_cfg,
+ .enum_mbus_code = imx290_enum_mbus_code,
+ .enum_frame_size = imx290_enum_frame_size,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = imx290_set_fmt,
+ .get_selection = imx290_get_selection,
+};
- return 0;
-}
+static const struct v4l2_subdev_ops imx290_subdev_ops = {
+ .video = &imx290_video_ops,
+ .pad = &imx290_pad_ops,
+};
-/* Start streaming */
-static int imx290_start_streaming(struct imx290 *imx290)
+static const struct media_entity_operations imx290_subdev_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static int imx290_subdev_init(struct imx290 *imx290)
{
+ struct i2c_client *client = to_i2c_client(imx290->dev);
+ const struct v4l2_mbus_framefmt *format;
+ struct v4l2_subdev_state *state;
int ret;
- /* Set init register settings */
- ret = imx290_set_register_array(imx290, imx290_global_init_settings,
- ARRAY_SIZE(
- imx290_global_init_settings));
- if (ret < 0) {
- dev_err(imx290->dev, "Could not set init registers\n");
- return ret;
- }
+ imx290->current_mode = &imx290_modes_ptr(imx290)[0];
- /* Apply the register values related to current frame format */
- ret = imx290_write_current_format(imx290);
+ v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
+ imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ imx290->sd.dev = imx290->dev;
+ imx290->sd.entity.ops = &imx290_subdev_entity_ops;
+ imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ imx290->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&imx290->sd.entity, 1, &imx290->pad);
if (ret < 0) {
- dev_err(imx290->dev, "Could not set frame format\n");
+ dev_err(imx290->dev, "Could not register media entity\n");
return ret;
}
- /* Apply default values of current mode */
- ret = imx290_set_register_array(imx290, imx290->current_mode->data,
- imx290->current_mode->data_size);
+ ret = imx290_ctrl_init(imx290);
if (ret < 0) {
- dev_err(imx290->dev, "Could not set current mode\n");
- return ret;
+ dev_err(imx290->dev, "Control initialization error %d\n", ret);
+ goto err_media;
}
- ret = imx290_write(imx290, IMX290_HMAX, imx290->current_mode->hmax,
- NULL);
- if (ret)
- return ret;
+ imx290->sd.state_lock = imx290->ctrls.lock;
- /* Apply customized values from user */
- ret = v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler);
- if (ret) {
- dev_err(imx290->dev, "Could not sync v4l2 controls\n");
- return ret;
+ ret = v4l2_subdev_init_finalize(&imx290->sd);
+ if (ret < 0) {
+ dev_err(imx290->dev, "subdev initialization error %d\n", ret);
+ goto err_ctrls;
}
- imx290_write(imx290, IMX290_STANDBY, 0x00, &ret);
-
- msleep(30);
-
- /* Start streaming */
- return imx290_write(imx290, IMX290_XMSTA, 0x00, &ret);
-}
-
-static int imx290_set_stream(struct v4l2_subdev *sd, int enable)
-{
- struct imx290 *imx290 = to_imx290(sd);
- int ret = 0;
+ state = v4l2_subdev_lock_and_get_active_state(&imx290->sd);
+ format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
+ imx290_ctrl_update(imx290, format, imx290->current_mode);
+ v4l2_subdev_unlock_state(state);
- if (enable) {
- ret = pm_runtime_resume_and_get(imx290->dev);
- if (ret < 0)
- goto unlock_and_return;
-
- ret = imx290_start_streaming(imx290);
- if (ret) {
- dev_err(imx290->dev, "Start stream failed\n");
- pm_runtime_put(imx290->dev);
- goto unlock_and_return;
- }
- } else {
- imx290_stop_streaming(imx290);
- pm_runtime_put(imx290->dev);
- }
-
-unlock_and_return:
+ return 0;
+err_ctrls:
+ v4l2_ctrl_handler_free(&imx290->ctrls);
+err_media:
+ media_entity_cleanup(&imx290->sd.entity);
return ret;
}
-static int imx290_get_regulators(struct device *dev, struct imx290 *imx290)
+static void imx290_subdev_cleanup(struct imx290 *imx290)
{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(imx290->supplies); i++)
- imx290->supplies[i].supply = imx290_supply_name[i];
-
- return devm_regulator_bulk_get(dev, ARRAY_SIZE(imx290->supplies),
- imx290->supplies);
+ v4l2_subdev_cleanup(&imx290->sd);
+ media_entity_cleanup(&imx290->sd.entity);
+ v4l2_ctrl_handler_free(&imx290->ctrls);
}
-static int imx290_set_data_lanes(struct imx290 *imx290)
-{
- int ret = 0, laneval, frsel;
-
- switch (imx290->nlanes) {
- case 2:
- laneval = 0x01;
- frsel = 0x02;
- break;
- case 4:
- laneval = 0x03;
- frsel = 0x01;
- break;
- default:
- /*
- * We should never hit this since the data lane count is
- * validated in probe itself
- */
- dev_err(imx290->dev, "Lane configuration not supported\n");
- return -EINVAL;
- }
-
- imx290_write(imx290, IMX290_PHY_LANE_NUM, laneval, &ret);
- imx290_write(imx290, IMX290_CSI_LANE_MODE, laneval, &ret);
- imx290_write(imx290, IMX290_FR_FDG_SEL, frsel, &ret);
-
- return ret;
-}
+/* ----------------------------------------------------------------------------
+ * Power management
+ */
-static int imx290_power_on(struct device *dev)
+static int imx290_power_on(struct imx290 *imx290)
{
- struct v4l2_subdev *sd = dev_get_drvdata(dev);
- struct imx290 *imx290 = to_imx290(sd);
int ret;
ret = clk_prepare_enable(imx290->xclk);
if (ret) {
- dev_err(dev, "Failed to enable clock\n");
+ dev_err(imx290->dev, "Failed to enable clock\n");
return ret;
}
ret = regulator_bulk_enable(ARRAY_SIZE(imx290->supplies),
imx290->supplies);
if (ret) {
- dev_err(dev, "Failed to enable regulators\n");
+ dev_err(imx290->dev, "Failed to enable regulators\n");
clk_disable_unprepare(imx290->xclk);
return ret;
}
@@ -944,123 +1069,86 @@ static int imx290_power_on(struct device *dev)
gpiod_set_value_cansleep(imx290->rst_gpio, 0);
usleep_range(30000, 31000);
- /* Set data lane count */
- imx290_set_data_lanes(imx290);
-
return 0;
}
-static int imx290_power_off(struct device *dev)
+static void imx290_power_off(struct imx290 *imx290)
{
- struct v4l2_subdev *sd = dev_get_drvdata(dev);
- struct imx290 *imx290 = to_imx290(sd);
-
clk_disable_unprepare(imx290->xclk);
gpiod_set_value_cansleep(imx290->rst_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(imx290->supplies), imx290->supplies);
+}
+
+static int imx290_runtime_resume(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct imx290 *imx290 = to_imx290(sd);
+
+ return imx290_power_on(imx290);
+}
+
+static int imx290_runtime_suspend(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct imx290 *imx290 = to_imx290(sd);
+
+ imx290_power_off(imx290);
return 0;
}
static const struct dev_pm_ops imx290_pm_ops = {
- SET_RUNTIME_PM_OPS(imx290_power_off, imx290_power_on, NULL)
-};
-
-static const struct v4l2_subdev_video_ops imx290_video_ops = {
- .s_stream = imx290_set_stream,
+ SET_RUNTIME_PM_OPS(imx290_runtime_suspend, imx290_runtime_resume, NULL)
};
-static const struct v4l2_subdev_pad_ops imx290_pad_ops = {
- .init_cfg = imx290_entity_init_cfg,
- .enum_mbus_code = imx290_enum_mbus_code,
- .enum_frame_size = imx290_enum_frame_size,
- .get_fmt = imx290_get_fmt,
- .set_fmt = imx290_set_fmt,
- .get_selection = imx290_get_selection,
-};
+/* ----------------------------------------------------------------------------
+ * Probe & remove
+ */
-static const struct v4l2_subdev_ops imx290_subdev_ops = {
- .video = &imx290_video_ops,
- .pad = &imx290_pad_ops,
+static const struct regmap_config imx290_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
};
-static const struct media_entity_operations imx290_subdev_entity_ops = {
- .link_validate = v4l2_subdev_link_validate,
+static const char * const imx290_supply_name[IMX290_NUM_SUPPLIES] = {
+ "vdda",
+ "vddd",
+ "vdddo",
};
-static int imx290_ctrl_init(struct imx290 *imx290)
+static int imx290_get_regulators(struct device *dev, struct imx290 *imx290)
{
- struct v4l2_fwnode_device_properties props;
- unsigned int blank;
- int ret;
-
- ret = v4l2_fwnode_device_parse(imx290->dev, &props);
- if (ret < 0)
- return ret;
-
- v4l2_ctrl_handler_init(&imx290->ctrls, 9);
- imx290->ctrls.lock = &imx290->lock;
-
- /*
- * The sensor has an analog gain and a digital gain, both controlled
- * through a single gain value, expressed in 0.3dB increments. Values
- * from 0.0dB (0) to 30.0dB (100) apply analog gain only, higher values
- * up to 72.0dB (240) add further digital gain. Limit the range to
- * analog gain only, support for digital gain can be added separately
- * if needed.
- *
- * The IMX327 and IMX462 are largely compatible with the IMX290, but
- * have an analog gain range of 0.0dB to 29.4dB and 42dB of digital
- * gain. When support for those sensors gets added to the driver, the
- * gain control should be adjusted accordingly.
- */
- v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
- V4L2_CID_ANALOGUE_GAIN, 0, 100, 1, 0);
-
- v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
- V4L2_CID_EXPOSURE, 1, IMX290_VMAX_DEFAULT - 2, 1,
- IMX290_VMAX_DEFAULT - 2);
-
- imx290->link_freq =
- v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops,
- V4L2_CID_LINK_FREQ,
- imx290_link_freqs_num(imx290) - 1, 0,
- imx290_link_freqs_ptr(imx290));
- if (imx290->link_freq)
- imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
-
- imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
- V4L2_CID_PIXEL_RATE,
- 1, INT_MAX, 1,
- imx290_calc_pixel_rate(imx290));
+ unsigned int i;
- v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops,
- V4L2_CID_TEST_PATTERN,
- ARRAY_SIZE(imx290_test_pattern_menu) - 1,
- 0, 0, imx290_test_pattern_menu);
+ for (i = 0; i < ARRAY_SIZE(imx290->supplies); i++)
+ imx290->supplies[i].supply = imx290_supply_name[i];
- blank = imx290->current_mode->hmax - imx290->current_mode->width;
- imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
- V4L2_CID_HBLANK, blank, blank, 1,
- blank);
- if (imx290->hblank)
- imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ return devm_regulator_bulk_get(dev, ARRAY_SIZE(imx290->supplies),
+ imx290->supplies);
+}
- blank = IMX290_VMAX_DEFAULT - imx290->current_mode->height;
- imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
- V4L2_CID_VBLANK, blank, blank, 1,
- blank);
- if (imx290->vblank)
- imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+static int imx290_init_clk(struct imx290 *imx290)
+{
+ u32 xclk_freq;
+ int ret;
- v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops,
- &props);
+ ret = fwnode_property_read_u32(dev_fwnode(imx290->dev),
+ "clock-frequency", &xclk_freq);
+ if (ret) {
+ dev_err(imx290->dev, "Could not get xclk frequency\n");
+ return ret;
+ }
- imx290->sd.ctrl_handler = &imx290->ctrls;
+ /* external clock must be 37.125 MHz */
+ if (xclk_freq != 37125000) {
+ dev_err(imx290->dev, "External clock frequency %u is not supported\n",
+ xclk_freq);
+ return -EINVAL;
+ }
- if (imx290->ctrls.error) {
- ret = imx290->ctrls.error;
- v4l2_ctrl_handler_free(&imx290->ctrls);
+ ret = clk_set_rate(imx290->xclk, xclk_freq);
+ if (ret) {
+ dev_err(imx290->dev, "Could not set xclk frequency\n");
return ret;
}
@@ -1089,171 +1177,159 @@ static s64 imx290_check_link_freqs(const struct imx290 *imx290,
return 0;
}
-static int imx290_probe(struct i2c_client *client)
+static int imx290_parse_dt(struct imx290 *imx290)
{
- struct device *dev = &client->dev;
- struct fwnode_handle *endpoint;
/* Only CSI2 is supported for now: */
struct v4l2_fwnode_endpoint ep = {
.bus_type = V4L2_MBUS_CSI2_DPHY
};
- struct imx290 *imx290;
- u32 xclk_freq;
- s64 fq;
+ struct fwnode_handle *endpoint;
int ret;
+ s64 fq;
- imx290 = devm_kzalloc(dev, sizeof(*imx290), GFP_KERNEL);
- if (!imx290)
- return -ENOMEM;
-
- imx290->dev = dev;
- imx290->regmap = devm_regmap_init_i2c(client, &imx290_regmap_config);
- if (IS_ERR(imx290->regmap)) {
- dev_err(dev, "Unable to initialize I2C\n");
- return -ENODEV;
- }
-
- endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
+ endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(imx290->dev), NULL);
if (!endpoint) {
- dev_err(dev, "Endpoint node not found\n");
+ dev_err(imx290->dev, "Endpoint node not found\n");
return -EINVAL;
}
ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep);
fwnode_handle_put(endpoint);
if (ret == -ENXIO) {
- dev_err(dev, "Unsupported bus type, should be CSI2\n");
- goto free_err;
+ dev_err(imx290->dev, "Unsupported bus type, should be CSI2\n");
+ goto done;
} else if (ret) {
- dev_err(dev, "Parsing endpoint node failed\n");
- goto free_err;
+ dev_err(imx290->dev, "Parsing endpoint node failed\n");
+ goto done;
}
/* Get number of data lanes */
imx290->nlanes = ep.bus.mipi_csi2.num_data_lanes;
if (imx290->nlanes != 2 && imx290->nlanes != 4) {
- dev_err(dev, "Invalid data lanes: %d\n", imx290->nlanes);
+ dev_err(imx290->dev, "Invalid data lanes: %d\n", imx290->nlanes);
ret = -EINVAL;
- goto free_err;
+ goto done;
}
- dev_dbg(dev, "Using %u data lanes\n", imx290->nlanes);
+ dev_dbg(imx290->dev, "Using %u data lanes\n", imx290->nlanes);
if (!ep.nr_of_link_frequencies) {
- dev_err(dev, "link-frequency property not found in DT\n");
+ dev_err(imx290->dev, "link-frequency property not found in DT\n");
ret = -EINVAL;
- goto free_err;
+ goto done;
}
/* Check that link frequences for all the modes are in device tree */
fq = imx290_check_link_freqs(imx290, &ep);
if (fq) {
- dev_err(dev, "Link frequency of %lld is not supported\n", fq);
+ dev_err(imx290->dev, "Link frequency of %lld is not supported\n",
+ fq);
ret = -EINVAL;
- goto free_err;
+ goto done;
}
- /* get system clock (xclk) */
- imx290->xclk = devm_clk_get(dev, "xclk");
- if (IS_ERR(imx290->xclk)) {
- dev_err(dev, "Could not get xclk");
- ret = PTR_ERR(imx290->xclk);
- goto free_err;
- }
+ ret = 0;
- ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
- &xclk_freq);
- if (ret) {
- dev_err(dev, "Could not get xclk frequency\n");
- goto free_err;
- }
+done:
+ v4l2_fwnode_endpoint_free(&ep);
+ return ret;
+}
- /* external clock must be 37.125 MHz */
- if (xclk_freq != 37125000) {
- dev_err(dev, "External clock frequency %u is not supported\n",
- xclk_freq);
- ret = -EINVAL;
- goto free_err;
- }
+static int imx290_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct imx290 *imx290;
+ int ret;
- ret = clk_set_rate(imx290->xclk, xclk_freq);
- if (ret) {
- dev_err(dev, "Could not set xclk frequency\n");
- goto free_err;
+ imx290 = devm_kzalloc(dev, sizeof(*imx290), GFP_KERNEL);
+ if (!imx290)
+ return -ENOMEM;
+
+ imx290->dev = dev;
+ imx290->regmap = devm_regmap_init_i2c(client, &imx290_regmap_config);
+ if (IS_ERR(imx290->regmap)) {
+ dev_err(dev, "Unable to initialize I2C\n");
+ return -ENODEV;
}
+ ret = imx290_parse_dt(imx290);
+ if (ret)
+ return ret;
+
+ /* Acquire resources. */
+ imx290->xclk = devm_clk_get(dev, "xclk");
+ if (IS_ERR(imx290->xclk))
+ return dev_err_probe(dev, PTR_ERR(imx290->xclk),
+ "Could not get xclk");
+
ret = imx290_get_regulators(dev, imx290);
- if (ret < 0) {
- dev_err(dev, "Cannot get regulators\n");
- goto free_err;
- }
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Cannot get regulators\n");
imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_HIGH);
- if (IS_ERR(imx290->rst_gpio)) {
- dev_err(dev, "Cannot get reset gpio\n");
- ret = PTR_ERR(imx290->rst_gpio);
- goto free_err;
- }
+ if (IS_ERR(imx290->rst_gpio))
+ return dev_err_probe(dev, PTR_ERR(imx290->rst_gpio),
+ "Cannot get reset gpio\n");
- mutex_init(&imx290->lock);
+ /* Initialize external clock frequency. */
+ ret = imx290_init_clk(imx290);
+ if (ret)
+ return ret;
/*
- * Initialize the frame format. In particular, imx290->current_mode
- * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() call
- * below relies on these fields.
+ * Enable power management. The driver supports runtime PM, but needs to
+ * work when runtime PM is disabled in the kernel. To that end, power
+ * the sensor on manually here.
*/
- imx290_entity_init_cfg(&imx290->sd, NULL);
-
- ret = imx290_ctrl_init(imx290);
+ ret = imx290_power_on(imx290);
if (ret < 0) {
- dev_err(dev, "Control initialization error %d\n", ret);
- goto free_mutex;
+ dev_err(dev, "Could not power on the device\n");
+ return ret;
}
- v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
- imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- imx290->sd.dev = &client->dev;
- imx290->sd.entity.ops = &imx290_subdev_entity_ops;
- imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ /*
+ * Enable runtime PM with autosuspend. As the device has been powered
+ * manually, mark it as active, and increase the usage count without
+ * resuming the device.
+ */
+ pm_runtime_set_active(dev);
+ pm_runtime_get_noresume(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
- imx290->pad.flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_pads_init(&imx290->sd.entity, 1, &imx290->pad);
- if (ret < 0) {
- dev_err(dev, "Could not register media entity\n");
- goto free_ctrl;
- }
+ /* Initialize the V4L2 subdev. */
+ ret = imx290_subdev_init(imx290);
+ if (ret)
+ goto err_pm;
+ /*
+ * Finally, register the V4L2 subdev. This must be done after
+ * initializing everything as the subdev can be used immediately after
+ * being registered.
+ */
ret = v4l2_async_register_subdev(&imx290->sd);
if (ret < 0) {
dev_err(dev, "Could not register v4l2 device\n");
- goto free_entity;
- }
-
- /* Power on the device to match runtime PM state below */
- ret = imx290_power_on(dev);
- if (ret < 0) {
- dev_err(dev, "Could not power on the device\n");
- goto free_entity;
+ goto err_subdev;
}
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
- pm_runtime_idle(dev);
-
- v4l2_fwnode_endpoint_free(&ep);
+ /*
+ * Decrease the PM usage count. The device will get suspended after the
+ * autosuspend delay, turning the power off.
+ */
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
return 0;
-free_entity:
- media_entity_cleanup(&imx290->sd.entity);
-free_ctrl:
- v4l2_ctrl_handler_free(&imx290->ctrls);
-free_mutex:
- mutex_destroy(&imx290->lock);
-free_err:
- v4l2_fwnode_endpoint_free(&ep);
-
+err_subdev:
+ imx290_subdev_cleanup(imx290);
+err_pm:
+ pm_runtime_disable(dev);
+ pm_runtime_put_noidle(dev);
+ imx290_power_off(imx290);
return ret;
}
@@ -1263,14 +1339,15 @@ static void imx290_remove(struct i2c_client *client)
struct imx290 *imx290 = to_imx290(sd);
v4l2_async_unregister_subdev(sd);
- media_entity_cleanup(&sd->entity);
- v4l2_ctrl_handler_free(sd->ctrl_handler);
-
- mutex_destroy(&imx290->lock);
+ imx290_subdev_cleanup(imx290);
+ /*
+ * Disable runtime PM. In case runtime PM is disabled in the kernel,
+ * make sure to turn power off manually.
+ */
pm_runtime_disable(imx290->dev);
if (!pm_runtime_status_suspended(imx290->dev))
- imx290_power_off(imx290->dev);
+ imx290_power_off(imx290);
pm_runtime_set_suspended(imx290->dev);
}
diff --git a/drivers/media/i2c/imx296.c b/drivers/media/i2c/imx296.c
new file mode 100644
index 000000000000..3c12b6edeac9
--- /dev/null
+++ b/drivers/media/i2c/imx296.c
@@ -0,0 +1,1172 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for IMX296 CMOS Image Sensor from Sony
+ *
+ * Copyright 2019 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#define IMX296_PIXEL_ARRAY_WIDTH 1456
+#define IMX296_PIXEL_ARRAY_HEIGHT 1088
+
+#define IMX296_REG_8BIT(n) ((1 << 16) | (n))
+#define IMX296_REG_16BIT(n) ((2 << 16) | (n))
+#define IMX296_REG_24BIT(n) ((3 << 16) | (n))
+#define IMX296_REG_SIZE_SHIFT 16
+#define IMX296_REG_ADDR_MASK 0xffff
+
+#define IMX296_CTRL00 IMX296_REG_8BIT(0x3000)
+#define IMX296_CTRL00_STANDBY BIT(0)
+#define IMX296_CTRL08 IMX296_REG_8BIT(0x3008)
+#define IMX296_CTRL08_REGHOLD BIT(0)
+#define IMX296_CTRL0A IMX296_REG_8BIT(0x300a)
+#define IMX296_CTRL0A_XMSTA BIT(0)
+#define IMX296_CTRL0B IMX296_REG_8BIT(0x300b)
+#define IMX296_CTRL0B_TRIGEN BIT(0)
+#define IMX296_CTRL0D IMX296_REG_8BIT(0x300d)
+#define IMX296_CTRL0D_WINMODE_ALL (0 << 0)
+#define IMX296_CTRL0D_WINMODE_FD_BINNING (2 << 0)
+#define IMX296_CTRL0D_HADD_ON_BINNING BIT(5)
+#define IMX296_CTRL0D_SAT_CNT BIT(6)
+#define IMX296_CTRL0E IMX296_REG_8BIT(0x300e)
+#define IMX296_CTRL0E_VREVERSE BIT(0)
+#define IMX296_CTRL0E_HREVERSE BIT(1)
+#define IMX296_VMAX IMX296_REG_24BIT(0x3010)
+#define IMX296_HMAX IMX296_REG_16BIT(0x3014)
+#define IMX296_TMDCTRL IMX296_REG_8BIT(0x301d)
+#define IMX296_TMDCTRL_LATCH BIT(0)
+#define IMX296_TMDOUT IMX296_REG_16BIT(0x301e)
+#define IMX296_TMDOUT_MASK 0x3ff
+#define IMX296_WDSEL IMX296_REG_8BIT(0x3021)
+#define IMX296_WDSEL_NORMAL (0 << 0)
+#define IMX296_WDSEL_MULTI_2 (1 << 0)
+#define IMX296_WDSEL_MULTI_4 (3 << 0)
+#define IMX296_BLKLEVELAUTO IMX296_REG_8BIT(0x3022)
+#define IMX296_BLKLEVELAUTO_ON 0x01
+#define IMX296_BLKLEVELAUTO_OFF 0xf0
+#define IMX296_SST IMX296_REG_8BIT(0x3024)
+#define IMX296_SST_EN BIT(0)
+#define IMX296_CTRLTOUT IMX296_REG_8BIT(0x3026)
+#define IMX296_CTRLTOUT_TOUT1SEL_LOW (0 << 0)
+#define IMX296_CTRLTOUT_TOUT1SEL_PULSE (3 << 0)
+#define IMX296_CTRLTOUT_TOUT2SEL_LOW (0 << 2)
+#define IMX296_CTRLTOUT_TOUT2SEL_PULSE (3 << 2)
+#define IMX296_CTRLTRIG IMX296_REG_8BIT(0x3029)
+#define IMX296_CTRLTRIG_TOUT1_SEL_LOW (0 << 0)
+#define IMX296_CTRLTRIG_TOUT1_SEL_PULSE1 (1 << 0)
+#define IMX296_CTRLTRIG_TOUT2_SEL_LOW (0 << 4)
+#define IMX296_CTRLTRIG_TOUT2_SEL_PULSE2 (2 << 4)
+#define IMX296_SYNCSEL IMX296_REG_8BIT(0x3036)
+#define IMX296_SYNCSEL_NORMAL 0xc0
+#define IMX296_SYNCSEL_HIZ 0xf0
+#define IMX296_PULSE1 IMX296_REG_8BIT(0x306d)
+#define IMX296_PULSE1_EN_NOR BIT(0)
+#define IMX296_PULSE1_EN_TRIG BIT(1)
+#define IMX296_PULSE1_POL_HIGH (0 << 2)
+#define IMX296_PULSE1_POL_LOW (1 << 2)
+#define IMX296_PULSE1_UP IMX296_REG_24BIT(0x3070)
+#define IMX296_PULSE1_DN IMX296_REG_24BIT(0x3074)
+#define IMX296_PULSE2 IMX296_REG_8BIT(0x3079)
+#define IMX296_PULSE2_EN_NOR BIT(0)
+#define IMX296_PULSE2_EN_TRIG BIT(1)
+#define IMX296_PULSE2_POL_HIGH (0 << 2)
+#define IMX296_PULSE2_POL_LOW (1 << 2)
+#define IMX296_PULSE2_UP IMX296_REG_24BIT(0x307c)
+#define IMX296_PULSE2_DN IMX296_REG_24BIT(0x3080)
+#define IMX296_INCKSEL(n) IMX296_REG_8BIT(0x3089 + (n))
+#define IMX296_SHS1 IMX296_REG_24BIT(0x308d)
+#define IMX296_SHS2 IMX296_REG_24BIT(0x3090)
+#define IMX296_SHS3 IMX296_REG_24BIT(0x3094)
+#define IMX296_SHS4 IMX296_REG_24BIT(0x3098)
+#define IMX296_VBLANKLP IMX296_REG_8BIT(0x309c)
+#define IMX296_VBLANKLP_NORMAL 0x04
+#define IMX296_VBLANKLP_LOW_POWER 0x2c
+#define IMX296_EXP_CNT IMX296_REG_8BIT(0x30a3)
+#define IMX296_EXP_CNT_RESET BIT(0)
+#define IMX296_EXP_MAX IMX296_REG_16BIT(0x30a6)
+#define IMX296_VINT IMX296_REG_8BIT(0x30aa)
+#define IMX296_VINT_EN BIT(0)
+#define IMX296_LOWLAGTRG IMX296_REG_8BIT(0x30ae)
+#define IMX296_LOWLAGTRG_FAST BIT(0)
+#define IMX296_I2CCTRL IMX296_REG_8BIT(0x30ef)
+#define IMX296_I2CCTRL_I2CACKEN BIT(0)
+
+#define IMX296_SENSOR_INFO IMX296_REG_16BIT(0x3148)
+#define IMX296_SENSOR_INFO_MONO BIT(15)
+#define IMX296_SENSOR_INFO_IMX296LQ 0x4a00
+#define IMX296_SENSOR_INFO_IMX296LL 0xca00
+#define IMX296_S_SHSA IMX296_REG_16BIT(0x31ca)
+#define IMX296_S_SHSB IMX296_REG_16BIT(0x31d2)
+/*
+ * Registers 0x31c8 to 0x31cd, 0x31d0 to 0x31d5, 0x31e2, 0x31e3, 0x31ea and
+ * 0x31eb are related to exposure mode but otherwise not documented.
+ */
+
+#define IMX296_GAINCTRL IMX296_REG_8BIT(0x3200)
+#define IMX296_GAINCTRL_WD_GAIN_MODE_NORMAL 0x01
+#define IMX296_GAINCTRL_WD_GAIN_MODE_MULTI 0x41
+#define IMX296_GAIN IMX296_REG_16BIT(0x3204)
+#define IMX296_GAIN_MIN 0
+#define IMX296_GAIN_MAX 480
+#define IMX296_GAIN1 IMX296_REG_16BIT(0x3208)
+#define IMX296_GAIN2 IMX296_REG_16BIT(0x320c)
+#define IMX296_GAIN3 IMX296_REG_16BIT(0x3210)
+#define IMX296_GAINDLY IMX296_REG_8BIT(0x3212)
+#define IMX296_GAINDLY_NONE 0x08
+#define IMX296_GAINDLY_1FRAME 0x09
+#define IMX296_PGCTRL IMX296_REG_8BIT(0x3238)
+#define IMX296_PGCTRL_REGEN BIT(0)
+#define IMX296_PGCTRL_THRU BIT(1)
+#define IMX296_PGCTRL_CLKEN BIT(2)
+#define IMX296_PGCTRL_MODE(n) ((n) << 3)
+#define IMX296_PGHPOS IMX296_REG_16BIT(0x3239)
+#define IMX296_PGVPOS IMX296_REG_16BIT(0x323c)
+#define IMX296_PGHPSTEP IMX296_REG_8BIT(0x323e)
+#define IMX296_PGVPSTEP IMX296_REG_8BIT(0x323f)
+#define IMX296_PGHPNUM IMX296_REG_8BIT(0x3240)
+#define IMX296_PGVPNUM IMX296_REG_8BIT(0x3241)
+#define IMX296_PGDATA1 IMX296_REG_16BIT(0x3244)
+#define IMX296_PGDATA2 IMX296_REG_16BIT(0x3246)
+#define IMX296_PGHGSTEP IMX296_REG_8BIT(0x3249)
+#define IMX296_BLKLEVEL IMX296_REG_16BIT(0x3254)
+
+#define IMX296_FID0_ROI IMX296_REG_8BIT(0x3300)
+#define IMX296_FID0_ROIH1ON BIT(0)
+#define IMX296_FID0_ROIV1ON BIT(1)
+#define IMX296_FID0_ROIPH1 IMX296_REG_16BIT(0x3310)
+#define IMX296_FID0_ROIPV1 IMX296_REG_16BIT(0x3312)
+#define IMX296_FID0_ROIWH1 IMX296_REG_16BIT(0x3314)
+#define IMX296_FID0_ROIWH1_MIN 80
+#define IMX296_FID0_ROIWV1 IMX296_REG_16BIT(0x3316)
+#define IMX296_FID0_ROIWV1_MIN 4
+
+#define IMX296_CM_HSST_STARTTMG IMX296_REG_16BIT(0x4018)
+#define IMX296_CM_HSST_ENDTMG IMX296_REG_16BIT(0x401a)
+#define IMX296_DA_HSST_STARTTMG IMX296_REG_16BIT(0x404d)
+#define IMX296_DA_HSST_ENDTMG IMX296_REG_16BIT(0x4050)
+#define IMX296_LM_HSST_STARTTMG IMX296_REG_16BIT(0x4094)
+#define IMX296_LM_HSST_ENDTMG IMX296_REG_16BIT(0x4096)
+#define IMX296_SST_SIEASTA1_SET IMX296_REG_8BIT(0x40c9)
+#define IMX296_SST_SIEASTA1PRE_1U IMX296_REG_16BIT(0x40cc)
+#define IMX296_SST_SIEASTA1PRE_1D IMX296_REG_16BIT(0x40ce)
+#define IMX296_SST_SIEASTA1PRE_2U IMX296_REG_16BIT(0x40d0)
+#define IMX296_SST_SIEASTA1PRE_2D IMX296_REG_16BIT(0x40d2)
+#define IMX296_HSST IMX296_REG_8BIT(0x40dc)
+#define IMX296_HSST_EN BIT(2)
+
+#define IMX296_CKREQSEL IMX296_REG_8BIT(0x4101)
+#define IMX296_CKREQSEL_HS BIT(2)
+#define IMX296_GTTABLENUM IMX296_REG_8BIT(0x4114)
+#define IMX296_CTRL418C IMX296_REG_8BIT(0x418c)
+
+struct imx296_clk_params {
+ unsigned int freq;
+ u8 incksel[4];
+ u8 ctrl418c;
+};
+
+static const struct imx296_clk_params imx296_clk_params[] = {
+ { 37125000, { 0x80, 0x0b, 0x80, 0x08 }, 116 },
+ { 54000000, { 0xb0, 0x0f, 0xb0, 0x0c }, 168 },
+ { 74250000, { 0x80, 0x0f, 0x80, 0x0c }, 232 },
+};
+
+static const char * const imx296_supply_names[] = {
+ "dvdd",
+ "ovdd",
+ "avdd",
+};
+
+struct imx296 {
+ struct device *dev;
+ struct clk *clk;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(imx296_supply_names)];
+ struct gpio_desc *reset;
+ struct regmap *regmap;
+
+ const struct imx296_clk_params *clk_params;
+ bool mono;
+
+ bool streaming;
+
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+
+ struct v4l2_ctrl_handler ctrls;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *vblank;
+};
+
+static inline struct imx296 *to_imx296(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct imx296, subdev);
+}
+
+static int imx296_read(struct imx296 *sensor, u32 addr)
+{
+ u8 data[3] = { 0, 0, 0 };
+ int ret;
+
+ ret = regmap_raw_read(sensor->regmap, addr & IMX296_REG_ADDR_MASK, data,
+ (addr >> IMX296_REG_SIZE_SHIFT) & 3);
+ if (ret < 0)
+ return ret;
+
+ return (data[2] << 16) | (data[1] << 8) | data[0];
+}
+
+static int imx296_write(struct imx296 *sensor, u32 addr, u32 value, int *err)
+{
+ u8 data[3] = { value & 0xff, (value >> 8) & 0xff, value >> 16 };
+ int ret;
+
+ if (err && *err)
+ return *err;
+
+ ret = regmap_raw_write(sensor->regmap, addr & IMX296_REG_ADDR_MASK,
+ data, (addr >> IMX296_REG_SIZE_SHIFT) & 3);
+ if (ret < 0) {
+ dev_err(sensor->dev, "%u-bit write to 0x%04x failed: %d\n",
+ ((addr >> IMX296_REG_SIZE_SHIFT) & 3) * 8,
+ addr & IMX296_REG_ADDR_MASK, ret);
+ if (err)
+ *err = ret;
+ }
+
+ return ret;
+}
+
+static int imx296_power_on(struct imx296 *sensor)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(sensor->supplies),
+ sensor->supplies);
+ if (ret < 0)
+ return ret;
+
+ udelay(1);
+
+ ret = gpiod_direction_output(sensor->reset, 0);
+ if (ret < 0)
+ goto err_supply;
+
+ udelay(1);
+
+ ret = clk_prepare_enable(sensor->clk);
+ if (ret < 0)
+ goto err_reset;
+
+ /*
+ * The documentation doesn't explicitly say how much time is required
+ * after providing a clock and before starting I2C communication. It
+ * mentions a delay of 20µs in 4-wire mode, but tests showed that a
+ * delay of 100µs resulted in I2C communication failures, while 500µs
+ * seems to be enough. Be conservative.
+ */
+ usleep_range(1000, 2000);
+
+ return 0;
+
+err_reset:
+ gpiod_direction_output(sensor->reset, 1);
+err_supply:
+ regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies);
+ return ret;
+}
+
+static void imx296_power_off(struct imx296 *sensor)
+{
+ clk_disable_unprepare(sensor->clk);
+ gpiod_direction_output(sensor->reset, 1);
+ regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies);
+}
+
+/* -----------------------------------------------------------------------------
+ * Controls
+ */
+
+static const char * const imx296_test_pattern_menu[] = {
+ "Disabled",
+ "Multiple Pixels",
+ "Sequence 1",
+ "Sequence 2",
+ "Gradient",
+ "Row",
+ "Column",
+ "Cross",
+ "Stripe",
+ "Checks",
+};
+
+static int imx296_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct imx296 *sensor = container_of(ctrl->handler, struct imx296, ctrls);
+ const struct v4l2_mbus_framefmt *format;
+ struct v4l2_subdev_state *state;
+ unsigned int vmax;
+ int ret = 0;
+
+ if (!sensor->streaming)
+ return 0;
+
+ state = v4l2_subdev_get_locked_active_state(&sensor->subdev);
+ format = v4l2_subdev_get_pad_format(&sensor->subdev, state, 0);
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ /* Clamp the exposure value to VMAX. */
+ vmax = format->height + sensor->vblank->cur.val;
+ ctrl->val = min_t(int, ctrl->val, vmax);
+ imx296_write(sensor, IMX296_SHS1, vmax - ctrl->val, &ret);
+ break;
+
+ case V4L2_CID_ANALOGUE_GAIN:
+ imx296_write(sensor, IMX296_GAIN, ctrl->val, &ret);
+ break;
+
+ case V4L2_CID_VBLANK:
+ imx296_write(sensor, IMX296_VMAX, format->height + ctrl->val,
+ &ret);
+ break;
+
+ case V4L2_CID_TEST_PATTERN:
+ if (ctrl->val) {
+ imx296_write(sensor, IMX296_PGHPOS, 8, &ret);
+ imx296_write(sensor, IMX296_PGVPOS, 8, &ret);
+ imx296_write(sensor, IMX296_PGHPSTEP, 8, &ret);
+ imx296_write(sensor, IMX296_PGVPSTEP, 8, &ret);
+ imx296_write(sensor, IMX296_PGHPNUM, 100, &ret);
+ imx296_write(sensor, IMX296_PGVPNUM, 100, &ret);
+ imx296_write(sensor, IMX296_PGDATA1, 0x300, &ret);
+ imx296_write(sensor, IMX296_PGDATA2, 0x100, &ret);
+ imx296_write(sensor, IMX296_PGHGSTEP, 0, &ret);
+ imx296_write(sensor, IMX296_BLKLEVEL, 0, &ret);
+ imx296_write(sensor, IMX296_BLKLEVELAUTO,
+ IMX296_BLKLEVELAUTO_OFF, &ret);
+ imx296_write(sensor, IMX296_PGCTRL,
+ IMX296_PGCTRL_REGEN |
+ IMX296_PGCTRL_CLKEN |
+ IMX296_PGCTRL_MODE(ctrl->val - 1), &ret);
+ } else {
+ imx296_write(sensor, IMX296_PGCTRL,
+ IMX296_PGCTRL_CLKEN, &ret);
+ imx296_write(sensor, IMX296_BLKLEVEL, 0x3c, &ret);
+ imx296_write(sensor, IMX296_BLKLEVELAUTO,
+ IMX296_BLKLEVELAUTO_ON, &ret);
+ }
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops imx296_ctrl_ops = {
+ .s_ctrl = imx296_s_ctrl,
+};
+
+static int imx296_ctrls_init(struct imx296 *sensor)
+{
+ struct v4l2_fwnode_device_properties props;
+ unsigned int hblank;
+ int ret;
+
+ ret = v4l2_fwnode_device_parse(sensor->dev, &props);
+ if (ret < 0)
+ return ret;
+
+ v4l2_ctrl_handler_init(&sensor->ctrls, 9);
+
+ v4l2_ctrl_new_std(&sensor->ctrls, &imx296_ctrl_ops,
+ V4L2_CID_EXPOSURE, 1, 1048575, 1, 1104);
+ v4l2_ctrl_new_std(&sensor->ctrls, &imx296_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN, IMX296_GAIN_MIN,
+ IMX296_GAIN_MAX, 1, IMX296_GAIN_MIN);
+
+ /*
+ * Horizontal blanking is controlled through the HMAX register, which
+ * contains a line length in INCK clock units. The INCK frequency is
+ * fixed to 74.25 MHz. The HMAX value is currently fixed to 1100,
+ * convert it to a number of pixels based on the nominal pixel rate.
+ */
+ hblank = 1100 * 1188000000ULL / 10 / 74250000
+ - IMX296_PIXEL_ARRAY_WIDTH;
+ sensor->hblank = v4l2_ctrl_new_std(&sensor->ctrls, &imx296_ctrl_ops,
+ V4L2_CID_HBLANK, hblank, hblank, 1,
+ hblank);
+ if (sensor->hblank)
+ sensor->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ sensor->vblank = v4l2_ctrl_new_std(&sensor->ctrls, &imx296_ctrl_ops,
+ V4L2_CID_VBLANK, 30,
+ 1048575 - IMX296_PIXEL_ARRAY_HEIGHT,
+ 1, 30);
+ /*
+ * The sensor calculates the MIPI timings internally to achieve a bit
+ * rate between 1122 and 1198 Mbps. The exact value is unfortunately not
+ * reported, at least according to the documentation. Report a nominal
+ * rate of 1188 Mbps as that is used by the datasheet in multiple
+ * examples.
+ */
+ v4l2_ctrl_new_std(&sensor->ctrls, NULL, V4L2_CID_PIXEL_RATE,
+ 1122000000 / 10, 1198000000 / 10, 1, 1188000000 / 10);
+ v4l2_ctrl_new_std_menu_items(&sensor->ctrls, &imx296_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(imx296_test_pattern_menu) - 1,
+ 0, 0, imx296_test_pattern_menu);
+
+ v4l2_ctrl_new_fwnode_properties(&sensor->ctrls, &imx296_ctrl_ops,
+ &props);
+
+ if (sensor->ctrls.error) {
+ dev_err(sensor->dev, "failed to add controls (%d)\n",
+ sensor->ctrls.error);
+ v4l2_ctrl_handler_free(&sensor->ctrls);
+ return sensor->ctrls.error;
+ }
+
+ sensor->subdev.ctrl_handler = &sensor->ctrls;
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdev Operations
+ */
+
+/*
+ * This table is extracted from vendor data that is entirely undocumented. The
+ * first register write is required to activate the CSI-2 output. The other
+ * entries may or may not be optional?
+ */
+static const struct {
+ unsigned int reg;
+ unsigned int value;
+} imx296_init_table[] = {
+ { IMX296_REG_8BIT(0x3005), 0xf0 },
+ { IMX296_REG_8BIT(0x309e), 0x04 },
+ { IMX296_REG_8BIT(0x30a0), 0x04 },
+ { IMX296_REG_8BIT(0x30a1), 0x3c },
+ { IMX296_REG_8BIT(0x30a4), 0x5f },
+ { IMX296_REG_8BIT(0x30a8), 0x91 },
+ { IMX296_REG_8BIT(0x30ac), 0x28 },
+ { IMX296_REG_8BIT(0x30af), 0x09 },
+ { IMX296_REG_8BIT(0x30df), 0x00 },
+ { IMX296_REG_8BIT(0x3165), 0x00 },
+ { IMX296_REG_8BIT(0x3169), 0x10 },
+ { IMX296_REG_8BIT(0x316a), 0x02 },
+ { IMX296_REG_8BIT(0x31c8), 0xf3 }, /* Exposure-related */
+ { IMX296_REG_8BIT(0x31d0), 0xf4 }, /* Exposure-related */
+ { IMX296_REG_8BIT(0x321a), 0x00 },
+ { IMX296_REG_8BIT(0x3226), 0x02 },
+ { IMX296_REG_8BIT(0x3256), 0x01 },
+ { IMX296_REG_8BIT(0x3541), 0x72 },
+ { IMX296_REG_8BIT(0x3516), 0x77 },
+ { IMX296_REG_8BIT(0x350b), 0x7f },
+ { IMX296_REG_8BIT(0x3758), 0xa3 },
+ { IMX296_REG_8BIT(0x3759), 0x00 },
+ { IMX296_REG_8BIT(0x375a), 0x85 },
+ { IMX296_REG_8BIT(0x375b), 0x00 },
+ { IMX296_REG_8BIT(0x3832), 0xf5 },
+ { IMX296_REG_8BIT(0x3833), 0x00 },
+ { IMX296_REG_8BIT(0x38a2), 0xf6 },
+ { IMX296_REG_8BIT(0x38a3), 0x00 },
+ { IMX296_REG_8BIT(0x3a00), 0x80 },
+ { IMX296_REG_8BIT(0x3d48), 0xa3 },
+ { IMX296_REG_8BIT(0x3d49), 0x00 },
+ { IMX296_REG_8BIT(0x3d4a), 0x85 },
+ { IMX296_REG_8BIT(0x3d4b), 0x00 },
+ { IMX296_REG_8BIT(0x400e), 0x58 },
+ { IMX296_REG_8BIT(0x4014), 0x1c },
+ { IMX296_REG_8BIT(0x4041), 0x2a },
+ { IMX296_REG_8BIT(0x40a2), 0x06 },
+ { IMX296_REG_8BIT(0x40c1), 0xf6 },
+ { IMX296_REG_8BIT(0x40c7), 0x0f },
+ { IMX296_REG_8BIT(0x40c8), 0x00 },
+ { IMX296_REG_8BIT(0x4174), 0x00 },
+};
+
+static int imx296_setup(struct imx296 *sensor, struct v4l2_subdev_state *state)
+{
+ const struct v4l2_mbus_framefmt *format;
+ const struct v4l2_rect *crop;
+ unsigned int i;
+ int ret = 0;
+
+ format = v4l2_subdev_get_pad_format(&sensor->subdev, state, 0);
+ crop = v4l2_subdev_get_pad_crop(&sensor->subdev, state, 0);
+
+ for (i = 0; i < ARRAY_SIZE(imx296_init_table); ++i)
+ imx296_write(sensor, imx296_init_table[i].reg,
+ imx296_init_table[i].value, &ret);
+
+ if (crop->width != IMX296_PIXEL_ARRAY_WIDTH ||
+ crop->height != IMX296_PIXEL_ARRAY_HEIGHT) {
+ imx296_write(sensor, IMX296_FID0_ROI,
+ IMX296_FID0_ROIH1ON | IMX296_FID0_ROIV1ON, &ret);
+ imx296_write(sensor, IMX296_FID0_ROIPH1, crop->left, &ret);
+ imx296_write(sensor, IMX296_FID0_ROIPV1, crop->top, &ret);
+ imx296_write(sensor, IMX296_FID0_ROIWH1, crop->width, &ret);
+ imx296_write(sensor, IMX296_FID0_ROIWV1, crop->height, &ret);
+ } else {
+ imx296_write(sensor, IMX296_FID0_ROI, 0, &ret);
+ }
+
+ imx296_write(sensor, IMX296_CTRL0D,
+ (crop->width != format->width ?
+ IMX296_CTRL0D_HADD_ON_BINNING : 0) |
+ (crop->height != format->height ?
+ IMX296_CTRL0D_WINMODE_FD_BINNING : 0),
+ &ret);
+
+ /*
+ * HMAX and VMAX configure horizontal and vertical blanking by
+ * specifying the total line time and frame time respectively. The line
+ * time is specified in operational clock units (which appears to be the
+ * output of an internal PLL, fixed at 74.25 MHz regardless of the
+ * exernal clock frequency), while the frame time is specified as a
+ * number of lines.
+ *
+ * In the vertical direction the sensor outputs the following:
+ *
+ * - one line for the FS packet
+ * - two lines of embedded data (DT 0x12)
+ * - six null lines (DT 0x10)
+ * - four lines of vertical effective optical black (DT 0x37)
+ * - 8 to 1088 lines of active image data (RAW10, DT 0x2b)
+ * - one line for the FE packet
+ * - 16 or more lines of vertical blanking
+ */
+ imx296_write(sensor, IMX296_HMAX, 1100, &ret);
+ imx296_write(sensor, IMX296_VMAX,
+ format->height + sensor->vblank->cur.val, &ret);
+
+ for (i = 0; i < ARRAY_SIZE(sensor->clk_params->incksel); ++i)
+ imx296_write(sensor, IMX296_INCKSEL(i),
+ sensor->clk_params->incksel[i], &ret);
+ imx296_write(sensor, IMX296_GTTABLENUM, 0xc5, &ret);
+ imx296_write(sensor, IMX296_CTRL418C, sensor->clk_params->ctrl418c,
+ &ret);
+
+ imx296_write(sensor, IMX296_GAINDLY, IMX296_GAINDLY_NONE, &ret);
+ imx296_write(sensor, IMX296_BLKLEVEL, 0x03c, &ret);
+
+ return ret;
+}
+
+static int imx296_stream_on(struct imx296 *sensor)
+{
+ int ret = 0;
+
+ imx296_write(sensor, IMX296_CTRL00, 0, &ret);
+ usleep_range(2000, 5000);
+ imx296_write(sensor, IMX296_CTRL0A, 0, &ret);
+
+ return ret;
+}
+
+static int imx296_stream_off(struct imx296 *sensor)
+{
+ int ret = 0;
+
+ imx296_write(sensor, IMX296_CTRL0A, IMX296_CTRL0A_XMSTA, &ret);
+ imx296_write(sensor, IMX296_CTRL00, IMX296_CTRL00_STANDBY, &ret);
+
+ return ret;
+}
+
+static int imx296_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct imx296 *sensor = to_imx296(sd);
+ struct v4l2_subdev_state *state;
+ int ret;
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ if (!enable) {
+ ret = imx296_stream_off(sensor);
+
+ pm_runtime_mark_last_busy(sensor->dev);
+ pm_runtime_put_autosuspend(sensor->dev);
+
+ sensor->streaming = false;
+
+ goto unlock;
+ }
+
+ ret = pm_runtime_resume_and_get(sensor->dev);
+ if (ret < 0)
+ goto unlock;
+
+ ret = imx296_setup(sensor, state);
+ if (ret < 0)
+ goto err_pm;
+
+ /*
+ * Set streaming to true to ensure __v4l2_ctrl_handler_setup() will set
+ * the controls. The flag is reset to false further down if an error
+ * occurs.
+ */
+ sensor->streaming = true;
+
+ ret = __v4l2_ctrl_handler_setup(&sensor->ctrls);
+ if (ret < 0)
+ goto err_pm;
+
+ ret = imx296_stream_on(sensor);
+ if (ret)
+ goto err_pm;
+
+unlock:
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
+
+err_pm:
+ /*
+ * In case of error, turn the power off synchronously as the device
+ * likely has no other chance to recover.
+ */
+ pm_runtime_put_sync(sensor->dev);
+ sensor->streaming = false;
+
+ goto unlock;
+}
+
+static int imx296_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct imx296 *sensor = to_imx296(sd);
+
+ if (code->index != 0)
+ return -EINVAL;
+
+ code->code = sensor->mono ? MEDIA_BUS_FMT_Y10_1X10
+ : MEDIA_BUS_FMT_SBGGR10_1X10;
+
+ return 0;
+}
+
+static int imx296_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ const struct v4l2_mbus_framefmt *format;
+
+ format = v4l2_subdev_get_pad_format(sd, state, fse->pad);
+
+ if (fse->index >= 2 || fse->code != format->code)
+ return -EINVAL;
+
+ fse->min_width = IMX296_PIXEL_ARRAY_WIDTH / (fse->index + 1);
+ fse->max_width = fse->min_width;
+ fse->min_height = IMX296_PIXEL_ARRAY_HEIGHT / (fse->index + 1);
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static int imx296_get_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *fmt)
+{
+ fmt->format = *v4l2_subdev_get_pad_format(sd, state, fmt->pad);
+
+ return 0;
+}
+
+static int imx296_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct imx296 *sensor = to_imx296(sd);
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
+
+ crop = v4l2_subdev_get_pad_crop(sd, state, fmt->pad);
+ format = v4l2_subdev_get_pad_format(sd, state, fmt->pad);
+
+ /*
+ * Binning is only allowed when cropping is disabled according to the
+ * documentation. This should be double-checked.
+ */
+ if (crop->width == IMX296_PIXEL_ARRAY_WIDTH &&
+ crop->height == IMX296_PIXEL_ARRAY_HEIGHT) {
+ unsigned int width;
+ unsigned int height;
+ unsigned int hratio;
+ unsigned int vratio;
+
+ /* Clamp the width and height to avoid dividing by zero. */
+ width = clamp_t(unsigned int, fmt->format.width,
+ crop->width / 2, crop->width);
+ height = clamp_t(unsigned int, fmt->format.height,
+ crop->height / 2, crop->height);
+
+ hratio = DIV_ROUND_CLOSEST(crop->width, width);
+ vratio = DIV_ROUND_CLOSEST(crop->height, height);
+
+ format->width = crop->width / hratio;
+ format->height = crop->height / vratio;
+ } else {
+ format->width = crop->width;
+ format->height = crop->height;
+ }
+
+ format->code = sensor->mono ? MEDIA_BUS_FMT_Y10_1X10
+ : MEDIA_BUS_FMT_SBGGR10_1X10;
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_RAW;
+ format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ format->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ format->xfer_func = V4L2_XFER_FUNC_NONE;
+
+ fmt->format = *format;
+
+ return 0;
+}
+
+static int imx296_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ sel->r = *v4l2_subdev_get_pad_crop(sd, state, sel->pad);
+ break;
+
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_NATIVE_SIZE:
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = IMX296_PIXEL_ARRAY_WIDTH;
+ sel->r.height = IMX296_PIXEL_ARRAY_HEIGHT;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int imx296_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
+ struct v4l2_rect rect;
+
+ if (sel->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ /*
+ * Clamp the crop rectangle boundaries and align them to a multiple of 4
+ * pixels to satisfy hardware requirements.
+ */
+ rect.left = clamp(ALIGN(sel->r.left, 4), 0,
+ IMX296_PIXEL_ARRAY_WIDTH - IMX296_FID0_ROIWH1_MIN);
+ rect.top = clamp(ALIGN(sel->r.top, 4), 0,
+ IMX296_PIXEL_ARRAY_HEIGHT - IMX296_FID0_ROIWV1_MIN);
+ rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 4),
+ IMX296_FID0_ROIWH1_MIN, IMX296_PIXEL_ARRAY_WIDTH);
+ rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 4),
+ IMX296_FID0_ROIWV1_MIN, IMX296_PIXEL_ARRAY_HEIGHT);
+
+ rect.width = min_t(unsigned int, rect.width,
+ IMX296_PIXEL_ARRAY_WIDTH - rect.left);
+ rect.height = min_t(unsigned int, rect.height,
+ IMX296_PIXEL_ARRAY_HEIGHT - rect.top);
+
+ crop = v4l2_subdev_get_pad_crop(sd, state, sel->pad);
+
+ if (rect.width != crop->width || rect.height != crop->height) {
+ /*
+ * Reset the output image size if the crop rectangle size has
+ * been modified.
+ */
+ format = v4l2_subdev_get_pad_format(sd, state, sel->pad);
+ format->width = rect.width;
+ format->height = rect.height;
+ }
+
+ *crop = rect;
+ sel->r = rect;
+
+ return 0;
+}
+
+static int imx296_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_subdev_selection sel = {
+ .target = V4L2_SEL_TGT_CROP,
+ .r.width = IMX296_PIXEL_ARRAY_WIDTH,
+ .r.height = IMX296_PIXEL_ARRAY_HEIGHT,
+ };
+ struct v4l2_subdev_format format = {
+ .format = {
+ .width = IMX296_PIXEL_ARRAY_WIDTH,
+ .height = IMX296_PIXEL_ARRAY_HEIGHT,
+ },
+ };
+
+ imx296_set_selection(sd, state, &sel);
+ imx296_set_format(sd, state, &format);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops imx296_subdev_video_ops = {
+ .s_stream = imx296_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops imx296_subdev_pad_ops = {
+ .enum_mbus_code = imx296_enum_mbus_code,
+ .enum_frame_size = imx296_enum_frame_size,
+ .get_fmt = imx296_get_format,
+ .set_fmt = imx296_set_format,
+ .get_selection = imx296_get_selection,
+ .set_selection = imx296_set_selection,
+ .init_cfg = imx296_init_cfg,
+};
+
+static const struct v4l2_subdev_ops imx296_subdev_ops = {
+ .video = &imx296_subdev_video_ops,
+ .pad = &imx296_subdev_pad_ops,
+};
+
+static int imx296_subdev_init(struct imx296 *sensor)
+{
+ struct i2c_client *client = to_i2c_client(sensor->dev);
+ int ret;
+
+ v4l2_i2c_subdev_init(&sensor->subdev, client, &imx296_subdev_ops);
+
+ ret = imx296_ctrls_init(sensor);
+ if (ret < 0)
+ return ret;
+
+ sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sensor->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&sensor->subdev.entity, 1, &sensor->pad);
+ if (ret < 0) {
+ v4l2_ctrl_handler_free(&sensor->ctrls);
+ return ret;
+ }
+
+ sensor->subdev.state_lock = sensor->subdev.ctrl_handler->lock;
+
+ v4l2_subdev_init_finalize(&sensor->subdev);
+
+ return ret;
+}
+
+static void imx296_subdev_cleanup(struct imx296 *sensor)
+{
+ media_entity_cleanup(&sensor->subdev.entity);
+ v4l2_ctrl_handler_free(&sensor->ctrls);
+}
+
+/* -----------------------------------------------------------------------------
+ * Power management
+ */
+
+static int __maybe_unused imx296_runtime_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct imx296 *sensor = to_imx296(subdev);
+
+ return imx296_power_on(sensor);
+}
+
+static int __maybe_unused imx296_runtime_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct imx296 *sensor = to_imx296(subdev);
+
+ imx296_power_off(sensor);
+
+ return 0;
+}
+
+static const struct dev_pm_ops imx296_pm_ops = {
+ SET_RUNTIME_PM_OPS(imx296_runtime_suspend, imx296_runtime_resume, NULL)
+};
+
+/* -----------------------------------------------------------------------------
+ * Probe & Remove
+ */
+
+static int imx296_read_temperature(struct imx296 *sensor, int *temp)
+{
+ int tmdout;
+ int ret;
+
+ ret = imx296_write(sensor, IMX296_TMDCTRL, IMX296_TMDCTRL_LATCH, NULL);
+ if (ret < 0)
+ return ret;
+
+ tmdout = imx296_read(sensor, IMX296_TMDOUT) & IMX296_TMDOUT_MASK;
+ if (tmdout < 0)
+ return tmdout;
+
+ /* T(°C) = 246.312 - 0.304 * TMDOUT */;
+ *temp = 246312 - 304 * tmdout;
+
+ return imx296_write(sensor, IMX296_TMDCTRL, 0, NULL);
+}
+
+static int imx296_identify_model(struct imx296 *sensor)
+{
+ unsigned int model;
+ int temp = 0;
+ int ret;
+
+ model = (uintptr_t)of_device_get_match_data(sensor->dev);
+ if (model) {
+ dev_dbg(sensor->dev,
+ "sensor model auto-detection disabled, forcing 0x%04x\n",
+ model);
+ sensor->mono = model & IMX296_SENSOR_INFO_MONO;
+ return 0;
+ }
+
+ /*
+ * While most registers can be read when the sensor is in standby, this
+ * is not the case of the sensor info register :-(
+ */
+ ret = imx296_write(sensor, IMX296_CTRL00, 0, NULL);
+ if (ret < 0) {
+ dev_err(sensor->dev,
+ "failed to get sensor out of standby (%d)\n", ret);
+ return ret;
+ }
+
+ ret = imx296_read(sensor, IMX296_SENSOR_INFO);
+ if (ret < 0) {
+ dev_err(sensor->dev, "failed to read sensor information (%d)\n",
+ ret);
+ goto done;
+ }
+
+ model = (ret >> 6) & 0x1ff;
+
+ switch (model) {
+ case 296:
+ sensor->mono = ret & IMX296_SENSOR_INFO_MONO;
+ break;
+ /*
+ * The IMX297 seems to share features with the IMX296, it may be
+ * possible to support it in the same driver.
+ */
+ case 297:
+ default:
+ dev_err(sensor->dev, "invalid device model 0x%04x\n", ret);
+ ret = -ENODEV;
+ goto done;
+ }
+
+ ret = imx296_read_temperature(sensor, &temp);
+ if (ret < 0)
+ goto done;
+
+ dev_info(sensor->dev, "found IMX%u%s (%u.%uC)\n", model,
+ sensor->mono ? "LL" : "LQ", temp / 1000, (temp / 100) % 10);
+
+done:
+ imx296_write(sensor, IMX296_CTRL00, IMX296_CTRL00_STANDBY, NULL);
+ return ret;
+}
+
+static const struct regmap_config imx296_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+
+ .wr_table = &(const struct regmap_access_table) {
+ .no_ranges = (const struct regmap_range[]) {
+ {
+ .range_min = IMX296_SENSOR_INFO & 0xffff,
+ .range_max = (IMX296_SENSOR_INFO & 0xffff) + 1,
+ },
+ },
+ .n_no_ranges = 1,
+ },
+};
+
+static int imx296_probe(struct i2c_client *client)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ unsigned long clk_rate;
+ struct imx296 *sensor;
+ unsigned int i;
+ int ret;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_warn(&adapter->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
+ return -EIO;
+ }
+
+ sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
+ if (!sensor)
+ return -ENOMEM;
+
+ sensor->dev = &client->dev;
+
+ /* Acquire resources. */
+ for (i = 0; i < ARRAY_SIZE(sensor->supplies); ++i)
+ sensor->supplies[i].supply = imx296_supply_names[i];
+
+ ret = devm_regulator_bulk_get(sensor->dev, ARRAY_SIZE(sensor->supplies),
+ sensor->supplies);
+ if (ret) {
+ dev_err_probe(sensor->dev, ret, "failed to get supplies\n");
+ return ret;
+ }
+
+ sensor->reset = devm_gpiod_get_optional(sensor->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(sensor->reset))
+ return dev_err_probe(sensor->dev, PTR_ERR(sensor->reset),
+ "failed to get reset GPIO\n");
+
+ sensor->clk = devm_clk_get(sensor->dev, "inck");
+ if (IS_ERR(sensor->clk))
+ return dev_err_probe(sensor->dev, PTR_ERR(sensor->clk),
+ "failed to get clock\n");
+
+ clk_rate = clk_get_rate(sensor->clk);
+ for (i = 0; i < ARRAY_SIZE(imx296_clk_params); ++i) {
+ if (clk_rate == imx296_clk_params[i].freq) {
+ sensor->clk_params = &imx296_clk_params[i];
+ break;
+ }
+ }
+
+ if (!sensor->clk_params) {
+ dev_err(sensor->dev, "unsupported clock rate %lu\n", clk_rate);
+ return -EINVAL;
+ }
+
+ sensor->regmap = devm_regmap_init_i2c(client, &imx296_regmap_config);
+ if (IS_ERR(sensor->regmap))
+ return PTR_ERR(sensor->regmap);
+
+ /*
+ * Enable power management. The driver supports runtime PM, but needs to
+ * work when runtime PM is disabled in the kernel. To that end, power
+ * the sensor on manually here, identify it, and fully initialize it.
+ */
+ ret = imx296_power_on(sensor);
+ if (ret < 0)
+ return ret;
+
+ ret = imx296_identify_model(sensor);
+ if (ret < 0)
+ goto err_power;
+
+ /* Initialize the V4L2 subdev. */
+ ret = imx296_subdev_init(sensor);
+ if (ret < 0)
+ goto err_power;
+
+ /*
+ * Enable runtime PM. As the device has been powered manually, mark it
+ * as active, and increase the usage count without resuming the device.
+ */
+ pm_runtime_set_active(sensor->dev);
+ pm_runtime_get_noresume(sensor->dev);
+ pm_runtime_enable(sensor->dev);
+
+ /* Register the V4L2 subdev. */
+ ret = v4l2_async_register_subdev(&sensor->subdev);
+ if (ret < 0)
+ goto err_pm;
+
+ /*
+ * Finally, enable autosuspend and decrease the usage count. The device
+ * will get suspended after the autosuspend delay, turning the power
+ * off.
+ */
+ pm_runtime_set_autosuspend_delay(sensor->dev, 1000);
+ pm_runtime_use_autosuspend(sensor->dev);
+ pm_runtime_put_autosuspend(sensor->dev);
+
+ return 0;
+
+err_pm:
+ pm_runtime_disable(sensor->dev);
+ pm_runtime_put_noidle(sensor->dev);
+ imx296_subdev_cleanup(sensor);
+err_power:
+ imx296_power_off(sensor);
+ return ret;
+}
+
+static void imx296_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct imx296 *sensor = to_imx296(subdev);
+
+ v4l2_async_unregister_subdev(subdev);
+
+ imx296_subdev_cleanup(sensor);
+
+ /*
+ * Disable runtime PM. In case runtime PM is disabled in the kernel,
+ * make sure to turn power off manually.
+ */
+ pm_runtime_disable(sensor->dev);
+ if (!pm_runtime_status_suspended(sensor->dev))
+ imx296_power_off(sensor);
+ pm_runtime_set_suspended(sensor->dev);
+}
+
+static const struct of_device_id imx296_of_match[] = {
+ { .compatible = "sony,imx296", .data = NULL },
+ { .compatible = "sony,imx296ll", .data = (void *)IMX296_SENSOR_INFO_IMX296LL },
+ { .compatible = "sony,imx296lq", .data = (void *)IMX296_SENSOR_INFO_IMX296LQ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, imx296_of_match);
+
+static struct i2c_driver imx296_i2c_driver = {
+ .driver = {
+ .of_match_table = imx296_of_match,
+ .name = "imx296",
+ .pm = &imx296_pm_ops
+ },
+ .probe_new = imx296_probe,
+ .remove = imx296_remove,
+};
+
+module_i2c_driver(imx296_i2c_driver);
+
+MODULE_DESCRIPTION("Sony IMX296 Camera driver");
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/imx415.c b/drivers/media/i2c/imx415.c
new file mode 100644
index 000000000000..d90392df98c7
--- /dev/null
+++ b/drivers/media/i2c/imx415.c
@@ -0,0 +1,1300 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for the Sony IMX415 CMOS Image Sensor.
+ *
+ * Copyright (C) 2023 WolfVision GmbH.
+ */
+
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#define IMX415_PIXEL_ARRAY_TOP 0
+#define IMX415_PIXEL_ARRAY_LEFT 0
+#define IMX415_PIXEL_ARRAY_WIDTH 3864
+#define IMX415_PIXEL_ARRAY_HEIGHT 2192
+#define IMX415_PIXEL_ARRAY_VBLANK 58
+
+#define IMX415_NUM_CLK_PARAM_REGS 11
+
+#define IMX415_REG_8BIT(n) ((1 << 16) | (n))
+#define IMX415_REG_16BIT(n) ((2 << 16) | (n))
+#define IMX415_REG_24BIT(n) ((3 << 16) | (n))
+#define IMX415_REG_SIZE_SHIFT 16
+#define IMX415_REG_ADDR_MASK 0xffff
+
+#define IMX415_MODE IMX415_REG_8BIT(0x3000)
+#define IMX415_MODE_OPERATING (0)
+#define IMX415_MODE_STANDBY BIT(0)
+#define IMX415_REGHOLD IMX415_REG_8BIT(0x3001)
+#define IMX415_REGHOLD_INVALID (0)
+#define IMX415_REGHOLD_VALID BIT(0)
+#define IMX415_XMSTA IMX415_REG_8BIT(0x3002)
+#define IMX415_XMSTA_START (0)
+#define IMX415_XMSTA_STOP BIT(0)
+#define IMX415_BCWAIT_TIME IMX415_REG_16BIT(0x3008)
+#define IMX415_CPWAIT_TIME IMX415_REG_16BIT(0x300A)
+#define IMX415_WINMODE IMX415_REG_8BIT(0x301C)
+#define IMX415_ADDMODE IMX415_REG_8BIT(0x3022)
+#define IMX415_REVERSE IMX415_REG_8BIT(0x3030)
+#define IMX415_HREVERSE_SHIFT (0)
+#define IMX415_VREVERSE_SHIFT BIT(0)
+#define IMX415_ADBIT IMX415_REG_8BIT(0x3031)
+#define IMX415_MDBIT IMX415_REG_8BIT(0x3032)
+#define IMX415_SYS_MODE IMX415_REG_8BIT(0x3033)
+#define IMX415_OUTSEL IMX415_REG_8BIT(0x30C0)
+#define IMX415_DRV IMX415_REG_8BIT(0x30C1)
+#define IMX415_VMAX IMX415_REG_24BIT(0x3024)
+#define IMX415_HMAX IMX415_REG_16BIT(0x3028)
+#define IMX415_SHR0 IMX415_REG_24BIT(0x3050)
+#define IMX415_GAIN_PCG_0 IMX415_REG_16BIT(0x3090)
+#define IMX415_AGAIN_MIN 0
+#define IMX415_AGAIN_MAX 100
+#define IMX415_AGAIN_STEP 1
+#define IMX415_BLKLEVEL IMX415_REG_16BIT(0x30E2)
+#define IMX415_BLKLEVEL_DEFAULT 50
+#define IMX415_TPG_EN_DUOUT IMX415_REG_8BIT(0x30E4)
+#define IMX415_TPG_PATSEL_DUOUT IMX415_REG_8BIT(0x30E6)
+#define IMX415_TPG_COLORWIDTH IMX415_REG_8BIT(0x30E8)
+#define IMX415_TESTCLKEN_MIPI IMX415_REG_8BIT(0x3110)
+#define IMX415_INCKSEL1 IMX415_REG_8BIT(0x3115)
+#define IMX415_INCKSEL2 IMX415_REG_8BIT(0x3116)
+#define IMX415_INCKSEL3 IMX415_REG_16BIT(0x3118)
+#define IMX415_INCKSEL4 IMX415_REG_16BIT(0x311A)
+#define IMX415_INCKSEL5 IMX415_REG_8BIT(0x311E)
+#define IMX415_DIG_CLP_MODE IMX415_REG_8BIT(0x32C8)
+#define IMX415_WRJ_OPEN IMX415_REG_8BIT(0x3390)
+#define IMX415_SENSOR_INFO IMX415_REG_16BIT(0x3F12)
+#define IMX415_SENSOR_INFO_MASK 0xFFF
+#define IMX415_CHIP_ID 0x514
+#define IMX415_LANEMODE IMX415_REG_16BIT(0x4001)
+#define IMX415_LANEMODE_2 1
+#define IMX415_LANEMODE_4 3
+#define IMX415_TXCLKESC_FREQ IMX415_REG_16BIT(0x4004)
+#define IMX415_INCKSEL6 IMX415_REG_8BIT(0x400C)
+#define IMX415_TCLKPOST IMX415_REG_16BIT(0x4018)
+#define IMX415_TCLKPREPARE IMX415_REG_16BIT(0x401A)
+#define IMX415_TCLKTRAIL IMX415_REG_16BIT(0x401C)
+#define IMX415_TCLKZERO IMX415_REG_16BIT(0x401E)
+#define IMX415_THSPREPARE IMX415_REG_16BIT(0x4020)
+#define IMX415_THSZERO IMX415_REG_16BIT(0x4022)
+#define IMX415_THSTRAIL IMX415_REG_16BIT(0x4024)
+#define IMX415_THSEXIT IMX415_REG_16BIT(0x4026)
+#define IMX415_TLPX IMX415_REG_16BIT(0x4028)
+#define IMX415_INCKSEL7 IMX415_REG_8BIT(0x4074)
+
+struct imx415_reg {
+ u32 address;
+ u32 val;
+};
+
+static const char *const imx415_supply_names[] = {
+ "dvdd",
+ "ovdd",
+ "avdd",
+};
+
+/*
+ * The IMX415 data sheet uses lane rates but v4l2 uses link frequency to
+ * describe MIPI CSI-2 speed. This driver uses lane rates wherever possible
+ * and converts them to link frequencies by a factor of two when needed.
+ */
+static const s64 link_freq_menu_items[] = {
+ 594000000 / 2, 720000000 / 2, 891000000 / 2,
+ 1440000000 / 2, 1485000000 / 2,
+};
+
+struct imx415_clk_params {
+ u64 lane_rate;
+ u64 inck;
+ struct imx415_reg regs[IMX415_NUM_CLK_PARAM_REGS];
+};
+
+/* INCK Settings - includes all lane rate and INCK dependent registers */
+static const struct imx415_clk_params imx415_clk_params[] = {
+ {
+ .lane_rate = 594000000,
+ .inck = 27000000,
+ .regs[0] = { IMX415_BCWAIT_TIME, 0x05D },
+ .regs[1] = { IMX415_CPWAIT_TIME, 0x042 },
+ .regs[2] = { IMX415_SYS_MODE, 0x7 },
+ .regs[3] = { IMX415_INCKSEL1, 0x00 },
+ .regs[4] = { IMX415_INCKSEL2, 0x23 },
+ .regs[5] = { IMX415_INCKSEL3, 0x084 },
+ .regs[6] = { IMX415_INCKSEL4, 0x0E7 },
+ .regs[7] = { IMX415_INCKSEL5, 0x23 },
+ .regs[8] = { IMX415_INCKSEL6, 0x0 },
+ .regs[9] = { IMX415_INCKSEL7, 0x1 },
+ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x06C0 },
+ },
+ {
+ .lane_rate = 720000000,
+ .inck = 24000000,
+ .regs[0] = { IMX415_BCWAIT_TIME, 0x054 },
+ .regs[1] = { IMX415_CPWAIT_TIME, 0x03B },
+ .regs[2] = { IMX415_SYS_MODE, 0x9 },
+ .regs[3] = { IMX415_INCKSEL1, 0x00 },
+ .regs[4] = { IMX415_INCKSEL2, 0x23 },
+ .regs[5] = { IMX415_INCKSEL3, 0x0B4 },
+ .regs[6] = { IMX415_INCKSEL4, 0x0FC },
+ .regs[7] = { IMX415_INCKSEL5, 0x23 },
+ .regs[8] = { IMX415_INCKSEL6, 0x0 },
+ .regs[9] = { IMX415_INCKSEL7, 0x1 },
+ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x0600 },
+ },
+ {
+ .lane_rate = 891000000,
+ .inck = 27000000,
+ .regs[0] = { IMX415_BCWAIT_TIME, 0x05D },
+ .regs[1] = { IMX415_CPWAIT_TIME, 0x042 },
+ .regs[2] = { IMX415_SYS_MODE, 0x5 },
+ .regs[3] = { IMX415_INCKSEL1, 0x00 },
+ .regs[4] = { IMX415_INCKSEL2, 0x23 },
+ .regs[5] = { IMX415_INCKSEL3, 0x0C6 },
+ .regs[6] = { IMX415_INCKSEL4, 0x0E7 },
+ .regs[7] = { IMX415_INCKSEL5, 0x23 },
+ .regs[8] = { IMX415_INCKSEL6, 0x0 },
+ .regs[9] = { IMX415_INCKSEL7, 0x1 },
+ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x06C0 },
+ },
+ {
+ .lane_rate = 1440000000,
+ .inck = 24000000,
+ .regs[0] = { IMX415_BCWAIT_TIME, 0x054 },
+ .regs[1] = { IMX415_CPWAIT_TIME, 0x03B },
+ .regs[2] = { IMX415_SYS_MODE, 0x8 },
+ .regs[3] = { IMX415_INCKSEL1, 0x00 },
+ .regs[4] = { IMX415_INCKSEL2, 0x23 },
+ .regs[5] = { IMX415_INCKSEL3, 0x0B4 },
+ .regs[6] = { IMX415_INCKSEL4, 0x0FC },
+ .regs[7] = { IMX415_INCKSEL5, 0x23 },
+ .regs[8] = { IMX415_INCKSEL6, 0x1 },
+ .regs[9] = { IMX415_INCKSEL7, 0x0 },
+ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x0600 },
+ },
+ {
+ .lane_rate = 1485000000,
+ .inck = 27000000,
+ .regs[0] = { IMX415_BCWAIT_TIME, 0x05D },
+ .regs[1] = { IMX415_CPWAIT_TIME, 0x042 },
+ .regs[2] = { IMX415_SYS_MODE, 0x8 },
+ .regs[3] = { IMX415_INCKSEL1, 0x00 },
+ .regs[4] = { IMX415_INCKSEL2, 0x23 },
+ .regs[5] = { IMX415_INCKSEL3, 0x0A5 },
+ .regs[6] = { IMX415_INCKSEL4, 0x0E7 },
+ .regs[7] = { IMX415_INCKSEL5, 0x23 },
+ .regs[8] = { IMX415_INCKSEL6, 0x1 },
+ .regs[9] = { IMX415_INCKSEL7, 0x0 },
+ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x06C0 },
+ },
+};
+
+/* all-pixel 2-lane 720 Mbps 15.74 Hz mode */
+static const struct imx415_reg imx415_mode_2_720[] = {
+ { IMX415_VMAX, 0x08CA },
+ { IMX415_HMAX, 0x07F0 },
+ { IMX415_LANEMODE, IMX415_LANEMODE_2 },
+ { IMX415_TCLKPOST, 0x006F },
+ { IMX415_TCLKPREPARE, 0x002F },
+ { IMX415_TCLKTRAIL, 0x002F },
+ { IMX415_TCLKZERO, 0x00BF },
+ { IMX415_THSPREPARE, 0x002F },
+ { IMX415_THSZERO, 0x0057 },
+ { IMX415_THSTRAIL, 0x002F },
+ { IMX415_THSEXIT, 0x004F },
+ { IMX415_TLPX, 0x0027 },
+};
+
+/* all-pixel 2-lane 1440 Mbps 30.01 Hz mode */
+static const struct imx415_reg imx415_mode_2_1440[] = {
+ { IMX415_VMAX, 0x08CA },
+ { IMX415_HMAX, 0x042A },
+ { IMX415_LANEMODE, IMX415_LANEMODE_2 },
+ { IMX415_TCLKPOST, 0x009F },
+ { IMX415_TCLKPREPARE, 0x0057 },
+ { IMX415_TCLKTRAIL, 0x0057 },
+ { IMX415_TCLKZERO, 0x0187 },
+ { IMX415_THSPREPARE, 0x005F },
+ { IMX415_THSZERO, 0x00A7 },
+ { IMX415_THSTRAIL, 0x005F },
+ { IMX415_THSEXIT, 0x0097 },
+ { IMX415_TLPX, 0x004F },
+};
+
+/* all-pixel 4-lane 891 Mbps 30 Hz mode */
+static const struct imx415_reg imx415_mode_4_891[] = {
+ { IMX415_VMAX, 0x08CA },
+ { IMX415_HMAX, 0x044C },
+ { IMX415_LANEMODE, IMX415_LANEMODE_4 },
+ { IMX415_TCLKPOST, 0x007F },
+ { IMX415_TCLKPREPARE, 0x0037 },
+ { IMX415_TCLKTRAIL, 0x0037 },
+ { IMX415_TCLKZERO, 0x00F7 },
+ { IMX415_THSPREPARE, 0x003F },
+ { IMX415_THSZERO, 0x006F },
+ { IMX415_THSTRAIL, 0x003F },
+ { IMX415_THSEXIT, 0x005F },
+ { IMX415_TLPX, 0x002F },
+};
+
+struct imx415_mode_reg_list {
+ u32 num_of_regs;
+ const struct imx415_reg *regs;
+};
+
+/*
+ * Mode : number of lanes, lane rate and frame rate dependent settings
+ *
+ * pixel_rate and hmax_pix are needed to calculate hblank for the v4l2 ctrl
+ * interface. These values can not be found in the data sheet and should be
+ * treated as virtual values. Use following table when adding new modes.
+ *
+ * lane_rate lanes fps hmax_pix pixel_rate
+ *
+ * 594 2 10.000 4400 99000000
+ * 891 2 15.000 4400 148500000
+ * 720 2 15.748 4064 144000000
+ * 1782 2 30.000 4400 297000000
+ * 2079 2 30.000 4400 297000000
+ * 1440 2 30.019 4510 304615385
+ *
+ * 594 4 20.000 5500 247500000
+ * 594 4 25.000 4400 247500000
+ * 720 4 25.000 4400 247500000
+ * 720 4 30.019 4510 304615385
+ * 891 4 30.000 4400 297000000
+ * 1440 4 30.019 4510 304615385
+ * 1440 4 60.038 4510 609230769
+ * 1485 4 60.000 4400 594000000
+ * 1782 4 60.000 4400 594000000
+ * 2079 4 60.000 4400 594000000
+ * 2376 4 90.164 4392 891000000
+ */
+struct imx415_mode {
+ u64 lane_rate;
+ u32 lanes;
+ u32 hmax_pix;
+ u64 pixel_rate;
+ struct imx415_mode_reg_list reg_list;
+};
+
+/* mode configs */
+static const struct imx415_mode supported_modes[] = {
+ {
+ .lane_rate = 720000000,
+ .lanes = 2,
+ .hmax_pix = 4064,
+ .pixel_rate = 144000000,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(imx415_mode_2_720),
+ .regs = imx415_mode_2_720,
+ },
+ },
+ {
+ .lane_rate = 1440000000,
+ .lanes = 2,
+ .hmax_pix = 4510,
+ .pixel_rate = 304615385,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(imx415_mode_2_1440),
+ .regs = imx415_mode_2_1440,
+ },
+ },
+ {
+ .lane_rate = 891000000,
+ .lanes = 4,
+ .hmax_pix = 4400,
+ .pixel_rate = 297000000,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(imx415_mode_4_891),
+ .regs = imx415_mode_4_891,
+ },
+ },
+};
+
+static const struct regmap_config imx415_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+};
+
+static const char *const imx415_test_pattern_menu[] = {
+ "disabled",
+ "solid black",
+ "solid white",
+ "solid dark gray",
+ "solid light gray",
+ "stripes light/dark grey",
+ "stripes dark/light grey",
+ "stripes black/dark grey",
+ "stripes dark grey/black",
+ "stripes black/white",
+ "stripes white/black",
+ "horizontal color bar",
+ "vertical color bar",
+};
+
+struct imx415 {
+ struct device *dev;
+ struct clk *clk;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(imx415_supply_names)];
+ struct gpio_desc *reset;
+ struct regmap *regmap;
+
+ const struct imx415_clk_params *clk_params;
+
+ bool streaming;
+
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+
+ struct v4l2_ctrl_handler ctrls;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+
+ unsigned int cur_mode;
+ unsigned int num_data_lanes;
+};
+
+/*
+ * This table includes fixed register settings and a bunch of undocumented
+ * registers that have to be set to another value than default.
+ */
+static const struct imx415_reg imx415_init_table[] = {
+ /* use all-pixel readout mode, no flip */
+ { IMX415_WINMODE, 0x00 },
+ { IMX415_ADDMODE, 0x00 },
+ { IMX415_REVERSE, 0x00 },
+ /* use RAW 10-bit mode */
+ { IMX415_ADBIT, 0x00 },
+ { IMX415_MDBIT, 0x00 },
+ /* output VSYNC on XVS and low on XHS */
+ { IMX415_OUTSEL, 0x22 },
+ { IMX415_DRV, 0x00 },
+
+ /* SONY magic registers */
+ { IMX415_REG_8BIT(0x32D4), 0x21 },
+ { IMX415_REG_8BIT(0x32EC), 0xA1 },
+ { IMX415_REG_8BIT(0x3452), 0x7F },
+ { IMX415_REG_8BIT(0x3453), 0x03 },
+ { IMX415_REG_8BIT(0x358A), 0x04 },
+ { IMX415_REG_8BIT(0x35A1), 0x02 },
+ { IMX415_REG_8BIT(0x36BC), 0x0C },
+ { IMX415_REG_8BIT(0x36CC), 0x53 },
+ { IMX415_REG_8BIT(0x36CD), 0x00 },
+ { IMX415_REG_8BIT(0x36CE), 0x3C },
+ { IMX415_REG_8BIT(0x36D0), 0x8C },
+ { IMX415_REG_8BIT(0x36D1), 0x00 },
+ { IMX415_REG_8BIT(0x36D2), 0x71 },
+ { IMX415_REG_8BIT(0x36D4), 0x3C },
+ { IMX415_REG_8BIT(0x36D6), 0x53 },
+ { IMX415_REG_8BIT(0x36D7), 0x00 },
+ { IMX415_REG_8BIT(0x36D8), 0x71 },
+ { IMX415_REG_8BIT(0x36DA), 0x8C },
+ { IMX415_REG_8BIT(0x36DB), 0x00 },
+ { IMX415_REG_8BIT(0x3724), 0x02 },
+ { IMX415_REG_8BIT(0x3726), 0x02 },
+ { IMX415_REG_8BIT(0x3732), 0x02 },
+ { IMX415_REG_8BIT(0x3734), 0x03 },
+ { IMX415_REG_8BIT(0x3736), 0x03 },
+ { IMX415_REG_8BIT(0x3742), 0x03 },
+ { IMX415_REG_8BIT(0x3862), 0xE0 },
+ { IMX415_REG_8BIT(0x38CC), 0x30 },
+ { IMX415_REG_8BIT(0x38CD), 0x2F },
+ { IMX415_REG_8BIT(0x395C), 0x0C },
+ { IMX415_REG_8BIT(0x3A42), 0xD1 },
+ { IMX415_REG_8BIT(0x3A4C), 0x77 },
+ { IMX415_REG_8BIT(0x3AE0), 0x02 },
+ { IMX415_REG_8BIT(0x3AEC), 0x0C },
+ { IMX415_REG_8BIT(0x3B00), 0x2E },
+ { IMX415_REG_8BIT(0x3B06), 0x29 },
+ { IMX415_REG_8BIT(0x3B98), 0x25 },
+ { IMX415_REG_8BIT(0x3B99), 0x21 },
+ { IMX415_REG_8BIT(0x3B9B), 0x13 },
+ { IMX415_REG_8BIT(0x3B9C), 0x13 },
+ { IMX415_REG_8BIT(0x3B9D), 0x13 },
+ { IMX415_REG_8BIT(0x3B9E), 0x13 },
+ { IMX415_REG_8BIT(0x3BA1), 0x00 },
+ { IMX415_REG_8BIT(0x3BA2), 0x06 },
+ { IMX415_REG_8BIT(0x3BA3), 0x0B },
+ { IMX415_REG_8BIT(0x3BA4), 0x10 },
+ { IMX415_REG_8BIT(0x3BA5), 0x14 },
+ { IMX415_REG_8BIT(0x3BA6), 0x18 },
+ { IMX415_REG_8BIT(0x3BA7), 0x1A },
+ { IMX415_REG_8BIT(0x3BA8), 0x1A },
+ { IMX415_REG_8BIT(0x3BA9), 0x1A },
+ { IMX415_REG_8BIT(0x3BAC), 0xED },
+ { IMX415_REG_8BIT(0x3BAD), 0x01 },
+ { IMX415_REG_8BIT(0x3BAE), 0xF6 },
+ { IMX415_REG_8BIT(0x3BAF), 0x02 },
+ { IMX415_REG_8BIT(0x3BB0), 0xA2 },
+ { IMX415_REG_8BIT(0x3BB1), 0x03 },
+ { IMX415_REG_8BIT(0x3BB2), 0xE0 },
+ { IMX415_REG_8BIT(0x3BB3), 0x03 },
+ { IMX415_REG_8BIT(0x3BB4), 0xE0 },
+ { IMX415_REG_8BIT(0x3BB5), 0x03 },
+ { IMX415_REG_8BIT(0x3BB6), 0xE0 },
+ { IMX415_REG_8BIT(0x3BB7), 0x03 },
+ { IMX415_REG_8BIT(0x3BB8), 0xE0 },
+ { IMX415_REG_8BIT(0x3BBA), 0xE0 },
+ { IMX415_REG_8BIT(0x3BBC), 0xDA },
+ { IMX415_REG_8BIT(0x3BBE), 0x88 },
+ { IMX415_REG_8BIT(0x3BC0), 0x44 },
+ { IMX415_REG_8BIT(0x3BC2), 0x7B },
+ { IMX415_REG_8BIT(0x3BC4), 0xA2 },
+ { IMX415_REG_8BIT(0x3BC8), 0xBD },
+ { IMX415_REG_8BIT(0x3BCA), 0xBD },
+};
+
+static inline struct imx415 *to_imx415(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct imx415, subdev);
+}
+
+static int imx415_read(struct imx415 *sensor, u32 addr)
+{
+ u8 data[3] = { 0 };
+ int ret;
+
+ ret = regmap_raw_read(sensor->regmap, addr & IMX415_REG_ADDR_MASK, data,
+ (addr >> IMX415_REG_SIZE_SHIFT) & 3);
+ if (ret < 0)
+ return ret;
+
+ return (data[2] << 16) | (data[1] << 8) | data[0];
+}
+
+static int imx415_write(struct imx415 *sensor, u32 addr, u32 value)
+{
+ u8 data[3] = { value & 0xff, (value >> 8) & 0xff, value >> 16 };
+ int ret;
+
+ ret = regmap_raw_write(sensor->regmap, addr & IMX415_REG_ADDR_MASK,
+ data, (addr >> IMX415_REG_SIZE_SHIFT) & 3);
+ if (ret < 0)
+ dev_err_ratelimited(sensor->dev,
+ "%u-bit write to 0x%04x failed: %d\n",
+ ((addr >> IMX415_REG_SIZE_SHIFT) & 3) * 8,
+ addr & IMX415_REG_ADDR_MASK, ret);
+
+ return 0;
+}
+
+static int imx415_set_testpattern(struct imx415 *sensor, int val)
+{
+ int ret;
+
+ if (val) {
+ ret = imx415_write(sensor, IMX415_BLKLEVEL, 0x00);
+ if (ret)
+ return ret;
+ ret = imx415_write(sensor, IMX415_TPG_EN_DUOUT, 0x01);
+ if (ret)
+ return ret;
+ ret = imx415_write(sensor, IMX415_TPG_PATSEL_DUOUT, val - 1);
+ if (ret)
+ return ret;
+ ret = imx415_write(sensor, IMX415_TPG_COLORWIDTH, 0x01);
+ if (ret)
+ return ret;
+ ret = imx415_write(sensor, IMX415_TESTCLKEN_MIPI, 0x20);
+ if (ret)
+ return ret;
+ ret = imx415_write(sensor, IMX415_DIG_CLP_MODE, 0x00);
+ if (ret)
+ return ret;
+ ret = imx415_write(sensor, IMX415_WRJ_OPEN, 0x00);
+ } else {
+ ret = imx415_write(sensor, IMX415_BLKLEVEL,
+ IMX415_BLKLEVEL_DEFAULT);
+ if (ret)
+ return ret;
+ ret = imx415_write(sensor, IMX415_TPG_EN_DUOUT, 0x00);
+ if (ret)
+ return ret;
+ ret = imx415_write(sensor, IMX415_TESTCLKEN_MIPI, 0x00);
+ if (ret)
+ return ret;
+ ret = imx415_write(sensor, IMX415_DIG_CLP_MODE, 0x01);
+ if (ret)
+ return ret;
+ ret = imx415_write(sensor, IMX415_WRJ_OPEN, 0x01);
+ }
+ return 0;
+}
+
+static int imx415_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct imx415 *sensor = container_of(ctrl->handler, struct imx415,
+ ctrls);
+ const struct v4l2_mbus_framefmt *format;
+ struct v4l2_subdev_state *state;
+ unsigned int vmax;
+ unsigned int flip;
+
+ if (!sensor->streaming)
+ return 0;
+
+ state = v4l2_subdev_get_locked_active_state(&sensor->subdev);
+ format = v4l2_subdev_get_pad_format(&sensor->subdev, state, 0);
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ /* clamp the exposure value to VMAX. */
+ vmax = format->height + sensor->vblank->cur.val;
+ ctrl->val = min_t(int, ctrl->val, vmax);
+ return imx415_write(sensor, IMX415_SHR0, vmax - ctrl->val);
+
+ case V4L2_CID_ANALOGUE_GAIN:
+ /* analogue gain in 0.3 dB step size */
+ return imx415_write(sensor, IMX415_GAIN_PCG_0, ctrl->val);
+
+ case V4L2_CID_HFLIP:
+ case V4L2_CID_VFLIP:
+ flip = (sensor->hflip->val << IMX415_HREVERSE_SHIFT) |
+ (sensor->vflip->val << IMX415_VREVERSE_SHIFT);
+ return imx415_write(sensor, IMX415_REVERSE, flip);
+
+ case V4L2_CID_TEST_PATTERN:
+ return imx415_set_testpattern(sensor, ctrl->val);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct v4l2_ctrl_ops imx415_ctrl_ops = {
+ .s_ctrl = imx415_s_ctrl,
+};
+
+static int imx415_ctrls_init(struct imx415 *sensor)
+{
+ struct v4l2_fwnode_device_properties props;
+ struct v4l2_ctrl *ctrl;
+ u64 pixel_rate = supported_modes[sensor->cur_mode].pixel_rate;
+ u64 lane_rate = supported_modes[sensor->cur_mode].lane_rate;
+ u32 exposure_max = IMX415_PIXEL_ARRAY_HEIGHT +
+ IMX415_PIXEL_ARRAY_VBLANK - 8;
+ u32 hblank;
+ unsigned int i;
+ int ret;
+
+ ret = v4l2_fwnode_device_parse(sensor->dev, &props);
+ if (ret < 0)
+ return ret;
+
+ v4l2_ctrl_handler_init(&sensor->ctrls, 10);
+
+ for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); ++i) {
+ if (lane_rate == link_freq_menu_items[i] * 2)
+ break;
+ }
+ if (i == ARRAY_SIZE(link_freq_menu_items)) {
+ return dev_err_probe(sensor->dev, -EINVAL,
+ "lane rate %llu not supported\n",
+ lane_rate);
+ }
+
+ ctrl = v4l2_ctrl_new_int_menu(&sensor->ctrls, &imx415_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(link_freq_menu_items) - 1, i,
+ link_freq_menu_items);
+
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, V4L2_CID_EXPOSURE,
+ 4, exposure_max, 1, exposure_max);
+
+ v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN, IMX415_AGAIN_MIN,
+ IMX415_AGAIN_MAX, IMX415_AGAIN_STEP,
+ IMX415_AGAIN_MIN);
+
+ hblank = supported_modes[sensor->cur_mode].hmax_pix -
+ IMX415_PIXEL_ARRAY_WIDTH;
+ ctrl = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops,
+ V4L2_CID_HBLANK, hblank, hblank, 1, hblank);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ sensor->vblank = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops,
+ V4L2_CID_VBLANK,
+ IMX415_PIXEL_ARRAY_VBLANK,
+ IMX415_PIXEL_ARRAY_VBLANK, 1,
+ IMX415_PIXEL_ARRAY_VBLANK);
+ if (sensor->vblank)
+ sensor->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ /*
+ * The pixel rate used here is a virtual value and can be used for
+ * calculating the frame rate together with hblank. It may not
+ * necessarily be the physically correct pixel clock.
+ */
+ v4l2_ctrl_new_std(&sensor->ctrls, NULL, V4L2_CID_PIXEL_RATE, pixel_rate,
+ pixel_rate, 1, pixel_rate);
+
+ sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ sensor->vflip = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+ v4l2_ctrl_new_std_menu_items(&sensor->ctrls, &imx415_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(imx415_test_pattern_menu) - 1,
+ 0, 0, imx415_test_pattern_menu);
+
+ v4l2_ctrl_new_fwnode_properties(&sensor->ctrls, &imx415_ctrl_ops,
+ &props);
+
+ if (sensor->ctrls.error) {
+ dev_err_probe(sensor->dev, sensor->ctrls.error,
+ "failed to add controls\n");
+ v4l2_ctrl_handler_free(&sensor->ctrls);
+ return sensor->ctrls.error;
+ }
+ sensor->subdev.ctrl_handler = &sensor->ctrls;
+
+ return 0;
+}
+
+static int imx415_set_mode(struct imx415 *sensor, int mode)
+{
+ const struct imx415_reg *reg;
+ unsigned int i;
+ int ret = 0;
+
+ if (mode >= ARRAY_SIZE(supported_modes)) {
+ dev_err(sensor->dev, "Mode %d not supported\n", mode);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < supported_modes[mode].reg_list.num_of_regs; ++i) {
+ reg = &supported_modes[mode].reg_list.regs[i];
+ ret = imx415_write(sensor, reg->address, reg->val);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < IMX415_NUM_CLK_PARAM_REGS; ++i) {
+ reg = &sensor->clk_params->regs[i];
+ ret = imx415_write(sensor, reg->address, reg->val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int imx415_setup(struct imx415 *sensor, struct v4l2_subdev_state *state)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(imx415_init_table); ++i) {
+ ret = imx415_write(sensor, imx415_init_table[i].address,
+ imx415_init_table[i].val);
+ if (ret)
+ return ret;
+ }
+
+ return imx415_set_mode(sensor, sensor->cur_mode);
+}
+
+static int imx415_wakeup(struct imx415 *sensor)
+{
+ int ret;
+
+ ret = imx415_write(sensor, IMX415_MODE, IMX415_MODE_OPERATING);
+ if (ret)
+ return ret;
+
+ /*
+ * According to the datasheet we have to wait at least 63 us after
+ * leaving standby mode. But this doesn't work even after 30 ms.
+ * So probably this should be 63 ms and therefore we wait for 80 ms.
+ */
+ msleep(80);
+
+ return 0;
+}
+
+static int imx415_stream_on(struct imx415 *sensor)
+{
+ int ret;
+
+ ret = imx415_wakeup(sensor);
+ if (ret)
+ return ret;
+
+ return imx415_write(sensor, IMX415_XMSTA, IMX415_XMSTA_START);
+}
+
+static int imx415_stream_off(struct imx415 *sensor)
+{
+ int ret;
+
+ ret = imx415_write(sensor, IMX415_XMSTA, IMX415_XMSTA_STOP);
+ if (ret)
+ return ret;
+
+ return imx415_write(sensor, IMX415_MODE, IMX415_MODE_STANDBY);
+}
+
+static int imx415_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct imx415 *sensor = to_imx415(sd);
+ struct v4l2_subdev_state *state;
+ int ret;
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ if (!enable) {
+ ret = imx415_stream_off(sensor);
+
+ pm_runtime_mark_last_busy(sensor->dev);
+ pm_runtime_put_autosuspend(sensor->dev);
+
+ sensor->streaming = false;
+
+ goto unlock;
+ }
+
+ ret = pm_runtime_resume_and_get(sensor->dev);
+ if (ret < 0)
+ goto unlock;
+
+ ret = imx415_setup(sensor, state);
+ if (ret)
+ goto err_pm;
+
+ /*
+ * Set streaming to true to ensure __v4l2_ctrl_handler_setup() will set
+ * the controls. The flag is reset to false further down if an error
+ * occurs.
+ */
+ sensor->streaming = true;
+
+ ret = __v4l2_ctrl_handler_setup(&sensor->ctrls);
+ if (ret < 0)
+ goto err_pm;
+
+ ret = imx415_stream_on(sensor);
+ if (ret)
+ goto err_pm;
+
+ ret = 0;
+
+unlock:
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
+
+err_pm:
+ /*
+ * In case of error, turn the power off synchronously as the device
+ * likely has no other chance to recover.
+ */
+ pm_runtime_put_sync(sensor->dev);
+ sensor->streaming = false;
+
+ goto unlock;
+}
+
+static int imx415_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index != 0)
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_SGBRG10_1X10;
+
+ return 0;
+}
+
+static int imx415_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ const struct v4l2_mbus_framefmt *format;
+
+ format = v4l2_subdev_get_pad_format(sd, state, fse->pad);
+
+ if (fse->index > 0 || fse->code != format->code)
+ return -EINVAL;
+
+ fse->min_width = IMX415_PIXEL_ARRAY_WIDTH;
+ fse->max_width = fse->min_width;
+ fse->min_height = IMX415_PIXEL_ARRAY_HEIGHT;
+ fse->max_height = fse->min_height;
+ return 0;
+}
+
+static int imx415_get_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *fmt)
+{
+ fmt->format = *v4l2_subdev_get_pad_format(sd, state, fmt->pad);
+
+ return 0;
+}
+
+static int imx415_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct v4l2_mbus_framefmt *format;
+
+ format = v4l2_subdev_get_pad_format(sd, state, fmt->pad);
+
+ format->width = fmt->format.width;
+ format->height = fmt->format.height;
+ format->code = MEDIA_BUS_FMT_SGBRG10_1X10;
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_RAW;
+ format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ format->quantization = V4L2_QUANTIZATION_DEFAULT;
+ format->xfer_func = V4L2_XFER_FUNC_NONE;
+
+ fmt->format = *format;
+ return 0;
+}
+
+static int imx415_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_selection *sel)
+{
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.top = IMX415_PIXEL_ARRAY_TOP;
+ sel->r.left = IMX415_PIXEL_ARRAY_LEFT;
+ sel->r.width = IMX415_PIXEL_ARRAY_WIDTH;
+ sel->r.height = IMX415_PIXEL_ARRAY_HEIGHT;
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int imx415_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_subdev_format format = {
+ .format = {
+ .width = IMX415_PIXEL_ARRAY_WIDTH,
+ .height = IMX415_PIXEL_ARRAY_HEIGHT,
+ },
+ };
+
+ imx415_set_format(sd, state, &format);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops imx415_subdev_video_ops = {
+ .s_stream = imx415_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops imx415_subdev_pad_ops = {
+ .enum_mbus_code = imx415_enum_mbus_code,
+ .enum_frame_size = imx415_enum_frame_size,
+ .get_fmt = imx415_get_format,
+ .set_fmt = imx415_set_format,
+ .get_selection = imx415_get_selection,
+ .init_cfg = imx415_init_cfg,
+};
+
+static const struct v4l2_subdev_ops imx415_subdev_ops = {
+ .video = &imx415_subdev_video_ops,
+ .pad = &imx415_subdev_pad_ops,
+};
+
+static int imx415_subdev_init(struct imx415 *sensor)
+{
+ struct i2c_client *client = to_i2c_client(sensor->dev);
+ int ret;
+
+ v4l2_i2c_subdev_init(&sensor->subdev, client, &imx415_subdev_ops);
+
+ ret = imx415_ctrls_init(sensor);
+ if (ret)
+ return ret;
+
+ sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+ V4L2_SUBDEV_FL_HAS_EVENTS;
+ sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sensor->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&sensor->subdev.entity, 1, &sensor->pad);
+ if (ret < 0) {
+ v4l2_ctrl_handler_free(&sensor->ctrls);
+ return ret;
+ }
+
+ sensor->subdev.state_lock = sensor->subdev.ctrl_handler->lock;
+ v4l2_subdev_init_finalize(&sensor->subdev);
+
+ return 0;
+}
+
+static void imx415_subdev_cleanup(struct imx415 *sensor)
+{
+ media_entity_cleanup(&sensor->subdev.entity);
+ v4l2_ctrl_handler_free(&sensor->ctrls);
+}
+
+static int imx415_power_on(struct imx415 *sensor)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(sensor->supplies),
+ sensor->supplies);
+ if (ret < 0)
+ return ret;
+
+ gpiod_set_value_cansleep(sensor->reset, 0);
+
+ udelay(1);
+
+ ret = clk_prepare_enable(sensor->clk);
+ if (ret < 0)
+ goto err_reset;
+
+ /*
+ * Data sheet states that 20 us are required before communication start,
+ * but this doesn't work in all cases. Use 100 us to be on the safe
+ * side.
+ */
+ usleep_range(100, 200);
+
+ return 0;
+
+err_reset:
+ gpiod_set_value_cansleep(sensor->reset, 1);
+ regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies);
+ return ret;
+}
+
+static void imx415_power_off(struct imx415 *sensor)
+{
+ clk_disable_unprepare(sensor->clk);
+ gpiod_set_value_cansleep(sensor->reset, 1);
+ regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies);
+}
+
+static int imx415_identify_model(struct imx415 *sensor)
+{
+ int model, ret;
+
+ /*
+ * While most registers can be read when the sensor is in standby, this
+ * is not the case of the sensor info register :-(
+ */
+ ret = imx415_wakeup(sensor);
+ if (ret)
+ return dev_err_probe(sensor->dev, ret,
+ "failed to get sensor out of standby\n");
+
+ ret = imx415_read(sensor, IMX415_SENSOR_INFO);
+ if (ret < 0) {
+ dev_err_probe(sensor->dev, ret,
+ "failed to read sensor information\n");
+ goto done;
+ }
+
+ model = ret & IMX415_SENSOR_INFO_MASK;
+
+ switch (model) {
+ case IMX415_CHIP_ID:
+ dev_info(sensor->dev, "Detected IMX415 image sensor\n");
+ break;
+ default:
+ ret = dev_err_probe(sensor->dev, -ENODEV,
+ "invalid device model 0x%04x\n", model);
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ imx415_write(sensor, IMX415_MODE, IMX415_MODE_STANDBY);
+ return ret;
+}
+
+static int imx415_check_inck(unsigned long inck, u64 link_frequency)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(imx415_clk_params); ++i) {
+ if ((imx415_clk_params[i].lane_rate == link_frequency * 2) &&
+ imx415_clk_params[i].inck == inck)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(imx415_clk_params))
+ return -EINVAL;
+ else
+ return 0;
+}
+
+static int imx415_parse_hw_config(struct imx415 *sensor)
+{
+ struct v4l2_fwnode_endpoint bus_cfg = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY,
+ };
+ struct fwnode_handle *ep;
+ u64 lane_rate;
+ unsigned long inck;
+ unsigned int i, j;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(sensor->supplies); ++i)
+ sensor->supplies[i].supply = imx415_supply_names[i];
+
+ ret = devm_regulator_bulk_get(sensor->dev, ARRAY_SIZE(sensor->supplies),
+ sensor->supplies);
+ if (ret)
+ return dev_err_probe(sensor->dev, ret,
+ "failed to get supplies\n");
+
+ sensor->reset = devm_gpiod_get_optional(sensor->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(sensor->reset))
+ return dev_err_probe(sensor->dev, PTR_ERR(sensor->reset),
+ "failed to get reset GPIO\n");
+
+ sensor->clk = devm_clk_get(sensor->dev, "inck");
+ if (IS_ERR(sensor->clk))
+ return dev_err_probe(sensor->dev, PTR_ERR(sensor->clk),
+ "failed to get clock\n");
+
+ ep = fwnode_graph_get_next_endpoint(dev_fwnode(sensor->dev), NULL);
+ if (!ep)
+ return -ENXIO;
+
+ ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
+ fwnode_handle_put(ep);
+ if (ret)
+ return ret;
+
+ switch (bus_cfg.bus.mipi_csi2.num_data_lanes) {
+ case 2:
+ case 4:
+ sensor->num_data_lanes = bus_cfg.bus.mipi_csi2.num_data_lanes;
+ break;
+ default:
+ ret = dev_err_probe(sensor->dev, -EINVAL,
+ "invalid number of CSI2 data lanes %d\n",
+ bus_cfg.bus.mipi_csi2.num_data_lanes);
+ goto done_endpoint_free;
+ }
+
+ if (!bus_cfg.nr_of_link_frequencies) {
+ ret = dev_err_probe(sensor->dev, -EINVAL,
+ "no link frequencies defined");
+ goto done_endpoint_free;
+ }
+
+ /*
+ * Check if there exists a sensor mode defined for current INCK,
+ * number of lanes and given lane rates.
+ */
+ inck = clk_get_rate(sensor->clk);
+ for (i = 0; i < bus_cfg.nr_of_link_frequencies; ++i) {
+ if (imx415_check_inck(inck, bus_cfg.link_frequencies[i])) {
+ dev_dbg(sensor->dev,
+ "INCK %lu Hz not supported for this link freq",
+ inck);
+ continue;
+ }
+
+ for (j = 0; j < ARRAY_SIZE(supported_modes); ++j) {
+ if (sensor->num_data_lanes != supported_modes[j].lanes)
+ continue;
+ if (bus_cfg.link_frequencies[i] * 2 !=
+ supported_modes[j].lane_rate)
+ continue;
+ sensor->cur_mode = j;
+ break;
+ }
+ if (j < ARRAY_SIZE(supported_modes))
+ break;
+ }
+ if (i == bus_cfg.nr_of_link_frequencies) {
+ ret = dev_err_probe(sensor->dev, -EINVAL,
+ "no valid sensor mode defined\n");
+ goto done_endpoint_free;
+ }
+
+ lane_rate = supported_modes[sensor->cur_mode].lane_rate;
+ for (i = 0; i < ARRAY_SIZE(imx415_clk_params); ++i) {
+ if (lane_rate == imx415_clk_params[i].lane_rate &&
+ inck == imx415_clk_params[i].inck) {
+ sensor->clk_params = &imx415_clk_params[i];
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(imx415_clk_params)) {
+ ret = dev_err_probe(sensor->dev, -EINVAL,
+ "Mode %d not supported\n",
+ sensor->cur_mode);
+ goto done_endpoint_free;
+ }
+
+ ret = 0;
+ dev_dbg(sensor->dev, "clock: %lu Hz, lane_rate: %llu bps, lanes: %d\n",
+ inck, lane_rate, sensor->num_data_lanes);
+
+done_endpoint_free:
+ v4l2_fwnode_endpoint_free(&bus_cfg);
+
+ return ret;
+}
+
+static int imx415_probe(struct i2c_client *client)
+{
+ struct imx415 *sensor;
+ int ret;
+
+ sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
+ if (!sensor)
+ return -ENOMEM;
+
+ sensor->dev = &client->dev;
+
+ ret = imx415_parse_hw_config(sensor);
+ if (ret)
+ return ret;
+
+ sensor->regmap = devm_regmap_init_i2c(client, &imx415_regmap_config);
+ if (IS_ERR(sensor->regmap))
+ return PTR_ERR(sensor->regmap);
+
+ /*
+ * Enable power management. The driver supports runtime PM, but needs to
+ * work when runtime PM is disabled in the kernel. To that end, power
+ * the sensor on manually here, identify it, and fully initialize it.
+ */
+ ret = imx415_power_on(sensor);
+ if (ret)
+ return ret;
+
+ ret = imx415_identify_model(sensor);
+ if (ret)
+ goto err_power;
+
+ ret = imx415_subdev_init(sensor);
+ if (ret)
+ goto err_power;
+
+ /*
+ * Enable runtime PM. As the device has been powered manually, mark it
+ * as active, and increase the usage count without resuming the device.
+ */
+ pm_runtime_set_active(sensor->dev);
+ pm_runtime_get_noresume(sensor->dev);
+ pm_runtime_enable(sensor->dev);
+
+ ret = v4l2_async_register_subdev_sensor(&sensor->subdev);
+ if (ret < 0)
+ goto err_pm;
+
+ /*
+ * Finally, enable autosuspend and decrease the usage count. The device
+ * will get suspended after the autosuspend delay, turning the power
+ * off.
+ */
+ pm_runtime_set_autosuspend_delay(sensor->dev, 1000);
+ pm_runtime_use_autosuspend(sensor->dev);
+ pm_runtime_put_autosuspend(sensor->dev);
+
+ return 0;
+
+err_pm:
+ pm_runtime_disable(sensor->dev);
+ pm_runtime_put_noidle(sensor->dev);
+ imx415_subdev_cleanup(sensor);
+err_power:
+ imx415_power_off(sensor);
+ return ret;
+}
+
+static void imx415_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct imx415 *sensor = to_imx415(subdev);
+
+ v4l2_async_unregister_subdev(subdev);
+
+ imx415_subdev_cleanup(sensor);
+
+ /*
+ * Disable runtime PM. In case runtime PM is disabled in the kernel,
+ * make sure to turn power off manually.
+ */
+ pm_runtime_disable(sensor->dev);
+ if (!pm_runtime_status_suspended(sensor->dev))
+ imx415_power_off(sensor);
+ pm_runtime_set_suspended(sensor->dev);
+}
+
+static int imx415_runtime_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct imx415 *sensor = to_imx415(subdev);
+
+ return imx415_power_on(sensor);
+}
+
+static int imx415_runtime_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct imx415 *sensor = to_imx415(subdev);
+
+ imx415_power_off(sensor);
+
+ return 0;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(imx415_pm_ops, imx415_runtime_suspend,
+ imx415_runtime_resume, NULL);
+
+static const struct of_device_id imx415_of_match[] = {
+ { .compatible = "sony,imx415" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, imx415_of_match);
+
+static struct i2c_driver imx415_driver = {
+ .probe_new = imx415_probe,
+ .remove = imx415_remove,
+ .driver = {
+ .name = "imx415",
+ .of_match_table = imx415_of_match,
+ .pm = pm_ptr(&imx415_pm_ops),
+ },
+};
+
+module_i2c_driver(imx415_driver);
+
+MODULE_DESCRIPTION("Sony IMX415 image sensor driver");
+MODULE_AUTHOR("Gerald Loacker <gerald.loacker@wolfvision.net>");
+MODULE_AUTHOR("Michael Riesch <michael.riesch@wolfvision.net>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c
index 25bf1132dbff..51921068931d 100644
--- a/drivers/media/i2c/ir-kbd-i2c.c
+++ b/drivers/media/i2c/ir-kbd-i2c.c
@@ -757,8 +757,9 @@ static int zilog_tx_duty_cycle(struct rc_dev *dev, u32 duty_cycle)
return 0;
}
-static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
+static int ir_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
char *ir_codes = NULL;
const char *name = NULL;
u64 rc_proto = RC_PROTO_BIT_UNKNOWN;
@@ -987,7 +988,7 @@ static struct i2c_driver ir_kbd_driver = {
.driver = {
.name = "ir-kbd-i2c",
},
- .probe = ir_probe,
+ .probe_new = ir_probe,
.remove = ir_remove,
.id_table = ir_kbd_id,
};
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index 9c083cf14231..701038d6d19b 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -72,7 +72,7 @@
#define MAX9286_DATATYPE_USER_YUV_12BIT (10 << 0)
#define MAX9286_DATATYPE_USER_24BIT (9 << 0)
#define MAX9286_DATATYPE_RAW14 (8 << 0)
-#define MAX9286_DATATYPE_RAW11 (7 << 0)
+#define MAX9286_DATATYPE_RAW12 (7 << 0)
#define MAX9286_DATATYPE_RAW10 (6 << 0)
#define MAX9286_DATATYPE_RAW8 (5 << 0)
#define MAX9286_DATATYPE_YUV422_10BIT (4 << 0)
@@ -81,13 +81,21 @@
#define MAX9286_DATATYPE_RGB565 (1 << 0)
#define MAX9286_DATATYPE_RGB888 (0 << 0)
/* Register 0x15 */
+#define MAX9286_CSI_IMAGE_TYP BIT(7)
#define MAX9286_VC(n) ((n) << 5)
#define MAX9286_VCTYPE BIT(4)
#define MAX9286_CSIOUTEN BIT(3)
-#define MAX9286_0X15_RESV (3 << 0)
+#define MAX9286_SWP_ENDIAN BIT(2)
+#define MAX9286_EN_CCBSYB_CLK_STR BIT(1)
+#define MAX9286_EN_GPI_CCBSYB BIT(0)
/* Register 0x1b */
#define MAX9286_SWITCHIN(n) (1 << ((n) + 4))
#define MAX9286_ENEQ(n) (1 << (n))
+/* Register 0x1c */
+#define MAX9286_HIGHIMM(n) BIT((n) + 4)
+#define MAX9286_I2CSEL BIT(2)
+#define MAX9286_HIBW BIT(1)
+#define MAX9286_BWS BIT(0)
/* Register 0x27 */
#define MAX9286_LOCKED BIT(7)
/* Register 0x31 */
@@ -136,9 +144,20 @@
#define MAX9286_N_PADS 5
#define MAX9286_SRC_PAD 4
+struct max9286_format_info {
+ u32 code;
+ u8 datatype;
+};
+
+struct max9286_i2c_speed {
+ u32 rate;
+ u8 mstbt;
+};
+
struct max9286_source {
struct v4l2_subdev *sd;
struct fwnode_handle *fwnode;
+ struct regulator *regulator;
};
struct max9286_asd {
@@ -168,13 +187,18 @@ struct max9286_priv {
/* The initial reverse control channel amplitude. */
u32 init_rev_chan_mv;
u32 rev_chan_mv;
+ u8 i2c_mstbt;
+ u32 bus_width;
+ bool use_gpio_poc;
u32 gpio_poc[2];
struct v4l2_ctrl_handler ctrls;
- struct v4l2_ctrl *pixelrate;
+ struct v4l2_ctrl *pixelrate_ctrl;
+ unsigned int pixelrate;
struct v4l2_mbus_framefmt fmt[MAX9286_N_SINKS];
+ struct v4l2_fract interval;
/* Protects controls and fmt structures */
struct mutex mutex;
@@ -214,6 +238,45 @@ static inline struct max9286_priv *sd_to_max9286(struct v4l2_subdev *sd)
return container_of(sd, struct max9286_priv, sd);
}
+static const struct max9286_format_info max9286_formats[] = {
+ {
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .datatype = MAX9286_DATATYPE_YUV422_8BIT,
+ }, {
+ .code = MEDIA_BUS_FMT_VYUY8_1X16,
+ .datatype = MAX9286_DATATYPE_YUV422_8BIT,
+ }, {
+ .code = MEDIA_BUS_FMT_YUYV8_1X16,
+ .datatype = MAX9286_DATATYPE_YUV422_8BIT,
+ }, {
+ .code = MEDIA_BUS_FMT_YVYU8_1X16,
+ .datatype = MAX9286_DATATYPE_YUV422_8BIT,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .datatype = MAX9286_DATATYPE_RAW12,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .datatype = MAX9286_DATATYPE_RAW12,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .datatype = MAX9286_DATATYPE_RAW12,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .datatype = MAX9286_DATATYPE_RAW12,
+ },
+};
+
+static const struct max9286_i2c_speed max9286_i2c_speeds[] = {
+ { .rate = 8470, .mstbt = MAX9286_I2CMSTBT_8KBPS },
+ { .rate = 28300, .mstbt = MAX9286_I2CMSTBT_28KBPS },
+ { .rate = 84700, .mstbt = MAX9286_I2CMSTBT_84KBPS },
+ { .rate = 105000, .mstbt = MAX9286_I2CMSTBT_105KBPS },
+ { .rate = 173000, .mstbt = MAX9286_I2CMSTBT_173KBPS },
+ { .rate = 339000, .mstbt = MAX9286_I2CMSTBT_339KBPS },
+ { .rate = 533000, .mstbt = MAX9286_I2CMSTBT_533KBPS },
+ { .rate = 837000, .mstbt = MAX9286_I2CMSTBT_837KBPS },
+};
+
/* -----------------------------------------------------------------------------
* I2C IO
*/
@@ -334,7 +397,7 @@ error:
static void max9286_configure_i2c(struct max9286_priv *priv, bool localack)
{
u8 config = MAX9286_I2CSLVSH_469NS_234NS | MAX9286_I2CSLVTO_1024US |
- MAX9286_I2CMSTBT_105KBPS;
+ priv->i2c_mstbt;
if (localack)
config |= MAX9286_I2CLOCACK;
@@ -475,6 +538,77 @@ static int max9286_check_config_link(struct max9286_priv *priv,
return 0;
}
+static void max9286_set_video_format(struct max9286_priv *priv,
+ const struct v4l2_mbus_framefmt *format)
+{
+ const struct max9286_format_info *info = NULL;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(max9286_formats); ++i) {
+ if (max9286_formats[i].code == format->code) {
+ info = &max9286_formats[i];
+ break;
+ }
+ }
+
+ if (WARN_ON(!info))
+ return;
+
+ /*
+ * Video format setup: disable CSI output, set VC according to Link
+ * number, enable I2C clock stretching when CCBSY is low, enable CCBSY
+ * in external GPI-to-GPO mode.
+ */
+ max9286_write(priv, 0x15, MAX9286_VCTYPE | MAX9286_EN_CCBSYB_CLK_STR |
+ MAX9286_EN_GPI_CCBSYB);
+
+ /* Enable CSI-2 Lane D0-D3 only, DBL mode. */
+ max9286_write(priv, 0x12, MAX9286_CSIDBL | MAX9286_DBL |
+ MAX9286_CSILANECNT(priv->csi2_data_lanes) |
+ info->datatype);
+
+ /*
+ * Enable HS/VS encoding, use HS as line valid source, use D14/15 for
+ * HS/VS, invert VS.
+ */
+ max9286_write(priv, 0x0c, MAX9286_HVEN | MAX9286_DESEL |
+ MAX9286_INVVS | MAX9286_HVSRC_D14);
+}
+
+static void max9286_set_fsync_period(struct max9286_priv *priv)
+{
+ u32 fsync;
+
+ if (!priv->interval.numerator || !priv->interval.denominator) {
+ /*
+ * Special case, a null interval enables automatic FRAMESYNC
+ * mode. FRAMESYNC is taken from the slowest link.
+ */
+ max9286_write(priv, 0x01, MAX9286_FSYNCMODE_INT_HIZ |
+ MAX9286_FSYNCMETH_AUTO);
+ return;
+ }
+
+ /*
+ * Manual FRAMESYNC
+ *
+ * The FRAMESYNC generator is configured with a period expressed as a
+ * number of PCLK periods.
+ */
+ fsync = div_u64((u64)priv->pixelrate * priv->interval.numerator,
+ priv->interval.denominator);
+
+ dev_dbg(&priv->client->dev, "fsync period %u (pclk %u)\n", fsync,
+ priv->pixelrate);
+
+ max9286_write(priv, 0x01, MAX9286_FSYNCMODE_INT_OUT |
+ MAX9286_FSYNCMETH_MANUAL);
+
+ max9286_write(priv, 0x06, (fsync >> 0) & 0xff);
+ max9286_write(priv, 0x07, (fsync >> 8) & 0xff);
+ max9286_write(priv, 0x08, (fsync >> 16) & 0xff);
+}
+
/* -----------------------------------------------------------------------------
* V4L2 Subdev
*/
@@ -513,11 +647,13 @@ static int max9286_set_pixelrate(struct max9286_priv *priv)
return -EINVAL;
}
+ priv->pixelrate = pixelrate;
+
/*
* The CSI-2 transmitter pixel rate is the single source rate multiplied
* by the number of available sources.
*/
- return v4l2_ctrl_s_ctrl_int64(priv->pixelrate,
+ return v4l2_ctrl_s_ctrl_int64(priv->pixelrate_ctrl,
pixelrate * priv->nsources);
}
@@ -657,6 +793,17 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable)
int ret;
if (enable) {
+ const struct v4l2_mbus_framefmt *format;
+
+ /*
+ * Get the format from the first used sink pad, as all sink
+ * formats must be identical.
+ */
+ format = &priv->fmt[__ffs(priv->bound_sources)];
+
+ max9286_set_video_format(priv, format);
+ max9286_set_fsync_period(priv);
+
/*
* The frame sync between cameras is transmitted across the
* reverse channel as GPIO. We must open all channels while
@@ -698,13 +845,17 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable)
}
/*
- * Enable CSI output, VC set according to link number.
- * Bit 7 must be set (chip manual says it's 0 and reserved).
+ * Configure the CSI-2 output to line interleaved mode (W x (N
+ * x H), as opposed to the (N x W) x H mode that outputs the
+ * images stitched side-by-side) and enable it.
*/
- max9286_write(priv, 0x15, 0x80 | MAX9286_VCTYPE |
- MAX9286_CSIOUTEN | MAX9286_0X15_RESV);
+ max9286_write(priv, 0x15, MAX9286_CSI_IMAGE_TYP | MAX9286_VCTYPE |
+ MAX9286_CSIOUTEN | MAX9286_EN_CCBSYB_CLK_STR |
+ MAX9286_EN_GPI_CCBSYB);
} else {
- max9286_write(priv, 0x15, MAX9286_VCTYPE | MAX9286_0X15_RESV);
+ max9286_write(priv, 0x15, MAX9286_VCTYPE |
+ MAX9286_EN_CCBSYB_CLK_STR |
+ MAX9286_EN_GPI_CCBSYB);
/* Stop all cameras. */
for_each_source(priv, source)
@@ -716,6 +867,32 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
+static int max9286_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *interval)
+{
+ struct max9286_priv *priv = sd_to_max9286(sd);
+
+ if (interval->pad != MAX9286_SRC_PAD)
+ return -EINVAL;
+
+ interval->interval = priv->interval;
+
+ return 0;
+}
+
+static int max9286_s_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *interval)
+{
+ struct max9286_priv *priv = sd_to_max9286(sd);
+
+ if (interval->pad != MAX9286_SRC_PAD)
+ return -EINVAL;
+
+ priv->interval = interval->interval;
+
+ return 0;
+}
+
static int max9286_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
@@ -749,22 +926,20 @@ static int max9286_set_fmt(struct v4l2_subdev *sd,
{
struct max9286_priv *priv = sd_to_max9286(sd);
struct v4l2_mbus_framefmt *cfg_fmt;
+ unsigned int i;
if (format->pad == MAX9286_SRC_PAD)
return -EINVAL;
- /* Refuse non YUV422 formats as we hardcode DT to 8 bit YUV422 */
- switch (format->format.code) {
- case MEDIA_BUS_FMT_UYVY8_1X16:
- case MEDIA_BUS_FMT_VYUY8_1X16:
- case MEDIA_BUS_FMT_YUYV8_1X16:
- case MEDIA_BUS_FMT_YVYU8_1X16:
- break;
- default:
- format->format.code = MEDIA_BUS_FMT_UYVY8_1X16;
- break;
+ /* Validate the format. */
+ for (i = 0; i < ARRAY_SIZE(max9286_formats); ++i) {
+ if (max9286_formats[i].code == format->format.code)
+ break;
}
+ if (i == ARRAY_SIZE(max9286_formats))
+ format->format.code = max9286_formats[0].code;
+
cfg_fmt = max9286_get_pad_format(priv, sd_state, format->pad,
format->which);
if (!cfg_fmt)
@@ -807,6 +982,8 @@ static int max9286_get_fmt(struct v4l2_subdev *sd,
static const struct v4l2_subdev_video_ops max9286_video_ops = {
.s_stream = max9286_s_stream,
+ .g_frame_interval = max9286_g_frame_interval,
+ .s_frame_interval = max9286_s_frame_interval,
};
static const struct v4l2_subdev_pad_ops max9286_pad_ops = {
@@ -820,16 +997,20 @@ static const struct v4l2_subdev_ops max9286_subdev_ops = {
.pad = &max9286_pad_ops,
};
+static const struct v4l2_mbus_framefmt max9286_default_format = {
+ .width = 1280,
+ .height = 800,
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .field = V4L2_FIELD_NONE,
+ .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
+ .quantization = V4L2_QUANTIZATION_DEFAULT,
+ .xfer_func = V4L2_XFER_FUNC_DEFAULT,
+};
+
static void max9286_init_format(struct v4l2_mbus_framefmt *fmt)
{
- fmt->width = 1280;
- fmt->height = 800;
- fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
- fmt->colorspace = V4L2_COLORSPACE_SRGB;
- fmt->field = V4L2_FIELD_NONE;
- fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
- fmt->quantization = V4L2_QUANTIZATION_DEFAULT;
- fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ *fmt = max9286_default_format;
}
static int max9286_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
@@ -891,10 +1072,10 @@ static int max9286_v4l2_register(struct max9286_priv *priv)
priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
v4l2_ctrl_handler_init(&priv->ctrls, 1);
- priv->pixelrate = v4l2_ctrl_new_std(&priv->ctrls,
- &max9286_ctrl_ops,
- V4L2_CID_PIXEL_RATE,
- 1, INT_MAX, 1, 50000000);
+ priv->pixelrate_ctrl = v4l2_ctrl_new_std(&priv->ctrls,
+ &max9286_ctrl_ops,
+ V4L2_CID_PIXEL_RATE,
+ 1, INT_MAX, 1, 50000000);
priv->sd.ctrl_handler = &priv->ctrls;
ret = priv->ctrls.error;
@@ -932,6 +1113,7 @@ static int max9286_v4l2_register(struct max9286_priv *priv)
err_put_node:
fwnode_handle_put(ep);
err_async:
+ v4l2_ctrl_handler_free(&priv->ctrls);
max9286_v4l2_notifier_unregister(priv);
return ret;
@@ -975,6 +1157,7 @@ static int max9286_setup(struct max9286_priv *priv)
(2 << 6) | (1 << 4) | (0 << 2) | (3 << 0), /* 210x */
(3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* 3210 */
};
+ int cfg;
/*
* Set the I2C bus speed.
@@ -993,24 +1176,27 @@ static int max9286_setup(struct max9286_priv *priv)
max9286_write(priv, 0x0b, link_order[priv->route_mask]);
max9286_write(priv, 0x69, (0xf & ~priv->route_mask));
- /*
- * Video format setup:
- * Disable CSI output, VC is set according to Link number.
- */
- max9286_write(priv, 0x15, MAX9286_VCTYPE | MAX9286_0X15_RESV);
+ max9286_set_video_format(priv, &max9286_default_format);
+ max9286_set_fsync_period(priv);
- /* Enable CSI-2 Lane D0-D3 only, DBL mode, YUV422 8-bit. */
- max9286_write(priv, 0x12, MAX9286_CSIDBL | MAX9286_DBL |
- MAX9286_CSILANECNT(priv->csi2_data_lanes) |
- MAX9286_DATATYPE_YUV422_8BIT);
+ cfg = max9286_read(priv, 0x1c);
+ if (cfg < 0)
+ return cfg;
+
+ dev_dbg(&priv->client->dev, "power-up config: %s immunity, %u-bit bus\n",
+ cfg & MAX9286_HIGHIMM(0) ? "high" : "legacy",
+ cfg & MAX9286_BWS ? 32 : cfg & MAX9286_HIBW ? 27 : 24);
- /* Automatic: FRAMESYNC taken from the slowest Link. */
- max9286_write(priv, 0x01, MAX9286_FSYNCMODE_INT_HIZ |
- MAX9286_FSYNCMETH_AUTO);
+ if (priv->bus_width) {
+ cfg &= ~(MAX9286_HIBW | MAX9286_BWS);
- /* Enable HS/VS encoding, use D14/15 for HS/VS, invert VS. */
- max9286_write(priv, 0x0c, MAX9286_HVEN | MAX9286_INVVS |
- MAX9286_HVSRC_D14);
+ if (priv->bus_width == 27)
+ cfg |= MAX9286_HIBW;
+ else if (priv->bus_width == 32)
+ cfg |= MAX9286_BWS;
+
+ max9286_write(priv, 0x1c, cfg);
+ }
/*
* The overlap window seems to provide additional validation by tracking
@@ -1088,9 +1274,6 @@ static int max9286_parse_gpios(struct max9286_priv *priv)
struct device *dev = &priv->client->dev;
int ret;
- /* GPIO values default to high */
- priv->gpio_state = BIT(0) | BIT(1);
-
/*
* Parse the "gpio-poc" vendor property. If the property is not
* specified the camera power is controlled by a regulator.
@@ -1102,18 +1285,7 @@ static int max9286_parse_gpios(struct max9286_priv *priv)
* If gpio lines are not used for the camera power, register
* a gpio controller for consumers.
*/
- ret = max9286_register_gpio(priv);
- if (ret)
- return ret;
-
- priv->regulator = devm_regulator_get(dev, "poc");
- if (IS_ERR(priv->regulator)) {
- return dev_err_probe(dev, PTR_ERR(priv->regulator),
- "Unable to get PoC regulator (%ld)\n",
- PTR_ERR(priv->regulator));
- }
-
- return 0;
+ return max9286_register_gpio(priv);
}
/* If the property is specified make sure it is well formed. */
@@ -1124,21 +1296,75 @@ static int max9286_parse_gpios(struct max9286_priv *priv)
return -EINVAL;
}
+ priv->use_gpio_poc = true;
+ return 0;
+}
+
+static int max9286_poc_power_on(struct max9286_priv *priv)
+{
+ struct max9286_source *source;
+ unsigned int enabled = 0;
+ int ret;
+
+ /* Enable the global regulator if available. */
+ if (priv->regulator)
+ return regulator_enable(priv->regulator);
+
+ if (priv->use_gpio_poc)
+ return max9286_gpio_set(priv, priv->gpio_poc[0],
+ !priv->gpio_poc[1]);
+
+ /* Otherwise use the per-port regulators. */
+ for_each_source(priv, source) {
+ ret = regulator_enable(source->regulator);
+ if (ret < 0)
+ goto error;
+
+ enabled |= BIT(to_index(priv, source));
+ }
+
return 0;
+
+error:
+ for_each_source(priv, source) {
+ if (enabled & BIT(to_index(priv, source)))
+ regulator_disable(source->regulator);
+ }
+
+ return ret;
+}
+
+static int max9286_poc_power_off(struct max9286_priv *priv)
+{
+ struct max9286_source *source;
+ int ret = 0;
+
+ if (priv->regulator)
+ return regulator_disable(priv->regulator);
+
+ if (priv->use_gpio_poc)
+ return max9286_gpio_set(priv, priv->gpio_poc[0],
+ priv->gpio_poc[1]);
+
+ for_each_source(priv, source) {
+ int err;
+
+ err = regulator_disable(source->regulator);
+ if (!ret)
+ ret = err;
+ }
+
+ return ret;
}
static int max9286_poc_enable(struct max9286_priv *priv, bool enable)
{
int ret;
- /* If the regulator is not available, use gpio to control power. */
- if (!priv->regulator)
- ret = max9286_gpio_set(priv, priv->gpio_poc[0],
- enable ^ priv->gpio_poc[1]);
- else if (enable)
- ret = regulator_enable(priv->regulator);
+ if (enable)
+ ret = max9286_poc_power_on(priv);
else
- ret = regulator_disable(priv->regulator);
+ ret = max9286_poc_power_off(priv);
if (ret < 0)
dev_err(&priv->client->dev, "Unable to turn power %s\n",
@@ -1208,6 +1434,8 @@ static int max9286_parse_dt(struct max9286_priv *priv)
struct device_node *node = NULL;
unsigned int i2c_mux_mask = 0;
u32 reverse_channel_microvolt;
+ u32 i2c_clk_freq = 105000;
+ unsigned int i;
/* Balance the of_node_put() performed by of_find_node_by_name(). */
of_node_get(dev->of_node);
@@ -1298,6 +1526,40 @@ static int max9286_parse_dt(struct max9286_priv *priv)
}
of_node_put(node);
+ of_property_read_u32(dev->of_node, "maxim,bus-width", &priv->bus_width);
+ switch (priv->bus_width) {
+ case 0:
+ /*
+ * The property isn't specified in the device tree, the driver
+ * will keep the default value selected by the BWS pin.
+ */
+ case 24:
+ case 27:
+ case 32:
+ break;
+ default:
+ dev_err(dev, "Invalid %s value %u\n", "maxim,bus-width",
+ priv->bus_width);
+ return -EINVAL;
+ }
+
+ of_property_read_u32(dev->of_node, "maxim,i2c-remote-bus-hz",
+ &i2c_clk_freq);
+ for (i = 0; i < ARRAY_SIZE(max9286_i2c_speeds); ++i) {
+ const struct max9286_i2c_speed *speed = &max9286_i2c_speeds[i];
+
+ if (speed->rate == i2c_clk_freq) {
+ priv->i2c_mstbt = speed->mstbt;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(max9286_i2c_speeds)) {
+ dev_err(dev, "Invalid %s value %u\n", "maxim,i2c-remote-bus-hz",
+ i2c_clk_freq);
+ return -EINVAL;
+ }
+
/*
* Parse the initial value of the reverse channel amplitude from
* the firmware interface and convert it to millivolts.
@@ -1317,6 +1579,44 @@ static int max9286_parse_dt(struct max9286_priv *priv)
return 0;
}
+static int max9286_get_poc_supplies(struct max9286_priv *priv)
+{
+ struct device *dev = &priv->client->dev;
+ struct max9286_source *source;
+ int ret;
+
+ /* Start by getting the global regulator. */
+ priv->regulator = devm_regulator_get_optional(dev, "poc");
+ if (!IS_ERR(priv->regulator))
+ return 0;
+
+ if (PTR_ERR(priv->regulator) != -ENODEV)
+ return dev_err_probe(dev, PTR_ERR(priv->regulator),
+ "Unable to get PoC regulator\n");
+
+ /* If there's no global regulator, get per-port regulators. */
+ dev_dbg(dev,
+ "No global PoC regulator, looking for per-port regulators\n");
+ priv->regulator = NULL;
+
+ for_each_source(priv, source) {
+ unsigned int index = to_index(priv, source);
+ char name[10];
+
+ snprintf(name, sizeof(name), "port%u-poc", index);
+ source->regulator = devm_regulator_get(dev, name);
+ if (IS_ERR(source->regulator)) {
+ ret = PTR_ERR(source->regulator);
+ dev_err_probe(dev, ret,
+ "Unable to get port %u PoC regulator\n",
+ index);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static int max9286_probe(struct i2c_client *client)
{
struct max9286_priv *priv;
@@ -1330,10 +1630,19 @@ static int max9286_probe(struct i2c_client *client)
priv->client = client;
+ /* GPIO values default to high */
+ priv->gpio_state = BIT(0) | BIT(1);
+
+ ret = max9286_parse_dt(priv);
+ if (ret)
+ goto err_cleanup_dt;
+
priv->gpiod_pwdn = devm_gpiod_get_optional(&client->dev, "enable",
GPIOD_OUT_HIGH);
- if (IS_ERR(priv->gpiod_pwdn))
- return PTR_ERR(priv->gpiod_pwdn);
+ if (IS_ERR(priv->gpiod_pwdn)) {
+ ret = PTR_ERR(priv->gpiod_pwdn);
+ goto err_cleanup_dt;
+ }
gpiod_set_consumer_name(priv->gpiod_pwdn, "max9286-pwdn");
gpiod_set_value_cansleep(priv->gpiod_pwdn, 1);
@@ -1360,9 +1669,11 @@ static int max9286_probe(struct i2c_client *client)
if (ret)
goto err_powerdown;
- ret = max9286_parse_dt(priv);
- if (ret)
- goto err_powerdown;
+ if (!priv->use_gpio_poc) {
+ ret = max9286_get_poc_supplies(priv);
+ if (ret)
+ goto err_cleanup_dt;
+ }
ret = max9286_init(priv);
if (ret < 0)
@@ -1370,10 +1681,10 @@ static int max9286_probe(struct i2c_client *client)
return 0;
-err_cleanup_dt:
- max9286_cleanup_dt(priv);
err_powerdown:
gpiod_set_value_cansleep(priv->gpiod_pwdn, 0);
+err_cleanup_dt:
+ max9286_cleanup_dt(priv);
return ret;
}
diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c
index 4ce7a15a9884..12032e28b428 100644
--- a/drivers/media/i2c/msp3400-driver.c
+++ b/drivers/media/i2c/msp3400-driver.c
@@ -663,8 +663,9 @@ static const char * const opmode_str[] = {
[OPMODE_AUTOSELECT] = "autodetect and autoselect",
};
-static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
+static int msp_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct msp_state *state;
struct v4l2_subdev *sd;
struct v4l2_ctrl_handler *hdl;
@@ -891,7 +892,7 @@ static struct i2c_driver msp_driver = {
.name = "msp3400",
.pm = &msp3400_pm_ops,
},
- .probe = msp_probe,
+ .probe_new = msp_probe,
.remove = msp_remove,
.id_table = msp_id,
};
diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
index 4ffc2f6e7db4..9e023a4b9bd1 100644
--- a/drivers/media/i2c/mt9p031.c
+++ b/drivers/media/i2c/mt9p031.c
@@ -1102,9 +1102,9 @@ done:
return pdata;
}
-static int mt9p031_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
+static int mt9p031_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *did = i2c_client_get_device_id(client);
struct mt9p031_platform_data *pdata = mt9p031_get_pdata(client);
struct i2c_adapter *adapter = client->adapter;
struct mt9p031 *mt9p031;
@@ -1248,7 +1248,7 @@ static struct i2c_driver mt9p031_i2c_driver = {
.of_match_table = of_match_ptr(mt9p031_of_match),
.name = "mt9p031",
},
- .probe = mt9p031_probe,
+ .probe_new = mt9p031_probe,
.remove = mt9p031_remove,
.id_table = mt9p031_id,
};
diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c
index bc4388ccc2a8..7cfd4ebdd2e6 100644
--- a/drivers/media/i2c/mt9v032.c
+++ b/drivers/media/i2c/mt9v032.c
@@ -1044,9 +1044,9 @@ done:
return pdata;
}
-static int mt9v032_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
+static int mt9v032_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *did = i2c_client_get_device_id(client);
struct mt9v032_platform_data *pdata = mt9v032_get_pdata(client);
struct mt9v032 *mt9v032;
unsigned int i;
@@ -1296,7 +1296,7 @@ static struct i2c_driver mt9v032_driver = {
.name = "mt9v032",
.of_match_table = of_match_ptr(mt9v032_of_match),
},
- .probe = mt9v032_probe,
+ .probe_new = mt9v032_probe,
.remove = mt9v032_remove,
.id_table = mt9v032_id,
};
diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c
index f3731f932a94..89d126240c34 100644
--- a/drivers/media/i2c/ov2740.c
+++ b/drivers/media/i2c/ov2740.c
@@ -629,8 +629,10 @@ static int ov2740_init_controls(struct ov2740 *ov2740)
V4L2_CID_TEST_PATTERN,
ARRAY_SIZE(ov2740_test_pattern_menu) - 1,
0, 0, ov2740_test_pattern_menu);
- if (ctrl_hdlr->error)
+ if (ctrl_hdlr->error) {
+ v4l2_ctrl_handler_free(ctrl_hdlr);
return ctrl_hdlr->error;
+ }
ov2740->sd.ctrl_handler = ctrl_hdlr;
diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index e0f908af581b..1536649b9e90 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -50,6 +50,7 @@
#define OV5640_REG_SYS_CTRL0 0x3008
#define OV5640_REG_SYS_CTRL0_SW_PWDN 0x42
#define OV5640_REG_SYS_CTRL0_SW_PWUP 0x02
+#define OV5640_REG_SYS_CTRL0_SW_RST 0x82
#define OV5640_REG_CHIP_ID 0x300a
#define OV5640_REG_IO_MIPI_CTRL00 0x300e
#define OV5640_REG_PAD_OUTPUT_ENABLE01 0x3017
@@ -520,7 +521,18 @@ static u32 ov5640_code_to_bpp(struct ov5640_dev *sensor, u32 code)
*/
/* YUV422 UYVY VGA@30fps */
-static const struct v4l2_mbus_framefmt ov5640_default_fmt = {
+static const struct v4l2_mbus_framefmt ov5640_csi2_default_fmt = {
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .width = 640,
+ .height = 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_SRGB),
+ .quantization = V4L2_QUANTIZATION_FULL_RANGE,
+ .xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(V4L2_COLORSPACE_SRGB),
+ .field = V4L2_FIELD_NONE,
+};
+
+static const struct v4l2_mbus_framefmt ov5640_dvp_default_fmt = {
.code = MEDIA_BUS_FMT_UYVY8_2X8,
.width = 640,
.height = 480,
@@ -532,7 +544,7 @@ static const struct v4l2_mbus_framefmt ov5640_default_fmt = {
};
static const struct reg_value ov5640_init_setting[] = {
- {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0},
+ {0x3103, 0x11, 0, 0},
{0x3103, 0x03, 0, 0}, {0x3630, 0x36, 0, 0},
{0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0},
{0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0},
@@ -2424,24 +2436,48 @@ static void ov5640_power(struct ov5640_dev *sensor, bool enable)
gpiod_set_value_cansleep(sensor->pwdn_gpio, enable ? 0 : 1);
}
-static void ov5640_reset(struct ov5640_dev *sensor)
+/*
+ * From section 2.7 power up sequence:
+ * t0 + t1 + t2 >= 5ms Delay from DOVDD stable to PWDN pull down
+ * t3 >= 1ms Delay from PWDN pull down to RESETB pull up
+ * t4 >= 20ms Delay from RESETB pull up to SCCB (i2c) stable
+ *
+ * Some modules don't expose RESETB/PWDN pins directly, instead providing a
+ * "PWUP" GPIO which is wired through appropriate delays and inverters to the
+ * pins.
+ *
+ * In such cases, this gpio should be mapped to pwdn_gpio in the driver, and we
+ * should still toggle the pwdn_gpio below with the appropriate delays, while
+ * the calls to reset_gpio will be ignored.
+ */
+static void ov5640_powerup_sequence(struct ov5640_dev *sensor)
{
- if (!sensor->reset_gpio)
- return;
-
- gpiod_set_value_cansleep(sensor->reset_gpio, 0);
+ if (sensor->pwdn_gpio) {
+ gpiod_set_value_cansleep(sensor->reset_gpio, 0);
- /* camera power cycle */
- ov5640_power(sensor, false);
- usleep_range(5000, 10000);
- ov5640_power(sensor, true);
- usleep_range(5000, 10000);
+ /* camera power cycle */
+ ov5640_power(sensor, false);
+ usleep_range(5000, 10000);
+ ov5640_power(sensor, true);
+ usleep_range(5000, 10000);
- gpiod_set_value_cansleep(sensor->reset_gpio, 1);
- usleep_range(1000, 2000);
+ gpiod_set_value_cansleep(sensor->reset_gpio, 1);
+ usleep_range(1000, 2000);
- gpiod_set_value_cansleep(sensor->reset_gpio, 0);
+ gpiod_set_value_cansleep(sensor->reset_gpio, 0);
+ } else {
+ /* software reset */
+ ov5640_write_reg(sensor, OV5640_REG_SYS_CTRL0,
+ OV5640_REG_SYS_CTRL0_SW_RST);
+ }
usleep_range(20000, 25000);
+
+ /*
+ * software standby: allows registers programming;
+ * exit at restore_mode() for CSI, s_stream(1) for DVP
+ */
+ ov5640_write_reg(sensor, OV5640_REG_SYS_CTRL0,
+ OV5640_REG_SYS_CTRL0_SW_PWDN);
}
static int ov5640_set_power_on(struct ov5640_dev *sensor)
@@ -2464,8 +2500,7 @@ static int ov5640_set_power_on(struct ov5640_dev *sensor)
goto xclk_off;
}
- ov5640_reset(sensor);
- ov5640_power(sensor, true);
+ ov5640_powerup_sequence(sensor);
ret = ov5640_init_slave_id(sensor);
if (ret)
@@ -3316,6 +3351,7 @@ static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
break;
}
+ pm_runtime_mark_last_busy(&sensor->i2c_client->dev);
pm_runtime_put_autosuspend(&sensor->i2c_client->dev);
return 0;
@@ -3391,6 +3427,7 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
break;
}
+ pm_runtime_mark_last_busy(&sensor->i2c_client->dev);
pm_runtime_put_autosuspend(&sensor->i2c_client->dev);
return ret;
@@ -3458,7 +3495,7 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
/* Auto/manual gain */
ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN,
0, 1, 1, 1);
- ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
+ ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
0, 1023, 1, 0);
ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION,
@@ -3710,8 +3747,10 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int enable)
out:
mutex_unlock(&sensor->lock);
- if (!enable || ret)
+ if (!enable || ret) {
+ pm_runtime_mark_last_busy(&sensor->i2c_client->dev);
pm_runtime_put_autosuspend(&sensor->i2c_client->dev);
+ }
return ret;
}
@@ -3719,11 +3758,13 @@ out:
static int ov5640_init_cfg(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state)
{
+ struct ov5640_dev *sensor = to_ov5640_dev(sd);
struct v4l2_mbus_framefmt *fmt =
v4l2_subdev_get_try_format(sd, state, 0);
struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, state, 0);
- *fmt = ov5640_default_fmt;
+ *fmt = ov5640_is_csi2(sensor) ? ov5640_csi2_default_fmt :
+ ov5640_dvp_default_fmt;
crop->left = OV5640_PIXEL_ARRAY_LEFT;
crop->top = OV5640_PIXEL_ARRAY_TOP;
@@ -3812,7 +3853,6 @@ static int ov5640_probe(struct i2c_client *client)
* default init sequence initialize sensor to
* YUV422 UYVY VGA@30fps
*/
- sensor->fmt = ov5640_default_fmt;
sensor->frame_interval.numerator = 1;
sensor->frame_interval.denominator = ov5640_framerates[OV5640_30_FPS];
sensor->current_fr = OV5640_30_FPS;
@@ -3845,6 +3885,9 @@ static int ov5640_probe(struct i2c_client *client)
return -EINVAL;
}
+ sensor->fmt = ov5640_is_csi2(sensor) ? ov5640_csi2_default_fmt :
+ ov5640_dvp_default_fmt;
+
/* get system clock (xclk) */
sensor->xclk = devm_clk_get(dev, "xclk");
if (IS_ERR(sensor->xclk)) {
@@ -3912,6 +3955,7 @@ static int ov5640_probe(struct i2c_client *client)
pm_runtime_set_autosuspend_delay(dev, 1000);
pm_runtime_use_autosuspend(dev);
+ pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;
diff --git a/drivers/media/i2c/ov5670.c b/drivers/media/i2c/ov5670.c
index bc9fc3bc90c2..f79d908f4531 100644
--- a/drivers/media/i2c/ov5670.c
+++ b/drivers/media/i2c/ov5670.c
@@ -1,15 +1,24 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2017 Intel Corporation.
+#include <asm/unaligned.h>
#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
#include <media/v4l2-fwnode.h>
+#define OV5670_XVCLK_FREQ 19200000
+
#define OV5670_REG_CHIP_ID 0x300a
#define OV5670_CHIP_ID 0x005670
@@ -65,6 +74,10 @@
#define OV5670_REG_VALUE_16BIT 2
#define OV5670_REG_VALUE_24BIT 3
+/* Pixel Array */
+#define OV5670_NATIVE_WIDTH 2624
+#define OV5670_NATIVE_HEIGHT 1980
+
/* Initial number of frames to skip to avoid possible garbage */
#define OV5670_NUM_OF_SKIP_FRAMES 2
@@ -83,6 +96,14 @@ struct ov5670_link_freq_config {
const struct ov5670_reg_list reg_list;
};
+static const char * const ov5670_supply_names[] = {
+ "avdd", /* Analog power */
+ "dvdd", /* Digital power */
+ "dovdd", /* Digital output power */
+};
+
+#define OV5670_NUM_SUPPLIES ARRAY_SIZE(ov5670_supply_names)
+
struct ov5670_mode {
/* Frame width in pixels */
u32 width;
@@ -99,10 +120,25 @@ struct ov5670_mode {
/* Link frequency needed for this resolution */
u32 link_freq_index;
+ /* Analog crop rectangle */
+ const struct v4l2_rect *analog_crop;
+
/* Sensor register settings for this resolution */
const struct ov5670_reg_list reg_list;
};
+/*
+ * All the modes supported by the driver are obtained by subsampling the
+ * full pixel array. The below values are reflected in registers from
+ * 0x3800-0x3807 in the modes register-value tables.
+ */
+static const struct v4l2_rect ov5670_analog_crop = {
+ .left = 12,
+ .top = 4,
+ .width = 2600,
+ .height = 1952,
+};
+
static const struct ov5670_reg mipi_data_rate_840mbps[] = {
{0x0300, 0x04},
{0x0301, 0x00},
@@ -1750,66 +1786,73 @@ static const struct ov5670_mode supported_modes[] = {
.height = 1944,
.vts_def = OV5670_VTS_30FPS,
.vts_min = OV5670_VTS_30FPS,
+ .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
+ .analog_crop = &ov5670_analog_crop,
.reg_list = {
.num_of_regs = ARRAY_SIZE(mode_2592x1944_regs),
.regs = mode_2592x1944_regs,
},
- .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
},
{
.width = 1296,
.height = 972,
.vts_def = OV5670_VTS_30FPS,
.vts_min = 996,
+ .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
+ .analog_crop = &ov5670_analog_crop,
.reg_list = {
.num_of_regs = ARRAY_SIZE(mode_1296x972_regs),
.regs = mode_1296x972_regs,
},
- .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
},
{
.width = 648,
.height = 486,
.vts_def = OV5670_VTS_30FPS,
.vts_min = 516,
+ .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
+ .analog_crop = &ov5670_analog_crop,
.reg_list = {
.num_of_regs = ARRAY_SIZE(mode_648x486_regs),
.regs = mode_648x486_regs,
},
- .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
},
{
.width = 2560,
.height = 1440,
.vts_def = OV5670_VTS_30FPS,
.vts_min = OV5670_VTS_30FPS,
+ .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
+ .analog_crop = &ov5670_analog_crop,
.reg_list = {
.num_of_regs = ARRAY_SIZE(mode_2560x1440_regs),
.regs = mode_2560x1440_regs,
},
- .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
},
{
.width = 1280,
.height = 720,
.vts_def = OV5670_VTS_30FPS,
.vts_min = 1020,
+
+ .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
+ .analog_crop = &ov5670_analog_crop,
.reg_list = {
.num_of_regs = ARRAY_SIZE(mode_1280x720_regs),
.regs = mode_1280x720_regs,
},
- .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
},
{
.width = 640,
.height = 360,
.vts_def = OV5670_VTS_30FPS,
.vts_min = 510,
+ .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
+ .analog_crop = &ov5670_analog_crop,
.reg_list = {
.num_of_regs = ARRAY_SIZE(mode_640x360_regs),
.regs = mode_640x360_regs,
},
- .link_freq_index = OV5670_LINK_FREQ_422MHZ_INDEX,
}
};
@@ -1828,6 +1871,16 @@ struct ov5670 {
/* Current mode */
const struct ov5670_mode *cur_mode;
+ /* xvclk input clock */
+ struct clk *xvclk;
+
+ /* Regulators */
+ struct regulator_bulk_data supplies[OV5670_NUM_SUPPLIES];
+
+ /* Power-down and reset gpios. */
+ struct gpio_desc *pwdn_gpio; /* PWDNB pin. */
+ struct gpio_desc *reset_gpio; /* XSHUTDOWN pin. */
+
/* To serialize asynchronus callbacks */
struct mutex mutex;
@@ -1935,27 +1988,6 @@ static int ov5670_write_reg_list(struct ov5670 *ov5670,
return ov5670_write_regs(ov5670, r_list->regs, r_list->num_of_regs);
}
-/* Open sub-device */
-static int ov5670_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
-{
- struct ov5670 *ov5670 = to_ov5670(sd);
- struct v4l2_mbus_framefmt *try_fmt =
- v4l2_subdev_get_try_format(sd, fh->state, 0);
-
- mutex_lock(&ov5670->mutex);
-
- /* Initialize try_fmt */
- try_fmt->width = ov5670->cur_mode->width;
- try_fmt->height = ov5670->cur_mode->height;
- try_fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
- try_fmt->field = V4L2_FIELD_NONE;
-
- /* No crop or compose */
- mutex_unlock(&ov5670->mutex);
-
- return 0;
-}
-
static int ov5670_update_digital_gain(struct ov5670 *ov5670, u32 d_gain)
{
int ret;
@@ -2006,7 +2038,7 @@ static int ov5670_set_ctrl(struct v4l2_ctrl *ctrl)
struct ov5670, ctrl_handler);
struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd);
s64 max;
- int ret = 0;
+ int ret;
/* Propagate change of current control to all related controls */
switch (ctrl->id) {
@@ -2045,7 +2077,13 @@ static int ov5670_set_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_TEST_PATTERN:
ret = ov5670_enable_test_pattern(ov5670, ctrl->val);
break;
+ case V4L2_CID_HBLANK:
+ case V4L2_CID_LINK_FREQ:
+ case V4L2_CID_PIXEL_RATE:
+ ret = 0;
+ break;
default:
+ ret = -EINVAL;
dev_info(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
__func__, ctrl->id, ctrl->val);
break;
@@ -2155,6 +2193,28 @@ error:
return ret;
}
+static int ov5670_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *fmt =
+ v4l2_subdev_get_try_format(sd, state, 0);
+ const struct ov5670_mode *default_mode = &supported_modes[0];
+ struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, state, 0);
+
+ fmt->width = default_mode->width;
+ fmt->height = default_mode->height;
+ fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_SRGB);
+ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(V4L2_COLORSPACE_SRGB);
+
+ *crop = *default_mode->analog_crop;
+
+ return 0;
+}
+
static int ov5670_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
@@ -2404,6 +2464,49 @@ unlock_and_return:
return ret;
}
+static int __maybe_unused ov5670_runtime_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov5670 *ov5670 = to_ov5670(sd);
+ unsigned long delay_us;
+ int ret;
+
+ ret = clk_prepare_enable(ov5670->xvclk);
+ if (ret)
+ return ret;
+
+ ret = regulator_bulk_enable(OV5670_NUM_SUPPLIES, ov5670->supplies);
+ if (ret) {
+ clk_disable_unprepare(ov5670->xvclk);
+ return ret;
+ }
+
+ gpiod_set_value_cansleep(ov5670->pwdn_gpio, 0);
+ gpiod_set_value_cansleep(ov5670->reset_gpio, 0);
+
+ /* 8192 * 2 clock pulses before the first SCCB transaction. */
+ delay_us = DIV_ROUND_UP(8192 * 2 * 1000,
+ DIV_ROUND_UP(OV5670_XVCLK_FREQ, 1000));
+ fsleep(delay_us);
+
+ return 0;
+}
+
+static int __maybe_unused ov5670_runtime_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov5670 *ov5670 = to_ov5670(sd);
+
+ gpiod_set_value_cansleep(ov5670->reset_gpio, 1);
+ gpiod_set_value_cansleep(ov5670->pwdn_gpio, 1);
+ regulator_bulk_disable(OV5670_NUM_SUPPLIES, ov5670->supplies);
+ clk_disable_unprepare(ov5670->xvclk);
+
+ return 0;
+}
+
static int __maybe_unused ov5670_suspend(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
@@ -2438,15 +2541,64 @@ static const struct v4l2_subdev_core_ops ov5670_core_ops = {
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
};
+static const struct v4l2_rect *
+__ov5670_get_pad_crop(struct ov5670 *sensor, struct v4l2_subdev_state *state,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ const struct ov5670_mode *mode = sensor->cur_mode;
+
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_crop(&sensor->sd, state, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return mode->analog_crop;
+ }
+
+ return NULL;
+}
+
+static int ov5670_get_selection(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct ov5670 *sensor = to_ov5670(subdev);
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ mutex_lock(&sensor->mutex);
+ sel->r = *__ov5670_get_pad_crop(sensor, state, sel->pad,
+ sel->which);
+ mutex_unlock(&sensor->mutex);
+ break;
+ case V4L2_SEL_TGT_NATIVE_SIZE:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.top = 0;
+ sel->r.left = 0;
+ sel->r.width = OV5670_NATIVE_WIDTH;
+ sel->r.height = OV5670_NATIVE_HEIGHT;
+ break;
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ sel->r = ov5670_analog_crop;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static const struct v4l2_subdev_video_ops ov5670_video_ops = {
.s_stream = ov5670_set_stream,
};
static const struct v4l2_subdev_pad_ops ov5670_pad_ops = {
+ .init_cfg = ov5670_init_cfg,
.enum_mbus_code = ov5670_enum_mbus_code,
.get_fmt = ov5670_get_pad_format,
.set_fmt = ov5670_set_pad_format,
.enum_frame_size = ov5670_enum_frame_size,
+ .get_selection = ov5670_get_selection,
+ .set_selection = ov5670_get_selection,
};
static const struct v4l2_subdev_sensor_ops ov5670_sensor_ops = {
@@ -2464,9 +2616,34 @@ static const struct media_entity_operations ov5670_subdev_entity_ops = {
.link_validate = v4l2_subdev_link_validate,
};
-static const struct v4l2_subdev_internal_ops ov5670_internal_ops = {
- .open = ov5670_open,
-};
+static int ov5670_regulators_probe(struct ov5670 *ov5670)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd);
+ unsigned int i;
+
+ for (i = 0; i < OV5670_NUM_SUPPLIES; i++)
+ ov5670->supplies[i].supply = ov5670_supply_names[i];
+
+ return devm_regulator_bulk_get(&client->dev, OV5670_NUM_SUPPLIES,
+ ov5670->supplies);
+}
+
+static int ov5670_gpio_probe(struct ov5670 *ov5670)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd);
+
+ ov5670->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ov5670->pwdn_gpio))
+ return PTR_ERR(ov5670->pwdn_gpio);
+
+ ov5670->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ov5670->reset_gpio))
+ return PTR_ERR(ov5670->reset_gpio);
+
+ return 0;
+}
static int ov5670_probe(struct i2c_client *client)
{
@@ -2476,10 +2653,6 @@ static int ov5670_probe(struct i2c_client *client)
bool full_power;
int ret;
- device_property_read_u32(&client->dev, "clock-frequency", &input_clk);
- if (input_clk != 19200000)
- return -EINVAL;
-
ov5670 = devm_kzalloc(&client->dev, sizeof(*ov5670), GFP_KERNEL);
if (!ov5670) {
ret = -ENOMEM;
@@ -2487,16 +2660,50 @@ static int ov5670_probe(struct i2c_client *client)
goto error_print;
}
+ ov5670->xvclk = devm_clk_get(&client->dev, NULL);
+ if (!IS_ERR_OR_NULL(ov5670->xvclk))
+ input_clk = clk_get_rate(ov5670->xvclk);
+ else if (PTR_ERR(ov5670->xvclk) == -ENOENT)
+ device_property_read_u32(&client->dev, "clock-frequency",
+ &input_clk);
+ else
+ return dev_err_probe(&client->dev, PTR_ERR(ov5670->xvclk),
+ "error getting clock\n");
+
+ if (input_clk != OV5670_XVCLK_FREQ) {
+ dev_err(&client->dev,
+ "Unsupported clock frequency %u\n", input_clk);
+ return -EINVAL;
+ }
+
/* Initialize subdev */
v4l2_i2c_subdev_init(&ov5670->sd, client, &ov5670_subdev_ops);
+ ret = ov5670_regulators_probe(ov5670);
+ if (ret) {
+ err_msg = "Regulators probe failed";
+ goto error_print;
+ }
+
+ ret = ov5670_gpio_probe(ov5670);
+ if (ret) {
+ err_msg = "GPIO probe failed";
+ goto error_print;
+ }
+
full_power = acpi_dev_state_d0(&client->dev);
if (full_power) {
+ ret = ov5670_runtime_resume(&client->dev);
+ if (ret) {
+ err_msg = "Power up failed";
+ goto error_print;
+ }
+
/* Check module identity */
ret = ov5670_identify_module(ov5670);
if (ret) {
err_msg = "ov5670_identify_module() error";
- goto error_print;
+ goto error_power_off;
}
}
@@ -2511,7 +2718,6 @@ static int ov5670_probe(struct i2c_client *client)
goto error_mutex_destroy;
}
- ov5670->sd.internal_ops = &ov5670_internal_ops;
ov5670->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
V4L2_SUBDEV_FL_HAS_EVENTS;
ov5670->sd.entity.ops = &ov5670_subdev_entity_ops;
@@ -2525,24 +2731,27 @@ static int ov5670_probe(struct i2c_client *client)
goto error_handler_free;
}
- /* Async register for subdev */
- ret = v4l2_async_register_subdev_sensor(&ov5670->sd);
- if (ret < 0) {
- err_msg = "v4l2_async_register_subdev() error";
- goto error_entity_cleanup;
- }
-
ov5670->streaming = false;
/* Set the device's state to active if it's in D0 state. */
if (full_power)
pm_runtime_set_active(&client->dev);
pm_runtime_enable(&client->dev);
+
+ /* Async register for subdev */
+ ret = v4l2_async_register_subdev_sensor(&ov5670->sd);
+ if (ret < 0) {
+ err_msg = "v4l2_async_register_subdev() error";
+ goto error_pm_disable;
+ }
+
pm_runtime_idle(&client->dev);
return 0;
-error_entity_cleanup:
+error_pm_disable:
+ pm_runtime_disable(&client->dev);
+
media_entity_cleanup(&ov5670->sd.entity);
error_handler_free:
@@ -2551,6 +2760,10 @@ error_handler_free:
error_mutex_destroy:
mutex_destroy(&ov5670->mutex);
+error_power_off:
+ if (full_power)
+ ov5670_runtime_suspend(&client->dev);
+
error_print:
dev_err(&client->dev, "%s: %s %d\n", __func__, err_msg, ret);
@@ -2568,10 +2781,12 @@ static void ov5670_remove(struct i2c_client *client)
mutex_destroy(&ov5670->mutex);
pm_runtime_disable(&client->dev);
+ ov5670_runtime_suspend(&client->dev);
}
static const struct dev_pm_ops ov5670_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(ov5670_suspend, ov5670_resume)
+ SET_RUNTIME_PM_OPS(ov5670_runtime_suspend, ov5670_runtime_resume, NULL)
};
#ifdef CONFIG_ACPI
@@ -2583,11 +2798,18 @@ static const struct acpi_device_id ov5670_acpi_ids[] = {
MODULE_DEVICE_TABLE(acpi, ov5670_acpi_ids);
#endif
+static const struct of_device_id ov5670_of_ids[] = {
+ { .compatible = "ovti,ov5670" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ov5670_of_ids);
+
static struct i2c_driver ov5670_i2c_driver = {
.driver = {
.name = "ov5670",
.pm = &ov5670_pm_ops,
.acpi_match_table = ACPI_PTR(ov5670_acpi_ids),
+ .of_match_table = ov5670_of_ids,
},
.probe_new = ov5670_probe,
.remove = ov5670_remove,
diff --git a/drivers/media/i2c/ov5675.c b/drivers/media/i2c/ov5675.c
index 94dc8cb7a7c0..d55180b3b7aa 100644
--- a/drivers/media/i2c/ov5675.c
+++ b/drivers/media/i2c/ov5675.c
@@ -3,10 +3,14 @@
#include <asm/unaligned.h>
#include <linux/acpi.h>
+#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
@@ -17,7 +21,7 @@
#define OV5675_LINK_FREQ_450MHZ 450000000ULL
#define OV5675_SCLK 90000000LL
-#define OV5675_MCLK 19200000
+#define OV5675_XVCLK_19_2 19200000
#define OV5675_DATA_LANES 2
#define OV5675_RGB_DEPTH 10
@@ -76,6 +80,14 @@
#define to_ov5675(_sd) container_of(_sd, struct ov5675, sd)
+static const char * const ov5675_supply_names[] = {
+ "avdd", /* Analog power */
+ "dovdd", /* Digital I/O power */
+ "dvdd", /* Digital core power */
+};
+
+#define OV5675_NUM_SUPPLIES ARRAY_SIZE(ov5675_supply_names)
+
enum {
OV5675_LINK_FREQ_900MBPS,
};
@@ -484,6 +496,9 @@ struct ov5675 {
struct v4l2_subdev sd;
struct media_pad pad;
struct v4l2_ctrl_handler ctrl_handler;
+ struct clk *xvclk;
+ struct gpio_desc *reset_gpio;
+ struct regulator_bulk_data supplies[OV5675_NUM_SUPPLIES];
/* V4L2 Controls */
struct v4l2_ctrl *link_freq;
@@ -764,12 +779,14 @@ static const struct v4l2_ctrl_ops ov5675_ctrl_ops = {
static int ov5675_init_controls(struct ov5675 *ov5675)
{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov5675->sd);
+ struct v4l2_fwnode_device_properties props;
struct v4l2_ctrl_handler *ctrl_hdlr;
s64 exposure_max, h_blank;
int ret;
ctrl_hdlr = &ov5675->ctrl_handler;
- ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8);
+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10);
if (ret)
return ret;
@@ -820,12 +837,28 @@ static int ov5675_init_controls(struct ov5675 *ov5675)
v4l2_ctrl_new_std(ctrl_hdlr, &ov5675_ctrl_ops,
V4L2_CID_VFLIP, 0, 1, 1, 0);
- if (ctrl_hdlr->error)
+ if (ctrl_hdlr->error) {
+ v4l2_ctrl_handler_free(ctrl_hdlr);
return ctrl_hdlr->error;
+ }
+
+ ret = v4l2_fwnode_device_parse(&client->dev, &props);
+ if (ret)
+ goto error;
+
+ ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov5675_ctrl_ops,
+ &props);
+ if (ret)
+ goto error;
ov5675->sd.ctrl_handler = ctrl_hdlr;
return 0;
+
+error:
+ v4l2_ctrl_handler_free(ctrl_hdlr);
+
+ return ret;
}
static void ov5675_update_pad_format(const struct ov5675_mode *mode,
@@ -944,6 +977,56 @@ static int ov5675_set_stream(struct v4l2_subdev *sd, int enable)
return ret;
}
+static int ov5675_power_off(struct device *dev)
+{
+ /* 512 xvclk cycles after the last SCCB transation or MIPI frame end */
+ u32 delay_us = DIV_ROUND_UP(512, OV5675_XVCLK_19_2 / 1000 / 1000);
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct ov5675 *ov5675 = to_ov5675(sd);
+
+ usleep_range(delay_us, delay_us * 2);
+
+ clk_disable_unprepare(ov5675->xvclk);
+ gpiod_set_value_cansleep(ov5675->reset_gpio, 1);
+ regulator_bulk_disable(OV5675_NUM_SUPPLIES, ov5675->supplies);
+
+ return 0;
+}
+
+static int ov5675_power_on(struct device *dev)
+{
+ u32 delay_us = DIV_ROUND_UP(8192, OV5675_XVCLK_19_2 / 1000 / 1000);
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct ov5675 *ov5675 = to_ov5675(sd);
+ int ret;
+
+ ret = clk_prepare_enable(ov5675->xvclk);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable xvclk: %d\n", ret);
+ return ret;
+ }
+
+ gpiod_set_value_cansleep(ov5675->reset_gpio, 1);
+
+ ret = regulator_bulk_enable(OV5675_NUM_SUPPLIES, ov5675->supplies);
+ if (ret) {
+ clk_disable_unprepare(ov5675->xvclk);
+ return ret;
+ }
+
+ /* Reset pulse should be at least 2ms and reset gpio released only once
+ * regulators are stable.
+ */
+ usleep_range(2000, 2200);
+
+ gpiod_set_value_cansleep(ov5675->reset_gpio, 0);
+
+ /* 8192 xvclk cycles prior to the first SCCB transation */
+ usleep_range(delay_us, delay_us * 2);
+
+ return 0;
+}
+
static int __maybe_unused ov5675_suspend(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
@@ -1040,6 +1123,31 @@ static int ov5675_get_format(struct v4l2_subdev *sd,
return 0;
}
+static int ov5675_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.top = 0;
+ sel->r.left = 0;
+ sel->r.width = 2624;
+ sel->r.height = 2000;
+ return 0;
+ case V4L2_SEL_TGT_CROP:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ sel->r.top = 16;
+ sel->r.left = 16;
+ sel->r.width = 2592;
+ sel->r.height = 1944;
+ return 0;
+ }
+ return -EINVAL;
+}
+
static int ov5675_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
@@ -1089,6 +1197,7 @@ static const struct v4l2_subdev_video_ops ov5675_video_ops = {
static const struct v4l2_subdev_pad_ops ov5675_pad_ops = {
.set_fmt = ov5675_set_format,
.get_fmt = ov5675_get_format,
+ .get_selection = ov5675_get_selection,
.enum_mbus_code = ov5675_enum_mbus_code,
.enum_frame_size = ov5675_enum_frame_size,
};
@@ -1106,32 +1215,60 @@ static const struct v4l2_subdev_internal_ops ov5675_internal_ops = {
.open = ov5675_open,
};
-static int ov5675_check_hwcfg(struct device *dev)
+static int ov5675_get_hwcfg(struct ov5675 *ov5675, struct device *dev)
{
struct fwnode_handle *ep;
struct fwnode_handle *fwnode = dev_fwnode(dev);
struct v4l2_fwnode_endpoint bus_cfg = {
.bus_type = V4L2_MBUS_CSI2_DPHY
};
- u32 mclk;
+ u32 xvclk_rate;
int ret;
unsigned int i, j;
if (!fwnode)
return -ENXIO;
- ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk);
+ ov5675->xvclk = devm_clk_get_optional(dev, NULL);
+ if (IS_ERR(ov5675->xvclk))
+ return dev_err_probe(dev, PTR_ERR(ov5675->xvclk),
+ "failed to get xvclk: %ld\n",
+ PTR_ERR(ov5675->xvclk));
- if (ret) {
- dev_err(dev, "can't get clock frequency");
- return ret;
+ if (ov5675->xvclk) {
+ xvclk_rate = clk_get_rate(ov5675->xvclk);
+ } else {
+ ret = fwnode_property_read_u32(fwnode, "clock-frequency",
+ &xvclk_rate);
+
+ if (ret) {
+ dev_err(dev, "can't get clock frequency");
+ return ret;
+ }
}
- if (mclk != OV5675_MCLK) {
- dev_err(dev, "external clock %d is not supported", mclk);
+ if (xvclk_rate != OV5675_XVCLK_19_2) {
+ dev_err(dev, "external clock rate %u is unsupported",
+ xvclk_rate);
return -EINVAL;
}
+ ov5675->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ov5675->reset_gpio)) {
+ ret = PTR_ERR(ov5675->reset_gpio);
+ dev_err(dev, "failed to get reset-gpios: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < OV5675_NUM_SUPPLIES; i++)
+ ov5675->supplies[i].supply = ov5675_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, OV5675_NUM_SUPPLIES,
+ ov5675->supplies);
+ if (ret)
+ return ret;
+
ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
if (!ep)
return -ENXIO;
@@ -1185,6 +1322,10 @@ static void ov5675_remove(struct i2c_client *client)
v4l2_ctrl_handler_free(sd->ctrl_handler);
pm_runtime_disable(&client->dev);
mutex_destroy(&ov5675->mutex);
+
+ if (!pm_runtime_status_suspended(&client->dev))
+ ov5675_power_off(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
}
static int ov5675_probe(struct i2c_client *client)
@@ -1193,25 +1334,31 @@ static int ov5675_probe(struct i2c_client *client)
bool full_power;
int ret;
- ret = ov5675_check_hwcfg(&client->dev);
+ ov5675 = devm_kzalloc(&client->dev, sizeof(*ov5675), GFP_KERNEL);
+ if (!ov5675)
+ return -ENOMEM;
+
+ ret = ov5675_get_hwcfg(ov5675, &client->dev);
if (ret) {
- dev_err(&client->dev, "failed to check HW configuration: %d",
+ dev_err(&client->dev, "failed to get HW configuration: %d",
ret);
return ret;
}
- ov5675 = devm_kzalloc(&client->dev, sizeof(*ov5675), GFP_KERNEL);
- if (!ov5675)
- return -ENOMEM;
-
v4l2_i2c_subdev_init(&ov5675->sd, client, &ov5675_subdev_ops);
+ ret = ov5675_power_on(&client->dev);
+ if (ret) {
+ dev_err(&client->dev, "failed to power on: %d\n", ret);
+ return ret;
+ }
+
full_power = acpi_dev_state_d0(&client->dev);
if (full_power) {
ret = ov5675_identify_module(ov5675);
if (ret) {
dev_err(&client->dev, "failed to find sensor: %d", ret);
- return ret;
+ goto probe_power_off;
}
}
@@ -1241,11 +1388,6 @@ static int ov5675_probe(struct i2c_client *client)
goto probe_error_media_entity_cleanup;
}
- /*
- * Device is already turned on by i2c-core with ACPI domain PM.
- * Enable runtime PM and turn off the device.
- */
-
/* Set the device's state to active if it's in D0 state. */
if (full_power)
pm_runtime_set_active(&client->dev);
@@ -1260,12 +1402,15 @@ probe_error_media_entity_cleanup:
probe_error_v4l2_ctrl_handler_free:
v4l2_ctrl_handler_free(ov5675->sd.ctrl_handler);
mutex_destroy(&ov5675->mutex);
+probe_power_off:
+ ov5675_power_off(&client->dev);
return ret;
}
static const struct dev_pm_ops ov5675_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(ov5675_suspend, ov5675_resume)
+ SET_RUNTIME_PM_OPS(ov5675_power_off, ov5675_power_on, NULL)
};
#ifdef CONFIG_ACPI
@@ -1277,11 +1422,18 @@ static const struct acpi_device_id ov5675_acpi_ids[] = {
MODULE_DEVICE_TABLE(acpi, ov5675_acpi_ids);
#endif
+static const struct of_device_id ov5675_of_match[] = {
+ { .compatible = "ovti,ov5675", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ov5675_of_match);
+
static struct i2c_driver ov5675_i2c_driver = {
.driver = {
.name = "ov5675",
.pm = &ov5675_pm_ops,
.acpi_match_table = ACPI_PTR(ov5675_acpi_ids),
+ .of_match_table = ov5675_of_match,
},
.probe_new = ov5675_probe,
.remove = ov5675_remove,
diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c
index 11d3bef65d43..b1bb0833571e 100644
--- a/drivers/media/i2c/ov7670.c
+++ b/drivers/media/i2c/ov7670.c
@@ -1840,16 +1840,16 @@ static int ov7670_parse_dt(struct device *dev,
if (bus_cfg.bus_type != V4L2_MBUS_PARALLEL) {
dev_err(dev, "Unsupported media bus type\n");
- return ret;
+ return -EINVAL;
}
info->mbus_config = bus_cfg.bus.parallel.flags;
return 0;
}
-static int ov7670_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int ov7670_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct v4l2_fract tpf;
struct v4l2_subdev *sd;
struct ov7670_info *info;
@@ -2038,7 +2038,7 @@ static struct i2c_driver ov7670_driver = {
.name = "ov7670",
.of_match_table = of_match_ptr(ov7670_of_match),
},
- .probe = ov7670_probe,
+ .probe_new = ov7670_probe,
.remove = ov7670_remove,
.id_table = ov7670_id,
};
diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index 4189e3fc3d53..a238e63425f8 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -1462,7 +1462,7 @@ static int ov772x_probe(struct i2c_client *client)
priv->subdev.ctrl_handler = &priv->hdl;
if (priv->hdl.error) {
ret = priv->hdl.error;
- goto error_mutex_destroy;
+ goto error_ctrl_free;
}
priv->clk = clk_get(&client->dev, NULL);
@@ -1515,7 +1515,6 @@ error_clk_put:
clk_put(priv->clk);
error_ctrl_free:
v4l2_ctrl_handler_free(&priv->hdl);
-error_mutex_destroy:
mutex_destroy(&priv->lock);
return ret;
diff --git a/drivers/media/i2c/ov8858.c b/drivers/media/i2c/ov8858.c
new file mode 100644
index 000000000000..9ca8a17bfbb9
--- /dev/null
+++ b/drivers/media/i2c/ov8858.c
@@ -0,0 +1,2008 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+ * Copyright (C) 2022 Nicholas Roth <nicholas@rothemail.net>
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
+ */
+
+#include <asm/unaligned.h>
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mediabus.h>
+#include <media/v4l2-subdev.h>
+
+#define OV8858_LINK_FREQ 360000000U
+#define OV8858_XVCLK_FREQ 24000000
+
+#define OV8858_REG_SIZE_SHIFT 16
+#define OV8858_REG_ADDR_MASK 0xffff
+#define OV8858_REG_8BIT(n) ((1U << OV8858_REG_SIZE_SHIFT) | (n))
+#define OV8858_REG_16BIT(n) ((2U << OV8858_REG_SIZE_SHIFT) | (n))
+#define OV8858_REG_24BIT(n) ((3U << OV8858_REG_SIZE_SHIFT) | (n))
+
+#define OV8858_REG_SC_CTRL0100 OV8858_REG_8BIT(0x0100)
+#define OV8858_MODE_SW_STANDBY 0x0
+#define OV8858_MODE_STREAMING 0x1
+
+#define OV8858_REG_CHIP_ID OV8858_REG_24BIT(0x300a)
+#define OV8858_CHIP_ID 0x008858
+
+#define OV8858_REG_SUB_ID OV8858_REG_8BIT(0x302a)
+#define OV8858_R1A 0xb0
+#define OV8858_R2A 0xb2
+
+#define OV8858_REG_LONG_EXPO OV8858_REG_24BIT(0x3500)
+#define OV8858_EXPOSURE_MIN 4
+#define OV8858_EXPOSURE_STEP 1
+#define OV8858_EXPOSURE_MARGIN 4
+
+#define OV8858_REG_LONG_GAIN OV8858_REG_16BIT(0x3508)
+#define OV8858_LONG_GAIN_MIN 0x0
+#define OV8858_LONG_GAIN_MAX 0x7ff
+#define OV8858_LONG_GAIN_STEP 1
+#define OV8858_LONG_GAIN_DEFAULT 0x80
+
+#define OV8858_REG_LONG_DIGIGAIN OV8858_REG_16BIT(0x350a)
+#define OV8858_LONG_DIGIGAIN_H_MASK 0x3fc0
+#define OV8858_LONG_DIGIGAIN_L_MASK 0x3f
+#define OV8858_LONG_DIGIGAIN_H_SHIFT 2
+#define OV8858_LONG_DIGIGAIN_MIN 0x0
+#define OV8858_LONG_DIGIGAIN_MAX 0x3fff
+#define OV8858_LONG_DIGIGAIN_STEP 1
+#define OV8858_LONG_DIGIGAIN_DEFAULT 0x200
+
+#define OV8858_REG_VTS OV8858_REG_16BIT(0x380e)
+#define OV8858_VTS_MAX 0x7fff
+
+#define OV8858_REG_TEST_PATTERN OV8858_REG_8BIT(0x5e00)
+#define OV8858_TEST_PATTERN_ENABLE 0x80
+#define OV8858_TEST_PATTERN_DISABLE 0x0
+
+#define REG_NULL 0xffff
+
+static const char * const ov8858_supply_names[] = {
+ "avdd", /* Analog power */
+ "dovdd", /* Digital I/O power */
+ "dvdd", /* Digital core power */
+};
+
+struct regval {
+ u16 addr;
+ u8 val;
+};
+
+struct regval_modes {
+ const struct regval *mode_2lanes;
+ const struct regval *mode_4lanes;
+};
+
+struct ov8858_mode {
+ u32 width;
+ u32 height;
+ u32 hts_def;
+ u32 vts_def;
+ u32 exp_def;
+ const struct regval_modes reg_modes;
+};
+
+struct ov8858 {
+ struct clk *xvclk;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *pwdn_gpio;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(ov8858_supply_names)];
+
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *vblank;
+
+ const struct regval *global_regs;
+
+ unsigned int num_lanes;
+};
+
+static inline struct ov8858 *sd_to_ov8858(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct ov8858, subdev);
+}
+
+static const struct regval ov8858_global_regs_r1a[] = {
+ {0x0100, 0x00},
+ {0x0100, 0x00},
+ {0x0100, 0x00},
+ {0x0100, 0x00},
+ {0x0302, 0x1e},
+ {0x0303, 0x00},
+ {0x0304, 0x03},
+ {0x030e, 0x00},
+ {0x030f, 0x09},
+ {0x0312, 0x01},
+ {0x031e, 0x0c},
+ {0x3600, 0x00},
+ {0x3601, 0x00},
+ {0x3602, 0x00},
+ {0x3603, 0x00},
+ {0x3604, 0x22},
+ {0x3605, 0x30},
+ {0x3606, 0x00},
+ {0x3607, 0x20},
+ {0x3608, 0x11},
+ {0x3609, 0x28},
+ {0x360a, 0x00},
+ {0x360b, 0x06},
+ {0x360c, 0xdc},
+ {0x360d, 0x40},
+ {0x360e, 0x0c},
+ {0x360f, 0x20},
+ {0x3610, 0x07},
+ {0x3611, 0x20},
+ {0x3612, 0x88},
+ {0x3613, 0x80},
+ {0x3614, 0x58},
+ {0x3615, 0x00},
+ {0x3616, 0x4a},
+ {0x3617, 0xb0},
+ {0x3618, 0x56},
+ {0x3619, 0x70},
+ {0x361a, 0x99},
+ {0x361b, 0x00},
+ {0x361c, 0x07},
+ {0x361d, 0x00},
+ {0x361e, 0x00},
+ {0x361f, 0x00},
+ {0x3638, 0xff},
+ {0x3633, 0x0c},
+ {0x3634, 0x0c},
+ {0x3635, 0x0c},
+ {0x3636, 0x0c},
+ {0x3645, 0x13},
+ {0x3646, 0x83},
+ {0x364a, 0x07},
+ {0x3015, 0x01},
+ {0x3018, 0x32},
+ {0x3020, 0x93},
+ {0x3022, 0x01},
+ {0x3031, 0x0a},
+ {0x3034, 0x00},
+ {0x3106, 0x01},
+ {0x3305, 0xf1},
+ {0x3308, 0x00},
+ {0x3309, 0x28},
+ {0x330a, 0x00},
+ {0x330b, 0x20},
+ {0x330c, 0x00},
+ {0x330d, 0x00},
+ {0x330e, 0x00},
+ {0x330f, 0x40},
+ {0x3307, 0x04},
+ {0x3500, 0x00},
+ {0x3501, 0x4d},
+ {0x3502, 0x40},
+ {0x3503, 0x00},
+ {0x3505, 0x80},
+ {0x3508, 0x04},
+ {0x3509, 0x00},
+ {0x350c, 0x00},
+ {0x350d, 0x80},
+ {0x3510, 0x00},
+ {0x3511, 0x02},
+ {0x3512, 0x00},
+ {0x3700, 0x18},
+ {0x3701, 0x0c},
+ {0x3702, 0x28},
+ {0x3703, 0x19},
+ {0x3704, 0x14},
+ {0x3705, 0x00},
+ {0x3706, 0x35},
+ {0x3707, 0x04},
+ {0x3708, 0x24},
+ {0x3709, 0x33},
+ {0x370a, 0x00},
+ {0x370b, 0xb5},
+ {0x370c, 0x04},
+ {0x3718, 0x12},
+ {0x3719, 0x31},
+ {0x3712, 0x42},
+ {0x3714, 0x24},
+ {0x371e, 0x19},
+ {0x371f, 0x40},
+ {0x3720, 0x05},
+ {0x3721, 0x05},
+ {0x3724, 0x06},
+ {0x3725, 0x01},
+ {0x3726, 0x06},
+ {0x3728, 0x05},
+ {0x3729, 0x02},
+ {0x372a, 0x03},
+ {0x372b, 0x53},
+ {0x372c, 0xa3},
+ {0x372d, 0x53},
+ {0x372e, 0x06},
+ {0x372f, 0x10},
+ {0x3730, 0x01},
+ {0x3731, 0x06},
+ {0x3732, 0x14},
+ {0x3733, 0x10},
+ {0x3734, 0x40},
+ {0x3736, 0x20},
+ {0x373a, 0x05},
+ {0x373b, 0x06},
+ {0x373c, 0x0a},
+ {0x373e, 0x03},
+ {0x3755, 0x10},
+ {0x3758, 0x00},
+ {0x3759, 0x4c},
+ {0x375a, 0x06},
+ {0x375b, 0x13},
+ {0x375c, 0x20},
+ {0x375d, 0x02},
+ {0x375e, 0x00},
+ {0x375f, 0x14},
+ {0x3768, 0x22},
+ {0x3769, 0x44},
+ {0x376a, 0x44},
+ {0x3761, 0x00},
+ {0x3762, 0x00},
+ {0x3763, 0x00},
+ {0x3766, 0xff},
+ {0x376b, 0x00},
+ {0x3772, 0x23},
+ {0x3773, 0x02},
+ {0x3774, 0x16},
+ {0x3775, 0x12},
+ {0x3776, 0x04},
+ {0x3777, 0x00},
+ {0x3778, 0x1b},
+ {0x37a0, 0x44},
+ {0x37a1, 0x3d},
+ {0x37a2, 0x3d},
+ {0x37a3, 0x00},
+ {0x37a4, 0x00},
+ {0x37a5, 0x00},
+ {0x37a6, 0x00},
+ {0x37a7, 0x44},
+ {0x37a8, 0x4c},
+ {0x37a9, 0x4c},
+ {0x3760, 0x00},
+ {0x376f, 0x01},
+ {0x37aa, 0x44},
+ {0x37ab, 0x2e},
+ {0x37ac, 0x2e},
+ {0x37ad, 0x33},
+ {0x37ae, 0x0d},
+ {0x37af, 0x0d},
+ {0x37b0, 0x00},
+ {0x37b1, 0x00},
+ {0x37b2, 0x00},
+ {0x37b3, 0x42},
+ {0x37b4, 0x42},
+ {0x37b5, 0x33},
+ {0x37b6, 0x00},
+ {0x37b7, 0x00},
+ {0x37b8, 0x00},
+ {0x37b9, 0xff},
+ {0x3800, 0x00},
+ {0x3801, 0x0c},
+ {0x3802, 0x00},
+ {0x3803, 0x0c},
+ {0x3804, 0x0c},
+ {0x3805, 0xd3},
+ {0x3806, 0x09},
+ {0x3807, 0xa3},
+ {0x3808, 0x06},
+ {0x3809, 0x60},
+ {0x380a, 0x04},
+ {0x380b, 0xc8},
+ {0x380c, 0x07},
+ {0x380d, 0x88},
+ {0x380e, 0x04},
+ {0x380f, 0xdc},
+ {0x3810, 0x00},
+ {0x3811, 0x04},
+ {0x3813, 0x02},
+ {0x3814, 0x03},
+ {0x3815, 0x01},
+ {0x3820, 0x00},
+ {0x3821, 0x67},
+ {0x382a, 0x03},
+ {0x382b, 0x01},
+ {0x3830, 0x08},
+ {0x3836, 0x02},
+ {0x3837, 0x18},
+ {0x3841, 0xff},
+ {0x3846, 0x48},
+ {0x3d85, 0x14},
+ {0x3f08, 0x08},
+ {0x3f0a, 0x80},
+ {0x4000, 0xf1},
+ {0x4001, 0x10},
+ {0x4005, 0x10},
+ {0x4002, 0x27},
+ {0x4009, 0x81},
+ {0x400b, 0x0c},
+ {0x401b, 0x00},
+ {0x401d, 0x00},
+ {0x4020, 0x00},
+ {0x4021, 0x04},
+ {0x4022, 0x04},
+ {0x4023, 0xb9},
+ {0x4024, 0x05},
+ {0x4025, 0x2a},
+ {0x4026, 0x05},
+ {0x4027, 0x2b},
+ {0x4028, 0x00},
+ {0x4029, 0x02},
+ {0x402a, 0x04},
+ {0x402b, 0x04},
+ {0x402c, 0x02},
+ {0x402d, 0x02},
+ {0x402e, 0x08},
+ {0x402f, 0x02},
+ {0x401f, 0x00},
+ {0x4034, 0x3f},
+ {0x403d, 0x04},
+ {0x4300, 0xff},
+ {0x4301, 0x00},
+ {0x4302, 0x0f},
+ {0x4316, 0x00},
+ {0x4500, 0x38},
+ {0x4503, 0x18},
+ {0x4600, 0x00},
+ {0x4601, 0xcb},
+ {0x481f, 0x32},
+ {0x4837, 0x16},
+ {0x4850, 0x10},
+ {0x4851, 0x32},
+ {0x4b00, 0x2a},
+ {0x4b0d, 0x00},
+ {0x4d00, 0x04},
+ {0x4d01, 0x18},
+ {0x4d02, 0xc3},
+ {0x4d03, 0xff},
+ {0x4d04, 0xff},
+ {0x4d05, 0xff},
+ {0x5000, 0x7e},
+ {0x5001, 0x01},
+ {0x5002, 0x08},
+ {0x5003, 0x20},
+ {0x5046, 0x12},
+ {0x5901, 0x00},
+ {0x5e00, 0x00},
+ {0x5e01, 0x41},
+ {0x382d, 0x7f},
+ {0x4825, 0x3a},
+ {0x4826, 0x40},
+ {0x4808, 0x25},
+ {REG_NULL, 0x00},
+};
+
+static const struct regval ov8858_global_regs_r2a_2lane[] = {
+ /*
+ * MIPI=720Mbps, SysClk=144Mhz,Dac Clock=360Mhz.
+ * v00_01_00 (05/29/2014) : initial setting
+ * AM19 : 3617 <- 0xC0
+ * AM20 : change FWC_6K_EN to be default 0x3618=0x5a
+ */
+ {0x0103, 0x01}, /* software reset */
+ {0x0100, 0x00}, /* software standby */
+ {0x0302, 0x1e}, /* pll1_multi */
+ {0x0303, 0x00}, /* pll1_divm */
+ {0x0304, 0x03}, /* pll1_div_mipi */
+ {0x030e, 0x02}, /* pll2_rdiv */
+ {0x030f, 0x04}, /* pll2_divsp */
+ {0x0312, 0x03}, /* pll2_pre_div0, pll2_r_divdac */
+ {0x031e, 0x0c}, /* pll1_no_lat */
+ {0x3600, 0x00},
+ {0x3601, 0x00},
+ {0x3602, 0x00},
+ {0x3603, 0x00},
+ {0x3604, 0x22},
+ {0x3605, 0x20},
+ {0x3606, 0x00},
+ {0x3607, 0x20},
+ {0x3608, 0x11},
+ {0x3609, 0x28},
+ {0x360a, 0x00},
+ {0x360b, 0x05},
+ {0x360c, 0xd4},
+ {0x360d, 0x40},
+ {0x360e, 0x0c},
+ {0x360f, 0x20},
+ {0x3610, 0x07},
+ {0x3611, 0x20},
+ {0x3612, 0x88},
+ {0x3613, 0x80},
+ {0x3614, 0x58},
+ {0x3615, 0x00},
+ {0x3616, 0x4a},
+ {0x3617, 0x90},
+ {0x3618, 0x5a},
+ {0x3619, 0x70},
+ {0x361a, 0x99},
+ {0x361b, 0x0a},
+ {0x361c, 0x07},
+ {0x361d, 0x00},
+ {0x361e, 0x00},
+ {0x361f, 0x00},
+ {0x3638, 0xff},
+ {0x3633, 0x0f},
+ {0x3634, 0x0f},
+ {0x3635, 0x0f},
+ {0x3636, 0x12},
+ {0x3645, 0x13},
+ {0x3646, 0x83},
+ {0x364a, 0x07},
+ {0x3015, 0x00},
+ {0x3018, 0x32}, /* MIPI 2 lane */
+ {0x3020, 0x93}, /* Clock switch output normal, pclk_div =/1 */
+ {0x3022, 0x01}, /* pd_mipi enable when rst_sync */
+ {0x3031, 0x0a}, /* MIPI 10-bit mode */
+ {0x3034, 0x00},
+ {0x3106, 0x01}, /* sclk_div, sclk_pre_div */
+ {0x3305, 0xf1},
+ {0x3308, 0x00},
+ {0x3309, 0x28},
+ {0x330a, 0x00},
+ {0x330b, 0x20},
+ {0x330c, 0x00},
+ {0x330d, 0x00},
+ {0x330e, 0x00},
+ {0x330f, 0x40},
+ {0x3307, 0x04},
+ {0x3500, 0x00}, /* exposure H */
+ {0x3501, 0x4d}, /* exposure M */
+ {0x3502, 0x40}, /* exposure L */
+ {0x3503, 0x80}, /* gain delay ?, exposure delay 1 frame, real gain */
+ {0x3505, 0x80}, /* gain option */
+ {0x3508, 0x02}, /* gain H */
+ {0x3509, 0x00}, /* gain L */
+ {0x350c, 0x00}, /* short gain H */
+ {0x350d, 0x80}, /* short gain L */
+ {0x3510, 0x00}, /* short exposure H */
+ {0x3511, 0x02}, /* short exposure M */
+ {0x3512, 0x00}, /* short exposure L */
+ {0x3700, 0x18},
+ {0x3701, 0x0c},
+ {0x3702, 0x28},
+ {0x3703, 0x19},
+ {0x3704, 0x14},
+ {0x3705, 0x00},
+ {0x3706, 0x82},
+ {0x3707, 0x04},
+ {0x3708, 0x24},
+ {0x3709, 0x33},
+ {0x370a, 0x01},
+ {0x370b, 0x82},
+ {0x370c, 0x04},
+ {0x3718, 0x12},
+ {0x3719, 0x31},
+ {0x3712, 0x42},
+ {0x3714, 0x24},
+ {0x371e, 0x19},
+ {0x371f, 0x40},
+ {0x3720, 0x05},
+ {0x3721, 0x05},
+ {0x3724, 0x06},
+ {0x3725, 0x01},
+ {0x3726, 0x06},
+ {0x3728, 0x05},
+ {0x3729, 0x02},
+ {0x372a, 0x03},
+ {0x372b, 0x53},
+ {0x372c, 0xa3},
+ {0x372d, 0x53},
+ {0x372e, 0x06},
+ {0x372f, 0x10},
+ {0x3730, 0x01},
+ {0x3731, 0x06},
+ {0x3732, 0x14},
+ {0x3733, 0x10},
+ {0x3734, 0x40},
+ {0x3736, 0x20},
+ {0x373a, 0x05},
+ {0x373b, 0x06},
+ {0x373c, 0x0a},
+ {0x373e, 0x03},
+ {0x3750, 0x0a},
+ {0x3751, 0x0e},
+ {0x3755, 0x10},
+ {0x3758, 0x00},
+ {0x3759, 0x4c},
+ {0x375a, 0x06},
+ {0x375b, 0x13},
+ {0x375c, 0x20},
+ {0x375d, 0x02},
+ {0x375e, 0x00},
+ {0x375f, 0x14},
+ {0x3768, 0x22},
+ {0x3769, 0x44},
+ {0x376a, 0x44},
+ {0x3761, 0x00},
+ {0x3762, 0x00},
+ {0x3763, 0x00},
+ {0x3766, 0xff},
+ {0x376b, 0x00},
+ {0x3772, 0x23},
+ {0x3773, 0x02},
+ {0x3774, 0x16},
+ {0x3775, 0x12},
+ {0x3776, 0x04},
+ {0x3777, 0x00},
+ {0x3778, 0x17},
+ {0x37a0, 0x44},
+ {0x37a1, 0x3d},
+ {0x37a2, 0x3d},
+ {0x37a3, 0x00},
+ {0x37a4, 0x00},
+ {0x37a5, 0x00},
+ {0x37a6, 0x00},
+ {0x37a7, 0x44},
+ {0x37a8, 0x4c},
+ {0x37a9, 0x4c},
+ {0x3760, 0x00},
+ {0x376f, 0x01},
+ {0x37aa, 0x44},
+ {0x37ab, 0x2e},
+ {0x37ac, 0x2e},
+ {0x37ad, 0x33},
+ {0x37ae, 0x0d},
+ {0x37af, 0x0d},
+ {0x37b0, 0x00},
+ {0x37b1, 0x00},
+ {0x37b2, 0x00},
+ {0x37b3, 0x42},
+ {0x37b4, 0x42},
+ {0x37b5, 0x31},
+ {0x37b6, 0x00},
+ {0x37b7, 0x00},
+ {0x37b8, 0x00},
+ {0x37b9, 0xff},
+ {0x3800, 0x00}, /* x start H */
+ {0x3801, 0x0c}, /* x start L */
+ {0x3802, 0x00}, /* y start H */
+ {0x3803, 0x0c}, /* y start L */
+ {0x3804, 0x0c}, /* x end H */
+ {0x3805, 0xd3}, /* x end L */
+ {0x3806, 0x09}, /* y end H */
+ {0x3807, 0xa3}, /* y end L */
+ {0x3808, 0x06}, /* x output size H */
+ {0x3809, 0x60}, /* x output size L */
+ {0x380a, 0x04}, /* y output size H */
+ {0x380b, 0xc8}, /* y output size L */
+ {0x380c, 0x07}, /* HTS H */
+ {0x380d, 0x88}, /* HTS L */
+ {0x380e, 0x04}, /* VTS H */
+ {0x380f, 0xdc}, /* VTS L */
+ {0x3810, 0x00}, /* ISP x win H */
+ {0x3811, 0x04}, /* ISP x win L */
+ {0x3813, 0x02}, /* ISP y win L */
+ {0x3814, 0x03}, /* x odd inc */
+ {0x3815, 0x01}, /* x even inc */
+ {0x3820, 0x00}, /* vflip off */
+ {0x3821, 0x67}, /* mirror on, bin on */
+ {0x382a, 0x03}, /* y odd inc */
+ {0x382b, 0x01}, /* y even inc */
+ {0x3830, 0x08},
+ {0x3836, 0x02},
+ {0x3837, 0x18},
+ {0x3841, 0xff}, /* window auto size enable */
+ {0x3846, 0x48},
+ {0x3d85, 0x16}, /* OTP power up load data enable with BIST */
+ {0x3d8c, 0x73}, /* OTP setting start High */
+ {0x3d8d, 0xde}, /* OTP setting start Low */
+ {0x3f08, 0x08},
+ {0x3f0a, 0x00},
+ {0x4000, 0xf1}, /* out_range_trig, format_chg_trig */
+ {0x4001, 0x10}, /* total 128 black column */
+ {0x4005, 0x10}, /* BLC target L */
+ {0x4002, 0x27}, /* value used to limit BLC offset */
+ {0x4009, 0x81}, /* final BLC offset limitation enable */
+ {0x400b, 0x0c}, /* DCBLC on, DCBLC manual mode on */
+ {0x401b, 0x00}, /* zero line R coefficient */
+ {0x401d, 0x00}, /* zoro line T coefficient */
+ {0x4020, 0x00}, /* Anchor left start H */
+ {0x4021, 0x04}, /* Anchor left start L */
+ {0x4022, 0x06}, /* Anchor left end H */
+ {0x4023, 0x00}, /* Anchor left end L */
+ {0x4024, 0x0f}, /* Anchor right start H */
+ {0x4025, 0x2a}, /* Anchor right start L */
+ {0x4026, 0x0f}, /* Anchor right end H */
+ {0x4027, 0x2b}, /* Anchor right end L */
+ {0x4028, 0x00}, /* top zero line start */
+ {0x4029, 0x02}, /* top zero line number */
+ {0x402a, 0x04}, /* top black line start */
+ {0x402b, 0x04}, /* top black line number */
+ {0x402c, 0x00}, /* bottom zero line start */
+ {0x402d, 0x02}, /* bottom zoro line number */
+ {0x402e, 0x04}, /* bottom black line start */
+ {0x402f, 0x04}, /* bottom black line number */
+ {0x401f, 0x00}, /* interpolation x/y disable, Anchor one disable */
+ {0x4034, 0x3f},
+ {0x403d, 0x04}, /* md_precision_en */
+ {0x4300, 0xff}, /* clip max H */
+ {0x4301, 0x00}, /* clip min H */
+ {0x4302, 0x0f}, /* clip min L, clip max L */
+ {0x4316, 0x00},
+ {0x4500, 0x58},
+ {0x4503, 0x18},
+ {0x4600, 0x00},
+ {0x4601, 0xcb},
+ {0x481f, 0x32}, /* clk prepare min */
+ {0x4837, 0x16}, /* global timing */
+ {0x4850, 0x10}, /* lane 1 = 1, lane 0 = 0 */
+ {0x4851, 0x32}, /* lane 3 = 3, lane 2 = 2 */
+ {0x4b00, 0x2a},
+ {0x4b0d, 0x00},
+ {0x4d00, 0x04}, /* temperature sensor */
+ {0x4d01, 0x18},
+ {0x4d02, 0xc3},
+ {0x4d03, 0xff},
+ {0x4d04, 0xff},
+ {0x4d05, 0xff}, /* temperature sensor */
+ {0x5000, 0xfe}, /* lenc on, slave/master AWB gain/statistics enable */
+ {0x5001, 0x01}, /* BLC on */
+ {0x5002, 0x08}, /* H scale off, WBMATCH off, OTP_DPC */
+ {0x5003, 0x20}, /* DPC_DBC buffer control enable, WB */
+ {0x501e, 0x93}, /* enable digital gain */
+ {0x5046, 0x12},
+ {0x5780, 0x3e}, /* DPC */
+ {0x5781, 0x0f},
+ {0x5782, 0x44},
+ {0x5783, 0x02},
+ {0x5784, 0x01},
+ {0x5785, 0x00},
+ {0x5786, 0x00},
+ {0x5787, 0x04},
+ {0x5788, 0x02},
+ {0x5789, 0x0f},
+ {0x578a, 0xfd},
+ {0x578b, 0xf5},
+ {0x578c, 0xf5},
+ {0x578d, 0x03},
+ {0x578e, 0x08},
+ {0x578f, 0x0c},
+ {0x5790, 0x08},
+ {0x5791, 0x04},
+ {0x5792, 0x00},
+ {0x5793, 0x52},
+ {0x5794, 0xa3}, /* DPC */
+ {0x5871, 0x0d}, /* Lenc */
+ {0x5870, 0x18},
+ {0x586e, 0x10},
+ {0x586f, 0x08},
+ {0x58f7, 0x01},
+ {0x58f8, 0x3d}, /* Lenc */
+ {0x5901, 0x00}, /* H skip off, V skip off */
+ {0x5b00, 0x02}, /* OTP DPC start address */
+ {0x5b01, 0x10}, /* OTP DPC start address */
+ {0x5b02, 0x03}, /* OTP DPC end address */
+ {0x5b03, 0xcf}, /* OTP DPC end address */
+ {0x5b05, 0x6c}, /* recover method = 2b11, */
+ {0x5e00, 0x00}, /* use 0x3ff to test pattern off */
+ {0x5e01, 0x41}, /* window cut enable */
+ {0x382d, 0x7f},
+ {0x4825, 0x3a}, /* lpx_p_min */
+ {0x4826, 0x40}, /* hs_prepare_min */
+ {0x4808, 0x25}, /* wake up delay in 1/1024 s */
+ {0x3763, 0x18},
+ {0x3768, 0xcc},
+ {0x470b, 0x28},
+ {0x4202, 0x00},
+ {0x400d, 0x10}, /* BLC offset trigger L */
+ {0x4040, 0x04}, /* BLC gain th2 */
+ {0x403e, 0x04}, /* BLC gain th1 */
+ {0x4041, 0xc6}, /* BLC */
+ {0x3007, 0x80},
+ {0x400a, 0x01},
+ {REG_NULL, 0x00},
+};
+
+/*
+ * Xclk 24Mhz
+ * max_framerate 30fps
+ * mipi_datarate per lane 720Mbps
+ */
+static const struct regval ov8858_1632x1224_regs_2lane[] = {
+ /*
+ * MIPI=720Mbps, SysClk=144Mhz,Dac Clock=360Mhz.
+ * v00_01_00 (05/29/2014) : initial setting
+ * AM19 : 3617 <- 0xC0
+ * AM20 : change FWC_6K_EN to be default 0x3618=0x5a
+ */
+ {0x0100, 0x00},
+ {0x3501, 0x4d}, /* exposure M */
+ {0x3502, 0x40}, /* exposure L */
+ {0x3778, 0x17},
+ {0x3808, 0x06}, /* x output size H */
+ {0x3809, 0x60}, /* x output size L */
+ {0x380a, 0x04}, /* y output size H */
+ {0x380b, 0xc8}, /* y output size L */
+ {0x380c, 0x07}, /* HTS H */
+ {0x380d, 0x88}, /* HTS L */
+ {0x380e, 0x04}, /* VTS H */
+ {0x380f, 0xdc}, /* VTS L */
+ {0x3814, 0x03}, /* x odd inc */
+ {0x3821, 0x67}, /* mirror on, bin on */
+ {0x382a, 0x03}, /* y odd inc */
+ {0x3830, 0x08},
+ {0x3836, 0x02},
+ {0x3f0a, 0x00},
+ {0x4001, 0x10}, /* total 128 black column */
+ {0x4022, 0x06}, /* Anchor left end H */
+ {0x4023, 0x00}, /* Anchor left end L */
+ {0x4025, 0x2a}, /* Anchor right start L */
+ {0x4027, 0x2b}, /* Anchor right end L */
+ {0x402b, 0x04}, /* top black line number */
+ {0x402f, 0x04}, /* bottom black line number */
+ {0x4500, 0x58},
+ {0x4600, 0x00},
+ {0x4601, 0xcb},
+ {0x382d, 0x7f},
+ {0x0100, 0x01},
+ {REG_NULL, 0x00},
+};
+
+/*
+ * Xclk 24Mhz
+ * max_framerate 15fps
+ * mipi_datarate per lane 720Mbps
+ */
+static const struct regval ov8858_3264x2448_regs_2lane[] = {
+ {0x0100, 0x00},
+ {0x3501, 0x9a}, /* exposure M */
+ {0x3502, 0x20}, /* exposure L */
+ {0x3778, 0x1a},
+ {0x3808, 0x0c}, /* x output size H */
+ {0x3809, 0xc0}, /* x output size L */
+ {0x380a, 0x09}, /* y output size H */
+ {0x380b, 0x90}, /* y output size L */
+ {0x380c, 0x07}, /* HTS H */
+ {0x380d, 0x94}, /* HTS L */
+ {0x380e, 0x09}, /* VTS H */
+ {0x380f, 0xaa}, /* VTS L */
+ {0x3814, 0x01}, /* x odd inc */
+ {0x3821, 0x46}, /* mirror on, bin off */
+ {0x382a, 0x01}, /* y odd inc */
+ {0x3830, 0x06},
+ {0x3836, 0x01},
+ {0x3f0a, 0x00},
+ {0x4001, 0x00}, /* total 256 black column */
+ {0x4022, 0x0c}, /* Anchor left end H */
+ {0x4023, 0x60}, /* Anchor left end L */
+ {0x4025, 0x36}, /* Anchor right start L */
+ {0x4027, 0x37}, /* Anchor right end L */
+ {0x402b, 0x08}, /* top black line number */
+ {0x402f, 0x08}, /* bottom black line number */
+ {0x4500, 0x58},
+ {0x4600, 0x01},
+ {0x4601, 0x97},
+ {0x382d, 0xff},
+ {REG_NULL, 0x00},
+};
+
+static const struct regval ov8858_global_regs_r2a_4lane[] = {
+ /*
+ * MIPI=720Mbps, SysClk=144Mhz,Dac Clock=360Mhz.
+ * v00_01_00 (05/29/2014) : initial setting
+ * AM19 : 3617 <- 0xC0
+ * AM20 : change FWC_6K_EN to be default 0x3618=0x5a
+ */
+ {0x0103, 0x01}, /* software reset for OVTATool only */
+ {0x0103, 0x01}, /* software reset */
+ {0x0100, 0x00}, /* software standby */
+ {0x0302, 0x1e}, /* pll1_multi */
+ {0x0303, 0x00}, /* pll1_divm */
+ {0x0304, 0x03}, /* pll1_div_mipi */
+ {0x030e, 0x00}, /* pll2_rdiv */
+ {0x030f, 0x04}, /* pll2_divsp */
+ {0x0312, 0x01}, /* pll2_pre_div0, pll2_r_divdac */
+ {0x031e, 0x0c}, /* pll1_no_lat */
+ {0x3600, 0x00},
+ {0x3601, 0x00},
+ {0x3602, 0x00},
+ {0x3603, 0x00},
+ {0x3604, 0x22},
+ {0x3605, 0x20},
+ {0x3606, 0x00},
+ {0x3607, 0x20},
+ {0x3608, 0x11},
+ {0x3609, 0x28},
+ {0x360a, 0x00},
+ {0x360b, 0x05},
+ {0x360c, 0xd4},
+ {0x360d, 0x40},
+ {0x360e, 0x0c},
+ {0x360f, 0x20},
+ {0x3610, 0x07},
+ {0x3611, 0x20},
+ {0x3612, 0x88},
+ {0x3613, 0x80},
+ {0x3614, 0x58},
+ {0x3615, 0x00},
+ {0x3616, 0x4a},
+ {0x3617, 0x90},
+ {0x3618, 0x5a},
+ {0x3619, 0x70},
+ {0x361a, 0x99},
+ {0x361b, 0x0a},
+ {0x361c, 0x07},
+ {0x361d, 0x00},
+ {0x361e, 0x00},
+ {0x361f, 0x00},
+ {0x3638, 0xff},
+ {0x3633, 0x0f},
+ {0x3634, 0x0f},
+ {0x3635, 0x0f},
+ {0x3636, 0x12},
+ {0x3645, 0x13},
+ {0x3646, 0x83},
+ {0x364a, 0x07},
+ {0x3015, 0x01},
+ {0x3018, 0x72}, /* MIPI 4 lane */
+ {0x3020, 0x93}, /* Clock switch output normal, pclk_div =/1 */
+ {0x3022, 0x01}, /* pd_mipi enable when rst_sync */
+ {0x3031, 0x0a}, /* MIPI 10-bit mode */
+ {0x3034, 0x00},
+ {0x3106, 0x01}, /* sclk_div, sclk_pre_div */
+ {0x3305, 0xf1},
+ {0x3308, 0x00},
+ {0x3309, 0x28},
+ {0x330a, 0x00},
+ {0x330b, 0x20},
+ {0x330c, 0x00},
+ {0x330d, 0x00},
+ {0x330e, 0x00},
+ {0x330f, 0x40},
+ {0x3307, 0x04},
+ {0x3500, 0x00}, /* exposure H */
+ {0x3501, 0x4d}, /* exposure M */
+ {0x3502, 0x40}, /* exposure L */
+ {0x3503, 0x80}, /* gain delay ?, exposure delay 1 frame, real gain */
+ {0x3505, 0x80}, /* gain option */
+ {0x3508, 0x02}, /* gain H */
+ {0x3509, 0x00}, /* gain L */
+ {0x350c, 0x00}, /* short gain H */
+ {0x350d, 0x80}, /* short gain L */
+ {0x3510, 0x00}, /* short exposure H */
+ {0x3511, 0x02}, /* short exposure M */
+ {0x3512, 0x00}, /* short exposure L */
+ {0x3700, 0x30},
+ {0x3701, 0x18},
+ {0x3702, 0x50},
+ {0x3703, 0x32},
+ {0x3704, 0x28},
+ {0x3705, 0x00},
+ {0x3706, 0x82},
+ {0x3707, 0x08},
+ {0x3708, 0x48},
+ {0x3709, 0x66},
+ {0x370a, 0x01},
+ {0x370b, 0x82},
+ {0x370c, 0x07},
+ {0x3718, 0x14},
+ {0x3719, 0x31},
+ {0x3712, 0x44},
+ {0x3714, 0x24},
+ {0x371e, 0x31},
+ {0x371f, 0x7f},
+ {0x3720, 0x0a},
+ {0x3721, 0x0a},
+ {0x3724, 0x0c},
+ {0x3725, 0x02},
+ {0x3726, 0x0c},
+ {0x3728, 0x0a},
+ {0x3729, 0x03},
+ {0x372a, 0x06},
+ {0x372b, 0xa6},
+ {0x372c, 0xa6},
+ {0x372d, 0xa6},
+ {0x372e, 0x0c},
+ {0x372f, 0x20},
+ {0x3730, 0x02},
+ {0x3731, 0x0c},
+ {0x3732, 0x28},
+ {0x3733, 0x10},
+ {0x3734, 0x40},
+ {0x3736, 0x30},
+ {0x373a, 0x0a},
+ {0x373b, 0x0b},
+ {0x373c, 0x14},
+ {0x373e, 0x06},
+ {0x3750, 0x0a},
+ {0x3751, 0x0e},
+ {0x3755, 0x10},
+ {0x3758, 0x00},
+ {0x3759, 0x4c},
+ {0x375a, 0x0c},
+ {0x375b, 0x26},
+ {0x375c, 0x20},
+ {0x375d, 0x04},
+ {0x375e, 0x00},
+ {0x375f, 0x28},
+ {0x3768, 0x22},
+ {0x3769, 0x44},
+ {0x376a, 0x44},
+ {0x3761, 0x00},
+ {0x3762, 0x00},
+ {0x3763, 0x00},
+ {0x3766, 0xff},
+ {0x376b, 0x00},
+ {0x3772, 0x46},
+ {0x3773, 0x04},
+ {0x3774, 0x2c},
+ {0x3775, 0x13},
+ {0x3776, 0x08},
+ {0x3777, 0x00},
+ {0x3778, 0x17},
+ {0x37a0, 0x88},
+ {0x37a1, 0x7a},
+ {0x37a2, 0x7a},
+ {0x37a3, 0x00},
+ {0x37a4, 0x00},
+ {0x37a5, 0x00},
+ {0x37a6, 0x00},
+ {0x37a7, 0x88},
+ {0x37a8, 0x98},
+ {0x37a9, 0x98},
+ {0x3760, 0x00},
+ {0x376f, 0x01},
+ {0x37aa, 0x88},
+ {0x37ab, 0x5c},
+ {0x37ac, 0x5c},
+ {0x37ad, 0x55},
+ {0x37ae, 0x19},
+ {0x37af, 0x19},
+ {0x37b0, 0x00},
+ {0x37b1, 0x00},
+ {0x37b2, 0x00},
+ {0x37b3, 0x84},
+ {0x37b4, 0x84},
+ {0x37b5, 0x60},
+ {0x37b6, 0x00},
+ {0x37b7, 0x00},
+ {0x37b8, 0x00},
+ {0x37b9, 0xff},
+ {0x3800, 0x00}, /* x start H */
+ {0x3801, 0x0c}, /* x start L */
+ {0x3802, 0x00}, /* y start H */
+ {0x3803, 0x0c}, /* y start L */
+ {0x3804, 0x0c}, /* x end H */
+ {0x3805, 0xd3}, /* x end L */
+ {0x3806, 0x09}, /* y end H */
+ {0x3807, 0xa3}, /* y end L */
+ {0x3808, 0x06}, /* x output size H */
+ {0x3809, 0x60}, /* x output size L */
+ {0x380a, 0x04}, /* y output size H */
+ {0x380b, 0xc8}, /* y output size L */
+ {0x380c, 0x07}, /* HTS H */
+ {0x380d, 0x88}, /* HTS L */
+ {0x380e, 0x04}, /* VTS H */
+ {0x380f, 0xdc}, /* VTS L */
+ {0x3810, 0x00}, /* ISP x win H */
+ {0x3811, 0x04}, /* ISP x win L */
+ {0x3813, 0x02}, /* ISP y win L */
+ {0x3814, 0x03}, /* x odd inc */
+ {0x3815, 0x01}, /* x even inc */
+ {0x3820, 0x00}, /* vflip off */
+ {0x3821, 0x67}, /* mirror on, bin o */
+ {0x382a, 0x03}, /* y odd inc */
+ {0x382b, 0x01}, /* y even inc */
+ {0x3830, 0x08},
+ {0x3836, 0x02},
+ {0x3837, 0x18},
+ {0x3841, 0xff}, /* window auto size enable */
+ {0x3846, 0x48},
+ {0x3d85, 0x16}, /* OTP power up load data/setting enable */
+ {0x3d8c, 0x73}, /* OTP setting start High */
+ {0x3d8d, 0xde}, /* OTP setting start Low */
+ {0x3f08, 0x10},
+ {0x3f0a, 0x00},
+ {0x4000, 0xf1}, /* out_range/format_chg/gain/exp_chg trig enable */
+ {0x4001, 0x10}, /* total 128 black column */
+ {0x4005, 0x10}, /* BLC target L */
+ {0x4002, 0x27}, /* value used to limit BLC offset */
+ {0x4009, 0x81}, /* final BLC offset limitation enable */
+ {0x400b, 0x0c}, /* DCBLC on, DCBLC manual mode on */
+ {0x401b, 0x00}, /* zero line R coefficient */
+ {0x401d, 0x00}, /* zoro line T coefficient */
+ {0x4020, 0x00}, /* Anchor left start H */
+ {0x4021, 0x04}, /* Anchor left start L */
+ {0x4022, 0x06}, /* Anchor left end H */
+ {0x4023, 0x00}, /* Anchor left end L */
+ {0x4024, 0x0f}, /* Anchor right start H */
+ {0x4025, 0x2a}, /* Anchor right start L */
+ {0x4026, 0x0f}, /* Anchor right end H */
+ {0x4027, 0x2b}, /* Anchor right end L */
+ {0x4028, 0x00}, /* top zero line start */
+ {0x4029, 0x02}, /* top zero line number */
+ {0x402a, 0x04}, /* top black line start */
+ {0x402b, 0x04}, /* top black line number */
+ {0x402c, 0x00}, /* bottom zero line start */
+ {0x402d, 0x02}, /* bottom zoro line number */
+ {0x402e, 0x04}, /* bottom black line start */
+ {0x402f, 0x04}, /* bottom black line number */
+ {0x401f, 0x00}, /* interpolation x/y disable, Anchor one disable */
+ {0x4034, 0x3f},
+ {0x403d, 0x04}, /* md_precision_en */
+ {0x4300, 0xff}, /* clip max H */
+ {0x4301, 0x00}, /* clip min H */
+ {0x4302, 0x0f}, /* clip min L, clip max L */
+ {0x4316, 0x00},
+ {0x4500, 0x58},
+ {0x4503, 0x18},
+ {0x4600, 0x00},
+ {0x4601, 0xcb},
+ {0x481f, 0x32}, /* clk prepare min */
+ {0x4837, 0x16}, /* global timing */
+ {0x4850, 0x10}, /* lane 1 = 1, lane 0 = 0 */
+ {0x4851, 0x32}, /* lane 3 = 3, lane 2 = 2 */
+ {0x4b00, 0x2a},
+ {0x4b0d, 0x00},
+ {0x4d00, 0x04}, /* temperature sensor */
+ {0x4d01, 0x18},
+ {0x4d02, 0xc3},
+ {0x4d03, 0xff},
+ {0x4d04, 0xff},
+ {0x4d05, 0xff}, /* temperature sensor */
+ {0x5000, 0xfe}, /* lenc on, slave/master AWB gain/statistics enable */
+ {0x5001, 0x01}, /* BLC on */
+ {0x5002, 0x08}, /* WBMATCH sensor's gain, H scale/WBMATCH/OTP_DPC off */
+ {0x5003, 0x20}, /* DPC_DBC buffer control enable, WB */
+ {0x501e, 0x93}, /* enable digital gain */
+ {0x5046, 0x12},
+ {0x5780, 0x3e}, /* DPC */
+ {0x5781, 0x0f},
+ {0x5782, 0x44},
+ {0x5783, 0x02},
+ {0x5784, 0x01},
+ {0x5785, 0x00},
+ {0x5786, 0x00},
+ {0x5787, 0x04},
+ {0x5788, 0x02},
+ {0x5789, 0x0f},
+ {0x578a, 0xfd},
+ {0x578b, 0xf5},
+ {0x578c, 0xf5},
+ {0x578d, 0x03},
+ {0x578e, 0x08},
+ {0x578f, 0x0c},
+ {0x5790, 0x08},
+ {0x5791, 0x04},
+ {0x5792, 0x00},
+ {0x5793, 0x52},
+ {0x5794, 0xa3}, /* DPC */
+ {0x5871, 0x0d}, /* Lenc */
+ {0x5870, 0x18},
+ {0x586e, 0x10},
+ {0x586f, 0x08},
+ {0x58f7, 0x01},
+ {0x58f8, 0x3d}, /* Lenc */
+ {0x5901, 0x00}, /* H skip off, V skip off */
+ {0x5b00, 0x02}, /* OTP DPC start address */
+ {0x5b01, 0x10}, /* OTP DPC start address */
+ {0x5b02, 0x03}, /* OTP DPC end address */
+ {0x5b03, 0xcf}, /* OTP DPC end address */
+ {0x5b05, 0x6c}, /* recover method = 2b11 */
+ {0x5e00, 0x00}, /* use 0x3ff to test pattern off */
+ {0x5e01, 0x41}, /* window cut enable */
+ {0x382d, 0x7f},
+ {0x4825, 0x3a}, /* lpx_p_min */
+ {0x4826, 0x40}, /* hs_prepare_min */
+ {0x4808, 0x25}, /* wake up delay in 1/1024 s */
+ {0x3763, 0x18},
+ {0x3768, 0xcc},
+ {0x470b, 0x28},
+ {0x4202, 0x00},
+ {0x400d, 0x10}, /* BLC offset trigger L */
+ {0x4040, 0x04}, /* BLC gain th2 */
+ {0x403e, 0x04}, /* BLC gain th1 */
+ {0x4041, 0xc6}, /* BLC */
+ {0x3007, 0x80},
+ {0x400a, 0x01},
+ {REG_NULL, 0x00},
+};
+
+/*
+ * Xclk 24Mhz
+ * max_framerate 60fps
+ * mipi_datarate per lane 720Mbps
+ */
+static const struct regval ov8858_1632x1224_regs_4lane[] = {
+ {0x0100, 0x00},
+ {0x3501, 0x4d}, /* exposure M */
+ {0x3502, 0x40}, /* exposure L */
+ {0x3808, 0x06}, /* x output size H */
+ {0x3809, 0x60}, /* x output size L */
+ {0x380a, 0x04}, /* y output size H */
+ {0x380b, 0xc8}, /* y output size L */
+ {0x380c, 0x07}, /* HTS H */
+ {0x380d, 0x88}, /* HTS L */
+ {0x380e, 0x04}, /* VTS H */
+ {0x380f, 0xdc}, /* VTS L */
+ {0x3814, 0x03}, /* x odd inc */
+ {0x3821, 0x67}, /* mirror on, bin on */
+ {0x382a, 0x03}, /* y odd inc */
+ {0x3830, 0x08},
+ {0x3836, 0x02},
+ {0x3f0a, 0x00},
+ {0x4001, 0x10}, /* total 128 black column */
+ {0x4022, 0x06}, /* Anchor left end H */
+ {0x4023, 0x00}, /* Anchor left end L */
+ {0x4025, 0x2a}, /* Anchor right start L */
+ {0x4027, 0x2b}, /* Anchor right end L */
+ {0x402b, 0x04}, /* top black line number */
+ {0x402f, 0x04}, /* bottom black line number */
+ {0x4500, 0x58},
+ {0x4600, 0x00},
+ {0x4601, 0xcb},
+ {0x382d, 0x7f},
+ {0x0100, 0x01},
+ {REG_NULL, 0x00},
+};
+
+/*
+ * Xclk 24Mhz
+ * max_framerate 30fps
+ * mipi_datarate per lane 720Mbps
+ */
+static const struct regval ov8858_3264x2448_regs_4lane[] = {
+ {0x0100, 0x00},
+ {0x3501, 0x9a}, /* exposure M */
+ {0x3502, 0x20}, /* exposure L */
+ {0x3808, 0x0c}, /* x output size H */
+ {0x3809, 0xc0}, /* x output size L */
+ {0x380a, 0x09}, /* y output size H */
+ {0x380b, 0x90}, /* y output size L */
+ {0x380c, 0x07}, /* HTS H */
+ {0x380d, 0x94}, /* HTS L */
+ {0x380e, 0x09}, /* VTS H */
+ {0x380f, 0xaa}, /* VTS L */
+ {0x3814, 0x01}, /* x odd inc */
+ {0x3821, 0x46}, /* mirror on, bin off */
+ {0x382a, 0x01}, /* y odd inc */
+ {0x3830, 0x06},
+ {0x3836, 0x01},
+ {0x3f0a, 0x00},
+ {0x4001, 0x00}, /* total 256 black column */
+ {0x4022, 0x0c}, /* Anchor left end H */
+ {0x4023, 0x60}, /* Anchor left end L */
+ {0x4025, 0x36}, /* Anchor right start L */
+ {0x4027, 0x37}, /* Anchor right end L */
+ {0x402b, 0x08}, /* top black line number */
+ {0x402f, 0x08}, /* interpolation x/y disable, Anchor one disable */
+ {0x4500, 0x58},
+ {0x4600, 0x01},
+ {0x4601, 0x97},
+ {0x382d, 0xff},
+ {REG_NULL, 0x00},
+};
+
+static const struct ov8858_mode ov8858_modes[] = {
+ {
+ .width = 3264,
+ .height = 2448,
+ .exp_def = 2464,
+ .hts_def = 1940 * 2,
+ .vts_def = 2472,
+ .reg_modes = {
+ .mode_2lanes = ov8858_3264x2448_regs_2lane,
+ .mode_4lanes = ov8858_3264x2448_regs_4lane,
+ },
+ },
+ {
+ .width = 1632,
+ .height = 1224,
+ .exp_def = 1232,
+ .hts_def = 1928 * 2,
+ .vts_def = 1244,
+ .reg_modes = {
+ .mode_2lanes = ov8858_1632x1224_regs_2lane,
+ .mode_4lanes = ov8858_1632x1224_regs_4lane,
+ },
+ },
+};
+
+static const s64 link_freq_menu_items[] = {
+ OV8858_LINK_FREQ
+};
+
+static const char * const ov8858_test_pattern_menu[] = {
+ "Disabled",
+ "Vertical Color Bar Type 1",
+ "Vertical Color Bar Type 2",
+ "Vertical Color Bar Type 3",
+ "Vertical Color Bar Type 4"
+};
+
+/* ----------------------------------------------------------------------------
+ * HW access
+ */
+
+static int ov8858_write(struct ov8858 *ov8858, u32 reg, u32 val, int *err)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov8858->subdev);
+ unsigned int len = (reg >> OV8858_REG_SIZE_SHIFT) & 3;
+ u16 addr = reg & OV8858_REG_ADDR_MASK;
+ u8 buf[6];
+ int ret;
+
+ if (err && *err)
+ return *err;
+
+ put_unaligned_be16(addr, buf);
+ put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
+
+ ret = i2c_master_send(client, buf, len + 2);
+ if (ret != len + 2) {
+ ret = ret < 0 ? ret : -EIO;
+ if (err)
+ *err = ret;
+
+ dev_err(&client->dev,
+ "Failed to write reg %u: %d\n", addr, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ov8858_write_array(struct ov8858 *ov8858, const struct regval *regs)
+{
+ unsigned int i;
+ int ret = 0;
+
+ for (i = 0; ret == 0 && regs[i].addr != REG_NULL; ++i) {
+ ov8858_write(ov8858, OV8858_REG_8BIT(regs[i].addr),
+ regs[i].val, &ret);
+ }
+
+ return ret;
+}
+
+static int ov8858_read(struct ov8858 *ov8858, u32 reg, u32 *val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov8858->subdev);
+ __be16 reg_addr_be = cpu_to_be16(reg & OV8858_REG_ADDR_MASK);
+ unsigned int len = (reg >> OV8858_REG_SIZE_SHIFT) & 3;
+ struct i2c_msg msgs[2];
+ __be32 data_be = 0;
+ u8 *data_be_p;
+ int ret;
+
+ data_be_p = (u8 *)&data_be;
+
+ /* Write register address */
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = 2;
+ msgs[0].buf = (u8 *)&reg_addr_be;
+
+ /* Read data from register */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = len;
+ msgs[1].buf = &data_be_p[4 - len];
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs)) {
+ ret = ret < 0 ? ret : -EIO;
+ dev_err(&client->dev,
+ "Failed to read reg %u: %d\n", reg, ret);
+ return ret;
+ }
+
+ *val = be32_to_cpu(data_be);
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------------
+ * Streaming
+ */
+
+static int ov8858_start_stream(struct ov8858 *ov8858,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *format;
+ const struct ov8858_mode *mode;
+ const struct regval *reg_list;
+ int ret;
+
+ ret = ov8858_write_array(ov8858, ov8858->global_regs);
+ if (ret)
+ return ret;
+
+ format = v4l2_subdev_get_pad_format(&ov8858->subdev, state, 0);
+ mode = v4l2_find_nearest_size(ov8858_modes, ARRAY_SIZE(ov8858_modes),
+ width, height, format->width,
+ format->height);
+
+ reg_list = ov8858->num_lanes == 4
+ ? mode->reg_modes.mode_4lanes
+ : mode->reg_modes.mode_2lanes;
+
+ ret = ov8858_write_array(ov8858, reg_list);
+ if (ret)
+ return ret;
+
+ /* 200 usec max to let PLL stabilize. */
+ fsleep(200);
+
+ ret = __v4l2_ctrl_handler_setup(&ov8858->ctrl_handler);
+ if (ret)
+ return ret;
+
+ ret = ov8858_write(ov8858, OV8858_REG_SC_CTRL0100,
+ OV8858_MODE_STREAMING, NULL);
+ if (ret)
+ return ret;
+
+ /* t5 (fixed) = 10msec before entering streaming state */
+ fsleep(10000);
+
+ return 0;
+}
+
+static int ov8858_stop_stream(struct ov8858 *ov8858)
+{
+ return ov8858_write(ov8858, OV8858_REG_SC_CTRL0100,
+ OV8858_MODE_SW_STANDBY, NULL);
+}
+
+static int ov8858_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov8858 *ov8858 = sd_to_ov8858(sd);
+ struct v4l2_subdev_state *state;
+ int ret = 0;
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ if (on) {
+ ret = pm_runtime_resume_and_get(&client->dev);
+ if (ret < 0)
+ goto unlock_and_return;
+
+ ret = ov8858_start_stream(ov8858, state);
+ if (ret) {
+ dev_err(&client->dev, "Failed to start streaming\n");
+ pm_runtime_put_sync(&client->dev);
+ goto unlock_and_return;
+ }
+ } else {
+ ov8858_stop_stream(ov8858);
+ pm_runtime_mark_last_busy(&client->dev);
+ pm_runtime_put_autosuspend(&client->dev);
+ }
+
+unlock_and_return:
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_video_ops ov8858_video_ops = {
+ .s_stream = ov8858_s_stream,
+};
+
+/* ----------------------------------------------------------------------------
+ * Pad ops
+ */
+
+static int ov8858_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct ov8858 *ov8858 = sd_to_ov8858(sd);
+ const struct ov8858_mode *mode;
+ s64 h_blank, vblank_def;
+
+ mode = v4l2_find_nearest_size(ov8858_modes, ARRAY_SIZE(ov8858_modes),
+ width, height, fmt->format.width,
+ fmt->format.height);
+
+ fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ fmt->format.width = mode->width;
+ fmt->format.height = mode->height;
+ fmt->format.field = V4L2_FIELD_NONE;
+
+ /* Store the format in the current subdev state. */
+ *v4l2_subdev_get_pad_format(sd, state, 0) = fmt->format;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+ return 0;
+
+ /* Adjust control limits when a new mode is applied. */
+ h_blank = mode->hts_def - mode->width;
+ __v4l2_ctrl_modify_range(ov8858->hblank, h_blank, h_blank, 1,
+ h_blank);
+
+ vblank_def = mode->vts_def - mode->height;
+ __v4l2_ctrl_modify_range(ov8858->vblank, vblank_def,
+ OV8858_VTS_MAX - mode->height, 1,
+ vblank_def);
+
+ return 0;
+}
+
+static int ov8858_enum_frame_sizes(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->index >= ARRAY_SIZE(ov8858_modes))
+ return -EINVAL;
+
+ if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10)
+ return -EINVAL;
+
+ fse->min_width = ov8858_modes[fse->index].width;
+ fse->max_width = ov8858_modes[fse->index].width;
+ fse->max_height = ov8858_modes[fse->index].height;
+ fse->min_height = ov8858_modes[fse->index].height;
+
+ return 0;
+}
+
+static int ov8858_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index != 0)
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+
+ return 0;
+}
+
+static int ov8858_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
+{
+ const struct ov8858_mode *def_mode = &ov8858_modes[0];
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ .format = {
+ .width = def_mode->width,
+ .height = def_mode->height,
+ },
+ };
+
+ ov8858_set_fmt(sd, sd_state, &fmt);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops ov8858_pad_ops = {
+ .init_cfg = ov8858_init_cfg,
+ .enum_mbus_code = ov8858_enum_mbus_code,
+ .enum_frame_size = ov8858_enum_frame_sizes,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = ov8858_set_fmt,
+};
+
+static const struct v4l2_subdev_core_ops ov8858_core_ops = {
+ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static const struct v4l2_subdev_ops ov8858_subdev_ops = {
+ .core = &ov8858_core_ops,
+ .video = &ov8858_video_ops,
+ .pad = &ov8858_pad_ops,
+};
+
+/* ----------------------------------------------------------------------------
+ * Controls handling
+ */
+
+static int ov8858_enable_test_pattern(struct ov8858 *ov8858, u32 pattern)
+{
+ u32 val;
+
+ if (pattern)
+ val = (pattern - 1) | OV8858_TEST_PATTERN_ENABLE;
+ else
+ val = OV8858_TEST_PATTERN_DISABLE;
+
+ return ov8858_write(ov8858, OV8858_REG_TEST_PATTERN, val, NULL);
+}
+
+static int ov8858_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov8858 *ov8858 = container_of(ctrl->handler,
+ struct ov8858, ctrl_handler);
+
+ struct i2c_client *client = v4l2_get_subdevdata(&ov8858->subdev);
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_subdev_state *state;
+ u16 digi_gain;
+ s64 max_exp;
+ int ret;
+
+ /*
+ * The control handler and the subdev state use the same mutex and the
+ * mutex is guaranteed to be locked:
+ * - by the core when s_ctrl is called int the VIDIOC_S_CTRL call path
+ * - by the driver when s_ctrl is called in the s_stream(1) call path
+ */
+ state = v4l2_subdev_get_locked_active_state(&ov8858->subdev);
+ format = v4l2_subdev_get_pad_format(&ov8858->subdev, state, 0);
+
+ /* Propagate change of current control to all related controls */
+ switch (ctrl->id) {
+ case V4L2_CID_VBLANK:
+ /* Update max exposure while meeting expected vblanking */
+ max_exp = format->height + ctrl->val - OV8858_EXPOSURE_MARGIN;
+ __v4l2_ctrl_modify_range(ov8858->exposure,
+ ov8858->exposure->minimum, max_exp,
+ ov8858->exposure->step,
+ ov8858->exposure->default_value);
+ break;
+ }
+
+ if (!pm_runtime_get_if_in_use(&client->dev))
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ /* 4 least significant bits of exposure are fractional part */
+ ret = ov8858_write(ov8858, OV8858_REG_LONG_EXPO,
+ ctrl->val << 4, NULL);
+ break;
+ case V4L2_CID_ANALOGUE_GAIN:
+ ret = ov8858_write(ov8858, OV8858_REG_LONG_GAIN,
+ ctrl->val, NULL);
+ break;
+ case V4L2_CID_DIGITAL_GAIN:
+ /*
+ * Digital gain is assembled as:
+ * 0x350a[7:0] = dgain[13:6]
+ * 0x350b[5:0] = dgain[5:0]
+ * Reassemble the control value to write it in one go.
+ */
+ digi_gain = (ctrl->val & OV8858_LONG_DIGIGAIN_L_MASK)
+ | ((ctrl->val & OV8858_LONG_DIGIGAIN_H_MASK) <<
+ OV8858_LONG_DIGIGAIN_H_SHIFT);
+ ret = ov8858_write(ov8858, OV8858_REG_LONG_DIGIGAIN,
+ digi_gain, NULL);
+ break;
+ case V4L2_CID_VBLANK:
+ ret = ov8858_write(ov8858, OV8858_REG_VTS,
+ ctrl->val + format->height, NULL);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ ret = ov8858_enable_test_pattern(ov8858, ctrl->val);
+ break;
+ default:
+ ret = -EINVAL;
+ dev_warn(&client->dev, "%s Unhandled id: 0x%x\n",
+ __func__, ctrl->id);
+ break;
+ }
+
+ pm_runtime_put(&client->dev);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops ov8858_ctrl_ops = {
+ .s_ctrl = ov8858_set_ctrl,
+};
+
+/* ----------------------------------------------------------------------------
+ * Power Management
+ */
+
+static int ov8858_power_on(struct ov8858 *ov8858)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov8858->subdev);
+ struct device *dev = &client->dev;
+ unsigned long delay_us;
+ int ret;
+
+ if (clk_get_rate(ov8858->xvclk) != OV8858_XVCLK_FREQ)
+ dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
+
+ ret = clk_prepare_enable(ov8858->xvclk);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable xvclk\n");
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ov8858_supply_names),
+ ov8858->supplies);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable regulators\n");
+ goto disable_clk;
+ }
+
+ /*
+ * The chip manual only suggests 8192 cycles prior to first SCCB
+ * transaction, but a double sleep between the release of gpios
+ * helps with sporadic failures observed at probe time.
+ */
+ delay_us = DIV_ROUND_UP(8192, OV8858_XVCLK_FREQ / 1000 / 1000);
+
+ gpiod_set_value_cansleep(ov8858->reset_gpio, 0);
+ fsleep(delay_us);
+ gpiod_set_value_cansleep(ov8858->pwdn_gpio, 0);
+ fsleep(delay_us);
+
+ return 0;
+
+disable_clk:
+ clk_disable_unprepare(ov8858->xvclk);
+
+ return ret;
+}
+
+static void ov8858_power_off(struct ov8858 *ov8858)
+{
+ gpiod_set_value_cansleep(ov8858->pwdn_gpio, 1);
+ clk_disable_unprepare(ov8858->xvclk);
+ gpiod_set_value_cansleep(ov8858->reset_gpio, 1);
+
+ regulator_bulk_disable(ARRAY_SIZE(ov8858_supply_names),
+ ov8858->supplies);
+}
+
+static int ov8858_runtime_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov8858 *ov8858 = sd_to_ov8858(sd);
+
+ return ov8858_power_on(ov8858);
+}
+
+static int ov8858_runtime_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov8858 *ov8858 = sd_to_ov8858(sd);
+
+ ov8858_power_off(ov8858);
+
+ return 0;
+}
+
+static const struct dev_pm_ops ov8858_pm_ops = {
+ SET_RUNTIME_PM_OPS(ov8858_runtime_suspend,
+ ov8858_runtime_resume, NULL)
+};
+
+/* ----------------------------------------------------------------------------
+ * Probe and initialization
+ */
+
+static int ov8858_init_ctrls(struct ov8858 *ov8858)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov8858->subdev);
+ struct v4l2_ctrl_handler *handler = &ov8858->ctrl_handler;
+ const struct ov8858_mode *mode = &ov8858_modes[0];
+ struct v4l2_fwnode_device_properties props;
+ s64 exposure_max, vblank_def;
+ unsigned int pixel_rate;
+ struct v4l2_ctrl *ctrl;
+ u32 h_blank;
+ int ret;
+
+ ret = v4l2_ctrl_handler_init(handler, 10);
+ if (ret)
+ return ret;
+
+ ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
+ 0, 0, link_freq_menu_items);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ /* pixel rate = link frequency * 2 * lanes / bpp */
+ pixel_rate = OV8858_LINK_FREQ * 2 * ov8858->num_lanes / 10;
+ v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,
+ 0, pixel_rate, 1, pixel_rate);
+
+ h_blank = mode->hts_def - mode->width;
+ ov8858->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
+ h_blank, h_blank, 1, h_blank);
+ if (ov8858->hblank)
+ ov8858->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ vblank_def = mode->vts_def - mode->height;
+ ov8858->vblank = v4l2_ctrl_new_std(handler, &ov8858_ctrl_ops,
+ V4L2_CID_VBLANK, vblank_def,
+ OV8858_VTS_MAX - mode->height,
+ 1, vblank_def);
+
+ exposure_max = mode->vts_def - OV8858_EXPOSURE_MARGIN;
+ ov8858->exposure = v4l2_ctrl_new_std(handler, &ov8858_ctrl_ops,
+ V4L2_CID_EXPOSURE,
+ OV8858_EXPOSURE_MIN,
+ exposure_max, OV8858_EXPOSURE_STEP,
+ mode->exp_def);
+
+ v4l2_ctrl_new_std(handler, &ov8858_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
+ OV8858_LONG_GAIN_MIN, OV8858_LONG_GAIN_MAX,
+ OV8858_LONG_GAIN_STEP, OV8858_LONG_GAIN_DEFAULT);
+
+ v4l2_ctrl_new_std(handler, &ov8858_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
+ OV8858_LONG_DIGIGAIN_MIN, OV8858_LONG_DIGIGAIN_MAX,
+ OV8858_LONG_DIGIGAIN_STEP,
+ OV8858_LONG_DIGIGAIN_DEFAULT);
+
+ v4l2_ctrl_new_std_menu_items(handler, &ov8858_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(ov8858_test_pattern_menu) - 1,
+ 0, 0, ov8858_test_pattern_menu);
+
+ if (handler->error) {
+ ret = handler->error;
+ goto err_free_handler;
+ }
+
+ ret = v4l2_fwnode_device_parse(&client->dev, &props);
+ if (ret)
+ goto err_free_handler;
+
+ ret = v4l2_ctrl_new_fwnode_properties(handler, &ov8858_ctrl_ops,
+ &props);
+ if (ret)
+ goto err_free_handler;
+
+ ov8858->subdev.ctrl_handler = handler;
+
+ return 0;
+
+err_free_handler:
+ dev_err(&client->dev, "Failed to init controls: %d\n", ret);
+ v4l2_ctrl_handler_free(handler);
+
+ return ret;
+}
+
+static int ov8858_check_sensor_id(struct ov8858 *ov8858)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov8858->subdev);
+ u32 id = 0;
+ int ret;
+
+ ret = ov8858_read(ov8858, OV8858_REG_CHIP_ID, &id);
+ if (ret)
+ return ret;
+
+ if (id != OV8858_CHIP_ID) {
+ dev_err(&client->dev, "Unexpected sensor id 0x%x\n", id);
+ return -ENODEV;
+ }
+
+ ret = ov8858_read(ov8858, OV8858_REG_SUB_ID, &id);
+ if (ret)
+ return ret;
+
+ dev_info(&client->dev, "Detected OV8858 sensor, revision 0x%x\n", id);
+
+ if (id == OV8858_R2A) {
+ /* R2A supports 2 and 4 lanes modes. */
+ ov8858->global_regs = ov8858->num_lanes == 4
+ ? ov8858_global_regs_r2a_4lane
+ : ov8858_global_regs_r2a_2lane;
+ } else if (ov8858->num_lanes == 2) {
+ /*
+ * R1A only supports 2 lanes mode and it's only partially
+ * supported.
+ */
+ ov8858->global_regs = ov8858_global_regs_r1a;
+ dev_warn(&client->dev, "R1A may not work well!\n");
+ } else {
+ dev_err(&client->dev,
+ "Unsupported number of data lanes for R1A revision.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ov8858_configure_regulators(struct ov8858 *ov8858)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov8858->subdev);
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(ov8858_supply_names); i++)
+ ov8858->supplies[i].supply = ov8858_supply_names[i];
+
+ return devm_regulator_bulk_get(&client->dev,
+ ARRAY_SIZE(ov8858_supply_names),
+ ov8858->supplies);
+}
+
+static int ov8858_parse_of(struct ov8858 *ov8858)
+{
+ struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_CSI2_DPHY };
+ struct i2c_client *client = v4l2_get_subdevdata(&ov8858->subdev);
+ struct device *dev = &client->dev;
+ struct fwnode_handle *endpoint;
+ int ret;
+
+ endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
+ if (!endpoint) {
+ dev_err(dev, "Failed to get endpoint\n");
+ return -EINVAL;
+ }
+
+ ret = v4l2_fwnode_endpoint_parse(endpoint, &vep);
+ if (ret) {
+ dev_err(dev, "Failed to parse endpoint: %d\n", ret);
+ fwnode_handle_put(endpoint);
+ return ret;
+ }
+
+ ov8858->num_lanes = vep.bus.mipi_csi2.num_data_lanes;
+ switch (ov8858->num_lanes) {
+ case 4:
+ case 2:
+ break;
+ default:
+ dev_err(dev, "Unsupported number of data lanes %u\n",
+ ov8858->num_lanes);
+ fwnode_handle_put(endpoint);
+ return -EINVAL;
+ }
+
+ ov8858->subdev.fwnode = endpoint;
+
+ return 0;
+}
+
+static int ov8858_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct v4l2_subdev *sd;
+ struct ov8858 *ov8858;
+ int ret;
+
+ ov8858 = devm_kzalloc(dev, sizeof(*ov8858), GFP_KERNEL);
+ if (!ov8858)
+ return -ENOMEM;
+
+ ov8858->xvclk = devm_clk_get(dev, "xvclk");
+ if (IS_ERR(ov8858->xvclk))
+ return dev_err_probe(dev, PTR_ERR(ov8858->xvclk),
+ "Failed to get xvclk\n");
+
+ ov8858->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ov8858->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(ov8858->reset_gpio),
+ "Failed to get reset gpio\n");
+
+ ov8858->pwdn_gpio = devm_gpiod_get_optional(dev, "powerdown",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ov8858->pwdn_gpio))
+ return dev_err_probe(dev, PTR_ERR(ov8858->pwdn_gpio),
+ "Failed to get powerdown gpio\n");
+
+ v4l2_i2c_subdev_init(&ov8858->subdev, client, &ov8858_subdev_ops);
+
+ ret = ov8858_configure_regulators(ov8858);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get regulators\n");
+
+ ret = ov8858_parse_of(ov8858);
+ if (ret)
+ return ret;
+
+ ret = ov8858_init_ctrls(ov8858);
+ if (ret)
+ goto err_put_fwnode;
+
+ sd = &ov8858->subdev;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+ ov8858->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&sd->entity, 1, &ov8858->pad);
+ if (ret < 0)
+ goto err_free_handler;
+
+ sd->state_lock = ov8858->ctrl_handler.lock;
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret < 0) {
+ dev_err(&client->dev, "Subdev initialization error %d\n", ret);
+ goto err_clean_entity;
+ }
+
+ ret = ov8858_power_on(ov8858);
+ if (ret)
+ goto err_clean_entity;
+
+ pm_runtime_set_active(dev);
+ pm_runtime_get_noresume(dev);
+ pm_runtime_enable(dev);
+
+ ret = ov8858_check_sensor_id(ov8858);
+ if (ret)
+ goto err_power_off;
+
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+
+ ret = v4l2_async_register_subdev_sensor(sd);
+ if (ret) {
+ dev_err(dev, "v4l2 async register subdev failed\n");
+ goto err_power_off;
+ }
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+
+err_power_off:
+ pm_runtime_disable(dev);
+ pm_runtime_put_noidle(dev);
+ ov8858_power_off(ov8858);
+err_clean_entity:
+ media_entity_cleanup(&sd->entity);
+err_free_handler:
+ v4l2_ctrl_handler_free(&ov8858->ctrl_handler);
+err_put_fwnode:
+ fwnode_handle_put(ov8858->subdev.fwnode);
+
+ return ret;
+}
+
+static void ov8858_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov8858 *ov8858 = sd_to_ov8858(sd);
+
+ v4l2_async_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(&ov8858->ctrl_handler);
+ fwnode_handle_put(ov8858->subdev.fwnode);
+
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev))
+ ov8858_power_off(ov8858);
+ pm_runtime_set_suspended(&client->dev);
+}
+
+static const struct of_device_id ov8858_of_match[] = {
+ { .compatible = "ovti,ov8858" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ov8858_of_match);
+
+static struct i2c_driver ov8858_i2c_driver = {
+ .driver = {
+ .name = "ov8858",
+ .pm = &ov8858_pm_ops,
+ .of_match_table = ov8858_of_match,
+ },
+ .probe_new = &ov8858_probe,
+ .remove = &ov8858_remove,
+};
+
+module_i2c_driver(ov8858_i2c_driver);
+
+MODULE_DESCRIPTION("OmniVision OV8858 sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c
index 37a55d53af56..7f46cac38aab 100644
--- a/drivers/media/i2c/ov9282.c
+++ b/drivers/media/i2c/ov9282.c
@@ -148,7 +148,6 @@ struct ov9282_mode {
/**
* struct ov9282 - ov9282 sensor device structure
* @dev: Pointer to generic device
- * @client: Pointer to i2c client
* @sd: V4L2 sub-device
* @pad: Media pad. Only one pad supported
* @reset_gpio: Sensor reset gpio
@@ -170,7 +169,6 @@ struct ov9282_mode {
*/
struct ov9282 {
struct device *dev;
- struct i2c_client *client;
struct v4l2_subdev sd;
struct media_pad pad;
struct gpio_desc *reset_gpio;
@@ -1144,10 +1142,9 @@ static int ov9282_parse_hw_config(struct ov9282 *ov9282)
}
ret = ov9282_configure_regulators(ov9282);
- if (ret) {
- dev_err(ov9282->dev, "Failed to get power regulators\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(ov9282->dev, ret,
+ "Failed to get power regulators\n");
rate = clk_get_rate(ov9282->inclk);
if (rate != OV9282_INCLK_RATE) {
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
index 59b03b0860d5..7938a3327d3e 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
@@ -26,7 +26,6 @@
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-mediabus.h>
-#include <media/i2c/s5c73m3.h>
#include <media/v4l2-fwnode.h>
#include "s5c73m3.h"
@@ -436,7 +435,7 @@ static int __s5c73m3_s_stream(struct s5c73m3 *state, struct v4l2_subdev *sd,
state->streaming = !!on;
if (!on)
- return ret;
+ return 0;
if (state->apply_fiv) {
ret = s5c73m3_set_frame_rate(state);
@@ -1522,25 +1521,16 @@ static const struct v4l2_subdev_ops oif_subdev_ops = {
.video = &s5c73m3_oif_video_ops,
};
-static int s5c73m3_get_platform_data(struct s5c73m3 *state)
+static int s5c73m3_get_dt_data(struct s5c73m3 *state)
{
- struct i2c_client *c = state->i2c_client;
- struct device *dev = &c->dev;
- const struct s5c73m3_platform_data *pdata = dev->platform_data;
+ struct device *dev = &state->i2c_client->dev;
struct device_node *node = dev->of_node;
struct device_node *node_ep;
struct v4l2_fwnode_endpoint ep = { .bus_type = 0 };
int ret;
- if (!node) {
- if (!pdata) {
- dev_err(dev, "Platform data not specified\n");
- return -EINVAL;
- }
-
- state->mclk_frequency = pdata->mclk_frequency;
- return 0;
- }
+ if (!node)
+ return -EINVAL;
state->clock = devm_clk_get(dev, S5C73M3_CLK_NAME);
if (IS_ERR(state->clock))
@@ -1603,7 +1593,7 @@ static int s5c73m3_probe(struct i2c_client *client)
return -ENOMEM;
state->i2c_client = client;
- ret = s5c73m3_get_platform_data(state);
+ ret = s5c73m3_get_dt_data(state);
if (ret < 0)
return ret;
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c b/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c
index e3543ae384ed..1c8103670fa2 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c
@@ -23,7 +23,6 @@
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-mediabus.h>
-#include <media/i2c/s5c73m3.h>
#include "s5c73m3.h"
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3.h b/drivers/media/i2c/s5c73m3/s5c73m3.h
index 1fc7df41c5ee..627e80cf5b72 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3.h
+++ b/drivers/media/i2c/s5c73m3/s5c73m3.h
@@ -16,7 +16,6 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
-#include <media/i2c/s5c73m3.h>
#define DRIVER_NAME "S5C73M3"
diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c
index 86e70a980218..efeda3956f81 100644
--- a/drivers/media/i2c/saa7115.c
+++ b/drivers/media/i2c/saa7115.c
@@ -1804,9 +1804,9 @@ static int saa711x_detect_chip(struct i2c_client *client,
return -ENODEV;
}
-static int saa711x_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int saa711x_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct saa711x_state *state;
struct v4l2_subdev *sd;
struct v4l2_ctrl_handler *hdl;
@@ -1951,7 +1951,7 @@ static struct i2c_driver saa711x_driver = {
.driver = {
.name = "saa7115",
},
- .probe = saa711x_probe,
+ .probe_new = saa711x_probe,
.remove = saa711x_remove,
.id_table = saa711x_id,
};
diff --git a/drivers/media/i2c/saa7127.c b/drivers/media/i2c/saa7127.c
index 78c9388c2ea1..f98f3a1c38a9 100644
--- a/drivers/media/i2c/saa7127.c
+++ b/drivers/media/i2c/saa7127.c
@@ -708,9 +708,9 @@ static const struct v4l2_subdev_ops saa7127_ops = {
/* ----------------------------------------------------------------------- */
-static int saa7127_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int saa7127_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct saa7127_state *state;
struct v4l2_subdev *sd;
struct v4l2_sliced_vbi_data vbi = { 0, 0, 0, 0 }; /* set to disabled */
@@ -810,7 +810,7 @@ static struct i2c_driver saa7127_driver = {
.driver = {
.name = "saa7127",
},
- .probe = saa7127_probe,
+ .probe_new = saa7127_probe,
.remove = saa7127_remove,
.id_table = saa7127_id,
};
diff --git a/drivers/media/i2c/st-vgxy61.c b/drivers/media/i2c/st-vgxy61.c
index 826baf4e064d..5dcabee6677d 100644
--- a/drivers/media/i2c/st-vgxy61.c
+++ b/drivers/media/i2c/st-vgxy61.c
@@ -5,7 +5,6 @@
* Copyright (C) 2022 STMicroelectronics SA
*/
-#include <asm-generic/unaligned.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
@@ -15,6 +14,9 @@
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/units.h>
+
+#include <asm/unaligned.h>
+
#include <media/mipi-csi2.h>
#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
diff --git a/drivers/media/i2c/tc358746.c b/drivers/media/i2c/tc358746.c
index d1f552bd81d4..4063754a6732 100644
--- a/drivers/media/i2c/tc358746.c
+++ b/drivers/media/i2c/tc358746.c
@@ -406,7 +406,7 @@ tc358746_apply_pll_config(struct tc358746 *tc358746)
val = PLL_FRS(ilog2(post)) | RESETB | PLL_EN;
mask = PLL_FRS_MASK | RESETB | PLL_EN;
- tc358746_update_bits(tc358746, PLLCTL1_REG, mask, val);
+ err = tc358746_update_bits(tc358746, PLLCTL1_REG, mask, val);
if (err)
return err;
@@ -988,6 +988,8 @@ static int __maybe_unused
tc358746_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
{
struct tc358746 *tc358746 = to_tc358746(sd);
+ u32 val;
+ int err;
/* 32-bit registers starting from CLW_DPHYCONTTX */
reg->size = reg->reg < CLW_DPHYCONTTX_REG ? 2 : 4;
@@ -995,12 +997,13 @@ tc358746_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
if (!pm_runtime_get_if_in_use(sd->dev))
return 0;
- tc358746_read(tc358746, reg->reg, (u32 *)&reg->val);
+ err = tc358746_read(tc358746, reg->reg, &val);
+ reg->val = val;
pm_runtime_mark_last_busy(sd->dev);
pm_runtime_put_sync_autosuspend(sd->dev);
- return 0;
+ return err;
}
static int __maybe_unused
diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c
index 83931826cf6f..27f6393dc327 100644
--- a/drivers/media/i2c/tda1997x.c
+++ b/drivers/media/i2c/tda1997x.c
@@ -2519,9 +2519,9 @@ static struct snd_soc_component_driver tda1997x_codec_driver = {
.endianness = 1,
};
-static int tda1997x_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int tda1997x_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct tda1997x_state *state;
struct tda1997x_platform_data *pdata;
struct v4l2_subdev *sd;
@@ -2834,7 +2834,7 @@ static struct i2c_driver tda1997x_i2c_driver = {
.name = "tda1997x",
.of_match_table = of_match_ptr(tda1997x_of_id),
},
- .probe = tda1997x_probe,
+ .probe_new = tda1997x_probe,
.remove = tda1997x_remove,
.id_table = tda1997x_i2c_id,
};
diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c
index 9f1ed078b661..a54c76d9e23b 100644
--- a/drivers/media/i2c/tvaudio.c
+++ b/drivers/media/i2c/tvaudio.c
@@ -1934,8 +1934,9 @@ static const struct v4l2_subdev_ops tvaudio_ops = {
/* i2c registration */
-static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id *id)
+static int tvaudio_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct CHIPSTATE *chip;
struct CHIPDESC *desc;
struct v4l2_subdev *sd;
@@ -2094,7 +2095,7 @@ static struct i2c_driver tvaudio_driver = {
.driver = {
.name = "tvaudio",
},
- .probe = tvaudio_probe,
+ .probe_new = tvaudio_probe,
.remove = tvaudio_remove,
.id_table = tvaudio_id,
};
diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c
index a746d96875f9..f294cae72b01 100644
--- a/drivers/media/i2c/tvp514x.c
+++ b/drivers/media/i2c/tvp514x.c
@@ -1017,14 +1017,14 @@ done:
/**
* tvp514x_probe() - decoder driver i2c probe handler
* @client: i2c driver client device structure
- * @id: i2c driver id table
*
* Register decoder as an i2c client device and V4L2
* device.
*/
static int
-tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id)
+tvp514x_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct tvp514x_platform_data *pdata = tvp514x_get_pdata(client);
struct tvp514x_decoder *decoder;
struct v4l2_subdev *sd;
@@ -1208,7 +1208,7 @@ static struct i2c_driver tvp514x_driver = {
.of_match_table = of_match_ptr(tvp514x_of_match),
.name = TVP514X_MODULE_NAME,
},
- .probe = tvp514x_probe,
+ .probe_new = tvp514x_probe,
.remove = tvp514x_remove,
.id_table = tvp514x_id,
};
diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
index f15ef2d13059..dddf9827b314 100644
--- a/drivers/media/i2c/video-i2c.c
+++ b/drivers/media/i2c/video-i2c.c
@@ -757,9 +757,9 @@ static void video_i2c_release(struct video_device *vdev)
kfree(data);
}
-static int video_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int video_i2c_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct video_i2c_data *data;
struct v4l2_device *v4l2_dev;
struct vb2_queue *queue;
@@ -959,7 +959,7 @@ static struct i2c_driver video_i2c_driver = {
.of_match_table = video_i2c_of_match,
.pm = &video_i2c_pm_ops,
},
- .probe = video_i2c_probe,
+ .probe_new = video_i2c_probe,
.remove = video_i2c_remove,
.id_table = video_i2c_id_table,
};