xref: /openbmc/linux/drivers/media/i2c/imx290.c (revision 0b274ef2)
1828dbc29SManivannan Sadhasivam // SPDX-License-Identifier: GPL-2.0
2828dbc29SManivannan Sadhasivam /*
3828dbc29SManivannan Sadhasivam  * Sony IMX290 CMOS Image Sensor Driver
4828dbc29SManivannan Sadhasivam  *
5828dbc29SManivannan Sadhasivam  * Copyright (C) 2019 FRAMOS GmbH.
6828dbc29SManivannan Sadhasivam  *
7828dbc29SManivannan Sadhasivam  * Copyright (C) 2019 Linaro Ltd.
8828dbc29SManivannan Sadhasivam  * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
9828dbc29SManivannan Sadhasivam  */
10828dbc29SManivannan Sadhasivam 
11828dbc29SManivannan Sadhasivam #include <linux/clk.h>
12828dbc29SManivannan Sadhasivam #include <linux/delay.h>
13828dbc29SManivannan Sadhasivam #include <linux/gpio/consumer.h>
14828dbc29SManivannan Sadhasivam #include <linux/i2c.h>
15828dbc29SManivannan Sadhasivam #include <linux/module.h>
16828dbc29SManivannan Sadhasivam #include <linux/pm_runtime.h>
17828dbc29SManivannan Sadhasivam #include <linux/regmap.h>
18828dbc29SManivannan Sadhasivam #include <linux/regulator/consumer.h>
19828dbc29SManivannan Sadhasivam #include <media/media-entity.h>
20828dbc29SManivannan Sadhasivam #include <media/v4l2-ctrls.h>
21828dbc29SManivannan Sadhasivam #include <media/v4l2-device.h>
22828dbc29SManivannan Sadhasivam #include <media/v4l2-fwnode.h>
23828dbc29SManivannan Sadhasivam #include <media/v4l2-subdev.h>
24828dbc29SManivannan Sadhasivam 
25e70abe88SLaurent Pinchart #define IMX290_REG_SIZE_SHIFT				16
26e70abe88SLaurent Pinchart #define IMX290_REG_ADDR_MASK				0xffff
27e70abe88SLaurent Pinchart #define IMX290_REG_8BIT(n)				((1U << IMX290_REG_SIZE_SHIFT) | (n))
28e70abe88SLaurent Pinchart #define IMX290_REG_16BIT(n)				((2U << IMX290_REG_SIZE_SHIFT) | (n))
29e70abe88SLaurent Pinchart #define IMX290_REG_24BIT(n)				((3U << IMX290_REG_SIZE_SHIFT) | (n))
30e70abe88SLaurent Pinchart 
31e70abe88SLaurent Pinchart #define IMX290_STANDBY					IMX290_REG_8BIT(0x3000)
32e70abe88SLaurent Pinchart #define IMX290_REGHOLD					IMX290_REG_8BIT(0x3001)
33e70abe88SLaurent Pinchart #define IMX290_XMSTA					IMX290_REG_8BIT(0x3002)
3479d99ae8SLaurent Pinchart #define IMX290_ADBIT					IMX290_REG_8BIT(0x3005)
3579d99ae8SLaurent Pinchart #define IMX290_ADBIT_10BIT				(0 << 0)
3679d99ae8SLaurent Pinchart #define IMX290_ADBIT_12BIT				(1 << 0)
3779d99ae8SLaurent Pinchart #define IMX290_CTRL_07					IMX290_REG_8BIT(0x3007)
3879d99ae8SLaurent Pinchart #define IMX290_VREVERSE					BIT(0)
3979d99ae8SLaurent Pinchart #define IMX290_HREVERSE					BIT(1)
4079d99ae8SLaurent Pinchart #define IMX290_WINMODE_1080P				(0 << 4)
4179d99ae8SLaurent Pinchart #define IMX290_WINMODE_720P				(1 << 4)
4279d99ae8SLaurent Pinchart #define IMX290_WINMODE_CROP				(4 << 4)
43e70abe88SLaurent Pinchart #define IMX290_FR_FDG_SEL				IMX290_REG_8BIT(0x3009)
44454a86f3SLaurent Pinchart #define IMX290_BLKLEVEL					IMX290_REG_16BIT(0x300a)
45e70abe88SLaurent Pinchart #define IMX290_GAIN					IMX290_REG_8BIT(0x3014)
4679d99ae8SLaurent Pinchart #define IMX290_VMAX					IMX290_REG_24BIT(0x3018)
47454a86f3SLaurent Pinchart #define IMX290_HMAX					IMX290_REG_16BIT(0x301c)
4879d99ae8SLaurent Pinchart #define IMX290_SHS1					IMX290_REG_24BIT(0x3020)
4979d99ae8SLaurent Pinchart #define IMX290_WINWV_OB					IMX290_REG_8BIT(0x303a)
5079d99ae8SLaurent Pinchart #define IMX290_WINPV					IMX290_REG_16BIT(0x303c)
5179d99ae8SLaurent Pinchart #define IMX290_WINWV					IMX290_REG_16BIT(0x303e)
5279d99ae8SLaurent Pinchart #define IMX290_WINPH					IMX290_REG_16BIT(0x3040)
5379d99ae8SLaurent Pinchart #define IMX290_WINWH					IMX290_REG_16BIT(0x3042)
5479d99ae8SLaurent Pinchart #define IMX290_OUT_CTRL					IMX290_REG_8BIT(0x3046)
5579d99ae8SLaurent Pinchart #define IMX290_ODBIT_10BIT				(0 << 0)
5679d99ae8SLaurent Pinchart #define IMX290_ODBIT_12BIT				(1 << 0)
5779d99ae8SLaurent Pinchart #define IMX290_OPORTSEL_PARALLEL			(0x0 << 4)
5879d99ae8SLaurent Pinchart #define IMX290_OPORTSEL_LVDS_2CH			(0xd << 4)
5979d99ae8SLaurent Pinchart #define IMX290_OPORTSEL_LVDS_4CH			(0xe << 4)
6079d99ae8SLaurent Pinchart #define IMX290_OPORTSEL_LVDS_8CH			(0xf << 4)
6179d99ae8SLaurent Pinchart #define IMX290_XSOUTSEL					IMX290_REG_8BIT(0x304b)
6279d99ae8SLaurent Pinchart #define IMX290_XSOUTSEL_XVSOUTSEL_HIGH			(0 << 0)
6379d99ae8SLaurent Pinchart #define IMX290_XSOUTSEL_XVSOUTSEL_VSYNC			(2 << 0)
6479d99ae8SLaurent Pinchart #define IMX290_XSOUTSEL_XHSOUTSEL_HIGH			(0 << 2)
6579d99ae8SLaurent Pinchart #define IMX290_XSOUTSEL_XHSOUTSEL_HSYNC			(2 << 2)
6679d99ae8SLaurent Pinchart #define IMX290_INCKSEL1					IMX290_REG_8BIT(0x305c)
6779d99ae8SLaurent Pinchart #define IMX290_INCKSEL2					IMX290_REG_8BIT(0x305d)
6879d99ae8SLaurent Pinchart #define IMX290_INCKSEL3					IMX290_REG_8BIT(0x305e)
6979d99ae8SLaurent Pinchart #define IMX290_INCKSEL4					IMX290_REG_8BIT(0x305f)
70e70abe88SLaurent Pinchart #define IMX290_PGCTRL					IMX290_REG_8BIT(0x308c)
7179d99ae8SLaurent Pinchart #define IMX290_ADBIT1					IMX290_REG_8BIT(0x3129)
7279d99ae8SLaurent Pinchart #define IMX290_ADBIT1_10BIT				0x1d
7379d99ae8SLaurent Pinchart #define IMX290_ADBIT1_12BIT				0x00
7479d99ae8SLaurent Pinchart #define IMX290_INCKSEL5					IMX290_REG_8BIT(0x315e)
7579d99ae8SLaurent Pinchart #define IMX290_INCKSEL6					IMX290_REG_8BIT(0x3164)
7679d99ae8SLaurent Pinchart #define IMX290_ADBIT2					IMX290_REG_8BIT(0x317c)
7779d99ae8SLaurent Pinchart #define IMX290_ADBIT2_10BIT				0x12
7879d99ae8SLaurent Pinchart #define IMX290_ADBIT2_12BIT				0x00
79454a86f3SLaurent Pinchart #define IMX290_CHIP_ID					IMX290_REG_16BIT(0x319a)
8079d99ae8SLaurent Pinchart #define IMX290_ADBIT3					IMX290_REG_8BIT(0x31ec)
8179d99ae8SLaurent Pinchart #define IMX290_ADBIT3_10BIT				0x37
8279d99ae8SLaurent Pinchart #define IMX290_ADBIT3_12BIT				0x0e
8379d99ae8SLaurent Pinchart #define IMX290_REPETITION				IMX290_REG_8BIT(0x3405)
84e70abe88SLaurent Pinchart #define IMX290_PHY_LANE_NUM				IMX290_REG_8BIT(0x3407)
8579d99ae8SLaurent Pinchart #define IMX290_OPB_SIZE_V				IMX290_REG_8BIT(0x3414)
8679d99ae8SLaurent Pinchart #define IMX290_Y_OUT_SIZE				IMX290_REG_16BIT(0x3418)
8779d99ae8SLaurent Pinchart #define IMX290_CSI_DT_FMT				IMX290_REG_16BIT(0x3441)
8879d99ae8SLaurent Pinchart #define IMX290_CSI_DT_FMT_RAW10				0x0a0a
8979d99ae8SLaurent Pinchart #define IMX290_CSI_DT_FMT_RAW12				0x0c0c
90e70abe88SLaurent Pinchart #define IMX290_CSI_LANE_MODE				IMX290_REG_8BIT(0x3443)
9179d99ae8SLaurent Pinchart #define IMX290_EXTCK_FREQ				IMX290_REG_16BIT(0x3444)
9279d99ae8SLaurent Pinchart #define IMX290_TCLKPOST					IMX290_REG_16BIT(0x3446)
9379d99ae8SLaurent Pinchart #define IMX290_THSZERO					IMX290_REG_16BIT(0x3448)
9479d99ae8SLaurent Pinchart #define IMX290_THSPREPARE				IMX290_REG_16BIT(0x344a)
9579d99ae8SLaurent Pinchart #define IMX290_TCLKTRAIL				IMX290_REG_16BIT(0x344c)
9679d99ae8SLaurent Pinchart #define IMX290_THSTRAIL					IMX290_REG_16BIT(0x344e)
9779d99ae8SLaurent Pinchart #define IMX290_TCLKZERO					IMX290_REG_16BIT(0x3450)
9879d99ae8SLaurent Pinchart #define IMX290_TCLKPREPARE				IMX290_REG_16BIT(0x3452)
9979d99ae8SLaurent Pinchart #define IMX290_TLPX					IMX290_REG_16BIT(0x3454)
10079d99ae8SLaurent Pinchart #define IMX290_X_OUT_SIZE				IMX290_REG_16BIT(0x3472)
101828dbc29SManivannan Sadhasivam 
102a58df1f9SManivannan Sadhasivam #define IMX290_PGCTRL_REGEN				BIT(0)
103a58df1f9SManivannan Sadhasivam #define IMX290_PGCTRL_THRU				BIT(1)
104a58df1f9SManivannan Sadhasivam #define IMX290_PGCTRL_MODE(n)				((n) << 4)
105a58df1f9SManivannan Sadhasivam 
106827c7e69SLaurent Pinchart #define IMX290_VMAX_DEFAULT				1125
107827c7e69SLaurent Pinchart 
108828dbc29SManivannan Sadhasivam static const char * const imx290_supply_name[] = {
109828dbc29SManivannan Sadhasivam 	"vdda",
110828dbc29SManivannan Sadhasivam 	"vddd",
111828dbc29SManivannan Sadhasivam 	"vdddo",
112828dbc29SManivannan Sadhasivam };
113828dbc29SManivannan Sadhasivam 
114828dbc29SManivannan Sadhasivam #define IMX290_NUM_SUPPLIES ARRAY_SIZE(imx290_supply_name)
115828dbc29SManivannan Sadhasivam 
116828dbc29SManivannan Sadhasivam struct imx290_regval {
117e70abe88SLaurent Pinchart 	u32 reg;
11879d99ae8SLaurent Pinchart 	u32 val;
119828dbc29SManivannan Sadhasivam };
120828dbc29SManivannan Sadhasivam 
121828dbc29SManivannan Sadhasivam struct imx290_mode {
122828dbc29SManivannan Sadhasivam 	u32 width;
123828dbc29SManivannan Sadhasivam 	u32 height;
12497589ad6SManivannan Sadhasivam 	u32 hmax;
12598e0500eSManivannan Sadhasivam 	u8 link_freq_index;
126828dbc29SManivannan Sadhasivam 
127828dbc29SManivannan Sadhasivam 	const struct imx290_regval *data;
128828dbc29SManivannan Sadhasivam 	u32 data_size;
129828dbc29SManivannan Sadhasivam };
130828dbc29SManivannan Sadhasivam 
131828dbc29SManivannan Sadhasivam struct imx290 {
132828dbc29SManivannan Sadhasivam 	struct device *dev;
133828dbc29SManivannan Sadhasivam 	struct clk *xclk;
134828dbc29SManivannan Sadhasivam 	struct regmap *regmap;
13597589ad6SManivannan Sadhasivam 	u8 nlanes;
136c566ac01SManivannan Sadhasivam 	u8 bpp;
137828dbc29SManivannan Sadhasivam 
138828dbc29SManivannan Sadhasivam 	struct v4l2_subdev sd;
139828dbc29SManivannan Sadhasivam 	struct media_pad pad;
140828dbc29SManivannan Sadhasivam 	struct v4l2_mbus_framefmt current_format;
141828dbc29SManivannan Sadhasivam 	const struct imx290_mode *current_mode;
142828dbc29SManivannan Sadhasivam 
143828dbc29SManivannan Sadhasivam 	struct regulator_bulk_data supplies[IMX290_NUM_SUPPLIES];
144828dbc29SManivannan Sadhasivam 	struct gpio_desc *rst_gpio;
145828dbc29SManivannan Sadhasivam 
146828dbc29SManivannan Sadhasivam 	struct v4l2_ctrl_handler ctrls;
147828dbc29SManivannan Sadhasivam 	struct v4l2_ctrl *link_freq;
148828dbc29SManivannan Sadhasivam 	struct v4l2_ctrl *pixel_rate;
1490c3b56c9SLaurent Pinchart 	struct v4l2_ctrl *hblank;
1500c3b56c9SLaurent Pinchart 	struct v4l2_ctrl *vblank;
151828dbc29SManivannan Sadhasivam 
152828dbc29SManivannan Sadhasivam 	struct mutex lock;
153828dbc29SManivannan Sadhasivam };
154828dbc29SManivannan Sadhasivam 
155828dbc29SManivannan Sadhasivam struct imx290_pixfmt {
156828dbc29SManivannan Sadhasivam 	u32 code;
157c566ac01SManivannan Sadhasivam 	u8 bpp;
158828dbc29SManivannan Sadhasivam };
159828dbc29SManivannan Sadhasivam 
160828dbc29SManivannan Sadhasivam static const struct imx290_pixfmt imx290_formats[] = {
161c566ac01SManivannan Sadhasivam 	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
162c566ac01SManivannan Sadhasivam 	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
163828dbc29SManivannan Sadhasivam };
164828dbc29SManivannan Sadhasivam 
165828dbc29SManivannan Sadhasivam static const struct regmap_config imx290_regmap_config = {
166828dbc29SManivannan Sadhasivam 	.reg_bits = 16,
167828dbc29SManivannan Sadhasivam 	.val_bits = 8,
168828dbc29SManivannan Sadhasivam };
169828dbc29SManivannan Sadhasivam 
170a58df1f9SManivannan Sadhasivam static const char * const imx290_test_pattern_menu[] = {
171a58df1f9SManivannan Sadhasivam 	"Disabled",
172a58df1f9SManivannan Sadhasivam 	"Sequence Pattern 1",
173a58df1f9SManivannan Sadhasivam 	"Horizontal Color-bar Chart",
174a58df1f9SManivannan Sadhasivam 	"Vertical Color-bar Chart",
175a58df1f9SManivannan Sadhasivam 	"Sequence Pattern 2",
176a58df1f9SManivannan Sadhasivam 	"Gradation Pattern 1",
177a58df1f9SManivannan Sadhasivam 	"Gradation Pattern 2",
178a58df1f9SManivannan Sadhasivam 	"000/555h Toggle Pattern",
179a58df1f9SManivannan Sadhasivam };
180a58df1f9SManivannan Sadhasivam 
181828dbc29SManivannan Sadhasivam static const struct imx290_regval imx290_global_init_settings[] = {
18279d99ae8SLaurent Pinchart 	{ IMX290_CTRL_07, IMX290_WINMODE_1080P },
183827c7e69SLaurent Pinchart 	{ IMX290_VMAX, IMX290_VMAX_DEFAULT },
18479d99ae8SLaurent Pinchart 	{ IMX290_EXTCK_FREQ, 0x2520 },
18579d99ae8SLaurent Pinchart 	{ IMX290_WINWV_OB, 12 },
18679d99ae8SLaurent Pinchart 	{ IMX290_WINPH, 0 },
18779d99ae8SLaurent Pinchart 	{ IMX290_WINPV, 0 },
18879d99ae8SLaurent Pinchart 	{ IMX290_WINWH, 1948 },
18979d99ae8SLaurent Pinchart 	{ IMX290_WINWV, 1097 },
19079d99ae8SLaurent Pinchart 	{ IMX290_XSOUTSEL, IMX290_XSOUTSEL_XVSOUTSEL_VSYNC |
19179d99ae8SLaurent Pinchart 			   IMX290_XSOUTSEL_XHSOUTSEL_HSYNC },
192e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x300f), 0x00 },
193e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3010), 0x21 },
194e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3012), 0x64 },
195*0b274ef2SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3013), 0x00 },
196e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3016), 0x09 },
197e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3070), 0x02 },
198e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3071), 0x11 },
199e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x309b), 0x10 },
200e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x309c), 0x22 },
201e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x30a2), 0x02 },
202e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x30a6), 0x20 },
203e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x30a8), 0x20 },
204e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x30aa), 0x20 },
205e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x30ac), 0x20 },
206e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x30b0), 0x43 },
207e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3119), 0x9e },
208e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x311c), 0x1e },
209e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x311e), 0x08 },
210e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3128), 0x05 },
211e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x313d), 0x83 },
212e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3150), 0x03 },
213e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x317e), 0x00 },
214e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x32b8), 0x50 },
215e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x32b9), 0x10 },
216e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x32ba), 0x00 },
217e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x32bb), 0x04 },
218e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x32c8), 0x50 },
219e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x32c9), 0x10 },
220e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x32ca), 0x00 },
221e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x32cb), 0x04 },
222e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x332c), 0xd3 },
223e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x332d), 0x10 },
224e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x332e), 0x0d },
225e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3358), 0x06 },
226e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3359), 0xe1 },
227e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x335a), 0x11 },
228e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3360), 0x1e },
229e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3361), 0x61 },
230e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3362), 0x10 },
231e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x33b0), 0x50 },
232e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x33b2), 0x1a },
233e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x33b3), 0x04 },
234*0b274ef2SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3480), 0x49 },
235828dbc29SManivannan Sadhasivam };
236828dbc29SManivannan Sadhasivam 
237828dbc29SManivannan Sadhasivam static const struct imx290_regval imx290_1080p_settings[] = {
238828dbc29SManivannan Sadhasivam 	/* mode settings */
23979d99ae8SLaurent Pinchart 	{ IMX290_CTRL_07, IMX290_WINMODE_1080P },
24079d99ae8SLaurent Pinchart 	{ IMX290_WINWV_OB, 12 },
24179d99ae8SLaurent Pinchart 	{ IMX290_OPB_SIZE_V, 10 },
24279d99ae8SLaurent Pinchart 	{ IMX290_X_OUT_SIZE, 1920 },
24379d99ae8SLaurent Pinchart 	{ IMX290_Y_OUT_SIZE, 1080 },
24479d99ae8SLaurent Pinchart 	{ IMX290_INCKSEL1, 0x18 },
24579d99ae8SLaurent Pinchart 	{ IMX290_INCKSEL2, 0x03 },
24679d99ae8SLaurent Pinchart 	{ IMX290_INCKSEL3, 0x20 },
24779d99ae8SLaurent Pinchart 	{ IMX290_INCKSEL4, 0x01 },
24879d99ae8SLaurent Pinchart 	{ IMX290_INCKSEL5, 0x1a },
24979d99ae8SLaurent Pinchart 	{ IMX290_INCKSEL6, 0x1a },
250828dbc29SManivannan Sadhasivam 	/* data rate settings */
25179d99ae8SLaurent Pinchart 	{ IMX290_REPETITION, 0x10 },
25279d99ae8SLaurent Pinchart 	{ IMX290_TCLKPOST, 87 },
25379d99ae8SLaurent Pinchart 	{ IMX290_THSZERO, 55 },
25479d99ae8SLaurent Pinchart 	{ IMX290_THSPREPARE, 31 },
25579d99ae8SLaurent Pinchart 	{ IMX290_TCLKTRAIL, 31 },
25679d99ae8SLaurent Pinchart 	{ IMX290_THSTRAIL, 31 },
25779d99ae8SLaurent Pinchart 	{ IMX290_TCLKZERO, 119 },
25879d99ae8SLaurent Pinchart 	{ IMX290_TCLKPREPARE, 31 },
25979d99ae8SLaurent Pinchart 	{ IMX290_TLPX, 23 },
260828dbc29SManivannan Sadhasivam };
261828dbc29SManivannan Sadhasivam 
262828dbc29SManivannan Sadhasivam static const struct imx290_regval imx290_720p_settings[] = {
263828dbc29SManivannan Sadhasivam 	/* mode settings */
26479d99ae8SLaurent Pinchart 	{ IMX290_CTRL_07, IMX290_WINMODE_720P },
26579d99ae8SLaurent Pinchart 	{ IMX290_WINWV_OB, 6 },
26679d99ae8SLaurent Pinchart 	{ IMX290_OPB_SIZE_V, 4 },
26779d99ae8SLaurent Pinchart 	{ IMX290_X_OUT_SIZE, 1280 },
26879d99ae8SLaurent Pinchart 	{ IMX290_Y_OUT_SIZE, 720 },
26979d99ae8SLaurent Pinchart 	{ IMX290_INCKSEL1, 0x20 },
27079d99ae8SLaurent Pinchart 	{ IMX290_INCKSEL2, 0x00 },
27179d99ae8SLaurent Pinchart 	{ IMX290_INCKSEL3, 0x20 },
27279d99ae8SLaurent Pinchart 	{ IMX290_INCKSEL4, 0x01 },
27379d99ae8SLaurent Pinchart 	{ IMX290_INCKSEL5, 0x1a },
27479d99ae8SLaurent Pinchart 	{ IMX290_INCKSEL6, 0x1a },
275828dbc29SManivannan Sadhasivam 	/* data rate settings */
27679d99ae8SLaurent Pinchart 	{ IMX290_REPETITION, 0x10 },
27779d99ae8SLaurent Pinchart 	{ IMX290_TCLKPOST, 79 },
27879d99ae8SLaurent Pinchart 	{ IMX290_THSZERO, 47 },
27979d99ae8SLaurent Pinchart 	{ IMX290_THSPREPARE, 23 },
28079d99ae8SLaurent Pinchart 	{ IMX290_TCLKTRAIL, 23 },
28179d99ae8SLaurent Pinchart 	{ IMX290_THSTRAIL, 23 },
28279d99ae8SLaurent Pinchart 	{ IMX290_TCLKZERO, 87 },
28379d99ae8SLaurent Pinchart 	{ IMX290_TCLKPREPARE, 23 },
28479d99ae8SLaurent Pinchart 	{ IMX290_TLPX, 23 },
285828dbc29SManivannan Sadhasivam };
286828dbc29SManivannan Sadhasivam 
287828dbc29SManivannan Sadhasivam static const struct imx290_regval imx290_10bit_settings[] = {
28879d99ae8SLaurent Pinchart 	{ IMX290_ADBIT, IMX290_ADBIT_10BIT },
28979d99ae8SLaurent Pinchart 	{ IMX290_OUT_CTRL, IMX290_ODBIT_10BIT },
29079d99ae8SLaurent Pinchart 	{ IMX290_ADBIT1, IMX290_ADBIT1_10BIT },
29179d99ae8SLaurent Pinchart 	{ IMX290_ADBIT2, IMX290_ADBIT2_10BIT },
29279d99ae8SLaurent Pinchart 	{ IMX290_ADBIT3, IMX290_ADBIT3_10BIT },
29379d99ae8SLaurent Pinchart 	{ IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW10 },
29479d99ae8SLaurent Pinchart 	{ IMX290_BLKLEVEL, 60 },
295828dbc29SManivannan Sadhasivam };
296828dbc29SManivannan Sadhasivam 
297c566ac01SManivannan Sadhasivam static const struct imx290_regval imx290_12bit_settings[] = {
29879d99ae8SLaurent Pinchart 	{ IMX290_ADBIT, IMX290_ADBIT_12BIT },
29979d99ae8SLaurent Pinchart 	{ IMX290_OUT_CTRL, IMX290_ODBIT_12BIT },
30079d99ae8SLaurent Pinchart 	{ IMX290_ADBIT1, IMX290_ADBIT1_12BIT },
30179d99ae8SLaurent Pinchart 	{ IMX290_ADBIT2, IMX290_ADBIT2_12BIT },
30279d99ae8SLaurent Pinchart 	{ IMX290_ADBIT3, IMX290_ADBIT3_12BIT },
30379d99ae8SLaurent Pinchart 	{ IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW12 },
30479d99ae8SLaurent Pinchart 	{ IMX290_BLKLEVEL, 240 },
305c566ac01SManivannan Sadhasivam };
306c566ac01SManivannan Sadhasivam 
307828dbc29SManivannan Sadhasivam /* supported link frequencies */
30898e0500eSManivannan Sadhasivam #define FREQ_INDEX_1080P	0
30998e0500eSManivannan Sadhasivam #define FREQ_INDEX_720P		1
31098e0500eSManivannan Sadhasivam static const s64 imx290_link_freq_2lanes[] = {
31198e0500eSManivannan Sadhasivam 	[FREQ_INDEX_1080P] = 445500000,
31298e0500eSManivannan Sadhasivam 	[FREQ_INDEX_720P] = 297000000,
313828dbc29SManivannan Sadhasivam };
31498e0500eSManivannan Sadhasivam static const s64 imx290_link_freq_4lanes[] = {
31598e0500eSManivannan Sadhasivam 	[FREQ_INDEX_1080P] = 222750000,
31698e0500eSManivannan Sadhasivam 	[FREQ_INDEX_720P] = 148500000,
31798e0500eSManivannan Sadhasivam };
31898e0500eSManivannan Sadhasivam 
31998e0500eSManivannan Sadhasivam /*
32098e0500eSManivannan Sadhasivam  * In this function and in the similar ones below We rely on imx290_probe()
32198e0500eSManivannan Sadhasivam  * to ensure that nlanes is either 2 or 4.
32298e0500eSManivannan Sadhasivam  */
32398e0500eSManivannan Sadhasivam static inline const s64 *imx290_link_freqs_ptr(const struct imx290 *imx290)
32498e0500eSManivannan Sadhasivam {
32598e0500eSManivannan Sadhasivam 	if (imx290->nlanes == 2)
32698e0500eSManivannan Sadhasivam 		return imx290_link_freq_2lanes;
32798e0500eSManivannan Sadhasivam 	else
32898e0500eSManivannan Sadhasivam 		return imx290_link_freq_4lanes;
32998e0500eSManivannan Sadhasivam }
33098e0500eSManivannan Sadhasivam 
33198e0500eSManivannan Sadhasivam static inline int imx290_link_freqs_num(const struct imx290 *imx290)
33298e0500eSManivannan Sadhasivam {
33398e0500eSManivannan Sadhasivam 	if (imx290->nlanes == 2)
33498e0500eSManivannan Sadhasivam 		return ARRAY_SIZE(imx290_link_freq_2lanes);
33598e0500eSManivannan Sadhasivam 	else
33698e0500eSManivannan Sadhasivam 		return ARRAY_SIZE(imx290_link_freq_4lanes);
33798e0500eSManivannan Sadhasivam }
338828dbc29SManivannan Sadhasivam 
339828dbc29SManivannan Sadhasivam /* Mode configs */
34097589ad6SManivannan Sadhasivam static const struct imx290_mode imx290_modes_2lanes[] = {
341828dbc29SManivannan Sadhasivam 	{
342828dbc29SManivannan Sadhasivam 		.width = 1920,
343828dbc29SManivannan Sadhasivam 		.height = 1080,
34472825bc6SLaurent Pinchart 		.hmax = 4400,
34598e0500eSManivannan Sadhasivam 		.link_freq_index = FREQ_INDEX_1080P,
346828dbc29SManivannan Sadhasivam 		.data = imx290_1080p_settings,
347828dbc29SManivannan Sadhasivam 		.data_size = ARRAY_SIZE(imx290_1080p_settings),
348828dbc29SManivannan Sadhasivam 	},
349828dbc29SManivannan Sadhasivam 	{
350828dbc29SManivannan Sadhasivam 		.width = 1280,
351828dbc29SManivannan Sadhasivam 		.height = 720,
35272825bc6SLaurent Pinchart 		.hmax = 6600,
35398e0500eSManivannan Sadhasivam 		.link_freq_index = FREQ_INDEX_720P,
354828dbc29SManivannan Sadhasivam 		.data = imx290_720p_settings,
355828dbc29SManivannan Sadhasivam 		.data_size = ARRAY_SIZE(imx290_720p_settings),
356828dbc29SManivannan Sadhasivam 	},
357828dbc29SManivannan Sadhasivam };
358828dbc29SManivannan Sadhasivam 
35997589ad6SManivannan Sadhasivam static const struct imx290_mode imx290_modes_4lanes[] = {
36097589ad6SManivannan Sadhasivam 	{
36197589ad6SManivannan Sadhasivam 		.width = 1920,
36297589ad6SManivannan Sadhasivam 		.height = 1080,
36372825bc6SLaurent Pinchart 		.hmax = 2200,
36498e0500eSManivannan Sadhasivam 		.link_freq_index = FREQ_INDEX_1080P,
36597589ad6SManivannan Sadhasivam 		.data = imx290_1080p_settings,
36697589ad6SManivannan Sadhasivam 		.data_size = ARRAY_SIZE(imx290_1080p_settings),
36797589ad6SManivannan Sadhasivam 	},
36897589ad6SManivannan Sadhasivam 	{
36997589ad6SManivannan Sadhasivam 		.width = 1280,
37097589ad6SManivannan Sadhasivam 		.height = 720,
37172825bc6SLaurent Pinchart 		.hmax = 3300,
37298e0500eSManivannan Sadhasivam 		.link_freq_index = FREQ_INDEX_720P,
37397589ad6SManivannan Sadhasivam 		.data = imx290_720p_settings,
37497589ad6SManivannan Sadhasivam 		.data_size = ARRAY_SIZE(imx290_720p_settings),
37597589ad6SManivannan Sadhasivam 	},
37697589ad6SManivannan Sadhasivam };
37797589ad6SManivannan Sadhasivam 
37897589ad6SManivannan Sadhasivam static inline const struct imx290_mode *imx290_modes_ptr(const struct imx290 *imx290)
37997589ad6SManivannan Sadhasivam {
38097589ad6SManivannan Sadhasivam 	if (imx290->nlanes == 2)
38197589ad6SManivannan Sadhasivam 		return imx290_modes_2lanes;
38297589ad6SManivannan Sadhasivam 	else
38397589ad6SManivannan Sadhasivam 		return imx290_modes_4lanes;
38497589ad6SManivannan Sadhasivam }
38597589ad6SManivannan Sadhasivam 
38697589ad6SManivannan Sadhasivam static inline int imx290_modes_num(const struct imx290 *imx290)
38797589ad6SManivannan Sadhasivam {
38897589ad6SManivannan Sadhasivam 	if (imx290->nlanes == 2)
38997589ad6SManivannan Sadhasivam 		return ARRAY_SIZE(imx290_modes_2lanes);
39097589ad6SManivannan Sadhasivam 	else
39197589ad6SManivannan Sadhasivam 		return ARRAY_SIZE(imx290_modes_4lanes);
39297589ad6SManivannan Sadhasivam }
39397589ad6SManivannan Sadhasivam 
394828dbc29SManivannan Sadhasivam static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
395828dbc29SManivannan Sadhasivam {
396828dbc29SManivannan Sadhasivam 	return container_of(_sd, struct imx290, sd);
397828dbc29SManivannan Sadhasivam }
398828dbc29SManivannan Sadhasivam 
399e611f3daSLaurent Pinchart static int __always_unused imx290_read(struct imx290 *imx290, u32 addr, u32 *value)
400828dbc29SManivannan Sadhasivam {
401e70abe88SLaurent Pinchart 	u8 data[3] = { 0, 0, 0 };
402828dbc29SManivannan Sadhasivam 	int ret;
403828dbc29SManivannan Sadhasivam 
404e70abe88SLaurent Pinchart 	ret = regmap_raw_read(imx290->regmap, addr & IMX290_REG_ADDR_MASK,
405e70abe88SLaurent Pinchart 			      data, (addr >> IMX290_REG_SIZE_SHIFT) & 3);
406e70abe88SLaurent Pinchart 	if (ret < 0) {
407e70abe88SLaurent Pinchart 		dev_err(imx290->dev, "%u-bit read from 0x%04x failed: %d\n",
408e70abe88SLaurent Pinchart 			 ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8,
409e70abe88SLaurent Pinchart 			 addr & IMX290_REG_ADDR_MASK, ret);
410828dbc29SManivannan Sadhasivam 		return ret;
411828dbc29SManivannan Sadhasivam 	}
412828dbc29SManivannan Sadhasivam 
413e70abe88SLaurent Pinchart 	*value = (data[2] << 16) | (data[1] << 8) | data[0];
414828dbc29SManivannan Sadhasivam 	return 0;
415828dbc29SManivannan Sadhasivam }
416828dbc29SManivannan Sadhasivam 
417e611f3daSLaurent Pinchart static int imx290_write(struct imx290 *imx290, u32 addr, u32 value, int *err)
418828dbc29SManivannan Sadhasivam {
419e70abe88SLaurent Pinchart 	u8 data[3] = { value & 0xff, (value >> 8) & 0xff, value >> 16 };
420828dbc29SManivannan Sadhasivam 	int ret;
421828dbc29SManivannan Sadhasivam 
422e611f3daSLaurent Pinchart 	if (err && *err)
423e611f3daSLaurent Pinchart 		return *err;
424e611f3daSLaurent Pinchart 
425e70abe88SLaurent Pinchart 	ret = regmap_raw_write(imx290->regmap, addr & IMX290_REG_ADDR_MASK,
426e70abe88SLaurent Pinchart 			       data, (addr >> IMX290_REG_SIZE_SHIFT) & 3);
427e611f3daSLaurent Pinchart 	if (ret < 0) {
428e70abe88SLaurent Pinchart 		dev_err(imx290->dev, "%u-bit write to 0x%04x failed: %d\n",
429e70abe88SLaurent Pinchart 			 ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8,
430e70abe88SLaurent Pinchart 			 addr & IMX290_REG_ADDR_MASK, ret);
431e611f3daSLaurent Pinchart 		if (err)
432e611f3daSLaurent Pinchart 			*err = ret;
433e611f3daSLaurent Pinchart 	}
434828dbc29SManivannan Sadhasivam 
435828dbc29SManivannan Sadhasivam 	return ret;
436828dbc29SManivannan Sadhasivam }
437828dbc29SManivannan Sadhasivam 
438828dbc29SManivannan Sadhasivam static int imx290_set_register_array(struct imx290 *imx290,
439828dbc29SManivannan Sadhasivam 				     const struct imx290_regval *settings,
440828dbc29SManivannan Sadhasivam 				     unsigned int num_settings)
441828dbc29SManivannan Sadhasivam {
442828dbc29SManivannan Sadhasivam 	unsigned int i;
443828dbc29SManivannan Sadhasivam 	int ret;
444828dbc29SManivannan Sadhasivam 
445828dbc29SManivannan Sadhasivam 	for (i = 0; i < num_settings; ++i, ++settings) {
446e611f3daSLaurent Pinchart 		ret = imx290_write(imx290, settings->reg, settings->val, NULL);
447828dbc29SManivannan Sadhasivam 		if (ret < 0)
448828dbc29SManivannan Sadhasivam 			return ret;
449828dbc29SManivannan Sadhasivam 	}
450828dbc29SManivannan Sadhasivam 
4516544af9bSManivannan Sadhasivam 	/* Provide 10ms settle time */
4528e5652aeSAndrey Konovalov 	usleep_range(10000, 11000);
4536544af9bSManivannan Sadhasivam 
454828dbc29SManivannan Sadhasivam 	return 0;
455828dbc29SManivannan Sadhasivam }
456828dbc29SManivannan Sadhasivam 
457828dbc29SManivannan Sadhasivam /* Stop streaming */
458828dbc29SManivannan Sadhasivam static int imx290_stop_streaming(struct imx290 *imx290)
459828dbc29SManivannan Sadhasivam {
460e611f3daSLaurent Pinchart 	int ret = 0;
461828dbc29SManivannan Sadhasivam 
462e611f3daSLaurent Pinchart 	imx290_write(imx290, IMX290_STANDBY, 0x01, &ret);
463828dbc29SManivannan Sadhasivam 
464828dbc29SManivannan Sadhasivam 	msleep(30);
465828dbc29SManivannan Sadhasivam 
466e611f3daSLaurent Pinchart 	return imx290_write(imx290, IMX290_XMSTA, 0x01, &ret);
467828dbc29SManivannan Sadhasivam }
468828dbc29SManivannan Sadhasivam 
469828dbc29SManivannan Sadhasivam static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
470828dbc29SManivannan Sadhasivam {
471828dbc29SManivannan Sadhasivam 	struct imx290 *imx290 = container_of(ctrl->handler,
472828dbc29SManivannan Sadhasivam 					     struct imx290, ctrls);
473828dbc29SManivannan Sadhasivam 	int ret = 0;
474828dbc29SManivannan Sadhasivam 
475828dbc29SManivannan Sadhasivam 	/* V4L2 controls values will be applied only when power is already up */
476828dbc29SManivannan Sadhasivam 	if (!pm_runtime_get_if_in_use(imx290->dev))
477828dbc29SManivannan Sadhasivam 		return 0;
478828dbc29SManivannan Sadhasivam 
479828dbc29SManivannan Sadhasivam 	switch (ctrl->id) {
480828dbc29SManivannan Sadhasivam 	case V4L2_CID_GAIN:
481e611f3daSLaurent Pinchart 		ret = imx290_write(imx290, IMX290_GAIN, ctrl->val, NULL);
482828dbc29SManivannan Sadhasivam 		break;
483827c7e69SLaurent Pinchart 
484827c7e69SLaurent Pinchart 	case V4L2_CID_EXPOSURE:
485827c7e69SLaurent Pinchart 		ret = imx290_write(imx290, IMX290_SHS1,
486827c7e69SLaurent Pinchart 				   IMX290_VMAX_DEFAULT - ctrl->val - 1, NULL);
487827c7e69SLaurent Pinchart 		break;
488827c7e69SLaurent Pinchart 
489a58df1f9SManivannan Sadhasivam 	case V4L2_CID_TEST_PATTERN:
490a58df1f9SManivannan Sadhasivam 		if (ctrl->val) {
491e611f3daSLaurent Pinchart 			imx290_write(imx290, IMX290_BLKLEVEL, 0, &ret);
4928e5652aeSAndrey Konovalov 			usleep_range(10000, 11000);
493e611f3daSLaurent Pinchart 			imx290_write(imx290, IMX290_PGCTRL,
494a58df1f9SManivannan Sadhasivam 				     (u8)(IMX290_PGCTRL_REGEN |
495a58df1f9SManivannan Sadhasivam 				     IMX290_PGCTRL_THRU |
496e611f3daSLaurent Pinchart 				     IMX290_PGCTRL_MODE(ctrl->val)), &ret);
497a58df1f9SManivannan Sadhasivam 		} else {
498e611f3daSLaurent Pinchart 			imx290_write(imx290, IMX290_PGCTRL, 0x00, &ret);
4998e5652aeSAndrey Konovalov 			usleep_range(10000, 11000);
500c566ac01SManivannan Sadhasivam 			if (imx290->bpp == 10)
501e611f3daSLaurent Pinchart 				imx290_write(imx290, IMX290_BLKLEVEL, 0x3c,
502e611f3daSLaurent Pinchart 					     &ret);
503c566ac01SManivannan Sadhasivam 			else /* 12 bits per pixel */
504e611f3daSLaurent Pinchart 				imx290_write(imx290, IMX290_BLKLEVEL, 0xf0,
505e611f3daSLaurent Pinchart 					     &ret);
506a58df1f9SManivannan Sadhasivam 		}
507a58df1f9SManivannan Sadhasivam 		break;
508828dbc29SManivannan Sadhasivam 	default:
509828dbc29SManivannan Sadhasivam 		ret = -EINVAL;
510828dbc29SManivannan Sadhasivam 		break;
511828dbc29SManivannan Sadhasivam 	}
512828dbc29SManivannan Sadhasivam 
513828dbc29SManivannan Sadhasivam 	pm_runtime_put(imx290->dev);
514828dbc29SManivannan Sadhasivam 
515828dbc29SManivannan Sadhasivam 	return ret;
516828dbc29SManivannan Sadhasivam }
517828dbc29SManivannan Sadhasivam 
518828dbc29SManivannan Sadhasivam static const struct v4l2_ctrl_ops imx290_ctrl_ops = {
519828dbc29SManivannan Sadhasivam 	.s_ctrl = imx290_set_ctrl,
520828dbc29SManivannan Sadhasivam };
521828dbc29SManivannan Sadhasivam 
522828dbc29SManivannan Sadhasivam static int imx290_enum_mbus_code(struct v4l2_subdev *sd,
5230d346d2aSTomi Valkeinen 				 struct v4l2_subdev_state *sd_state,
524828dbc29SManivannan Sadhasivam 				 struct v4l2_subdev_mbus_code_enum *code)
525828dbc29SManivannan Sadhasivam {
526828dbc29SManivannan Sadhasivam 	if (code->index >= ARRAY_SIZE(imx290_formats))
527828dbc29SManivannan Sadhasivam 		return -EINVAL;
528828dbc29SManivannan Sadhasivam 
529828dbc29SManivannan Sadhasivam 	code->code = imx290_formats[code->index].code;
530828dbc29SManivannan Sadhasivam 
531828dbc29SManivannan Sadhasivam 	return 0;
532828dbc29SManivannan Sadhasivam }
533828dbc29SManivannan Sadhasivam 
5343b867fb6SManivannan Sadhasivam static int imx290_enum_frame_size(struct v4l2_subdev *sd,
5350d346d2aSTomi Valkeinen 				  struct v4l2_subdev_state *sd_state,
5363b867fb6SManivannan Sadhasivam 				  struct v4l2_subdev_frame_size_enum *fse)
5373b867fb6SManivannan Sadhasivam {
5383b867fb6SManivannan Sadhasivam 	const struct imx290 *imx290 = to_imx290(sd);
5393b867fb6SManivannan Sadhasivam 	const struct imx290_mode *imx290_modes = imx290_modes_ptr(imx290);
5403b867fb6SManivannan Sadhasivam 
5413b867fb6SManivannan Sadhasivam 	if ((fse->code != imx290_formats[0].code) &&
5423b867fb6SManivannan Sadhasivam 	    (fse->code != imx290_formats[1].code))
5433b867fb6SManivannan Sadhasivam 		return -EINVAL;
5443b867fb6SManivannan Sadhasivam 
5453b867fb6SManivannan Sadhasivam 	if (fse->index >= imx290_modes_num(imx290))
5463b867fb6SManivannan Sadhasivam 		return -EINVAL;
5473b867fb6SManivannan Sadhasivam 
5483b867fb6SManivannan Sadhasivam 	fse->min_width = imx290_modes[fse->index].width;
5493b867fb6SManivannan Sadhasivam 	fse->max_width = imx290_modes[fse->index].width;
5503b867fb6SManivannan Sadhasivam 	fse->min_height = imx290_modes[fse->index].height;
5513b867fb6SManivannan Sadhasivam 	fse->max_height = imx290_modes[fse->index].height;
5523b867fb6SManivannan Sadhasivam 
5533b867fb6SManivannan Sadhasivam 	return 0;
5543b867fb6SManivannan Sadhasivam }
5553b867fb6SManivannan Sadhasivam 
556828dbc29SManivannan Sadhasivam static int imx290_get_fmt(struct v4l2_subdev *sd,
5570d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
558828dbc29SManivannan Sadhasivam 			  struct v4l2_subdev_format *fmt)
559828dbc29SManivannan Sadhasivam {
560828dbc29SManivannan Sadhasivam 	struct imx290 *imx290 = to_imx290(sd);
561828dbc29SManivannan Sadhasivam 	struct v4l2_mbus_framefmt *framefmt;
562828dbc29SManivannan Sadhasivam 
563828dbc29SManivannan Sadhasivam 	mutex_lock(&imx290->lock);
564828dbc29SManivannan Sadhasivam 
565828dbc29SManivannan Sadhasivam 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
5660d346d2aSTomi Valkeinen 		framefmt = v4l2_subdev_get_try_format(&imx290->sd, sd_state,
567828dbc29SManivannan Sadhasivam 						      fmt->pad);
568828dbc29SManivannan Sadhasivam 	else
569828dbc29SManivannan Sadhasivam 		framefmt = &imx290->current_format;
570828dbc29SManivannan Sadhasivam 
571828dbc29SManivannan Sadhasivam 	fmt->format = *framefmt;
572828dbc29SManivannan Sadhasivam 
573828dbc29SManivannan Sadhasivam 	mutex_unlock(&imx290->lock);
574828dbc29SManivannan Sadhasivam 
575828dbc29SManivannan Sadhasivam 	return 0;
576828dbc29SManivannan Sadhasivam }
577828dbc29SManivannan Sadhasivam 
57898e0500eSManivannan Sadhasivam static inline u8 imx290_get_link_freq_index(struct imx290 *imx290)
57998e0500eSManivannan Sadhasivam {
58098e0500eSManivannan Sadhasivam 	return imx290->current_mode->link_freq_index;
58198e0500eSManivannan Sadhasivam }
58298e0500eSManivannan Sadhasivam 
58398e0500eSManivannan Sadhasivam static s64 imx290_get_link_freq(struct imx290 *imx290)
58498e0500eSManivannan Sadhasivam {
58598e0500eSManivannan Sadhasivam 	u8 index = imx290_get_link_freq_index(imx290);
58698e0500eSManivannan Sadhasivam 
58798e0500eSManivannan Sadhasivam 	return *(imx290_link_freqs_ptr(imx290) + index);
58898e0500eSManivannan Sadhasivam }
58998e0500eSManivannan Sadhasivam 
59098e0500eSManivannan Sadhasivam static u64 imx290_calc_pixel_rate(struct imx290 *imx290)
59198e0500eSManivannan Sadhasivam {
59298e0500eSManivannan Sadhasivam 	s64 link_freq = imx290_get_link_freq(imx290);
59398e0500eSManivannan Sadhasivam 	u8 nlanes = imx290->nlanes;
59498e0500eSManivannan Sadhasivam 	u64 pixel_rate;
59598e0500eSManivannan Sadhasivam 
59698e0500eSManivannan Sadhasivam 	/* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */
59798e0500eSManivannan Sadhasivam 	pixel_rate = link_freq * 2 * nlanes;
598c566ac01SManivannan Sadhasivam 	do_div(pixel_rate, imx290->bpp);
59998e0500eSManivannan Sadhasivam 	return pixel_rate;
60098e0500eSManivannan Sadhasivam }
60198e0500eSManivannan Sadhasivam 
602828dbc29SManivannan Sadhasivam static int imx290_set_fmt(struct v4l2_subdev *sd,
6030d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
604828dbc29SManivannan Sadhasivam 			  struct v4l2_subdev_format *fmt)
605828dbc29SManivannan Sadhasivam {
606828dbc29SManivannan Sadhasivam 	struct imx290 *imx290 = to_imx290(sd);
607828dbc29SManivannan Sadhasivam 	const struct imx290_mode *mode;
608828dbc29SManivannan Sadhasivam 	struct v4l2_mbus_framefmt *format;
609828dbc29SManivannan Sadhasivam 	unsigned int i;
610828dbc29SManivannan Sadhasivam 
611828dbc29SManivannan Sadhasivam 	mutex_lock(&imx290->lock);
612828dbc29SManivannan Sadhasivam 
61397589ad6SManivannan Sadhasivam 	mode = v4l2_find_nearest_size(imx290_modes_ptr(imx290),
61497589ad6SManivannan Sadhasivam 				      imx290_modes_num(imx290), width, height,
615828dbc29SManivannan Sadhasivam 				      fmt->format.width, fmt->format.height);
616828dbc29SManivannan Sadhasivam 
617828dbc29SManivannan Sadhasivam 	fmt->format.width = mode->width;
618828dbc29SManivannan Sadhasivam 	fmt->format.height = mode->height;
619828dbc29SManivannan Sadhasivam 
620828dbc29SManivannan Sadhasivam 	for (i = 0; i < ARRAY_SIZE(imx290_formats); i++)
621828dbc29SManivannan Sadhasivam 		if (imx290_formats[i].code == fmt->format.code)
622828dbc29SManivannan Sadhasivam 			break;
623828dbc29SManivannan Sadhasivam 
624828dbc29SManivannan Sadhasivam 	if (i >= ARRAY_SIZE(imx290_formats))
625828dbc29SManivannan Sadhasivam 		i = 0;
626828dbc29SManivannan Sadhasivam 
627828dbc29SManivannan Sadhasivam 	fmt->format.code = imx290_formats[i].code;
628828dbc29SManivannan Sadhasivam 	fmt->format.field = V4L2_FIELD_NONE;
629828dbc29SManivannan Sadhasivam 
630828dbc29SManivannan Sadhasivam 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
6310d346d2aSTomi Valkeinen 		format = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
632828dbc29SManivannan Sadhasivam 	} else {
633828dbc29SManivannan Sadhasivam 		format = &imx290->current_format;
634828dbc29SManivannan Sadhasivam 		imx290->current_mode = mode;
635c566ac01SManivannan Sadhasivam 		imx290->bpp = imx290_formats[i].bpp;
63698e0500eSManivannan Sadhasivam 
63798e0500eSManivannan Sadhasivam 		if (imx290->link_freq)
63898e0500eSManivannan Sadhasivam 			__v4l2_ctrl_s_ctrl(imx290->link_freq,
63998e0500eSManivannan Sadhasivam 					   imx290_get_link_freq_index(imx290));
64098e0500eSManivannan Sadhasivam 		if (imx290->pixel_rate)
64198e0500eSManivannan Sadhasivam 			__v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate,
64298e0500eSManivannan Sadhasivam 						 imx290_calc_pixel_rate(imx290));
6430c3b56c9SLaurent Pinchart 
6440c3b56c9SLaurent Pinchart 		if (imx290->hblank) {
6450c3b56c9SLaurent Pinchart 			unsigned int hblank = mode->hmax - mode->width;
6460c3b56c9SLaurent Pinchart 
6470c3b56c9SLaurent Pinchart 			__v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank,
6480c3b56c9SLaurent Pinchart 						 1, hblank);
6490c3b56c9SLaurent Pinchart 		}
6500c3b56c9SLaurent Pinchart 
6510c3b56c9SLaurent Pinchart 		if (imx290->vblank) {
6520c3b56c9SLaurent Pinchart 			unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
6530c3b56c9SLaurent Pinchart 
6540c3b56c9SLaurent Pinchart 			__v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank,
6550c3b56c9SLaurent Pinchart 						 1, vblank);
6560c3b56c9SLaurent Pinchart 		}
657828dbc29SManivannan Sadhasivam 	}
658828dbc29SManivannan Sadhasivam 
659828dbc29SManivannan Sadhasivam 	*format = fmt->format;
660828dbc29SManivannan Sadhasivam 
661828dbc29SManivannan Sadhasivam 	mutex_unlock(&imx290->lock);
662828dbc29SManivannan Sadhasivam 
663828dbc29SManivannan Sadhasivam 	return 0;
664828dbc29SManivannan Sadhasivam }
665828dbc29SManivannan Sadhasivam 
666828dbc29SManivannan Sadhasivam static int imx290_entity_init_cfg(struct v4l2_subdev *subdev,
6670d346d2aSTomi Valkeinen 				  struct v4l2_subdev_state *sd_state)
668828dbc29SManivannan Sadhasivam {
669828dbc29SManivannan Sadhasivam 	struct v4l2_subdev_format fmt = { 0 };
670828dbc29SManivannan Sadhasivam 
6710d346d2aSTomi Valkeinen 	fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
672828dbc29SManivannan Sadhasivam 	fmt.format.width = 1920;
673828dbc29SManivannan Sadhasivam 	fmt.format.height = 1080;
674828dbc29SManivannan Sadhasivam 
6750d346d2aSTomi Valkeinen 	imx290_set_fmt(subdev, sd_state, &fmt);
676828dbc29SManivannan Sadhasivam 
677828dbc29SManivannan Sadhasivam 	return 0;
678828dbc29SManivannan Sadhasivam }
679828dbc29SManivannan Sadhasivam 
68098e0500eSManivannan Sadhasivam static int imx290_write_current_format(struct imx290 *imx290)
681828dbc29SManivannan Sadhasivam {
682828dbc29SManivannan Sadhasivam 	int ret;
683828dbc29SManivannan Sadhasivam 
68498e0500eSManivannan Sadhasivam 	switch (imx290->current_format.code) {
685828dbc29SManivannan Sadhasivam 	case MEDIA_BUS_FMT_SRGGB10_1X10:
686828dbc29SManivannan Sadhasivam 		ret = imx290_set_register_array(imx290, imx290_10bit_settings,
687828dbc29SManivannan Sadhasivam 						ARRAY_SIZE(
688828dbc29SManivannan Sadhasivam 							imx290_10bit_settings));
689828dbc29SManivannan Sadhasivam 		if (ret < 0) {
690828dbc29SManivannan Sadhasivam 			dev_err(imx290->dev, "Could not set format registers\n");
691828dbc29SManivannan Sadhasivam 			return ret;
692828dbc29SManivannan Sadhasivam 		}
693828dbc29SManivannan Sadhasivam 		break;
694c566ac01SManivannan Sadhasivam 	case MEDIA_BUS_FMT_SRGGB12_1X12:
695c566ac01SManivannan Sadhasivam 		ret = imx290_set_register_array(imx290, imx290_12bit_settings,
696c566ac01SManivannan Sadhasivam 						ARRAY_SIZE(
697c566ac01SManivannan Sadhasivam 							imx290_12bit_settings));
698c566ac01SManivannan Sadhasivam 		if (ret < 0) {
699c566ac01SManivannan Sadhasivam 			dev_err(imx290->dev, "Could not set format registers\n");
700c566ac01SManivannan Sadhasivam 			return ret;
701c566ac01SManivannan Sadhasivam 		}
702c566ac01SManivannan Sadhasivam 		break;
703828dbc29SManivannan Sadhasivam 	default:
704828dbc29SManivannan Sadhasivam 		dev_err(imx290->dev, "Unknown pixel format\n");
705828dbc29SManivannan Sadhasivam 		return -EINVAL;
706828dbc29SManivannan Sadhasivam 	}
707828dbc29SManivannan Sadhasivam 
708828dbc29SManivannan Sadhasivam 	return 0;
709828dbc29SManivannan Sadhasivam }
710828dbc29SManivannan Sadhasivam 
711828dbc29SManivannan Sadhasivam /* Start streaming */
712828dbc29SManivannan Sadhasivam static int imx290_start_streaming(struct imx290 *imx290)
713828dbc29SManivannan Sadhasivam {
714828dbc29SManivannan Sadhasivam 	int ret;
715828dbc29SManivannan Sadhasivam 
716828dbc29SManivannan Sadhasivam 	/* Set init register settings */
717828dbc29SManivannan Sadhasivam 	ret = imx290_set_register_array(imx290, imx290_global_init_settings,
718828dbc29SManivannan Sadhasivam 					ARRAY_SIZE(
719828dbc29SManivannan Sadhasivam 						imx290_global_init_settings));
720828dbc29SManivannan Sadhasivam 	if (ret < 0) {
721828dbc29SManivannan Sadhasivam 		dev_err(imx290->dev, "Could not set init registers\n");
722828dbc29SManivannan Sadhasivam 		return ret;
723828dbc29SManivannan Sadhasivam 	}
724828dbc29SManivannan Sadhasivam 
72598e0500eSManivannan Sadhasivam 	/* Apply the register values related to current frame format */
72698e0500eSManivannan Sadhasivam 	ret = imx290_write_current_format(imx290);
727828dbc29SManivannan Sadhasivam 	if (ret < 0) {
728828dbc29SManivannan Sadhasivam 		dev_err(imx290->dev, "Could not set frame format\n");
729828dbc29SManivannan Sadhasivam 		return ret;
730828dbc29SManivannan Sadhasivam 	}
731828dbc29SManivannan Sadhasivam 
732828dbc29SManivannan Sadhasivam 	/* Apply default values of current mode */
733828dbc29SManivannan Sadhasivam 	ret = imx290_set_register_array(imx290, imx290->current_mode->data,
734828dbc29SManivannan Sadhasivam 					imx290->current_mode->data_size);
735828dbc29SManivannan Sadhasivam 	if (ret < 0) {
736828dbc29SManivannan Sadhasivam 		dev_err(imx290->dev, "Could not set current mode\n");
737828dbc29SManivannan Sadhasivam 		return ret;
738828dbc29SManivannan Sadhasivam 	}
739454a86f3SLaurent Pinchart 
740e611f3daSLaurent Pinchart 	ret = imx290_write(imx290, IMX290_HMAX, imx290->current_mode->hmax,
741e611f3daSLaurent Pinchart 			   NULL);
742454a86f3SLaurent Pinchart 	if (ret)
74397589ad6SManivannan Sadhasivam 		return ret;
744828dbc29SManivannan Sadhasivam 
745828dbc29SManivannan Sadhasivam 	/* Apply customized values from user */
746828dbc29SManivannan Sadhasivam 	ret = v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler);
747828dbc29SManivannan Sadhasivam 	if (ret) {
748828dbc29SManivannan Sadhasivam 		dev_err(imx290->dev, "Could not sync v4l2 controls\n");
749828dbc29SManivannan Sadhasivam 		return ret;
750828dbc29SManivannan Sadhasivam 	}
751828dbc29SManivannan Sadhasivam 
752e611f3daSLaurent Pinchart 	imx290_write(imx290, IMX290_STANDBY, 0x00, &ret);
753828dbc29SManivannan Sadhasivam 
754828dbc29SManivannan Sadhasivam 	msleep(30);
755828dbc29SManivannan Sadhasivam 
756828dbc29SManivannan Sadhasivam 	/* Start streaming */
757e611f3daSLaurent Pinchart 	return imx290_write(imx290, IMX290_XMSTA, 0x00, &ret);
758828dbc29SManivannan Sadhasivam }
759828dbc29SManivannan Sadhasivam 
760828dbc29SManivannan Sadhasivam static int imx290_set_stream(struct v4l2_subdev *sd, int enable)
761828dbc29SManivannan Sadhasivam {
762828dbc29SManivannan Sadhasivam 	struct imx290 *imx290 = to_imx290(sd);
763828dbc29SManivannan Sadhasivam 	int ret = 0;
764828dbc29SManivannan Sadhasivam 
765828dbc29SManivannan Sadhasivam 	if (enable) {
766739d9c64SMauro Carvalho Chehab 		ret = pm_runtime_resume_and_get(imx290->dev);
767739d9c64SMauro Carvalho Chehab 		if (ret < 0)
768828dbc29SManivannan Sadhasivam 			goto unlock_and_return;
769828dbc29SManivannan Sadhasivam 
770828dbc29SManivannan Sadhasivam 		ret = imx290_start_streaming(imx290);
771828dbc29SManivannan Sadhasivam 		if (ret) {
772828dbc29SManivannan Sadhasivam 			dev_err(imx290->dev, "Start stream failed\n");
773828dbc29SManivannan Sadhasivam 			pm_runtime_put(imx290->dev);
774828dbc29SManivannan Sadhasivam 			goto unlock_and_return;
775828dbc29SManivannan Sadhasivam 		}
776828dbc29SManivannan Sadhasivam 	} else {
777828dbc29SManivannan Sadhasivam 		imx290_stop_streaming(imx290);
778828dbc29SManivannan Sadhasivam 		pm_runtime_put(imx290->dev);
779828dbc29SManivannan Sadhasivam 	}
780828dbc29SManivannan Sadhasivam 
781828dbc29SManivannan Sadhasivam unlock_and_return:
782828dbc29SManivannan Sadhasivam 
783828dbc29SManivannan Sadhasivam 	return ret;
784828dbc29SManivannan Sadhasivam }
785828dbc29SManivannan Sadhasivam 
786828dbc29SManivannan Sadhasivam static int imx290_get_regulators(struct device *dev, struct imx290 *imx290)
787828dbc29SManivannan Sadhasivam {
788828dbc29SManivannan Sadhasivam 	unsigned int i;
789828dbc29SManivannan Sadhasivam 
7902548df53SLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(imx290->supplies); i++)
791828dbc29SManivannan Sadhasivam 		imx290->supplies[i].supply = imx290_supply_name[i];
792828dbc29SManivannan Sadhasivam 
7932548df53SLaurent Pinchart 	return devm_regulator_bulk_get(dev, ARRAY_SIZE(imx290->supplies),
794828dbc29SManivannan Sadhasivam 				       imx290->supplies);
795828dbc29SManivannan Sadhasivam }
796828dbc29SManivannan Sadhasivam 
79797589ad6SManivannan Sadhasivam static int imx290_set_data_lanes(struct imx290 *imx290)
79897589ad6SManivannan Sadhasivam {
79997589ad6SManivannan Sadhasivam 	int ret = 0, laneval, frsel;
80097589ad6SManivannan Sadhasivam 
80197589ad6SManivannan Sadhasivam 	switch (imx290->nlanes) {
80297589ad6SManivannan Sadhasivam 	case 2:
80397589ad6SManivannan Sadhasivam 		laneval = 0x01;
80497589ad6SManivannan Sadhasivam 		frsel = 0x02;
80597589ad6SManivannan Sadhasivam 		break;
80697589ad6SManivannan Sadhasivam 	case 4:
80797589ad6SManivannan Sadhasivam 		laneval = 0x03;
80897589ad6SManivannan Sadhasivam 		frsel = 0x01;
80997589ad6SManivannan Sadhasivam 		break;
81097589ad6SManivannan Sadhasivam 	default:
81197589ad6SManivannan Sadhasivam 		/*
81297589ad6SManivannan Sadhasivam 		 * We should never hit this since the data lane count is
81397589ad6SManivannan Sadhasivam 		 * validated in probe itself
81497589ad6SManivannan Sadhasivam 		 */
81597589ad6SManivannan Sadhasivam 		dev_err(imx290->dev, "Lane configuration not supported\n");
816e611f3daSLaurent Pinchart 		return -EINVAL;
81797589ad6SManivannan Sadhasivam 	}
81897589ad6SManivannan Sadhasivam 
819e611f3daSLaurent Pinchart 	imx290_write(imx290, IMX290_PHY_LANE_NUM, laneval, &ret);
820e611f3daSLaurent Pinchart 	imx290_write(imx290, IMX290_CSI_LANE_MODE, laneval, &ret);
821e611f3daSLaurent Pinchart 	imx290_write(imx290, IMX290_FR_FDG_SEL, frsel, &ret);
82297589ad6SManivannan Sadhasivam 
82397589ad6SManivannan Sadhasivam 	return ret;
82497589ad6SManivannan Sadhasivam }
82597589ad6SManivannan Sadhasivam 
826828dbc29SManivannan Sadhasivam static int imx290_power_on(struct device *dev)
827828dbc29SManivannan Sadhasivam {
828b50ce25dSKrzysztof Kozlowski 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
829828dbc29SManivannan Sadhasivam 	struct imx290 *imx290 = to_imx290(sd);
830828dbc29SManivannan Sadhasivam 	int ret;
831828dbc29SManivannan Sadhasivam 
832828dbc29SManivannan Sadhasivam 	ret = clk_prepare_enable(imx290->xclk);
833828dbc29SManivannan Sadhasivam 	if (ret) {
834b50ce25dSKrzysztof Kozlowski 		dev_err(dev, "Failed to enable clock\n");
835828dbc29SManivannan Sadhasivam 		return ret;
836828dbc29SManivannan Sadhasivam 	}
837828dbc29SManivannan Sadhasivam 
8382548df53SLaurent Pinchart 	ret = regulator_bulk_enable(ARRAY_SIZE(imx290->supplies),
8392548df53SLaurent Pinchart 				    imx290->supplies);
840828dbc29SManivannan Sadhasivam 	if (ret) {
841b50ce25dSKrzysztof Kozlowski 		dev_err(dev, "Failed to enable regulators\n");
842828dbc29SManivannan Sadhasivam 		clk_disable_unprepare(imx290->xclk);
843828dbc29SManivannan Sadhasivam 		return ret;
844828dbc29SManivannan Sadhasivam 	}
845828dbc29SManivannan Sadhasivam 
846828dbc29SManivannan Sadhasivam 	usleep_range(1, 2);
8473909a92dSAndrey Konovalov 	gpiod_set_value_cansleep(imx290->rst_gpio, 0);
848828dbc29SManivannan Sadhasivam 	usleep_range(30000, 31000);
849828dbc29SManivannan Sadhasivam 
85097589ad6SManivannan Sadhasivam 	/* Set data lane count */
85197589ad6SManivannan Sadhasivam 	imx290_set_data_lanes(imx290);
85297589ad6SManivannan Sadhasivam 
853828dbc29SManivannan Sadhasivam 	return 0;
854828dbc29SManivannan Sadhasivam }
855828dbc29SManivannan Sadhasivam 
856828dbc29SManivannan Sadhasivam static int imx290_power_off(struct device *dev)
857828dbc29SManivannan Sadhasivam {
858b50ce25dSKrzysztof Kozlowski 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
859828dbc29SManivannan Sadhasivam 	struct imx290 *imx290 = to_imx290(sd);
860828dbc29SManivannan Sadhasivam 
861828dbc29SManivannan Sadhasivam 	clk_disable_unprepare(imx290->xclk);
8623909a92dSAndrey Konovalov 	gpiod_set_value_cansleep(imx290->rst_gpio, 1);
8632548df53SLaurent Pinchart 	regulator_bulk_disable(ARRAY_SIZE(imx290->supplies), imx290->supplies);
864828dbc29SManivannan Sadhasivam 
865828dbc29SManivannan Sadhasivam 	return 0;
866828dbc29SManivannan Sadhasivam }
867828dbc29SManivannan Sadhasivam 
868828dbc29SManivannan Sadhasivam static const struct dev_pm_ops imx290_pm_ops = {
8698d2d1bedSAndrey Konovalov 	SET_RUNTIME_PM_OPS(imx290_power_off, imx290_power_on, NULL)
870828dbc29SManivannan Sadhasivam };
871828dbc29SManivannan Sadhasivam 
872828dbc29SManivannan Sadhasivam static const struct v4l2_subdev_video_ops imx290_video_ops = {
873828dbc29SManivannan Sadhasivam 	.s_stream = imx290_set_stream,
874828dbc29SManivannan Sadhasivam };
875828dbc29SManivannan Sadhasivam 
876828dbc29SManivannan Sadhasivam static const struct v4l2_subdev_pad_ops imx290_pad_ops = {
877828dbc29SManivannan Sadhasivam 	.init_cfg = imx290_entity_init_cfg,
878828dbc29SManivannan Sadhasivam 	.enum_mbus_code = imx290_enum_mbus_code,
8793b867fb6SManivannan Sadhasivam 	.enum_frame_size = imx290_enum_frame_size,
880828dbc29SManivannan Sadhasivam 	.get_fmt = imx290_get_fmt,
881828dbc29SManivannan Sadhasivam 	.set_fmt = imx290_set_fmt,
882828dbc29SManivannan Sadhasivam };
883828dbc29SManivannan Sadhasivam 
884828dbc29SManivannan Sadhasivam static const struct v4l2_subdev_ops imx290_subdev_ops = {
885828dbc29SManivannan Sadhasivam 	.video = &imx290_video_ops,
886828dbc29SManivannan Sadhasivam 	.pad = &imx290_pad_ops,
887828dbc29SManivannan Sadhasivam };
888828dbc29SManivannan Sadhasivam 
889828dbc29SManivannan Sadhasivam static const struct media_entity_operations imx290_subdev_entity_ops = {
890828dbc29SManivannan Sadhasivam 	.link_validate = v4l2_subdev_link_validate,
891828dbc29SManivannan Sadhasivam };
892828dbc29SManivannan Sadhasivam 
89372c87b7aSLaurent Pinchart static int imx290_ctrl_init(struct imx290 *imx290)
89472c87b7aSLaurent Pinchart {
8954c9c93cfSLaurent Pinchart 	struct v4l2_fwnode_device_properties props;
8960c3b56c9SLaurent Pinchart 	unsigned int blank;
89772c87b7aSLaurent Pinchart 	int ret;
89872c87b7aSLaurent Pinchart 
8994c9c93cfSLaurent Pinchart 	ret = v4l2_fwnode_device_parse(imx290->dev, &props);
9004c9c93cfSLaurent Pinchart 	if (ret < 0)
9014c9c93cfSLaurent Pinchart 		return ret;
9024c9c93cfSLaurent Pinchart 
9034c9c93cfSLaurent Pinchart 	v4l2_ctrl_handler_init(&imx290->ctrls, 9);
90472c87b7aSLaurent Pinchart 	imx290->ctrls.lock = &imx290->lock;
90572c87b7aSLaurent Pinchart 
90672c87b7aSLaurent Pinchart 	/*
90772c87b7aSLaurent Pinchart 	 * The sensor has an analog gain and a digital gain, both controlled
90872c87b7aSLaurent Pinchart 	 * through a single gain value, expressed in 0.3dB increments. Values
90972c87b7aSLaurent Pinchart 	 * from 0.0dB (0) to 30.0dB (100) apply analog gain only, higher values
91072c87b7aSLaurent Pinchart 	 * up to 72.0dB (240) add further digital gain. Limit the range to
91172c87b7aSLaurent Pinchart 	 * analog gain only, support for digital gain can be added separately
91272c87b7aSLaurent Pinchart 	 * if needed.
91372c87b7aSLaurent Pinchart 	 *
91472c87b7aSLaurent Pinchart 	 * The IMX327 and IMX462 are largely compatible with the IMX290, but
91572c87b7aSLaurent Pinchart 	 * have an analog gain range of 0.0dB to 29.4dB and 42dB of digital
91672c87b7aSLaurent Pinchart 	 * gain. When support for those sensors gets added to the driver, the
91772c87b7aSLaurent Pinchart 	 * gain control should be adjusted accordingly.
91872c87b7aSLaurent Pinchart 	 */
91972c87b7aSLaurent Pinchart 	v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
92072c87b7aSLaurent Pinchart 			  V4L2_CID_GAIN, 0, 100, 1, 0);
92172c87b7aSLaurent Pinchart 
92272c87b7aSLaurent Pinchart 	v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
92372c87b7aSLaurent Pinchart 			  V4L2_CID_EXPOSURE, 1, IMX290_VMAX_DEFAULT - 2, 1,
92472c87b7aSLaurent Pinchart 			  IMX290_VMAX_DEFAULT - 2);
92572c87b7aSLaurent Pinchart 
92672c87b7aSLaurent Pinchart 	imx290->link_freq =
92772c87b7aSLaurent Pinchart 		v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops,
92872c87b7aSLaurent Pinchart 				       V4L2_CID_LINK_FREQ,
92972c87b7aSLaurent Pinchart 				       imx290_link_freqs_num(imx290) - 1, 0,
93072c87b7aSLaurent Pinchart 				       imx290_link_freqs_ptr(imx290));
93172c87b7aSLaurent Pinchart 	if (imx290->link_freq)
93272c87b7aSLaurent Pinchart 		imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
93372c87b7aSLaurent Pinchart 
93472c87b7aSLaurent Pinchart 	imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
93572c87b7aSLaurent Pinchart 					       V4L2_CID_PIXEL_RATE,
93672c87b7aSLaurent Pinchart 					       1, INT_MAX, 1,
93772c87b7aSLaurent Pinchart 					       imx290_calc_pixel_rate(imx290));
93872c87b7aSLaurent Pinchart 
93972c87b7aSLaurent Pinchart 	v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops,
94072c87b7aSLaurent Pinchart 				     V4L2_CID_TEST_PATTERN,
94172c87b7aSLaurent Pinchart 				     ARRAY_SIZE(imx290_test_pattern_menu) - 1,
94272c87b7aSLaurent Pinchart 				     0, 0, imx290_test_pattern_menu);
94372c87b7aSLaurent Pinchart 
9440c3b56c9SLaurent Pinchart 	blank = imx290->current_mode->hmax - imx290->current_mode->width;
9450c3b56c9SLaurent Pinchart 	imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
9460c3b56c9SLaurent Pinchart 					   V4L2_CID_HBLANK, blank, blank, 1,
9470c3b56c9SLaurent Pinchart 					   blank);
9480c3b56c9SLaurent Pinchart 	if (imx290->hblank)
9490c3b56c9SLaurent Pinchart 		imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
9500c3b56c9SLaurent Pinchart 
9510c3b56c9SLaurent Pinchart 	blank = IMX290_VMAX_DEFAULT - imx290->current_mode->height;
9520c3b56c9SLaurent Pinchart 	imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
9530c3b56c9SLaurent Pinchart 					   V4L2_CID_VBLANK, blank, blank, 1,
9540c3b56c9SLaurent Pinchart 					   blank);
9550c3b56c9SLaurent Pinchart 	if (imx290->vblank)
9560c3b56c9SLaurent Pinchart 		imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
9570c3b56c9SLaurent Pinchart 
9584c9c93cfSLaurent Pinchart 	v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops,
9594c9c93cfSLaurent Pinchart 					&props);
9604c9c93cfSLaurent Pinchart 
96172c87b7aSLaurent Pinchart 	imx290->sd.ctrl_handler = &imx290->ctrls;
96272c87b7aSLaurent Pinchart 
96372c87b7aSLaurent Pinchart 	if (imx290->ctrls.error) {
96472c87b7aSLaurent Pinchart 		ret = imx290->ctrls.error;
96572c87b7aSLaurent Pinchart 		v4l2_ctrl_handler_free(&imx290->ctrls);
96672c87b7aSLaurent Pinchart 		return ret;
96772c87b7aSLaurent Pinchart 	}
96872c87b7aSLaurent Pinchart 
96972c87b7aSLaurent Pinchart 	return 0;
97072c87b7aSLaurent Pinchart }
97172c87b7aSLaurent Pinchart 
97298e0500eSManivannan Sadhasivam /*
97398e0500eSManivannan Sadhasivam  * Returns 0 if all link frequencies used by the driver for the given number
97498e0500eSManivannan Sadhasivam  * of MIPI data lanes are mentioned in the device tree, or the value of the
97598e0500eSManivannan Sadhasivam  * first missing frequency otherwise.
97698e0500eSManivannan Sadhasivam  */
977a2706758SAndrey Konovalov static s64 imx290_check_link_freqs(const struct imx290 *imx290,
978a2706758SAndrey Konovalov 				   const struct v4l2_fwnode_endpoint *ep)
97998e0500eSManivannan Sadhasivam {
98098e0500eSManivannan Sadhasivam 	int i, j;
98198e0500eSManivannan Sadhasivam 	const s64 *freqs = imx290_link_freqs_ptr(imx290);
98298e0500eSManivannan Sadhasivam 	int freqs_count = imx290_link_freqs_num(imx290);
98398e0500eSManivannan Sadhasivam 
98498e0500eSManivannan Sadhasivam 	for (i = 0; i < freqs_count; i++) {
985a2706758SAndrey Konovalov 		for (j = 0; j < ep->nr_of_link_frequencies; j++)
986a2706758SAndrey Konovalov 			if (freqs[i] == ep->link_frequencies[j])
98798e0500eSManivannan Sadhasivam 				break;
988a2706758SAndrey Konovalov 		if (j == ep->nr_of_link_frequencies)
98998e0500eSManivannan Sadhasivam 			return freqs[i];
99098e0500eSManivannan Sadhasivam 	}
99198e0500eSManivannan Sadhasivam 	return 0;
99298e0500eSManivannan Sadhasivam }
99398e0500eSManivannan Sadhasivam 
994828dbc29SManivannan Sadhasivam static int imx290_probe(struct i2c_client *client)
995828dbc29SManivannan Sadhasivam {
996828dbc29SManivannan Sadhasivam 	struct device *dev = &client->dev;
997828dbc29SManivannan Sadhasivam 	struct fwnode_handle *endpoint;
998a2706758SAndrey Konovalov 	/* Only CSI2 is supported for now: */
999a2706758SAndrey Konovalov 	struct v4l2_fwnode_endpoint ep = {
1000a2706758SAndrey Konovalov 		.bus_type = V4L2_MBUS_CSI2_DPHY
1001a2706758SAndrey Konovalov 	};
1002828dbc29SManivannan Sadhasivam 	struct imx290 *imx290;
1003828dbc29SManivannan Sadhasivam 	u32 xclk_freq;
100498e0500eSManivannan Sadhasivam 	s64 fq;
1005828dbc29SManivannan Sadhasivam 	int ret;
1006828dbc29SManivannan Sadhasivam 
1007828dbc29SManivannan Sadhasivam 	imx290 = devm_kzalloc(dev, sizeof(*imx290), GFP_KERNEL);
1008828dbc29SManivannan Sadhasivam 	if (!imx290)
1009828dbc29SManivannan Sadhasivam 		return -ENOMEM;
1010828dbc29SManivannan Sadhasivam 
1011828dbc29SManivannan Sadhasivam 	imx290->dev = dev;
1012828dbc29SManivannan Sadhasivam 	imx290->regmap = devm_regmap_init_i2c(client, &imx290_regmap_config);
1013828dbc29SManivannan Sadhasivam 	if (IS_ERR(imx290->regmap)) {
1014828dbc29SManivannan Sadhasivam 		dev_err(dev, "Unable to initialize I2C\n");
1015828dbc29SManivannan Sadhasivam 		return -ENODEV;
1016828dbc29SManivannan Sadhasivam 	}
1017828dbc29SManivannan Sadhasivam 
1018828dbc29SManivannan Sadhasivam 	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
1019828dbc29SManivannan Sadhasivam 	if (!endpoint) {
1020828dbc29SManivannan Sadhasivam 		dev_err(dev, "Endpoint node not found\n");
1021828dbc29SManivannan Sadhasivam 		return -EINVAL;
1022828dbc29SManivannan Sadhasivam 	}
1023828dbc29SManivannan Sadhasivam 
1024a2706758SAndrey Konovalov 	ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep);
1025828dbc29SManivannan Sadhasivam 	fwnode_handle_put(endpoint);
1026a2706758SAndrey Konovalov 	if (ret == -ENXIO) {
1027a2706758SAndrey Konovalov 		dev_err(dev, "Unsupported bus type, should be CSI2\n");
1028a2706758SAndrey Konovalov 		goto free_err;
1029a2706758SAndrey Konovalov 	} else if (ret) {
1030828dbc29SManivannan Sadhasivam 		dev_err(dev, "Parsing endpoint node failed\n");
1031828dbc29SManivannan Sadhasivam 		goto free_err;
1032828dbc29SManivannan Sadhasivam 	}
1033828dbc29SManivannan Sadhasivam 
103497589ad6SManivannan Sadhasivam 	/* Get number of data lanes */
1035a2706758SAndrey Konovalov 	imx290->nlanes = ep.bus.mipi_csi2.num_data_lanes;
103697589ad6SManivannan Sadhasivam 	if (imx290->nlanes != 2 && imx290->nlanes != 4) {
103797589ad6SManivannan Sadhasivam 		dev_err(dev, "Invalid data lanes: %d\n", imx290->nlanes);
103897589ad6SManivannan Sadhasivam 		ret = -EINVAL;
103997589ad6SManivannan Sadhasivam 		goto free_err;
104097589ad6SManivannan Sadhasivam 	}
104197589ad6SManivannan Sadhasivam 
104297589ad6SManivannan Sadhasivam 	dev_dbg(dev, "Using %u data lanes\n", imx290->nlanes);
104397589ad6SManivannan Sadhasivam 
1044a2706758SAndrey Konovalov 	if (!ep.nr_of_link_frequencies) {
1045828dbc29SManivannan Sadhasivam 		dev_err(dev, "link-frequency property not found in DT\n");
1046828dbc29SManivannan Sadhasivam 		ret = -EINVAL;
1047828dbc29SManivannan Sadhasivam 		goto free_err;
1048828dbc29SManivannan Sadhasivam 	}
1049828dbc29SManivannan Sadhasivam 
105098e0500eSManivannan Sadhasivam 	/* Check that link frequences for all the modes are in device tree */
1051a2706758SAndrey Konovalov 	fq = imx290_check_link_freqs(imx290, &ep);
105298e0500eSManivannan Sadhasivam 	if (fq) {
105398e0500eSManivannan Sadhasivam 		dev_err(dev, "Link frequency of %lld is not supported\n", fq);
1054828dbc29SManivannan Sadhasivam 		ret = -EINVAL;
1055828dbc29SManivannan Sadhasivam 		goto free_err;
1056828dbc29SManivannan Sadhasivam 	}
1057828dbc29SManivannan Sadhasivam 
1058828dbc29SManivannan Sadhasivam 	/* get system clock (xclk) */
1059828dbc29SManivannan Sadhasivam 	imx290->xclk = devm_clk_get(dev, "xclk");
1060828dbc29SManivannan Sadhasivam 	if (IS_ERR(imx290->xclk)) {
1061828dbc29SManivannan Sadhasivam 		dev_err(dev, "Could not get xclk");
1062828dbc29SManivannan Sadhasivam 		ret = PTR_ERR(imx290->xclk);
1063828dbc29SManivannan Sadhasivam 		goto free_err;
1064828dbc29SManivannan Sadhasivam 	}
1065828dbc29SManivannan Sadhasivam 
1066828dbc29SManivannan Sadhasivam 	ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
1067828dbc29SManivannan Sadhasivam 				       &xclk_freq);
1068828dbc29SManivannan Sadhasivam 	if (ret) {
1069828dbc29SManivannan Sadhasivam 		dev_err(dev, "Could not get xclk frequency\n");
1070828dbc29SManivannan Sadhasivam 		goto free_err;
1071828dbc29SManivannan Sadhasivam 	}
1072828dbc29SManivannan Sadhasivam 
1073828dbc29SManivannan Sadhasivam 	/* external clock must be 37.125 MHz */
1074828dbc29SManivannan Sadhasivam 	if (xclk_freq != 37125000) {
1075828dbc29SManivannan Sadhasivam 		dev_err(dev, "External clock frequency %u is not supported\n",
1076828dbc29SManivannan Sadhasivam 			xclk_freq);
1077828dbc29SManivannan Sadhasivam 		ret = -EINVAL;
1078828dbc29SManivannan Sadhasivam 		goto free_err;
1079828dbc29SManivannan Sadhasivam 	}
1080828dbc29SManivannan Sadhasivam 
1081828dbc29SManivannan Sadhasivam 	ret = clk_set_rate(imx290->xclk, xclk_freq);
1082828dbc29SManivannan Sadhasivam 	if (ret) {
1083828dbc29SManivannan Sadhasivam 		dev_err(dev, "Could not set xclk frequency\n");
1084828dbc29SManivannan Sadhasivam 		goto free_err;
1085828dbc29SManivannan Sadhasivam 	}
1086828dbc29SManivannan Sadhasivam 
1087828dbc29SManivannan Sadhasivam 	ret = imx290_get_regulators(dev, imx290);
1088828dbc29SManivannan Sadhasivam 	if (ret < 0) {
1089828dbc29SManivannan Sadhasivam 		dev_err(dev, "Cannot get regulators\n");
1090828dbc29SManivannan Sadhasivam 		goto free_err;
1091828dbc29SManivannan Sadhasivam 	}
1092828dbc29SManivannan Sadhasivam 
10933909a92dSAndrey Konovalov 	imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset",
10943909a92dSAndrey Konovalov 						   GPIOD_OUT_HIGH);
1095828dbc29SManivannan Sadhasivam 	if (IS_ERR(imx290->rst_gpio)) {
1096828dbc29SManivannan Sadhasivam 		dev_err(dev, "Cannot get reset gpio\n");
1097828dbc29SManivannan Sadhasivam 		ret = PTR_ERR(imx290->rst_gpio);
1098828dbc29SManivannan Sadhasivam 		goto free_err;
1099828dbc29SManivannan Sadhasivam 	}
1100828dbc29SManivannan Sadhasivam 
1101828dbc29SManivannan Sadhasivam 	mutex_init(&imx290->lock);
1102828dbc29SManivannan Sadhasivam 
110398e0500eSManivannan Sadhasivam 	/*
110498e0500eSManivannan Sadhasivam 	 * Initialize the frame format. In particular, imx290->current_mode
110598e0500eSManivannan Sadhasivam 	 * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() call
110698e0500eSManivannan Sadhasivam 	 * below relies on these fields.
110798e0500eSManivannan Sadhasivam 	 */
110898e0500eSManivannan Sadhasivam 	imx290_entity_init_cfg(&imx290->sd, NULL);
110998e0500eSManivannan Sadhasivam 
111072c87b7aSLaurent Pinchart 	ret = imx290_ctrl_init(imx290);
111172c87b7aSLaurent Pinchart 	if (ret < 0) {
111272c87b7aSLaurent Pinchart 		dev_err(dev, "Control initialization error %d\n", ret);
111372c87b7aSLaurent Pinchart 		goto free_mutex;
1114828dbc29SManivannan Sadhasivam 	}
1115828dbc29SManivannan Sadhasivam 
1116828dbc29SManivannan Sadhasivam 	v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
1117828dbc29SManivannan Sadhasivam 	imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
1118828dbc29SManivannan Sadhasivam 	imx290->sd.dev = &client->dev;
1119828dbc29SManivannan Sadhasivam 	imx290->sd.entity.ops = &imx290_subdev_entity_ops;
1120828dbc29SManivannan Sadhasivam 	imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
1121828dbc29SManivannan Sadhasivam 
1122828dbc29SManivannan Sadhasivam 	imx290->pad.flags = MEDIA_PAD_FL_SOURCE;
1123828dbc29SManivannan Sadhasivam 	ret = media_entity_pads_init(&imx290->sd.entity, 1, &imx290->pad);
1124828dbc29SManivannan Sadhasivam 	if (ret < 0) {
1125828dbc29SManivannan Sadhasivam 		dev_err(dev, "Could not register media entity\n");
1126828dbc29SManivannan Sadhasivam 		goto free_ctrl;
1127828dbc29SManivannan Sadhasivam 	}
1128828dbc29SManivannan Sadhasivam 
1129828dbc29SManivannan Sadhasivam 	ret = v4l2_async_register_subdev(&imx290->sd);
1130828dbc29SManivannan Sadhasivam 	if (ret < 0) {
1131828dbc29SManivannan Sadhasivam 		dev_err(dev, "Could not register v4l2 device\n");
1132828dbc29SManivannan Sadhasivam 		goto free_entity;
1133828dbc29SManivannan Sadhasivam 	}
1134828dbc29SManivannan Sadhasivam 
1135828dbc29SManivannan Sadhasivam 	/* Power on the device to match runtime PM state below */
1136828dbc29SManivannan Sadhasivam 	ret = imx290_power_on(dev);
1137828dbc29SManivannan Sadhasivam 	if (ret < 0) {
1138828dbc29SManivannan Sadhasivam 		dev_err(dev, "Could not power on the device\n");
1139828dbc29SManivannan Sadhasivam 		goto free_entity;
1140828dbc29SManivannan Sadhasivam 	}
1141828dbc29SManivannan Sadhasivam 
1142828dbc29SManivannan Sadhasivam 	pm_runtime_set_active(dev);
1143828dbc29SManivannan Sadhasivam 	pm_runtime_enable(dev);
1144828dbc29SManivannan Sadhasivam 	pm_runtime_idle(dev);
1145828dbc29SManivannan Sadhasivam 
1146a2706758SAndrey Konovalov 	v4l2_fwnode_endpoint_free(&ep);
1147828dbc29SManivannan Sadhasivam 
1148828dbc29SManivannan Sadhasivam 	return 0;
1149828dbc29SManivannan Sadhasivam 
1150828dbc29SManivannan Sadhasivam free_entity:
1151828dbc29SManivannan Sadhasivam 	media_entity_cleanup(&imx290->sd.entity);
1152828dbc29SManivannan Sadhasivam free_ctrl:
1153828dbc29SManivannan Sadhasivam 	v4l2_ctrl_handler_free(&imx290->ctrls);
115472c87b7aSLaurent Pinchart free_mutex:
1155828dbc29SManivannan Sadhasivam 	mutex_destroy(&imx290->lock);
1156828dbc29SManivannan Sadhasivam free_err:
1157a2706758SAndrey Konovalov 	v4l2_fwnode_endpoint_free(&ep);
1158828dbc29SManivannan Sadhasivam 
1159828dbc29SManivannan Sadhasivam 	return ret;
1160828dbc29SManivannan Sadhasivam }
1161828dbc29SManivannan Sadhasivam 
1162ed5c2f5fSUwe Kleine-König static void imx290_remove(struct i2c_client *client)
1163828dbc29SManivannan Sadhasivam {
1164828dbc29SManivannan Sadhasivam 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
1165828dbc29SManivannan Sadhasivam 	struct imx290 *imx290 = to_imx290(sd);
1166828dbc29SManivannan Sadhasivam 
1167828dbc29SManivannan Sadhasivam 	v4l2_async_unregister_subdev(sd);
1168828dbc29SManivannan Sadhasivam 	media_entity_cleanup(&sd->entity);
1169828dbc29SManivannan Sadhasivam 	v4l2_ctrl_handler_free(sd->ctrl_handler);
1170828dbc29SManivannan Sadhasivam 
1171828dbc29SManivannan Sadhasivam 	mutex_destroy(&imx290->lock);
1172828dbc29SManivannan Sadhasivam 
1173828dbc29SManivannan Sadhasivam 	pm_runtime_disable(imx290->dev);
1174828dbc29SManivannan Sadhasivam 	if (!pm_runtime_status_suspended(imx290->dev))
1175828dbc29SManivannan Sadhasivam 		imx290_power_off(imx290->dev);
1176828dbc29SManivannan Sadhasivam 	pm_runtime_set_suspended(imx290->dev);
1177828dbc29SManivannan Sadhasivam }
1178828dbc29SManivannan Sadhasivam 
1179828dbc29SManivannan Sadhasivam static const struct of_device_id imx290_of_match[] = {
1180828dbc29SManivannan Sadhasivam 	{ .compatible = "sony,imx290" },
1181828dbc29SManivannan Sadhasivam 	{ /* sentinel */ }
1182828dbc29SManivannan Sadhasivam };
1183828dbc29SManivannan Sadhasivam MODULE_DEVICE_TABLE(of, imx290_of_match);
1184828dbc29SManivannan Sadhasivam 
1185828dbc29SManivannan Sadhasivam static struct i2c_driver imx290_i2c_driver = {
1186828dbc29SManivannan Sadhasivam 	.probe_new  = imx290_probe,
1187828dbc29SManivannan Sadhasivam 	.remove = imx290_remove,
1188828dbc29SManivannan Sadhasivam 	.driver = {
1189828dbc29SManivannan Sadhasivam 		.name  = "imx290",
1190828dbc29SManivannan Sadhasivam 		.pm = &imx290_pm_ops,
1191828dbc29SManivannan Sadhasivam 		.of_match_table = of_match_ptr(imx290_of_match),
1192828dbc29SManivannan Sadhasivam 	},
1193828dbc29SManivannan Sadhasivam };
1194828dbc29SManivannan Sadhasivam 
1195828dbc29SManivannan Sadhasivam module_i2c_driver(imx290_i2c_driver);
1196828dbc29SManivannan Sadhasivam 
1197828dbc29SManivannan Sadhasivam MODULE_DESCRIPTION("Sony IMX290 CMOS Image Sensor Driver");
1198828dbc29SManivannan Sadhasivam MODULE_AUTHOR("FRAMOS GmbH");
1199828dbc29SManivannan Sadhasivam MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
1200828dbc29SManivannan Sadhasivam MODULE_LICENSE("GPL v2");
1201