xref: /openbmc/linux/drivers/media/i2c/imx219.c (revision cf331730)
11283b3b8SDave Stevenson // SPDX-License-Identifier: GPL-2.0
21283b3b8SDave Stevenson /*
31283b3b8SDave Stevenson  * A V4L2 driver for Sony IMX219 cameras.
41283b3b8SDave Stevenson  * Copyright (C) 2019, Raspberry Pi (Trading) Ltd
51283b3b8SDave Stevenson  *
61283b3b8SDave Stevenson  * Based on Sony imx258 camera driver
71283b3b8SDave Stevenson  * Copyright (C) 2018 Intel Corporation
81283b3b8SDave Stevenson  *
91283b3b8SDave Stevenson  * DT / fwnode changes, and regulator / GPIO control taken from imx214 driver
101283b3b8SDave Stevenson  * Copyright 2018 Qtechnology A/S
111283b3b8SDave Stevenson  *
121283b3b8SDave Stevenson  * Flip handling taken from the Sony IMX319 driver.
131283b3b8SDave Stevenson  * Copyright (C) 2018 Intel Corporation
141283b3b8SDave Stevenson  *
151283b3b8SDave Stevenson  */
161283b3b8SDave Stevenson 
171283b3b8SDave Stevenson #include <linux/clk.h>
181283b3b8SDave Stevenson #include <linux/delay.h>
191283b3b8SDave Stevenson #include <linux/gpio/consumer.h>
201283b3b8SDave Stevenson #include <linux/i2c.h>
211283b3b8SDave Stevenson #include <linux/module.h>
221283b3b8SDave Stevenson #include <linux/pm_runtime.h>
231283b3b8SDave Stevenson #include <linux/regulator/consumer.h>
242e943af4SLaurent Pinchart 
252e943af4SLaurent Pinchart #include <media/v4l2-cci.h>
261283b3b8SDave Stevenson #include <media/v4l2-ctrls.h>
271283b3b8SDave Stevenson #include <media/v4l2-device.h>
281283b3b8SDave Stevenson #include <media/v4l2-event.h>
291283b3b8SDave Stevenson #include <media/v4l2-fwnode.h>
301283b3b8SDave Stevenson #include <media/v4l2-mediabus.h>
311283b3b8SDave Stevenson 
322e943af4SLaurent Pinchart /* Chip ID */
332e943af4SLaurent Pinchart #define IMX219_REG_CHIP_ID		CCI_REG16(0x0000)
342e943af4SLaurent Pinchart #define IMX219_CHIP_ID			0x0219
351283b3b8SDave Stevenson 
362e943af4SLaurent Pinchart #define IMX219_REG_MODE_SELECT		CCI_REG8(0x0100)
371283b3b8SDave Stevenson #define IMX219_MODE_STANDBY		0x00
381283b3b8SDave Stevenson #define IMX219_MODE_STREAMING		0x01
391283b3b8SDave Stevenson 
402e943af4SLaurent Pinchart #define IMX219_REG_CSI_LANE_MODE	CCI_REG8(0x0114)
41ceddfd44SAdam Ford #define IMX219_CSI_2_LANE_MODE		0x01
42ceddfd44SAdam Ford #define IMX219_CSI_4_LANE_MODE		0x03
431283b3b8SDave Stevenson 
44*ae6f196aSLaurent Pinchart #define IMX219_REG_DPHY_CTRL		CCI_REG8(0x0128)
45*ae6f196aSLaurent Pinchart #define IMX219_DPHY_CTRL_TIMING_AUTO	0
46*ae6f196aSLaurent Pinchart #define IMX219_DPHY_CTRL_TIMING_MANUAL	1
47*ae6f196aSLaurent Pinchart 
48*ae6f196aSLaurent Pinchart #define IMX219_REG_EXCK_FREQ		CCI_REG16(0x012a)
49*ae6f196aSLaurent Pinchart #define IMX219_EXCK_FREQ(n)		((n) * 256)		/* n expressed in MHz */
50*ae6f196aSLaurent Pinchart 
512e943af4SLaurent Pinchart /* Analog gain control */
522e943af4SLaurent Pinchart #define IMX219_REG_ANALOG_GAIN		CCI_REG8(0x0157)
532e943af4SLaurent Pinchart #define IMX219_ANA_GAIN_MIN		0
542e943af4SLaurent Pinchart #define IMX219_ANA_GAIN_MAX		232
552e943af4SLaurent Pinchart #define IMX219_ANA_GAIN_STEP		1
562e943af4SLaurent Pinchart #define IMX219_ANA_GAIN_DEFAULT		0x0
572e943af4SLaurent Pinchart 
582e943af4SLaurent Pinchart /* Digital gain control */
592e943af4SLaurent Pinchart #define IMX219_REG_DIGITAL_GAIN		CCI_REG16(0x0158)
602e943af4SLaurent Pinchart #define IMX219_DGTL_GAIN_MIN		0x0100
612e943af4SLaurent Pinchart #define IMX219_DGTL_GAIN_MAX		0x0fff
622e943af4SLaurent Pinchart #define IMX219_DGTL_GAIN_DEFAULT	0x0100
632e943af4SLaurent Pinchart #define IMX219_DGTL_GAIN_STEP		1
642e943af4SLaurent Pinchart 
652e943af4SLaurent Pinchart /* Exposure control */
662e943af4SLaurent Pinchart #define IMX219_REG_EXPOSURE		CCI_REG16(0x015a)
672e943af4SLaurent Pinchart #define IMX219_EXPOSURE_MIN		4
682e943af4SLaurent Pinchart #define IMX219_EXPOSURE_STEP		1
692e943af4SLaurent Pinchart #define IMX219_EXPOSURE_DEFAULT		0x640
702e943af4SLaurent Pinchart #define IMX219_EXPOSURE_MAX		65535
712e943af4SLaurent Pinchart 
721283b3b8SDave Stevenson /* V_TIMING internal */
732e943af4SLaurent Pinchart #define IMX219_REG_VTS			CCI_REG16(0x0160)
741283b3b8SDave Stevenson #define IMX219_VTS_15FPS		0x0dc6
751283b3b8SDave Stevenson #define IMX219_VTS_30FPS_1080P		0x06e3
761283b3b8SDave Stevenson #define IMX219_VTS_30FPS_BINNED		0x06e3
7725130b8aSLad Prabhakar #define IMX219_VTS_30FPS_640x480	0x06e3
781283b3b8SDave Stevenson #define IMX219_VTS_MAX			0xffff
791283b3b8SDave Stevenson 
801283b3b8SDave Stevenson #define IMX219_VBLANK_MIN		4
811283b3b8SDave Stevenson 
821283b3b8SDave Stevenson /*Frame Length Line*/
831283b3b8SDave Stevenson #define IMX219_FLL_MIN			0x08a6
841283b3b8SDave Stevenson #define IMX219_FLL_MAX			0xffff
851283b3b8SDave Stevenson #define IMX219_FLL_STEP			1
861283b3b8SDave Stevenson #define IMX219_FLL_DEFAULT		0x0c98
871283b3b8SDave Stevenson 
881283b3b8SDave Stevenson /* HBLANK control - read only */
891283b3b8SDave Stevenson #define IMX219_PPL_DEFAULT		3448
901283b3b8SDave Stevenson 
91*ae6f196aSLaurent Pinchart #define IMX219_REG_LINE_LENGTH_A	CCI_REG16(0x0162)
92*ae6f196aSLaurent Pinchart #define IMX219_REG_X_ADD_STA_A		CCI_REG16(0x0164)
93*ae6f196aSLaurent Pinchart #define IMX219_REG_X_ADD_END_A		CCI_REG16(0x0166)
94*ae6f196aSLaurent Pinchart #define IMX219_REG_Y_ADD_STA_A		CCI_REG16(0x0168)
95*ae6f196aSLaurent Pinchart #define IMX219_REG_Y_ADD_END_A		CCI_REG16(0x016a)
96*ae6f196aSLaurent Pinchart #define IMX219_REG_X_OUTPUT_SIZE	CCI_REG16(0x016c)
97*ae6f196aSLaurent Pinchart #define IMX219_REG_Y_OUTPUT_SIZE	CCI_REG16(0x016e)
98*ae6f196aSLaurent Pinchart #define IMX219_REG_X_ODD_INC_A		CCI_REG8(0x0170)
99*ae6f196aSLaurent Pinchart #define IMX219_REG_Y_ODD_INC_A		CCI_REG8(0x0171)
1002e943af4SLaurent Pinchart #define IMX219_REG_ORIENTATION		CCI_REG8(0x0172)
1011283b3b8SDave Stevenson 
102ef86447eSJai Luthra /* Binning  Mode */
1032e943af4SLaurent Pinchart #define IMX219_REG_BINNING_MODE		CCI_REG16(0x0174)
104ef86447eSJai Luthra #define IMX219_BINNING_NONE		0x0000
105ef86447eSJai Luthra #define IMX219_BINNING_2X2		0x0101
106ef86447eSJai Luthra #define IMX219_BINNING_2X2_ANALOG	0x0303
107ef86447eSJai Luthra 
108*ae6f196aSLaurent Pinchart #define IMX219_REG_CSI_DATA_FORMAT_A	CCI_REG16(0x018c)
109*ae6f196aSLaurent Pinchart 
110*ae6f196aSLaurent Pinchart /* PLL Settings */
111*ae6f196aSLaurent Pinchart #define IMX219_REG_VTPXCK_DIV		CCI_REG8(0x0301)
112*ae6f196aSLaurent Pinchart #define IMX219_REG_VTSYCK_DIV		CCI_REG8(0x0303)
113*ae6f196aSLaurent Pinchart #define IMX219_REG_PREPLLCK_VT_DIV	CCI_REG8(0x0304)
114*ae6f196aSLaurent Pinchart #define IMX219_REG_PREPLLCK_OP_DIV	CCI_REG8(0x0305)
115*ae6f196aSLaurent Pinchart #define IMX219_REG_PLL_VT_MPY		CCI_REG16(0x0306)
116*ae6f196aSLaurent Pinchart #define IMX219_REG_OPPXCK_DIV		CCI_REG8(0x0309)
117*ae6f196aSLaurent Pinchart #define IMX219_REG_OPSYCK_DIV		CCI_REG8(0x030b)
118*ae6f196aSLaurent Pinchart #define IMX219_REG_PLL_OP_MPY		CCI_REG16(0x030c)
119*ae6f196aSLaurent Pinchart 
1201283b3b8SDave Stevenson /* Test Pattern Control */
1212e943af4SLaurent Pinchart #define IMX219_REG_TEST_PATTERN		CCI_REG16(0x0600)
1221283b3b8SDave Stevenson #define IMX219_TEST_PATTERN_DISABLE	0
1231283b3b8SDave Stevenson #define IMX219_TEST_PATTERN_SOLID_COLOR	1
1241283b3b8SDave Stevenson #define IMX219_TEST_PATTERN_COLOR_BARS	2
1251283b3b8SDave Stevenson #define IMX219_TEST_PATTERN_GREY_COLOR	3
1261283b3b8SDave Stevenson #define IMX219_TEST_PATTERN_PN9		4
1271283b3b8SDave Stevenson 
1281283b3b8SDave Stevenson /* Test pattern colour components */
1292e943af4SLaurent Pinchart #define IMX219_REG_TESTP_RED		CCI_REG16(0x0602)
1302e943af4SLaurent Pinchart #define IMX219_REG_TESTP_GREENR		CCI_REG16(0x0604)
1312e943af4SLaurent Pinchart #define IMX219_REG_TESTP_BLUE		CCI_REG16(0x0606)
1322e943af4SLaurent Pinchart #define IMX219_REG_TESTP_GREENB		CCI_REG16(0x0608)
1331283b3b8SDave Stevenson #define IMX219_TESTP_COLOUR_MIN		0
1341283b3b8SDave Stevenson #define IMX219_TESTP_COLOUR_MAX		0x03ff
1351283b3b8SDave Stevenson #define IMX219_TESTP_COLOUR_STEP	1
1361283b3b8SDave Stevenson #define IMX219_TESTP_RED_DEFAULT	IMX219_TESTP_COLOUR_MAX
1371283b3b8SDave Stevenson #define IMX219_TESTP_GREENR_DEFAULT	0
1381283b3b8SDave Stevenson #define IMX219_TESTP_BLUE_DEFAULT	0
1391283b3b8SDave Stevenson #define IMX219_TESTP_GREENB_DEFAULT	0
1401283b3b8SDave Stevenson 
141*ae6f196aSLaurent Pinchart #define IMX219_REG_TP_WINDOW_WIDTH	CCI_REG16(0x0624)
142*ae6f196aSLaurent Pinchart #define IMX219_REG_TP_WINDOW_HEIGHT	CCI_REG16(0x0626)
143*ae6f196aSLaurent Pinchart 
1442e943af4SLaurent Pinchart /* External clock frequency is 24.0M */
1452e943af4SLaurent Pinchart #define IMX219_XCLK_FREQ		24000000
1462e943af4SLaurent Pinchart 
1472e943af4SLaurent Pinchart /* Pixel rate is fixed for all the modes */
1482e943af4SLaurent Pinchart #define IMX219_PIXEL_RATE		182400000
1492e943af4SLaurent Pinchart #define IMX219_PIXEL_RATE_4LANE		280800000
1502e943af4SLaurent Pinchart 
1512e943af4SLaurent Pinchart #define IMX219_DEFAULT_LINK_FREQ	456000000
1522e943af4SLaurent Pinchart #define IMX219_DEFAULT_LINK_FREQ_4LANE	363000000
1532e943af4SLaurent Pinchart 
154e6d4ef7dSJacopo Mondi /* IMX219 native and active pixel array size. */
155e6d4ef7dSJacopo Mondi #define IMX219_NATIVE_WIDTH		3296U
156e6d4ef7dSJacopo Mondi #define IMX219_NATIVE_HEIGHT		2480U
157e6d4ef7dSJacopo Mondi #define IMX219_PIXEL_ARRAY_LEFT		8U
158e6d4ef7dSJacopo Mondi #define IMX219_PIXEL_ARRAY_TOP		8U
159e6d4ef7dSJacopo Mondi #define IMX219_PIXEL_ARRAY_WIDTH	3280U
160e6d4ef7dSJacopo Mondi #define IMX219_PIXEL_ARRAY_HEIGHT	2464U
161e6d4ef7dSJacopo Mondi 
1621283b3b8SDave Stevenson struct imx219_reg_list {
1631283b3b8SDave Stevenson 	unsigned int num_of_regs;
1642e943af4SLaurent Pinchart 	const struct cci_reg_sequence *regs;
1651283b3b8SDave Stevenson };
1661283b3b8SDave Stevenson 
1671283b3b8SDave Stevenson /* Mode : resolution and related config&values */
1681283b3b8SDave Stevenson struct imx219_mode {
1691283b3b8SDave Stevenson 	/* Frame width */
1701283b3b8SDave Stevenson 	unsigned int width;
1711283b3b8SDave Stevenson 	/* Frame height */
1721283b3b8SDave Stevenson 	unsigned int height;
1731283b3b8SDave Stevenson 
174e6d4ef7dSJacopo Mondi 	/* Analog crop rectangle. */
175e6d4ef7dSJacopo Mondi 	struct v4l2_rect crop;
176e6d4ef7dSJacopo Mondi 
1771283b3b8SDave Stevenson 	/* V-timing */
1781283b3b8SDave Stevenson 	unsigned int vts_def;
1791283b3b8SDave Stevenson 
1801283b3b8SDave Stevenson 	/* Default register values */
1811283b3b8SDave Stevenson 	struct imx219_reg_list reg_list;
182ef86447eSJai Luthra 
183ef86447eSJai Luthra 	/* 2x2 binning is used */
184ef86447eSJai Luthra 	bool binning;
1851283b3b8SDave Stevenson };
1861283b3b8SDave Stevenson 
1872e943af4SLaurent Pinchart static const struct cci_reg_sequence imx219_common_regs[] = {
188*ae6f196aSLaurent Pinchart 	{ IMX219_REG_MODE_SELECT, 0x00 },	/* Mode Select */
18985084559SAdam Ford 
19085084559SAdam Ford 	/* To Access Addresses 3000-5fff, send the following commands */
1912e943af4SLaurent Pinchart 	{ CCI_REG8(0x30eb), 0x0c },
1922e943af4SLaurent Pinchart 	{ CCI_REG8(0x30eb), 0x05 },
1932e943af4SLaurent Pinchart 	{ CCI_REG8(0x300a), 0xff },
1942e943af4SLaurent Pinchart 	{ CCI_REG8(0x300b), 0xff },
1952e943af4SLaurent Pinchart 	{ CCI_REG8(0x30eb), 0x05 },
1962e943af4SLaurent Pinchart 	{ CCI_REG8(0x30eb), 0x09 },
19785084559SAdam Ford 
19885084559SAdam Ford 	/* PLL Clock Table */
199*ae6f196aSLaurent Pinchart 	{ IMX219_REG_VTPXCK_DIV, 5 },
200*ae6f196aSLaurent Pinchart 	{ IMX219_REG_VTSYCK_DIV, 1 },
201*ae6f196aSLaurent Pinchart 	{ IMX219_REG_PREPLLCK_VT_DIV, 3 },	/* 0x03 = AUTO set */
202*ae6f196aSLaurent Pinchart 	{ IMX219_REG_PREPLLCK_OP_DIV, 3 },	/* 0x03 = AUTO set */
203*ae6f196aSLaurent Pinchart 	{ IMX219_REG_PLL_VT_MPY, 57 },
204*ae6f196aSLaurent Pinchart 	{ IMX219_REG_OPSYCK_DIV, 1 },
205*ae6f196aSLaurent Pinchart 	{ IMX219_REG_PLL_OP_MPY, 114 },
20685084559SAdam Ford 
20785084559SAdam Ford 	/* Undocumented registers */
2082e943af4SLaurent Pinchart 	{ CCI_REG8(0x455e), 0x00 },
2092e943af4SLaurent Pinchart 	{ CCI_REG8(0x471e), 0x4b },
2102e943af4SLaurent Pinchart 	{ CCI_REG8(0x4767), 0x0f },
2112e943af4SLaurent Pinchart 	{ CCI_REG8(0x4750), 0x14 },
2122e943af4SLaurent Pinchart 	{ CCI_REG8(0x4540), 0x00 },
2132e943af4SLaurent Pinchart 	{ CCI_REG8(0x47b4), 0x14 },
2142e943af4SLaurent Pinchart 	{ CCI_REG8(0x4713), 0x30 },
2152e943af4SLaurent Pinchart 	{ CCI_REG8(0x478b), 0x10 },
2162e943af4SLaurent Pinchart 	{ CCI_REG8(0x478f), 0x10 },
2172e943af4SLaurent Pinchart 	{ CCI_REG8(0x4793), 0x10 },
2182e943af4SLaurent Pinchart 	{ CCI_REG8(0x4797), 0x0e },
2192e943af4SLaurent Pinchart 	{ CCI_REG8(0x479b), 0x0e },
22085084559SAdam Ford 
22185084559SAdam Ford 	/* Frame Bank Register Group "A" */
222*ae6f196aSLaurent Pinchart 	{ IMX219_REG_LINE_LENGTH_A, 3448 },
223*ae6f196aSLaurent Pinchart 	{ IMX219_REG_X_ODD_INC_A, 1 },
224*ae6f196aSLaurent Pinchart 	{ IMX219_REG_Y_ODD_INC_A, 1 },
22585084559SAdam Ford 
22685084559SAdam Ford 	/* Output setup registers */
227*ae6f196aSLaurent Pinchart 	{ IMX219_REG_DPHY_CTRL, IMX219_DPHY_CTRL_TIMING_AUTO },
228*ae6f196aSLaurent Pinchart 	{ IMX219_REG_EXCK_FREQ, IMX219_EXCK_FREQ(IMX219_XCLK_FREQ / 1000000) },
22985084559SAdam Ford };
23085084559SAdam Ford 
23185084559SAdam Ford /*
23285084559SAdam Ford  * Register sets lifted off the i2C interface from the Raspberry Pi firmware
23385084559SAdam Ford  * driver.
23485084559SAdam Ford  * 3280x2464 = mode 2, 1920x1080 = mode 1, 1640x1232 = mode 4, 640x480 = mode 7.
23585084559SAdam Ford  */
2362e943af4SLaurent Pinchart static const struct cci_reg_sequence mode_3280x2464_regs[] = {
237*ae6f196aSLaurent Pinchart 	{ IMX219_REG_X_ADD_STA_A, 0 },
238*ae6f196aSLaurent Pinchart 	{ IMX219_REG_X_ADD_END_A, 3279 },
239*ae6f196aSLaurent Pinchart 	{ IMX219_REG_Y_ADD_STA_A, 0 },
240*ae6f196aSLaurent Pinchart 	{ IMX219_REG_Y_ADD_END_A, 2463 },
241*ae6f196aSLaurent Pinchart 	{ IMX219_REG_X_OUTPUT_SIZE, 3280 },
242*ae6f196aSLaurent Pinchart 	{ IMX219_REG_Y_OUTPUT_SIZE, 2464 },
243*ae6f196aSLaurent Pinchart 	{ IMX219_REG_TP_WINDOW_WIDTH, 3280 },
244*ae6f196aSLaurent Pinchart 	{ IMX219_REG_TP_WINDOW_HEIGHT, 2464 },
2451283b3b8SDave Stevenson };
2461283b3b8SDave Stevenson 
2472e943af4SLaurent Pinchart static const struct cci_reg_sequence mode_1920_1080_regs[] = {
248*ae6f196aSLaurent Pinchart 	{ IMX219_REG_X_ADD_STA_A, 680 },
249*ae6f196aSLaurent Pinchart 	{ IMX219_REG_X_ADD_END_A, 2599 },
250*ae6f196aSLaurent Pinchart 	{ IMX219_REG_Y_ADD_STA_A, 692 },
251*ae6f196aSLaurent Pinchart 	{ IMX219_REG_Y_ADD_END_A, 1771 },
252*ae6f196aSLaurent Pinchart 	{ IMX219_REG_X_OUTPUT_SIZE, 1920 },
253*ae6f196aSLaurent Pinchart 	{ IMX219_REG_Y_OUTPUT_SIZE, 1080 },
254*ae6f196aSLaurent Pinchart 	{ IMX219_REG_TP_WINDOW_WIDTH, 1920 },
255*ae6f196aSLaurent Pinchart 	{ IMX219_REG_TP_WINDOW_HEIGHT, 1080 },
2561283b3b8SDave Stevenson };
2571283b3b8SDave Stevenson 
2582e943af4SLaurent Pinchart static const struct cci_reg_sequence mode_1640_1232_regs[] = {
259*ae6f196aSLaurent Pinchart 	{ IMX219_REG_X_ADD_STA_A, 0 },
260*ae6f196aSLaurent Pinchart 	{ IMX219_REG_X_ADD_END_A, 3279 },
261*ae6f196aSLaurent Pinchart 	{ IMX219_REG_Y_ADD_STA_A, 0 },
262*ae6f196aSLaurent Pinchart 	{ IMX219_REG_Y_ADD_END_A, 2463 },
263*ae6f196aSLaurent Pinchart 	{ IMX219_REG_X_OUTPUT_SIZE, 1640 },
264*ae6f196aSLaurent Pinchart 	{ IMX219_REG_Y_OUTPUT_SIZE, 1232 },
265*ae6f196aSLaurent Pinchart 	{ IMX219_REG_TP_WINDOW_WIDTH, 1640 },
266*ae6f196aSLaurent Pinchart 	{ IMX219_REG_TP_WINDOW_HEIGHT, 1232 },
2671283b3b8SDave Stevenson };
2681283b3b8SDave Stevenson 
2692e943af4SLaurent Pinchart static const struct cci_reg_sequence mode_640_480_regs[] = {
270*ae6f196aSLaurent Pinchart 	{ IMX219_REG_X_ADD_STA_A, 1000 },
271*ae6f196aSLaurent Pinchart 	{ IMX219_REG_X_ADD_END_A, 2279 },
272*ae6f196aSLaurent Pinchart 	{ IMX219_REG_Y_ADD_STA_A, 752 },
273*ae6f196aSLaurent Pinchart 	{ IMX219_REG_Y_ADD_END_A, 1711 },
274*ae6f196aSLaurent Pinchart 	{ IMX219_REG_X_OUTPUT_SIZE, 640 },
275*ae6f196aSLaurent Pinchart 	{ IMX219_REG_Y_OUTPUT_SIZE, 480 },
276*ae6f196aSLaurent Pinchart 	{ IMX219_REG_TP_WINDOW_WIDTH, 1640 },
277*ae6f196aSLaurent Pinchart 	{ IMX219_REG_TP_WINDOW_HEIGHT, 1232 },
27825130b8aSLad Prabhakar };
27925130b8aSLad Prabhakar 
2802e943af4SLaurent Pinchart static const struct cci_reg_sequence raw8_framefmt_regs[] = {
281*ae6f196aSLaurent Pinchart 	{ IMX219_REG_CSI_DATA_FORMAT_A, 0x0808 },
282*ae6f196aSLaurent Pinchart 	{ IMX219_REG_OPPXCK_DIV, 8 },
28322da1d56SLad Prabhakar };
28422da1d56SLad Prabhakar 
2852e943af4SLaurent Pinchart static const struct cci_reg_sequence raw10_framefmt_regs[] = {
286*ae6f196aSLaurent Pinchart 	{ IMX219_REG_CSI_DATA_FORMAT_A, 0x0a0a },
287*ae6f196aSLaurent Pinchart 	{ IMX219_REG_OPPXCK_DIV, 10 },
28822da1d56SLad Prabhakar };
28922da1d56SLad Prabhakar 
29049b94d58SAndrey Konovalov static const s64 imx219_link_freq_menu[] = {
29149b94d58SAndrey Konovalov 	IMX219_DEFAULT_LINK_FREQ,
29249b94d58SAndrey Konovalov };
29349b94d58SAndrey Konovalov 
294ceddfd44SAdam Ford static const s64 imx219_link_freq_4lane_menu[] = {
295ceddfd44SAdam Ford 	IMX219_DEFAULT_LINK_FREQ_4LANE,
296ceddfd44SAdam Ford };
297ceddfd44SAdam Ford 
2981283b3b8SDave Stevenson static const char * const imx219_test_pattern_menu[] = {
2991283b3b8SDave Stevenson 	"Disabled",
3001283b3b8SDave Stevenson 	"Color Bars",
3011283b3b8SDave Stevenson 	"Solid Color",
3021283b3b8SDave Stevenson 	"Grey Color Bars",
3031283b3b8SDave Stevenson 	"PN9"
3041283b3b8SDave Stevenson };
3051283b3b8SDave Stevenson 
3061283b3b8SDave Stevenson static const int imx219_test_pattern_val[] = {
3071283b3b8SDave Stevenson 	IMX219_TEST_PATTERN_DISABLE,
3081283b3b8SDave Stevenson 	IMX219_TEST_PATTERN_COLOR_BARS,
3091283b3b8SDave Stevenson 	IMX219_TEST_PATTERN_SOLID_COLOR,
3101283b3b8SDave Stevenson 	IMX219_TEST_PATTERN_GREY_COLOR,
3111283b3b8SDave Stevenson 	IMX219_TEST_PATTERN_PN9,
3121283b3b8SDave Stevenson };
3131283b3b8SDave Stevenson 
3141283b3b8SDave Stevenson /* regulator supplies */
3151283b3b8SDave Stevenson static const char * const imx219_supply_name[] = {
3161283b3b8SDave Stevenson 	/* Supplies can be enabled in any order */
3171283b3b8SDave Stevenson 	"VANA",  /* Analog (2.8V) supply */
3181283b3b8SDave Stevenson 	"VDIG",  /* Digital Core (1.8V) supply */
3191283b3b8SDave Stevenson 	"VDDL",  /* IF (1.2V) supply */
3201283b3b8SDave Stevenson };
3211283b3b8SDave Stevenson 
3221283b3b8SDave Stevenson #define IMX219_NUM_SUPPLIES ARRAY_SIZE(imx219_supply_name)
3231283b3b8SDave Stevenson 
3241283b3b8SDave Stevenson /*
32522da1d56SLad Prabhakar  * The supported formats.
32622da1d56SLad Prabhakar  * This table MUST contain 4 entries per format, to cover the various flip
32722da1d56SLad Prabhakar  * combinations in the order
32822da1d56SLad Prabhakar  * - no flip
32922da1d56SLad Prabhakar  * - h flip
33022da1d56SLad Prabhakar  * - v flip
33122da1d56SLad Prabhakar  * - h&v flips
33222da1d56SLad Prabhakar  */
333917e26cbSJean-Michel Hautbois static const u32 imx219_mbus_formats[] = {
33422da1d56SLad Prabhakar 	MEDIA_BUS_FMT_SRGGB10_1X10,
33522da1d56SLad Prabhakar 	MEDIA_BUS_FMT_SGRBG10_1X10,
33622da1d56SLad Prabhakar 	MEDIA_BUS_FMT_SGBRG10_1X10,
33722da1d56SLad Prabhakar 	MEDIA_BUS_FMT_SBGGR10_1X10,
33822da1d56SLad Prabhakar 
33922da1d56SLad Prabhakar 	MEDIA_BUS_FMT_SRGGB8_1X8,
34022da1d56SLad Prabhakar 	MEDIA_BUS_FMT_SGRBG8_1X8,
34122da1d56SLad Prabhakar 	MEDIA_BUS_FMT_SGBRG8_1X8,
34222da1d56SLad Prabhakar 	MEDIA_BUS_FMT_SBGGR8_1X8,
34322da1d56SLad Prabhakar };
34422da1d56SLad Prabhakar 
34522da1d56SLad Prabhakar /*
3461283b3b8SDave Stevenson  * Initialisation delay between XCLR low->high and the moment when the sensor
3471283b3b8SDave Stevenson  * can start capture (i.e. can leave software stanby) must be not less than:
3481283b3b8SDave Stevenson  *   t4 + max(t5, t6 + <time to initialize the sensor register over I2C>)
3491283b3b8SDave Stevenson  * where
3501283b3b8SDave Stevenson  *   t4 is fixed, and is max 200uS,
3511283b3b8SDave Stevenson  *   t5 is fixed, and is 6000uS,
3521283b3b8SDave Stevenson  *   t6 depends on the sensor external clock, and is max 32000 clock periods.
3531283b3b8SDave Stevenson  * As per sensor datasheet, the external clock must be from 6MHz to 27MHz.
3541283b3b8SDave Stevenson  * So for any acceptable external clock t6 is always within the range of
3551283b3b8SDave Stevenson  * 1185 to 5333 uS, and is always less than t5.
3561283b3b8SDave Stevenson  * For this reason this is always safe to wait (t4 + t5) = 6200 uS, then
3571283b3b8SDave Stevenson  * initialize the sensor over I2C, and then exit the software standby.
3581283b3b8SDave Stevenson  *
3591283b3b8SDave Stevenson  * This start-up time can be optimized a bit more, if we start the writes
3601283b3b8SDave Stevenson  * over I2C after (t4+t6), but before (t4+t5) expires. But then sensor
3611283b3b8SDave Stevenson  * initialization over I2C may complete before (t4+t5) expires, and we must
3621283b3b8SDave Stevenson  * ensure that capture is not started before (t4+t5).
3631283b3b8SDave Stevenson  *
3641283b3b8SDave Stevenson  * This delay doesn't account for the power supply startup time. If needed,
3651283b3b8SDave Stevenson  * this should be taken care of via the regulator framework. E.g. in the
3661283b3b8SDave Stevenson  * case of DT for regulator-fixed one should define the startup-delay-us
3671283b3b8SDave Stevenson  * property.
3681283b3b8SDave Stevenson  */
3691283b3b8SDave Stevenson #define IMX219_XCLR_MIN_DELAY_US	6200
3701283b3b8SDave Stevenson #define IMX219_XCLR_DELAY_RANGE_US	1000
3711283b3b8SDave Stevenson 
3721283b3b8SDave Stevenson /* Mode configs */
3731283b3b8SDave Stevenson static const struct imx219_mode supported_modes[] = {
3741283b3b8SDave Stevenson 	{
3751283b3b8SDave Stevenson 		/* 8MPix 15fps mode */
3761283b3b8SDave Stevenson 		.width = 3280,
3771283b3b8SDave Stevenson 		.height = 2464,
378e6d4ef7dSJacopo Mondi 		.crop = {
3791ed36ecdSHans Verkuil 			.left = IMX219_PIXEL_ARRAY_LEFT,
3801ed36ecdSHans Verkuil 			.top = IMX219_PIXEL_ARRAY_TOP,
381e6d4ef7dSJacopo Mondi 			.width = 3280,
382e6d4ef7dSJacopo Mondi 			.height = 2464
383e6d4ef7dSJacopo Mondi 		},
3841283b3b8SDave Stevenson 		.vts_def = IMX219_VTS_15FPS,
3851283b3b8SDave Stevenson 		.reg_list = {
3861283b3b8SDave Stevenson 			.num_of_regs = ARRAY_SIZE(mode_3280x2464_regs),
3871283b3b8SDave Stevenson 			.regs = mode_3280x2464_regs,
3881283b3b8SDave Stevenson 		},
389ef86447eSJai Luthra 		.binning = false,
3901283b3b8SDave Stevenson 	},
3911283b3b8SDave Stevenson 	{
3921283b3b8SDave Stevenson 		/* 1080P 30fps cropped */
3931283b3b8SDave Stevenson 		.width = 1920,
3941283b3b8SDave Stevenson 		.height = 1080,
395e6d4ef7dSJacopo Mondi 		.crop = {
3961ed36ecdSHans Verkuil 			.left = 688,
3971ed36ecdSHans Verkuil 			.top = 700,
398e6d4ef7dSJacopo Mondi 			.width = 1920,
399e6d4ef7dSJacopo Mondi 			.height = 1080
400e6d4ef7dSJacopo Mondi 		},
4011283b3b8SDave Stevenson 		.vts_def = IMX219_VTS_30FPS_1080P,
4021283b3b8SDave Stevenson 		.reg_list = {
4031283b3b8SDave Stevenson 			.num_of_regs = ARRAY_SIZE(mode_1920_1080_regs),
4041283b3b8SDave Stevenson 			.regs = mode_1920_1080_regs,
4051283b3b8SDave Stevenson 		},
406ef86447eSJai Luthra 		.binning = false,
4071283b3b8SDave Stevenson 	},
4081283b3b8SDave Stevenson 	{
4091283b3b8SDave Stevenson 		/* 2x2 binned 30fps mode */
4101283b3b8SDave Stevenson 		.width = 1640,
4111283b3b8SDave Stevenson 		.height = 1232,
412e6d4ef7dSJacopo Mondi 		.crop = {
4131ed36ecdSHans Verkuil 			.left = IMX219_PIXEL_ARRAY_LEFT,
4141ed36ecdSHans Verkuil 			.top = IMX219_PIXEL_ARRAY_TOP,
415e6d4ef7dSJacopo Mondi 			.width = 3280,
416e6d4ef7dSJacopo Mondi 			.height = 2464
417e6d4ef7dSJacopo Mondi 		},
4181283b3b8SDave Stevenson 		.vts_def = IMX219_VTS_30FPS_BINNED,
4191283b3b8SDave Stevenson 		.reg_list = {
4201283b3b8SDave Stevenson 			.num_of_regs = ARRAY_SIZE(mode_1640_1232_regs),
4211283b3b8SDave Stevenson 			.regs = mode_1640_1232_regs,
4221283b3b8SDave Stevenson 		},
423ef86447eSJai Luthra 		.binning = true,
4241283b3b8SDave Stevenson 	},
42525130b8aSLad Prabhakar 	{
42625130b8aSLad Prabhakar 		/* 640x480 30fps mode */
42725130b8aSLad Prabhakar 		.width = 640,
42825130b8aSLad Prabhakar 		.height = 480,
429e6d4ef7dSJacopo Mondi 		.crop = {
4301ed36ecdSHans Verkuil 			.left = 1008,
4311ed36ecdSHans Verkuil 			.top = 760,
432e6d4ef7dSJacopo Mondi 			.width = 1280,
433e6d4ef7dSJacopo Mondi 			.height = 960
434e6d4ef7dSJacopo Mondi 		},
43525130b8aSLad Prabhakar 		.vts_def = IMX219_VTS_30FPS_640x480,
43625130b8aSLad Prabhakar 		.reg_list = {
43725130b8aSLad Prabhakar 			.num_of_regs = ARRAY_SIZE(mode_640_480_regs),
43825130b8aSLad Prabhakar 			.regs = mode_640_480_regs,
43925130b8aSLad Prabhakar 		},
440ef86447eSJai Luthra 		.binning = true,
44125130b8aSLad Prabhakar 	},
4421283b3b8SDave Stevenson };
4431283b3b8SDave Stevenson 
4441283b3b8SDave Stevenson struct imx219 {
4451283b3b8SDave Stevenson 	struct v4l2_subdev sd;
4461283b3b8SDave Stevenson 	struct media_pad pad;
4471283b3b8SDave Stevenson 
4482e943af4SLaurent Pinchart 	struct regmap *regmap;
4491283b3b8SDave Stevenson 	struct clk *xclk; /* system clock to IMX219 */
4501283b3b8SDave Stevenson 	u32 xclk_freq;
4511283b3b8SDave Stevenson 
4521283b3b8SDave Stevenson 	struct gpio_desc *reset_gpio;
4531283b3b8SDave Stevenson 	struct regulator_bulk_data supplies[IMX219_NUM_SUPPLIES];
4541283b3b8SDave Stevenson 
4551283b3b8SDave Stevenson 	struct v4l2_ctrl_handler ctrl_handler;
4561283b3b8SDave Stevenson 	/* V4L2 Controls */
4571283b3b8SDave Stevenson 	struct v4l2_ctrl *pixel_rate;
45849b94d58SAndrey Konovalov 	struct v4l2_ctrl *link_freq;
4591283b3b8SDave Stevenson 	struct v4l2_ctrl *exposure;
4601283b3b8SDave Stevenson 	struct v4l2_ctrl *vflip;
4611283b3b8SDave Stevenson 	struct v4l2_ctrl *hflip;
4621283b3b8SDave Stevenson 	struct v4l2_ctrl *vblank;
4631283b3b8SDave Stevenson 	struct v4l2_ctrl *hblank;
4641283b3b8SDave Stevenson 
4651283b3b8SDave Stevenson 	/* Current mode */
4661283b3b8SDave Stevenson 	const struct imx219_mode *mode;
4671283b3b8SDave Stevenson 
4681283b3b8SDave Stevenson 	/* Streaming on/off */
4691283b3b8SDave Stevenson 	bool streaming;
470ceddfd44SAdam Ford 
471ceddfd44SAdam Ford 	/* Two or Four lanes */
472ceddfd44SAdam Ford 	u8 lanes;
4731283b3b8SDave Stevenson };
4741283b3b8SDave Stevenson 
to_imx219(struct v4l2_subdev * _sd)4751283b3b8SDave Stevenson static inline struct imx219 *to_imx219(struct v4l2_subdev *_sd)
4761283b3b8SDave Stevenson {
4771283b3b8SDave Stevenson 	return container_of(_sd, struct imx219, sd);
4781283b3b8SDave Stevenson }
4791283b3b8SDave Stevenson 
4801283b3b8SDave Stevenson /* Get bayer order based on flip setting. */
imx219_get_format_code(struct imx219 * imx219,u32 code)48122da1d56SLad Prabhakar static u32 imx219_get_format_code(struct imx219 *imx219, u32 code)
4821283b3b8SDave Stevenson {
48322da1d56SLad Prabhakar 	unsigned int i;
4841283b3b8SDave Stevenson 
485917e26cbSJean-Michel Hautbois 	for (i = 0; i < ARRAY_SIZE(imx219_mbus_formats); i++)
486917e26cbSJean-Michel Hautbois 		if (imx219_mbus_formats[i] == code)
48722da1d56SLad Prabhakar 			break;
48822da1d56SLad Prabhakar 
489917e26cbSJean-Michel Hautbois 	if (i >= ARRAY_SIZE(imx219_mbus_formats))
49022da1d56SLad Prabhakar 		i = 0;
49122da1d56SLad Prabhakar 
49222da1d56SLad Prabhakar 	i = (i & ~3) | (imx219->vflip->val ? 2 : 0) |
49322da1d56SLad Prabhakar 	    (imx219->hflip->val ? 1 : 0);
49422da1d56SLad Prabhakar 
495917e26cbSJean-Michel Hautbois 	return imx219_mbus_formats[i];
49622da1d56SLad Prabhakar }
49722da1d56SLad Prabhakar 
imx219_set_ctrl(struct v4l2_ctrl * ctrl)4981283b3b8SDave Stevenson static int imx219_set_ctrl(struct v4l2_ctrl *ctrl)
4991283b3b8SDave Stevenson {
5001283b3b8SDave Stevenson 	struct imx219 *imx219 =
5011283b3b8SDave Stevenson 		container_of(ctrl->handler, struct imx219, ctrl_handler);
5021283b3b8SDave Stevenson 	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
5032e943af4SLaurent Pinchart 	int ret = 0;
5041283b3b8SDave Stevenson 
5051283b3b8SDave Stevenson 	if (ctrl->id == V4L2_CID_VBLANK) {
5061283b3b8SDave Stevenson 		int exposure_max, exposure_def;
5071283b3b8SDave Stevenson 
5081283b3b8SDave Stevenson 		/* Update max exposure while meeting expected vblanking */
5091283b3b8SDave Stevenson 		exposure_max = imx219->mode->height + ctrl->val - 4;
5101283b3b8SDave Stevenson 		exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
5111283b3b8SDave Stevenson 			exposure_max : IMX219_EXPOSURE_DEFAULT;
5121283b3b8SDave Stevenson 		__v4l2_ctrl_modify_range(imx219->exposure,
5131283b3b8SDave Stevenson 					 imx219->exposure->minimum,
5141283b3b8SDave Stevenson 					 exposure_max, imx219->exposure->step,
5151283b3b8SDave Stevenson 					 exposure_def);
5161283b3b8SDave Stevenson 	}
5171283b3b8SDave Stevenson 
5181283b3b8SDave Stevenson 	/*
5191283b3b8SDave Stevenson 	 * Applying V4L2 control value only happens
5201283b3b8SDave Stevenson 	 * when power is up for streaming
5211283b3b8SDave Stevenson 	 */
5221283b3b8SDave Stevenson 	if (pm_runtime_get_if_in_use(&client->dev) == 0)
5231283b3b8SDave Stevenson 		return 0;
5241283b3b8SDave Stevenson 
5251283b3b8SDave Stevenson 	switch (ctrl->id) {
5261283b3b8SDave Stevenson 	case V4L2_CID_ANALOGUE_GAIN:
5272e943af4SLaurent Pinchart 		cci_write(imx219->regmap, IMX219_REG_ANALOG_GAIN,
5282e943af4SLaurent Pinchart 			  ctrl->val, &ret);
5291283b3b8SDave Stevenson 		break;
5301283b3b8SDave Stevenson 	case V4L2_CID_EXPOSURE:
5312e943af4SLaurent Pinchart 		cci_write(imx219->regmap, IMX219_REG_EXPOSURE,
5322e943af4SLaurent Pinchart 			  ctrl->val, &ret);
5331283b3b8SDave Stevenson 		break;
5341283b3b8SDave Stevenson 	case V4L2_CID_DIGITAL_GAIN:
5352e943af4SLaurent Pinchart 		cci_write(imx219->regmap, IMX219_REG_DIGITAL_GAIN,
5362e943af4SLaurent Pinchart 			  ctrl->val, &ret);
5371283b3b8SDave Stevenson 		break;
5381283b3b8SDave Stevenson 	case V4L2_CID_TEST_PATTERN:
5392e943af4SLaurent Pinchart 		cci_write(imx219->regmap, IMX219_REG_TEST_PATTERN,
5402e943af4SLaurent Pinchart 			  imx219_test_pattern_val[ctrl->val], &ret);
5411283b3b8SDave Stevenson 		break;
5421283b3b8SDave Stevenson 	case V4L2_CID_HFLIP:
5431283b3b8SDave Stevenson 	case V4L2_CID_VFLIP:
5442e943af4SLaurent Pinchart 		cci_write(imx219->regmap, IMX219_REG_ORIENTATION,
5452e943af4SLaurent Pinchart 			  imx219->hflip->val | imx219->vflip->val << 1, &ret);
5461283b3b8SDave Stevenson 		break;
5471283b3b8SDave Stevenson 	case V4L2_CID_VBLANK:
5482e943af4SLaurent Pinchart 		cci_write(imx219->regmap, IMX219_REG_VTS,
5492e943af4SLaurent Pinchart 			  imx219->mode->height + ctrl->val, &ret);
5501283b3b8SDave Stevenson 		break;
5511283b3b8SDave Stevenson 	case V4L2_CID_TEST_PATTERN_RED:
5522e943af4SLaurent Pinchart 		cci_write(imx219->regmap, IMX219_REG_TESTP_RED,
5532e943af4SLaurent Pinchart 			  ctrl->val, &ret);
5541283b3b8SDave Stevenson 		break;
5551283b3b8SDave Stevenson 	case V4L2_CID_TEST_PATTERN_GREENR:
5562e943af4SLaurent Pinchart 		cci_write(imx219->regmap, IMX219_REG_TESTP_GREENR,
5572e943af4SLaurent Pinchart 			  ctrl->val, &ret);
5581283b3b8SDave Stevenson 		break;
5591283b3b8SDave Stevenson 	case V4L2_CID_TEST_PATTERN_BLUE:
5602e943af4SLaurent Pinchart 		cci_write(imx219->regmap, IMX219_REG_TESTP_BLUE,
5612e943af4SLaurent Pinchart 			  ctrl->val, &ret);
5621283b3b8SDave Stevenson 		break;
5631283b3b8SDave Stevenson 	case V4L2_CID_TEST_PATTERN_GREENB:
5642e943af4SLaurent Pinchart 		cci_write(imx219->regmap, IMX219_REG_TESTP_GREENB,
5652e943af4SLaurent Pinchart 			  ctrl->val, &ret);
5661283b3b8SDave Stevenson 		break;
5671283b3b8SDave Stevenson 	default:
5681283b3b8SDave Stevenson 		dev_info(&client->dev,
5691283b3b8SDave Stevenson 			 "ctrl(id:0x%x,val:0x%x) is not handled\n",
5701283b3b8SDave Stevenson 			 ctrl->id, ctrl->val);
5711283b3b8SDave Stevenson 		ret = -EINVAL;
5721283b3b8SDave Stevenson 		break;
5731283b3b8SDave Stevenson 	}
5741283b3b8SDave Stevenson 
5751283b3b8SDave Stevenson 	pm_runtime_put(&client->dev);
5761283b3b8SDave Stevenson 
5771283b3b8SDave Stevenson 	return ret;
5781283b3b8SDave Stevenson }
5791283b3b8SDave Stevenson 
5801283b3b8SDave Stevenson static const struct v4l2_ctrl_ops imx219_ctrl_ops = {
5811283b3b8SDave Stevenson 	.s_ctrl = imx219_set_ctrl,
5821283b3b8SDave Stevenson };
5831283b3b8SDave Stevenson 
imx219_update_pad_format(struct imx219 * imx219,const struct imx219_mode * mode,struct v4l2_mbus_framefmt * fmt,u32 code)5847319d570SJacopo Mondi static void imx219_update_pad_format(struct imx219 *imx219,
5857319d570SJacopo Mondi 				     const struct imx219_mode *mode,
5867319d570SJacopo Mondi 				     struct v4l2_mbus_framefmt *fmt, u32 code)
5877319d570SJacopo Mondi {
5887319d570SJacopo Mondi 	/* Bayer order varies with flips */
5897319d570SJacopo Mondi 	fmt->code = imx219_get_format_code(imx219, code);
5907319d570SJacopo Mondi 	fmt->width = mode->width;
5917319d570SJacopo Mondi 	fmt->height = mode->height;
5927319d570SJacopo Mondi 	fmt->field = V4L2_FIELD_NONE;
5937319d570SJacopo Mondi 	fmt->colorspace = V4L2_COLORSPACE_RAW;
5947319d570SJacopo Mondi 	fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
5957319d570SJacopo Mondi 	fmt->xfer_func = V4L2_XFER_FUNC_NONE;
5967319d570SJacopo Mondi }
5977319d570SJacopo Mondi 
imx219_init_cfg(struct v4l2_subdev * sd,struct v4l2_subdev_state * state)5987e700847SJean-Michel Hautbois static int imx219_init_cfg(struct v4l2_subdev *sd,
5997e700847SJean-Michel Hautbois 			   struct v4l2_subdev_state *state)
6007e700847SJean-Michel Hautbois {
6017e700847SJean-Michel Hautbois 	struct imx219 *imx219 = to_imx219(sd);
6027e700847SJean-Michel Hautbois 	struct v4l2_mbus_framefmt *format;
6037e700847SJean-Michel Hautbois 	struct v4l2_rect *crop;
6047e700847SJean-Michel Hautbois 
6055cb218ffSLaurent Pinchart 	/* Initialize the format. */
6067e700847SJean-Michel Hautbois 	format = v4l2_subdev_get_pad_format(sd, state, 0);
6077319d570SJacopo Mondi 	imx219_update_pad_format(imx219, &supported_modes[0], format,
6087e700847SJean-Michel Hautbois 				 MEDIA_BUS_FMT_SRGGB10_1X10);
6097e700847SJean-Michel Hautbois 
6105cb218ffSLaurent Pinchart 	/* Initialize the crop rectangle. */
6117e700847SJean-Michel Hautbois 	crop = v4l2_subdev_get_pad_crop(sd, state, 0);
6127e700847SJean-Michel Hautbois 	crop->top = IMX219_PIXEL_ARRAY_TOP;
6137e700847SJean-Michel Hautbois 	crop->left = IMX219_PIXEL_ARRAY_LEFT;
6147e700847SJean-Michel Hautbois 	crop->width = IMX219_PIXEL_ARRAY_WIDTH;
6157e700847SJean-Michel Hautbois 	crop->height = IMX219_PIXEL_ARRAY_HEIGHT;
6167e700847SJean-Michel Hautbois 
6177e700847SJean-Michel Hautbois 	return 0;
6187e700847SJean-Michel Hautbois }
6197e700847SJean-Michel Hautbois 
imx219_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)6201283b3b8SDave Stevenson static int imx219_enum_mbus_code(struct v4l2_subdev *sd,
6210d346d2aSTomi Valkeinen 				 struct v4l2_subdev_state *sd_state,
6221283b3b8SDave Stevenson 				 struct v4l2_subdev_mbus_code_enum *code)
6231283b3b8SDave Stevenson {
6241283b3b8SDave Stevenson 	struct imx219 *imx219 = to_imx219(sd);
6251283b3b8SDave Stevenson 
626917e26cbSJean-Michel Hautbois 	if (code->index >= (ARRAY_SIZE(imx219_mbus_formats) / 4))
6271283b3b8SDave Stevenson 		return -EINVAL;
6281283b3b8SDave Stevenson 
629917e26cbSJean-Michel Hautbois 	code->code = imx219_get_format_code(imx219, imx219_mbus_formats[code->index * 4]);
6301283b3b8SDave Stevenson 
6311283b3b8SDave Stevenson 	return 0;
6321283b3b8SDave Stevenson }
6331283b3b8SDave Stevenson 
imx219_enum_frame_size(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_frame_size_enum * fse)6341283b3b8SDave Stevenson static int imx219_enum_frame_size(struct v4l2_subdev *sd,
6350d346d2aSTomi Valkeinen 				  struct v4l2_subdev_state *sd_state,
6361283b3b8SDave Stevenson 				  struct v4l2_subdev_frame_size_enum *fse)
6371283b3b8SDave Stevenson {
6381283b3b8SDave Stevenson 	struct imx219 *imx219 = to_imx219(sd);
63981015221SHans Verkuil 	u32 code;
6401283b3b8SDave Stevenson 
6411283b3b8SDave Stevenson 	if (fse->index >= ARRAY_SIZE(supported_modes))
6421283b3b8SDave Stevenson 		return -EINVAL;
6431283b3b8SDave Stevenson 
64481015221SHans Verkuil 	code = imx219_get_format_code(imx219, fse->code);
64581015221SHans Verkuil 	if (fse->code != code)
6461283b3b8SDave Stevenson 		return -EINVAL;
6471283b3b8SDave Stevenson 
6481283b3b8SDave Stevenson 	fse->min_width = supported_modes[fse->index].width;
6491283b3b8SDave Stevenson 	fse->max_width = fse->min_width;
6501283b3b8SDave Stevenson 	fse->min_height = supported_modes[fse->index].height;
6511283b3b8SDave Stevenson 	fse->max_height = fse->min_height;
6521283b3b8SDave Stevenson 
6531283b3b8SDave Stevenson 	return 0;
6541283b3b8SDave Stevenson }
6551283b3b8SDave Stevenson 
imx219_set_pad_format(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)6561283b3b8SDave Stevenson static int imx219_set_pad_format(struct v4l2_subdev *sd,
6570d346d2aSTomi Valkeinen 				 struct v4l2_subdev_state *sd_state,
6581283b3b8SDave Stevenson 				 struct v4l2_subdev_format *fmt)
6591283b3b8SDave Stevenson {
6601283b3b8SDave Stevenson 	struct imx219 *imx219 = to_imx219(sd);
6611283b3b8SDave Stevenson 	const struct imx219_mode *mode;
6621283b3b8SDave Stevenson 	int exposure_max, exposure_def, hblank;
663e8a5b1dfSJacopo Mondi 	struct v4l2_mbus_framefmt *format;
664bb2d0112SLaurent Pinchart 	struct v4l2_rect *crop;
66522da1d56SLad Prabhakar 
6661283b3b8SDave Stevenson 	mode = v4l2_find_nearest_size(supported_modes,
6671283b3b8SDave Stevenson 				      ARRAY_SIZE(supported_modes),
6681283b3b8SDave Stevenson 				      width, height,
6691283b3b8SDave Stevenson 				      fmt->format.width, fmt->format.height);
670e8a5b1dfSJacopo Mondi 
67134e3d3c9SJacopo Mondi 	imx219_update_pad_format(imx219, mode, &fmt->format, fmt->format.code);
672bb2d0112SLaurent Pinchart 
673e8a5b1dfSJacopo Mondi 	format = v4l2_subdev_get_pad_format(sd, sd_state, 0);
674bb2d0112SLaurent Pinchart 	crop = v4l2_subdev_get_pad_crop(sd, sd_state, 0);
675e8a5b1dfSJacopo Mondi 
676bb2d0112SLaurent Pinchart 	*format = fmt->format;
677bb2d0112SLaurent Pinchart 	*crop = mode->crop;
678bb2d0112SLaurent Pinchart 
679e8a5b1dfSJacopo Mondi 	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
6801283b3b8SDave Stevenson 		imx219->mode = mode;
6811283b3b8SDave Stevenson 		/* Update limits and set FPS to default */
6821283b3b8SDave Stevenson 		__v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN,
6831283b3b8SDave Stevenson 					 IMX219_VTS_MAX - mode->height, 1,
6841283b3b8SDave Stevenson 					 mode->vts_def - mode->height);
6851283b3b8SDave Stevenson 		__v4l2_ctrl_s_ctrl(imx219->vblank,
6861283b3b8SDave Stevenson 				   mode->vts_def - mode->height);
6871283b3b8SDave Stevenson 		/* Update max exposure while meeting expected vblanking */
6881283b3b8SDave Stevenson 		exposure_max = mode->vts_def - 4;
6891283b3b8SDave Stevenson 		exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
6901283b3b8SDave Stevenson 			exposure_max : IMX219_EXPOSURE_DEFAULT;
6911283b3b8SDave Stevenson 		__v4l2_ctrl_modify_range(imx219->exposure,
6921283b3b8SDave Stevenson 					 imx219->exposure->minimum,
6931283b3b8SDave Stevenson 					 exposure_max, imx219->exposure->step,
6941283b3b8SDave Stevenson 					 exposure_def);
6951283b3b8SDave Stevenson 		/*
6961283b3b8SDave Stevenson 		 * Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank
6971283b3b8SDave Stevenson 		 * depends on mode->width only, and is not changeble in any
6981283b3b8SDave Stevenson 		 * way other than changing the mode.
6991283b3b8SDave Stevenson 		 */
7001283b3b8SDave Stevenson 		hblank = IMX219_PPL_DEFAULT - mode->width;
7011283b3b8SDave Stevenson 		__v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1,
7021283b3b8SDave Stevenson 					 hblank);
7031283b3b8SDave Stevenson 	}
7041283b3b8SDave Stevenson 
7051283b3b8SDave Stevenson 	return 0;
7061283b3b8SDave Stevenson }
7071283b3b8SDave Stevenson 
imx219_set_framefmt(struct imx219 * imx219,const struct v4l2_mbus_framefmt * format)708e8a5b1dfSJacopo Mondi static int imx219_set_framefmt(struct imx219 *imx219,
709e8a5b1dfSJacopo Mondi 			       const struct v4l2_mbus_framefmt *format)
71022da1d56SLad Prabhakar {
711e8a5b1dfSJacopo Mondi 	switch (format->code) {
71222da1d56SLad Prabhakar 	case MEDIA_BUS_FMT_SRGGB8_1X8:
71322da1d56SLad Prabhakar 	case MEDIA_BUS_FMT_SGRBG8_1X8:
71422da1d56SLad Prabhakar 	case MEDIA_BUS_FMT_SGBRG8_1X8:
71522da1d56SLad Prabhakar 	case MEDIA_BUS_FMT_SBGGR8_1X8:
7162e943af4SLaurent Pinchart 		return cci_multi_reg_write(imx219->regmap, raw8_framefmt_regs,
7172e943af4SLaurent Pinchart 					   ARRAY_SIZE(raw8_framefmt_regs), NULL);
71822da1d56SLad Prabhakar 
71922da1d56SLad Prabhakar 	case MEDIA_BUS_FMT_SRGGB10_1X10:
72022da1d56SLad Prabhakar 	case MEDIA_BUS_FMT_SGRBG10_1X10:
72122da1d56SLad Prabhakar 	case MEDIA_BUS_FMT_SGBRG10_1X10:
72222da1d56SLad Prabhakar 	case MEDIA_BUS_FMT_SBGGR10_1X10:
7232e943af4SLaurent Pinchart 		return cci_multi_reg_write(imx219->regmap, raw10_framefmt_regs,
7242e943af4SLaurent Pinchart 					   ARRAY_SIZE(raw10_framefmt_regs), NULL);
72522da1d56SLad Prabhakar 	}
72622da1d56SLad Prabhakar 
72722da1d56SLad Prabhakar 	return -EINVAL;
72822da1d56SLad Prabhakar }
72922da1d56SLad Prabhakar 
imx219_set_binning(struct imx219 * imx219,const struct v4l2_mbus_framefmt * format)730e8a5b1dfSJacopo Mondi static int imx219_set_binning(struct imx219 *imx219,
731e8a5b1dfSJacopo Mondi 			      const struct v4l2_mbus_framefmt *format)
732ef86447eSJai Luthra {
7332e943af4SLaurent Pinchart 	if (!imx219->mode->binning)
7342e943af4SLaurent Pinchart 		return cci_write(imx219->regmap, IMX219_REG_BINNING_MODE,
7352e943af4SLaurent Pinchart 				 IMX219_BINNING_NONE, NULL);
736ef86447eSJai Luthra 
737e8a5b1dfSJacopo Mondi 	switch (format->code) {
738ef86447eSJai Luthra 	case MEDIA_BUS_FMT_SRGGB8_1X8:
739ef86447eSJai Luthra 	case MEDIA_BUS_FMT_SGRBG8_1X8:
740ef86447eSJai Luthra 	case MEDIA_BUS_FMT_SGBRG8_1X8:
741ef86447eSJai Luthra 	case MEDIA_BUS_FMT_SBGGR8_1X8:
7422e943af4SLaurent Pinchart 		return cci_write(imx219->regmap, IMX219_REG_BINNING_MODE,
7432e943af4SLaurent Pinchart 				 IMX219_BINNING_2X2_ANALOG, NULL);
744ef86447eSJai Luthra 
745ef86447eSJai Luthra 	case MEDIA_BUS_FMT_SRGGB10_1X10:
746ef86447eSJai Luthra 	case MEDIA_BUS_FMT_SGRBG10_1X10:
747ef86447eSJai Luthra 	case MEDIA_BUS_FMT_SGBRG10_1X10:
748ef86447eSJai Luthra 	case MEDIA_BUS_FMT_SBGGR10_1X10:
7492e943af4SLaurent Pinchart 		return cci_write(imx219->regmap, IMX219_REG_BINNING_MODE,
7502e943af4SLaurent Pinchart 				 IMX219_BINNING_2X2, NULL);
751ef86447eSJai Luthra 	}
752ef86447eSJai Luthra 
753ef86447eSJai Luthra 	return -EINVAL;
754ef86447eSJai Luthra }
755ef86447eSJai Luthra 
imx219_get_selection(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_selection * sel)756e6d4ef7dSJacopo Mondi static int imx219_get_selection(struct v4l2_subdev *sd,
7570d346d2aSTomi Valkeinen 				struct v4l2_subdev_state *sd_state,
758e6d4ef7dSJacopo Mondi 				struct v4l2_subdev_selection *sel)
759e6d4ef7dSJacopo Mondi {
760e6d4ef7dSJacopo Mondi 	switch (sel->target) {
761e6d4ef7dSJacopo Mondi 	case V4L2_SEL_TGT_CROP: {
762e8a5b1dfSJacopo Mondi 		sel->r = *v4l2_subdev_get_pad_crop(sd, sd_state, 0);
763e6d4ef7dSJacopo Mondi 		return 0;
764e6d4ef7dSJacopo Mondi 	}
765e6d4ef7dSJacopo Mondi 
766e6d4ef7dSJacopo Mondi 	case V4L2_SEL_TGT_NATIVE_SIZE:
767e6d4ef7dSJacopo Mondi 		sel->r.top = 0;
768e6d4ef7dSJacopo Mondi 		sel->r.left = 0;
769e6d4ef7dSJacopo Mondi 		sel->r.width = IMX219_NATIVE_WIDTH;
770e6d4ef7dSJacopo Mondi 		sel->r.height = IMX219_NATIVE_HEIGHT;
771e6d4ef7dSJacopo Mondi 
772e6d4ef7dSJacopo Mondi 		return 0;
773e6d4ef7dSJacopo Mondi 
774e6d4ef7dSJacopo Mondi 	case V4L2_SEL_TGT_CROP_DEFAULT:
7751ed36ecdSHans Verkuil 	case V4L2_SEL_TGT_CROP_BOUNDS:
776e6d4ef7dSJacopo Mondi 		sel->r.top = IMX219_PIXEL_ARRAY_TOP;
777e6d4ef7dSJacopo Mondi 		sel->r.left = IMX219_PIXEL_ARRAY_LEFT;
778e6d4ef7dSJacopo Mondi 		sel->r.width = IMX219_PIXEL_ARRAY_WIDTH;
779e6d4ef7dSJacopo Mondi 		sel->r.height = IMX219_PIXEL_ARRAY_HEIGHT;
780e6d4ef7dSJacopo Mondi 
781e6d4ef7dSJacopo Mondi 		return 0;
782e6d4ef7dSJacopo Mondi 	}
783e6d4ef7dSJacopo Mondi 
784e6d4ef7dSJacopo Mondi 	return -EINVAL;
785e6d4ef7dSJacopo Mondi }
786e6d4ef7dSJacopo Mondi 
imx219_configure_lanes(struct imx219 * imx219)787ceddfd44SAdam Ford static int imx219_configure_lanes(struct imx219 *imx219)
788ceddfd44SAdam Ford {
7892e943af4SLaurent Pinchart 	return cci_write(imx219->regmap, IMX219_REG_CSI_LANE_MODE,
7902e943af4SLaurent Pinchart 			 imx219->lanes == 2 ? IMX219_CSI_2_LANE_MODE :
7912e943af4SLaurent Pinchart 			 IMX219_CSI_4_LANE_MODE, NULL);
792ceddfd44SAdam Ford };
793ceddfd44SAdam Ford 
imx219_start_streaming(struct imx219 * imx219,struct v4l2_subdev_state * state)794e8a5b1dfSJacopo Mondi static int imx219_start_streaming(struct imx219 *imx219,
795e8a5b1dfSJacopo Mondi 				  struct v4l2_subdev_state *state)
7961283b3b8SDave Stevenson {
7971283b3b8SDave Stevenson 	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
798e8a5b1dfSJacopo Mondi 	const struct v4l2_mbus_framefmt *format;
7991283b3b8SDave Stevenson 	const struct imx219_reg_list *reg_list;
8001283b3b8SDave Stevenson 	int ret;
8011283b3b8SDave Stevenson 
80230ad4559SMauro Carvalho Chehab 	ret = pm_runtime_resume_and_get(&client->dev);
80330ad4559SMauro Carvalho Chehab 	if (ret < 0)
804dd90caa0SLad Prabhakar 		return ret;
805dd90caa0SLad Prabhakar 
80685084559SAdam Ford 	/* Send all registers that are common to all modes */
8072e943af4SLaurent Pinchart 	ret = cci_multi_reg_write(imx219->regmap, imx219_common_regs,
8082e943af4SLaurent Pinchart 				  ARRAY_SIZE(imx219_common_regs), NULL);
80985084559SAdam Ford 	if (ret) {
81085084559SAdam Ford 		dev_err(&client->dev, "%s failed to send mfg header\n", __func__);
81185084559SAdam Ford 		goto err_rpm_put;
81285084559SAdam Ford 	}
81385084559SAdam Ford 
814ceddfd44SAdam Ford 	/* Configure two or four Lane mode */
815ceddfd44SAdam Ford 	ret = imx219_configure_lanes(imx219);
816ceddfd44SAdam Ford 	if (ret) {
817ceddfd44SAdam Ford 		dev_err(&client->dev, "%s failed to configure lanes\n", __func__);
818ceddfd44SAdam Ford 		goto err_rpm_put;
819ceddfd44SAdam Ford 	}
820ceddfd44SAdam Ford 
8211283b3b8SDave Stevenson 	/* Apply default values of current mode */
8221283b3b8SDave Stevenson 	reg_list = &imx219->mode->reg_list;
8232e943af4SLaurent Pinchart 	ret = cci_multi_reg_write(imx219->regmap, reg_list->regs,
8242e943af4SLaurent Pinchart 				  reg_list->num_of_regs, NULL);
8251283b3b8SDave Stevenson 	if (ret) {
8261283b3b8SDave Stevenson 		dev_err(&client->dev, "%s failed to set mode\n", __func__);
827dd90caa0SLad Prabhakar 		goto err_rpm_put;
8281283b3b8SDave Stevenson 	}
8291283b3b8SDave Stevenson 
830e8a5b1dfSJacopo Mondi 	format = v4l2_subdev_get_pad_format(&imx219->sd, state, 0);
831e8a5b1dfSJacopo Mondi 	ret = imx219_set_framefmt(imx219, format);
83222da1d56SLad Prabhakar 	if (ret) {
83322da1d56SLad Prabhakar 		dev_err(&client->dev, "%s failed to set frame format: %d\n",
83422da1d56SLad Prabhakar 			__func__, ret);
835dd90caa0SLad Prabhakar 		goto err_rpm_put;
83622da1d56SLad Prabhakar 	}
83722da1d56SLad Prabhakar 
838e8a5b1dfSJacopo Mondi 	ret = imx219_set_binning(imx219, format);
839ef86447eSJai Luthra 	if (ret) {
840ef86447eSJai Luthra 		dev_err(&client->dev, "%s failed to set binning: %d\n",
841ef86447eSJai Luthra 			__func__, ret);
842ef86447eSJai Luthra 		goto err_rpm_put;
843ef86447eSJai Luthra 	}
844ef86447eSJai Luthra 
8451283b3b8SDave Stevenson 	/* Apply customized values from user */
8461283b3b8SDave Stevenson 	ret =  __v4l2_ctrl_handler_setup(imx219->sd.ctrl_handler);
8471283b3b8SDave Stevenson 	if (ret)
848dd90caa0SLad Prabhakar 		goto err_rpm_put;
8491283b3b8SDave Stevenson 
8501283b3b8SDave Stevenson 	/* set stream on register */
8512e943af4SLaurent Pinchart 	ret = cci_write(imx219->regmap, IMX219_REG_MODE_SELECT,
8522e943af4SLaurent Pinchart 			IMX219_MODE_STREAMING, NULL);
853745d4612SLad Prabhakar 	if (ret)
854dd90caa0SLad Prabhakar 		goto err_rpm_put;
855745d4612SLad Prabhakar 
856745d4612SLad Prabhakar 	/* vflip and hflip cannot change during streaming */
857745d4612SLad Prabhakar 	__v4l2_ctrl_grab(imx219->vflip, true);
858745d4612SLad Prabhakar 	__v4l2_ctrl_grab(imx219->hflip, true);
859745d4612SLad Prabhakar 
860745d4612SLad Prabhakar 	return 0;
861dd90caa0SLad Prabhakar 
862dd90caa0SLad Prabhakar err_rpm_put:
863dd90caa0SLad Prabhakar 	pm_runtime_put(&client->dev);
864dd90caa0SLad Prabhakar 	return ret;
8651283b3b8SDave Stevenson }
8661283b3b8SDave Stevenson 
imx219_stop_streaming(struct imx219 * imx219)8671283b3b8SDave Stevenson static void imx219_stop_streaming(struct imx219 *imx219)
8681283b3b8SDave Stevenson {
8691283b3b8SDave Stevenson 	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
8701283b3b8SDave Stevenson 	int ret;
8711283b3b8SDave Stevenson 
8721283b3b8SDave Stevenson 	/* set stream off register */
8732e943af4SLaurent Pinchart 	ret = cci_write(imx219->regmap, IMX219_REG_MODE_SELECT,
8742e943af4SLaurent Pinchart 			IMX219_MODE_STANDBY, NULL);
8751283b3b8SDave Stevenson 	if (ret)
8761283b3b8SDave Stevenson 		dev_err(&client->dev, "%s failed to set stream\n", __func__);
877745d4612SLad Prabhakar 
878745d4612SLad Prabhakar 	__v4l2_ctrl_grab(imx219->vflip, false);
879745d4612SLad Prabhakar 	__v4l2_ctrl_grab(imx219->hflip, false);
880dd90caa0SLad Prabhakar 
881dd90caa0SLad Prabhakar 	pm_runtime_put(&client->dev);
8821283b3b8SDave Stevenson }
8831283b3b8SDave Stevenson 
imx219_set_stream(struct v4l2_subdev * sd,int enable)8841283b3b8SDave Stevenson static int imx219_set_stream(struct v4l2_subdev *sd, int enable)
8851283b3b8SDave Stevenson {
8861283b3b8SDave Stevenson 	struct imx219 *imx219 = to_imx219(sd);
887e8a5b1dfSJacopo Mondi 	struct v4l2_subdev_state *state;
8881283b3b8SDave Stevenson 	int ret = 0;
8891283b3b8SDave Stevenson 
890e8a5b1dfSJacopo Mondi 	state = v4l2_subdev_lock_and_get_active_state(sd);
891e8a5b1dfSJacopo Mondi 
892e8a5b1dfSJacopo Mondi 	if (imx219->streaming == enable)
893e8a5b1dfSJacopo Mondi 		goto unlock;
8941283b3b8SDave Stevenson 
8951283b3b8SDave Stevenson 	if (enable) {
8961283b3b8SDave Stevenson 		/*
8971283b3b8SDave Stevenson 		 * Apply default & customized values
8981283b3b8SDave Stevenson 		 * and then start streaming.
8991283b3b8SDave Stevenson 		 */
900e8a5b1dfSJacopo Mondi 		ret = imx219_start_streaming(imx219, state);
9011283b3b8SDave Stevenson 		if (ret)
902e8a5b1dfSJacopo Mondi 			goto unlock;
9031283b3b8SDave Stevenson 	} else {
9041283b3b8SDave Stevenson 		imx219_stop_streaming(imx219);
9051283b3b8SDave Stevenson 	}
9061283b3b8SDave Stevenson 
9071283b3b8SDave Stevenson 	imx219->streaming = enable;
9081283b3b8SDave Stevenson 
909e8a5b1dfSJacopo Mondi unlock:
910e8a5b1dfSJacopo Mondi 	v4l2_subdev_unlock_state(state);
9111283b3b8SDave Stevenson 	return ret;
9121283b3b8SDave Stevenson }
9131283b3b8SDave Stevenson 
9141283b3b8SDave Stevenson /* Power/clock management functions */
imx219_power_on(struct device * dev)9151283b3b8SDave Stevenson static int imx219_power_on(struct device *dev)
9161283b3b8SDave Stevenson {
91737bb22edSKrzysztof Kozlowski 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
9181283b3b8SDave Stevenson 	struct imx219 *imx219 = to_imx219(sd);
9191283b3b8SDave Stevenson 	int ret;
9201283b3b8SDave Stevenson 
9211283b3b8SDave Stevenson 	ret = regulator_bulk_enable(IMX219_NUM_SUPPLIES,
9221283b3b8SDave Stevenson 				    imx219->supplies);
9231283b3b8SDave Stevenson 	if (ret) {
92437bb22edSKrzysztof Kozlowski 		dev_err(dev, "%s: failed to enable regulators\n",
9251283b3b8SDave Stevenson 			__func__);
9261283b3b8SDave Stevenson 		return ret;
9271283b3b8SDave Stevenson 	}
9281283b3b8SDave Stevenson 
9291283b3b8SDave Stevenson 	ret = clk_prepare_enable(imx219->xclk);
9301283b3b8SDave Stevenson 	if (ret) {
93137bb22edSKrzysztof Kozlowski 		dev_err(dev, "%s: failed to enable clock\n",
9321283b3b8SDave Stevenson 			__func__);
9331283b3b8SDave Stevenson 		goto reg_off;
9341283b3b8SDave Stevenson 	}
9351283b3b8SDave Stevenson 
9361283b3b8SDave Stevenson 	gpiod_set_value_cansleep(imx219->reset_gpio, 1);
9371283b3b8SDave Stevenson 	usleep_range(IMX219_XCLR_MIN_DELAY_US,
9381283b3b8SDave Stevenson 		     IMX219_XCLR_MIN_DELAY_US + IMX219_XCLR_DELAY_RANGE_US);
9391283b3b8SDave Stevenson 
9401283b3b8SDave Stevenson 	return 0;
9411283b3b8SDave Stevenson 
9421283b3b8SDave Stevenson reg_off:
9431283b3b8SDave Stevenson 	regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies);
9441283b3b8SDave Stevenson 
9451283b3b8SDave Stevenson 	return ret;
9461283b3b8SDave Stevenson }
9471283b3b8SDave Stevenson 
imx219_power_off(struct device * dev)9481283b3b8SDave Stevenson static int imx219_power_off(struct device *dev)
9491283b3b8SDave Stevenson {
95037bb22edSKrzysztof Kozlowski 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
9511283b3b8SDave Stevenson 	struct imx219 *imx219 = to_imx219(sd);
9521283b3b8SDave Stevenson 
9531283b3b8SDave Stevenson 	gpiod_set_value_cansleep(imx219->reset_gpio, 0);
9541283b3b8SDave Stevenson 	regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies);
9551283b3b8SDave Stevenson 	clk_disable_unprepare(imx219->xclk);
9561283b3b8SDave Stevenson 
9571283b3b8SDave Stevenson 	return 0;
9581283b3b8SDave Stevenson }
9591283b3b8SDave Stevenson 
imx219_suspend(struct device * dev)9601283b3b8SDave Stevenson static int __maybe_unused imx219_suspend(struct device *dev)
9611283b3b8SDave Stevenson {
96237bb22edSKrzysztof Kozlowski 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
9631283b3b8SDave Stevenson 	struct imx219 *imx219 = to_imx219(sd);
9641283b3b8SDave Stevenson 
9651283b3b8SDave Stevenson 	if (imx219->streaming)
9661283b3b8SDave Stevenson 		imx219_stop_streaming(imx219);
9671283b3b8SDave Stevenson 
9681283b3b8SDave Stevenson 	return 0;
9691283b3b8SDave Stevenson }
9701283b3b8SDave Stevenson 
imx219_resume(struct device * dev)9711283b3b8SDave Stevenson static int __maybe_unused imx219_resume(struct device *dev)
9721283b3b8SDave Stevenson {
97337bb22edSKrzysztof Kozlowski 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
9741283b3b8SDave Stevenson 	struct imx219 *imx219 = to_imx219(sd);
975e8a5b1dfSJacopo Mondi 	struct v4l2_subdev_state *state;
9761283b3b8SDave Stevenson 	int ret;
9771283b3b8SDave Stevenson 
9781283b3b8SDave Stevenson 	if (imx219->streaming) {
979e8a5b1dfSJacopo Mondi 		state = v4l2_subdev_lock_and_get_active_state(sd);
980e8a5b1dfSJacopo Mondi 		ret = imx219_start_streaming(imx219, state);
981e8a5b1dfSJacopo Mondi 		v4l2_subdev_unlock_state(state);
9821283b3b8SDave Stevenson 		if (ret)
9831283b3b8SDave Stevenson 			goto error;
9841283b3b8SDave Stevenson 	}
9851283b3b8SDave Stevenson 
9861283b3b8SDave Stevenson 	return 0;
9871283b3b8SDave Stevenson 
9881283b3b8SDave Stevenson error:
9891283b3b8SDave Stevenson 	imx219_stop_streaming(imx219);
990c90b4d70SDaniel W. S. Almeida 	imx219->streaming = false;
9911283b3b8SDave Stevenson 
9921283b3b8SDave Stevenson 	return ret;
9931283b3b8SDave Stevenson }
9941283b3b8SDave Stevenson 
imx219_get_regulators(struct imx219 * imx219)9951283b3b8SDave Stevenson static int imx219_get_regulators(struct imx219 *imx219)
9961283b3b8SDave Stevenson {
9971283b3b8SDave Stevenson 	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
9981283b3b8SDave Stevenson 	unsigned int i;
9991283b3b8SDave Stevenson 
10001283b3b8SDave Stevenson 	for (i = 0; i < IMX219_NUM_SUPPLIES; i++)
10011283b3b8SDave Stevenson 		imx219->supplies[i].supply = imx219_supply_name[i];
10021283b3b8SDave Stevenson 
10031283b3b8SDave Stevenson 	return devm_regulator_bulk_get(&client->dev,
10041283b3b8SDave Stevenson 				       IMX219_NUM_SUPPLIES,
10051283b3b8SDave Stevenson 				       imx219->supplies);
10061283b3b8SDave Stevenson }
10071283b3b8SDave Stevenson 
10081283b3b8SDave Stevenson /* Verify chip ID */
imx219_identify_module(struct imx219 * imx219)10091283b3b8SDave Stevenson static int imx219_identify_module(struct imx219 *imx219)
10101283b3b8SDave Stevenson {
10111283b3b8SDave Stevenson 	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
10121283b3b8SDave Stevenson 	int ret;
10132e943af4SLaurent Pinchart 	u64 val;
10141283b3b8SDave Stevenson 
10152e943af4SLaurent Pinchart 	ret = cci_read(imx219->regmap, IMX219_REG_CHIP_ID, &val, NULL);
10161283b3b8SDave Stevenson 	if (ret) {
10171283b3b8SDave Stevenson 		dev_err(&client->dev, "failed to read chip id %x\n",
10181283b3b8SDave Stevenson 			IMX219_CHIP_ID);
10191283b3b8SDave Stevenson 		return ret;
10201283b3b8SDave Stevenson 	}
10211283b3b8SDave Stevenson 
10221283b3b8SDave Stevenson 	if (val != IMX219_CHIP_ID) {
10232e943af4SLaurent Pinchart 		dev_err(&client->dev, "chip id mismatch: %x!=%llx\n",
10241283b3b8SDave Stevenson 			IMX219_CHIP_ID, val);
10251283b3b8SDave Stevenson 		return -EIO;
10261283b3b8SDave Stevenson 	}
10271283b3b8SDave Stevenson 
10281283b3b8SDave Stevenson 	return 0;
10291283b3b8SDave Stevenson }
10301283b3b8SDave Stevenson 
10311283b3b8SDave Stevenson static const struct v4l2_subdev_core_ops imx219_core_ops = {
10321283b3b8SDave Stevenson 	.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
10331283b3b8SDave Stevenson 	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
10341283b3b8SDave Stevenson };
10351283b3b8SDave Stevenson 
10361283b3b8SDave Stevenson static const struct v4l2_subdev_video_ops imx219_video_ops = {
10371283b3b8SDave Stevenson 	.s_stream = imx219_set_stream,
10381283b3b8SDave Stevenson };
10391283b3b8SDave Stevenson 
10401283b3b8SDave Stevenson static const struct v4l2_subdev_pad_ops imx219_pad_ops = {
10417e700847SJean-Michel Hautbois 	.init_cfg = imx219_init_cfg,
10421283b3b8SDave Stevenson 	.enum_mbus_code = imx219_enum_mbus_code,
1043e8a5b1dfSJacopo Mondi 	.get_fmt = v4l2_subdev_get_fmt,
10441283b3b8SDave Stevenson 	.set_fmt = imx219_set_pad_format,
1045e6d4ef7dSJacopo Mondi 	.get_selection = imx219_get_selection,
10461283b3b8SDave Stevenson 	.enum_frame_size = imx219_enum_frame_size,
10471283b3b8SDave Stevenson };
10481283b3b8SDave Stevenson 
10491283b3b8SDave Stevenson static const struct v4l2_subdev_ops imx219_subdev_ops = {
10501283b3b8SDave Stevenson 	.core = &imx219_core_ops,
10511283b3b8SDave Stevenson 	.video = &imx219_video_ops,
10521283b3b8SDave Stevenson 	.pad = &imx219_pad_ops,
10531283b3b8SDave Stevenson };
10541283b3b8SDave Stevenson 
10551283b3b8SDave Stevenson 
imx219_get_pixel_rate(struct imx219 * imx219)1056ceddfd44SAdam Ford static unsigned long imx219_get_pixel_rate(struct imx219 *imx219)
1057ceddfd44SAdam Ford {
1058ceddfd44SAdam Ford 	return (imx219->lanes == 2) ? IMX219_PIXEL_RATE : IMX219_PIXEL_RATE_4LANE;
1059ceddfd44SAdam Ford }
1060ceddfd44SAdam Ford 
10611283b3b8SDave Stevenson /* Initialize control handlers */
imx219_init_controls(struct imx219 * imx219)10621283b3b8SDave Stevenson static int imx219_init_controls(struct imx219 *imx219)
10631283b3b8SDave Stevenson {
10641283b3b8SDave Stevenson 	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
10651283b3b8SDave Stevenson 	struct v4l2_ctrl_handler *ctrl_hdlr;
10661283b3b8SDave Stevenson 	unsigned int height = imx219->mode->height;
1067ad3a44cbSJacopo Mondi 	struct v4l2_fwnode_device_properties props;
10681283b3b8SDave Stevenson 	int exposure_max, exposure_def, hblank;
10691283b3b8SDave Stevenson 	int i, ret;
10701283b3b8SDave Stevenson 
10711283b3b8SDave Stevenson 	ctrl_hdlr = &imx219->ctrl_handler;
107249b94d58SAndrey Konovalov 	ret = v4l2_ctrl_handler_init(ctrl_hdlr, 12);
10731283b3b8SDave Stevenson 	if (ret)
10741283b3b8SDave Stevenson 		return ret;
10751283b3b8SDave Stevenson 
10761283b3b8SDave Stevenson 	/* By default, PIXEL_RATE is read only */
10771283b3b8SDave Stevenson 	imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
10781283b3b8SDave Stevenson 					       V4L2_CID_PIXEL_RATE,
1079ceddfd44SAdam Ford 					       imx219_get_pixel_rate(imx219),
1080ceddfd44SAdam Ford 					       imx219_get_pixel_rate(imx219), 1,
1081ceddfd44SAdam Ford 					       imx219_get_pixel_rate(imx219));
10821283b3b8SDave Stevenson 
108349b94d58SAndrey Konovalov 	imx219->link_freq =
108449b94d58SAndrey Konovalov 		v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx219_ctrl_ops,
108549b94d58SAndrey Konovalov 				       V4L2_CID_LINK_FREQ,
108649b94d58SAndrey Konovalov 				       ARRAY_SIZE(imx219_link_freq_menu) - 1, 0,
1087ceddfd44SAdam Ford 				       (imx219->lanes == 2) ? imx219_link_freq_menu :
1088ceddfd44SAdam Ford 				       imx219_link_freq_4lane_menu);
108949b94d58SAndrey Konovalov 	if (imx219->link_freq)
109049b94d58SAndrey Konovalov 		imx219->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
109149b94d58SAndrey Konovalov 
10921283b3b8SDave Stevenson 	/* Initial vblank/hblank/exposure parameters based on current mode */
10931283b3b8SDave Stevenson 	imx219->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
10941283b3b8SDave Stevenson 					   V4L2_CID_VBLANK, IMX219_VBLANK_MIN,
10951283b3b8SDave Stevenson 					   IMX219_VTS_MAX - height, 1,
10961283b3b8SDave Stevenson 					   imx219->mode->vts_def - height);
10971283b3b8SDave Stevenson 	hblank = IMX219_PPL_DEFAULT - imx219->mode->width;
10981283b3b8SDave Stevenson 	imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
10991283b3b8SDave Stevenson 					   V4L2_CID_HBLANK, hblank, hblank,
11001283b3b8SDave Stevenson 					   1, hblank);
11011283b3b8SDave Stevenson 	if (imx219->hblank)
11021283b3b8SDave Stevenson 		imx219->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
11031283b3b8SDave Stevenson 	exposure_max = imx219->mode->vts_def - 4;
11041283b3b8SDave Stevenson 	exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
11051283b3b8SDave Stevenson 		exposure_max : IMX219_EXPOSURE_DEFAULT;
11061283b3b8SDave Stevenson 	imx219->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
11071283b3b8SDave Stevenson 					     V4L2_CID_EXPOSURE,
11081283b3b8SDave Stevenson 					     IMX219_EXPOSURE_MIN, exposure_max,
11091283b3b8SDave Stevenson 					     IMX219_EXPOSURE_STEP,
11101283b3b8SDave Stevenson 					     exposure_def);
11111283b3b8SDave Stevenson 
11121283b3b8SDave Stevenson 	v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
11131283b3b8SDave Stevenson 			  IMX219_ANA_GAIN_MIN, IMX219_ANA_GAIN_MAX,
11141283b3b8SDave Stevenson 			  IMX219_ANA_GAIN_STEP, IMX219_ANA_GAIN_DEFAULT);
11151283b3b8SDave Stevenson 
11161283b3b8SDave Stevenson 	v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
11171283b3b8SDave Stevenson 			  IMX219_DGTL_GAIN_MIN, IMX219_DGTL_GAIN_MAX,
11181283b3b8SDave Stevenson 			  IMX219_DGTL_GAIN_STEP, IMX219_DGTL_GAIN_DEFAULT);
11191283b3b8SDave Stevenson 
11201283b3b8SDave Stevenson 	imx219->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
11211283b3b8SDave Stevenson 					  V4L2_CID_HFLIP, 0, 1, 1, 0);
11221283b3b8SDave Stevenson 	if (imx219->hflip)
11231283b3b8SDave Stevenson 		imx219->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
11241283b3b8SDave Stevenson 
11251283b3b8SDave Stevenson 	imx219->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
11261283b3b8SDave Stevenson 					  V4L2_CID_VFLIP, 0, 1, 1, 0);
11271283b3b8SDave Stevenson 	if (imx219->vflip)
11281283b3b8SDave Stevenson 		imx219->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
11291283b3b8SDave Stevenson 
11301283b3b8SDave Stevenson 	v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx219_ctrl_ops,
11311283b3b8SDave Stevenson 				     V4L2_CID_TEST_PATTERN,
11321283b3b8SDave Stevenson 				     ARRAY_SIZE(imx219_test_pattern_menu) - 1,
11331283b3b8SDave Stevenson 				     0, 0, imx219_test_pattern_menu);
11341283b3b8SDave Stevenson 	for (i = 0; i < 4; i++) {
11351283b3b8SDave Stevenson 		/*
11361283b3b8SDave Stevenson 		 * The assumption is that
11371283b3b8SDave Stevenson 		 * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1
11381283b3b8SDave Stevenson 		 * V4L2_CID_TEST_PATTERN_BLUE   == V4L2_CID_TEST_PATTERN_RED + 2
11391283b3b8SDave Stevenson 		 * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3
11401283b3b8SDave Stevenson 		 */
11411283b3b8SDave Stevenson 		v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
11421283b3b8SDave Stevenson 				  V4L2_CID_TEST_PATTERN_RED + i,
11431283b3b8SDave Stevenson 				  IMX219_TESTP_COLOUR_MIN,
11441283b3b8SDave Stevenson 				  IMX219_TESTP_COLOUR_MAX,
11451283b3b8SDave Stevenson 				  IMX219_TESTP_COLOUR_STEP,
11461283b3b8SDave Stevenson 				  IMX219_TESTP_COLOUR_MAX);
11471283b3b8SDave Stevenson 		/* The "Solid color" pattern is white by default */
11481283b3b8SDave Stevenson 	}
11491283b3b8SDave Stevenson 
11501283b3b8SDave Stevenson 	if (ctrl_hdlr->error) {
11511283b3b8SDave Stevenson 		ret = ctrl_hdlr->error;
11521283b3b8SDave Stevenson 		dev_err(&client->dev, "%s control init failed (%d)\n",
11531283b3b8SDave Stevenson 			__func__, ret);
11541283b3b8SDave Stevenson 		goto error;
11551283b3b8SDave Stevenson 	}
11561283b3b8SDave Stevenson 
1157ad3a44cbSJacopo Mondi 	ret = v4l2_fwnode_device_parse(&client->dev, &props);
1158ad3a44cbSJacopo Mondi 	if (ret)
1159ad3a44cbSJacopo Mondi 		goto error;
1160ad3a44cbSJacopo Mondi 
1161ad3a44cbSJacopo Mondi 	ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx219_ctrl_ops,
1162ad3a44cbSJacopo Mondi 					      &props);
1163ad3a44cbSJacopo Mondi 	if (ret)
1164ad3a44cbSJacopo Mondi 		goto error;
1165ad3a44cbSJacopo Mondi 
11661283b3b8SDave Stevenson 	imx219->sd.ctrl_handler = ctrl_hdlr;
11671283b3b8SDave Stevenson 
11681283b3b8SDave Stevenson 	return 0;
11691283b3b8SDave Stevenson 
11701283b3b8SDave Stevenson error:
11711283b3b8SDave Stevenson 	v4l2_ctrl_handler_free(ctrl_hdlr);
11721283b3b8SDave Stevenson 
11731283b3b8SDave Stevenson 	return ret;
11741283b3b8SDave Stevenson }
11751283b3b8SDave Stevenson 
imx219_free_controls(struct imx219 * imx219)11761283b3b8SDave Stevenson static void imx219_free_controls(struct imx219 *imx219)
11771283b3b8SDave Stevenson {
11781283b3b8SDave Stevenson 	v4l2_ctrl_handler_free(imx219->sd.ctrl_handler);
11791283b3b8SDave Stevenson }
11801283b3b8SDave Stevenson 
imx219_check_hwcfg(struct device * dev,struct imx219 * imx219)1181ceddfd44SAdam Ford static int imx219_check_hwcfg(struct device *dev, struct imx219 *imx219)
11821283b3b8SDave Stevenson {
11831283b3b8SDave Stevenson 	struct fwnode_handle *endpoint;
11841283b3b8SDave Stevenson 	struct v4l2_fwnode_endpoint ep_cfg = {
11851283b3b8SDave Stevenson 		.bus_type = V4L2_MBUS_CSI2_DPHY
11861283b3b8SDave Stevenson 	};
11871283b3b8SDave Stevenson 	int ret = -EINVAL;
11881283b3b8SDave Stevenson 
11891283b3b8SDave Stevenson 	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
11901283b3b8SDave Stevenson 	if (!endpoint) {
11911283b3b8SDave Stevenson 		dev_err(dev, "endpoint node not found\n");
11921283b3b8SDave Stevenson 		return -EINVAL;
11931283b3b8SDave Stevenson 	}
11941283b3b8SDave Stevenson 
11951283b3b8SDave Stevenson 	if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) {
11961283b3b8SDave Stevenson 		dev_err(dev, "could not parse endpoint\n");
11971283b3b8SDave Stevenson 		goto error_out;
11981283b3b8SDave Stevenson 	}
11991283b3b8SDave Stevenson 
12001283b3b8SDave Stevenson 	/* Check the number of MIPI CSI2 data lanes */
1201ceddfd44SAdam Ford 	if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2 &&
1202ceddfd44SAdam Ford 	    ep_cfg.bus.mipi_csi2.num_data_lanes != 4) {
1203ceddfd44SAdam Ford 		dev_err(dev, "only 2 or 4 data lanes are currently supported\n");
12041283b3b8SDave Stevenson 		goto error_out;
12051283b3b8SDave Stevenson 	}
1206ceddfd44SAdam Ford 	imx219->lanes = ep_cfg.bus.mipi_csi2.num_data_lanes;
12071283b3b8SDave Stevenson 
12081283b3b8SDave Stevenson 	/* Check the link frequency set in device tree */
12091283b3b8SDave Stevenson 	if (!ep_cfg.nr_of_link_frequencies) {
12101283b3b8SDave Stevenson 		dev_err(dev, "link-frequency property not found in DT\n");
12111283b3b8SDave Stevenson 		goto error_out;
12121283b3b8SDave Stevenson 	}
12131283b3b8SDave Stevenson 
12141283b3b8SDave Stevenson 	if (ep_cfg.nr_of_link_frequencies != 1 ||
1215ceddfd44SAdam Ford 	   (ep_cfg.link_frequencies[0] != ((imx219->lanes == 2) ?
1216ceddfd44SAdam Ford 	    IMX219_DEFAULT_LINK_FREQ : IMX219_DEFAULT_LINK_FREQ_4LANE))) {
12171283b3b8SDave Stevenson 		dev_err(dev, "Link frequency not supported: %lld\n",
12181283b3b8SDave Stevenson 			ep_cfg.link_frequencies[0]);
12191283b3b8SDave Stevenson 		goto error_out;
12201283b3b8SDave Stevenson 	}
12211283b3b8SDave Stevenson 
12221283b3b8SDave Stevenson 	ret = 0;
12231283b3b8SDave Stevenson 
12241283b3b8SDave Stevenson error_out:
12251283b3b8SDave Stevenson 	v4l2_fwnode_endpoint_free(&ep_cfg);
12261283b3b8SDave Stevenson 	fwnode_handle_put(endpoint);
12271283b3b8SDave Stevenson 
12281283b3b8SDave Stevenson 	return ret;
12291283b3b8SDave Stevenson }
12301283b3b8SDave Stevenson 
imx219_probe(struct i2c_client * client)12311283b3b8SDave Stevenson static int imx219_probe(struct i2c_client *client)
12321283b3b8SDave Stevenson {
12331283b3b8SDave Stevenson 	struct device *dev = &client->dev;
12341283b3b8SDave Stevenson 	struct imx219 *imx219;
12351283b3b8SDave Stevenson 	int ret;
12361283b3b8SDave Stevenson 
12371283b3b8SDave Stevenson 	imx219 = devm_kzalloc(&client->dev, sizeof(*imx219), GFP_KERNEL);
12381283b3b8SDave Stevenson 	if (!imx219)
12391283b3b8SDave Stevenson 		return -ENOMEM;
12401283b3b8SDave Stevenson 
12411283b3b8SDave Stevenson 	v4l2_i2c_subdev_init(&imx219->sd, client, &imx219_subdev_ops);
12421283b3b8SDave Stevenson 
12431283b3b8SDave Stevenson 	/* Check the hardware configuration in device tree */
1244ceddfd44SAdam Ford 	if (imx219_check_hwcfg(dev, imx219))
12451283b3b8SDave Stevenson 		return -EINVAL;
12461283b3b8SDave Stevenson 
12472e943af4SLaurent Pinchart 	imx219->regmap = devm_cci_regmap_init_i2c(client, 16);
12482e943af4SLaurent Pinchart 	if (IS_ERR(imx219->regmap)) {
12492e943af4SLaurent Pinchart 		ret = PTR_ERR(imx219->regmap);
12502e943af4SLaurent Pinchart 		dev_err(dev, "failed to initialize CCI: %d\n", ret);
12512e943af4SLaurent Pinchart 		return ret;
12522e943af4SLaurent Pinchart 	}
12532e943af4SLaurent Pinchart 
12541283b3b8SDave Stevenson 	/* Get system clock (xclk) */
12551283b3b8SDave Stevenson 	imx219->xclk = devm_clk_get(dev, NULL);
12561283b3b8SDave Stevenson 	if (IS_ERR(imx219->xclk)) {
12571283b3b8SDave Stevenson 		dev_err(dev, "failed to get xclk\n");
12581283b3b8SDave Stevenson 		return PTR_ERR(imx219->xclk);
12591283b3b8SDave Stevenson 	}
12601283b3b8SDave Stevenson 
12611283b3b8SDave Stevenson 	imx219->xclk_freq = clk_get_rate(imx219->xclk);
12621283b3b8SDave Stevenson 	if (imx219->xclk_freq != IMX219_XCLK_FREQ) {
12631283b3b8SDave Stevenson 		dev_err(dev, "xclk frequency not supported: %d Hz\n",
12641283b3b8SDave Stevenson 			imx219->xclk_freq);
12651283b3b8SDave Stevenson 		return -EINVAL;
12661283b3b8SDave Stevenson 	}
12671283b3b8SDave Stevenson 
12681283b3b8SDave Stevenson 	ret = imx219_get_regulators(imx219);
12691283b3b8SDave Stevenson 	if (ret) {
12701283b3b8SDave Stevenson 		dev_err(dev, "failed to get regulators\n");
12711283b3b8SDave Stevenson 		return ret;
12721283b3b8SDave Stevenson 	}
12731283b3b8SDave Stevenson 
12741283b3b8SDave Stevenson 	/* Request optional enable pin */
12751283b3b8SDave Stevenson 	imx219->reset_gpio = devm_gpiod_get_optional(dev, "reset",
12761283b3b8SDave Stevenson 						     GPIOD_OUT_HIGH);
12771283b3b8SDave Stevenson 
12781283b3b8SDave Stevenson 	/*
12791283b3b8SDave Stevenson 	 * The sensor must be powered for imx219_identify_module()
12801283b3b8SDave Stevenson 	 * to be able to read the CHIP_ID register
12811283b3b8SDave Stevenson 	 */
12821283b3b8SDave Stevenson 	ret = imx219_power_on(dev);
12831283b3b8SDave Stevenson 	if (ret)
12841283b3b8SDave Stevenson 		return ret;
12851283b3b8SDave Stevenson 
12861283b3b8SDave Stevenson 	ret = imx219_identify_module(imx219);
12871283b3b8SDave Stevenson 	if (ret)
12881283b3b8SDave Stevenson 		goto error_power_off;
12891283b3b8SDave Stevenson 
12901283b3b8SDave Stevenson 	/* Set default mode to max resolution */
12911283b3b8SDave Stevenson 	imx219->mode = &supported_modes[0];
12921283b3b8SDave Stevenson 
1293ca45448aSLad Prabhakar 	/* sensor doesn't enter LP-11 state upon power up until and unless
1294ca45448aSLad Prabhakar 	 * streaming is started, so upon power up switch the modes to:
1295ca45448aSLad Prabhakar 	 * streaming -> standby
1296ca45448aSLad Prabhakar 	 */
12972e943af4SLaurent Pinchart 	ret = cci_write(imx219->regmap, IMX219_REG_MODE_SELECT,
12982e943af4SLaurent Pinchart 			IMX219_MODE_STREAMING, NULL);
1299ca45448aSLad Prabhakar 	if (ret < 0)
1300ca45448aSLad Prabhakar 		goto error_power_off;
13012e943af4SLaurent Pinchart 
1302ca45448aSLad Prabhakar 	usleep_range(100, 110);
1303ca45448aSLad Prabhakar 
1304ca45448aSLad Prabhakar 	/* put sensor back to standby mode */
13052e943af4SLaurent Pinchart 	ret = cci_write(imx219->regmap, IMX219_REG_MODE_SELECT,
13062e943af4SLaurent Pinchart 			IMX219_MODE_STANDBY, NULL);
1307ca45448aSLad Prabhakar 	if (ret < 0)
1308ca45448aSLad Prabhakar 		goto error_power_off;
13092e943af4SLaurent Pinchart 
1310ca45448aSLad Prabhakar 	usleep_range(100, 110);
1311ca45448aSLad Prabhakar 
13121283b3b8SDave Stevenson 	ret = imx219_init_controls(imx219);
13131283b3b8SDave Stevenson 	if (ret)
13141283b3b8SDave Stevenson 		goto error_power_off;
13151283b3b8SDave Stevenson 
13161283b3b8SDave Stevenson 	/* Initialize subdev */
1317defbac5dSDave Stevenson 	imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
1318defbac5dSDave Stevenson 			    V4L2_SUBDEV_FL_HAS_EVENTS;
13191283b3b8SDave Stevenson 	imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
13201283b3b8SDave Stevenson 
13211283b3b8SDave Stevenson 	/* Initialize source pad */
13221283b3b8SDave Stevenson 	imx219->pad.flags = MEDIA_PAD_FL_SOURCE;
13231283b3b8SDave Stevenson 
13241283b3b8SDave Stevenson 	ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad);
13251283b3b8SDave Stevenson 	if (ret) {
13261283b3b8SDave Stevenson 		dev_err(dev, "failed to init entity pads: %d\n", ret);
13271283b3b8SDave Stevenson 		goto error_handler_free;
13281283b3b8SDave Stevenson 	}
13291283b3b8SDave Stevenson 
1330e8a5b1dfSJacopo Mondi 	imx219->sd.state_lock = imx219->ctrl_handler.lock;
1331e8a5b1dfSJacopo Mondi 	ret = v4l2_subdev_init_finalize(&imx219->sd);
1332e8a5b1dfSJacopo Mondi 	if (ret < 0) {
1333e8a5b1dfSJacopo Mondi 		dev_err(dev, "subdev init error: %d\n", ret);
1334e8a5b1dfSJacopo Mondi 		goto error_media_entity;
1335e8a5b1dfSJacopo Mondi 	}
1336e8a5b1dfSJacopo Mondi 
133715786f7bSSakari Ailus 	ret = v4l2_async_register_subdev_sensor(&imx219->sd);
13381283b3b8SDave Stevenson 	if (ret < 0) {
13391283b3b8SDave Stevenson 		dev_err(dev, "failed to register sensor sub-device: %d\n", ret);
1340e8a5b1dfSJacopo Mondi 		goto error_subdev_cleanup;
13411283b3b8SDave Stevenson 	}
13421283b3b8SDave Stevenson 
13431283b3b8SDave Stevenson 	/* Enable runtime PM and turn off the device */
13441283b3b8SDave Stevenson 	pm_runtime_set_active(dev);
13451283b3b8SDave Stevenson 	pm_runtime_enable(dev);
13461283b3b8SDave Stevenson 	pm_runtime_idle(dev);
13471283b3b8SDave Stevenson 
13481283b3b8SDave Stevenson 	return 0;
13491283b3b8SDave Stevenson 
1350e8a5b1dfSJacopo Mondi error_subdev_cleanup:
1351e8a5b1dfSJacopo Mondi 	v4l2_subdev_cleanup(&imx219->sd);
1352e8a5b1dfSJacopo Mondi 
13531283b3b8SDave Stevenson error_media_entity:
13541283b3b8SDave Stevenson 	media_entity_cleanup(&imx219->sd.entity);
13551283b3b8SDave Stevenson 
13561283b3b8SDave Stevenson error_handler_free:
13571283b3b8SDave Stevenson 	imx219_free_controls(imx219);
13581283b3b8SDave Stevenson 
13591283b3b8SDave Stevenson error_power_off:
13601283b3b8SDave Stevenson 	imx219_power_off(dev);
13611283b3b8SDave Stevenson 
13621283b3b8SDave Stevenson 	return ret;
13631283b3b8SDave Stevenson }
13641283b3b8SDave Stevenson 
imx219_remove(struct i2c_client * client)1365ed5c2f5fSUwe Kleine-König static void imx219_remove(struct i2c_client *client)
13661283b3b8SDave Stevenson {
13671283b3b8SDave Stevenson 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
13681283b3b8SDave Stevenson 	struct imx219 *imx219 = to_imx219(sd);
13691283b3b8SDave Stevenson 
13701283b3b8SDave Stevenson 	v4l2_async_unregister_subdev(sd);
1371e8a5b1dfSJacopo Mondi 	v4l2_subdev_cleanup(sd);
13721283b3b8SDave Stevenson 	media_entity_cleanup(&sd->entity);
13731283b3b8SDave Stevenson 	imx219_free_controls(imx219);
13741283b3b8SDave Stevenson 
13751283b3b8SDave Stevenson 	pm_runtime_disable(&client->dev);
13761283b3b8SDave Stevenson 	if (!pm_runtime_status_suspended(&client->dev))
13771283b3b8SDave Stevenson 		imx219_power_off(&client->dev);
13781283b3b8SDave Stevenson 	pm_runtime_set_suspended(&client->dev);
13791283b3b8SDave Stevenson }
13801283b3b8SDave Stevenson 
13811283b3b8SDave Stevenson static const struct of_device_id imx219_dt_ids[] = {
13821283b3b8SDave Stevenson 	{ .compatible = "sony,imx219" },
13831283b3b8SDave Stevenson 	{ /* sentinel */ }
13841283b3b8SDave Stevenson };
13851283b3b8SDave Stevenson MODULE_DEVICE_TABLE(of, imx219_dt_ids);
13861283b3b8SDave Stevenson 
13871283b3b8SDave Stevenson static const struct dev_pm_ops imx219_pm_ops = {
13881283b3b8SDave Stevenson 	SET_SYSTEM_SLEEP_PM_OPS(imx219_suspend, imx219_resume)
13891283b3b8SDave Stevenson 	SET_RUNTIME_PM_OPS(imx219_power_off, imx219_power_on, NULL)
13901283b3b8SDave Stevenson };
13911283b3b8SDave Stevenson 
13921283b3b8SDave Stevenson static struct i2c_driver imx219_i2c_driver = {
13931283b3b8SDave Stevenson 	.driver = {
13941283b3b8SDave Stevenson 		.name = "imx219",
13951283b3b8SDave Stevenson 		.of_match_table	= imx219_dt_ids,
13961283b3b8SDave Stevenson 		.pm = &imx219_pm_ops,
13971283b3b8SDave Stevenson 	},
1398aaeb31c0SUwe Kleine-König 	.probe = imx219_probe,
13991283b3b8SDave Stevenson 	.remove = imx219_remove,
14001283b3b8SDave Stevenson };
14011283b3b8SDave Stevenson 
14021283b3b8SDave Stevenson module_i2c_driver(imx219_i2c_driver);
14031283b3b8SDave Stevenson 
14041283b3b8SDave Stevenson MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.com");
14051283b3b8SDave Stevenson MODULE_DESCRIPTION("Sony IMX219 sensor driver");
14061283b3b8SDave Stevenson MODULE_LICENSE("GPL v2");
1407