xref: /openbmc/linux/drivers/media/i2c/imx412.c (revision aaeb31c0)
19214e86cSMartina Krasteva // SPDX-License-Identifier: GPL-2.0-only
29214e86cSMartina Krasteva /*
39214e86cSMartina Krasteva  * Sony imx412 Camera Sensor Driver
49214e86cSMartina Krasteva  *
59214e86cSMartina Krasteva  * Copyright (C) 2021 Intel Corporation
69214e86cSMartina Krasteva  */
79214e86cSMartina Krasteva #include <asm/unaligned.h>
89214e86cSMartina Krasteva 
99214e86cSMartina Krasteva #include <linux/clk.h>
109214e86cSMartina Krasteva #include <linux/delay.h>
119214e86cSMartina Krasteva #include <linux/i2c.h>
129214e86cSMartina Krasteva #include <linux/module.h>
139214e86cSMartina Krasteva #include <linux/pm_runtime.h>
143de9dc7fSBryan O'Donoghue #include <linux/regulator/consumer.h>
159214e86cSMartina Krasteva 
169214e86cSMartina Krasteva #include <media/v4l2-ctrls.h>
179214e86cSMartina Krasteva #include <media/v4l2-fwnode.h>
189214e86cSMartina Krasteva #include <media/v4l2-subdev.h>
199214e86cSMartina Krasteva 
209214e86cSMartina Krasteva /* Streaming Mode */
219214e86cSMartina Krasteva #define IMX412_REG_MODE_SELECT	0x0100
229214e86cSMartina Krasteva #define IMX412_MODE_STANDBY	0x00
239214e86cSMartina Krasteva #define IMX412_MODE_STREAMING	0x01
249214e86cSMartina Krasteva 
259214e86cSMartina Krasteva /* Lines per frame */
269214e86cSMartina Krasteva #define IMX412_REG_LPFR		0x0340
279214e86cSMartina Krasteva 
289214e86cSMartina Krasteva /* Chip ID */
299214e86cSMartina Krasteva #define IMX412_REG_ID		0x0016
309214e86cSMartina Krasteva #define IMX412_ID		0x577
319214e86cSMartina Krasteva 
329214e86cSMartina Krasteva /* Exposure control */
339214e86cSMartina Krasteva #define IMX412_REG_EXPOSURE_CIT	0x0202
349214e86cSMartina Krasteva #define IMX412_EXPOSURE_MIN	8
359214e86cSMartina Krasteva #define IMX412_EXPOSURE_OFFSET	22
369214e86cSMartina Krasteva #define IMX412_EXPOSURE_STEP	1
379214e86cSMartina Krasteva #define IMX412_EXPOSURE_DEFAULT	0x0648
389214e86cSMartina Krasteva 
399214e86cSMartina Krasteva /* Analog gain control */
409214e86cSMartina Krasteva #define IMX412_REG_AGAIN	0x0204
419214e86cSMartina Krasteva #define IMX412_AGAIN_MIN	0
429214e86cSMartina Krasteva #define IMX412_AGAIN_MAX	978
439214e86cSMartina Krasteva #define IMX412_AGAIN_STEP	1
449214e86cSMartina Krasteva #define IMX412_AGAIN_DEFAULT	0
459214e86cSMartina Krasteva 
469214e86cSMartina Krasteva /* Group hold register */
479214e86cSMartina Krasteva #define IMX412_REG_HOLD		0x0104
489214e86cSMartina Krasteva 
499214e86cSMartina Krasteva /* Input clock rate */
509214e86cSMartina Krasteva #define IMX412_INCLK_RATE	24000000
519214e86cSMartina Krasteva 
529214e86cSMartina Krasteva /* CSI2 HW configuration */
539214e86cSMartina Krasteva #define IMX412_LINK_FREQ	600000000
549214e86cSMartina Krasteva #define IMX412_NUM_DATA_LANES	4
559214e86cSMartina Krasteva 
569214e86cSMartina Krasteva #define IMX412_REG_MIN		0x00
579214e86cSMartina Krasteva #define IMX412_REG_MAX		0xffff
589214e86cSMartina Krasteva 
599214e86cSMartina Krasteva /**
609214e86cSMartina Krasteva  * struct imx412_reg - imx412 sensor register
619214e86cSMartina Krasteva  * @address: Register address
629214e86cSMartina Krasteva  * @val: Register value
639214e86cSMartina Krasteva  */
649214e86cSMartina Krasteva struct imx412_reg {
659214e86cSMartina Krasteva 	u16 address;
669214e86cSMartina Krasteva 	u8 val;
679214e86cSMartina Krasteva };
689214e86cSMartina Krasteva 
699214e86cSMartina Krasteva /**
709214e86cSMartina Krasteva  * struct imx412_reg_list - imx412 sensor register list
719214e86cSMartina Krasteva  * @num_of_regs: Number of registers in the list
729214e86cSMartina Krasteva  * @regs: Pointer to register list
739214e86cSMartina Krasteva  */
749214e86cSMartina Krasteva struct imx412_reg_list {
759214e86cSMartina Krasteva 	u32 num_of_regs;
769214e86cSMartina Krasteva 	const struct imx412_reg *regs;
779214e86cSMartina Krasteva };
789214e86cSMartina Krasteva 
799214e86cSMartina Krasteva /**
809214e86cSMartina Krasteva  * struct imx412_mode - imx412 sensor mode structure
819214e86cSMartina Krasteva  * @width: Frame width
829214e86cSMartina Krasteva  * @height: Frame height
839214e86cSMartina Krasteva  * @code: Format code
849214e86cSMartina Krasteva  * @hblank: Horizontal blanking in lines
859214e86cSMartina Krasteva  * @vblank: Vertical blanking in lines
869214e86cSMartina Krasteva  * @vblank_min: Minimum vertical blanking in lines
879214e86cSMartina Krasteva  * @vblank_max: Maximum vertical blanking in lines
889214e86cSMartina Krasteva  * @pclk: Sensor pixel clock
899214e86cSMartina Krasteva  * @link_freq_idx: Link frequency index
909214e86cSMartina Krasteva  * @reg_list: Register list for sensor mode
919214e86cSMartina Krasteva  */
929214e86cSMartina Krasteva struct imx412_mode {
939214e86cSMartina Krasteva 	u32 width;
949214e86cSMartina Krasteva 	u32 height;
959214e86cSMartina Krasteva 	u32 code;
969214e86cSMartina Krasteva 	u32 hblank;
979214e86cSMartina Krasteva 	u32 vblank;
989214e86cSMartina Krasteva 	u32 vblank_min;
999214e86cSMartina Krasteva 	u32 vblank_max;
1009214e86cSMartina Krasteva 	u64 pclk;
1019214e86cSMartina Krasteva 	u32 link_freq_idx;
1029214e86cSMartina Krasteva 	struct imx412_reg_list reg_list;
1039214e86cSMartina Krasteva };
1049214e86cSMartina Krasteva 
1053de9dc7fSBryan O'Donoghue static const char * const imx412_supply_names[] = {
1063de9dc7fSBryan O'Donoghue 	"dovdd",	/* Digital I/O power */
1073de9dc7fSBryan O'Donoghue 	"avdd",		/* Analog power */
1083de9dc7fSBryan O'Donoghue 	"dvdd",		/* Digital core power */
1093de9dc7fSBryan O'Donoghue };
1103de9dc7fSBryan O'Donoghue 
1119214e86cSMartina Krasteva /**
1129214e86cSMartina Krasteva  * struct imx412 - imx412 sensor device structure
1139214e86cSMartina Krasteva  * @dev: Pointer to generic device
1149214e86cSMartina Krasteva  * @client: Pointer to i2c client
1159214e86cSMartina Krasteva  * @sd: V4L2 sub-device
1169214e86cSMartina Krasteva  * @pad: Media pad. Only one pad supported
1179214e86cSMartina Krasteva  * @reset_gpio: Sensor reset gpio
1189214e86cSMartina Krasteva  * @inclk: Sensor input clock
1193de9dc7fSBryan O'Donoghue  * @supplies: Regulator supplies
1209214e86cSMartina Krasteva  * @ctrl_handler: V4L2 control handler
1219214e86cSMartina Krasteva  * @link_freq_ctrl: Pointer to link frequency control
1229214e86cSMartina Krasteva  * @pclk_ctrl: Pointer to pixel clock control
1239214e86cSMartina Krasteva  * @hblank_ctrl: Pointer to horizontal blanking control
1249214e86cSMartina Krasteva  * @vblank_ctrl: Pointer to vertical blanking control
1259214e86cSMartina Krasteva  * @exp_ctrl: Pointer to exposure control
1269214e86cSMartina Krasteva  * @again_ctrl: Pointer to analog gain control
1279214e86cSMartina Krasteva  * @vblank: Vertical blanking in lines
1289214e86cSMartina Krasteva  * @cur_mode: Pointer to current selected sensor mode
1299214e86cSMartina Krasteva  * @mutex: Mutex for serializing sensor controls
1309214e86cSMartina Krasteva  * @streaming: Flag indicating streaming state
1319214e86cSMartina Krasteva  */
1329214e86cSMartina Krasteva struct imx412 {
1339214e86cSMartina Krasteva 	struct device *dev;
1349214e86cSMartina Krasteva 	struct i2c_client *client;
1359214e86cSMartina Krasteva 	struct v4l2_subdev sd;
1369214e86cSMartina Krasteva 	struct media_pad pad;
1379214e86cSMartina Krasteva 	struct gpio_desc *reset_gpio;
1389214e86cSMartina Krasteva 	struct clk *inclk;
1393de9dc7fSBryan O'Donoghue 	struct regulator_bulk_data supplies[ARRAY_SIZE(imx412_supply_names)];
1409214e86cSMartina Krasteva 	struct v4l2_ctrl_handler ctrl_handler;
1419214e86cSMartina Krasteva 	struct v4l2_ctrl *link_freq_ctrl;
1429214e86cSMartina Krasteva 	struct v4l2_ctrl *pclk_ctrl;
1439214e86cSMartina Krasteva 	struct v4l2_ctrl *hblank_ctrl;
1449214e86cSMartina Krasteva 	struct v4l2_ctrl *vblank_ctrl;
1459214e86cSMartina Krasteva 	struct {
1469214e86cSMartina Krasteva 		struct v4l2_ctrl *exp_ctrl;
1479214e86cSMartina Krasteva 		struct v4l2_ctrl *again_ctrl;
1489214e86cSMartina Krasteva 	};
1499214e86cSMartina Krasteva 	u32 vblank;
1509214e86cSMartina Krasteva 	const struct imx412_mode *cur_mode;
1519214e86cSMartina Krasteva 	struct mutex mutex;
1529214e86cSMartina Krasteva 	bool streaming;
1539214e86cSMartina Krasteva };
1549214e86cSMartina Krasteva 
1559214e86cSMartina Krasteva static const s64 link_freq[] = {
1569214e86cSMartina Krasteva 	IMX412_LINK_FREQ,
1579214e86cSMartina Krasteva };
1589214e86cSMartina Krasteva 
1599214e86cSMartina Krasteva /* Sensor mode registers */
1609214e86cSMartina Krasteva static const struct imx412_reg mode_4056x3040_regs[] = {
1619214e86cSMartina Krasteva 	{0x0136, 0x18},
1629214e86cSMartina Krasteva 	{0x0137, 0x00},
1639214e86cSMartina Krasteva 	{0x3c7e, 0x08},
1649214e86cSMartina Krasteva 	{0x3c7f, 0x02},
1659214e86cSMartina Krasteva 	{0x38a8, 0x1f},
1669214e86cSMartina Krasteva 	{0x38a9, 0xff},
1679214e86cSMartina Krasteva 	{0x38aa, 0x1f},
1689214e86cSMartina Krasteva 	{0x38ab, 0xff},
1699214e86cSMartina Krasteva 	{0x55d4, 0x00},
1709214e86cSMartina Krasteva 	{0x55d5, 0x00},
1719214e86cSMartina Krasteva 	{0x55d6, 0x07},
1729214e86cSMartina Krasteva 	{0x55d7, 0xff},
1739214e86cSMartina Krasteva 	{0x55e8, 0x07},
1749214e86cSMartina Krasteva 	{0x55e9, 0xff},
1759214e86cSMartina Krasteva 	{0x55ea, 0x00},
1769214e86cSMartina Krasteva 	{0x55eb, 0x00},
1779214e86cSMartina Krasteva 	{0x575c, 0x07},
1789214e86cSMartina Krasteva 	{0x575d, 0xff},
1799214e86cSMartina Krasteva 	{0x575e, 0x00},
1809214e86cSMartina Krasteva 	{0x575f, 0x00},
1819214e86cSMartina Krasteva 	{0x5764, 0x00},
1829214e86cSMartina Krasteva 	{0x5765, 0x00},
1839214e86cSMartina Krasteva 	{0x5766, 0x07},
1849214e86cSMartina Krasteva 	{0x5767, 0xff},
1859214e86cSMartina Krasteva 	{0x5974, 0x04},
1869214e86cSMartina Krasteva 	{0x5975, 0x01},
1879214e86cSMartina Krasteva 	{0x5f10, 0x09},
1889214e86cSMartina Krasteva 	{0x5f11, 0x92},
1899214e86cSMartina Krasteva 	{0x5f12, 0x32},
1909214e86cSMartina Krasteva 	{0x5f13, 0x72},
1919214e86cSMartina Krasteva 	{0x5f14, 0x16},
1929214e86cSMartina Krasteva 	{0x5f15, 0xba},
1939214e86cSMartina Krasteva 	{0x5f17, 0x13},
1949214e86cSMartina Krasteva 	{0x5f18, 0x24},
1959214e86cSMartina Krasteva 	{0x5f19, 0x60},
1969214e86cSMartina Krasteva 	{0x5f1a, 0xe3},
1979214e86cSMartina Krasteva 	{0x5f1b, 0xad},
1989214e86cSMartina Krasteva 	{0x5f1c, 0x74},
1999214e86cSMartina Krasteva 	{0x5f2d, 0x25},
2009214e86cSMartina Krasteva 	{0x5f5c, 0xd0},
2019214e86cSMartina Krasteva 	{0x6a22, 0x00},
2029214e86cSMartina Krasteva 	{0x6a23, 0x1d},
2039214e86cSMartina Krasteva 	{0x7ba8, 0x00},
2049214e86cSMartina Krasteva 	{0x7ba9, 0x00},
2059214e86cSMartina Krasteva 	{0x886b, 0x00},
2069214e86cSMartina Krasteva 	{0x9002, 0x0a},
2079214e86cSMartina Krasteva 	{0x9004, 0x1a},
2089214e86cSMartina Krasteva 	{0x9214, 0x93},
2099214e86cSMartina Krasteva 	{0x9215, 0x69},
2109214e86cSMartina Krasteva 	{0x9216, 0x93},
2119214e86cSMartina Krasteva 	{0x9217, 0x6b},
2129214e86cSMartina Krasteva 	{0x9218, 0x93},
2139214e86cSMartina Krasteva 	{0x9219, 0x6d},
2149214e86cSMartina Krasteva 	{0x921a, 0x57},
2159214e86cSMartina Krasteva 	{0x921b, 0x58},
2169214e86cSMartina Krasteva 	{0x921c, 0x57},
2179214e86cSMartina Krasteva 	{0x921d, 0x59},
2189214e86cSMartina Krasteva 	{0x921e, 0x57},
2199214e86cSMartina Krasteva 	{0x921f, 0x5a},
2209214e86cSMartina Krasteva 	{0x9220, 0x57},
2219214e86cSMartina Krasteva 	{0x9221, 0x5b},
2229214e86cSMartina Krasteva 	{0x9222, 0x93},
2239214e86cSMartina Krasteva 	{0x9223, 0x02},
2249214e86cSMartina Krasteva 	{0x9224, 0x93},
2259214e86cSMartina Krasteva 	{0x9225, 0x03},
2269214e86cSMartina Krasteva 	{0x9226, 0x93},
2279214e86cSMartina Krasteva 	{0x9227, 0x04},
2289214e86cSMartina Krasteva 	{0x9228, 0x93},
2299214e86cSMartina Krasteva 	{0x9229, 0x05},
2309214e86cSMartina Krasteva 	{0x922a, 0x98},
2319214e86cSMartina Krasteva 	{0x922b, 0x21},
2329214e86cSMartina Krasteva 	{0x922c, 0xb2},
2339214e86cSMartina Krasteva 	{0x922d, 0xdb},
2349214e86cSMartina Krasteva 	{0x922e, 0xb2},
2359214e86cSMartina Krasteva 	{0x922f, 0xdc},
2369214e86cSMartina Krasteva 	{0x9230, 0xb2},
2379214e86cSMartina Krasteva 	{0x9231, 0xdd},
2389214e86cSMartina Krasteva 	{0x9232, 0xe2},
2399214e86cSMartina Krasteva 	{0x9233, 0xe1},
2409214e86cSMartina Krasteva 	{0x9234, 0xb2},
2419214e86cSMartina Krasteva 	{0x9235, 0xe2},
2429214e86cSMartina Krasteva 	{0x9236, 0xb2},
2439214e86cSMartina Krasteva 	{0x9237, 0xe3},
2449214e86cSMartina Krasteva 	{0x9238, 0xb7},
2459214e86cSMartina Krasteva 	{0x9239, 0xb9},
2469214e86cSMartina Krasteva 	{0x923a, 0xb7},
2479214e86cSMartina Krasteva 	{0x923b, 0xbb},
2489214e86cSMartina Krasteva 	{0x923c, 0xb7},
2499214e86cSMartina Krasteva 	{0x923d, 0xbc},
2509214e86cSMartina Krasteva 	{0x923e, 0xb7},
2519214e86cSMartina Krasteva 	{0x923f, 0xc5},
2529214e86cSMartina Krasteva 	{0x9240, 0xb7},
2539214e86cSMartina Krasteva 	{0x9241, 0xc7},
2549214e86cSMartina Krasteva 	{0x9242, 0xb7},
2559214e86cSMartina Krasteva 	{0x9243, 0xc9},
2569214e86cSMartina Krasteva 	{0x9244, 0x98},
2579214e86cSMartina Krasteva 	{0x9245, 0x56},
2589214e86cSMartina Krasteva 	{0x9246, 0x98},
2599214e86cSMartina Krasteva 	{0x9247, 0x55},
2609214e86cSMartina Krasteva 	{0x9380, 0x00},
2619214e86cSMartina Krasteva 	{0x9381, 0x62},
2629214e86cSMartina Krasteva 	{0x9382, 0x00},
2639214e86cSMartina Krasteva 	{0x9383, 0x56},
2649214e86cSMartina Krasteva 	{0x9384, 0x00},
2659214e86cSMartina Krasteva 	{0x9385, 0x52},
2669214e86cSMartina Krasteva 	{0x9388, 0x00},
2679214e86cSMartina Krasteva 	{0x9389, 0x55},
2689214e86cSMartina Krasteva 	{0x938a, 0x00},
2699214e86cSMartina Krasteva 	{0x938b, 0x55},
2709214e86cSMartina Krasteva 	{0x938c, 0x00},
2719214e86cSMartina Krasteva 	{0x938d, 0x41},
2729214e86cSMartina Krasteva 	{0x5078, 0x01},
2739214e86cSMartina Krasteva 	{0x0112, 0x0a},
2749214e86cSMartina Krasteva 	{0x0113, 0x0a},
2759214e86cSMartina Krasteva 	{0x0114, 0x03},
2769214e86cSMartina Krasteva 	{0x0342, 0x11},
2779214e86cSMartina Krasteva 	{0x0343, 0xa0},
2789214e86cSMartina Krasteva 	{0x0340, 0x0d},
2799214e86cSMartina Krasteva 	{0x0341, 0xda},
2809214e86cSMartina Krasteva 	{0x3210, 0x00},
2819214e86cSMartina Krasteva 	{0x0344, 0x00},
2829214e86cSMartina Krasteva 	{0x0345, 0x00},
2839214e86cSMartina Krasteva 	{0x0346, 0x00},
2849214e86cSMartina Krasteva 	{0x0347, 0x00},
2859214e86cSMartina Krasteva 	{0x0348, 0x0f},
2869214e86cSMartina Krasteva 	{0x0349, 0xd7},
2879214e86cSMartina Krasteva 	{0x034a, 0x0b},
2889214e86cSMartina Krasteva 	{0x034b, 0xdf},
2899214e86cSMartina Krasteva 	{0x00e3, 0x00},
2909214e86cSMartina Krasteva 	{0x00e4, 0x00},
2919214e86cSMartina Krasteva 	{0x00e5, 0x01},
2929214e86cSMartina Krasteva 	{0x00fc, 0x0a},
2939214e86cSMartina Krasteva 	{0x00fd, 0x0a},
2949214e86cSMartina Krasteva 	{0x00fe, 0x0a},
2959214e86cSMartina Krasteva 	{0x00ff, 0x0a},
2969214e86cSMartina Krasteva 	{0xe013, 0x00},
2979214e86cSMartina Krasteva 	{0x0220, 0x00},
2989214e86cSMartina Krasteva 	{0x0221, 0x11},
2999214e86cSMartina Krasteva 	{0x0381, 0x01},
3009214e86cSMartina Krasteva 	{0x0383, 0x01},
3019214e86cSMartina Krasteva 	{0x0385, 0x01},
3029214e86cSMartina Krasteva 	{0x0387, 0x01},
3039214e86cSMartina Krasteva 	{0x0900, 0x00},
3049214e86cSMartina Krasteva 	{0x0901, 0x11},
3059214e86cSMartina Krasteva 	{0x0902, 0x00},
3069214e86cSMartina Krasteva 	{0x3140, 0x02},
3079214e86cSMartina Krasteva 	{0x3241, 0x11},
3089214e86cSMartina Krasteva 	{0x3250, 0x03},
3099214e86cSMartina Krasteva 	{0x3e10, 0x00},
3109214e86cSMartina Krasteva 	{0x3e11, 0x00},
3119214e86cSMartina Krasteva 	{0x3f0d, 0x00},
3129214e86cSMartina Krasteva 	{0x3f42, 0x00},
3139214e86cSMartina Krasteva 	{0x3f43, 0x00},
3149214e86cSMartina Krasteva 	{0x0401, 0x00},
3159214e86cSMartina Krasteva 	{0x0404, 0x00},
3169214e86cSMartina Krasteva 	{0x0405, 0x10},
3179214e86cSMartina Krasteva 	{0x0408, 0x00},
3189214e86cSMartina Krasteva 	{0x0409, 0x00},
3199214e86cSMartina Krasteva 	{0x040a, 0x00},
3209214e86cSMartina Krasteva 	{0x040b, 0x00},
3219214e86cSMartina Krasteva 	{0x040c, 0x0f},
3229214e86cSMartina Krasteva 	{0x040d, 0xd8},
3239214e86cSMartina Krasteva 	{0x040e, 0x0b},
3249214e86cSMartina Krasteva 	{0x040f, 0xe0},
3259214e86cSMartina Krasteva 	{0x034c, 0x0f},
3269214e86cSMartina Krasteva 	{0x034d, 0xd8},
3279214e86cSMartina Krasteva 	{0x034e, 0x0b},
3289214e86cSMartina Krasteva 	{0x034f, 0xe0},
3299214e86cSMartina Krasteva 	{0x0301, 0x05},
3309214e86cSMartina Krasteva 	{0x0303, 0x02},
3319214e86cSMartina Krasteva 	{0x0305, 0x04},
3329214e86cSMartina Krasteva 	{0x0306, 0x00},
3339214e86cSMartina Krasteva 	{0x0307, 0xc8},
3349214e86cSMartina Krasteva 	{0x0309, 0x0a},
3359214e86cSMartina Krasteva 	{0x030b, 0x01},
3369214e86cSMartina Krasteva 	{0x030d, 0x02},
3379214e86cSMartina Krasteva 	{0x030e, 0x01},
3389214e86cSMartina Krasteva 	{0x030f, 0x5e},
3399214e86cSMartina Krasteva 	{0x0310, 0x00},
3409214e86cSMartina Krasteva 	{0x0820, 0x12},
3419214e86cSMartina Krasteva 	{0x0821, 0xc0},
3429214e86cSMartina Krasteva 	{0x0822, 0x00},
3439214e86cSMartina Krasteva 	{0x0823, 0x00},
3449214e86cSMartina Krasteva 	{0x3e20, 0x01},
3459214e86cSMartina Krasteva 	{0x3e37, 0x00},
3469214e86cSMartina Krasteva 	{0x3f50, 0x00},
3479214e86cSMartina Krasteva 	{0x3f56, 0x00},
3489214e86cSMartina Krasteva 	{0x3f57, 0xe2},
3499214e86cSMartina Krasteva 	{0x3c0a, 0x5a},
3509214e86cSMartina Krasteva 	{0x3c0b, 0x55},
3519214e86cSMartina Krasteva 	{0x3c0c, 0x28},
3529214e86cSMartina Krasteva 	{0x3c0d, 0x07},
3539214e86cSMartina Krasteva 	{0x3c0e, 0xff},
3549214e86cSMartina Krasteva 	{0x3c0f, 0x00},
3559214e86cSMartina Krasteva 	{0x3c10, 0x00},
3569214e86cSMartina Krasteva 	{0x3c11, 0x02},
3579214e86cSMartina Krasteva 	{0x3c12, 0x00},
3589214e86cSMartina Krasteva 	{0x3c13, 0x03},
3599214e86cSMartina Krasteva 	{0x3c14, 0x00},
3609214e86cSMartina Krasteva 	{0x3c15, 0x00},
3619214e86cSMartina Krasteva 	{0x3c16, 0x0c},
3629214e86cSMartina Krasteva 	{0x3c17, 0x0c},
3639214e86cSMartina Krasteva 	{0x3c18, 0x0c},
3649214e86cSMartina Krasteva 	{0x3c19, 0x0a},
3659214e86cSMartina Krasteva 	{0x3c1a, 0x0a},
3669214e86cSMartina Krasteva 	{0x3c1b, 0x0a},
3679214e86cSMartina Krasteva 	{0x3c1c, 0x00},
3689214e86cSMartina Krasteva 	{0x3c1d, 0x00},
3699214e86cSMartina Krasteva 	{0x3c1e, 0x00},
3709214e86cSMartina Krasteva 	{0x3c1f, 0x00},
3719214e86cSMartina Krasteva 	{0x3c20, 0x00},
3729214e86cSMartina Krasteva 	{0x3c21, 0x00},
3739214e86cSMartina Krasteva 	{0x3c22, 0x3f},
3749214e86cSMartina Krasteva 	{0x3c23, 0x0a},
3759214e86cSMartina Krasteva 	{0x3e35, 0x01},
3769214e86cSMartina Krasteva 	{0x3f4a, 0x03},
3779214e86cSMartina Krasteva 	{0x3f4b, 0xbf},
3789214e86cSMartina Krasteva 	{0x3f26, 0x00},
3799214e86cSMartina Krasteva 	{0x0202, 0x0d},
3809214e86cSMartina Krasteva 	{0x0203, 0xc4},
3819214e86cSMartina Krasteva 	{0x0204, 0x00},
3829214e86cSMartina Krasteva 	{0x0205, 0x00},
3839214e86cSMartina Krasteva 	{0x020e, 0x01},
3849214e86cSMartina Krasteva 	{0x020f, 0x00},
3859214e86cSMartina Krasteva 	{0x0210, 0x01},
3869214e86cSMartina Krasteva 	{0x0211, 0x00},
3879214e86cSMartina Krasteva 	{0x0212, 0x01},
3889214e86cSMartina Krasteva 	{0x0213, 0x00},
3899214e86cSMartina Krasteva 	{0x0214, 0x01},
3909214e86cSMartina Krasteva 	{0x0215, 0x00},
3919214e86cSMartina Krasteva 	{0xbcf1, 0x00},
3929214e86cSMartina Krasteva };
3939214e86cSMartina Krasteva 
3949214e86cSMartina Krasteva /* Supported sensor mode configurations */
3959214e86cSMartina Krasteva static const struct imx412_mode supported_mode = {
3969214e86cSMartina Krasteva 	.width = 4056,
3979214e86cSMartina Krasteva 	.height = 3040,
3989214e86cSMartina Krasteva 	.hblank = 456,
3999214e86cSMartina Krasteva 	.vblank = 506,
4009214e86cSMartina Krasteva 	.vblank_min = 506,
4019214e86cSMartina Krasteva 	.vblank_max = 32420,
4029214e86cSMartina Krasteva 	.pclk = 480000000,
4039214e86cSMartina Krasteva 	.link_freq_idx = 0,
4049214e86cSMartina Krasteva 	.code = MEDIA_BUS_FMT_SRGGB10_1X10,
4059214e86cSMartina Krasteva 	.reg_list = {
4069214e86cSMartina Krasteva 		.num_of_regs = ARRAY_SIZE(mode_4056x3040_regs),
4079214e86cSMartina Krasteva 		.regs = mode_4056x3040_regs,
4089214e86cSMartina Krasteva 	},
4099214e86cSMartina Krasteva };
4109214e86cSMartina Krasteva 
4119214e86cSMartina Krasteva /**
4129214e86cSMartina Krasteva  * to_imx412() - imx412 V4L2 sub-device to imx412 device.
4139214e86cSMartina Krasteva  * @subdev: pointer to imx412 V4L2 sub-device
4149214e86cSMartina Krasteva  *
4159214e86cSMartina Krasteva  * Return: pointer to imx412 device
4169214e86cSMartina Krasteva  */
to_imx412(struct v4l2_subdev * subdev)4179214e86cSMartina Krasteva static inline struct imx412 *to_imx412(struct v4l2_subdev *subdev)
4189214e86cSMartina Krasteva {
4199214e86cSMartina Krasteva 	return container_of(subdev, struct imx412, sd);
4209214e86cSMartina Krasteva }
4219214e86cSMartina Krasteva 
4229214e86cSMartina Krasteva /**
4239214e86cSMartina Krasteva  * imx412_read_reg() - Read registers.
4249214e86cSMartina Krasteva  * @imx412: pointer to imx412 device
4259214e86cSMartina Krasteva  * @reg: register address
4269214e86cSMartina Krasteva  * @len: length of bytes to read. Max supported bytes is 4
4279214e86cSMartina Krasteva  * @val: pointer to register value to be filled.
4289214e86cSMartina Krasteva  *
4299214e86cSMartina Krasteva  * Return: 0 if successful, error code otherwise.
4309214e86cSMartina Krasteva  */
imx412_read_reg(struct imx412 * imx412,u16 reg,u32 len,u32 * val)4319214e86cSMartina Krasteva static int imx412_read_reg(struct imx412 *imx412, u16 reg, u32 len, u32 *val)
4329214e86cSMartina Krasteva {
4339214e86cSMartina Krasteva 	struct i2c_client *client = v4l2_get_subdevdata(&imx412->sd);
4349214e86cSMartina Krasteva 	struct i2c_msg msgs[2] = {0};
4359214e86cSMartina Krasteva 	u8 addr_buf[2] = {0};
4369214e86cSMartina Krasteva 	u8 data_buf[4] = {0};
4379214e86cSMartina Krasteva 	int ret;
4389214e86cSMartina Krasteva 
4399214e86cSMartina Krasteva 	if (WARN_ON(len > 4))
4409214e86cSMartina Krasteva 		return -EINVAL;
4419214e86cSMartina Krasteva 
4429214e86cSMartina Krasteva 	put_unaligned_be16(reg, addr_buf);
4439214e86cSMartina Krasteva 
4449214e86cSMartina Krasteva 	/* Write register address */
4459214e86cSMartina Krasteva 	msgs[0].addr = client->addr;
4469214e86cSMartina Krasteva 	msgs[0].flags = 0;
4479214e86cSMartina Krasteva 	msgs[0].len = ARRAY_SIZE(addr_buf);
4489214e86cSMartina Krasteva 	msgs[0].buf = addr_buf;
4499214e86cSMartina Krasteva 
4509214e86cSMartina Krasteva 	/* Read data from register */
4519214e86cSMartina Krasteva 	msgs[1].addr = client->addr;
4529214e86cSMartina Krasteva 	msgs[1].flags = I2C_M_RD;
4539214e86cSMartina Krasteva 	msgs[1].len = len;
4549214e86cSMartina Krasteva 	msgs[1].buf = &data_buf[4 - len];
4559214e86cSMartina Krasteva 
4569214e86cSMartina Krasteva 	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
4579214e86cSMartina Krasteva 	if (ret != ARRAY_SIZE(msgs))
4589214e86cSMartina Krasteva 		return -EIO;
4599214e86cSMartina Krasteva 
4609214e86cSMartina Krasteva 	*val = get_unaligned_be32(data_buf);
4619214e86cSMartina Krasteva 
4629214e86cSMartina Krasteva 	return 0;
4639214e86cSMartina Krasteva }
4649214e86cSMartina Krasteva 
4659214e86cSMartina Krasteva /**
4669214e86cSMartina Krasteva  * imx412_write_reg() - Write register
4679214e86cSMartina Krasteva  * @imx412: pointer to imx412 device
4689214e86cSMartina Krasteva  * @reg: register address
4699214e86cSMartina Krasteva  * @len: length of bytes. Max supported bytes is 4
4709214e86cSMartina Krasteva  * @val: register value
4719214e86cSMartina Krasteva  *
4729214e86cSMartina Krasteva  * Return: 0 if successful, error code otherwise.
4739214e86cSMartina Krasteva  */
imx412_write_reg(struct imx412 * imx412,u16 reg,u32 len,u32 val)4749214e86cSMartina Krasteva static int imx412_write_reg(struct imx412 *imx412, u16 reg, u32 len, u32 val)
4759214e86cSMartina Krasteva {
4769214e86cSMartina Krasteva 	struct i2c_client *client = v4l2_get_subdevdata(&imx412->sd);
4779214e86cSMartina Krasteva 	u8 buf[6] = {0};
4789214e86cSMartina Krasteva 
4799214e86cSMartina Krasteva 	if (WARN_ON(len > 4))
4809214e86cSMartina Krasteva 		return -EINVAL;
4819214e86cSMartina Krasteva 
4829214e86cSMartina Krasteva 	put_unaligned_be16(reg, buf);
4839214e86cSMartina Krasteva 	put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
4849214e86cSMartina Krasteva 	if (i2c_master_send(client, buf, len + 2) != len + 2)
4859214e86cSMartina Krasteva 		return -EIO;
4869214e86cSMartina Krasteva 
4879214e86cSMartina Krasteva 	return 0;
4889214e86cSMartina Krasteva }
4899214e86cSMartina Krasteva 
4909214e86cSMartina Krasteva /**
4919214e86cSMartina Krasteva  * imx412_write_regs() - Write a list of registers
4929214e86cSMartina Krasteva  * @imx412: pointer to imx412 device
4939214e86cSMartina Krasteva  * @regs: list of registers to be written
4949214e86cSMartina Krasteva  * @len: length of registers array
4959214e86cSMartina Krasteva  *
4969214e86cSMartina Krasteva  * Return: 0 if successful, error code otherwise.
4979214e86cSMartina Krasteva  */
imx412_write_regs(struct imx412 * imx412,const struct imx412_reg * regs,u32 len)4989214e86cSMartina Krasteva static int imx412_write_regs(struct imx412 *imx412,
4999214e86cSMartina Krasteva 			     const struct imx412_reg *regs, u32 len)
5009214e86cSMartina Krasteva {
5019214e86cSMartina Krasteva 	unsigned int i;
5029214e86cSMartina Krasteva 	int ret;
5039214e86cSMartina Krasteva 
5049214e86cSMartina Krasteva 	for (i = 0; i < len; i++) {
5059214e86cSMartina Krasteva 		ret = imx412_write_reg(imx412, regs[i].address, 1, regs[i].val);
5069214e86cSMartina Krasteva 		if (ret)
5079214e86cSMartina Krasteva 			return ret;
5089214e86cSMartina Krasteva 	}
5099214e86cSMartina Krasteva 
5109214e86cSMartina Krasteva 	return 0;
5119214e86cSMartina Krasteva }
5129214e86cSMartina Krasteva 
5139214e86cSMartina Krasteva /**
5149214e86cSMartina Krasteva  * imx412_update_controls() - Update control ranges based on streaming mode
5159214e86cSMartina Krasteva  * @imx412: pointer to imx412 device
5169214e86cSMartina Krasteva  * @mode: pointer to imx412_mode sensor mode
5179214e86cSMartina Krasteva  *
5189214e86cSMartina Krasteva  * Return: 0 if successful, error code otherwise.
5199214e86cSMartina Krasteva  */
imx412_update_controls(struct imx412 * imx412,const struct imx412_mode * mode)5209214e86cSMartina Krasteva static int imx412_update_controls(struct imx412 *imx412,
5219214e86cSMartina Krasteva 				  const struct imx412_mode *mode)
5229214e86cSMartina Krasteva {
5239214e86cSMartina Krasteva 	int ret;
5249214e86cSMartina Krasteva 
5259214e86cSMartina Krasteva 	ret = __v4l2_ctrl_s_ctrl(imx412->link_freq_ctrl, mode->link_freq_idx);
5269214e86cSMartina Krasteva 	if (ret)
5279214e86cSMartina Krasteva 		return ret;
5289214e86cSMartina Krasteva 
5299214e86cSMartina Krasteva 	ret = __v4l2_ctrl_s_ctrl(imx412->hblank_ctrl, mode->hblank);
5309214e86cSMartina Krasteva 	if (ret)
5319214e86cSMartina Krasteva 		return ret;
5329214e86cSMartina Krasteva 
5339214e86cSMartina Krasteva 	return __v4l2_ctrl_modify_range(imx412->vblank_ctrl, mode->vblank_min,
5349214e86cSMartina Krasteva 					mode->vblank_max, 1, mode->vblank);
5359214e86cSMartina Krasteva }
5369214e86cSMartina Krasteva 
5379214e86cSMartina Krasteva /**
5389214e86cSMartina Krasteva  * imx412_update_exp_gain() - Set updated exposure and gain
5399214e86cSMartina Krasteva  * @imx412: pointer to imx412 device
5409214e86cSMartina Krasteva  * @exposure: updated exposure value
5419214e86cSMartina Krasteva  * @gain: updated analog gain value
5429214e86cSMartina Krasteva  *
5439214e86cSMartina Krasteva  * Return: 0 if successful, error code otherwise.
5449214e86cSMartina Krasteva  */
imx412_update_exp_gain(struct imx412 * imx412,u32 exposure,u32 gain)5459214e86cSMartina Krasteva static int imx412_update_exp_gain(struct imx412 *imx412, u32 exposure, u32 gain)
5469214e86cSMartina Krasteva {
5479214e86cSMartina Krasteva 	u32 lpfr, shutter;
5489214e86cSMartina Krasteva 	int ret;
5499214e86cSMartina Krasteva 
5509214e86cSMartina Krasteva 	lpfr = imx412->vblank + imx412->cur_mode->height;
5519214e86cSMartina Krasteva 	shutter = lpfr - exposure;
5529214e86cSMartina Krasteva 
5539214e86cSMartina Krasteva 	dev_dbg(imx412->dev, "Set exp %u, analog gain %u, shutter %u, lpfr %u",
5549214e86cSMartina Krasteva 		exposure, gain, shutter, lpfr);
5559214e86cSMartina Krasteva 
5569214e86cSMartina Krasteva 	ret = imx412_write_reg(imx412, IMX412_REG_HOLD, 1, 1);
5579214e86cSMartina Krasteva 	if (ret)
5589214e86cSMartina Krasteva 		return ret;
5599214e86cSMartina Krasteva 
5609214e86cSMartina Krasteva 	ret = imx412_write_reg(imx412, IMX412_REG_LPFR, 2, lpfr);
5619214e86cSMartina Krasteva 	if (ret)
5629214e86cSMartina Krasteva 		goto error_release_group_hold;
5639214e86cSMartina Krasteva 
5649214e86cSMartina Krasteva 	ret = imx412_write_reg(imx412, IMX412_REG_EXPOSURE_CIT, 2, shutter);
5659214e86cSMartina Krasteva 	if (ret)
5669214e86cSMartina Krasteva 		goto error_release_group_hold;
5679214e86cSMartina Krasteva 
5689214e86cSMartina Krasteva 	ret = imx412_write_reg(imx412, IMX412_REG_AGAIN, 2, gain);
5699214e86cSMartina Krasteva 
5709214e86cSMartina Krasteva error_release_group_hold:
5719214e86cSMartina Krasteva 	imx412_write_reg(imx412, IMX412_REG_HOLD, 1, 0);
5729214e86cSMartina Krasteva 
5739214e86cSMartina Krasteva 	return ret;
5749214e86cSMartina Krasteva }
5759214e86cSMartina Krasteva 
5769214e86cSMartina Krasteva /**
5779214e86cSMartina Krasteva  * imx412_set_ctrl() - Set subdevice control
5789214e86cSMartina Krasteva  * @ctrl: pointer to v4l2_ctrl structure
5799214e86cSMartina Krasteva  *
5809214e86cSMartina Krasteva  * Supported controls:
5819214e86cSMartina Krasteva  * - V4L2_CID_VBLANK
5829214e86cSMartina Krasteva  * - cluster controls:
5839214e86cSMartina Krasteva  *   - V4L2_CID_ANALOGUE_GAIN
5849214e86cSMartina Krasteva  *   - V4L2_CID_EXPOSURE
5859214e86cSMartina Krasteva  *
5869214e86cSMartina Krasteva  * Return: 0 if successful, error code otherwise.
5879214e86cSMartina Krasteva  */
imx412_set_ctrl(struct v4l2_ctrl * ctrl)5889214e86cSMartina Krasteva static int imx412_set_ctrl(struct v4l2_ctrl *ctrl)
5899214e86cSMartina Krasteva {
5909214e86cSMartina Krasteva 	struct imx412 *imx412 =
5919214e86cSMartina Krasteva 		container_of(ctrl->handler, struct imx412, ctrl_handler);
5929214e86cSMartina Krasteva 	u32 analog_gain;
5939214e86cSMartina Krasteva 	u32 exposure;
5949214e86cSMartina Krasteva 	int ret;
5959214e86cSMartina Krasteva 
5969214e86cSMartina Krasteva 	switch (ctrl->id) {
5979214e86cSMartina Krasteva 	case V4L2_CID_VBLANK:
5989214e86cSMartina Krasteva 		imx412->vblank = imx412->vblank_ctrl->val;
5999214e86cSMartina Krasteva 
6009214e86cSMartina Krasteva 		dev_dbg(imx412->dev, "Received vblank %u, new lpfr %u",
6019214e86cSMartina Krasteva 			imx412->vblank,
6029214e86cSMartina Krasteva 			imx412->vblank + imx412->cur_mode->height);
6039214e86cSMartina Krasteva 
6049214e86cSMartina Krasteva 		ret = __v4l2_ctrl_modify_range(imx412->exp_ctrl,
6059214e86cSMartina Krasteva 					       IMX412_EXPOSURE_MIN,
6069214e86cSMartina Krasteva 					       imx412->vblank +
6079214e86cSMartina Krasteva 					       imx412->cur_mode->height -
6089214e86cSMartina Krasteva 					       IMX412_EXPOSURE_OFFSET,
6099214e86cSMartina Krasteva 					       1, IMX412_EXPOSURE_DEFAULT);
6109214e86cSMartina Krasteva 		break;
6119214e86cSMartina Krasteva 	case V4L2_CID_EXPOSURE:
6129214e86cSMartina Krasteva 		/* Set controls only if sensor is in power on state */
6139214e86cSMartina Krasteva 		if (!pm_runtime_get_if_in_use(imx412->dev))
6149214e86cSMartina Krasteva 			return 0;
6159214e86cSMartina Krasteva 
6169214e86cSMartina Krasteva 		exposure = ctrl->val;
6179214e86cSMartina Krasteva 		analog_gain = imx412->again_ctrl->val;
6189214e86cSMartina Krasteva 
6199214e86cSMartina Krasteva 		dev_dbg(imx412->dev, "Received exp %u, analog gain %u",
6209214e86cSMartina Krasteva 			exposure, analog_gain);
6219214e86cSMartina Krasteva 
6229214e86cSMartina Krasteva 		ret = imx412_update_exp_gain(imx412, exposure, analog_gain);
6239214e86cSMartina Krasteva 
6249214e86cSMartina Krasteva 		pm_runtime_put(imx412->dev);
6259214e86cSMartina Krasteva 
6269214e86cSMartina Krasteva 		break;
6279214e86cSMartina Krasteva 	default:
6289214e86cSMartina Krasteva 		dev_err(imx412->dev, "Invalid control %d", ctrl->id);
6299214e86cSMartina Krasteva 		ret = -EINVAL;
6309214e86cSMartina Krasteva 	}
6319214e86cSMartina Krasteva 
6329214e86cSMartina Krasteva 	return ret;
6339214e86cSMartina Krasteva }
6349214e86cSMartina Krasteva 
6359214e86cSMartina Krasteva /* V4l2 subdevice control ops*/
6369214e86cSMartina Krasteva static const struct v4l2_ctrl_ops imx412_ctrl_ops = {
6379214e86cSMartina Krasteva 	.s_ctrl = imx412_set_ctrl,
6389214e86cSMartina Krasteva };
6399214e86cSMartina Krasteva 
6409214e86cSMartina Krasteva /**
6419214e86cSMartina Krasteva  * imx412_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes
6429214e86cSMartina Krasteva  * @sd: pointer to imx412 V4L2 sub-device structure
6439214e86cSMartina Krasteva  * @sd_state: V4L2 sub-device configuration
6449214e86cSMartina Krasteva  * @code: V4L2 sub-device code enumeration need to be filled
6459214e86cSMartina Krasteva  *
6469214e86cSMartina Krasteva  * Return: 0 if successful, error code otherwise.
6479214e86cSMartina Krasteva  */
imx412_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)6489214e86cSMartina Krasteva static int imx412_enum_mbus_code(struct v4l2_subdev *sd,
6499214e86cSMartina Krasteva 				 struct v4l2_subdev_state *sd_state,
6509214e86cSMartina Krasteva 				 struct v4l2_subdev_mbus_code_enum *code)
6519214e86cSMartina Krasteva {
6529214e86cSMartina Krasteva 	if (code->index > 0)
6539214e86cSMartina Krasteva 		return -EINVAL;
6549214e86cSMartina Krasteva 
6559214e86cSMartina Krasteva 	code->code = supported_mode.code;
6569214e86cSMartina Krasteva 
6579214e86cSMartina Krasteva 	return 0;
6589214e86cSMartina Krasteva }
6599214e86cSMartina Krasteva 
6609214e86cSMartina Krasteva /**
6619214e86cSMartina Krasteva  * imx412_enum_frame_size() - Enumerate V4L2 sub-device frame sizes
6629214e86cSMartina Krasteva  * @sd: pointer to imx412 V4L2 sub-device structure
6639214e86cSMartina Krasteva  * @sd_state: V4L2 sub-device configuration
6649214e86cSMartina Krasteva  * @fsize: V4L2 sub-device size enumeration need to be filled
6659214e86cSMartina Krasteva  *
6669214e86cSMartina Krasteva  * Return: 0 if successful, error code otherwise.
6679214e86cSMartina Krasteva  */
imx412_enum_frame_size(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_frame_size_enum * fsize)6689214e86cSMartina Krasteva static int imx412_enum_frame_size(struct v4l2_subdev *sd,
6699214e86cSMartina Krasteva 				  struct v4l2_subdev_state *sd_state,
6709214e86cSMartina Krasteva 				  struct v4l2_subdev_frame_size_enum *fsize)
6719214e86cSMartina Krasteva {
6729214e86cSMartina Krasteva 	if (fsize->index > 0)
6739214e86cSMartina Krasteva 		return -EINVAL;
6749214e86cSMartina Krasteva 
6759214e86cSMartina Krasteva 	if (fsize->code != supported_mode.code)
6769214e86cSMartina Krasteva 		return -EINVAL;
6779214e86cSMartina Krasteva 
6789214e86cSMartina Krasteva 	fsize->min_width = supported_mode.width;
6799214e86cSMartina Krasteva 	fsize->max_width = fsize->min_width;
6809214e86cSMartina Krasteva 	fsize->min_height = supported_mode.height;
6819214e86cSMartina Krasteva 	fsize->max_height = fsize->min_height;
6829214e86cSMartina Krasteva 
6839214e86cSMartina Krasteva 	return 0;
6849214e86cSMartina Krasteva }
6859214e86cSMartina Krasteva 
6869214e86cSMartina Krasteva /**
6879214e86cSMartina Krasteva  * imx412_fill_pad_format() - Fill subdevice pad format
6889214e86cSMartina Krasteva  *                            from selected sensor mode
6899214e86cSMartina Krasteva  * @imx412: pointer to imx412 device
6909214e86cSMartina Krasteva  * @mode: pointer to imx412_mode sensor mode
6919214e86cSMartina Krasteva  * @fmt: V4L2 sub-device format need to be filled
6929214e86cSMartina Krasteva  */
imx412_fill_pad_format(struct imx412 * imx412,const struct imx412_mode * mode,struct v4l2_subdev_format * fmt)6939214e86cSMartina Krasteva static void imx412_fill_pad_format(struct imx412 *imx412,
6949214e86cSMartina Krasteva 				   const struct imx412_mode *mode,
6959214e86cSMartina Krasteva 				   struct v4l2_subdev_format *fmt)
6969214e86cSMartina Krasteva {
6979214e86cSMartina Krasteva 	fmt->format.width = mode->width;
6989214e86cSMartina Krasteva 	fmt->format.height = mode->height;
6999214e86cSMartina Krasteva 	fmt->format.code = mode->code;
7009214e86cSMartina Krasteva 	fmt->format.field = V4L2_FIELD_NONE;
7019214e86cSMartina Krasteva 	fmt->format.colorspace = V4L2_COLORSPACE_RAW;
7029214e86cSMartina Krasteva 	fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
7039214e86cSMartina Krasteva 	fmt->format.quantization = V4L2_QUANTIZATION_DEFAULT;
7049214e86cSMartina Krasteva 	fmt->format.xfer_func = V4L2_XFER_FUNC_NONE;
7059214e86cSMartina Krasteva }
7069214e86cSMartina Krasteva 
7079214e86cSMartina Krasteva /**
7089214e86cSMartina Krasteva  * imx412_get_pad_format() - Get subdevice pad format
7099214e86cSMartina Krasteva  * @sd: pointer to imx412 V4L2 sub-device structure
7109214e86cSMartina Krasteva  * @sd_state: V4L2 sub-device configuration
7119214e86cSMartina Krasteva  * @fmt: V4L2 sub-device format need to be set
7129214e86cSMartina Krasteva  *
7139214e86cSMartina Krasteva  * Return: 0 if successful, error code otherwise.
7149214e86cSMartina Krasteva  */
imx412_get_pad_format(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)7159214e86cSMartina Krasteva static int imx412_get_pad_format(struct v4l2_subdev *sd,
7169214e86cSMartina Krasteva 				 struct v4l2_subdev_state *sd_state,
7179214e86cSMartina Krasteva 				 struct v4l2_subdev_format *fmt)
7189214e86cSMartina Krasteva {
7199214e86cSMartina Krasteva 	struct imx412 *imx412 = to_imx412(sd);
7209214e86cSMartina Krasteva 
7219214e86cSMartina Krasteva 	mutex_lock(&imx412->mutex);
7229214e86cSMartina Krasteva 
7239214e86cSMartina Krasteva 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
7249214e86cSMartina Krasteva 		struct v4l2_mbus_framefmt *framefmt;
7259214e86cSMartina Krasteva 
7269214e86cSMartina Krasteva 		framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
7279214e86cSMartina Krasteva 		fmt->format = *framefmt;
7289214e86cSMartina Krasteva 	} else {
7299214e86cSMartina Krasteva 		imx412_fill_pad_format(imx412, imx412->cur_mode, fmt);
7309214e86cSMartina Krasteva 	}
7319214e86cSMartina Krasteva 
7329214e86cSMartina Krasteva 	mutex_unlock(&imx412->mutex);
7339214e86cSMartina Krasteva 
7349214e86cSMartina Krasteva 	return 0;
7359214e86cSMartina Krasteva }
7369214e86cSMartina Krasteva 
7379214e86cSMartina Krasteva /**
7389214e86cSMartina Krasteva  * imx412_set_pad_format() - Set subdevice pad format
7399214e86cSMartina Krasteva  * @sd: pointer to imx412 V4L2 sub-device structure
7409214e86cSMartina Krasteva  * @sd_state: V4L2 sub-device configuration
7419214e86cSMartina Krasteva  * @fmt: V4L2 sub-device format need to be set
7429214e86cSMartina Krasteva  *
7439214e86cSMartina Krasteva  * Return: 0 if successful, error code otherwise.
7449214e86cSMartina Krasteva  */
imx412_set_pad_format(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)7459214e86cSMartina Krasteva static int imx412_set_pad_format(struct v4l2_subdev *sd,
7469214e86cSMartina Krasteva 				 struct v4l2_subdev_state *sd_state,
7479214e86cSMartina Krasteva 				 struct v4l2_subdev_format *fmt)
7489214e86cSMartina Krasteva {
7499214e86cSMartina Krasteva 	struct imx412 *imx412 = to_imx412(sd);
7509214e86cSMartina Krasteva 	const struct imx412_mode *mode;
7519214e86cSMartina Krasteva 	int ret = 0;
7529214e86cSMartina Krasteva 
7539214e86cSMartina Krasteva 	mutex_lock(&imx412->mutex);
7549214e86cSMartina Krasteva 
7559214e86cSMartina Krasteva 	mode = &supported_mode;
7569214e86cSMartina Krasteva 	imx412_fill_pad_format(imx412, mode, fmt);
7579214e86cSMartina Krasteva 
7589214e86cSMartina Krasteva 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
7599214e86cSMartina Krasteva 		struct v4l2_mbus_framefmt *framefmt;
7609214e86cSMartina Krasteva 
7619214e86cSMartina Krasteva 		framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
7629214e86cSMartina Krasteva 		*framefmt = fmt->format;
7639214e86cSMartina Krasteva 	} else {
7649214e86cSMartina Krasteva 		ret = imx412_update_controls(imx412, mode);
7659214e86cSMartina Krasteva 		if (!ret)
7669214e86cSMartina Krasteva 			imx412->cur_mode = mode;
7679214e86cSMartina Krasteva 	}
7689214e86cSMartina Krasteva 
7699214e86cSMartina Krasteva 	mutex_unlock(&imx412->mutex);
7709214e86cSMartina Krasteva 
7719214e86cSMartina Krasteva 	return ret;
7729214e86cSMartina Krasteva }
7739214e86cSMartina Krasteva 
7749214e86cSMartina Krasteva /**
7759214e86cSMartina Krasteva  * imx412_init_pad_cfg() - Initialize sub-device pad configuration
7769214e86cSMartina Krasteva  * @sd: pointer to imx412 V4L2 sub-device structure
7779214e86cSMartina Krasteva  * @sd_state: V4L2 sub-device configuration
7789214e86cSMartina Krasteva  *
7799214e86cSMartina Krasteva  * Return: 0 if successful, error code otherwise.
7809214e86cSMartina Krasteva  */
imx412_init_pad_cfg(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state)7819214e86cSMartina Krasteva static int imx412_init_pad_cfg(struct v4l2_subdev *sd,
7829214e86cSMartina Krasteva 			       struct v4l2_subdev_state *sd_state)
7839214e86cSMartina Krasteva {
7849214e86cSMartina Krasteva 	struct imx412 *imx412 = to_imx412(sd);
7859214e86cSMartina Krasteva 	struct v4l2_subdev_format fmt = { 0 };
7869214e86cSMartina Krasteva 
7879214e86cSMartina Krasteva 	fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
7889214e86cSMartina Krasteva 	imx412_fill_pad_format(imx412, &supported_mode, &fmt);
7899214e86cSMartina Krasteva 
7909214e86cSMartina Krasteva 	return imx412_set_pad_format(sd, sd_state, &fmt);
7919214e86cSMartina Krasteva }
7929214e86cSMartina Krasteva 
7939214e86cSMartina Krasteva /**
7949214e86cSMartina Krasteva  * imx412_start_streaming() - Start sensor stream
7959214e86cSMartina Krasteva  * @imx412: pointer to imx412 device
7969214e86cSMartina Krasteva  *
7979214e86cSMartina Krasteva  * Return: 0 if successful, error code otherwise.
7989214e86cSMartina Krasteva  */
imx412_start_streaming(struct imx412 * imx412)7999214e86cSMartina Krasteva static int imx412_start_streaming(struct imx412 *imx412)
8009214e86cSMartina Krasteva {
8019214e86cSMartina Krasteva 	const struct imx412_reg_list *reg_list;
8029214e86cSMartina Krasteva 	int ret;
8039214e86cSMartina Krasteva 
8049214e86cSMartina Krasteva 	/* Write sensor mode registers */
8059214e86cSMartina Krasteva 	reg_list = &imx412->cur_mode->reg_list;
8069214e86cSMartina Krasteva 	ret = imx412_write_regs(imx412, reg_list->regs,
8079214e86cSMartina Krasteva 				reg_list->num_of_regs);
8089214e86cSMartina Krasteva 	if (ret) {
8099214e86cSMartina Krasteva 		dev_err(imx412->dev, "fail to write initial registers");
8109214e86cSMartina Krasteva 		return ret;
8119214e86cSMartina Krasteva 	}
8129214e86cSMartina Krasteva 
8139214e86cSMartina Krasteva 	/* Setup handler will write actual exposure and gain */
8149214e86cSMartina Krasteva 	ret =  __v4l2_ctrl_handler_setup(imx412->sd.ctrl_handler);
8159214e86cSMartina Krasteva 	if (ret) {
8169214e86cSMartina Krasteva 		dev_err(imx412->dev, "fail to setup handler");
8179214e86cSMartina Krasteva 		return ret;
8189214e86cSMartina Krasteva 	}
8199214e86cSMartina Krasteva 
8209214e86cSMartina Krasteva 	/* Delay is required before streaming*/
8219214e86cSMartina Krasteva 	usleep_range(7400, 8000);
8229214e86cSMartina Krasteva 
8239214e86cSMartina Krasteva 	/* Start streaming */
8249214e86cSMartina Krasteva 	ret = imx412_write_reg(imx412, IMX412_REG_MODE_SELECT,
8259214e86cSMartina Krasteva 			       1, IMX412_MODE_STREAMING);
8269214e86cSMartina Krasteva 	if (ret) {
8279214e86cSMartina Krasteva 		dev_err(imx412->dev, "fail to start streaming");
8289214e86cSMartina Krasteva 		return ret;
8299214e86cSMartina Krasteva 	}
8309214e86cSMartina Krasteva 
8319214e86cSMartina Krasteva 	return 0;
8329214e86cSMartina Krasteva }
8339214e86cSMartina Krasteva 
8349214e86cSMartina Krasteva /**
8359214e86cSMartina Krasteva  * imx412_stop_streaming() - Stop sensor stream
8369214e86cSMartina Krasteva  * @imx412: pointer to imx412 device
8379214e86cSMartina Krasteva  *
8389214e86cSMartina Krasteva  * Return: 0 if successful, error code otherwise.
8399214e86cSMartina Krasteva  */
imx412_stop_streaming(struct imx412 * imx412)8409214e86cSMartina Krasteva static int imx412_stop_streaming(struct imx412 *imx412)
8419214e86cSMartina Krasteva {
8429214e86cSMartina Krasteva 	return imx412_write_reg(imx412, IMX412_REG_MODE_SELECT,
8439214e86cSMartina Krasteva 				1, IMX412_MODE_STANDBY);
8449214e86cSMartina Krasteva }
8459214e86cSMartina Krasteva 
8469214e86cSMartina Krasteva /**
8479214e86cSMartina Krasteva  * imx412_set_stream() - Enable sensor streaming
8489214e86cSMartina Krasteva  * @sd: pointer to imx412 subdevice
8499214e86cSMartina Krasteva  * @enable: set to enable sensor streaming
8509214e86cSMartina Krasteva  *
8519214e86cSMartina Krasteva  * Return: 0 if successful, error code otherwise.
8529214e86cSMartina Krasteva  */
imx412_set_stream(struct v4l2_subdev * sd,int enable)8539214e86cSMartina Krasteva static int imx412_set_stream(struct v4l2_subdev *sd, int enable)
8549214e86cSMartina Krasteva {
8559214e86cSMartina Krasteva 	struct imx412 *imx412 = to_imx412(sd);
8569214e86cSMartina Krasteva 	int ret;
8579214e86cSMartina Krasteva 
8589214e86cSMartina Krasteva 	mutex_lock(&imx412->mutex);
8599214e86cSMartina Krasteva 
8609214e86cSMartina Krasteva 	if (imx412->streaming == enable) {
8619214e86cSMartina Krasteva 		mutex_unlock(&imx412->mutex);
8629214e86cSMartina Krasteva 		return 0;
8639214e86cSMartina Krasteva 	}
8649214e86cSMartina Krasteva 
8659214e86cSMartina Krasteva 	if (enable) {
8669214e86cSMartina Krasteva 		ret = pm_runtime_resume_and_get(imx412->dev);
8679214e86cSMartina Krasteva 		if (ret)
8689214e86cSMartina Krasteva 			goto error_unlock;
8699214e86cSMartina Krasteva 
8709214e86cSMartina Krasteva 		ret = imx412_start_streaming(imx412);
8719214e86cSMartina Krasteva 		if (ret)
8729214e86cSMartina Krasteva 			goto error_power_off;
8739214e86cSMartina Krasteva 	} else {
8749214e86cSMartina Krasteva 		imx412_stop_streaming(imx412);
8759214e86cSMartina Krasteva 		pm_runtime_put(imx412->dev);
8769214e86cSMartina Krasteva 	}
8779214e86cSMartina Krasteva 
8789214e86cSMartina Krasteva 	imx412->streaming = enable;
8799214e86cSMartina Krasteva 
8809214e86cSMartina Krasteva 	mutex_unlock(&imx412->mutex);
8819214e86cSMartina Krasteva 
8829214e86cSMartina Krasteva 	return 0;
8839214e86cSMartina Krasteva 
8849214e86cSMartina Krasteva error_power_off:
8859214e86cSMartina Krasteva 	pm_runtime_put(imx412->dev);
8869214e86cSMartina Krasteva error_unlock:
8879214e86cSMartina Krasteva 	mutex_unlock(&imx412->mutex);
8889214e86cSMartina Krasteva 
8899214e86cSMartina Krasteva 	return ret;
8909214e86cSMartina Krasteva }
8919214e86cSMartina Krasteva 
8929214e86cSMartina Krasteva /**
8939214e86cSMartina Krasteva  * imx412_detect() - Detect imx412 sensor
8949214e86cSMartina Krasteva  * @imx412: pointer to imx412 device
8959214e86cSMartina Krasteva  *
8969214e86cSMartina Krasteva  * Return: 0 if successful, -EIO if sensor id does not match
8979214e86cSMartina Krasteva  */
imx412_detect(struct imx412 * imx412)8989214e86cSMartina Krasteva static int imx412_detect(struct imx412 *imx412)
8999214e86cSMartina Krasteva {
9009214e86cSMartina Krasteva 	int ret;
9019214e86cSMartina Krasteva 	u32 val;
9029214e86cSMartina Krasteva 
9039214e86cSMartina Krasteva 	ret = imx412_read_reg(imx412, IMX412_REG_ID, 2, &val);
9049214e86cSMartina Krasteva 	if (ret)
9059214e86cSMartina Krasteva 		return ret;
9069214e86cSMartina Krasteva 
9079214e86cSMartina Krasteva 	if (val != IMX412_ID) {
9089214e86cSMartina Krasteva 		dev_err(imx412->dev, "chip id mismatch: %x!=%x",
9099214e86cSMartina Krasteva 			IMX412_ID, val);
9109214e86cSMartina Krasteva 		return -ENXIO;
9119214e86cSMartina Krasteva 	}
9129214e86cSMartina Krasteva 
9139214e86cSMartina Krasteva 	return 0;
9149214e86cSMartina Krasteva }
9159214e86cSMartina Krasteva 
9169214e86cSMartina Krasteva /**
9179214e86cSMartina Krasteva  * imx412_parse_hw_config() - Parse HW configuration and check if supported
9189214e86cSMartina Krasteva  * @imx412: pointer to imx412 device
9199214e86cSMartina Krasteva  *
9209214e86cSMartina Krasteva  * Return: 0 if successful, error code otherwise.
9219214e86cSMartina Krasteva  */
imx412_parse_hw_config(struct imx412 * imx412)9229214e86cSMartina Krasteva static int imx412_parse_hw_config(struct imx412 *imx412)
9239214e86cSMartina Krasteva {
9249214e86cSMartina Krasteva 	struct fwnode_handle *fwnode = dev_fwnode(imx412->dev);
9259214e86cSMartina Krasteva 	struct v4l2_fwnode_endpoint bus_cfg = {
9269214e86cSMartina Krasteva 		.bus_type = V4L2_MBUS_CSI2_DPHY
9279214e86cSMartina Krasteva 	};
9289214e86cSMartina Krasteva 	struct fwnode_handle *ep;
9299214e86cSMartina Krasteva 	unsigned long rate;
9309214e86cSMartina Krasteva 	unsigned int i;
9319214e86cSMartina Krasteva 	int ret;
9329214e86cSMartina Krasteva 
9339214e86cSMartina Krasteva 	if (!fwnode)
9349214e86cSMartina Krasteva 		return -ENXIO;
9359214e86cSMartina Krasteva 
9369214e86cSMartina Krasteva 	/* Request optional reset pin */
9379214e86cSMartina Krasteva 	imx412->reset_gpio = devm_gpiod_get_optional(imx412->dev, "reset",
9389214e86cSMartina Krasteva 						     GPIOD_OUT_LOW);
9399214e86cSMartina Krasteva 	if (IS_ERR(imx412->reset_gpio)) {
9409214e86cSMartina Krasteva 		dev_err(imx412->dev, "failed to get reset gpio %ld",
9419214e86cSMartina Krasteva 			PTR_ERR(imx412->reset_gpio));
9429214e86cSMartina Krasteva 		return PTR_ERR(imx412->reset_gpio);
9439214e86cSMartina Krasteva 	}
9449214e86cSMartina Krasteva 
9459214e86cSMartina Krasteva 	/* Get sensor input clock */
9469214e86cSMartina Krasteva 	imx412->inclk = devm_clk_get(imx412->dev, NULL);
9479214e86cSMartina Krasteva 	if (IS_ERR(imx412->inclk)) {
9489214e86cSMartina Krasteva 		dev_err(imx412->dev, "could not get inclk");
9499214e86cSMartina Krasteva 		return PTR_ERR(imx412->inclk);
9509214e86cSMartina Krasteva 	}
9519214e86cSMartina Krasteva 
9529214e86cSMartina Krasteva 	rate = clk_get_rate(imx412->inclk);
9539214e86cSMartina Krasteva 	if (rate != IMX412_INCLK_RATE) {
9549214e86cSMartina Krasteva 		dev_err(imx412->dev, "inclk frequency mismatch");
9559214e86cSMartina Krasteva 		return -EINVAL;
9569214e86cSMartina Krasteva 	}
9579214e86cSMartina Krasteva 
9583de9dc7fSBryan O'Donoghue 	/* Get optional DT defined regulators */
9593de9dc7fSBryan O'Donoghue 	for (i = 0; i < ARRAY_SIZE(imx412_supply_names); i++)
9603de9dc7fSBryan O'Donoghue 		imx412->supplies[i].supply = imx412_supply_names[i];
9613de9dc7fSBryan O'Donoghue 
9623de9dc7fSBryan O'Donoghue 	ret = devm_regulator_bulk_get(imx412->dev,
9633de9dc7fSBryan O'Donoghue 				      ARRAY_SIZE(imx412_supply_names),
9643de9dc7fSBryan O'Donoghue 				      imx412->supplies);
9653de9dc7fSBryan O'Donoghue 	if (ret)
9663de9dc7fSBryan O'Donoghue 		return ret;
9673de9dc7fSBryan O'Donoghue 
9689214e86cSMartina Krasteva 	ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
9699214e86cSMartina Krasteva 	if (!ep)
9709214e86cSMartina Krasteva 		return -ENXIO;
9719214e86cSMartina Krasteva 
9729214e86cSMartina Krasteva 	ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
9739214e86cSMartina Krasteva 	fwnode_handle_put(ep);
9749214e86cSMartina Krasteva 	if (ret)
9759214e86cSMartina Krasteva 		return ret;
9769214e86cSMartina Krasteva 
9779214e86cSMartina Krasteva 	if (bus_cfg.bus.mipi_csi2.num_data_lanes != IMX412_NUM_DATA_LANES) {
9789214e86cSMartina Krasteva 		dev_err(imx412->dev,
9799214e86cSMartina Krasteva 			"number of CSI2 data lanes %d is not supported",
9809214e86cSMartina Krasteva 			bus_cfg.bus.mipi_csi2.num_data_lanes);
9819214e86cSMartina Krasteva 		ret = -EINVAL;
9829214e86cSMartina Krasteva 		goto done_endpoint_free;
9839214e86cSMartina Krasteva 	}
9849214e86cSMartina Krasteva 
9859214e86cSMartina Krasteva 	if (!bus_cfg.nr_of_link_frequencies) {
9869214e86cSMartina Krasteva 		dev_err(imx412->dev, "no link frequencies defined");
9879214e86cSMartina Krasteva 		ret = -EINVAL;
9889214e86cSMartina Krasteva 		goto done_endpoint_free;
9899214e86cSMartina Krasteva 	}
9909214e86cSMartina Krasteva 
9919214e86cSMartina Krasteva 	for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++)
9929214e86cSMartina Krasteva 		if (bus_cfg.link_frequencies[i] == IMX412_LINK_FREQ)
9939214e86cSMartina Krasteva 			goto done_endpoint_free;
9949214e86cSMartina Krasteva 
9959214e86cSMartina Krasteva 	ret = -EINVAL;
9969214e86cSMartina Krasteva 
9979214e86cSMartina Krasteva done_endpoint_free:
9989214e86cSMartina Krasteva 	v4l2_fwnode_endpoint_free(&bus_cfg);
9999214e86cSMartina Krasteva 
10009214e86cSMartina Krasteva 	return ret;
10019214e86cSMartina Krasteva }
10029214e86cSMartina Krasteva 
10039214e86cSMartina Krasteva /* V4l2 subdevice ops */
10049214e86cSMartina Krasteva static const struct v4l2_subdev_video_ops imx412_video_ops = {
10059214e86cSMartina Krasteva 	.s_stream = imx412_set_stream,
10069214e86cSMartina Krasteva };
10079214e86cSMartina Krasteva 
10089214e86cSMartina Krasteva static const struct v4l2_subdev_pad_ops imx412_pad_ops = {
10099214e86cSMartina Krasteva 	.init_cfg = imx412_init_pad_cfg,
10109214e86cSMartina Krasteva 	.enum_mbus_code = imx412_enum_mbus_code,
10119214e86cSMartina Krasteva 	.enum_frame_size = imx412_enum_frame_size,
10129214e86cSMartina Krasteva 	.get_fmt = imx412_get_pad_format,
10139214e86cSMartina Krasteva 	.set_fmt = imx412_set_pad_format,
10149214e86cSMartina Krasteva };
10159214e86cSMartina Krasteva 
10169214e86cSMartina Krasteva static const struct v4l2_subdev_ops imx412_subdev_ops = {
10179214e86cSMartina Krasteva 	.video = &imx412_video_ops,
10189214e86cSMartina Krasteva 	.pad = &imx412_pad_ops,
10199214e86cSMartina Krasteva };
10209214e86cSMartina Krasteva 
10219214e86cSMartina Krasteva /**
10229214e86cSMartina Krasteva  * imx412_power_on() - Sensor power on sequence
10239214e86cSMartina Krasteva  * @dev: pointer to i2c device
10249214e86cSMartina Krasteva  *
10259214e86cSMartina Krasteva  * Return: 0 if successful, error code otherwise.
10269214e86cSMartina Krasteva  */
imx412_power_on(struct device * dev)10279214e86cSMartina Krasteva static int imx412_power_on(struct device *dev)
10289214e86cSMartina Krasteva {
10299214e86cSMartina Krasteva 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
10309214e86cSMartina Krasteva 	struct imx412 *imx412 = to_imx412(sd);
10319214e86cSMartina Krasteva 	int ret;
10329214e86cSMartina Krasteva 
10333de9dc7fSBryan O'Donoghue 	ret = regulator_bulk_enable(ARRAY_SIZE(imx412_supply_names),
10343de9dc7fSBryan O'Donoghue 				    imx412->supplies);
10353de9dc7fSBryan O'Donoghue 	if (ret < 0) {
10363de9dc7fSBryan O'Donoghue 		dev_err(dev, "failed to enable regulators\n");
10373de9dc7fSBryan O'Donoghue 		return ret;
10383de9dc7fSBryan O'Donoghue 	}
10393de9dc7fSBryan O'Donoghue 
1040bb25f071SBryan O'Donoghue 	gpiod_set_value_cansleep(imx412->reset_gpio, 0);
10419214e86cSMartina Krasteva 
10429214e86cSMartina Krasteva 	ret = clk_prepare_enable(imx412->inclk);
10439214e86cSMartina Krasteva 	if (ret) {
10449214e86cSMartina Krasteva 		dev_err(imx412->dev, "fail to enable inclk");
10459214e86cSMartina Krasteva 		goto error_reset;
10469214e86cSMartina Krasteva 	}
10479214e86cSMartina Krasteva 
10489214e86cSMartina Krasteva 	usleep_range(1000, 1200);
10499214e86cSMartina Krasteva 
10509214e86cSMartina Krasteva 	return 0;
10519214e86cSMartina Krasteva 
10529214e86cSMartina Krasteva error_reset:
1053bb25f071SBryan O'Donoghue 	gpiod_set_value_cansleep(imx412->reset_gpio, 1);
10543de9dc7fSBryan O'Donoghue 	regulator_bulk_disable(ARRAY_SIZE(imx412_supply_names),
10553de9dc7fSBryan O'Donoghue 			       imx412->supplies);
10569214e86cSMartina Krasteva 
10579214e86cSMartina Krasteva 	return ret;
10589214e86cSMartina Krasteva }
10599214e86cSMartina Krasteva 
10609214e86cSMartina Krasteva /**
10619214e86cSMartina Krasteva  * imx412_power_off() - Sensor power off sequence
10629214e86cSMartina Krasteva  * @dev: pointer to i2c device
10639214e86cSMartina Krasteva  *
10649214e86cSMartina Krasteva  * Return: 0 if successful, error code otherwise.
10659214e86cSMartina Krasteva  */
imx412_power_off(struct device * dev)10669214e86cSMartina Krasteva static int imx412_power_off(struct device *dev)
10679214e86cSMartina Krasteva {
10689214e86cSMartina Krasteva 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
10699214e86cSMartina Krasteva 	struct imx412 *imx412 = to_imx412(sd);
10709214e86cSMartina Krasteva 
10719214e86cSMartina Krasteva 	clk_disable_unprepare(imx412->inclk);
10729214e86cSMartina Krasteva 
10739a199694SBryan O'Donoghue 	gpiod_set_value_cansleep(imx412->reset_gpio, 1);
10749a199694SBryan O'Donoghue 
10753de9dc7fSBryan O'Donoghue 	regulator_bulk_disable(ARRAY_SIZE(imx412_supply_names),
10763de9dc7fSBryan O'Donoghue 			       imx412->supplies);
10773de9dc7fSBryan O'Donoghue 
10789214e86cSMartina Krasteva 	return 0;
10799214e86cSMartina Krasteva }
10809214e86cSMartina Krasteva 
10819214e86cSMartina Krasteva /**
10829214e86cSMartina Krasteva  * imx412_init_controls() - Initialize sensor subdevice controls
10839214e86cSMartina Krasteva  * @imx412: pointer to imx412 device
10849214e86cSMartina Krasteva  *
10859214e86cSMartina Krasteva  * Return: 0 if successful, error code otherwise.
10869214e86cSMartina Krasteva  */
imx412_init_controls(struct imx412 * imx412)10879214e86cSMartina Krasteva static int imx412_init_controls(struct imx412 *imx412)
10889214e86cSMartina Krasteva {
10899214e86cSMartina Krasteva 	struct v4l2_ctrl_handler *ctrl_hdlr = &imx412->ctrl_handler;
10909214e86cSMartina Krasteva 	const struct imx412_mode *mode = imx412->cur_mode;
10919214e86cSMartina Krasteva 	u32 lpfr;
10929214e86cSMartina Krasteva 	int ret;
10939214e86cSMartina Krasteva 
10949214e86cSMartina Krasteva 	ret = v4l2_ctrl_handler_init(ctrl_hdlr, 6);
10959214e86cSMartina Krasteva 	if (ret)
10969214e86cSMartina Krasteva 		return ret;
10979214e86cSMartina Krasteva 
10989214e86cSMartina Krasteva 	/* Serialize controls with sensor device */
10999214e86cSMartina Krasteva 	ctrl_hdlr->lock = &imx412->mutex;
11009214e86cSMartina Krasteva 
11019214e86cSMartina Krasteva 	/* Initialize exposure and gain */
11029214e86cSMartina Krasteva 	lpfr = mode->vblank + mode->height;
11039214e86cSMartina Krasteva 	imx412->exp_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
11049214e86cSMartina Krasteva 					     &imx412_ctrl_ops,
11059214e86cSMartina Krasteva 					     V4L2_CID_EXPOSURE,
11069214e86cSMartina Krasteva 					     IMX412_EXPOSURE_MIN,
11079214e86cSMartina Krasteva 					     lpfr - IMX412_EXPOSURE_OFFSET,
11089214e86cSMartina Krasteva 					     IMX412_EXPOSURE_STEP,
11099214e86cSMartina Krasteva 					     IMX412_EXPOSURE_DEFAULT);
11109214e86cSMartina Krasteva 
11119214e86cSMartina Krasteva 	imx412->again_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
11129214e86cSMartina Krasteva 					       &imx412_ctrl_ops,
11139214e86cSMartina Krasteva 					       V4L2_CID_ANALOGUE_GAIN,
11149214e86cSMartina Krasteva 					       IMX412_AGAIN_MIN,
11159214e86cSMartina Krasteva 					       IMX412_AGAIN_MAX,
11169214e86cSMartina Krasteva 					       IMX412_AGAIN_STEP,
11179214e86cSMartina Krasteva 					       IMX412_AGAIN_DEFAULT);
11189214e86cSMartina Krasteva 
11199214e86cSMartina Krasteva 	v4l2_ctrl_cluster(2, &imx412->exp_ctrl);
11209214e86cSMartina Krasteva 
11219214e86cSMartina Krasteva 	imx412->vblank_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
11229214e86cSMartina Krasteva 						&imx412_ctrl_ops,
11239214e86cSMartina Krasteva 						V4L2_CID_VBLANK,
11249214e86cSMartina Krasteva 						mode->vblank_min,
11259214e86cSMartina Krasteva 						mode->vblank_max,
11269214e86cSMartina Krasteva 						1, mode->vblank);
11279214e86cSMartina Krasteva 
11289214e86cSMartina Krasteva 	/* Read only controls */
11299214e86cSMartina Krasteva 	imx412->pclk_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
11309214e86cSMartina Krasteva 					      &imx412_ctrl_ops,
11319214e86cSMartina Krasteva 					      V4L2_CID_PIXEL_RATE,
11329214e86cSMartina Krasteva 					      mode->pclk, mode->pclk,
11339214e86cSMartina Krasteva 					      1, mode->pclk);
11349214e86cSMartina Krasteva 
11359214e86cSMartina Krasteva 	imx412->link_freq_ctrl = v4l2_ctrl_new_int_menu(ctrl_hdlr,
11369214e86cSMartina Krasteva 							&imx412_ctrl_ops,
11379214e86cSMartina Krasteva 							V4L2_CID_LINK_FREQ,
11389214e86cSMartina Krasteva 							ARRAY_SIZE(link_freq) -
11399214e86cSMartina Krasteva 							1,
11409214e86cSMartina Krasteva 							mode->link_freq_idx,
11419214e86cSMartina Krasteva 							link_freq);
11429214e86cSMartina Krasteva 	if (imx412->link_freq_ctrl)
11439214e86cSMartina Krasteva 		imx412->link_freq_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
11449214e86cSMartina Krasteva 
11459214e86cSMartina Krasteva 	imx412->hblank_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
11469214e86cSMartina Krasteva 						&imx412_ctrl_ops,
11479214e86cSMartina Krasteva 						V4L2_CID_HBLANK,
11489214e86cSMartina Krasteva 						IMX412_REG_MIN,
11499214e86cSMartina Krasteva 						IMX412_REG_MAX,
11509214e86cSMartina Krasteva 						1, mode->hblank);
11519214e86cSMartina Krasteva 	if (imx412->hblank_ctrl)
11529214e86cSMartina Krasteva 		imx412->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
11539214e86cSMartina Krasteva 
11549214e86cSMartina Krasteva 	if (ctrl_hdlr->error) {
11559214e86cSMartina Krasteva 		dev_err(imx412->dev, "control init failed: %d",
11569214e86cSMartina Krasteva 			ctrl_hdlr->error);
11579214e86cSMartina Krasteva 		v4l2_ctrl_handler_free(ctrl_hdlr);
11589214e86cSMartina Krasteva 		return ctrl_hdlr->error;
11599214e86cSMartina Krasteva 	}
11609214e86cSMartina Krasteva 
11619214e86cSMartina Krasteva 	imx412->sd.ctrl_handler = ctrl_hdlr;
11629214e86cSMartina Krasteva 
11639214e86cSMartina Krasteva 	return 0;
11649214e86cSMartina Krasteva }
11659214e86cSMartina Krasteva 
11669214e86cSMartina Krasteva /**
11679214e86cSMartina Krasteva  * imx412_probe() - I2C client device binding
11689214e86cSMartina Krasteva  * @client: pointer to i2c client device
11699214e86cSMartina Krasteva  *
11709214e86cSMartina Krasteva  * Return: 0 if successful, error code otherwise.
11719214e86cSMartina Krasteva  */
imx412_probe(struct i2c_client * client)11729214e86cSMartina Krasteva static int imx412_probe(struct i2c_client *client)
11739214e86cSMartina Krasteva {
11749214e86cSMartina Krasteva 	struct imx412 *imx412;
117563575dd3SBryan O'Donoghue 	const char *name;
11769214e86cSMartina Krasteva 	int ret;
11779214e86cSMartina Krasteva 
11789214e86cSMartina Krasteva 	imx412 = devm_kzalloc(&client->dev, sizeof(*imx412), GFP_KERNEL);
11799214e86cSMartina Krasteva 	if (!imx412)
11809214e86cSMartina Krasteva 		return -ENOMEM;
11819214e86cSMartina Krasteva 
11829214e86cSMartina Krasteva 	imx412->dev = &client->dev;
118363575dd3SBryan O'Donoghue 	name = device_get_match_data(&client->dev);
118463575dd3SBryan O'Donoghue 	if (!name)
118563575dd3SBryan O'Donoghue 		return -ENODEV;
11869214e86cSMartina Krasteva 
11879214e86cSMartina Krasteva 	/* Initialize subdev */
11889214e86cSMartina Krasteva 	v4l2_i2c_subdev_init(&imx412->sd, client, &imx412_subdev_ops);
11899214e86cSMartina Krasteva 
11909214e86cSMartina Krasteva 	ret = imx412_parse_hw_config(imx412);
11919214e86cSMartina Krasteva 	if (ret) {
11929214e86cSMartina Krasteva 		dev_err(imx412->dev, "HW configuration is not supported");
11939214e86cSMartina Krasteva 		return ret;
11949214e86cSMartina Krasteva 	}
11959214e86cSMartina Krasteva 
11969214e86cSMartina Krasteva 	mutex_init(&imx412->mutex);
11979214e86cSMartina Krasteva 
11989214e86cSMartina Krasteva 	ret = imx412_power_on(imx412->dev);
11999214e86cSMartina Krasteva 	if (ret) {
12009214e86cSMartina Krasteva 		dev_err(imx412->dev, "failed to power-on the sensor");
12019214e86cSMartina Krasteva 		goto error_mutex_destroy;
12029214e86cSMartina Krasteva 	}
12039214e86cSMartina Krasteva 
12049214e86cSMartina Krasteva 	/* Check module identity */
12059214e86cSMartina Krasteva 	ret = imx412_detect(imx412);
12069214e86cSMartina Krasteva 	if (ret) {
12079214e86cSMartina Krasteva 		dev_err(imx412->dev, "failed to find sensor: %d", ret);
12089214e86cSMartina Krasteva 		goto error_power_off;
12099214e86cSMartina Krasteva 	}
12109214e86cSMartina Krasteva 
12119214e86cSMartina Krasteva 	/* Set default mode to max resolution */
12129214e86cSMartina Krasteva 	imx412->cur_mode = &supported_mode;
12139214e86cSMartina Krasteva 	imx412->vblank = imx412->cur_mode->vblank;
12149214e86cSMartina Krasteva 
12159214e86cSMartina Krasteva 	ret = imx412_init_controls(imx412);
12169214e86cSMartina Krasteva 	if (ret) {
12179214e86cSMartina Krasteva 		dev_err(imx412->dev, "failed to init controls: %d", ret);
12189214e86cSMartina Krasteva 		goto error_power_off;
12199214e86cSMartina Krasteva 	}
12209214e86cSMartina Krasteva 
12219214e86cSMartina Krasteva 	/* Initialize subdev */
12229214e86cSMartina Krasteva 	imx412->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
12239214e86cSMartina Krasteva 	imx412->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
12249214e86cSMartina Krasteva 
122563575dd3SBryan O'Donoghue 	v4l2_i2c_subdev_set_name(&imx412->sd, client, name, NULL);
122663575dd3SBryan O'Donoghue 
12279214e86cSMartina Krasteva 	/* Initialize source pad */
12289214e86cSMartina Krasteva 	imx412->pad.flags = MEDIA_PAD_FL_SOURCE;
12299214e86cSMartina Krasteva 	ret = media_entity_pads_init(&imx412->sd.entity, 1, &imx412->pad);
12309214e86cSMartina Krasteva 	if (ret) {
12319214e86cSMartina Krasteva 		dev_err(imx412->dev, "failed to init entity pads: %d", ret);
12329214e86cSMartina Krasteva 		goto error_handler_free;
12339214e86cSMartina Krasteva 	}
12349214e86cSMartina Krasteva 
12359214e86cSMartina Krasteva 	ret = v4l2_async_register_subdev_sensor(&imx412->sd);
12369214e86cSMartina Krasteva 	if (ret < 0) {
12379214e86cSMartina Krasteva 		dev_err(imx412->dev,
12389214e86cSMartina Krasteva 			"failed to register async subdev: %d", ret);
12399214e86cSMartina Krasteva 		goto error_media_entity;
12409214e86cSMartina Krasteva 	}
12419214e86cSMartina Krasteva 
12429214e86cSMartina Krasteva 	pm_runtime_set_active(imx412->dev);
12439214e86cSMartina Krasteva 	pm_runtime_enable(imx412->dev);
12449214e86cSMartina Krasteva 	pm_runtime_idle(imx412->dev);
12459214e86cSMartina Krasteva 
12469214e86cSMartina Krasteva 	return 0;
12479214e86cSMartina Krasteva 
12489214e86cSMartina Krasteva error_media_entity:
12499214e86cSMartina Krasteva 	media_entity_cleanup(&imx412->sd.entity);
12509214e86cSMartina Krasteva error_handler_free:
12519214e86cSMartina Krasteva 	v4l2_ctrl_handler_free(imx412->sd.ctrl_handler);
12529214e86cSMartina Krasteva error_power_off:
12539214e86cSMartina Krasteva 	imx412_power_off(imx412->dev);
12549214e86cSMartina Krasteva error_mutex_destroy:
12559214e86cSMartina Krasteva 	mutex_destroy(&imx412->mutex);
12569214e86cSMartina Krasteva 
12579214e86cSMartina Krasteva 	return ret;
12589214e86cSMartina Krasteva }
12599214e86cSMartina Krasteva 
12609214e86cSMartina Krasteva /**
12619214e86cSMartina Krasteva  * imx412_remove() - I2C client device unbinding
12629214e86cSMartina Krasteva  * @client: pointer to I2C client device
12639214e86cSMartina Krasteva  *
12649214e86cSMartina Krasteva  * Return: 0 if successful, error code otherwise.
12659214e86cSMartina Krasteva  */
imx412_remove(struct i2c_client * client)1266ed5c2f5fSUwe Kleine-König static void imx412_remove(struct i2c_client *client)
12679214e86cSMartina Krasteva {
12689214e86cSMartina Krasteva 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
12699214e86cSMartina Krasteva 	struct imx412 *imx412 = to_imx412(sd);
12709214e86cSMartina Krasteva 
12719214e86cSMartina Krasteva 	v4l2_async_unregister_subdev(sd);
12729214e86cSMartina Krasteva 	media_entity_cleanup(&sd->entity);
12739214e86cSMartina Krasteva 	v4l2_ctrl_handler_free(sd->ctrl_handler);
12749214e86cSMartina Krasteva 
12759214e86cSMartina Krasteva 	pm_runtime_disable(&client->dev);
12769214e86cSMartina Krasteva 	if (!pm_runtime_status_suspended(&client->dev))
12779214e86cSMartina Krasteva 		imx412_power_off(&client->dev);
12789214e86cSMartina Krasteva 	pm_runtime_set_suspended(&client->dev);
12799214e86cSMartina Krasteva 
12809214e86cSMartina Krasteva 	mutex_destroy(&imx412->mutex);
12819214e86cSMartina Krasteva }
12829214e86cSMartina Krasteva 
12839214e86cSMartina Krasteva static const struct dev_pm_ops imx412_pm_ops = {
12849214e86cSMartina Krasteva 	SET_RUNTIME_PM_OPS(imx412_power_off, imx412_power_on, NULL)
12859214e86cSMartina Krasteva };
12869214e86cSMartina Krasteva 
12879214e86cSMartina Krasteva static const struct of_device_id imx412_of_match[] = {
128863575dd3SBryan O'Donoghue 	{ .compatible = "sony,imx412", .data = "imx412" },
128912516632SBryan O'Donoghue 	{ .compatible = "sony,imx577", .data = "imx577" },
12909214e86cSMartina Krasteva 	{ }
12919214e86cSMartina Krasteva };
12929214e86cSMartina Krasteva 
12939214e86cSMartina Krasteva MODULE_DEVICE_TABLE(of, imx412_of_match);
12949214e86cSMartina Krasteva 
12959214e86cSMartina Krasteva static struct i2c_driver imx412_driver = {
1296*aaeb31c0SUwe Kleine-König 	.probe = imx412_probe,
12979214e86cSMartina Krasteva 	.remove = imx412_remove,
12989214e86cSMartina Krasteva 	.driver = {
12999214e86cSMartina Krasteva 		.name = "imx412",
13009214e86cSMartina Krasteva 		.pm = &imx412_pm_ops,
13019214e86cSMartina Krasteva 		.of_match_table = imx412_of_match,
13029214e86cSMartina Krasteva 	},
13039214e86cSMartina Krasteva };
13049214e86cSMartina Krasteva 
13059214e86cSMartina Krasteva module_i2c_driver(imx412_driver);
13069214e86cSMartina Krasteva 
13079214e86cSMartina Krasteva MODULE_DESCRIPTION("Sony imx412 sensor driver");
13089214e86cSMartina Krasteva MODULE_LICENSE("GPL");
1309