xref: /openbmc/linux/drivers/media/i2c/imx290.c (revision 70bbf56a)
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 
108b4ab57b0SLaurent Pinchart 
109b4ab57b0SLaurent Pinchart /*
110b4ab57b0SLaurent Pinchart  * The IMX290 pixel array is organized as follows:
111b4ab57b0SLaurent Pinchart  *
112b4ab57b0SLaurent Pinchart  *     +------------------------------------+
113b4ab57b0SLaurent Pinchart  *     |           Optical Black            |     }  Vertical effective optical black (10)
114b4ab57b0SLaurent Pinchart  * +---+------------------------------------+---+
115b4ab57b0SLaurent Pinchart  * |   |                                    |   | }  Effective top margin (8)
116b4ab57b0SLaurent Pinchart  * |   |   +----------------------------+   |   | \
117b4ab57b0SLaurent Pinchart  * |   |   |                            |   |   |  |
118b4ab57b0SLaurent Pinchart  * |   |   |                            |   |   |  |
119b4ab57b0SLaurent Pinchart  * |   |   |                            |   |   |  |
120b4ab57b0SLaurent Pinchart  * |   |   |    Recording Pixel Area    |   |   |  | Recommended height (1080)
121b4ab57b0SLaurent Pinchart  * |   |   |                            |   |   |  |
122b4ab57b0SLaurent Pinchart  * |   |   |                            |   |   |  |
123b4ab57b0SLaurent Pinchart  * |   |   |                            |   |   |  |
124b4ab57b0SLaurent Pinchart  * |   |   +----------------------------+   |   | /
125b4ab57b0SLaurent Pinchart  * |   |                                    |   | }  Effective bottom margin (9)
126b4ab57b0SLaurent Pinchart  * +---+------------------------------------+---+
127b4ab57b0SLaurent Pinchart  *  <-> <-> <--------------------------> <-> <->
128b4ab57b0SLaurent Pinchart  *                                            \----  Ignored right margin (4)
129b4ab57b0SLaurent Pinchart  *                                        \--------  Effective right margin (9)
130b4ab57b0SLaurent Pinchart  *                       \-------------------------  Recommended width (1920)
131b4ab57b0SLaurent Pinchart  *       \-----------------------------------------  Effective left margin (8)
132b4ab57b0SLaurent Pinchart  *   \---------------------------------------------  Ignored left margin (4)
133b4ab57b0SLaurent Pinchart  *
134b4ab57b0SLaurent Pinchart  * The optical black lines are output over CSI-2 with a separate data type.
135b4ab57b0SLaurent Pinchart  *
136b4ab57b0SLaurent Pinchart  * The pixel array is meant to have 1920x1080 usable pixels after image
137b4ab57b0SLaurent Pinchart  * processing in an ISP. It has 8 (9) extra active pixels usable for color
138b4ab57b0SLaurent Pinchart  * processing in the ISP on the top and left (bottom and right) sides of the
139b4ab57b0SLaurent Pinchart  * image. In addition, 4 additional pixels are present on the left and right
140b4ab57b0SLaurent Pinchart  * sides of the image, documented as "ignored area".
141b4ab57b0SLaurent Pinchart  *
142b4ab57b0SLaurent Pinchart  * As far as is understood, all pixels of the pixel array (ignored area, color
143b4ab57b0SLaurent Pinchart  * processing margins and recording area) can be output by the sensor.
144b4ab57b0SLaurent Pinchart  */
145b4ab57b0SLaurent Pinchart 
146b4ab57b0SLaurent Pinchart #define IMX290_PIXEL_ARRAY_WIDTH			1945
147b4ab57b0SLaurent Pinchart #define IMX290_PIXEL_ARRAY_HEIGHT			1097
148b4ab57b0SLaurent Pinchart #define IMX920_PIXEL_ARRAY_MARGIN_LEFT			12
149b4ab57b0SLaurent Pinchart #define IMX920_PIXEL_ARRAY_MARGIN_RIGHT			13
150b4ab57b0SLaurent Pinchart #define IMX920_PIXEL_ARRAY_MARGIN_TOP			8
151b4ab57b0SLaurent Pinchart #define IMX920_PIXEL_ARRAY_MARGIN_BOTTOM		9
152b4ab57b0SLaurent Pinchart #define IMX290_PIXEL_ARRAY_RECORDING_WIDTH		1920
153b4ab57b0SLaurent Pinchart #define IMX290_PIXEL_ARRAY_RECORDING_HEIGHT		1080
154b4ab57b0SLaurent Pinchart 
155cb7e1c8dSLaurent Pinchart #define IMX290_NUM_SUPPLIES				3
156828dbc29SManivannan Sadhasivam 
157828dbc29SManivannan Sadhasivam struct imx290_regval {
158e70abe88SLaurent Pinchart 	u32 reg;
15979d99ae8SLaurent Pinchart 	u32 val;
160828dbc29SManivannan Sadhasivam };
161828dbc29SManivannan Sadhasivam 
162828dbc29SManivannan Sadhasivam struct imx290_mode {
163828dbc29SManivannan Sadhasivam 	u32 width;
164828dbc29SManivannan Sadhasivam 	u32 height;
16597589ad6SManivannan Sadhasivam 	u32 hmax;
16698e0500eSManivannan Sadhasivam 	u8 link_freq_index;
167828dbc29SManivannan Sadhasivam 
168828dbc29SManivannan Sadhasivam 	const struct imx290_regval *data;
169828dbc29SManivannan Sadhasivam 	u32 data_size;
170828dbc29SManivannan Sadhasivam };
171828dbc29SManivannan Sadhasivam 
172828dbc29SManivannan Sadhasivam struct imx290 {
173828dbc29SManivannan Sadhasivam 	struct device *dev;
174828dbc29SManivannan Sadhasivam 	struct clk *xclk;
175828dbc29SManivannan Sadhasivam 	struct regmap *regmap;
17697589ad6SManivannan Sadhasivam 	u8 nlanes;
177c566ac01SManivannan Sadhasivam 	u8 bpp;
178828dbc29SManivannan Sadhasivam 
179828dbc29SManivannan Sadhasivam 	struct v4l2_subdev sd;
180828dbc29SManivannan Sadhasivam 	struct media_pad pad;
181828dbc29SManivannan Sadhasivam 	struct v4l2_mbus_framefmt current_format;
182828dbc29SManivannan Sadhasivam 	const struct imx290_mode *current_mode;
183828dbc29SManivannan Sadhasivam 
184828dbc29SManivannan Sadhasivam 	struct regulator_bulk_data supplies[IMX290_NUM_SUPPLIES];
185828dbc29SManivannan Sadhasivam 	struct gpio_desc *rst_gpio;
186828dbc29SManivannan Sadhasivam 
187828dbc29SManivannan Sadhasivam 	struct v4l2_ctrl_handler ctrls;
188828dbc29SManivannan Sadhasivam 	struct v4l2_ctrl *link_freq;
189828dbc29SManivannan Sadhasivam 	struct v4l2_ctrl *pixel_rate;
1900c3b56c9SLaurent Pinchart 	struct v4l2_ctrl *hblank;
1910c3b56c9SLaurent Pinchart 	struct v4l2_ctrl *vblank;
192828dbc29SManivannan Sadhasivam 
193828dbc29SManivannan Sadhasivam 	struct mutex lock;
194828dbc29SManivannan Sadhasivam };
195828dbc29SManivannan Sadhasivam 
196cb7e1c8dSLaurent Pinchart static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
197cb7e1c8dSLaurent Pinchart {
198cb7e1c8dSLaurent Pinchart 	return container_of(_sd, struct imx290, sd);
199cb7e1c8dSLaurent Pinchart }
200828dbc29SManivannan Sadhasivam 
201cb7e1c8dSLaurent Pinchart /* -----------------------------------------------------------------------------
202cb7e1c8dSLaurent Pinchart  * Modes and formats
203cb7e1c8dSLaurent Pinchart  */
204a58df1f9SManivannan Sadhasivam 
205828dbc29SManivannan Sadhasivam static const struct imx290_regval imx290_global_init_settings[] = {
20679d99ae8SLaurent Pinchart 	{ IMX290_CTRL_07, IMX290_WINMODE_1080P },
207827c7e69SLaurent Pinchart 	{ IMX290_VMAX, IMX290_VMAX_DEFAULT },
20879d99ae8SLaurent Pinchart 	{ IMX290_EXTCK_FREQ, 0x2520 },
20979d99ae8SLaurent Pinchart 	{ IMX290_WINWV_OB, 12 },
21079d99ae8SLaurent Pinchart 	{ IMX290_WINPH, 0 },
21179d99ae8SLaurent Pinchart 	{ IMX290_WINPV, 0 },
21279d99ae8SLaurent Pinchart 	{ IMX290_WINWH, 1948 },
21379d99ae8SLaurent Pinchart 	{ IMX290_WINWV, 1097 },
21479d99ae8SLaurent Pinchart 	{ IMX290_XSOUTSEL, IMX290_XSOUTSEL_XVSOUTSEL_VSYNC |
21579d99ae8SLaurent Pinchart 			   IMX290_XSOUTSEL_XHSOUTSEL_HSYNC },
216e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x300f), 0x00 },
217e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3010), 0x21 },
218e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3012), 0x64 },
2190b274ef2SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3013), 0x00 },
220e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3016), 0x09 },
221e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3070), 0x02 },
222e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3071), 0x11 },
223e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x309b), 0x10 },
224e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x309c), 0x22 },
225e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x30a2), 0x02 },
226e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x30a6), 0x20 },
227e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x30a8), 0x20 },
228e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x30aa), 0x20 },
229e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x30ac), 0x20 },
230e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x30b0), 0x43 },
231e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3119), 0x9e },
232e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x311c), 0x1e },
233e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x311e), 0x08 },
234e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3128), 0x05 },
235e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x313d), 0x83 },
236e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3150), 0x03 },
237e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x317e), 0x00 },
238e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x32b8), 0x50 },
239e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x32b9), 0x10 },
240e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x32ba), 0x00 },
241e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x32bb), 0x04 },
242e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x32c8), 0x50 },
243e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x32c9), 0x10 },
244e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x32ca), 0x00 },
245e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x32cb), 0x04 },
246e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x332c), 0xd3 },
247e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x332d), 0x10 },
248e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x332e), 0x0d },
249e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3358), 0x06 },
250e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3359), 0xe1 },
251e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x335a), 0x11 },
252e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3360), 0x1e },
253e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3361), 0x61 },
254e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3362), 0x10 },
255e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x33b0), 0x50 },
256e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x33b2), 0x1a },
257e70abe88SLaurent Pinchart 	{ IMX290_REG_8BIT(0x33b3), 0x04 },
2580b274ef2SLaurent Pinchart 	{ IMX290_REG_8BIT(0x3480), 0x49 },
259828dbc29SManivannan Sadhasivam };
260828dbc29SManivannan Sadhasivam 
261828dbc29SManivannan Sadhasivam static const struct imx290_regval imx290_1080p_settings[] = {
262828dbc29SManivannan Sadhasivam 	/* mode settings */
26379d99ae8SLaurent Pinchart 	{ IMX290_CTRL_07, IMX290_WINMODE_1080P },
26479d99ae8SLaurent Pinchart 	{ IMX290_WINWV_OB, 12 },
26579d99ae8SLaurent Pinchart 	{ IMX290_OPB_SIZE_V, 10 },
26679d99ae8SLaurent Pinchart 	{ IMX290_X_OUT_SIZE, 1920 },
26779d99ae8SLaurent Pinchart 	{ IMX290_Y_OUT_SIZE, 1080 },
26879d99ae8SLaurent Pinchart 	{ IMX290_INCKSEL1, 0x18 },
26979d99ae8SLaurent Pinchart 	{ IMX290_INCKSEL2, 0x03 },
27079d99ae8SLaurent Pinchart 	{ IMX290_INCKSEL3, 0x20 },
27179d99ae8SLaurent Pinchart 	{ IMX290_INCKSEL4, 0x01 },
27279d99ae8SLaurent Pinchart 	{ IMX290_INCKSEL5, 0x1a },
27379d99ae8SLaurent Pinchart 	{ IMX290_INCKSEL6, 0x1a },
274828dbc29SManivannan Sadhasivam 	/* data rate settings */
27579d99ae8SLaurent Pinchart 	{ IMX290_REPETITION, 0x10 },
27679d99ae8SLaurent Pinchart 	{ IMX290_TCLKPOST, 87 },
27779d99ae8SLaurent Pinchart 	{ IMX290_THSZERO, 55 },
27879d99ae8SLaurent Pinchart 	{ IMX290_THSPREPARE, 31 },
27979d99ae8SLaurent Pinchart 	{ IMX290_TCLKTRAIL, 31 },
28079d99ae8SLaurent Pinchart 	{ IMX290_THSTRAIL, 31 },
28179d99ae8SLaurent Pinchart 	{ IMX290_TCLKZERO, 119 },
28279d99ae8SLaurent Pinchart 	{ IMX290_TCLKPREPARE, 31 },
28379d99ae8SLaurent Pinchart 	{ IMX290_TLPX, 23 },
284828dbc29SManivannan Sadhasivam };
285828dbc29SManivannan Sadhasivam 
286828dbc29SManivannan Sadhasivam static const struct imx290_regval imx290_720p_settings[] = {
287828dbc29SManivannan Sadhasivam 	/* mode settings */
28879d99ae8SLaurent Pinchart 	{ IMX290_CTRL_07, IMX290_WINMODE_720P },
28979d99ae8SLaurent Pinchart 	{ IMX290_WINWV_OB, 6 },
29079d99ae8SLaurent Pinchart 	{ IMX290_OPB_SIZE_V, 4 },
29179d99ae8SLaurent Pinchart 	{ IMX290_X_OUT_SIZE, 1280 },
29279d99ae8SLaurent Pinchart 	{ IMX290_Y_OUT_SIZE, 720 },
29379d99ae8SLaurent Pinchart 	{ IMX290_INCKSEL1, 0x20 },
29479d99ae8SLaurent Pinchart 	{ IMX290_INCKSEL2, 0x00 },
29579d99ae8SLaurent Pinchart 	{ IMX290_INCKSEL3, 0x20 },
29679d99ae8SLaurent Pinchart 	{ IMX290_INCKSEL4, 0x01 },
29779d99ae8SLaurent Pinchart 	{ IMX290_INCKSEL5, 0x1a },
29879d99ae8SLaurent Pinchart 	{ IMX290_INCKSEL6, 0x1a },
299828dbc29SManivannan Sadhasivam 	/* data rate settings */
30079d99ae8SLaurent Pinchart 	{ IMX290_REPETITION, 0x10 },
30179d99ae8SLaurent Pinchart 	{ IMX290_TCLKPOST, 79 },
30279d99ae8SLaurent Pinchart 	{ IMX290_THSZERO, 47 },
30379d99ae8SLaurent Pinchart 	{ IMX290_THSPREPARE, 23 },
30479d99ae8SLaurent Pinchart 	{ IMX290_TCLKTRAIL, 23 },
30579d99ae8SLaurent Pinchart 	{ IMX290_THSTRAIL, 23 },
30679d99ae8SLaurent Pinchart 	{ IMX290_TCLKZERO, 87 },
30779d99ae8SLaurent Pinchart 	{ IMX290_TCLKPREPARE, 23 },
30879d99ae8SLaurent Pinchart 	{ IMX290_TLPX, 23 },
309828dbc29SManivannan Sadhasivam };
310828dbc29SManivannan Sadhasivam 
311828dbc29SManivannan Sadhasivam static const struct imx290_regval imx290_10bit_settings[] = {
31279d99ae8SLaurent Pinchart 	{ IMX290_ADBIT, IMX290_ADBIT_10BIT },
31379d99ae8SLaurent Pinchart 	{ IMX290_OUT_CTRL, IMX290_ODBIT_10BIT },
31479d99ae8SLaurent Pinchart 	{ IMX290_ADBIT1, IMX290_ADBIT1_10BIT },
31579d99ae8SLaurent Pinchart 	{ IMX290_ADBIT2, IMX290_ADBIT2_10BIT },
31679d99ae8SLaurent Pinchart 	{ IMX290_ADBIT3, IMX290_ADBIT3_10BIT },
31779d99ae8SLaurent Pinchart 	{ IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW10 },
31879d99ae8SLaurent Pinchart 	{ IMX290_BLKLEVEL, 60 },
319828dbc29SManivannan Sadhasivam };
320828dbc29SManivannan Sadhasivam 
321c566ac01SManivannan Sadhasivam static const struct imx290_regval imx290_12bit_settings[] = {
32279d99ae8SLaurent Pinchart 	{ IMX290_ADBIT, IMX290_ADBIT_12BIT },
32379d99ae8SLaurent Pinchart 	{ IMX290_OUT_CTRL, IMX290_ODBIT_12BIT },
32479d99ae8SLaurent Pinchart 	{ IMX290_ADBIT1, IMX290_ADBIT1_12BIT },
32579d99ae8SLaurent Pinchart 	{ IMX290_ADBIT2, IMX290_ADBIT2_12BIT },
32679d99ae8SLaurent Pinchart 	{ IMX290_ADBIT3, IMX290_ADBIT3_12BIT },
32779d99ae8SLaurent Pinchart 	{ IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW12 },
32879d99ae8SLaurent Pinchart 	{ IMX290_BLKLEVEL, 240 },
329c566ac01SManivannan Sadhasivam };
330c566ac01SManivannan Sadhasivam 
331828dbc29SManivannan Sadhasivam /* supported link frequencies */
33298e0500eSManivannan Sadhasivam #define FREQ_INDEX_1080P	0
33398e0500eSManivannan Sadhasivam #define FREQ_INDEX_720P		1
33498e0500eSManivannan Sadhasivam static const s64 imx290_link_freq_2lanes[] = {
33598e0500eSManivannan Sadhasivam 	[FREQ_INDEX_1080P] = 445500000,
33698e0500eSManivannan Sadhasivam 	[FREQ_INDEX_720P] = 297000000,
337828dbc29SManivannan Sadhasivam };
33898e0500eSManivannan Sadhasivam static const s64 imx290_link_freq_4lanes[] = {
33998e0500eSManivannan Sadhasivam 	[FREQ_INDEX_1080P] = 222750000,
34098e0500eSManivannan Sadhasivam 	[FREQ_INDEX_720P] = 148500000,
34198e0500eSManivannan Sadhasivam };
34298e0500eSManivannan Sadhasivam 
34398e0500eSManivannan Sadhasivam /*
34498e0500eSManivannan Sadhasivam  * In this function and in the similar ones below We rely on imx290_probe()
34598e0500eSManivannan Sadhasivam  * to ensure that nlanes is either 2 or 4.
34698e0500eSManivannan Sadhasivam  */
34798e0500eSManivannan Sadhasivam static inline const s64 *imx290_link_freqs_ptr(const struct imx290 *imx290)
34898e0500eSManivannan Sadhasivam {
34998e0500eSManivannan Sadhasivam 	if (imx290->nlanes == 2)
35098e0500eSManivannan Sadhasivam 		return imx290_link_freq_2lanes;
35198e0500eSManivannan Sadhasivam 	else
35298e0500eSManivannan Sadhasivam 		return imx290_link_freq_4lanes;
35398e0500eSManivannan Sadhasivam }
35498e0500eSManivannan Sadhasivam 
35598e0500eSManivannan Sadhasivam static inline int imx290_link_freqs_num(const struct imx290 *imx290)
35698e0500eSManivannan Sadhasivam {
35798e0500eSManivannan Sadhasivam 	if (imx290->nlanes == 2)
35898e0500eSManivannan Sadhasivam 		return ARRAY_SIZE(imx290_link_freq_2lanes);
35998e0500eSManivannan Sadhasivam 	else
36098e0500eSManivannan Sadhasivam 		return ARRAY_SIZE(imx290_link_freq_4lanes);
36198e0500eSManivannan Sadhasivam }
362828dbc29SManivannan Sadhasivam 
363828dbc29SManivannan Sadhasivam /* Mode configs */
36497589ad6SManivannan Sadhasivam static const struct imx290_mode imx290_modes_2lanes[] = {
365828dbc29SManivannan Sadhasivam 	{
366828dbc29SManivannan Sadhasivam 		.width = 1920,
367828dbc29SManivannan Sadhasivam 		.height = 1080,
36872825bc6SLaurent Pinchart 		.hmax = 4400,
36998e0500eSManivannan Sadhasivam 		.link_freq_index = FREQ_INDEX_1080P,
370828dbc29SManivannan Sadhasivam 		.data = imx290_1080p_settings,
371828dbc29SManivannan Sadhasivam 		.data_size = ARRAY_SIZE(imx290_1080p_settings),
372828dbc29SManivannan Sadhasivam 	},
373828dbc29SManivannan Sadhasivam 	{
374828dbc29SManivannan Sadhasivam 		.width = 1280,
375828dbc29SManivannan Sadhasivam 		.height = 720,
37672825bc6SLaurent Pinchart 		.hmax = 6600,
37798e0500eSManivannan Sadhasivam 		.link_freq_index = FREQ_INDEX_720P,
378828dbc29SManivannan Sadhasivam 		.data = imx290_720p_settings,
379828dbc29SManivannan Sadhasivam 		.data_size = ARRAY_SIZE(imx290_720p_settings),
380828dbc29SManivannan Sadhasivam 	},
381828dbc29SManivannan Sadhasivam };
382828dbc29SManivannan Sadhasivam 
38397589ad6SManivannan Sadhasivam static const struct imx290_mode imx290_modes_4lanes[] = {
38497589ad6SManivannan Sadhasivam 	{
38597589ad6SManivannan Sadhasivam 		.width = 1920,
38697589ad6SManivannan Sadhasivam 		.height = 1080,
38772825bc6SLaurent Pinchart 		.hmax = 2200,
38898e0500eSManivannan Sadhasivam 		.link_freq_index = FREQ_INDEX_1080P,
38997589ad6SManivannan Sadhasivam 		.data = imx290_1080p_settings,
39097589ad6SManivannan Sadhasivam 		.data_size = ARRAY_SIZE(imx290_1080p_settings),
39197589ad6SManivannan Sadhasivam 	},
39297589ad6SManivannan Sadhasivam 	{
39397589ad6SManivannan Sadhasivam 		.width = 1280,
39497589ad6SManivannan Sadhasivam 		.height = 720,
39572825bc6SLaurent Pinchart 		.hmax = 3300,
39698e0500eSManivannan Sadhasivam 		.link_freq_index = FREQ_INDEX_720P,
39797589ad6SManivannan Sadhasivam 		.data = imx290_720p_settings,
39897589ad6SManivannan Sadhasivam 		.data_size = ARRAY_SIZE(imx290_720p_settings),
39997589ad6SManivannan Sadhasivam 	},
40097589ad6SManivannan Sadhasivam };
40197589ad6SManivannan Sadhasivam 
40297589ad6SManivannan Sadhasivam static inline const struct imx290_mode *imx290_modes_ptr(const struct imx290 *imx290)
40397589ad6SManivannan Sadhasivam {
40497589ad6SManivannan Sadhasivam 	if (imx290->nlanes == 2)
40597589ad6SManivannan Sadhasivam 		return imx290_modes_2lanes;
40697589ad6SManivannan Sadhasivam 	else
40797589ad6SManivannan Sadhasivam 		return imx290_modes_4lanes;
40897589ad6SManivannan Sadhasivam }
40997589ad6SManivannan Sadhasivam 
41097589ad6SManivannan Sadhasivam static inline int imx290_modes_num(const struct imx290 *imx290)
41197589ad6SManivannan Sadhasivam {
41297589ad6SManivannan Sadhasivam 	if (imx290->nlanes == 2)
41397589ad6SManivannan Sadhasivam 		return ARRAY_SIZE(imx290_modes_2lanes);
41497589ad6SManivannan Sadhasivam 	else
41597589ad6SManivannan Sadhasivam 		return ARRAY_SIZE(imx290_modes_4lanes);
41697589ad6SManivannan Sadhasivam }
41797589ad6SManivannan Sadhasivam 
418cb7e1c8dSLaurent Pinchart struct imx290_pixfmt {
419cb7e1c8dSLaurent Pinchart 	u32 code;
420cb7e1c8dSLaurent Pinchart 	u8 bpp;
421cb7e1c8dSLaurent Pinchart };
422cb7e1c8dSLaurent Pinchart 
423cb7e1c8dSLaurent Pinchart static const struct imx290_pixfmt imx290_formats[] = {
424cb7e1c8dSLaurent Pinchart 	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
425cb7e1c8dSLaurent Pinchart 	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
426cb7e1c8dSLaurent Pinchart };
427cb7e1c8dSLaurent Pinchart 
428cb7e1c8dSLaurent Pinchart /* -----------------------------------------------------------------------------
429cb7e1c8dSLaurent Pinchart  * Register access
430cb7e1c8dSLaurent Pinchart  */
431828dbc29SManivannan Sadhasivam 
432e611f3daSLaurent Pinchart static int __always_unused imx290_read(struct imx290 *imx290, u32 addr, u32 *value)
433828dbc29SManivannan Sadhasivam {
434e70abe88SLaurent Pinchart 	u8 data[3] = { 0, 0, 0 };
435828dbc29SManivannan Sadhasivam 	int ret;
436828dbc29SManivannan Sadhasivam 
437e70abe88SLaurent Pinchart 	ret = regmap_raw_read(imx290->regmap, addr & IMX290_REG_ADDR_MASK,
438e70abe88SLaurent Pinchart 			      data, (addr >> IMX290_REG_SIZE_SHIFT) & 3);
439e70abe88SLaurent Pinchart 	if (ret < 0) {
440e70abe88SLaurent Pinchart 		dev_err(imx290->dev, "%u-bit read from 0x%04x failed: %d\n",
441e70abe88SLaurent Pinchart 			 ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8,
442e70abe88SLaurent Pinchart 			 addr & IMX290_REG_ADDR_MASK, ret);
443828dbc29SManivannan Sadhasivam 		return ret;
444828dbc29SManivannan Sadhasivam 	}
445828dbc29SManivannan Sadhasivam 
446e70abe88SLaurent Pinchart 	*value = (data[2] << 16) | (data[1] << 8) | data[0];
447828dbc29SManivannan Sadhasivam 	return 0;
448828dbc29SManivannan Sadhasivam }
449828dbc29SManivannan Sadhasivam 
450e611f3daSLaurent Pinchart static int imx290_write(struct imx290 *imx290, u32 addr, u32 value, int *err)
451828dbc29SManivannan Sadhasivam {
452e70abe88SLaurent Pinchart 	u8 data[3] = { value & 0xff, (value >> 8) & 0xff, value >> 16 };
453828dbc29SManivannan Sadhasivam 	int ret;
454828dbc29SManivannan Sadhasivam 
455e611f3daSLaurent Pinchart 	if (err && *err)
456e611f3daSLaurent Pinchart 		return *err;
457e611f3daSLaurent Pinchart 
458e70abe88SLaurent Pinchart 	ret = regmap_raw_write(imx290->regmap, addr & IMX290_REG_ADDR_MASK,
459e70abe88SLaurent Pinchart 			       data, (addr >> IMX290_REG_SIZE_SHIFT) & 3);
460e611f3daSLaurent Pinchart 	if (ret < 0) {
461e70abe88SLaurent Pinchart 		dev_err(imx290->dev, "%u-bit write to 0x%04x failed: %d\n",
462e70abe88SLaurent Pinchart 			 ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8,
463e70abe88SLaurent Pinchart 			 addr & IMX290_REG_ADDR_MASK, ret);
464e611f3daSLaurent Pinchart 		if (err)
465e611f3daSLaurent Pinchart 			*err = ret;
466e611f3daSLaurent Pinchart 	}
467828dbc29SManivannan Sadhasivam 
468828dbc29SManivannan Sadhasivam 	return ret;
469828dbc29SManivannan Sadhasivam }
470828dbc29SManivannan Sadhasivam 
471828dbc29SManivannan Sadhasivam static int imx290_set_register_array(struct imx290 *imx290,
472828dbc29SManivannan Sadhasivam 				     const struct imx290_regval *settings,
473828dbc29SManivannan Sadhasivam 				     unsigned int num_settings)
474828dbc29SManivannan Sadhasivam {
475828dbc29SManivannan Sadhasivam 	unsigned int i;
476828dbc29SManivannan Sadhasivam 	int ret;
477828dbc29SManivannan Sadhasivam 
478828dbc29SManivannan Sadhasivam 	for (i = 0; i < num_settings; ++i, ++settings) {
479e611f3daSLaurent Pinchart 		ret = imx290_write(imx290, settings->reg, settings->val, NULL);
480828dbc29SManivannan Sadhasivam 		if (ret < 0)
481828dbc29SManivannan Sadhasivam 			return ret;
482828dbc29SManivannan Sadhasivam 	}
483828dbc29SManivannan Sadhasivam 
4846544af9bSManivannan Sadhasivam 	/* Provide 10ms settle time */
4858e5652aeSAndrey Konovalov 	usleep_range(10000, 11000);
4866544af9bSManivannan Sadhasivam 
487828dbc29SManivannan Sadhasivam 	return 0;
488828dbc29SManivannan Sadhasivam }
489828dbc29SManivannan Sadhasivam 
490cb7e1c8dSLaurent Pinchart static int imx290_set_data_lanes(struct imx290 *imx290)
491828dbc29SManivannan Sadhasivam {
492cb7e1c8dSLaurent Pinchart 	int ret = 0, laneval, frsel;
493828dbc29SManivannan Sadhasivam 
494cb7e1c8dSLaurent Pinchart 	switch (imx290->nlanes) {
495cb7e1c8dSLaurent Pinchart 	case 2:
496cb7e1c8dSLaurent Pinchart 		laneval = 0x01;
497cb7e1c8dSLaurent Pinchart 		frsel = 0x02;
498cb7e1c8dSLaurent Pinchart 		break;
499cb7e1c8dSLaurent Pinchart 	case 4:
500cb7e1c8dSLaurent Pinchart 		laneval = 0x03;
501cb7e1c8dSLaurent Pinchart 		frsel = 0x01;
502cb7e1c8dSLaurent Pinchart 		break;
503cb7e1c8dSLaurent Pinchart 	default:
504cb7e1c8dSLaurent Pinchart 		/*
505cb7e1c8dSLaurent Pinchart 		 * We should never hit this since the data lane count is
506cb7e1c8dSLaurent Pinchart 		 * validated in probe itself
507cb7e1c8dSLaurent Pinchart 		 */
508cb7e1c8dSLaurent Pinchart 		dev_err(imx290->dev, "Lane configuration not supported\n");
509cb7e1c8dSLaurent Pinchart 		return -EINVAL;
510828dbc29SManivannan Sadhasivam 	}
511828dbc29SManivannan Sadhasivam 
512cb7e1c8dSLaurent Pinchart 	imx290_write(imx290, IMX290_PHY_LANE_NUM, laneval, &ret);
513cb7e1c8dSLaurent Pinchart 	imx290_write(imx290, IMX290_CSI_LANE_MODE, laneval, &ret);
514cb7e1c8dSLaurent Pinchart 	imx290_write(imx290, IMX290_FR_FDG_SEL, frsel, &ret);
515cb7e1c8dSLaurent Pinchart 
516cb7e1c8dSLaurent Pinchart 	return ret;
517cb7e1c8dSLaurent Pinchart }
518cb7e1c8dSLaurent Pinchart 
519cb7e1c8dSLaurent Pinchart static int imx290_write_current_format(struct imx290 *imx290)
520cb7e1c8dSLaurent Pinchart {
521cb7e1c8dSLaurent Pinchart 	int ret;
522cb7e1c8dSLaurent Pinchart 
523cb7e1c8dSLaurent Pinchart 	switch (imx290->current_format.code) {
524cb7e1c8dSLaurent Pinchart 	case MEDIA_BUS_FMT_SRGGB10_1X10:
525cb7e1c8dSLaurent Pinchart 		ret = imx290_set_register_array(imx290, imx290_10bit_settings,
526cb7e1c8dSLaurent Pinchart 						ARRAY_SIZE(
527cb7e1c8dSLaurent Pinchart 							imx290_10bit_settings));
528cb7e1c8dSLaurent Pinchart 		if (ret < 0) {
529cb7e1c8dSLaurent Pinchart 			dev_err(imx290->dev, "Could not set format registers\n");
530cb7e1c8dSLaurent Pinchart 			return ret;
531cb7e1c8dSLaurent Pinchart 		}
532cb7e1c8dSLaurent Pinchart 		break;
533cb7e1c8dSLaurent Pinchart 	case MEDIA_BUS_FMT_SRGGB12_1X12:
534cb7e1c8dSLaurent Pinchart 		ret = imx290_set_register_array(imx290, imx290_12bit_settings,
535cb7e1c8dSLaurent Pinchart 						ARRAY_SIZE(
536cb7e1c8dSLaurent Pinchart 							imx290_12bit_settings));
537cb7e1c8dSLaurent Pinchart 		if (ret < 0) {
538cb7e1c8dSLaurent Pinchart 			dev_err(imx290->dev, "Could not set format registers\n");
539cb7e1c8dSLaurent Pinchart 			return ret;
540cb7e1c8dSLaurent Pinchart 		}
541cb7e1c8dSLaurent Pinchart 		break;
542cb7e1c8dSLaurent Pinchart 	default:
543cb7e1c8dSLaurent Pinchart 		dev_err(imx290->dev, "Unknown pixel format\n");
544cb7e1c8dSLaurent Pinchart 		return -EINVAL;
545cb7e1c8dSLaurent Pinchart 	}
546cb7e1c8dSLaurent Pinchart 
547cb7e1c8dSLaurent Pinchart 	return 0;
548cb7e1c8dSLaurent Pinchart }
549cb7e1c8dSLaurent Pinchart 
550cb7e1c8dSLaurent Pinchart static s64 imx290_get_link_freq(struct imx290 *imx290)
551cb7e1c8dSLaurent Pinchart {
552*70bbf56aSLaurent Pinchart 	u8 index = imx290->current_mode->link_freq_index;
553cb7e1c8dSLaurent Pinchart 
554cb7e1c8dSLaurent Pinchart 	return *(imx290_link_freqs_ptr(imx290) + index);
555cb7e1c8dSLaurent Pinchart }
556cb7e1c8dSLaurent Pinchart 
557cb7e1c8dSLaurent Pinchart static u64 imx290_calc_pixel_rate(struct imx290 *imx290)
558cb7e1c8dSLaurent Pinchart {
559cb7e1c8dSLaurent Pinchart 	s64 link_freq = imx290_get_link_freq(imx290);
560cb7e1c8dSLaurent Pinchart 	u8 nlanes = imx290->nlanes;
561cb7e1c8dSLaurent Pinchart 	u64 pixel_rate;
562cb7e1c8dSLaurent Pinchart 
563cb7e1c8dSLaurent Pinchart 	/* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */
564cb7e1c8dSLaurent Pinchart 	pixel_rate = link_freq * 2 * nlanes;
565cb7e1c8dSLaurent Pinchart 	do_div(pixel_rate, imx290->bpp);
566cb7e1c8dSLaurent Pinchart 	return pixel_rate;
567cb7e1c8dSLaurent Pinchart }
568cb7e1c8dSLaurent Pinchart 
569cb7e1c8dSLaurent Pinchart /* ----------------------------------------------------------------------------
570cb7e1c8dSLaurent Pinchart  * Controls
571cb7e1c8dSLaurent Pinchart  */
572cb7e1c8dSLaurent Pinchart 
573828dbc29SManivannan Sadhasivam static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
574828dbc29SManivannan Sadhasivam {
575828dbc29SManivannan Sadhasivam 	struct imx290 *imx290 = container_of(ctrl->handler,
576828dbc29SManivannan Sadhasivam 					     struct imx290, ctrls);
577828dbc29SManivannan Sadhasivam 	int ret = 0;
578828dbc29SManivannan Sadhasivam 
579828dbc29SManivannan Sadhasivam 	/* V4L2 controls values will be applied only when power is already up */
580828dbc29SManivannan Sadhasivam 	if (!pm_runtime_get_if_in_use(imx290->dev))
581828dbc29SManivannan Sadhasivam 		return 0;
582828dbc29SManivannan Sadhasivam 
583828dbc29SManivannan Sadhasivam 	switch (ctrl->id) {
5843dd10515SLaurent Pinchart 	case V4L2_CID_ANALOGUE_GAIN:
585e611f3daSLaurent Pinchart 		ret = imx290_write(imx290, IMX290_GAIN, ctrl->val, NULL);
586828dbc29SManivannan Sadhasivam 		break;
587827c7e69SLaurent Pinchart 
588827c7e69SLaurent Pinchart 	case V4L2_CID_EXPOSURE:
589827c7e69SLaurent Pinchart 		ret = imx290_write(imx290, IMX290_SHS1,
590827c7e69SLaurent Pinchart 				   IMX290_VMAX_DEFAULT - ctrl->val - 1, NULL);
591827c7e69SLaurent Pinchart 		break;
592827c7e69SLaurent Pinchart 
593a58df1f9SManivannan Sadhasivam 	case V4L2_CID_TEST_PATTERN:
594a58df1f9SManivannan Sadhasivam 		if (ctrl->val) {
595e611f3daSLaurent Pinchart 			imx290_write(imx290, IMX290_BLKLEVEL, 0, &ret);
5968e5652aeSAndrey Konovalov 			usleep_range(10000, 11000);
597e611f3daSLaurent Pinchart 			imx290_write(imx290, IMX290_PGCTRL,
598a58df1f9SManivannan Sadhasivam 				     (u8)(IMX290_PGCTRL_REGEN |
599a58df1f9SManivannan Sadhasivam 				     IMX290_PGCTRL_THRU |
600e611f3daSLaurent Pinchart 				     IMX290_PGCTRL_MODE(ctrl->val)), &ret);
601a58df1f9SManivannan Sadhasivam 		} else {
602e611f3daSLaurent Pinchart 			imx290_write(imx290, IMX290_PGCTRL, 0x00, &ret);
6038e5652aeSAndrey Konovalov 			usleep_range(10000, 11000);
604c566ac01SManivannan Sadhasivam 			if (imx290->bpp == 10)
605e611f3daSLaurent Pinchart 				imx290_write(imx290, IMX290_BLKLEVEL, 0x3c,
606e611f3daSLaurent Pinchart 					     &ret);
607c566ac01SManivannan Sadhasivam 			else /* 12 bits per pixel */
608e611f3daSLaurent Pinchart 				imx290_write(imx290, IMX290_BLKLEVEL, 0xf0,
609e611f3daSLaurent Pinchart 					     &ret);
610a58df1f9SManivannan Sadhasivam 		}
611a58df1f9SManivannan Sadhasivam 		break;
612828dbc29SManivannan Sadhasivam 	default:
613828dbc29SManivannan Sadhasivam 		ret = -EINVAL;
614828dbc29SManivannan Sadhasivam 		break;
615828dbc29SManivannan Sadhasivam 	}
616828dbc29SManivannan Sadhasivam 
617828dbc29SManivannan Sadhasivam 	pm_runtime_put(imx290->dev);
618828dbc29SManivannan Sadhasivam 
619828dbc29SManivannan Sadhasivam 	return ret;
620828dbc29SManivannan Sadhasivam }
621828dbc29SManivannan Sadhasivam 
622828dbc29SManivannan Sadhasivam static const struct v4l2_ctrl_ops imx290_ctrl_ops = {
623828dbc29SManivannan Sadhasivam 	.s_ctrl = imx290_set_ctrl,
624828dbc29SManivannan Sadhasivam };
625828dbc29SManivannan Sadhasivam 
626cb7e1c8dSLaurent Pinchart static const char * const imx290_test_pattern_menu[] = {
627cb7e1c8dSLaurent Pinchart 	"Disabled",
628cb7e1c8dSLaurent Pinchart 	"Sequence Pattern 1",
629cb7e1c8dSLaurent Pinchart 	"Horizontal Color-bar Chart",
630cb7e1c8dSLaurent Pinchart 	"Vertical Color-bar Chart",
631cb7e1c8dSLaurent Pinchart 	"Sequence Pattern 2",
632cb7e1c8dSLaurent Pinchart 	"Gradation Pattern 1",
633cb7e1c8dSLaurent Pinchart 	"Gradation Pattern 2",
634cb7e1c8dSLaurent Pinchart 	"000/555h Toggle Pattern",
635cb7e1c8dSLaurent Pinchart };
636cb7e1c8dSLaurent Pinchart 
637a7941da3SLaurent Pinchart static void imx290_ctrl_update(struct imx290 *imx290,
638a7941da3SLaurent Pinchart 			       const struct imx290_mode *mode)
639a7941da3SLaurent Pinchart {
640a7941da3SLaurent Pinchart 	unsigned int hblank = mode->hmax - mode->width;
641a7941da3SLaurent Pinchart 	unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
642a7941da3SLaurent Pinchart 
643a7941da3SLaurent Pinchart 	/*
644a7941da3SLaurent Pinchart 	 * This function may be called from imx290_set_fmt() before controls
645a7941da3SLaurent Pinchart 	 * get created by imx290_ctrl_init(). Return immediately in that case.
646a7941da3SLaurent Pinchart 	 */
647a7941da3SLaurent Pinchart 	if (!imx290->ctrls.lock)
648a7941da3SLaurent Pinchart 		return;
649a7941da3SLaurent Pinchart 
650*70bbf56aSLaurent Pinchart 	__v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
651a7941da3SLaurent Pinchart 	__v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate,
652a7941da3SLaurent Pinchart 				 imx290_calc_pixel_rate(imx290));
653a7941da3SLaurent Pinchart 
654a7941da3SLaurent Pinchart 	__v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 1, hblank);
655a7941da3SLaurent Pinchart 	__v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank);
656a7941da3SLaurent Pinchart }
657a7941da3SLaurent Pinchart 
658cb7e1c8dSLaurent Pinchart static int imx290_ctrl_init(struct imx290 *imx290)
659cb7e1c8dSLaurent Pinchart {
660cb7e1c8dSLaurent Pinchart 	struct v4l2_fwnode_device_properties props;
661cb7e1c8dSLaurent Pinchart 	unsigned int blank;
662cb7e1c8dSLaurent Pinchart 	int ret;
663cb7e1c8dSLaurent Pinchart 
664cb7e1c8dSLaurent Pinchart 	ret = v4l2_fwnode_device_parse(imx290->dev, &props);
665cb7e1c8dSLaurent Pinchart 	if (ret < 0)
666cb7e1c8dSLaurent Pinchart 		return ret;
667cb7e1c8dSLaurent Pinchart 
668cb7e1c8dSLaurent Pinchart 	v4l2_ctrl_handler_init(&imx290->ctrls, 9);
669cb7e1c8dSLaurent Pinchart 	imx290->ctrls.lock = &imx290->lock;
670cb7e1c8dSLaurent Pinchart 
671cb7e1c8dSLaurent Pinchart 	/*
672cb7e1c8dSLaurent Pinchart 	 * The sensor has an analog gain and a digital gain, both controlled
673cb7e1c8dSLaurent Pinchart 	 * through a single gain value, expressed in 0.3dB increments. Values
674cb7e1c8dSLaurent Pinchart 	 * from 0.0dB (0) to 30.0dB (100) apply analog gain only, higher values
675cb7e1c8dSLaurent Pinchart 	 * up to 72.0dB (240) add further digital gain. Limit the range to
676cb7e1c8dSLaurent Pinchart 	 * analog gain only, support for digital gain can be added separately
677cb7e1c8dSLaurent Pinchart 	 * if needed.
678cb7e1c8dSLaurent Pinchart 	 *
679cb7e1c8dSLaurent Pinchart 	 * The IMX327 and IMX462 are largely compatible with the IMX290, but
680cb7e1c8dSLaurent Pinchart 	 * have an analog gain range of 0.0dB to 29.4dB and 42dB of digital
681cb7e1c8dSLaurent Pinchart 	 * gain. When support for those sensors gets added to the driver, the
682cb7e1c8dSLaurent Pinchart 	 * gain control should be adjusted accordingly.
683cb7e1c8dSLaurent Pinchart 	 */
684cb7e1c8dSLaurent Pinchart 	v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
685cb7e1c8dSLaurent Pinchart 			  V4L2_CID_ANALOGUE_GAIN, 0, 100, 1, 0);
686cb7e1c8dSLaurent Pinchart 
687cb7e1c8dSLaurent Pinchart 	v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
688cb7e1c8dSLaurent Pinchart 			  V4L2_CID_EXPOSURE, 1, IMX290_VMAX_DEFAULT - 2, 1,
689cb7e1c8dSLaurent Pinchart 			  IMX290_VMAX_DEFAULT - 2);
690cb7e1c8dSLaurent Pinchart 
691cb7e1c8dSLaurent Pinchart 	imx290->link_freq =
692cb7e1c8dSLaurent Pinchart 		v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops,
693cb7e1c8dSLaurent Pinchart 				       V4L2_CID_LINK_FREQ,
694cb7e1c8dSLaurent Pinchart 				       imx290_link_freqs_num(imx290) - 1, 0,
695cb7e1c8dSLaurent Pinchart 				       imx290_link_freqs_ptr(imx290));
696cb7e1c8dSLaurent Pinchart 	if (imx290->link_freq)
697cb7e1c8dSLaurent Pinchart 		imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
698cb7e1c8dSLaurent Pinchart 
699cb7e1c8dSLaurent Pinchart 	imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
700cb7e1c8dSLaurent Pinchart 					       V4L2_CID_PIXEL_RATE,
701cb7e1c8dSLaurent Pinchart 					       1, INT_MAX, 1,
702cb7e1c8dSLaurent Pinchart 					       imx290_calc_pixel_rate(imx290));
703cb7e1c8dSLaurent Pinchart 
704cb7e1c8dSLaurent Pinchart 	v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops,
705cb7e1c8dSLaurent Pinchart 				     V4L2_CID_TEST_PATTERN,
706cb7e1c8dSLaurent Pinchart 				     ARRAY_SIZE(imx290_test_pattern_menu) - 1,
707cb7e1c8dSLaurent Pinchart 				     0, 0, imx290_test_pattern_menu);
708cb7e1c8dSLaurent Pinchart 
709cb7e1c8dSLaurent Pinchart 	blank = imx290->current_mode->hmax - imx290->current_mode->width;
710cb7e1c8dSLaurent Pinchart 	imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
711cb7e1c8dSLaurent Pinchart 					   V4L2_CID_HBLANK, blank, blank, 1,
712cb7e1c8dSLaurent Pinchart 					   blank);
713cb7e1c8dSLaurent Pinchart 	if (imx290->hblank)
714cb7e1c8dSLaurent Pinchart 		imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
715cb7e1c8dSLaurent Pinchart 
716cb7e1c8dSLaurent Pinchart 	blank = IMX290_VMAX_DEFAULT - imx290->current_mode->height;
717cb7e1c8dSLaurent Pinchart 	imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
718cb7e1c8dSLaurent Pinchart 					   V4L2_CID_VBLANK, blank, blank, 1,
719cb7e1c8dSLaurent Pinchart 					   blank);
720cb7e1c8dSLaurent Pinchart 	if (imx290->vblank)
721cb7e1c8dSLaurent Pinchart 		imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
722cb7e1c8dSLaurent Pinchart 
723cb7e1c8dSLaurent Pinchart 	v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops,
724cb7e1c8dSLaurent Pinchart 					&props);
725cb7e1c8dSLaurent Pinchart 
726cb7e1c8dSLaurent Pinchart 	imx290->sd.ctrl_handler = &imx290->ctrls;
727cb7e1c8dSLaurent Pinchart 
728cb7e1c8dSLaurent Pinchart 	if (imx290->ctrls.error) {
729cb7e1c8dSLaurent Pinchart 		ret = imx290->ctrls.error;
730cb7e1c8dSLaurent Pinchart 		v4l2_ctrl_handler_free(&imx290->ctrls);
731cb7e1c8dSLaurent Pinchart 		return ret;
732cb7e1c8dSLaurent Pinchart 	}
733cb7e1c8dSLaurent Pinchart 
734cb7e1c8dSLaurent Pinchart 	return 0;
735cb7e1c8dSLaurent Pinchart }
736cb7e1c8dSLaurent Pinchart 
737cb7e1c8dSLaurent Pinchart /* ----------------------------------------------------------------------------
738cb7e1c8dSLaurent Pinchart  * Subdev operations
739cb7e1c8dSLaurent Pinchart  */
740cb7e1c8dSLaurent Pinchart 
741cb7e1c8dSLaurent Pinchart /* Start streaming */
742cb7e1c8dSLaurent Pinchart static int imx290_start_streaming(struct imx290 *imx290)
743cb7e1c8dSLaurent Pinchart {
744cb7e1c8dSLaurent Pinchart 	int ret;
745cb7e1c8dSLaurent Pinchart 
746cb7e1c8dSLaurent Pinchart 	/* Set init register settings */
747cb7e1c8dSLaurent Pinchart 	ret = imx290_set_register_array(imx290, imx290_global_init_settings,
748cb7e1c8dSLaurent Pinchart 					ARRAY_SIZE(
749cb7e1c8dSLaurent Pinchart 						imx290_global_init_settings));
750cb7e1c8dSLaurent Pinchart 	if (ret < 0) {
751cb7e1c8dSLaurent Pinchart 		dev_err(imx290->dev, "Could not set init registers\n");
752cb7e1c8dSLaurent Pinchart 		return ret;
753cb7e1c8dSLaurent Pinchart 	}
754cb7e1c8dSLaurent Pinchart 
755cb7e1c8dSLaurent Pinchart 	/* Apply the register values related to current frame format */
756cb7e1c8dSLaurent Pinchart 	ret = imx290_write_current_format(imx290);
757cb7e1c8dSLaurent Pinchart 	if (ret < 0) {
758cb7e1c8dSLaurent Pinchart 		dev_err(imx290->dev, "Could not set frame format\n");
759cb7e1c8dSLaurent Pinchart 		return ret;
760cb7e1c8dSLaurent Pinchart 	}
761cb7e1c8dSLaurent Pinchart 
762cb7e1c8dSLaurent Pinchart 	/* Apply default values of current mode */
763cb7e1c8dSLaurent Pinchart 	ret = imx290_set_register_array(imx290, imx290->current_mode->data,
764cb7e1c8dSLaurent Pinchart 					imx290->current_mode->data_size);
765cb7e1c8dSLaurent Pinchart 	if (ret < 0) {
766cb7e1c8dSLaurent Pinchart 		dev_err(imx290->dev, "Could not set current mode\n");
767cb7e1c8dSLaurent Pinchart 		return ret;
768cb7e1c8dSLaurent Pinchart 	}
769cb7e1c8dSLaurent Pinchart 
770cb7e1c8dSLaurent Pinchart 	ret = imx290_write(imx290, IMX290_HMAX, imx290->current_mode->hmax,
771cb7e1c8dSLaurent Pinchart 			   NULL);
772cb7e1c8dSLaurent Pinchart 	if (ret)
773cb7e1c8dSLaurent Pinchart 		return ret;
774cb7e1c8dSLaurent Pinchart 
775cb7e1c8dSLaurent Pinchart 	/* Apply customized values from user */
776cb7e1c8dSLaurent Pinchart 	ret = v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler);
777cb7e1c8dSLaurent Pinchart 	if (ret) {
778cb7e1c8dSLaurent Pinchart 		dev_err(imx290->dev, "Could not sync v4l2 controls\n");
779cb7e1c8dSLaurent Pinchart 		return ret;
780cb7e1c8dSLaurent Pinchart 	}
781cb7e1c8dSLaurent Pinchart 
782cb7e1c8dSLaurent Pinchart 	imx290_write(imx290, IMX290_STANDBY, 0x00, &ret);
783cb7e1c8dSLaurent Pinchart 
784cb7e1c8dSLaurent Pinchart 	msleep(30);
785cb7e1c8dSLaurent Pinchart 
786cb7e1c8dSLaurent Pinchart 	/* Start streaming */
787cb7e1c8dSLaurent Pinchart 	return imx290_write(imx290, IMX290_XMSTA, 0x00, &ret);
788cb7e1c8dSLaurent Pinchart }
789cb7e1c8dSLaurent Pinchart 
790cb7e1c8dSLaurent Pinchart /* Stop streaming */
791cb7e1c8dSLaurent Pinchart static int imx290_stop_streaming(struct imx290 *imx290)
792cb7e1c8dSLaurent Pinchart {
793cb7e1c8dSLaurent Pinchart 	int ret = 0;
794cb7e1c8dSLaurent Pinchart 
795cb7e1c8dSLaurent Pinchart 	imx290_write(imx290, IMX290_STANDBY, 0x01, &ret);
796cb7e1c8dSLaurent Pinchart 
797cb7e1c8dSLaurent Pinchart 	msleep(30);
798cb7e1c8dSLaurent Pinchart 
799cb7e1c8dSLaurent Pinchart 	return imx290_write(imx290, IMX290_XMSTA, 0x01, &ret);
800cb7e1c8dSLaurent Pinchart }
801cb7e1c8dSLaurent Pinchart 
802cb7e1c8dSLaurent Pinchart static int imx290_set_stream(struct v4l2_subdev *sd, int enable)
803cb7e1c8dSLaurent Pinchart {
804cb7e1c8dSLaurent Pinchart 	struct imx290 *imx290 = to_imx290(sd);
805cb7e1c8dSLaurent Pinchart 	int ret = 0;
806cb7e1c8dSLaurent Pinchart 
807cb7e1c8dSLaurent Pinchart 	if (enable) {
808cb7e1c8dSLaurent Pinchart 		ret = pm_runtime_resume_and_get(imx290->dev);
809cb7e1c8dSLaurent Pinchart 		if (ret < 0)
810cb7e1c8dSLaurent Pinchart 			goto unlock_and_return;
811cb7e1c8dSLaurent Pinchart 
812cb7e1c8dSLaurent Pinchart 		ret = imx290_start_streaming(imx290);
813cb7e1c8dSLaurent Pinchart 		if (ret) {
814cb7e1c8dSLaurent Pinchart 			dev_err(imx290->dev, "Start stream failed\n");
815cb7e1c8dSLaurent Pinchart 			pm_runtime_put(imx290->dev);
816cb7e1c8dSLaurent Pinchart 			goto unlock_and_return;
817cb7e1c8dSLaurent Pinchart 		}
818cb7e1c8dSLaurent Pinchart 	} else {
819cb7e1c8dSLaurent Pinchart 		imx290_stop_streaming(imx290);
820cb7e1c8dSLaurent Pinchart 		pm_runtime_put(imx290->dev);
821cb7e1c8dSLaurent Pinchart 	}
822cb7e1c8dSLaurent Pinchart 
823cb7e1c8dSLaurent Pinchart unlock_and_return:
824cb7e1c8dSLaurent Pinchart 
825cb7e1c8dSLaurent Pinchart 	return ret;
826cb7e1c8dSLaurent Pinchart }
827cb7e1c8dSLaurent Pinchart 
828b25537efSLaurent Pinchart static struct v4l2_mbus_framefmt *
829b25537efSLaurent Pinchart imx290_get_pad_format(struct imx290 *imx290, struct v4l2_subdev_state *state,
830b25537efSLaurent Pinchart 		      u32 which)
831b25537efSLaurent Pinchart {
832b25537efSLaurent Pinchart 	if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
833b25537efSLaurent Pinchart 		return &imx290->current_format;
834b25537efSLaurent Pinchart 	else
835b25537efSLaurent Pinchart 		return v4l2_subdev_get_try_format(&imx290->sd, state, 0);
836b25537efSLaurent Pinchart }
837b25537efSLaurent Pinchart 
838828dbc29SManivannan Sadhasivam static int imx290_enum_mbus_code(struct v4l2_subdev *sd,
8390d346d2aSTomi Valkeinen 				 struct v4l2_subdev_state *sd_state,
840828dbc29SManivannan Sadhasivam 				 struct v4l2_subdev_mbus_code_enum *code)
841828dbc29SManivannan Sadhasivam {
842828dbc29SManivannan Sadhasivam 	if (code->index >= ARRAY_SIZE(imx290_formats))
843828dbc29SManivannan Sadhasivam 		return -EINVAL;
844828dbc29SManivannan Sadhasivam 
845828dbc29SManivannan Sadhasivam 	code->code = imx290_formats[code->index].code;
846828dbc29SManivannan Sadhasivam 
847828dbc29SManivannan Sadhasivam 	return 0;
848828dbc29SManivannan Sadhasivam }
849828dbc29SManivannan Sadhasivam 
8503b867fb6SManivannan Sadhasivam static int imx290_enum_frame_size(struct v4l2_subdev *sd,
8510d346d2aSTomi Valkeinen 				  struct v4l2_subdev_state *sd_state,
8523b867fb6SManivannan Sadhasivam 				  struct v4l2_subdev_frame_size_enum *fse)
8533b867fb6SManivannan Sadhasivam {
8543b867fb6SManivannan Sadhasivam 	const struct imx290 *imx290 = to_imx290(sd);
8553b867fb6SManivannan Sadhasivam 	const struct imx290_mode *imx290_modes = imx290_modes_ptr(imx290);
8563b867fb6SManivannan Sadhasivam 
8573b867fb6SManivannan Sadhasivam 	if ((fse->code != imx290_formats[0].code) &&
8583b867fb6SManivannan Sadhasivam 	    (fse->code != imx290_formats[1].code))
8593b867fb6SManivannan Sadhasivam 		return -EINVAL;
8603b867fb6SManivannan Sadhasivam 
8613b867fb6SManivannan Sadhasivam 	if (fse->index >= imx290_modes_num(imx290))
8623b867fb6SManivannan Sadhasivam 		return -EINVAL;
8633b867fb6SManivannan Sadhasivam 
8643b867fb6SManivannan Sadhasivam 	fse->min_width = imx290_modes[fse->index].width;
8653b867fb6SManivannan Sadhasivam 	fse->max_width = imx290_modes[fse->index].width;
8663b867fb6SManivannan Sadhasivam 	fse->min_height = imx290_modes[fse->index].height;
8673b867fb6SManivannan Sadhasivam 	fse->max_height = imx290_modes[fse->index].height;
8683b867fb6SManivannan Sadhasivam 
8693b867fb6SManivannan Sadhasivam 	return 0;
8703b867fb6SManivannan Sadhasivam }
8713b867fb6SManivannan Sadhasivam 
872828dbc29SManivannan Sadhasivam static int imx290_get_fmt(struct v4l2_subdev *sd,
8730d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
874828dbc29SManivannan Sadhasivam 			  struct v4l2_subdev_format *fmt)
875828dbc29SManivannan Sadhasivam {
876828dbc29SManivannan Sadhasivam 	struct imx290 *imx290 = to_imx290(sd);
877828dbc29SManivannan Sadhasivam 	struct v4l2_mbus_framefmt *framefmt;
878828dbc29SManivannan Sadhasivam 
879828dbc29SManivannan Sadhasivam 	mutex_lock(&imx290->lock);
880828dbc29SManivannan Sadhasivam 
881b25537efSLaurent Pinchart 	framefmt = imx290_get_pad_format(imx290, sd_state, fmt->which);
882828dbc29SManivannan Sadhasivam 	fmt->format = *framefmt;
883828dbc29SManivannan Sadhasivam 
884828dbc29SManivannan Sadhasivam 	mutex_unlock(&imx290->lock);
885828dbc29SManivannan Sadhasivam 
886828dbc29SManivannan Sadhasivam 	return 0;
887828dbc29SManivannan Sadhasivam }
888828dbc29SManivannan Sadhasivam 
889828dbc29SManivannan Sadhasivam static int imx290_set_fmt(struct v4l2_subdev *sd,
8900d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
891828dbc29SManivannan Sadhasivam 			  struct v4l2_subdev_format *fmt)
892828dbc29SManivannan Sadhasivam {
893828dbc29SManivannan Sadhasivam 	struct imx290 *imx290 = to_imx290(sd);
894828dbc29SManivannan Sadhasivam 	const struct imx290_mode *mode;
895828dbc29SManivannan Sadhasivam 	struct v4l2_mbus_framefmt *format;
896828dbc29SManivannan Sadhasivam 	unsigned int i;
897828dbc29SManivannan Sadhasivam 
898828dbc29SManivannan Sadhasivam 	mutex_lock(&imx290->lock);
899828dbc29SManivannan Sadhasivam 
90097589ad6SManivannan Sadhasivam 	mode = v4l2_find_nearest_size(imx290_modes_ptr(imx290),
90197589ad6SManivannan Sadhasivam 				      imx290_modes_num(imx290), width, height,
902828dbc29SManivannan Sadhasivam 				      fmt->format.width, fmt->format.height);
903828dbc29SManivannan Sadhasivam 
904828dbc29SManivannan Sadhasivam 	fmt->format.width = mode->width;
905828dbc29SManivannan Sadhasivam 	fmt->format.height = mode->height;
906828dbc29SManivannan Sadhasivam 
907828dbc29SManivannan Sadhasivam 	for (i = 0; i < ARRAY_SIZE(imx290_formats); i++)
908828dbc29SManivannan Sadhasivam 		if (imx290_formats[i].code == fmt->format.code)
909828dbc29SManivannan Sadhasivam 			break;
910828dbc29SManivannan Sadhasivam 
911828dbc29SManivannan Sadhasivam 	if (i >= ARRAY_SIZE(imx290_formats))
912828dbc29SManivannan Sadhasivam 		i = 0;
913828dbc29SManivannan Sadhasivam 
914828dbc29SManivannan Sadhasivam 	fmt->format.code = imx290_formats[i].code;
915828dbc29SManivannan Sadhasivam 	fmt->format.field = V4L2_FIELD_NONE;
916828dbc29SManivannan Sadhasivam 
917b25537efSLaurent Pinchart 	format = imx290_get_pad_format(imx290, sd_state, fmt->which);
918b25537efSLaurent Pinchart 
919b25537efSLaurent Pinchart 	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
920828dbc29SManivannan Sadhasivam 		imx290->current_mode = mode;
921c566ac01SManivannan Sadhasivam 		imx290->bpp = imx290_formats[i].bpp;
92298e0500eSManivannan Sadhasivam 
923a7941da3SLaurent Pinchart 		imx290_ctrl_update(imx290, mode);
924828dbc29SManivannan Sadhasivam 	}
925828dbc29SManivannan Sadhasivam 
926828dbc29SManivannan Sadhasivam 	*format = fmt->format;
927828dbc29SManivannan Sadhasivam 
928828dbc29SManivannan Sadhasivam 	mutex_unlock(&imx290->lock);
929828dbc29SManivannan Sadhasivam 
930828dbc29SManivannan Sadhasivam 	return 0;
931828dbc29SManivannan Sadhasivam }
932828dbc29SManivannan Sadhasivam 
933b4ab57b0SLaurent Pinchart static int imx290_get_selection(struct v4l2_subdev *sd,
934b4ab57b0SLaurent Pinchart 				struct v4l2_subdev_state *sd_state,
935b4ab57b0SLaurent Pinchart 				struct v4l2_subdev_selection *sel)
936b4ab57b0SLaurent Pinchart {
937b4ab57b0SLaurent Pinchart 	struct imx290 *imx290 = to_imx290(sd);
938b4ab57b0SLaurent Pinchart 	struct v4l2_mbus_framefmt *format;
939b4ab57b0SLaurent Pinchart 
940b4ab57b0SLaurent Pinchart 	switch (sel->target) {
941b4ab57b0SLaurent Pinchart 	case V4L2_SEL_TGT_CROP: {
942b4ab57b0SLaurent Pinchart 		format = imx290_get_pad_format(imx290, sd_state, sel->which);
943b4ab57b0SLaurent Pinchart 
944b4ab57b0SLaurent Pinchart 		mutex_lock(&imx290->lock);
945b4ab57b0SLaurent Pinchart 
946b4ab57b0SLaurent Pinchart 		sel->r.top = IMX920_PIXEL_ARRAY_MARGIN_TOP
947b4ab57b0SLaurent Pinchart 			   + (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT - format->height) / 2;
948b4ab57b0SLaurent Pinchart 		sel->r.left = IMX920_PIXEL_ARRAY_MARGIN_LEFT
949b4ab57b0SLaurent Pinchart 			    + (IMX290_PIXEL_ARRAY_RECORDING_WIDTH - format->width) / 2;
950b4ab57b0SLaurent Pinchart 		sel->r.width = format->width;
951b4ab57b0SLaurent Pinchart 		sel->r.height = format->height;
952b4ab57b0SLaurent Pinchart 
953b4ab57b0SLaurent Pinchart 		mutex_unlock(&imx290->lock);
954b4ab57b0SLaurent Pinchart 		return 0;
955b4ab57b0SLaurent Pinchart 	}
956b4ab57b0SLaurent Pinchart 
957b4ab57b0SLaurent Pinchart 	case V4L2_SEL_TGT_NATIVE_SIZE:
958b4ab57b0SLaurent Pinchart 	case V4L2_SEL_TGT_CROP_BOUNDS:
959b4ab57b0SLaurent Pinchart 		sel->r.top = 0;
960b4ab57b0SLaurent Pinchart 		sel->r.left = 0;
961b4ab57b0SLaurent Pinchart 		sel->r.width = IMX290_PIXEL_ARRAY_WIDTH;
962b4ab57b0SLaurent Pinchart 		sel->r.height = IMX290_PIXEL_ARRAY_HEIGHT;
963b4ab57b0SLaurent Pinchart 
964b4ab57b0SLaurent Pinchart 		return 0;
965b4ab57b0SLaurent Pinchart 
966b4ab57b0SLaurent Pinchart 	case V4L2_SEL_TGT_CROP_DEFAULT:
967b4ab57b0SLaurent Pinchart 		sel->r.top = IMX920_PIXEL_ARRAY_MARGIN_TOP;
968b4ab57b0SLaurent Pinchart 		sel->r.left = IMX920_PIXEL_ARRAY_MARGIN_LEFT;
969b4ab57b0SLaurent Pinchart 		sel->r.width = IMX290_PIXEL_ARRAY_RECORDING_WIDTH;
970b4ab57b0SLaurent Pinchart 		sel->r.height = IMX290_PIXEL_ARRAY_RECORDING_HEIGHT;
971b4ab57b0SLaurent Pinchart 
972b4ab57b0SLaurent Pinchart 		return 0;
973b4ab57b0SLaurent Pinchart 
974b4ab57b0SLaurent Pinchart 	default:
975b4ab57b0SLaurent Pinchart 		return -EINVAL;
976b4ab57b0SLaurent Pinchart 	}
977b4ab57b0SLaurent Pinchart }
978b4ab57b0SLaurent Pinchart 
979828dbc29SManivannan Sadhasivam static int imx290_entity_init_cfg(struct v4l2_subdev *subdev,
9800d346d2aSTomi Valkeinen 				  struct v4l2_subdev_state *sd_state)
981828dbc29SManivannan Sadhasivam {
982828dbc29SManivannan Sadhasivam 	struct v4l2_subdev_format fmt = { 0 };
983828dbc29SManivannan Sadhasivam 
9840d346d2aSTomi Valkeinen 	fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
985828dbc29SManivannan Sadhasivam 	fmt.format.width = 1920;
986828dbc29SManivannan Sadhasivam 	fmt.format.height = 1080;
987828dbc29SManivannan Sadhasivam 
9880d346d2aSTomi Valkeinen 	imx290_set_fmt(subdev, sd_state, &fmt);
989828dbc29SManivannan Sadhasivam 
990828dbc29SManivannan Sadhasivam 	return 0;
991828dbc29SManivannan Sadhasivam }
992828dbc29SManivannan Sadhasivam 
993cb7e1c8dSLaurent Pinchart static const struct v4l2_subdev_video_ops imx290_video_ops = {
994cb7e1c8dSLaurent Pinchart 	.s_stream = imx290_set_stream,
995cb7e1c8dSLaurent Pinchart };
996828dbc29SManivannan Sadhasivam 
997cb7e1c8dSLaurent Pinchart static const struct v4l2_subdev_pad_ops imx290_pad_ops = {
998cb7e1c8dSLaurent Pinchart 	.init_cfg = imx290_entity_init_cfg,
999cb7e1c8dSLaurent Pinchart 	.enum_mbus_code = imx290_enum_mbus_code,
1000cb7e1c8dSLaurent Pinchart 	.enum_frame_size = imx290_enum_frame_size,
1001cb7e1c8dSLaurent Pinchart 	.get_fmt = imx290_get_fmt,
1002cb7e1c8dSLaurent Pinchart 	.set_fmt = imx290_set_fmt,
1003cb7e1c8dSLaurent Pinchart 	.get_selection = imx290_get_selection,
1004cb7e1c8dSLaurent Pinchart };
1005828dbc29SManivannan Sadhasivam 
1006cb7e1c8dSLaurent Pinchart static const struct v4l2_subdev_ops imx290_subdev_ops = {
1007cb7e1c8dSLaurent Pinchart 	.video = &imx290_video_ops,
1008cb7e1c8dSLaurent Pinchart 	.pad = &imx290_pad_ops,
1009cb7e1c8dSLaurent Pinchart };
1010828dbc29SManivannan Sadhasivam 
1011cb7e1c8dSLaurent Pinchart static const struct media_entity_operations imx290_subdev_entity_ops = {
1012cb7e1c8dSLaurent Pinchart 	.link_validate = v4l2_subdev_link_validate,
1013cb7e1c8dSLaurent Pinchart };
1014828dbc29SManivannan Sadhasivam 
1015dfb704daSLaurent Pinchart static int imx290_subdev_init(struct imx290 *imx290)
1016dfb704daSLaurent Pinchart {
1017dfb704daSLaurent Pinchart 	struct i2c_client *client = to_i2c_client(imx290->dev);
1018dfb704daSLaurent Pinchart 	int ret;
1019dfb704daSLaurent Pinchart 
1020dfb704daSLaurent Pinchart 	/*
1021dfb704daSLaurent Pinchart 	 * Initialize the frame format. In particular, imx290->current_mode
1022dfb704daSLaurent Pinchart 	 * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() call
1023dfb704daSLaurent Pinchart 	 * below relies on these fields.
1024dfb704daSLaurent Pinchart 	 */
1025dfb704daSLaurent Pinchart 	imx290_entity_init_cfg(&imx290->sd, NULL);
1026dfb704daSLaurent Pinchart 
1027dfb704daSLaurent Pinchart 	ret = imx290_ctrl_init(imx290);
1028dfb704daSLaurent Pinchart 	if (ret < 0) {
1029dfb704daSLaurent Pinchart 		dev_err(imx290->dev, "Control initialization error %d\n", ret);
1030dfb704daSLaurent Pinchart 		return ret;
1031dfb704daSLaurent Pinchart 	}
1032dfb704daSLaurent Pinchart 
1033dfb704daSLaurent Pinchart 	v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
1034dfb704daSLaurent Pinchart 	imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
1035dfb704daSLaurent Pinchart 	imx290->sd.dev = imx290->dev;
1036dfb704daSLaurent Pinchart 	imx290->sd.entity.ops = &imx290_subdev_entity_ops;
1037dfb704daSLaurent Pinchart 	imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
1038dfb704daSLaurent Pinchart 
1039dfb704daSLaurent Pinchart 	imx290->pad.flags = MEDIA_PAD_FL_SOURCE;
1040dfb704daSLaurent Pinchart 	ret = media_entity_pads_init(&imx290->sd.entity, 1, &imx290->pad);
1041dfb704daSLaurent Pinchart 	if (ret < 0) {
1042dfb704daSLaurent Pinchart 		dev_err(imx290->dev, "Could not register media entity\n");
1043dfb704daSLaurent Pinchart 		v4l2_ctrl_handler_free(&imx290->ctrls);
1044dfb704daSLaurent Pinchart 		return ret;
1045dfb704daSLaurent Pinchart 	}
1046dfb704daSLaurent Pinchart 
1047dfb704daSLaurent Pinchart 	return 0;
1048dfb704daSLaurent Pinchart }
1049dfb704daSLaurent Pinchart 
1050dfb704daSLaurent Pinchart static void imx290_subdev_cleanup(struct imx290 *imx290)
1051dfb704daSLaurent Pinchart {
1052dfb704daSLaurent Pinchart 	media_entity_cleanup(&imx290->sd.entity);
1053dfb704daSLaurent Pinchart 	v4l2_ctrl_handler_free(&imx290->ctrls);
1054dfb704daSLaurent Pinchart }
1055dfb704daSLaurent Pinchart 
1056cb7e1c8dSLaurent Pinchart /* ----------------------------------------------------------------------------
1057cb7e1c8dSLaurent Pinchart  * Power management
105897589ad6SManivannan Sadhasivam  */
105997589ad6SManivannan Sadhasivam 
1060828dbc29SManivannan Sadhasivam static int imx290_power_on(struct device *dev)
1061828dbc29SManivannan Sadhasivam {
1062b50ce25dSKrzysztof Kozlowski 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
1063828dbc29SManivannan Sadhasivam 	struct imx290 *imx290 = to_imx290(sd);
1064828dbc29SManivannan Sadhasivam 	int ret;
1065828dbc29SManivannan Sadhasivam 
1066828dbc29SManivannan Sadhasivam 	ret = clk_prepare_enable(imx290->xclk);
1067828dbc29SManivannan Sadhasivam 	if (ret) {
1068b50ce25dSKrzysztof Kozlowski 		dev_err(dev, "Failed to enable clock\n");
1069828dbc29SManivannan Sadhasivam 		return ret;
1070828dbc29SManivannan Sadhasivam 	}
1071828dbc29SManivannan Sadhasivam 
10722548df53SLaurent Pinchart 	ret = regulator_bulk_enable(ARRAY_SIZE(imx290->supplies),
10732548df53SLaurent Pinchart 				    imx290->supplies);
1074828dbc29SManivannan Sadhasivam 	if (ret) {
1075b50ce25dSKrzysztof Kozlowski 		dev_err(dev, "Failed to enable regulators\n");
1076828dbc29SManivannan Sadhasivam 		clk_disable_unprepare(imx290->xclk);
1077828dbc29SManivannan Sadhasivam 		return ret;
1078828dbc29SManivannan Sadhasivam 	}
1079828dbc29SManivannan Sadhasivam 
1080828dbc29SManivannan Sadhasivam 	usleep_range(1, 2);
10813909a92dSAndrey Konovalov 	gpiod_set_value_cansleep(imx290->rst_gpio, 0);
1082828dbc29SManivannan Sadhasivam 	usleep_range(30000, 31000);
1083828dbc29SManivannan Sadhasivam 
108497589ad6SManivannan Sadhasivam 	/* Set data lane count */
108597589ad6SManivannan Sadhasivam 	imx290_set_data_lanes(imx290);
108697589ad6SManivannan Sadhasivam 
1087828dbc29SManivannan Sadhasivam 	return 0;
1088828dbc29SManivannan Sadhasivam }
1089828dbc29SManivannan Sadhasivam 
1090828dbc29SManivannan Sadhasivam static int imx290_power_off(struct device *dev)
1091828dbc29SManivannan Sadhasivam {
1092b50ce25dSKrzysztof Kozlowski 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
1093828dbc29SManivannan Sadhasivam 	struct imx290 *imx290 = to_imx290(sd);
1094828dbc29SManivannan Sadhasivam 
1095828dbc29SManivannan Sadhasivam 	clk_disable_unprepare(imx290->xclk);
10963909a92dSAndrey Konovalov 	gpiod_set_value_cansleep(imx290->rst_gpio, 1);
10972548df53SLaurent Pinchart 	regulator_bulk_disable(ARRAY_SIZE(imx290->supplies), imx290->supplies);
1098828dbc29SManivannan Sadhasivam 
1099828dbc29SManivannan Sadhasivam 	return 0;
1100828dbc29SManivannan Sadhasivam }
1101828dbc29SManivannan Sadhasivam 
1102828dbc29SManivannan Sadhasivam static const struct dev_pm_ops imx290_pm_ops = {
11038d2d1bedSAndrey Konovalov 	SET_RUNTIME_PM_OPS(imx290_power_off, imx290_power_on, NULL)
1104828dbc29SManivannan Sadhasivam };
1105828dbc29SManivannan Sadhasivam 
1106cb7e1c8dSLaurent Pinchart /* ----------------------------------------------------------------------------
1107cb7e1c8dSLaurent Pinchart  * Probe & remove
110872c87b7aSLaurent Pinchart  */
110972c87b7aSLaurent Pinchart 
1110cb7e1c8dSLaurent Pinchart static const struct regmap_config imx290_regmap_config = {
1111cb7e1c8dSLaurent Pinchart 	.reg_bits = 16,
1112cb7e1c8dSLaurent Pinchart 	.val_bits = 8,
1113cb7e1c8dSLaurent Pinchart };
111472c87b7aSLaurent Pinchart 
1115cb7e1c8dSLaurent Pinchart static const char * const imx290_supply_name[IMX290_NUM_SUPPLIES] = {
1116cb7e1c8dSLaurent Pinchart 	"vdda",
1117cb7e1c8dSLaurent Pinchart 	"vddd",
1118cb7e1c8dSLaurent Pinchart 	"vdddo",
1119cb7e1c8dSLaurent Pinchart };
112072c87b7aSLaurent Pinchart 
1121cb7e1c8dSLaurent Pinchart static int imx290_get_regulators(struct device *dev, struct imx290 *imx290)
1122cb7e1c8dSLaurent Pinchart {
1123cb7e1c8dSLaurent Pinchart 	unsigned int i;
112472c87b7aSLaurent Pinchart 
1125cb7e1c8dSLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(imx290->supplies); i++)
1126cb7e1c8dSLaurent Pinchart 		imx290->supplies[i].supply = imx290_supply_name[i];
112772c87b7aSLaurent Pinchart 
1128cb7e1c8dSLaurent Pinchart 	return devm_regulator_bulk_get(dev, ARRAY_SIZE(imx290->supplies),
1129cb7e1c8dSLaurent Pinchart 				       imx290->supplies);
113072c87b7aSLaurent Pinchart }
113172c87b7aSLaurent Pinchart 
113298e0500eSManivannan Sadhasivam /*
113398e0500eSManivannan Sadhasivam  * Returns 0 if all link frequencies used by the driver for the given number
113498e0500eSManivannan Sadhasivam  * of MIPI data lanes are mentioned in the device tree, or the value of the
113598e0500eSManivannan Sadhasivam  * first missing frequency otherwise.
113698e0500eSManivannan Sadhasivam  */
1137a2706758SAndrey Konovalov static s64 imx290_check_link_freqs(const struct imx290 *imx290,
1138a2706758SAndrey Konovalov 				   const struct v4l2_fwnode_endpoint *ep)
113998e0500eSManivannan Sadhasivam {
114098e0500eSManivannan Sadhasivam 	int i, j;
114198e0500eSManivannan Sadhasivam 	const s64 *freqs = imx290_link_freqs_ptr(imx290);
114298e0500eSManivannan Sadhasivam 	int freqs_count = imx290_link_freqs_num(imx290);
114398e0500eSManivannan Sadhasivam 
114498e0500eSManivannan Sadhasivam 	for (i = 0; i < freqs_count; i++) {
1145a2706758SAndrey Konovalov 		for (j = 0; j < ep->nr_of_link_frequencies; j++)
1146a2706758SAndrey Konovalov 			if (freqs[i] == ep->link_frequencies[j])
114798e0500eSManivannan Sadhasivam 				break;
1148a2706758SAndrey Konovalov 		if (j == ep->nr_of_link_frequencies)
114998e0500eSManivannan Sadhasivam 			return freqs[i];
115098e0500eSManivannan Sadhasivam 	}
115198e0500eSManivannan Sadhasivam 	return 0;
115298e0500eSManivannan Sadhasivam }
115398e0500eSManivannan Sadhasivam 
1154828dbc29SManivannan Sadhasivam static int imx290_probe(struct i2c_client *client)
1155828dbc29SManivannan Sadhasivam {
1156828dbc29SManivannan Sadhasivam 	struct device *dev = &client->dev;
1157828dbc29SManivannan Sadhasivam 	struct fwnode_handle *endpoint;
1158a2706758SAndrey Konovalov 	/* Only CSI2 is supported for now: */
1159a2706758SAndrey Konovalov 	struct v4l2_fwnode_endpoint ep = {
1160a2706758SAndrey Konovalov 		.bus_type = V4L2_MBUS_CSI2_DPHY
1161a2706758SAndrey Konovalov 	};
1162828dbc29SManivannan Sadhasivam 	struct imx290 *imx290;
1163828dbc29SManivannan Sadhasivam 	u32 xclk_freq;
116498e0500eSManivannan Sadhasivam 	s64 fq;
1165828dbc29SManivannan Sadhasivam 	int ret;
1166828dbc29SManivannan Sadhasivam 
1167828dbc29SManivannan Sadhasivam 	imx290 = devm_kzalloc(dev, sizeof(*imx290), GFP_KERNEL);
1168828dbc29SManivannan Sadhasivam 	if (!imx290)
1169828dbc29SManivannan Sadhasivam 		return -ENOMEM;
1170828dbc29SManivannan Sadhasivam 
1171828dbc29SManivannan Sadhasivam 	imx290->dev = dev;
1172828dbc29SManivannan Sadhasivam 	imx290->regmap = devm_regmap_init_i2c(client, &imx290_regmap_config);
1173828dbc29SManivannan Sadhasivam 	if (IS_ERR(imx290->regmap)) {
1174828dbc29SManivannan Sadhasivam 		dev_err(dev, "Unable to initialize I2C\n");
1175828dbc29SManivannan Sadhasivam 		return -ENODEV;
1176828dbc29SManivannan Sadhasivam 	}
1177828dbc29SManivannan Sadhasivam 
1178828dbc29SManivannan Sadhasivam 	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
1179828dbc29SManivannan Sadhasivam 	if (!endpoint) {
1180828dbc29SManivannan Sadhasivam 		dev_err(dev, "Endpoint node not found\n");
1181828dbc29SManivannan Sadhasivam 		return -EINVAL;
1182828dbc29SManivannan Sadhasivam 	}
1183828dbc29SManivannan Sadhasivam 
1184a2706758SAndrey Konovalov 	ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep);
1185828dbc29SManivannan Sadhasivam 	fwnode_handle_put(endpoint);
1186a2706758SAndrey Konovalov 	if (ret == -ENXIO) {
1187a2706758SAndrey Konovalov 		dev_err(dev, "Unsupported bus type, should be CSI2\n");
1188dfb704daSLaurent Pinchart 		goto err_endpoint;
1189a2706758SAndrey Konovalov 	} else if (ret) {
1190828dbc29SManivannan Sadhasivam 		dev_err(dev, "Parsing endpoint node failed\n");
1191dfb704daSLaurent Pinchart 		goto err_endpoint;
1192828dbc29SManivannan Sadhasivam 	}
1193828dbc29SManivannan Sadhasivam 
119497589ad6SManivannan Sadhasivam 	/* Get number of data lanes */
1195a2706758SAndrey Konovalov 	imx290->nlanes = ep.bus.mipi_csi2.num_data_lanes;
119697589ad6SManivannan Sadhasivam 	if (imx290->nlanes != 2 && imx290->nlanes != 4) {
119797589ad6SManivannan Sadhasivam 		dev_err(dev, "Invalid data lanes: %d\n", imx290->nlanes);
119897589ad6SManivannan Sadhasivam 		ret = -EINVAL;
1199dfb704daSLaurent Pinchart 		goto err_endpoint;
120097589ad6SManivannan Sadhasivam 	}
120197589ad6SManivannan Sadhasivam 
120297589ad6SManivannan Sadhasivam 	dev_dbg(dev, "Using %u data lanes\n", imx290->nlanes);
120397589ad6SManivannan Sadhasivam 
1204a2706758SAndrey Konovalov 	if (!ep.nr_of_link_frequencies) {
1205828dbc29SManivannan Sadhasivam 		dev_err(dev, "link-frequency property not found in DT\n");
1206828dbc29SManivannan Sadhasivam 		ret = -EINVAL;
1207dfb704daSLaurent Pinchart 		goto err_endpoint;
1208828dbc29SManivannan Sadhasivam 	}
1209828dbc29SManivannan Sadhasivam 
121098e0500eSManivannan Sadhasivam 	/* Check that link frequences for all the modes are in device tree */
1211a2706758SAndrey Konovalov 	fq = imx290_check_link_freqs(imx290, &ep);
121298e0500eSManivannan Sadhasivam 	if (fq) {
121398e0500eSManivannan Sadhasivam 		dev_err(dev, "Link frequency of %lld is not supported\n", fq);
1214828dbc29SManivannan Sadhasivam 		ret = -EINVAL;
1215dfb704daSLaurent Pinchart 		goto err_endpoint;
1216828dbc29SManivannan Sadhasivam 	}
1217828dbc29SManivannan Sadhasivam 
1218828dbc29SManivannan Sadhasivam 	/* get system clock (xclk) */
1219828dbc29SManivannan Sadhasivam 	imx290->xclk = devm_clk_get(dev, "xclk");
1220828dbc29SManivannan Sadhasivam 	if (IS_ERR(imx290->xclk)) {
1221828dbc29SManivannan Sadhasivam 		dev_err(dev, "Could not get xclk");
1222828dbc29SManivannan Sadhasivam 		ret = PTR_ERR(imx290->xclk);
1223dfb704daSLaurent Pinchart 		goto err_endpoint;
1224828dbc29SManivannan Sadhasivam 	}
1225828dbc29SManivannan Sadhasivam 
1226828dbc29SManivannan Sadhasivam 	ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
1227828dbc29SManivannan Sadhasivam 				       &xclk_freq);
1228828dbc29SManivannan Sadhasivam 	if (ret) {
1229828dbc29SManivannan Sadhasivam 		dev_err(dev, "Could not get xclk frequency\n");
1230dfb704daSLaurent Pinchart 		goto err_endpoint;
1231828dbc29SManivannan Sadhasivam 	}
1232828dbc29SManivannan Sadhasivam 
1233828dbc29SManivannan Sadhasivam 	/* external clock must be 37.125 MHz */
1234828dbc29SManivannan Sadhasivam 	if (xclk_freq != 37125000) {
1235828dbc29SManivannan Sadhasivam 		dev_err(dev, "External clock frequency %u is not supported\n",
1236828dbc29SManivannan Sadhasivam 			xclk_freq);
1237828dbc29SManivannan Sadhasivam 		ret = -EINVAL;
1238dfb704daSLaurent Pinchart 		goto err_endpoint;
1239828dbc29SManivannan Sadhasivam 	}
1240828dbc29SManivannan Sadhasivam 
1241828dbc29SManivannan Sadhasivam 	ret = clk_set_rate(imx290->xclk, xclk_freq);
1242828dbc29SManivannan Sadhasivam 	if (ret) {
1243828dbc29SManivannan Sadhasivam 		dev_err(dev, "Could not set xclk frequency\n");
1244dfb704daSLaurent Pinchart 		goto err_endpoint;
1245828dbc29SManivannan Sadhasivam 	}
1246828dbc29SManivannan Sadhasivam 
1247828dbc29SManivannan Sadhasivam 	ret = imx290_get_regulators(dev, imx290);
1248828dbc29SManivannan Sadhasivam 	if (ret < 0) {
1249828dbc29SManivannan Sadhasivam 		dev_err(dev, "Cannot get regulators\n");
1250dfb704daSLaurent Pinchart 		goto err_endpoint;
1251828dbc29SManivannan Sadhasivam 	}
1252828dbc29SManivannan Sadhasivam 
12533909a92dSAndrey Konovalov 	imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset",
12543909a92dSAndrey Konovalov 						   GPIOD_OUT_HIGH);
1255828dbc29SManivannan Sadhasivam 	if (IS_ERR(imx290->rst_gpio)) {
1256828dbc29SManivannan Sadhasivam 		dev_err(dev, "Cannot get reset gpio\n");
1257828dbc29SManivannan Sadhasivam 		ret = PTR_ERR(imx290->rst_gpio);
1258dfb704daSLaurent Pinchart 		goto err_endpoint;
1259828dbc29SManivannan Sadhasivam 	}
1260828dbc29SManivannan Sadhasivam 
1261828dbc29SManivannan Sadhasivam 	mutex_init(&imx290->lock);
1262828dbc29SManivannan Sadhasivam 
1263dfb704daSLaurent Pinchart 	ret = imx290_subdev_init(imx290);
1264dfb704daSLaurent Pinchart 	if (ret)
1265dfb704daSLaurent Pinchart 		goto err_mutex;
1266828dbc29SManivannan Sadhasivam 
1267828dbc29SManivannan Sadhasivam 	ret = v4l2_async_register_subdev(&imx290->sd);
1268828dbc29SManivannan Sadhasivam 	if (ret < 0) {
1269828dbc29SManivannan Sadhasivam 		dev_err(dev, "Could not register v4l2 device\n");
1270dfb704daSLaurent Pinchart 		goto err_subdev;
1271828dbc29SManivannan Sadhasivam 	}
1272828dbc29SManivannan Sadhasivam 
1273828dbc29SManivannan Sadhasivam 	/* Power on the device to match runtime PM state below */
1274828dbc29SManivannan Sadhasivam 	ret = imx290_power_on(dev);
1275828dbc29SManivannan Sadhasivam 	if (ret < 0) {
1276828dbc29SManivannan Sadhasivam 		dev_err(dev, "Could not power on the device\n");
1277dfb704daSLaurent Pinchart 		goto err_subdev;
1278828dbc29SManivannan Sadhasivam 	}
1279828dbc29SManivannan Sadhasivam 
1280828dbc29SManivannan Sadhasivam 	pm_runtime_set_active(dev);
1281828dbc29SManivannan Sadhasivam 	pm_runtime_enable(dev);
1282828dbc29SManivannan Sadhasivam 	pm_runtime_idle(dev);
1283828dbc29SManivannan Sadhasivam 
1284a2706758SAndrey Konovalov 	v4l2_fwnode_endpoint_free(&ep);
1285828dbc29SManivannan Sadhasivam 
1286828dbc29SManivannan Sadhasivam 	return 0;
1287828dbc29SManivannan Sadhasivam 
1288dfb704daSLaurent Pinchart err_subdev:
1289dfb704daSLaurent Pinchart 	imx290_subdev_cleanup(imx290);
1290dfb704daSLaurent Pinchart err_mutex:
1291828dbc29SManivannan Sadhasivam 	mutex_destroy(&imx290->lock);
1292dfb704daSLaurent Pinchart err_endpoint:
1293a2706758SAndrey Konovalov 	v4l2_fwnode_endpoint_free(&ep);
1294828dbc29SManivannan Sadhasivam 
1295828dbc29SManivannan Sadhasivam 	return ret;
1296828dbc29SManivannan Sadhasivam }
1297828dbc29SManivannan Sadhasivam 
1298ed5c2f5fSUwe Kleine-König static void imx290_remove(struct i2c_client *client)
1299828dbc29SManivannan Sadhasivam {
1300828dbc29SManivannan Sadhasivam 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
1301828dbc29SManivannan Sadhasivam 	struct imx290 *imx290 = to_imx290(sd);
1302828dbc29SManivannan Sadhasivam 
1303828dbc29SManivannan Sadhasivam 	v4l2_async_unregister_subdev(sd);
1304dfb704daSLaurent Pinchart 	imx290_subdev_cleanup(imx290);
1305828dbc29SManivannan Sadhasivam 
1306828dbc29SManivannan Sadhasivam 	mutex_destroy(&imx290->lock);
1307828dbc29SManivannan Sadhasivam 
1308828dbc29SManivannan Sadhasivam 	pm_runtime_disable(imx290->dev);
1309828dbc29SManivannan Sadhasivam 	if (!pm_runtime_status_suspended(imx290->dev))
1310828dbc29SManivannan Sadhasivam 		imx290_power_off(imx290->dev);
1311828dbc29SManivannan Sadhasivam 	pm_runtime_set_suspended(imx290->dev);
1312828dbc29SManivannan Sadhasivam }
1313828dbc29SManivannan Sadhasivam 
1314828dbc29SManivannan Sadhasivam static const struct of_device_id imx290_of_match[] = {
1315828dbc29SManivannan Sadhasivam 	{ .compatible = "sony,imx290" },
1316828dbc29SManivannan Sadhasivam 	{ /* sentinel */ }
1317828dbc29SManivannan Sadhasivam };
1318828dbc29SManivannan Sadhasivam MODULE_DEVICE_TABLE(of, imx290_of_match);
1319828dbc29SManivannan Sadhasivam 
1320828dbc29SManivannan Sadhasivam static struct i2c_driver imx290_i2c_driver = {
1321828dbc29SManivannan Sadhasivam 	.probe_new  = imx290_probe,
1322828dbc29SManivannan Sadhasivam 	.remove = imx290_remove,
1323828dbc29SManivannan Sadhasivam 	.driver = {
1324828dbc29SManivannan Sadhasivam 		.name  = "imx290",
1325828dbc29SManivannan Sadhasivam 		.pm = &imx290_pm_ops,
1326828dbc29SManivannan Sadhasivam 		.of_match_table = of_match_ptr(imx290_of_match),
1327828dbc29SManivannan Sadhasivam 	},
1328828dbc29SManivannan Sadhasivam };
1329828dbc29SManivannan Sadhasivam 
1330828dbc29SManivannan Sadhasivam module_i2c_driver(imx290_i2c_driver);
1331828dbc29SManivannan Sadhasivam 
1332828dbc29SManivannan Sadhasivam MODULE_DESCRIPTION("Sony IMX290 CMOS Image Sensor Driver");
1333828dbc29SManivannan Sadhasivam MODULE_AUTHOR("FRAMOS GmbH");
1334828dbc29SManivannan Sadhasivam MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
1335828dbc29SManivannan Sadhasivam MODULE_LICENSE("GPL v2");
1336