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