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> 24*2e943af4SLaurent Pinchart 25*2e943af4SLaurent 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 32*2e943af4SLaurent Pinchart /* Chip ID */ 33*2e943af4SLaurent Pinchart #define IMX219_REG_CHIP_ID CCI_REG16(0x0000) 34*2e943af4SLaurent Pinchart #define IMX219_CHIP_ID 0x0219 351283b3b8SDave Stevenson 36*2e943af4SLaurent 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 40*2e943af4SLaurent 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*2e943af4SLaurent Pinchart /* Analog gain control */ 45*2e943af4SLaurent Pinchart #define IMX219_REG_ANALOG_GAIN CCI_REG8(0x0157) 46*2e943af4SLaurent Pinchart #define IMX219_ANA_GAIN_MIN 0 47*2e943af4SLaurent Pinchart #define IMX219_ANA_GAIN_MAX 232 48*2e943af4SLaurent Pinchart #define IMX219_ANA_GAIN_STEP 1 49*2e943af4SLaurent Pinchart #define IMX219_ANA_GAIN_DEFAULT 0x0 50*2e943af4SLaurent Pinchart 51*2e943af4SLaurent Pinchart /* Digital gain control */ 52*2e943af4SLaurent Pinchart #define IMX219_REG_DIGITAL_GAIN CCI_REG16(0x0158) 53*2e943af4SLaurent Pinchart #define IMX219_DGTL_GAIN_MIN 0x0100 54*2e943af4SLaurent Pinchart #define IMX219_DGTL_GAIN_MAX 0x0fff 55*2e943af4SLaurent Pinchart #define IMX219_DGTL_GAIN_DEFAULT 0x0100 56*2e943af4SLaurent Pinchart #define IMX219_DGTL_GAIN_STEP 1 57*2e943af4SLaurent Pinchart 58*2e943af4SLaurent Pinchart /* Exposure control */ 59*2e943af4SLaurent Pinchart #define IMX219_REG_EXPOSURE CCI_REG16(0x015a) 60*2e943af4SLaurent Pinchart #define IMX219_EXPOSURE_MIN 4 61*2e943af4SLaurent Pinchart #define IMX219_EXPOSURE_STEP 1 62*2e943af4SLaurent Pinchart #define IMX219_EXPOSURE_DEFAULT 0x640 63*2e943af4SLaurent Pinchart #define IMX219_EXPOSURE_MAX 65535 64*2e943af4SLaurent Pinchart 651283b3b8SDave Stevenson /* V_TIMING internal */ 66*2e943af4SLaurent Pinchart #define IMX219_REG_VTS CCI_REG16(0x0160) 671283b3b8SDave Stevenson #define IMX219_VTS_15FPS 0x0dc6 681283b3b8SDave Stevenson #define IMX219_VTS_30FPS_1080P 0x06e3 691283b3b8SDave Stevenson #define IMX219_VTS_30FPS_BINNED 0x06e3 7025130b8aSLad Prabhakar #define IMX219_VTS_30FPS_640x480 0x06e3 711283b3b8SDave Stevenson #define IMX219_VTS_MAX 0xffff 721283b3b8SDave Stevenson 731283b3b8SDave Stevenson #define IMX219_VBLANK_MIN 4 741283b3b8SDave Stevenson 751283b3b8SDave Stevenson /*Frame Length Line*/ 761283b3b8SDave Stevenson #define IMX219_FLL_MIN 0x08a6 771283b3b8SDave Stevenson #define IMX219_FLL_MAX 0xffff 781283b3b8SDave Stevenson #define IMX219_FLL_STEP 1 791283b3b8SDave Stevenson #define IMX219_FLL_DEFAULT 0x0c98 801283b3b8SDave Stevenson 811283b3b8SDave Stevenson /* HBLANK control - read only */ 821283b3b8SDave Stevenson #define IMX219_PPL_DEFAULT 3448 831283b3b8SDave Stevenson 84*2e943af4SLaurent Pinchart #define IMX219_REG_ORIENTATION CCI_REG8(0x0172) 851283b3b8SDave Stevenson 86ef86447eSJai Luthra /* Binning Mode */ 87*2e943af4SLaurent Pinchart #define IMX219_REG_BINNING_MODE CCI_REG16(0x0174) 88ef86447eSJai Luthra #define IMX219_BINNING_NONE 0x0000 89ef86447eSJai Luthra #define IMX219_BINNING_2X2 0x0101 90ef86447eSJai Luthra #define IMX219_BINNING_2X2_ANALOG 0x0303 91ef86447eSJai Luthra 921283b3b8SDave Stevenson /* Test Pattern Control */ 93*2e943af4SLaurent Pinchart #define IMX219_REG_TEST_PATTERN CCI_REG16(0x0600) 941283b3b8SDave Stevenson #define IMX219_TEST_PATTERN_DISABLE 0 951283b3b8SDave Stevenson #define IMX219_TEST_PATTERN_SOLID_COLOR 1 961283b3b8SDave Stevenson #define IMX219_TEST_PATTERN_COLOR_BARS 2 971283b3b8SDave Stevenson #define IMX219_TEST_PATTERN_GREY_COLOR 3 981283b3b8SDave Stevenson #define IMX219_TEST_PATTERN_PN9 4 991283b3b8SDave Stevenson 1001283b3b8SDave Stevenson /* Test pattern colour components */ 101*2e943af4SLaurent Pinchart #define IMX219_REG_TESTP_RED CCI_REG16(0x0602) 102*2e943af4SLaurent Pinchart #define IMX219_REG_TESTP_GREENR CCI_REG16(0x0604) 103*2e943af4SLaurent Pinchart #define IMX219_REG_TESTP_BLUE CCI_REG16(0x0606) 104*2e943af4SLaurent Pinchart #define IMX219_REG_TESTP_GREENB CCI_REG16(0x0608) 1051283b3b8SDave Stevenson #define IMX219_TESTP_COLOUR_MIN 0 1061283b3b8SDave Stevenson #define IMX219_TESTP_COLOUR_MAX 0x03ff 1071283b3b8SDave Stevenson #define IMX219_TESTP_COLOUR_STEP 1 1081283b3b8SDave Stevenson #define IMX219_TESTP_RED_DEFAULT IMX219_TESTP_COLOUR_MAX 1091283b3b8SDave Stevenson #define IMX219_TESTP_GREENR_DEFAULT 0 1101283b3b8SDave Stevenson #define IMX219_TESTP_BLUE_DEFAULT 0 1111283b3b8SDave Stevenson #define IMX219_TESTP_GREENB_DEFAULT 0 1121283b3b8SDave Stevenson 113*2e943af4SLaurent Pinchart /* External clock frequency is 24.0M */ 114*2e943af4SLaurent Pinchart #define IMX219_XCLK_FREQ 24000000 115*2e943af4SLaurent Pinchart 116*2e943af4SLaurent Pinchart /* Pixel rate is fixed for all the modes */ 117*2e943af4SLaurent Pinchart #define IMX219_PIXEL_RATE 182400000 118*2e943af4SLaurent Pinchart #define IMX219_PIXEL_RATE_4LANE 280800000 119*2e943af4SLaurent Pinchart 120*2e943af4SLaurent Pinchart #define IMX219_DEFAULT_LINK_FREQ 456000000 121*2e943af4SLaurent Pinchart #define IMX219_DEFAULT_LINK_FREQ_4LANE 363000000 122*2e943af4SLaurent Pinchart 123e6d4ef7dSJacopo Mondi /* IMX219 native and active pixel array size. */ 124e6d4ef7dSJacopo Mondi #define IMX219_NATIVE_WIDTH 3296U 125e6d4ef7dSJacopo Mondi #define IMX219_NATIVE_HEIGHT 2480U 126e6d4ef7dSJacopo Mondi #define IMX219_PIXEL_ARRAY_LEFT 8U 127e6d4ef7dSJacopo Mondi #define IMX219_PIXEL_ARRAY_TOP 8U 128e6d4ef7dSJacopo Mondi #define IMX219_PIXEL_ARRAY_WIDTH 3280U 129e6d4ef7dSJacopo Mondi #define IMX219_PIXEL_ARRAY_HEIGHT 2464U 130e6d4ef7dSJacopo Mondi 1311283b3b8SDave Stevenson struct imx219_reg_list { 1321283b3b8SDave Stevenson unsigned int num_of_regs; 133*2e943af4SLaurent Pinchart const struct cci_reg_sequence *regs; 1341283b3b8SDave Stevenson }; 1351283b3b8SDave Stevenson 1361283b3b8SDave Stevenson /* Mode : resolution and related config&values */ 1371283b3b8SDave Stevenson struct imx219_mode { 1381283b3b8SDave Stevenson /* Frame width */ 1391283b3b8SDave Stevenson unsigned int width; 1401283b3b8SDave Stevenson /* Frame height */ 1411283b3b8SDave Stevenson unsigned int height; 1421283b3b8SDave Stevenson 143e6d4ef7dSJacopo Mondi /* Analog crop rectangle. */ 144e6d4ef7dSJacopo Mondi struct v4l2_rect crop; 145e6d4ef7dSJacopo Mondi 1461283b3b8SDave Stevenson /* V-timing */ 1471283b3b8SDave Stevenson unsigned int vts_def; 1481283b3b8SDave Stevenson 1491283b3b8SDave Stevenson /* Default register values */ 1501283b3b8SDave Stevenson struct imx219_reg_list reg_list; 151ef86447eSJai Luthra 152ef86447eSJai Luthra /* 2x2 binning is used */ 153ef86447eSJai Luthra bool binning; 1541283b3b8SDave Stevenson }; 1551283b3b8SDave Stevenson 156*2e943af4SLaurent Pinchart static const struct cci_reg_sequence imx219_common_regs[] = { 157*2e943af4SLaurent Pinchart { CCI_REG8(0x0100), 0x00 }, /* Mode Select */ 15885084559SAdam Ford 15985084559SAdam Ford /* To Access Addresses 3000-5fff, send the following commands */ 160*2e943af4SLaurent Pinchart { CCI_REG8(0x30eb), 0x0c }, 161*2e943af4SLaurent Pinchart { CCI_REG8(0x30eb), 0x05 }, 162*2e943af4SLaurent Pinchart { CCI_REG8(0x300a), 0xff }, 163*2e943af4SLaurent Pinchart { CCI_REG8(0x300b), 0xff }, 164*2e943af4SLaurent Pinchart { CCI_REG8(0x30eb), 0x05 }, 165*2e943af4SLaurent Pinchart { CCI_REG8(0x30eb), 0x09 }, 16685084559SAdam Ford 16785084559SAdam Ford /* PLL Clock Table */ 168*2e943af4SLaurent Pinchart { CCI_REG8(0x0301), 0x05 }, /* VTPXCK_DIV */ 169*2e943af4SLaurent Pinchart { CCI_REG8(0x0303), 0x01 }, /* VTSYSCK_DIV */ 170*2e943af4SLaurent Pinchart { CCI_REG8(0x0304), 0x03 }, /* PREPLLCK_VT_DIV 0x03 = AUTO set */ 171*2e943af4SLaurent Pinchart { CCI_REG8(0x0305), 0x03 }, /* PREPLLCK_OP_DIV 0x03 = AUTO set */ 172*2e943af4SLaurent Pinchart { CCI_REG8(0x0306), 0x00 }, /* PLL_VT_MPY */ 173*2e943af4SLaurent Pinchart { CCI_REG8(0x0307), 0x39 }, 174*2e943af4SLaurent Pinchart { CCI_REG8(0x030b), 0x01 }, /* OP_SYS_CLK_DIV */ 175*2e943af4SLaurent Pinchart { CCI_REG8(0x030c), 0x00 }, /* PLL_OP_MPY */ 176*2e943af4SLaurent Pinchart { CCI_REG8(0x030d), 0x72 }, 17785084559SAdam Ford 17885084559SAdam Ford /* Undocumented registers */ 179*2e943af4SLaurent Pinchart { CCI_REG8(0x455e), 0x00 }, 180*2e943af4SLaurent Pinchart { CCI_REG8(0x471e), 0x4b }, 181*2e943af4SLaurent Pinchart { CCI_REG8(0x4767), 0x0f }, 182*2e943af4SLaurent Pinchart { CCI_REG8(0x4750), 0x14 }, 183*2e943af4SLaurent Pinchart { CCI_REG8(0x4540), 0x00 }, 184*2e943af4SLaurent Pinchart { CCI_REG8(0x47b4), 0x14 }, 185*2e943af4SLaurent Pinchart { CCI_REG8(0x4713), 0x30 }, 186*2e943af4SLaurent Pinchart { CCI_REG8(0x478b), 0x10 }, 187*2e943af4SLaurent Pinchart { CCI_REG8(0x478f), 0x10 }, 188*2e943af4SLaurent Pinchart { CCI_REG8(0x4793), 0x10 }, 189*2e943af4SLaurent Pinchart { CCI_REG8(0x4797), 0x0e }, 190*2e943af4SLaurent Pinchart { CCI_REG8(0x479b), 0x0e }, 19185084559SAdam Ford 19285084559SAdam Ford /* Frame Bank Register Group "A" */ 193*2e943af4SLaurent Pinchart { CCI_REG8(0x0162), 0x0d }, /* Line_Length_A */ 194*2e943af4SLaurent Pinchart { CCI_REG8(0x0163), 0x78 }, 195*2e943af4SLaurent Pinchart { CCI_REG8(0x0170), 0x01 }, /* X_ODD_INC_A */ 196*2e943af4SLaurent Pinchart { CCI_REG8(0x0171), 0x01 }, /* Y_ODD_INC_A */ 19785084559SAdam Ford 19885084559SAdam Ford /* Output setup registers */ 199*2e943af4SLaurent Pinchart { CCI_REG8(0x0114), 0x01 }, /* CSI 2-Lane Mode */ 200*2e943af4SLaurent Pinchart { CCI_REG8(0x0128), 0x00 }, /* DPHY Auto Mode */ 201*2e943af4SLaurent Pinchart { CCI_REG8(0x012a), 0x18 }, /* EXCK_Freq */ 202*2e943af4SLaurent Pinchart { CCI_REG8(0x012b), 0x00 }, 20385084559SAdam Ford }; 20485084559SAdam Ford 20585084559SAdam Ford /* 20685084559SAdam Ford * Register sets lifted off the i2C interface from the Raspberry Pi firmware 20785084559SAdam Ford * driver. 20885084559SAdam Ford * 3280x2464 = mode 2, 1920x1080 = mode 1, 1640x1232 = mode 4, 640x480 = mode 7. 20985084559SAdam Ford */ 210*2e943af4SLaurent Pinchart static const struct cci_reg_sequence mode_3280x2464_regs[] = { 211*2e943af4SLaurent Pinchart { CCI_REG8(0x0164), 0x00 }, 212*2e943af4SLaurent Pinchart { CCI_REG8(0x0165), 0x00 }, 213*2e943af4SLaurent Pinchart { CCI_REG8(0x0166), 0x0c }, 214*2e943af4SLaurent Pinchart { CCI_REG8(0x0167), 0xcf }, 215*2e943af4SLaurent Pinchart { CCI_REG8(0x0168), 0x00 }, 216*2e943af4SLaurent Pinchart { CCI_REG8(0x0169), 0x00 }, 217*2e943af4SLaurent Pinchart { CCI_REG8(0x016a), 0x09 }, 218*2e943af4SLaurent Pinchart { CCI_REG8(0x016b), 0x9f }, 219*2e943af4SLaurent Pinchart { CCI_REG8(0x016c), 0x0c }, 220*2e943af4SLaurent Pinchart { CCI_REG8(0x016d), 0xd0 }, 221*2e943af4SLaurent Pinchart { CCI_REG8(0x016e), 0x09 }, 222*2e943af4SLaurent Pinchart { CCI_REG8(0x016f), 0xa0 }, 223*2e943af4SLaurent Pinchart { CCI_REG8(0x0624), 0x0c }, 224*2e943af4SLaurent Pinchart { CCI_REG8(0x0625), 0xd0 }, 225*2e943af4SLaurent Pinchart { CCI_REG8(0x0626), 0x09 }, 226*2e943af4SLaurent Pinchart { CCI_REG8(0x0627), 0xa0 }, 2271283b3b8SDave Stevenson }; 2281283b3b8SDave Stevenson 229*2e943af4SLaurent Pinchart static const struct cci_reg_sequence mode_1920_1080_regs[] = { 230*2e943af4SLaurent Pinchart { CCI_REG8(0x0164), 0x02 }, 231*2e943af4SLaurent Pinchart { CCI_REG8(0x0165), 0xa8 }, 232*2e943af4SLaurent Pinchart { CCI_REG8(0x0166), 0x0a }, 233*2e943af4SLaurent Pinchart { CCI_REG8(0x0167), 0x27 }, 234*2e943af4SLaurent Pinchart { CCI_REG8(0x0168), 0x02 }, 235*2e943af4SLaurent Pinchart { CCI_REG8(0x0169), 0xb4 }, 236*2e943af4SLaurent Pinchart { CCI_REG8(0x016a), 0x06 }, 237*2e943af4SLaurent Pinchart { CCI_REG8(0x016b), 0xeb }, 238*2e943af4SLaurent Pinchart { CCI_REG8(0x016c), 0x07 }, 239*2e943af4SLaurent Pinchart { CCI_REG8(0x016d), 0x80 }, 240*2e943af4SLaurent Pinchart { CCI_REG8(0x016e), 0x04 }, 241*2e943af4SLaurent Pinchart { CCI_REG8(0x016f), 0x38 }, 242*2e943af4SLaurent Pinchart { CCI_REG8(0x0624), 0x07 }, 243*2e943af4SLaurent Pinchart { CCI_REG8(0x0625), 0x80 }, 244*2e943af4SLaurent Pinchart { CCI_REG8(0x0626), 0x04 }, 245*2e943af4SLaurent Pinchart { CCI_REG8(0x0627), 0x38 }, 2461283b3b8SDave Stevenson }; 2471283b3b8SDave Stevenson 248*2e943af4SLaurent Pinchart static const struct cci_reg_sequence mode_1640_1232_regs[] = { 249*2e943af4SLaurent Pinchart { CCI_REG8(0x0164), 0x00 }, 250*2e943af4SLaurent Pinchart { CCI_REG8(0x0165), 0x00 }, 251*2e943af4SLaurent Pinchart { CCI_REG8(0x0166), 0x0c }, 252*2e943af4SLaurent Pinchart { CCI_REG8(0x0167), 0xcf }, 253*2e943af4SLaurent Pinchart { CCI_REG8(0x0168), 0x00 }, 254*2e943af4SLaurent Pinchart { CCI_REG8(0x0169), 0x00 }, 255*2e943af4SLaurent Pinchart { CCI_REG8(0x016a), 0x09 }, 256*2e943af4SLaurent Pinchart { CCI_REG8(0x016b), 0x9f }, 257*2e943af4SLaurent Pinchart { CCI_REG8(0x016c), 0x06 }, 258*2e943af4SLaurent Pinchart { CCI_REG8(0x016d), 0x68 }, 259*2e943af4SLaurent Pinchart { CCI_REG8(0x016e), 0x04 }, 260*2e943af4SLaurent Pinchart { CCI_REG8(0x016f), 0xd0 }, 261*2e943af4SLaurent Pinchart { CCI_REG8(0x0624), 0x06 }, 262*2e943af4SLaurent Pinchart { CCI_REG8(0x0625), 0x68 }, 263*2e943af4SLaurent Pinchart { CCI_REG8(0x0626), 0x04 }, 264*2e943af4SLaurent Pinchart { CCI_REG8(0x0627), 0xd0 }, 2651283b3b8SDave Stevenson }; 2661283b3b8SDave Stevenson 267*2e943af4SLaurent Pinchart static const struct cci_reg_sequence mode_640_480_regs[] = { 268*2e943af4SLaurent Pinchart { CCI_REG8(0x0164), 0x03 }, 269*2e943af4SLaurent Pinchart { CCI_REG8(0x0165), 0xe8 }, 270*2e943af4SLaurent Pinchart { CCI_REG8(0x0166), 0x08 }, 271*2e943af4SLaurent Pinchart { CCI_REG8(0x0167), 0xe7 }, 272*2e943af4SLaurent Pinchart { CCI_REG8(0x0168), 0x02 }, 273*2e943af4SLaurent Pinchart { CCI_REG8(0x0169), 0xf0 }, 274*2e943af4SLaurent Pinchart { CCI_REG8(0x016a), 0x06 }, 275*2e943af4SLaurent Pinchart { CCI_REG8(0x016b), 0xaf }, 276*2e943af4SLaurent Pinchart { CCI_REG8(0x016c), 0x02 }, 277*2e943af4SLaurent Pinchart { CCI_REG8(0x016d), 0x80 }, 278*2e943af4SLaurent Pinchart { CCI_REG8(0x016e), 0x01 }, 279*2e943af4SLaurent Pinchart { CCI_REG8(0x016f), 0xe0 }, 280*2e943af4SLaurent Pinchart { CCI_REG8(0x0624), 0x06 }, 281*2e943af4SLaurent Pinchart { CCI_REG8(0x0625), 0x68 }, 282*2e943af4SLaurent Pinchart { CCI_REG8(0x0626), 0x04 }, 283*2e943af4SLaurent Pinchart { CCI_REG8(0x0627), 0xd0 }, 28425130b8aSLad Prabhakar }; 28525130b8aSLad Prabhakar 286*2e943af4SLaurent Pinchart static const struct cci_reg_sequence raw8_framefmt_regs[] = { 287*2e943af4SLaurent Pinchart { CCI_REG8(0x018c), 0x08 }, 288*2e943af4SLaurent Pinchart { CCI_REG8(0x018d), 0x08 }, 289*2e943af4SLaurent Pinchart { CCI_REG8(0x0309), 0x08 }, 29022da1d56SLad Prabhakar }; 29122da1d56SLad Prabhakar 292*2e943af4SLaurent Pinchart static const struct cci_reg_sequence raw10_framefmt_regs[] = { 293*2e943af4SLaurent Pinchart { CCI_REG8(0x018c), 0x0a }, 294*2e943af4SLaurent Pinchart { CCI_REG8(0x018d), 0x0a }, 295*2e943af4SLaurent Pinchart { CCI_REG8(0x0309), 0x0a }, 29622da1d56SLad Prabhakar }; 29722da1d56SLad Prabhakar 29849b94d58SAndrey Konovalov static const s64 imx219_link_freq_menu[] = { 29949b94d58SAndrey Konovalov IMX219_DEFAULT_LINK_FREQ, 30049b94d58SAndrey Konovalov }; 30149b94d58SAndrey Konovalov 302ceddfd44SAdam Ford static const s64 imx219_link_freq_4lane_menu[] = { 303ceddfd44SAdam Ford IMX219_DEFAULT_LINK_FREQ_4LANE, 304ceddfd44SAdam Ford }; 305ceddfd44SAdam Ford 3061283b3b8SDave Stevenson static const char * const imx219_test_pattern_menu[] = { 3071283b3b8SDave Stevenson "Disabled", 3081283b3b8SDave Stevenson "Color Bars", 3091283b3b8SDave Stevenson "Solid Color", 3101283b3b8SDave Stevenson "Grey Color Bars", 3111283b3b8SDave Stevenson "PN9" 3121283b3b8SDave Stevenson }; 3131283b3b8SDave Stevenson 3141283b3b8SDave Stevenson static const int imx219_test_pattern_val[] = { 3151283b3b8SDave Stevenson IMX219_TEST_PATTERN_DISABLE, 3161283b3b8SDave Stevenson IMX219_TEST_PATTERN_COLOR_BARS, 3171283b3b8SDave Stevenson IMX219_TEST_PATTERN_SOLID_COLOR, 3181283b3b8SDave Stevenson IMX219_TEST_PATTERN_GREY_COLOR, 3191283b3b8SDave Stevenson IMX219_TEST_PATTERN_PN9, 3201283b3b8SDave Stevenson }; 3211283b3b8SDave Stevenson 3221283b3b8SDave Stevenson /* regulator supplies */ 3231283b3b8SDave Stevenson static const char * const imx219_supply_name[] = { 3241283b3b8SDave Stevenson /* Supplies can be enabled in any order */ 3251283b3b8SDave Stevenson "VANA", /* Analog (2.8V) supply */ 3261283b3b8SDave Stevenson "VDIG", /* Digital Core (1.8V) supply */ 3271283b3b8SDave Stevenson "VDDL", /* IF (1.2V) supply */ 3281283b3b8SDave Stevenson }; 3291283b3b8SDave Stevenson 3301283b3b8SDave Stevenson #define IMX219_NUM_SUPPLIES ARRAY_SIZE(imx219_supply_name) 3311283b3b8SDave Stevenson 3321283b3b8SDave Stevenson /* 33322da1d56SLad Prabhakar * The supported formats. 33422da1d56SLad Prabhakar * This table MUST contain 4 entries per format, to cover the various flip 33522da1d56SLad Prabhakar * combinations in the order 33622da1d56SLad Prabhakar * - no flip 33722da1d56SLad Prabhakar * - h flip 33822da1d56SLad Prabhakar * - v flip 33922da1d56SLad Prabhakar * - h&v flips 34022da1d56SLad Prabhakar */ 341917e26cbSJean-Michel Hautbois static const u32 imx219_mbus_formats[] = { 34222da1d56SLad Prabhakar MEDIA_BUS_FMT_SRGGB10_1X10, 34322da1d56SLad Prabhakar MEDIA_BUS_FMT_SGRBG10_1X10, 34422da1d56SLad Prabhakar MEDIA_BUS_FMT_SGBRG10_1X10, 34522da1d56SLad Prabhakar MEDIA_BUS_FMT_SBGGR10_1X10, 34622da1d56SLad Prabhakar 34722da1d56SLad Prabhakar MEDIA_BUS_FMT_SRGGB8_1X8, 34822da1d56SLad Prabhakar MEDIA_BUS_FMT_SGRBG8_1X8, 34922da1d56SLad Prabhakar MEDIA_BUS_FMT_SGBRG8_1X8, 35022da1d56SLad Prabhakar MEDIA_BUS_FMT_SBGGR8_1X8, 35122da1d56SLad Prabhakar }; 35222da1d56SLad Prabhakar 35322da1d56SLad Prabhakar /* 3541283b3b8SDave Stevenson * Initialisation delay between XCLR low->high and the moment when the sensor 3551283b3b8SDave Stevenson * can start capture (i.e. can leave software stanby) must be not less than: 3561283b3b8SDave Stevenson * t4 + max(t5, t6 + <time to initialize the sensor register over I2C>) 3571283b3b8SDave Stevenson * where 3581283b3b8SDave Stevenson * t4 is fixed, and is max 200uS, 3591283b3b8SDave Stevenson * t5 is fixed, and is 6000uS, 3601283b3b8SDave Stevenson * t6 depends on the sensor external clock, and is max 32000 clock periods. 3611283b3b8SDave Stevenson * As per sensor datasheet, the external clock must be from 6MHz to 27MHz. 3621283b3b8SDave Stevenson * So for any acceptable external clock t6 is always within the range of 3631283b3b8SDave Stevenson * 1185 to 5333 uS, and is always less than t5. 3641283b3b8SDave Stevenson * For this reason this is always safe to wait (t4 + t5) = 6200 uS, then 3651283b3b8SDave Stevenson * initialize the sensor over I2C, and then exit the software standby. 3661283b3b8SDave Stevenson * 3671283b3b8SDave Stevenson * This start-up time can be optimized a bit more, if we start the writes 3681283b3b8SDave Stevenson * over I2C after (t4+t6), but before (t4+t5) expires. But then sensor 3691283b3b8SDave Stevenson * initialization over I2C may complete before (t4+t5) expires, and we must 3701283b3b8SDave Stevenson * ensure that capture is not started before (t4+t5). 3711283b3b8SDave Stevenson * 3721283b3b8SDave Stevenson * This delay doesn't account for the power supply startup time. If needed, 3731283b3b8SDave Stevenson * this should be taken care of via the regulator framework. E.g. in the 3741283b3b8SDave Stevenson * case of DT for regulator-fixed one should define the startup-delay-us 3751283b3b8SDave Stevenson * property. 3761283b3b8SDave Stevenson */ 3771283b3b8SDave Stevenson #define IMX219_XCLR_MIN_DELAY_US 6200 3781283b3b8SDave Stevenson #define IMX219_XCLR_DELAY_RANGE_US 1000 3791283b3b8SDave Stevenson 3801283b3b8SDave Stevenson /* Mode configs */ 3811283b3b8SDave Stevenson static const struct imx219_mode supported_modes[] = { 3821283b3b8SDave Stevenson { 3831283b3b8SDave Stevenson /* 8MPix 15fps mode */ 3841283b3b8SDave Stevenson .width = 3280, 3851283b3b8SDave Stevenson .height = 2464, 386e6d4ef7dSJacopo Mondi .crop = { 3871ed36ecdSHans Verkuil .left = IMX219_PIXEL_ARRAY_LEFT, 3881ed36ecdSHans Verkuil .top = IMX219_PIXEL_ARRAY_TOP, 389e6d4ef7dSJacopo Mondi .width = 3280, 390e6d4ef7dSJacopo Mondi .height = 2464 391e6d4ef7dSJacopo Mondi }, 3921283b3b8SDave Stevenson .vts_def = IMX219_VTS_15FPS, 3931283b3b8SDave Stevenson .reg_list = { 3941283b3b8SDave Stevenson .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs), 3951283b3b8SDave Stevenson .regs = mode_3280x2464_regs, 3961283b3b8SDave Stevenson }, 397ef86447eSJai Luthra .binning = false, 3981283b3b8SDave Stevenson }, 3991283b3b8SDave Stevenson { 4001283b3b8SDave Stevenson /* 1080P 30fps cropped */ 4011283b3b8SDave Stevenson .width = 1920, 4021283b3b8SDave Stevenson .height = 1080, 403e6d4ef7dSJacopo Mondi .crop = { 4041ed36ecdSHans Verkuil .left = 688, 4051ed36ecdSHans Verkuil .top = 700, 406e6d4ef7dSJacopo Mondi .width = 1920, 407e6d4ef7dSJacopo Mondi .height = 1080 408e6d4ef7dSJacopo Mondi }, 4091283b3b8SDave Stevenson .vts_def = IMX219_VTS_30FPS_1080P, 4101283b3b8SDave Stevenson .reg_list = { 4111283b3b8SDave Stevenson .num_of_regs = ARRAY_SIZE(mode_1920_1080_regs), 4121283b3b8SDave Stevenson .regs = mode_1920_1080_regs, 4131283b3b8SDave Stevenson }, 414ef86447eSJai Luthra .binning = false, 4151283b3b8SDave Stevenson }, 4161283b3b8SDave Stevenson { 4171283b3b8SDave Stevenson /* 2x2 binned 30fps mode */ 4181283b3b8SDave Stevenson .width = 1640, 4191283b3b8SDave Stevenson .height = 1232, 420e6d4ef7dSJacopo Mondi .crop = { 4211ed36ecdSHans Verkuil .left = IMX219_PIXEL_ARRAY_LEFT, 4221ed36ecdSHans Verkuil .top = IMX219_PIXEL_ARRAY_TOP, 423e6d4ef7dSJacopo Mondi .width = 3280, 424e6d4ef7dSJacopo Mondi .height = 2464 425e6d4ef7dSJacopo Mondi }, 4261283b3b8SDave Stevenson .vts_def = IMX219_VTS_30FPS_BINNED, 4271283b3b8SDave Stevenson .reg_list = { 4281283b3b8SDave Stevenson .num_of_regs = ARRAY_SIZE(mode_1640_1232_regs), 4291283b3b8SDave Stevenson .regs = mode_1640_1232_regs, 4301283b3b8SDave Stevenson }, 431ef86447eSJai Luthra .binning = true, 4321283b3b8SDave Stevenson }, 43325130b8aSLad Prabhakar { 43425130b8aSLad Prabhakar /* 640x480 30fps mode */ 43525130b8aSLad Prabhakar .width = 640, 43625130b8aSLad Prabhakar .height = 480, 437e6d4ef7dSJacopo Mondi .crop = { 4381ed36ecdSHans Verkuil .left = 1008, 4391ed36ecdSHans Verkuil .top = 760, 440e6d4ef7dSJacopo Mondi .width = 1280, 441e6d4ef7dSJacopo Mondi .height = 960 442e6d4ef7dSJacopo Mondi }, 44325130b8aSLad Prabhakar .vts_def = IMX219_VTS_30FPS_640x480, 44425130b8aSLad Prabhakar .reg_list = { 44525130b8aSLad Prabhakar .num_of_regs = ARRAY_SIZE(mode_640_480_regs), 44625130b8aSLad Prabhakar .regs = mode_640_480_regs, 44725130b8aSLad Prabhakar }, 448ef86447eSJai Luthra .binning = true, 44925130b8aSLad Prabhakar }, 4501283b3b8SDave Stevenson }; 4511283b3b8SDave Stevenson 4521283b3b8SDave Stevenson struct imx219 { 4531283b3b8SDave Stevenson struct v4l2_subdev sd; 4541283b3b8SDave Stevenson struct media_pad pad; 4551283b3b8SDave Stevenson 456*2e943af4SLaurent Pinchart struct regmap *regmap; 4571283b3b8SDave Stevenson struct clk *xclk; /* system clock to IMX219 */ 4581283b3b8SDave Stevenson u32 xclk_freq; 4591283b3b8SDave Stevenson 4601283b3b8SDave Stevenson struct gpio_desc *reset_gpio; 4611283b3b8SDave Stevenson struct regulator_bulk_data supplies[IMX219_NUM_SUPPLIES]; 4621283b3b8SDave Stevenson 4631283b3b8SDave Stevenson struct v4l2_ctrl_handler ctrl_handler; 4641283b3b8SDave Stevenson /* V4L2 Controls */ 4651283b3b8SDave Stevenson struct v4l2_ctrl *pixel_rate; 46649b94d58SAndrey Konovalov struct v4l2_ctrl *link_freq; 4671283b3b8SDave Stevenson struct v4l2_ctrl *exposure; 4681283b3b8SDave Stevenson struct v4l2_ctrl *vflip; 4691283b3b8SDave Stevenson struct v4l2_ctrl *hflip; 4701283b3b8SDave Stevenson struct v4l2_ctrl *vblank; 4711283b3b8SDave Stevenson struct v4l2_ctrl *hblank; 4721283b3b8SDave Stevenson 4731283b3b8SDave Stevenson /* Current mode */ 4741283b3b8SDave Stevenson const struct imx219_mode *mode; 4751283b3b8SDave Stevenson 4761283b3b8SDave Stevenson /* Streaming on/off */ 4771283b3b8SDave Stevenson bool streaming; 478ceddfd44SAdam Ford 479ceddfd44SAdam Ford /* Two or Four lanes */ 480ceddfd44SAdam Ford u8 lanes; 4811283b3b8SDave Stevenson }; 4821283b3b8SDave Stevenson 4831283b3b8SDave Stevenson static inline struct imx219 *to_imx219(struct v4l2_subdev *_sd) 4841283b3b8SDave Stevenson { 4851283b3b8SDave Stevenson return container_of(_sd, struct imx219, sd); 4861283b3b8SDave Stevenson } 4871283b3b8SDave Stevenson 4881283b3b8SDave Stevenson /* Get bayer order based on flip setting. */ 48922da1d56SLad Prabhakar static u32 imx219_get_format_code(struct imx219 *imx219, u32 code) 4901283b3b8SDave Stevenson { 49122da1d56SLad Prabhakar unsigned int i; 4921283b3b8SDave Stevenson 493917e26cbSJean-Michel Hautbois for (i = 0; i < ARRAY_SIZE(imx219_mbus_formats); i++) 494917e26cbSJean-Michel Hautbois if (imx219_mbus_formats[i] == code) 49522da1d56SLad Prabhakar break; 49622da1d56SLad Prabhakar 497917e26cbSJean-Michel Hautbois if (i >= ARRAY_SIZE(imx219_mbus_formats)) 49822da1d56SLad Prabhakar i = 0; 49922da1d56SLad Prabhakar 50022da1d56SLad Prabhakar i = (i & ~3) | (imx219->vflip->val ? 2 : 0) | 50122da1d56SLad Prabhakar (imx219->hflip->val ? 1 : 0); 50222da1d56SLad Prabhakar 503917e26cbSJean-Michel Hautbois return imx219_mbus_formats[i]; 50422da1d56SLad Prabhakar } 50522da1d56SLad Prabhakar 5061283b3b8SDave Stevenson static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) 5071283b3b8SDave Stevenson { 5081283b3b8SDave Stevenson struct imx219 *imx219 = 5091283b3b8SDave Stevenson container_of(ctrl->handler, struct imx219, ctrl_handler); 5101283b3b8SDave Stevenson struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); 511*2e943af4SLaurent Pinchart int ret = 0; 5121283b3b8SDave Stevenson 5131283b3b8SDave Stevenson if (ctrl->id == V4L2_CID_VBLANK) { 5141283b3b8SDave Stevenson int exposure_max, exposure_def; 5151283b3b8SDave Stevenson 5161283b3b8SDave Stevenson /* Update max exposure while meeting expected vblanking */ 5171283b3b8SDave Stevenson exposure_max = imx219->mode->height + ctrl->val - 4; 5181283b3b8SDave Stevenson exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? 5191283b3b8SDave Stevenson exposure_max : IMX219_EXPOSURE_DEFAULT; 5201283b3b8SDave Stevenson __v4l2_ctrl_modify_range(imx219->exposure, 5211283b3b8SDave Stevenson imx219->exposure->minimum, 5221283b3b8SDave Stevenson exposure_max, imx219->exposure->step, 5231283b3b8SDave Stevenson exposure_def); 5241283b3b8SDave Stevenson } 5251283b3b8SDave Stevenson 5261283b3b8SDave Stevenson /* 5271283b3b8SDave Stevenson * Applying V4L2 control value only happens 5281283b3b8SDave Stevenson * when power is up for streaming 5291283b3b8SDave Stevenson */ 5301283b3b8SDave Stevenson if (pm_runtime_get_if_in_use(&client->dev) == 0) 5311283b3b8SDave Stevenson return 0; 5321283b3b8SDave Stevenson 5331283b3b8SDave Stevenson switch (ctrl->id) { 5341283b3b8SDave Stevenson case V4L2_CID_ANALOGUE_GAIN: 535*2e943af4SLaurent Pinchart cci_write(imx219->regmap, IMX219_REG_ANALOG_GAIN, 536*2e943af4SLaurent Pinchart ctrl->val, &ret); 5371283b3b8SDave Stevenson break; 5381283b3b8SDave Stevenson case V4L2_CID_EXPOSURE: 539*2e943af4SLaurent Pinchart cci_write(imx219->regmap, IMX219_REG_EXPOSURE, 540*2e943af4SLaurent Pinchart ctrl->val, &ret); 5411283b3b8SDave Stevenson break; 5421283b3b8SDave Stevenson case V4L2_CID_DIGITAL_GAIN: 543*2e943af4SLaurent Pinchart cci_write(imx219->regmap, IMX219_REG_DIGITAL_GAIN, 544*2e943af4SLaurent Pinchart ctrl->val, &ret); 5451283b3b8SDave Stevenson break; 5461283b3b8SDave Stevenson case V4L2_CID_TEST_PATTERN: 547*2e943af4SLaurent Pinchart cci_write(imx219->regmap, IMX219_REG_TEST_PATTERN, 548*2e943af4SLaurent Pinchart imx219_test_pattern_val[ctrl->val], &ret); 5491283b3b8SDave Stevenson break; 5501283b3b8SDave Stevenson case V4L2_CID_HFLIP: 5511283b3b8SDave Stevenson case V4L2_CID_VFLIP: 552*2e943af4SLaurent Pinchart cci_write(imx219->regmap, IMX219_REG_ORIENTATION, 553*2e943af4SLaurent Pinchart imx219->hflip->val | imx219->vflip->val << 1, &ret); 5541283b3b8SDave Stevenson break; 5551283b3b8SDave Stevenson case V4L2_CID_VBLANK: 556*2e943af4SLaurent Pinchart cci_write(imx219->regmap, IMX219_REG_VTS, 557*2e943af4SLaurent Pinchart imx219->mode->height + ctrl->val, &ret); 5581283b3b8SDave Stevenson break; 5591283b3b8SDave Stevenson case V4L2_CID_TEST_PATTERN_RED: 560*2e943af4SLaurent Pinchart cci_write(imx219->regmap, IMX219_REG_TESTP_RED, 561*2e943af4SLaurent Pinchart ctrl->val, &ret); 5621283b3b8SDave Stevenson break; 5631283b3b8SDave Stevenson case V4L2_CID_TEST_PATTERN_GREENR: 564*2e943af4SLaurent Pinchart cci_write(imx219->regmap, IMX219_REG_TESTP_GREENR, 565*2e943af4SLaurent Pinchart ctrl->val, &ret); 5661283b3b8SDave Stevenson break; 5671283b3b8SDave Stevenson case V4L2_CID_TEST_PATTERN_BLUE: 568*2e943af4SLaurent Pinchart cci_write(imx219->regmap, IMX219_REG_TESTP_BLUE, 569*2e943af4SLaurent Pinchart ctrl->val, &ret); 5701283b3b8SDave Stevenson break; 5711283b3b8SDave Stevenson case V4L2_CID_TEST_PATTERN_GREENB: 572*2e943af4SLaurent Pinchart cci_write(imx219->regmap, IMX219_REG_TESTP_GREENB, 573*2e943af4SLaurent Pinchart ctrl->val, &ret); 5741283b3b8SDave Stevenson break; 5751283b3b8SDave Stevenson default: 5761283b3b8SDave Stevenson dev_info(&client->dev, 5771283b3b8SDave Stevenson "ctrl(id:0x%x,val:0x%x) is not handled\n", 5781283b3b8SDave Stevenson ctrl->id, ctrl->val); 5791283b3b8SDave Stevenson ret = -EINVAL; 5801283b3b8SDave Stevenson break; 5811283b3b8SDave Stevenson } 5821283b3b8SDave Stevenson 5831283b3b8SDave Stevenson pm_runtime_put(&client->dev); 5841283b3b8SDave Stevenson 5851283b3b8SDave Stevenson return ret; 5861283b3b8SDave Stevenson } 5871283b3b8SDave Stevenson 5881283b3b8SDave Stevenson static const struct v4l2_ctrl_ops imx219_ctrl_ops = { 5891283b3b8SDave Stevenson .s_ctrl = imx219_set_ctrl, 5901283b3b8SDave Stevenson }; 5911283b3b8SDave Stevenson 5927319d570SJacopo Mondi static void imx219_update_pad_format(struct imx219 *imx219, 5937319d570SJacopo Mondi const struct imx219_mode *mode, 5947319d570SJacopo Mondi struct v4l2_mbus_framefmt *fmt, u32 code) 5957319d570SJacopo Mondi { 5967319d570SJacopo Mondi /* Bayer order varies with flips */ 5977319d570SJacopo Mondi fmt->code = imx219_get_format_code(imx219, code); 5987319d570SJacopo Mondi fmt->width = mode->width; 5997319d570SJacopo Mondi fmt->height = mode->height; 6007319d570SJacopo Mondi fmt->field = V4L2_FIELD_NONE; 6017319d570SJacopo Mondi fmt->colorspace = V4L2_COLORSPACE_RAW; 6027319d570SJacopo Mondi fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; 6037319d570SJacopo Mondi fmt->xfer_func = V4L2_XFER_FUNC_NONE; 6047319d570SJacopo Mondi } 6057319d570SJacopo Mondi 6067e700847SJean-Michel Hautbois static int imx219_init_cfg(struct v4l2_subdev *sd, 6077e700847SJean-Michel Hautbois struct v4l2_subdev_state *state) 6087e700847SJean-Michel Hautbois { 6097e700847SJean-Michel Hautbois struct imx219 *imx219 = to_imx219(sd); 6107e700847SJean-Michel Hautbois struct v4l2_mbus_framefmt *format; 6117e700847SJean-Michel Hautbois struct v4l2_rect *crop; 6127e700847SJean-Michel Hautbois 6135cb218ffSLaurent Pinchart /* Initialize the format. */ 6147e700847SJean-Michel Hautbois format = v4l2_subdev_get_pad_format(sd, state, 0); 6157319d570SJacopo Mondi imx219_update_pad_format(imx219, &supported_modes[0], format, 6167e700847SJean-Michel Hautbois MEDIA_BUS_FMT_SRGGB10_1X10); 6177e700847SJean-Michel Hautbois 6185cb218ffSLaurent Pinchart /* Initialize the crop rectangle. */ 6197e700847SJean-Michel Hautbois crop = v4l2_subdev_get_pad_crop(sd, state, 0); 6207e700847SJean-Michel Hautbois crop->top = IMX219_PIXEL_ARRAY_TOP; 6217e700847SJean-Michel Hautbois crop->left = IMX219_PIXEL_ARRAY_LEFT; 6227e700847SJean-Michel Hautbois crop->width = IMX219_PIXEL_ARRAY_WIDTH; 6237e700847SJean-Michel Hautbois crop->height = IMX219_PIXEL_ARRAY_HEIGHT; 6247e700847SJean-Michel Hautbois 6257e700847SJean-Michel Hautbois return 0; 6267e700847SJean-Michel Hautbois } 6277e700847SJean-Michel Hautbois 6281283b3b8SDave Stevenson static int imx219_enum_mbus_code(struct v4l2_subdev *sd, 6290d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 6301283b3b8SDave Stevenson struct v4l2_subdev_mbus_code_enum *code) 6311283b3b8SDave Stevenson { 6321283b3b8SDave Stevenson struct imx219 *imx219 = to_imx219(sd); 6331283b3b8SDave Stevenson 634917e26cbSJean-Michel Hautbois if (code->index >= (ARRAY_SIZE(imx219_mbus_formats) / 4)) 6351283b3b8SDave Stevenson return -EINVAL; 6361283b3b8SDave Stevenson 637917e26cbSJean-Michel Hautbois code->code = imx219_get_format_code(imx219, imx219_mbus_formats[code->index * 4]); 6381283b3b8SDave Stevenson 6391283b3b8SDave Stevenson return 0; 6401283b3b8SDave Stevenson } 6411283b3b8SDave Stevenson 6421283b3b8SDave Stevenson static int imx219_enum_frame_size(struct v4l2_subdev *sd, 6430d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 6441283b3b8SDave Stevenson struct v4l2_subdev_frame_size_enum *fse) 6451283b3b8SDave Stevenson { 6461283b3b8SDave Stevenson struct imx219 *imx219 = to_imx219(sd); 64781015221SHans Verkuil u32 code; 6481283b3b8SDave Stevenson 6491283b3b8SDave Stevenson if (fse->index >= ARRAY_SIZE(supported_modes)) 6501283b3b8SDave Stevenson return -EINVAL; 6511283b3b8SDave Stevenson 65281015221SHans Verkuil code = imx219_get_format_code(imx219, fse->code); 65381015221SHans Verkuil if (fse->code != code) 6541283b3b8SDave Stevenson return -EINVAL; 6551283b3b8SDave Stevenson 6561283b3b8SDave Stevenson fse->min_width = supported_modes[fse->index].width; 6571283b3b8SDave Stevenson fse->max_width = fse->min_width; 6581283b3b8SDave Stevenson fse->min_height = supported_modes[fse->index].height; 6591283b3b8SDave Stevenson fse->max_height = fse->min_height; 6601283b3b8SDave Stevenson 6611283b3b8SDave Stevenson return 0; 6621283b3b8SDave Stevenson } 6631283b3b8SDave Stevenson 6641283b3b8SDave Stevenson static int imx219_set_pad_format(struct v4l2_subdev *sd, 6650d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 6661283b3b8SDave Stevenson struct v4l2_subdev_format *fmt) 6671283b3b8SDave Stevenson { 6681283b3b8SDave Stevenson struct imx219 *imx219 = to_imx219(sd); 6691283b3b8SDave Stevenson const struct imx219_mode *mode; 6701283b3b8SDave Stevenson int exposure_max, exposure_def, hblank; 671e8a5b1dfSJacopo Mondi struct v4l2_mbus_framefmt *format; 672bb2d0112SLaurent Pinchart struct v4l2_rect *crop; 67322da1d56SLad Prabhakar 6741283b3b8SDave Stevenson mode = v4l2_find_nearest_size(supported_modes, 6751283b3b8SDave Stevenson ARRAY_SIZE(supported_modes), 6761283b3b8SDave Stevenson width, height, 6771283b3b8SDave Stevenson fmt->format.width, fmt->format.height); 678e8a5b1dfSJacopo Mondi 67934e3d3c9SJacopo Mondi imx219_update_pad_format(imx219, mode, &fmt->format, fmt->format.code); 680bb2d0112SLaurent Pinchart 681e8a5b1dfSJacopo Mondi format = v4l2_subdev_get_pad_format(sd, sd_state, 0); 682bb2d0112SLaurent Pinchart crop = v4l2_subdev_get_pad_crop(sd, sd_state, 0); 683e8a5b1dfSJacopo Mondi 684bb2d0112SLaurent Pinchart *format = fmt->format; 685bb2d0112SLaurent Pinchart *crop = mode->crop; 686bb2d0112SLaurent Pinchart 687e8a5b1dfSJacopo Mondi if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { 6881283b3b8SDave Stevenson imx219->mode = mode; 6891283b3b8SDave Stevenson /* Update limits and set FPS to default */ 6901283b3b8SDave Stevenson __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN, 6911283b3b8SDave Stevenson IMX219_VTS_MAX - mode->height, 1, 6921283b3b8SDave Stevenson mode->vts_def - mode->height); 6931283b3b8SDave Stevenson __v4l2_ctrl_s_ctrl(imx219->vblank, 6941283b3b8SDave Stevenson mode->vts_def - mode->height); 6951283b3b8SDave Stevenson /* Update max exposure while meeting expected vblanking */ 6961283b3b8SDave Stevenson exposure_max = mode->vts_def - 4; 6971283b3b8SDave Stevenson exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? 6981283b3b8SDave Stevenson exposure_max : IMX219_EXPOSURE_DEFAULT; 6991283b3b8SDave Stevenson __v4l2_ctrl_modify_range(imx219->exposure, 7001283b3b8SDave Stevenson imx219->exposure->minimum, 7011283b3b8SDave Stevenson exposure_max, imx219->exposure->step, 7021283b3b8SDave Stevenson exposure_def); 7031283b3b8SDave Stevenson /* 7041283b3b8SDave Stevenson * Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank 7051283b3b8SDave Stevenson * depends on mode->width only, and is not changeble in any 7061283b3b8SDave Stevenson * way other than changing the mode. 7071283b3b8SDave Stevenson */ 7081283b3b8SDave Stevenson hblank = IMX219_PPL_DEFAULT - mode->width; 7091283b3b8SDave Stevenson __v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1, 7101283b3b8SDave Stevenson hblank); 7111283b3b8SDave Stevenson } 7121283b3b8SDave Stevenson 7131283b3b8SDave Stevenson return 0; 7141283b3b8SDave Stevenson } 7151283b3b8SDave Stevenson 716e8a5b1dfSJacopo Mondi static int imx219_set_framefmt(struct imx219 *imx219, 717e8a5b1dfSJacopo Mondi const struct v4l2_mbus_framefmt *format) 71822da1d56SLad Prabhakar { 719e8a5b1dfSJacopo Mondi switch (format->code) { 72022da1d56SLad Prabhakar case MEDIA_BUS_FMT_SRGGB8_1X8: 72122da1d56SLad Prabhakar case MEDIA_BUS_FMT_SGRBG8_1X8: 72222da1d56SLad Prabhakar case MEDIA_BUS_FMT_SGBRG8_1X8: 72322da1d56SLad Prabhakar case MEDIA_BUS_FMT_SBGGR8_1X8: 724*2e943af4SLaurent Pinchart return cci_multi_reg_write(imx219->regmap, raw8_framefmt_regs, 725*2e943af4SLaurent Pinchart ARRAY_SIZE(raw8_framefmt_regs), NULL); 72622da1d56SLad Prabhakar 72722da1d56SLad Prabhakar case MEDIA_BUS_FMT_SRGGB10_1X10: 72822da1d56SLad Prabhakar case MEDIA_BUS_FMT_SGRBG10_1X10: 72922da1d56SLad Prabhakar case MEDIA_BUS_FMT_SGBRG10_1X10: 73022da1d56SLad Prabhakar case MEDIA_BUS_FMT_SBGGR10_1X10: 731*2e943af4SLaurent Pinchart return cci_multi_reg_write(imx219->regmap, raw10_framefmt_regs, 732*2e943af4SLaurent Pinchart ARRAY_SIZE(raw10_framefmt_regs), NULL); 73322da1d56SLad Prabhakar } 73422da1d56SLad Prabhakar 73522da1d56SLad Prabhakar return -EINVAL; 73622da1d56SLad Prabhakar } 73722da1d56SLad Prabhakar 738e8a5b1dfSJacopo Mondi static int imx219_set_binning(struct imx219 *imx219, 739e8a5b1dfSJacopo Mondi const struct v4l2_mbus_framefmt *format) 740ef86447eSJai Luthra { 741*2e943af4SLaurent Pinchart if (!imx219->mode->binning) 742*2e943af4SLaurent Pinchart return cci_write(imx219->regmap, IMX219_REG_BINNING_MODE, 743*2e943af4SLaurent Pinchart IMX219_BINNING_NONE, NULL); 744ef86447eSJai Luthra 745e8a5b1dfSJacopo Mondi switch (format->code) { 746ef86447eSJai Luthra case MEDIA_BUS_FMT_SRGGB8_1X8: 747ef86447eSJai Luthra case MEDIA_BUS_FMT_SGRBG8_1X8: 748ef86447eSJai Luthra case MEDIA_BUS_FMT_SGBRG8_1X8: 749ef86447eSJai Luthra case MEDIA_BUS_FMT_SBGGR8_1X8: 750*2e943af4SLaurent Pinchart return cci_write(imx219->regmap, IMX219_REG_BINNING_MODE, 751*2e943af4SLaurent Pinchart IMX219_BINNING_2X2_ANALOG, NULL); 752ef86447eSJai Luthra 753ef86447eSJai Luthra case MEDIA_BUS_FMT_SRGGB10_1X10: 754ef86447eSJai Luthra case MEDIA_BUS_FMT_SGRBG10_1X10: 755ef86447eSJai Luthra case MEDIA_BUS_FMT_SGBRG10_1X10: 756ef86447eSJai Luthra case MEDIA_BUS_FMT_SBGGR10_1X10: 757*2e943af4SLaurent Pinchart return cci_write(imx219->regmap, IMX219_REG_BINNING_MODE, 758*2e943af4SLaurent Pinchart IMX219_BINNING_2X2, NULL); 759ef86447eSJai Luthra } 760ef86447eSJai Luthra 761ef86447eSJai Luthra return -EINVAL; 762ef86447eSJai Luthra } 763ef86447eSJai Luthra 764e6d4ef7dSJacopo Mondi static int imx219_get_selection(struct v4l2_subdev *sd, 7650d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 766e6d4ef7dSJacopo Mondi struct v4l2_subdev_selection *sel) 767e6d4ef7dSJacopo Mondi { 768e6d4ef7dSJacopo Mondi switch (sel->target) { 769e6d4ef7dSJacopo Mondi case V4L2_SEL_TGT_CROP: { 770e8a5b1dfSJacopo Mondi sel->r = *v4l2_subdev_get_pad_crop(sd, sd_state, 0); 771e6d4ef7dSJacopo Mondi return 0; 772e6d4ef7dSJacopo Mondi } 773e6d4ef7dSJacopo Mondi 774e6d4ef7dSJacopo Mondi case V4L2_SEL_TGT_NATIVE_SIZE: 775e6d4ef7dSJacopo Mondi sel->r.top = 0; 776e6d4ef7dSJacopo Mondi sel->r.left = 0; 777e6d4ef7dSJacopo Mondi sel->r.width = IMX219_NATIVE_WIDTH; 778e6d4ef7dSJacopo Mondi sel->r.height = IMX219_NATIVE_HEIGHT; 779e6d4ef7dSJacopo Mondi 780e6d4ef7dSJacopo Mondi return 0; 781e6d4ef7dSJacopo Mondi 782e6d4ef7dSJacopo Mondi case V4L2_SEL_TGT_CROP_DEFAULT: 7831ed36ecdSHans Verkuil case V4L2_SEL_TGT_CROP_BOUNDS: 784e6d4ef7dSJacopo Mondi sel->r.top = IMX219_PIXEL_ARRAY_TOP; 785e6d4ef7dSJacopo Mondi sel->r.left = IMX219_PIXEL_ARRAY_LEFT; 786e6d4ef7dSJacopo Mondi sel->r.width = IMX219_PIXEL_ARRAY_WIDTH; 787e6d4ef7dSJacopo Mondi sel->r.height = IMX219_PIXEL_ARRAY_HEIGHT; 788e6d4ef7dSJacopo Mondi 789e6d4ef7dSJacopo Mondi return 0; 790e6d4ef7dSJacopo Mondi } 791e6d4ef7dSJacopo Mondi 792e6d4ef7dSJacopo Mondi return -EINVAL; 793e6d4ef7dSJacopo Mondi } 794e6d4ef7dSJacopo Mondi 795ceddfd44SAdam Ford static int imx219_configure_lanes(struct imx219 *imx219) 796ceddfd44SAdam Ford { 797*2e943af4SLaurent Pinchart return cci_write(imx219->regmap, IMX219_REG_CSI_LANE_MODE, 798*2e943af4SLaurent Pinchart imx219->lanes == 2 ? IMX219_CSI_2_LANE_MODE : 799*2e943af4SLaurent Pinchart IMX219_CSI_4_LANE_MODE, NULL); 800ceddfd44SAdam Ford }; 801ceddfd44SAdam Ford 802e8a5b1dfSJacopo Mondi static int imx219_start_streaming(struct imx219 *imx219, 803e8a5b1dfSJacopo Mondi struct v4l2_subdev_state *state) 8041283b3b8SDave Stevenson { 8051283b3b8SDave Stevenson struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); 806e8a5b1dfSJacopo Mondi const struct v4l2_mbus_framefmt *format; 8071283b3b8SDave Stevenson const struct imx219_reg_list *reg_list; 8081283b3b8SDave Stevenson int ret; 8091283b3b8SDave Stevenson 81030ad4559SMauro Carvalho Chehab ret = pm_runtime_resume_and_get(&client->dev); 81130ad4559SMauro Carvalho Chehab if (ret < 0) 812dd90caa0SLad Prabhakar return ret; 813dd90caa0SLad Prabhakar 81485084559SAdam Ford /* Send all registers that are common to all modes */ 815*2e943af4SLaurent Pinchart ret = cci_multi_reg_write(imx219->regmap, imx219_common_regs, 816*2e943af4SLaurent Pinchart ARRAY_SIZE(imx219_common_regs), NULL); 81785084559SAdam Ford if (ret) { 81885084559SAdam Ford dev_err(&client->dev, "%s failed to send mfg header\n", __func__); 81985084559SAdam Ford goto err_rpm_put; 82085084559SAdam Ford } 82185084559SAdam Ford 822ceddfd44SAdam Ford /* Configure two or four Lane mode */ 823ceddfd44SAdam Ford ret = imx219_configure_lanes(imx219); 824ceddfd44SAdam Ford if (ret) { 825ceddfd44SAdam Ford dev_err(&client->dev, "%s failed to configure lanes\n", __func__); 826ceddfd44SAdam Ford goto err_rpm_put; 827ceddfd44SAdam Ford } 828ceddfd44SAdam Ford 8291283b3b8SDave Stevenson /* Apply default values of current mode */ 8301283b3b8SDave Stevenson reg_list = &imx219->mode->reg_list; 831*2e943af4SLaurent Pinchart ret = cci_multi_reg_write(imx219->regmap, reg_list->regs, 832*2e943af4SLaurent Pinchart reg_list->num_of_regs, NULL); 8331283b3b8SDave Stevenson if (ret) { 8341283b3b8SDave Stevenson dev_err(&client->dev, "%s failed to set mode\n", __func__); 835dd90caa0SLad Prabhakar goto err_rpm_put; 8361283b3b8SDave Stevenson } 8371283b3b8SDave Stevenson 838e8a5b1dfSJacopo Mondi format = v4l2_subdev_get_pad_format(&imx219->sd, state, 0); 839e8a5b1dfSJacopo Mondi ret = imx219_set_framefmt(imx219, format); 84022da1d56SLad Prabhakar if (ret) { 84122da1d56SLad Prabhakar dev_err(&client->dev, "%s failed to set frame format: %d\n", 84222da1d56SLad Prabhakar __func__, ret); 843dd90caa0SLad Prabhakar goto err_rpm_put; 84422da1d56SLad Prabhakar } 84522da1d56SLad Prabhakar 846e8a5b1dfSJacopo Mondi ret = imx219_set_binning(imx219, format); 847ef86447eSJai Luthra if (ret) { 848ef86447eSJai Luthra dev_err(&client->dev, "%s failed to set binning: %d\n", 849ef86447eSJai Luthra __func__, ret); 850ef86447eSJai Luthra goto err_rpm_put; 851ef86447eSJai Luthra } 852ef86447eSJai Luthra 8531283b3b8SDave Stevenson /* Apply customized values from user */ 8541283b3b8SDave Stevenson ret = __v4l2_ctrl_handler_setup(imx219->sd.ctrl_handler); 8551283b3b8SDave Stevenson if (ret) 856dd90caa0SLad Prabhakar goto err_rpm_put; 8571283b3b8SDave Stevenson 8581283b3b8SDave Stevenson /* set stream on register */ 859*2e943af4SLaurent Pinchart ret = cci_write(imx219->regmap, IMX219_REG_MODE_SELECT, 860*2e943af4SLaurent Pinchart IMX219_MODE_STREAMING, NULL); 861745d4612SLad Prabhakar if (ret) 862dd90caa0SLad Prabhakar goto err_rpm_put; 863745d4612SLad Prabhakar 864745d4612SLad Prabhakar /* vflip and hflip cannot change during streaming */ 865745d4612SLad Prabhakar __v4l2_ctrl_grab(imx219->vflip, true); 866745d4612SLad Prabhakar __v4l2_ctrl_grab(imx219->hflip, true); 867745d4612SLad Prabhakar 868745d4612SLad Prabhakar return 0; 869dd90caa0SLad Prabhakar 870dd90caa0SLad Prabhakar err_rpm_put: 871dd90caa0SLad Prabhakar pm_runtime_put(&client->dev); 872dd90caa0SLad Prabhakar return ret; 8731283b3b8SDave Stevenson } 8741283b3b8SDave Stevenson 8751283b3b8SDave Stevenson static void imx219_stop_streaming(struct imx219 *imx219) 8761283b3b8SDave Stevenson { 8771283b3b8SDave Stevenson struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); 8781283b3b8SDave Stevenson int ret; 8791283b3b8SDave Stevenson 8801283b3b8SDave Stevenson /* set stream off register */ 881*2e943af4SLaurent Pinchart ret = cci_write(imx219->regmap, IMX219_REG_MODE_SELECT, 882*2e943af4SLaurent Pinchart IMX219_MODE_STANDBY, NULL); 8831283b3b8SDave Stevenson if (ret) 8841283b3b8SDave Stevenson dev_err(&client->dev, "%s failed to set stream\n", __func__); 885745d4612SLad Prabhakar 886745d4612SLad Prabhakar __v4l2_ctrl_grab(imx219->vflip, false); 887745d4612SLad Prabhakar __v4l2_ctrl_grab(imx219->hflip, false); 888dd90caa0SLad Prabhakar 889dd90caa0SLad Prabhakar pm_runtime_put(&client->dev); 8901283b3b8SDave Stevenson } 8911283b3b8SDave Stevenson 8921283b3b8SDave Stevenson static int imx219_set_stream(struct v4l2_subdev *sd, int enable) 8931283b3b8SDave Stevenson { 8941283b3b8SDave Stevenson struct imx219 *imx219 = to_imx219(sd); 895e8a5b1dfSJacopo Mondi struct v4l2_subdev_state *state; 8961283b3b8SDave Stevenson int ret = 0; 8971283b3b8SDave Stevenson 898e8a5b1dfSJacopo Mondi state = v4l2_subdev_lock_and_get_active_state(sd); 899e8a5b1dfSJacopo Mondi 900e8a5b1dfSJacopo Mondi if (imx219->streaming == enable) 901e8a5b1dfSJacopo Mondi goto unlock; 9021283b3b8SDave Stevenson 9031283b3b8SDave Stevenson if (enable) { 9041283b3b8SDave Stevenson /* 9051283b3b8SDave Stevenson * Apply default & customized values 9061283b3b8SDave Stevenson * and then start streaming. 9071283b3b8SDave Stevenson */ 908e8a5b1dfSJacopo Mondi ret = imx219_start_streaming(imx219, state); 9091283b3b8SDave Stevenson if (ret) 910e8a5b1dfSJacopo Mondi goto unlock; 9111283b3b8SDave Stevenson } else { 9121283b3b8SDave Stevenson imx219_stop_streaming(imx219); 9131283b3b8SDave Stevenson } 9141283b3b8SDave Stevenson 9151283b3b8SDave Stevenson imx219->streaming = enable; 9161283b3b8SDave Stevenson 917e8a5b1dfSJacopo Mondi unlock: 918e8a5b1dfSJacopo Mondi v4l2_subdev_unlock_state(state); 9191283b3b8SDave Stevenson return ret; 9201283b3b8SDave Stevenson } 9211283b3b8SDave Stevenson 9221283b3b8SDave Stevenson /* Power/clock management functions */ 9231283b3b8SDave Stevenson static int imx219_power_on(struct device *dev) 9241283b3b8SDave Stevenson { 92537bb22edSKrzysztof Kozlowski struct v4l2_subdev *sd = dev_get_drvdata(dev); 9261283b3b8SDave Stevenson struct imx219 *imx219 = to_imx219(sd); 9271283b3b8SDave Stevenson int ret; 9281283b3b8SDave Stevenson 9291283b3b8SDave Stevenson ret = regulator_bulk_enable(IMX219_NUM_SUPPLIES, 9301283b3b8SDave Stevenson imx219->supplies); 9311283b3b8SDave Stevenson if (ret) { 93237bb22edSKrzysztof Kozlowski dev_err(dev, "%s: failed to enable regulators\n", 9331283b3b8SDave Stevenson __func__); 9341283b3b8SDave Stevenson return ret; 9351283b3b8SDave Stevenson } 9361283b3b8SDave Stevenson 9371283b3b8SDave Stevenson ret = clk_prepare_enable(imx219->xclk); 9381283b3b8SDave Stevenson if (ret) { 93937bb22edSKrzysztof Kozlowski dev_err(dev, "%s: failed to enable clock\n", 9401283b3b8SDave Stevenson __func__); 9411283b3b8SDave Stevenson goto reg_off; 9421283b3b8SDave Stevenson } 9431283b3b8SDave Stevenson 9441283b3b8SDave Stevenson gpiod_set_value_cansleep(imx219->reset_gpio, 1); 9451283b3b8SDave Stevenson usleep_range(IMX219_XCLR_MIN_DELAY_US, 9461283b3b8SDave Stevenson IMX219_XCLR_MIN_DELAY_US + IMX219_XCLR_DELAY_RANGE_US); 9471283b3b8SDave Stevenson 9481283b3b8SDave Stevenson return 0; 9491283b3b8SDave Stevenson 9501283b3b8SDave Stevenson reg_off: 9511283b3b8SDave Stevenson regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies); 9521283b3b8SDave Stevenson 9531283b3b8SDave Stevenson return ret; 9541283b3b8SDave Stevenson } 9551283b3b8SDave Stevenson 9561283b3b8SDave Stevenson static int imx219_power_off(struct device *dev) 9571283b3b8SDave Stevenson { 95837bb22edSKrzysztof Kozlowski struct v4l2_subdev *sd = dev_get_drvdata(dev); 9591283b3b8SDave Stevenson struct imx219 *imx219 = to_imx219(sd); 9601283b3b8SDave Stevenson 9611283b3b8SDave Stevenson gpiod_set_value_cansleep(imx219->reset_gpio, 0); 9621283b3b8SDave Stevenson regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies); 9631283b3b8SDave Stevenson clk_disable_unprepare(imx219->xclk); 9641283b3b8SDave Stevenson 9651283b3b8SDave Stevenson return 0; 9661283b3b8SDave Stevenson } 9671283b3b8SDave Stevenson 9681283b3b8SDave Stevenson static int __maybe_unused imx219_suspend(struct device *dev) 9691283b3b8SDave Stevenson { 97037bb22edSKrzysztof Kozlowski struct v4l2_subdev *sd = dev_get_drvdata(dev); 9711283b3b8SDave Stevenson struct imx219 *imx219 = to_imx219(sd); 9721283b3b8SDave Stevenson 9731283b3b8SDave Stevenson if (imx219->streaming) 9741283b3b8SDave Stevenson imx219_stop_streaming(imx219); 9751283b3b8SDave Stevenson 9761283b3b8SDave Stevenson return 0; 9771283b3b8SDave Stevenson } 9781283b3b8SDave Stevenson 9791283b3b8SDave Stevenson static int __maybe_unused imx219_resume(struct device *dev) 9801283b3b8SDave Stevenson { 98137bb22edSKrzysztof Kozlowski struct v4l2_subdev *sd = dev_get_drvdata(dev); 9821283b3b8SDave Stevenson struct imx219 *imx219 = to_imx219(sd); 983e8a5b1dfSJacopo Mondi struct v4l2_subdev_state *state; 9841283b3b8SDave Stevenson int ret; 9851283b3b8SDave Stevenson 9861283b3b8SDave Stevenson if (imx219->streaming) { 987e8a5b1dfSJacopo Mondi state = v4l2_subdev_lock_and_get_active_state(sd); 988e8a5b1dfSJacopo Mondi ret = imx219_start_streaming(imx219, state); 989e8a5b1dfSJacopo Mondi v4l2_subdev_unlock_state(state); 9901283b3b8SDave Stevenson if (ret) 9911283b3b8SDave Stevenson goto error; 9921283b3b8SDave Stevenson } 9931283b3b8SDave Stevenson 9941283b3b8SDave Stevenson return 0; 9951283b3b8SDave Stevenson 9961283b3b8SDave Stevenson error: 9971283b3b8SDave Stevenson imx219_stop_streaming(imx219); 998c90b4d70SDaniel W. S. Almeida imx219->streaming = false; 9991283b3b8SDave Stevenson 10001283b3b8SDave Stevenson return ret; 10011283b3b8SDave Stevenson } 10021283b3b8SDave Stevenson 10031283b3b8SDave Stevenson static int imx219_get_regulators(struct imx219 *imx219) 10041283b3b8SDave Stevenson { 10051283b3b8SDave Stevenson struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); 10061283b3b8SDave Stevenson unsigned int i; 10071283b3b8SDave Stevenson 10081283b3b8SDave Stevenson for (i = 0; i < IMX219_NUM_SUPPLIES; i++) 10091283b3b8SDave Stevenson imx219->supplies[i].supply = imx219_supply_name[i]; 10101283b3b8SDave Stevenson 10111283b3b8SDave Stevenson return devm_regulator_bulk_get(&client->dev, 10121283b3b8SDave Stevenson IMX219_NUM_SUPPLIES, 10131283b3b8SDave Stevenson imx219->supplies); 10141283b3b8SDave Stevenson } 10151283b3b8SDave Stevenson 10161283b3b8SDave Stevenson /* Verify chip ID */ 10171283b3b8SDave Stevenson static int imx219_identify_module(struct imx219 *imx219) 10181283b3b8SDave Stevenson { 10191283b3b8SDave Stevenson struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); 10201283b3b8SDave Stevenson int ret; 1021*2e943af4SLaurent Pinchart u64 val; 10221283b3b8SDave Stevenson 1023*2e943af4SLaurent Pinchart ret = cci_read(imx219->regmap, IMX219_REG_CHIP_ID, &val, NULL); 10241283b3b8SDave Stevenson if (ret) { 10251283b3b8SDave Stevenson dev_err(&client->dev, "failed to read chip id %x\n", 10261283b3b8SDave Stevenson IMX219_CHIP_ID); 10271283b3b8SDave Stevenson return ret; 10281283b3b8SDave Stevenson } 10291283b3b8SDave Stevenson 10301283b3b8SDave Stevenson if (val != IMX219_CHIP_ID) { 1031*2e943af4SLaurent Pinchart dev_err(&client->dev, "chip id mismatch: %x!=%llx\n", 10321283b3b8SDave Stevenson IMX219_CHIP_ID, val); 10331283b3b8SDave Stevenson return -EIO; 10341283b3b8SDave Stevenson } 10351283b3b8SDave Stevenson 10361283b3b8SDave Stevenson return 0; 10371283b3b8SDave Stevenson } 10381283b3b8SDave Stevenson 10391283b3b8SDave Stevenson static const struct v4l2_subdev_core_ops imx219_core_ops = { 10401283b3b8SDave Stevenson .subscribe_event = v4l2_ctrl_subdev_subscribe_event, 10411283b3b8SDave Stevenson .unsubscribe_event = v4l2_event_subdev_unsubscribe, 10421283b3b8SDave Stevenson }; 10431283b3b8SDave Stevenson 10441283b3b8SDave Stevenson static const struct v4l2_subdev_video_ops imx219_video_ops = { 10451283b3b8SDave Stevenson .s_stream = imx219_set_stream, 10461283b3b8SDave Stevenson }; 10471283b3b8SDave Stevenson 10481283b3b8SDave Stevenson static const struct v4l2_subdev_pad_ops imx219_pad_ops = { 10497e700847SJean-Michel Hautbois .init_cfg = imx219_init_cfg, 10501283b3b8SDave Stevenson .enum_mbus_code = imx219_enum_mbus_code, 1051e8a5b1dfSJacopo Mondi .get_fmt = v4l2_subdev_get_fmt, 10521283b3b8SDave Stevenson .set_fmt = imx219_set_pad_format, 1053e6d4ef7dSJacopo Mondi .get_selection = imx219_get_selection, 10541283b3b8SDave Stevenson .enum_frame_size = imx219_enum_frame_size, 10551283b3b8SDave Stevenson }; 10561283b3b8SDave Stevenson 10571283b3b8SDave Stevenson static const struct v4l2_subdev_ops imx219_subdev_ops = { 10581283b3b8SDave Stevenson .core = &imx219_core_ops, 10591283b3b8SDave Stevenson .video = &imx219_video_ops, 10601283b3b8SDave Stevenson .pad = &imx219_pad_ops, 10611283b3b8SDave Stevenson }; 10621283b3b8SDave Stevenson 10631283b3b8SDave Stevenson 1064ceddfd44SAdam Ford static unsigned long imx219_get_pixel_rate(struct imx219 *imx219) 1065ceddfd44SAdam Ford { 1066ceddfd44SAdam Ford return (imx219->lanes == 2) ? IMX219_PIXEL_RATE : IMX219_PIXEL_RATE_4LANE; 1067ceddfd44SAdam Ford } 1068ceddfd44SAdam Ford 10691283b3b8SDave Stevenson /* Initialize control handlers */ 10701283b3b8SDave Stevenson static int imx219_init_controls(struct imx219 *imx219) 10711283b3b8SDave Stevenson { 10721283b3b8SDave Stevenson struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); 10731283b3b8SDave Stevenson struct v4l2_ctrl_handler *ctrl_hdlr; 10741283b3b8SDave Stevenson unsigned int height = imx219->mode->height; 1075ad3a44cbSJacopo Mondi struct v4l2_fwnode_device_properties props; 10761283b3b8SDave Stevenson int exposure_max, exposure_def, hblank; 10771283b3b8SDave Stevenson int i, ret; 10781283b3b8SDave Stevenson 10791283b3b8SDave Stevenson ctrl_hdlr = &imx219->ctrl_handler; 108049b94d58SAndrey Konovalov ret = v4l2_ctrl_handler_init(ctrl_hdlr, 12); 10811283b3b8SDave Stevenson if (ret) 10821283b3b8SDave Stevenson return ret; 10831283b3b8SDave Stevenson 10841283b3b8SDave Stevenson /* By default, PIXEL_RATE is read only */ 10851283b3b8SDave Stevenson imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, 10861283b3b8SDave Stevenson V4L2_CID_PIXEL_RATE, 1087ceddfd44SAdam Ford imx219_get_pixel_rate(imx219), 1088ceddfd44SAdam Ford imx219_get_pixel_rate(imx219), 1, 1089ceddfd44SAdam Ford imx219_get_pixel_rate(imx219)); 10901283b3b8SDave Stevenson 109149b94d58SAndrey Konovalov imx219->link_freq = 109249b94d58SAndrey Konovalov v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx219_ctrl_ops, 109349b94d58SAndrey Konovalov V4L2_CID_LINK_FREQ, 109449b94d58SAndrey Konovalov ARRAY_SIZE(imx219_link_freq_menu) - 1, 0, 1095ceddfd44SAdam Ford (imx219->lanes == 2) ? imx219_link_freq_menu : 1096ceddfd44SAdam Ford imx219_link_freq_4lane_menu); 109749b94d58SAndrey Konovalov if (imx219->link_freq) 109849b94d58SAndrey Konovalov imx219->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; 109949b94d58SAndrey Konovalov 11001283b3b8SDave Stevenson /* Initial vblank/hblank/exposure parameters based on current mode */ 11011283b3b8SDave Stevenson imx219->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, 11021283b3b8SDave Stevenson V4L2_CID_VBLANK, IMX219_VBLANK_MIN, 11031283b3b8SDave Stevenson IMX219_VTS_MAX - height, 1, 11041283b3b8SDave Stevenson imx219->mode->vts_def - height); 11051283b3b8SDave Stevenson hblank = IMX219_PPL_DEFAULT - imx219->mode->width; 11061283b3b8SDave Stevenson imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, 11071283b3b8SDave Stevenson V4L2_CID_HBLANK, hblank, hblank, 11081283b3b8SDave Stevenson 1, hblank); 11091283b3b8SDave Stevenson if (imx219->hblank) 11101283b3b8SDave Stevenson imx219->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; 11111283b3b8SDave Stevenson exposure_max = imx219->mode->vts_def - 4; 11121283b3b8SDave Stevenson exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? 11131283b3b8SDave Stevenson exposure_max : IMX219_EXPOSURE_DEFAULT; 11141283b3b8SDave Stevenson imx219->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, 11151283b3b8SDave Stevenson V4L2_CID_EXPOSURE, 11161283b3b8SDave Stevenson IMX219_EXPOSURE_MIN, exposure_max, 11171283b3b8SDave Stevenson IMX219_EXPOSURE_STEP, 11181283b3b8SDave Stevenson exposure_def); 11191283b3b8SDave Stevenson 11201283b3b8SDave Stevenson v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, 11211283b3b8SDave Stevenson IMX219_ANA_GAIN_MIN, IMX219_ANA_GAIN_MAX, 11221283b3b8SDave Stevenson IMX219_ANA_GAIN_STEP, IMX219_ANA_GAIN_DEFAULT); 11231283b3b8SDave Stevenson 11241283b3b8SDave Stevenson v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_DIGITAL_GAIN, 11251283b3b8SDave Stevenson IMX219_DGTL_GAIN_MIN, IMX219_DGTL_GAIN_MAX, 11261283b3b8SDave Stevenson IMX219_DGTL_GAIN_STEP, IMX219_DGTL_GAIN_DEFAULT); 11271283b3b8SDave Stevenson 11281283b3b8SDave Stevenson imx219->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, 11291283b3b8SDave Stevenson V4L2_CID_HFLIP, 0, 1, 1, 0); 11301283b3b8SDave Stevenson if (imx219->hflip) 11311283b3b8SDave Stevenson imx219->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; 11321283b3b8SDave Stevenson 11331283b3b8SDave Stevenson imx219->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, 11341283b3b8SDave Stevenson V4L2_CID_VFLIP, 0, 1, 1, 0); 11351283b3b8SDave Stevenson if (imx219->vflip) 11361283b3b8SDave Stevenson imx219->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; 11371283b3b8SDave Stevenson 11381283b3b8SDave Stevenson v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx219_ctrl_ops, 11391283b3b8SDave Stevenson V4L2_CID_TEST_PATTERN, 11401283b3b8SDave Stevenson ARRAY_SIZE(imx219_test_pattern_menu) - 1, 11411283b3b8SDave Stevenson 0, 0, imx219_test_pattern_menu); 11421283b3b8SDave Stevenson for (i = 0; i < 4; i++) { 11431283b3b8SDave Stevenson /* 11441283b3b8SDave Stevenson * The assumption is that 11451283b3b8SDave Stevenson * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1 11461283b3b8SDave Stevenson * V4L2_CID_TEST_PATTERN_BLUE == V4L2_CID_TEST_PATTERN_RED + 2 11471283b3b8SDave Stevenson * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3 11481283b3b8SDave Stevenson */ 11491283b3b8SDave Stevenson v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, 11501283b3b8SDave Stevenson V4L2_CID_TEST_PATTERN_RED + i, 11511283b3b8SDave Stevenson IMX219_TESTP_COLOUR_MIN, 11521283b3b8SDave Stevenson IMX219_TESTP_COLOUR_MAX, 11531283b3b8SDave Stevenson IMX219_TESTP_COLOUR_STEP, 11541283b3b8SDave Stevenson IMX219_TESTP_COLOUR_MAX); 11551283b3b8SDave Stevenson /* The "Solid color" pattern is white by default */ 11561283b3b8SDave Stevenson } 11571283b3b8SDave Stevenson 11581283b3b8SDave Stevenson if (ctrl_hdlr->error) { 11591283b3b8SDave Stevenson ret = ctrl_hdlr->error; 11601283b3b8SDave Stevenson dev_err(&client->dev, "%s control init failed (%d)\n", 11611283b3b8SDave Stevenson __func__, ret); 11621283b3b8SDave Stevenson goto error; 11631283b3b8SDave Stevenson } 11641283b3b8SDave Stevenson 1165ad3a44cbSJacopo Mondi ret = v4l2_fwnode_device_parse(&client->dev, &props); 1166ad3a44cbSJacopo Mondi if (ret) 1167ad3a44cbSJacopo Mondi goto error; 1168ad3a44cbSJacopo Mondi 1169ad3a44cbSJacopo Mondi ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx219_ctrl_ops, 1170ad3a44cbSJacopo Mondi &props); 1171ad3a44cbSJacopo Mondi if (ret) 1172ad3a44cbSJacopo Mondi goto error; 1173ad3a44cbSJacopo Mondi 11741283b3b8SDave Stevenson imx219->sd.ctrl_handler = ctrl_hdlr; 11751283b3b8SDave Stevenson 11761283b3b8SDave Stevenson return 0; 11771283b3b8SDave Stevenson 11781283b3b8SDave Stevenson error: 11791283b3b8SDave Stevenson v4l2_ctrl_handler_free(ctrl_hdlr); 11801283b3b8SDave Stevenson 11811283b3b8SDave Stevenson return ret; 11821283b3b8SDave Stevenson } 11831283b3b8SDave Stevenson 11841283b3b8SDave Stevenson static void imx219_free_controls(struct imx219 *imx219) 11851283b3b8SDave Stevenson { 11861283b3b8SDave Stevenson v4l2_ctrl_handler_free(imx219->sd.ctrl_handler); 11871283b3b8SDave Stevenson } 11881283b3b8SDave Stevenson 1189ceddfd44SAdam Ford static int imx219_check_hwcfg(struct device *dev, struct imx219 *imx219) 11901283b3b8SDave Stevenson { 11911283b3b8SDave Stevenson struct fwnode_handle *endpoint; 11921283b3b8SDave Stevenson struct v4l2_fwnode_endpoint ep_cfg = { 11931283b3b8SDave Stevenson .bus_type = V4L2_MBUS_CSI2_DPHY 11941283b3b8SDave Stevenson }; 11951283b3b8SDave Stevenson int ret = -EINVAL; 11961283b3b8SDave Stevenson 11971283b3b8SDave Stevenson endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); 11981283b3b8SDave Stevenson if (!endpoint) { 11991283b3b8SDave Stevenson dev_err(dev, "endpoint node not found\n"); 12001283b3b8SDave Stevenson return -EINVAL; 12011283b3b8SDave Stevenson } 12021283b3b8SDave Stevenson 12031283b3b8SDave Stevenson if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) { 12041283b3b8SDave Stevenson dev_err(dev, "could not parse endpoint\n"); 12051283b3b8SDave Stevenson goto error_out; 12061283b3b8SDave Stevenson } 12071283b3b8SDave Stevenson 12081283b3b8SDave Stevenson /* Check the number of MIPI CSI2 data lanes */ 1209ceddfd44SAdam Ford if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2 && 1210ceddfd44SAdam Ford ep_cfg.bus.mipi_csi2.num_data_lanes != 4) { 1211ceddfd44SAdam Ford dev_err(dev, "only 2 or 4 data lanes are currently supported\n"); 12121283b3b8SDave Stevenson goto error_out; 12131283b3b8SDave Stevenson } 1214ceddfd44SAdam Ford imx219->lanes = ep_cfg.bus.mipi_csi2.num_data_lanes; 12151283b3b8SDave Stevenson 12161283b3b8SDave Stevenson /* Check the link frequency set in device tree */ 12171283b3b8SDave Stevenson if (!ep_cfg.nr_of_link_frequencies) { 12181283b3b8SDave Stevenson dev_err(dev, "link-frequency property not found in DT\n"); 12191283b3b8SDave Stevenson goto error_out; 12201283b3b8SDave Stevenson } 12211283b3b8SDave Stevenson 12221283b3b8SDave Stevenson if (ep_cfg.nr_of_link_frequencies != 1 || 1223ceddfd44SAdam Ford (ep_cfg.link_frequencies[0] != ((imx219->lanes == 2) ? 1224ceddfd44SAdam Ford IMX219_DEFAULT_LINK_FREQ : IMX219_DEFAULT_LINK_FREQ_4LANE))) { 12251283b3b8SDave Stevenson dev_err(dev, "Link frequency not supported: %lld\n", 12261283b3b8SDave Stevenson ep_cfg.link_frequencies[0]); 12271283b3b8SDave Stevenson goto error_out; 12281283b3b8SDave Stevenson } 12291283b3b8SDave Stevenson 12301283b3b8SDave Stevenson ret = 0; 12311283b3b8SDave Stevenson 12321283b3b8SDave Stevenson error_out: 12331283b3b8SDave Stevenson v4l2_fwnode_endpoint_free(&ep_cfg); 12341283b3b8SDave Stevenson fwnode_handle_put(endpoint); 12351283b3b8SDave Stevenson 12361283b3b8SDave Stevenson return ret; 12371283b3b8SDave Stevenson } 12381283b3b8SDave Stevenson 12391283b3b8SDave Stevenson static int imx219_probe(struct i2c_client *client) 12401283b3b8SDave Stevenson { 12411283b3b8SDave Stevenson struct device *dev = &client->dev; 12421283b3b8SDave Stevenson struct imx219 *imx219; 12431283b3b8SDave Stevenson int ret; 12441283b3b8SDave Stevenson 12451283b3b8SDave Stevenson imx219 = devm_kzalloc(&client->dev, sizeof(*imx219), GFP_KERNEL); 12461283b3b8SDave Stevenson if (!imx219) 12471283b3b8SDave Stevenson return -ENOMEM; 12481283b3b8SDave Stevenson 12491283b3b8SDave Stevenson v4l2_i2c_subdev_init(&imx219->sd, client, &imx219_subdev_ops); 12501283b3b8SDave Stevenson 12511283b3b8SDave Stevenson /* Check the hardware configuration in device tree */ 1252ceddfd44SAdam Ford if (imx219_check_hwcfg(dev, imx219)) 12531283b3b8SDave Stevenson return -EINVAL; 12541283b3b8SDave Stevenson 1255*2e943af4SLaurent Pinchart imx219->regmap = devm_cci_regmap_init_i2c(client, 16); 1256*2e943af4SLaurent Pinchart if (IS_ERR(imx219->regmap)) { 1257*2e943af4SLaurent Pinchart ret = PTR_ERR(imx219->regmap); 1258*2e943af4SLaurent Pinchart dev_err(dev, "failed to initialize CCI: %d\n", ret); 1259*2e943af4SLaurent Pinchart return ret; 1260*2e943af4SLaurent Pinchart } 1261*2e943af4SLaurent Pinchart 12621283b3b8SDave Stevenson /* Get system clock (xclk) */ 12631283b3b8SDave Stevenson imx219->xclk = devm_clk_get(dev, NULL); 12641283b3b8SDave Stevenson if (IS_ERR(imx219->xclk)) { 12651283b3b8SDave Stevenson dev_err(dev, "failed to get xclk\n"); 12661283b3b8SDave Stevenson return PTR_ERR(imx219->xclk); 12671283b3b8SDave Stevenson } 12681283b3b8SDave Stevenson 12691283b3b8SDave Stevenson imx219->xclk_freq = clk_get_rate(imx219->xclk); 12701283b3b8SDave Stevenson if (imx219->xclk_freq != IMX219_XCLK_FREQ) { 12711283b3b8SDave Stevenson dev_err(dev, "xclk frequency not supported: %d Hz\n", 12721283b3b8SDave Stevenson imx219->xclk_freq); 12731283b3b8SDave Stevenson return -EINVAL; 12741283b3b8SDave Stevenson } 12751283b3b8SDave Stevenson 12761283b3b8SDave Stevenson ret = imx219_get_regulators(imx219); 12771283b3b8SDave Stevenson if (ret) { 12781283b3b8SDave Stevenson dev_err(dev, "failed to get regulators\n"); 12791283b3b8SDave Stevenson return ret; 12801283b3b8SDave Stevenson } 12811283b3b8SDave Stevenson 12821283b3b8SDave Stevenson /* Request optional enable pin */ 12831283b3b8SDave Stevenson imx219->reset_gpio = devm_gpiod_get_optional(dev, "reset", 12841283b3b8SDave Stevenson GPIOD_OUT_HIGH); 12851283b3b8SDave Stevenson 12861283b3b8SDave Stevenson /* 12871283b3b8SDave Stevenson * The sensor must be powered for imx219_identify_module() 12881283b3b8SDave Stevenson * to be able to read the CHIP_ID register 12891283b3b8SDave Stevenson */ 12901283b3b8SDave Stevenson ret = imx219_power_on(dev); 12911283b3b8SDave Stevenson if (ret) 12921283b3b8SDave Stevenson return ret; 12931283b3b8SDave Stevenson 12941283b3b8SDave Stevenson ret = imx219_identify_module(imx219); 12951283b3b8SDave Stevenson if (ret) 12961283b3b8SDave Stevenson goto error_power_off; 12971283b3b8SDave Stevenson 12981283b3b8SDave Stevenson /* Set default mode to max resolution */ 12991283b3b8SDave Stevenson imx219->mode = &supported_modes[0]; 13001283b3b8SDave Stevenson 1301ca45448aSLad Prabhakar /* sensor doesn't enter LP-11 state upon power up until and unless 1302ca45448aSLad Prabhakar * streaming is started, so upon power up switch the modes to: 1303ca45448aSLad Prabhakar * streaming -> standby 1304ca45448aSLad Prabhakar */ 1305*2e943af4SLaurent Pinchart ret = cci_write(imx219->regmap, IMX219_REG_MODE_SELECT, 1306*2e943af4SLaurent Pinchart IMX219_MODE_STREAMING, NULL); 1307ca45448aSLad Prabhakar if (ret < 0) 1308ca45448aSLad Prabhakar goto error_power_off; 1309*2e943af4SLaurent Pinchart 1310ca45448aSLad Prabhakar usleep_range(100, 110); 1311ca45448aSLad Prabhakar 1312ca45448aSLad Prabhakar /* put sensor back to standby mode */ 1313*2e943af4SLaurent Pinchart ret = cci_write(imx219->regmap, IMX219_REG_MODE_SELECT, 1314*2e943af4SLaurent Pinchart IMX219_MODE_STANDBY, NULL); 1315ca45448aSLad Prabhakar if (ret < 0) 1316ca45448aSLad Prabhakar goto error_power_off; 1317*2e943af4SLaurent Pinchart 1318ca45448aSLad Prabhakar usleep_range(100, 110); 1319ca45448aSLad Prabhakar 13201283b3b8SDave Stevenson ret = imx219_init_controls(imx219); 13211283b3b8SDave Stevenson if (ret) 13221283b3b8SDave Stevenson goto error_power_off; 13231283b3b8SDave Stevenson 13241283b3b8SDave Stevenson /* Initialize subdev */ 1325defbac5dSDave Stevenson imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | 1326defbac5dSDave Stevenson V4L2_SUBDEV_FL_HAS_EVENTS; 13271283b3b8SDave Stevenson imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; 13281283b3b8SDave Stevenson 13291283b3b8SDave Stevenson /* Initialize source pad */ 13301283b3b8SDave Stevenson imx219->pad.flags = MEDIA_PAD_FL_SOURCE; 13311283b3b8SDave Stevenson 13321283b3b8SDave Stevenson ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad); 13331283b3b8SDave Stevenson if (ret) { 13341283b3b8SDave Stevenson dev_err(dev, "failed to init entity pads: %d\n", ret); 13351283b3b8SDave Stevenson goto error_handler_free; 13361283b3b8SDave Stevenson } 13371283b3b8SDave Stevenson 1338e8a5b1dfSJacopo Mondi imx219->sd.state_lock = imx219->ctrl_handler.lock; 1339e8a5b1dfSJacopo Mondi ret = v4l2_subdev_init_finalize(&imx219->sd); 1340e8a5b1dfSJacopo Mondi if (ret < 0) { 1341e8a5b1dfSJacopo Mondi dev_err(dev, "subdev init error: %d\n", ret); 1342e8a5b1dfSJacopo Mondi goto error_media_entity; 1343e8a5b1dfSJacopo Mondi } 1344e8a5b1dfSJacopo Mondi 134515786f7bSSakari Ailus ret = v4l2_async_register_subdev_sensor(&imx219->sd); 13461283b3b8SDave Stevenson if (ret < 0) { 13471283b3b8SDave Stevenson dev_err(dev, "failed to register sensor sub-device: %d\n", ret); 1348e8a5b1dfSJacopo Mondi goto error_subdev_cleanup; 13491283b3b8SDave Stevenson } 13501283b3b8SDave Stevenson 13511283b3b8SDave Stevenson /* Enable runtime PM and turn off the device */ 13521283b3b8SDave Stevenson pm_runtime_set_active(dev); 13531283b3b8SDave Stevenson pm_runtime_enable(dev); 13541283b3b8SDave Stevenson pm_runtime_idle(dev); 13551283b3b8SDave Stevenson 13561283b3b8SDave Stevenson return 0; 13571283b3b8SDave Stevenson 1358e8a5b1dfSJacopo Mondi error_subdev_cleanup: 1359e8a5b1dfSJacopo Mondi v4l2_subdev_cleanup(&imx219->sd); 1360e8a5b1dfSJacopo Mondi 13611283b3b8SDave Stevenson error_media_entity: 13621283b3b8SDave Stevenson media_entity_cleanup(&imx219->sd.entity); 13631283b3b8SDave Stevenson 13641283b3b8SDave Stevenson error_handler_free: 13651283b3b8SDave Stevenson imx219_free_controls(imx219); 13661283b3b8SDave Stevenson 13671283b3b8SDave Stevenson error_power_off: 13681283b3b8SDave Stevenson imx219_power_off(dev); 13691283b3b8SDave Stevenson 13701283b3b8SDave Stevenson return ret; 13711283b3b8SDave Stevenson } 13721283b3b8SDave Stevenson 1373ed5c2f5fSUwe Kleine-König static void imx219_remove(struct i2c_client *client) 13741283b3b8SDave Stevenson { 13751283b3b8SDave Stevenson struct v4l2_subdev *sd = i2c_get_clientdata(client); 13761283b3b8SDave Stevenson struct imx219 *imx219 = to_imx219(sd); 13771283b3b8SDave Stevenson 13781283b3b8SDave Stevenson v4l2_async_unregister_subdev(sd); 1379e8a5b1dfSJacopo Mondi v4l2_subdev_cleanup(sd); 13801283b3b8SDave Stevenson media_entity_cleanup(&sd->entity); 13811283b3b8SDave Stevenson imx219_free_controls(imx219); 13821283b3b8SDave Stevenson 13831283b3b8SDave Stevenson pm_runtime_disable(&client->dev); 13841283b3b8SDave Stevenson if (!pm_runtime_status_suspended(&client->dev)) 13851283b3b8SDave Stevenson imx219_power_off(&client->dev); 13861283b3b8SDave Stevenson pm_runtime_set_suspended(&client->dev); 13871283b3b8SDave Stevenson } 13881283b3b8SDave Stevenson 13891283b3b8SDave Stevenson static const struct of_device_id imx219_dt_ids[] = { 13901283b3b8SDave Stevenson { .compatible = "sony,imx219" }, 13911283b3b8SDave Stevenson { /* sentinel */ } 13921283b3b8SDave Stevenson }; 13931283b3b8SDave Stevenson MODULE_DEVICE_TABLE(of, imx219_dt_ids); 13941283b3b8SDave Stevenson 13951283b3b8SDave Stevenson static const struct dev_pm_ops imx219_pm_ops = { 13961283b3b8SDave Stevenson SET_SYSTEM_SLEEP_PM_OPS(imx219_suspend, imx219_resume) 13971283b3b8SDave Stevenson SET_RUNTIME_PM_OPS(imx219_power_off, imx219_power_on, NULL) 13981283b3b8SDave Stevenson }; 13991283b3b8SDave Stevenson 14001283b3b8SDave Stevenson static struct i2c_driver imx219_i2c_driver = { 14011283b3b8SDave Stevenson .driver = { 14021283b3b8SDave Stevenson .name = "imx219", 14031283b3b8SDave Stevenson .of_match_table = imx219_dt_ids, 14041283b3b8SDave Stevenson .pm = &imx219_pm_ops, 14051283b3b8SDave Stevenson }, 1406aaeb31c0SUwe Kleine-König .probe = imx219_probe, 14071283b3b8SDave Stevenson .remove = imx219_remove, 14081283b3b8SDave Stevenson }; 14091283b3b8SDave Stevenson 14101283b3b8SDave Stevenson module_i2c_driver(imx219_i2c_driver); 14111283b3b8SDave Stevenson 14121283b3b8SDave Stevenson MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.com"); 14131283b3b8SDave Stevenson MODULE_DESCRIPTION("Sony IMX219 sensor driver"); 14141283b3b8SDave Stevenson MODULE_LICENSE("GPL v2"); 1415