1*fd7cb868SVladimir Zapolskiy // SPDX-License-Identifier: GPL-2.0 2*fd7cb868SVladimir Zapolskiy // Copyright (c) 2024-2025 Linaro Ltd 3*fd7cb868SVladimir Zapolskiy 4*fd7cb868SVladimir Zapolskiy #include <linux/clk.h> 5*fd7cb868SVladimir Zapolskiy #include <linux/delay.h> 6*fd7cb868SVladimir Zapolskiy #include <linux/gpio/consumer.h> 7*fd7cb868SVladimir Zapolskiy #include <linux/i2c.h> 8*fd7cb868SVladimir Zapolskiy #include <linux/module.h> 9*fd7cb868SVladimir Zapolskiy #include <linux/pm_runtime.h> 10*fd7cb868SVladimir Zapolskiy #include <linux/regulator/consumer.h> 11*fd7cb868SVladimir Zapolskiy #include <linux/units.h> 12*fd7cb868SVladimir Zapolskiy #include <media/v4l2-cci.h> 13*fd7cb868SVladimir Zapolskiy #include <media/v4l2-ctrls.h> 14*fd7cb868SVladimir Zapolskiy #include <media/v4l2-device.h> 15*fd7cb868SVladimir Zapolskiy #include <media/v4l2-fwnode.h> 16*fd7cb868SVladimir Zapolskiy 17*fd7cb868SVladimir Zapolskiy #define OV6211_LINK_FREQ_480MHZ (480 * HZ_PER_MHZ) 18*fd7cb868SVladimir Zapolskiy #define OV6211_MCLK_FREQ_24MHZ (24 * HZ_PER_MHZ) 19*fd7cb868SVladimir Zapolskiy 20*fd7cb868SVladimir Zapolskiy #define OV6211_REG_CHIP_ID CCI_REG16(0x300a) 21*fd7cb868SVladimir Zapolskiy #define OV6211_CHIP_ID 0x6710 22*fd7cb868SVladimir Zapolskiy 23*fd7cb868SVladimir Zapolskiy #define OV6211_REG_MODE_SELECT CCI_REG8(0x0100) 24*fd7cb868SVladimir Zapolskiy #define OV6211_MODE_STANDBY 0x00 25*fd7cb868SVladimir Zapolskiy #define OV6211_MODE_STREAMING BIT(0) 26*fd7cb868SVladimir Zapolskiy 27*fd7cb868SVladimir Zapolskiy #define OV6211_REG_SOFTWARE_RST CCI_REG8(0x0103) 28*fd7cb868SVladimir Zapolskiy #define OV6211_SOFTWARE_RST BIT(0) 29*fd7cb868SVladimir Zapolskiy 30*fd7cb868SVladimir Zapolskiy /* Exposure controls from sensor */ 31*fd7cb868SVladimir Zapolskiy #define OV6211_REG_EXPOSURE CCI_REG24(0x3500) 32*fd7cb868SVladimir Zapolskiy #define OV6211_EXPOSURE_MIN 1 33*fd7cb868SVladimir Zapolskiy #define OV6211_EXPOSURE_MAX_MARGIN 4 34*fd7cb868SVladimir Zapolskiy #define OV6211_EXPOSURE_STEP 1 35*fd7cb868SVladimir Zapolskiy #define OV6211_EXPOSURE_DEFAULT 210 36*fd7cb868SVladimir Zapolskiy 37*fd7cb868SVladimir Zapolskiy /* Analogue gain controls from sensor */ 38*fd7cb868SVladimir Zapolskiy #define OV6211_REG_ANALOGUE_GAIN CCI_REG16(0x350a) 39*fd7cb868SVladimir Zapolskiy #define OV6211_ANALOGUE_GAIN_MIN 1 40*fd7cb868SVladimir Zapolskiy #define OV6211_ANALOGUE_GAIN_MAX 0x3ff 41*fd7cb868SVladimir Zapolskiy #define OV6211_ANALOGUE_GAIN_STEP 1 42*fd7cb868SVladimir Zapolskiy #define OV6211_ANALOGUE_GAIN_DEFAULT 160 43*fd7cb868SVladimir Zapolskiy 44*fd7cb868SVladimir Zapolskiy /* Test pattern */ 45*fd7cb868SVladimir Zapolskiy #define OV6211_REG_PRE_ISP CCI_REG8(0x5e00) 46*fd7cb868SVladimir Zapolskiy #define OV6211_TEST_PATTERN_ENABLE BIT(7) 47*fd7cb868SVladimir Zapolskiy 48*fd7cb868SVladimir Zapolskiy #define to_ov6211(_sd) container_of(_sd, struct ov6211, sd) 49*fd7cb868SVladimir Zapolskiy 50*fd7cb868SVladimir Zapolskiy static const s64 ov6211_link_freq_menu[] = { 51*fd7cb868SVladimir Zapolskiy OV6211_LINK_FREQ_480MHZ, 52*fd7cb868SVladimir Zapolskiy }; 53*fd7cb868SVladimir Zapolskiy 54*fd7cb868SVladimir Zapolskiy struct ov6211_reg_list { 55*fd7cb868SVladimir Zapolskiy const struct cci_reg_sequence *regs; 56*fd7cb868SVladimir Zapolskiy unsigned int num_regs; 57*fd7cb868SVladimir Zapolskiy }; 58*fd7cb868SVladimir Zapolskiy 59*fd7cb868SVladimir Zapolskiy struct ov6211_mode { 60*fd7cb868SVladimir Zapolskiy u32 width; /* Frame width in pixels */ 61*fd7cb868SVladimir Zapolskiy u32 height; /* Frame height in pixels */ 62*fd7cb868SVladimir Zapolskiy u32 hts; /* Horizontal timing size */ 63*fd7cb868SVladimir Zapolskiy u32 vts; /* Default vertical timing size */ 64*fd7cb868SVladimir Zapolskiy u32 bpp; /* Bits per pixel */ 65*fd7cb868SVladimir Zapolskiy 66*fd7cb868SVladimir Zapolskiy const struct ov6211_reg_list reg_list; /* Sensor register setting */ 67*fd7cb868SVladimir Zapolskiy }; 68*fd7cb868SVladimir Zapolskiy 69*fd7cb868SVladimir Zapolskiy static const char * const ov6211_test_pattern_menu[] = { 70*fd7cb868SVladimir Zapolskiy "Disabled", 71*fd7cb868SVladimir Zapolskiy "Vertical Colour Bars", 72*fd7cb868SVladimir Zapolskiy }; 73*fd7cb868SVladimir Zapolskiy 74*fd7cb868SVladimir Zapolskiy static const char * const ov6211_supply_names[] = { 75*fd7cb868SVladimir Zapolskiy "avdd", /* Analog power */ 76*fd7cb868SVladimir Zapolskiy "dovdd", /* Digital I/O power */ 77*fd7cb868SVladimir Zapolskiy "dvdd", /* Digital core power */ 78*fd7cb868SVladimir Zapolskiy }; 79*fd7cb868SVladimir Zapolskiy 80*fd7cb868SVladimir Zapolskiy #define OV6211_NUM_SUPPLIES ARRAY_SIZE(ov6211_supply_names) 81*fd7cb868SVladimir Zapolskiy 82*fd7cb868SVladimir Zapolskiy struct ov6211 { 83*fd7cb868SVladimir Zapolskiy struct device *dev; 84*fd7cb868SVladimir Zapolskiy struct regmap *regmap; 85*fd7cb868SVladimir Zapolskiy struct clk *xvclk; 86*fd7cb868SVladimir Zapolskiy struct gpio_desc *reset_gpio; 87*fd7cb868SVladimir Zapolskiy struct regulator_bulk_data supplies[OV6211_NUM_SUPPLIES]; 88*fd7cb868SVladimir Zapolskiy 89*fd7cb868SVladimir Zapolskiy struct v4l2_subdev sd; 90*fd7cb868SVladimir Zapolskiy struct media_pad pad; 91*fd7cb868SVladimir Zapolskiy 92*fd7cb868SVladimir Zapolskiy struct v4l2_ctrl_handler ctrl_handler; 93*fd7cb868SVladimir Zapolskiy 94*fd7cb868SVladimir Zapolskiy /* Saved register values */ 95*fd7cb868SVladimir Zapolskiy u64 pre_isp; 96*fd7cb868SVladimir Zapolskiy }; 97*fd7cb868SVladimir Zapolskiy 98*fd7cb868SVladimir Zapolskiy static const struct cci_reg_sequence ov6211_400x400_120fps_mode[] = { 99*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3005), 0x00 }, 100*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3013), 0x12 }, 101*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3014), 0x04 }, 102*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3016), 0x10 }, 103*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3017), 0x00 }, 104*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3018), 0x00 }, 105*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x301a), 0x00 }, 106*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x301b), 0x00 }, 107*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x301c), 0x00 }, 108*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3037), 0xf0 }, 109*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3080), 0x01 }, 110*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3081), 0x00 }, 111*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3082), 0x01 }, 112*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3098), 0x04 }, 113*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3099), 0x28 }, 114*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x309a), 0x06 }, 115*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x309b), 0x04 }, 116*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x309c), 0x00 }, 117*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x309d), 0x00 }, 118*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x309e), 0x01 }, 119*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x309f), 0x00 }, 120*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x30b0), 0x08 }, 121*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x30b1), 0x02 }, 122*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x30b2), 0x00 }, 123*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x30b3), 0x28 }, 124*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x30b4), 0x02 }, 125*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x30b5), 0x00 }, 126*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3106), 0xd9 }, 127*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3503), 0x07 }, 128*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3509), 0x10 }, 129*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3600), 0xfc }, 130*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3620), 0xb7 }, 131*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3621), 0x05 }, 132*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3626), 0x31 }, 133*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3627), 0x40 }, 134*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3632), 0xa3 }, 135*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3633), 0x34 }, 136*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3634), 0x40 }, 137*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3636), 0x00 }, 138*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3660), 0x80 }, 139*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3662), 0x03 }, 140*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3664), 0xf0 }, 141*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x366a), 0x10 }, 142*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x366b), 0x06 }, 143*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3680), 0xf4 }, 144*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3681), 0x50 }, 145*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3682), 0x00 }, 146*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3708), 0x20 }, 147*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3709), 0x40 }, 148*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x370d), 0x03 }, 149*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x373b), 0x02 }, 150*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x373c), 0x08 }, 151*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3742), 0x00 }, 152*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3744), 0x16 }, 153*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3745), 0x08 }, 154*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3781), 0xfc }, 155*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3788), 0x00 }, 156*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3800), 0x00 }, 157*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3801), 0x04 }, 158*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3802), 0x00 }, 159*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3803), 0x04 }, 160*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3804), 0x01 }, 161*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3805), 0x9b }, 162*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3806), 0x01 }, 163*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3807), 0x9b }, 164*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3808), 0x01 }, /* output width */ 165*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3809), 0x90 }, 166*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x380a), 0x01 }, /* output height */ 167*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x380b), 0x90 }, 168*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x380c), 0x05 }, /* horizontal timing size */ 169*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x380d), 0xf2 }, 170*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x380e), 0x01 }, /* vertical timing size */ 171*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x380f), 0xb6 }, 172*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3810), 0x00 }, 173*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3811), 0x04 }, 174*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3812), 0x00 }, 175*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3813), 0x04 }, 176*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3814), 0x11 }, 177*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3815), 0x11 }, 178*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3820), 0x00 }, 179*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3821), 0x00 }, 180*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x382b), 0xfa }, 181*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x382f), 0x04 }, 182*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3832), 0x00 }, 183*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3833), 0x05 }, 184*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3834), 0x00 }, 185*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3835), 0x05 }, 186*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3882), 0x04 }, 187*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3883), 0x00 }, 188*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x38a4), 0x10 }, 189*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x38a5), 0x00 }, 190*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x38b1), 0x03 }, 191*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3b80), 0x00 }, 192*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3b81), 0xff }, 193*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3b82), 0x10 }, 194*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3b83), 0x00 }, 195*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3b84), 0x08 }, 196*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3b85), 0x00 }, 197*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3b86), 0x01 }, 198*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3b87), 0x00 }, 199*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3b88), 0x00 }, 200*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3b89), 0x00 }, 201*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3b8a), 0x00 }, 202*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3b8b), 0x05 }, 203*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3b8c), 0x00 }, 204*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3b8d), 0x00 }, 205*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3b8e), 0x01 }, 206*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3b8f), 0xb2 }, 207*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3b94), 0x05 }, 208*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3b95), 0xf2 }, 209*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x3b96), 0xc0 }, 210*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x4004), 0x04 }, 211*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x404e), 0x01 }, 212*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x4801), 0x0f }, 213*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x4806), 0x0f }, 214*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x4837), 0x43 }, 215*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x5a08), 0x00 }, 216*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x5a01), 0x00 }, 217*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x5a03), 0x00 }, 218*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x5a04), 0x10 }, 219*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x5a05), 0xa0 }, 220*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x5a06), 0x0c }, 221*fd7cb868SVladimir Zapolskiy { CCI_REG8(0x5a07), 0x78 }, 222*fd7cb868SVladimir Zapolskiy }; 223*fd7cb868SVladimir Zapolskiy 224*fd7cb868SVladimir Zapolskiy static const struct ov6211_mode supported_modes[] = { 225*fd7cb868SVladimir Zapolskiy { 226*fd7cb868SVladimir Zapolskiy .width = 400, 227*fd7cb868SVladimir Zapolskiy .height = 400, 228*fd7cb868SVladimir Zapolskiy .hts = 1522, 229*fd7cb868SVladimir Zapolskiy .vts = 438, 230*fd7cb868SVladimir Zapolskiy .bpp = 8, 231*fd7cb868SVladimir Zapolskiy .reg_list = { 232*fd7cb868SVladimir Zapolskiy .regs = ov6211_400x400_120fps_mode, 233*fd7cb868SVladimir Zapolskiy .num_regs = ARRAY_SIZE(ov6211_400x400_120fps_mode), 234*fd7cb868SVladimir Zapolskiy }, 235*fd7cb868SVladimir Zapolskiy }, 236*fd7cb868SVladimir Zapolskiy }; 237*fd7cb868SVladimir Zapolskiy 238*fd7cb868SVladimir Zapolskiy static int ov6211_set_test_pattern(struct ov6211 *ov6211, u32 pattern) 239*fd7cb868SVladimir Zapolskiy { 240*fd7cb868SVladimir Zapolskiy u64 val = ov6211->pre_isp; 241*fd7cb868SVladimir Zapolskiy 242*fd7cb868SVladimir Zapolskiy if (pattern) 243*fd7cb868SVladimir Zapolskiy val |= OV6211_TEST_PATTERN_ENABLE; 244*fd7cb868SVladimir Zapolskiy else 245*fd7cb868SVladimir Zapolskiy val &= ~OV6211_TEST_PATTERN_ENABLE; 246*fd7cb868SVladimir Zapolskiy 247*fd7cb868SVladimir Zapolskiy return cci_write(ov6211->regmap, OV6211_REG_PRE_ISP, val, NULL); 248*fd7cb868SVladimir Zapolskiy } 249*fd7cb868SVladimir Zapolskiy 250*fd7cb868SVladimir Zapolskiy static int ov6211_set_ctrl(struct v4l2_ctrl *ctrl) 251*fd7cb868SVladimir Zapolskiy { 252*fd7cb868SVladimir Zapolskiy struct ov6211 *ov6211 = container_of(ctrl->handler, struct ov6211, 253*fd7cb868SVladimir Zapolskiy ctrl_handler); 254*fd7cb868SVladimir Zapolskiy int ret; 255*fd7cb868SVladimir Zapolskiy 256*fd7cb868SVladimir Zapolskiy /* V4L2 controls are applied, when sensor is powered up for streaming */ 257*fd7cb868SVladimir Zapolskiy if (!pm_runtime_get_if_active(ov6211->dev)) 258*fd7cb868SVladimir Zapolskiy return 0; 259*fd7cb868SVladimir Zapolskiy 260*fd7cb868SVladimir Zapolskiy switch (ctrl->id) { 261*fd7cb868SVladimir Zapolskiy case V4L2_CID_ANALOGUE_GAIN: 262*fd7cb868SVladimir Zapolskiy ret = cci_write(ov6211->regmap, OV6211_REG_ANALOGUE_GAIN, 263*fd7cb868SVladimir Zapolskiy ctrl->val, NULL); 264*fd7cb868SVladimir Zapolskiy break; 265*fd7cb868SVladimir Zapolskiy case V4L2_CID_EXPOSURE: 266*fd7cb868SVladimir Zapolskiy ret = cci_write(ov6211->regmap, OV6211_REG_EXPOSURE, 267*fd7cb868SVladimir Zapolskiy ctrl->val << 4, NULL); 268*fd7cb868SVladimir Zapolskiy break; 269*fd7cb868SVladimir Zapolskiy case V4L2_CID_TEST_PATTERN: 270*fd7cb868SVladimir Zapolskiy ret = ov6211_set_test_pattern(ov6211, ctrl->val); 271*fd7cb868SVladimir Zapolskiy break; 272*fd7cb868SVladimir Zapolskiy default: 273*fd7cb868SVladimir Zapolskiy ret = -EINVAL; 274*fd7cb868SVladimir Zapolskiy break; 275*fd7cb868SVladimir Zapolskiy } 276*fd7cb868SVladimir Zapolskiy 277*fd7cb868SVladimir Zapolskiy pm_runtime_put(ov6211->dev); 278*fd7cb868SVladimir Zapolskiy 279*fd7cb868SVladimir Zapolskiy return ret; 280*fd7cb868SVladimir Zapolskiy } 281*fd7cb868SVladimir Zapolskiy 282*fd7cb868SVladimir Zapolskiy static const struct v4l2_ctrl_ops ov6211_ctrl_ops = { 283*fd7cb868SVladimir Zapolskiy .s_ctrl = ov6211_set_ctrl, 284*fd7cb868SVladimir Zapolskiy }; 285*fd7cb868SVladimir Zapolskiy 286*fd7cb868SVladimir Zapolskiy static int ov6211_init_controls(struct ov6211 *ov6211) 287*fd7cb868SVladimir Zapolskiy { 288*fd7cb868SVladimir Zapolskiy struct v4l2_ctrl_handler *ctrl_hdlr = &ov6211->ctrl_handler; 289*fd7cb868SVladimir Zapolskiy const struct ov6211_mode *mode = &supported_modes[0]; 290*fd7cb868SVladimir Zapolskiy struct v4l2_fwnode_device_properties props; 291*fd7cb868SVladimir Zapolskiy s64 exposure_max, pixel_rate, h_blank; 292*fd7cb868SVladimir Zapolskiy struct v4l2_ctrl *ctrl; 293*fd7cb868SVladimir Zapolskiy int ret; 294*fd7cb868SVladimir Zapolskiy 295*fd7cb868SVladimir Zapolskiy v4l2_ctrl_handler_init(ctrl_hdlr, 9); 296*fd7cb868SVladimir Zapolskiy 297*fd7cb868SVladimir Zapolskiy ctrl = v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov6211_ctrl_ops, 298*fd7cb868SVladimir Zapolskiy V4L2_CID_LINK_FREQ, 299*fd7cb868SVladimir Zapolskiy ARRAY_SIZE(ov6211_link_freq_menu) - 1, 300*fd7cb868SVladimir Zapolskiy 0, ov6211_link_freq_menu); 301*fd7cb868SVladimir Zapolskiy if (ctrl) 302*fd7cb868SVladimir Zapolskiy ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; 303*fd7cb868SVladimir Zapolskiy 304*fd7cb868SVladimir Zapolskiy pixel_rate = ov6211_link_freq_menu[0] / mode->bpp; 305*fd7cb868SVladimir Zapolskiy v4l2_ctrl_new_std(ctrl_hdlr, &ov6211_ctrl_ops, V4L2_CID_PIXEL_RATE, 306*fd7cb868SVladimir Zapolskiy 0, pixel_rate, 1, pixel_rate); 307*fd7cb868SVladimir Zapolskiy 308*fd7cb868SVladimir Zapolskiy h_blank = mode->hts - mode->width; 309*fd7cb868SVladimir Zapolskiy ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &ov6211_ctrl_ops, V4L2_CID_HBLANK, 310*fd7cb868SVladimir Zapolskiy h_blank, h_blank, 1, h_blank); 311*fd7cb868SVladimir Zapolskiy if (ctrl) 312*fd7cb868SVladimir Zapolskiy ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; 313*fd7cb868SVladimir Zapolskiy 314*fd7cb868SVladimir Zapolskiy ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &ov6211_ctrl_ops, V4L2_CID_VBLANK, 315*fd7cb868SVladimir Zapolskiy mode->vts - mode->height, 316*fd7cb868SVladimir Zapolskiy mode->vts - mode->height, 1, 317*fd7cb868SVladimir Zapolskiy mode->vts - mode->height); 318*fd7cb868SVladimir Zapolskiy if (ctrl) 319*fd7cb868SVladimir Zapolskiy ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; 320*fd7cb868SVladimir Zapolskiy 321*fd7cb868SVladimir Zapolskiy v4l2_ctrl_new_std(ctrl_hdlr, &ov6211_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, 322*fd7cb868SVladimir Zapolskiy OV6211_ANALOGUE_GAIN_MIN, OV6211_ANALOGUE_GAIN_MAX, 323*fd7cb868SVladimir Zapolskiy OV6211_ANALOGUE_GAIN_STEP, 324*fd7cb868SVladimir Zapolskiy OV6211_ANALOGUE_GAIN_DEFAULT); 325*fd7cb868SVladimir Zapolskiy 326*fd7cb868SVladimir Zapolskiy exposure_max = (mode->vts - OV6211_EXPOSURE_MAX_MARGIN); 327*fd7cb868SVladimir Zapolskiy v4l2_ctrl_new_std(ctrl_hdlr, &ov6211_ctrl_ops, 328*fd7cb868SVladimir Zapolskiy V4L2_CID_EXPOSURE, 329*fd7cb868SVladimir Zapolskiy OV6211_EXPOSURE_MIN, exposure_max, 330*fd7cb868SVladimir Zapolskiy OV6211_EXPOSURE_STEP, 331*fd7cb868SVladimir Zapolskiy OV6211_EXPOSURE_DEFAULT); 332*fd7cb868SVladimir Zapolskiy 333*fd7cb868SVladimir Zapolskiy v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov6211_ctrl_ops, 334*fd7cb868SVladimir Zapolskiy V4L2_CID_TEST_PATTERN, 335*fd7cb868SVladimir Zapolskiy ARRAY_SIZE(ov6211_test_pattern_menu) - 1, 336*fd7cb868SVladimir Zapolskiy 0, 0, ov6211_test_pattern_menu); 337*fd7cb868SVladimir Zapolskiy 338*fd7cb868SVladimir Zapolskiy if (ctrl_hdlr->error) 339*fd7cb868SVladimir Zapolskiy return ctrl_hdlr->error; 340*fd7cb868SVladimir Zapolskiy 341*fd7cb868SVladimir Zapolskiy ret = v4l2_fwnode_device_parse(ov6211->dev, &props); 342*fd7cb868SVladimir Zapolskiy if (ret) 343*fd7cb868SVladimir Zapolskiy goto error_free_hdlr; 344*fd7cb868SVladimir Zapolskiy 345*fd7cb868SVladimir Zapolskiy ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov6211_ctrl_ops, 346*fd7cb868SVladimir Zapolskiy &props); 347*fd7cb868SVladimir Zapolskiy if (ret) 348*fd7cb868SVladimir Zapolskiy goto error_free_hdlr; 349*fd7cb868SVladimir Zapolskiy 350*fd7cb868SVladimir Zapolskiy ov6211->sd.ctrl_handler = ctrl_hdlr; 351*fd7cb868SVladimir Zapolskiy 352*fd7cb868SVladimir Zapolskiy return 0; 353*fd7cb868SVladimir Zapolskiy 354*fd7cb868SVladimir Zapolskiy error_free_hdlr: 355*fd7cb868SVladimir Zapolskiy v4l2_ctrl_handler_free(ctrl_hdlr); 356*fd7cb868SVladimir Zapolskiy 357*fd7cb868SVladimir Zapolskiy return ret; 358*fd7cb868SVladimir Zapolskiy } 359*fd7cb868SVladimir Zapolskiy 360*fd7cb868SVladimir Zapolskiy static void ov6211_update_pad_format(const struct ov6211_mode *mode, 361*fd7cb868SVladimir Zapolskiy struct v4l2_mbus_framefmt *fmt) 362*fd7cb868SVladimir Zapolskiy { 363*fd7cb868SVladimir Zapolskiy fmt->code = MEDIA_BUS_FMT_Y8_1X8; 364*fd7cb868SVladimir Zapolskiy fmt->width = mode->width; 365*fd7cb868SVladimir Zapolskiy fmt->height = mode->height; 366*fd7cb868SVladimir Zapolskiy fmt->field = V4L2_FIELD_NONE; 367*fd7cb868SVladimir Zapolskiy fmt->colorspace = V4L2_COLORSPACE_RAW; 368*fd7cb868SVladimir Zapolskiy fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 369*fd7cb868SVladimir Zapolskiy fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; 370*fd7cb868SVladimir Zapolskiy fmt->xfer_func = V4L2_XFER_FUNC_NONE; 371*fd7cb868SVladimir Zapolskiy } 372*fd7cb868SVladimir Zapolskiy 373*fd7cb868SVladimir Zapolskiy static int ov6211_enable_streams(struct v4l2_subdev *sd, 374*fd7cb868SVladimir Zapolskiy struct v4l2_subdev_state *state, u32 pad, 375*fd7cb868SVladimir Zapolskiy u64 streams_mask) 376*fd7cb868SVladimir Zapolskiy { 377*fd7cb868SVladimir Zapolskiy const struct ov6211_reg_list *reg_list = &supported_modes[0].reg_list; 378*fd7cb868SVladimir Zapolskiy struct ov6211 *ov6211 = to_ov6211(sd); 379*fd7cb868SVladimir Zapolskiy int ret; 380*fd7cb868SVladimir Zapolskiy 381*fd7cb868SVladimir Zapolskiy ret = pm_runtime_resume_and_get(ov6211->dev); 382*fd7cb868SVladimir Zapolskiy if (ret) 383*fd7cb868SVladimir Zapolskiy return ret; 384*fd7cb868SVladimir Zapolskiy 385*fd7cb868SVladimir Zapolskiy /* Skip a step of explicit entering into the standby mode */ 386*fd7cb868SVladimir Zapolskiy ret = cci_write(ov6211->regmap, OV6211_REG_SOFTWARE_RST, 387*fd7cb868SVladimir Zapolskiy OV6211_SOFTWARE_RST, NULL); 388*fd7cb868SVladimir Zapolskiy if (ret) { 389*fd7cb868SVladimir Zapolskiy dev_err(ov6211->dev, "failed to software reset: %d\n", ret); 390*fd7cb868SVladimir Zapolskiy goto error; 391*fd7cb868SVladimir Zapolskiy } 392*fd7cb868SVladimir Zapolskiy 393*fd7cb868SVladimir Zapolskiy ret = cci_multi_reg_write(ov6211->regmap, reg_list->regs, 394*fd7cb868SVladimir Zapolskiy reg_list->num_regs, NULL); 395*fd7cb868SVladimir Zapolskiy if (ret) { 396*fd7cb868SVladimir Zapolskiy dev_err(ov6211->dev, "failed to set mode: %d\n", ret); 397*fd7cb868SVladimir Zapolskiy goto error; 398*fd7cb868SVladimir Zapolskiy } 399*fd7cb868SVladimir Zapolskiy 400*fd7cb868SVladimir Zapolskiy ret = __v4l2_ctrl_handler_setup(ov6211->sd.ctrl_handler); 401*fd7cb868SVladimir Zapolskiy if (ret) 402*fd7cb868SVladimir Zapolskiy goto error; 403*fd7cb868SVladimir Zapolskiy 404*fd7cb868SVladimir Zapolskiy ret = cci_write(ov6211->regmap, OV6211_REG_MODE_SELECT, 405*fd7cb868SVladimir Zapolskiy OV6211_MODE_STREAMING, NULL); 406*fd7cb868SVladimir Zapolskiy if (ret) { 407*fd7cb868SVladimir Zapolskiy dev_err(ov6211->dev, "failed to start streaming: %d\n", ret); 408*fd7cb868SVladimir Zapolskiy goto error; 409*fd7cb868SVladimir Zapolskiy } 410*fd7cb868SVladimir Zapolskiy 411*fd7cb868SVladimir Zapolskiy return 0; 412*fd7cb868SVladimir Zapolskiy 413*fd7cb868SVladimir Zapolskiy error: 414*fd7cb868SVladimir Zapolskiy pm_runtime_put_autosuspend(ov6211->dev); 415*fd7cb868SVladimir Zapolskiy 416*fd7cb868SVladimir Zapolskiy return ret; 417*fd7cb868SVladimir Zapolskiy } 418*fd7cb868SVladimir Zapolskiy 419*fd7cb868SVladimir Zapolskiy static int ov6211_disable_streams(struct v4l2_subdev *sd, 420*fd7cb868SVladimir Zapolskiy struct v4l2_subdev_state *state, u32 pad, 421*fd7cb868SVladimir Zapolskiy u64 streams_mask) 422*fd7cb868SVladimir Zapolskiy { 423*fd7cb868SVladimir Zapolskiy struct ov6211 *ov6211 = to_ov6211(sd); 424*fd7cb868SVladimir Zapolskiy int ret; 425*fd7cb868SVladimir Zapolskiy 426*fd7cb868SVladimir Zapolskiy ret = cci_write(ov6211->regmap, OV6211_REG_MODE_SELECT, 427*fd7cb868SVladimir Zapolskiy OV6211_MODE_STANDBY, NULL); 428*fd7cb868SVladimir Zapolskiy if (ret) 429*fd7cb868SVladimir Zapolskiy dev_err(ov6211->dev, "failed to stop streaming: %d\n", ret); 430*fd7cb868SVladimir Zapolskiy 431*fd7cb868SVladimir Zapolskiy pm_runtime_put_autosuspend(ov6211->dev); 432*fd7cb868SVladimir Zapolskiy 433*fd7cb868SVladimir Zapolskiy return ret; 434*fd7cb868SVladimir Zapolskiy } 435*fd7cb868SVladimir Zapolskiy 436*fd7cb868SVladimir Zapolskiy static int ov6211_set_pad_format(struct v4l2_subdev *sd, 437*fd7cb868SVladimir Zapolskiy struct v4l2_subdev_state *state, 438*fd7cb868SVladimir Zapolskiy struct v4l2_subdev_format *fmt) 439*fd7cb868SVladimir Zapolskiy { 440*fd7cb868SVladimir Zapolskiy struct v4l2_mbus_framefmt *format; 441*fd7cb868SVladimir Zapolskiy const struct ov6211_mode *mode; 442*fd7cb868SVladimir Zapolskiy 443*fd7cb868SVladimir Zapolskiy format = v4l2_subdev_state_get_format(state, 0); 444*fd7cb868SVladimir Zapolskiy 445*fd7cb868SVladimir Zapolskiy mode = v4l2_find_nearest_size(supported_modes, 446*fd7cb868SVladimir Zapolskiy ARRAY_SIZE(supported_modes), 447*fd7cb868SVladimir Zapolskiy width, height, 448*fd7cb868SVladimir Zapolskiy fmt->format.width, 449*fd7cb868SVladimir Zapolskiy fmt->format.height); 450*fd7cb868SVladimir Zapolskiy 451*fd7cb868SVladimir Zapolskiy ov6211_update_pad_format(mode, &fmt->format); 452*fd7cb868SVladimir Zapolskiy *format = fmt->format; 453*fd7cb868SVladimir Zapolskiy 454*fd7cb868SVladimir Zapolskiy return 0; 455*fd7cb868SVladimir Zapolskiy } 456*fd7cb868SVladimir Zapolskiy 457*fd7cb868SVladimir Zapolskiy static int ov6211_enum_mbus_code(struct v4l2_subdev *sd, 458*fd7cb868SVladimir Zapolskiy struct v4l2_subdev_state *sd_state, 459*fd7cb868SVladimir Zapolskiy struct v4l2_subdev_mbus_code_enum *code) 460*fd7cb868SVladimir Zapolskiy { 461*fd7cb868SVladimir Zapolskiy if (code->index > 0) 462*fd7cb868SVladimir Zapolskiy return -EINVAL; 463*fd7cb868SVladimir Zapolskiy 464*fd7cb868SVladimir Zapolskiy code->code = MEDIA_BUS_FMT_Y8_1X8; 465*fd7cb868SVladimir Zapolskiy 466*fd7cb868SVladimir Zapolskiy return 0; 467*fd7cb868SVladimir Zapolskiy } 468*fd7cb868SVladimir Zapolskiy 469*fd7cb868SVladimir Zapolskiy static int ov6211_enum_frame_size(struct v4l2_subdev *sd, 470*fd7cb868SVladimir Zapolskiy struct v4l2_subdev_state *sd_state, 471*fd7cb868SVladimir Zapolskiy struct v4l2_subdev_frame_size_enum *fse) 472*fd7cb868SVladimir Zapolskiy { 473*fd7cb868SVladimir Zapolskiy if (fse->index >= ARRAY_SIZE(supported_modes)) 474*fd7cb868SVladimir Zapolskiy return -EINVAL; 475*fd7cb868SVladimir Zapolskiy 476*fd7cb868SVladimir Zapolskiy if (fse->code != MEDIA_BUS_FMT_Y8_1X8) 477*fd7cb868SVladimir Zapolskiy return -EINVAL; 478*fd7cb868SVladimir Zapolskiy 479*fd7cb868SVladimir Zapolskiy fse->min_width = supported_modes[fse->index].width; 480*fd7cb868SVladimir Zapolskiy fse->max_width = fse->min_width; 481*fd7cb868SVladimir Zapolskiy fse->min_height = supported_modes[fse->index].height; 482*fd7cb868SVladimir Zapolskiy fse->max_height = fse->min_height; 483*fd7cb868SVladimir Zapolskiy 484*fd7cb868SVladimir Zapolskiy return 0; 485*fd7cb868SVladimir Zapolskiy } 486*fd7cb868SVladimir Zapolskiy 487*fd7cb868SVladimir Zapolskiy static int ov6211_init_state(struct v4l2_subdev *sd, 488*fd7cb868SVladimir Zapolskiy struct v4l2_subdev_state *state) 489*fd7cb868SVladimir Zapolskiy { 490*fd7cb868SVladimir Zapolskiy struct v4l2_subdev_format fmt = { 491*fd7cb868SVladimir Zapolskiy .which = V4L2_SUBDEV_FORMAT_TRY, 492*fd7cb868SVladimir Zapolskiy .pad = 0, 493*fd7cb868SVladimir Zapolskiy .format = { 494*fd7cb868SVladimir Zapolskiy .code = MEDIA_BUS_FMT_Y8_1X8, 495*fd7cb868SVladimir Zapolskiy .width = supported_modes[0].width, 496*fd7cb868SVladimir Zapolskiy .height = supported_modes[0].height, 497*fd7cb868SVladimir Zapolskiy }, 498*fd7cb868SVladimir Zapolskiy }; 499*fd7cb868SVladimir Zapolskiy 500*fd7cb868SVladimir Zapolskiy ov6211_set_pad_format(sd, state, &fmt); 501*fd7cb868SVladimir Zapolskiy 502*fd7cb868SVladimir Zapolskiy return 0; 503*fd7cb868SVladimir Zapolskiy } 504*fd7cb868SVladimir Zapolskiy 505*fd7cb868SVladimir Zapolskiy static const struct v4l2_subdev_video_ops ov6211_video_ops = { 506*fd7cb868SVladimir Zapolskiy .s_stream = v4l2_subdev_s_stream_helper, 507*fd7cb868SVladimir Zapolskiy }; 508*fd7cb868SVladimir Zapolskiy 509*fd7cb868SVladimir Zapolskiy static const struct v4l2_subdev_pad_ops ov6211_pad_ops = { 510*fd7cb868SVladimir Zapolskiy .set_fmt = ov6211_set_pad_format, 511*fd7cb868SVladimir Zapolskiy .get_fmt = v4l2_subdev_get_fmt, 512*fd7cb868SVladimir Zapolskiy .enum_mbus_code = ov6211_enum_mbus_code, 513*fd7cb868SVladimir Zapolskiy .enum_frame_size = ov6211_enum_frame_size, 514*fd7cb868SVladimir Zapolskiy .enable_streams = ov6211_enable_streams, 515*fd7cb868SVladimir Zapolskiy .disable_streams = ov6211_disable_streams, 516*fd7cb868SVladimir Zapolskiy }; 517*fd7cb868SVladimir Zapolskiy 518*fd7cb868SVladimir Zapolskiy static const struct v4l2_subdev_ops ov6211_subdev_ops = { 519*fd7cb868SVladimir Zapolskiy .video = &ov6211_video_ops, 520*fd7cb868SVladimir Zapolskiy .pad = &ov6211_pad_ops, 521*fd7cb868SVladimir Zapolskiy }; 522*fd7cb868SVladimir Zapolskiy 523*fd7cb868SVladimir Zapolskiy static const struct v4l2_subdev_internal_ops ov6211_internal_ops = { 524*fd7cb868SVladimir Zapolskiy .init_state = ov6211_init_state, 525*fd7cb868SVladimir Zapolskiy }; 526*fd7cb868SVladimir Zapolskiy 527*fd7cb868SVladimir Zapolskiy static const struct media_entity_operations ov6211_subdev_entity_ops = { 528*fd7cb868SVladimir Zapolskiy .link_validate = v4l2_subdev_link_validate, 529*fd7cb868SVladimir Zapolskiy }; 530*fd7cb868SVladimir Zapolskiy 531*fd7cb868SVladimir Zapolskiy static int ov6211_identify_sensor(struct ov6211 *ov6211) 532*fd7cb868SVladimir Zapolskiy { 533*fd7cb868SVladimir Zapolskiy u64 val; 534*fd7cb868SVladimir Zapolskiy int ret; 535*fd7cb868SVladimir Zapolskiy 536*fd7cb868SVladimir Zapolskiy ret = cci_read(ov6211->regmap, OV6211_REG_CHIP_ID, &val, NULL); 537*fd7cb868SVladimir Zapolskiy if (ret) { 538*fd7cb868SVladimir Zapolskiy dev_err(ov6211->dev, "failed to read chip id: %d\n", ret); 539*fd7cb868SVladimir Zapolskiy return ret; 540*fd7cb868SVladimir Zapolskiy } 541*fd7cb868SVladimir Zapolskiy 542*fd7cb868SVladimir Zapolskiy if (val != OV6211_CHIP_ID) { 543*fd7cb868SVladimir Zapolskiy dev_err(ov6211->dev, "chip id mismatch: %x!=%llx\n", 544*fd7cb868SVladimir Zapolskiy OV6211_CHIP_ID, val); 545*fd7cb868SVladimir Zapolskiy return -ENODEV; 546*fd7cb868SVladimir Zapolskiy } 547*fd7cb868SVladimir Zapolskiy 548*fd7cb868SVladimir Zapolskiy ret = cci_read(ov6211->regmap, OV6211_REG_PRE_ISP, 549*fd7cb868SVladimir Zapolskiy &ov6211->pre_isp, NULL); 550*fd7cb868SVladimir Zapolskiy if (ret) 551*fd7cb868SVladimir Zapolskiy dev_err(ov6211->dev, "failed to read pre_isp: %d\n", ret); 552*fd7cb868SVladimir Zapolskiy 553*fd7cb868SVladimir Zapolskiy return ret; 554*fd7cb868SVladimir Zapolskiy } 555*fd7cb868SVladimir Zapolskiy 556*fd7cb868SVladimir Zapolskiy static int ov6211_check_hwcfg(struct ov6211 *ov6211) 557*fd7cb868SVladimir Zapolskiy { 558*fd7cb868SVladimir Zapolskiy struct fwnode_handle *fwnode = dev_fwnode(ov6211->dev), *ep; 559*fd7cb868SVladimir Zapolskiy struct v4l2_fwnode_endpoint bus_cfg = { 560*fd7cb868SVladimir Zapolskiy .bus_type = V4L2_MBUS_CSI2_DPHY, 561*fd7cb868SVladimir Zapolskiy }; 562*fd7cb868SVladimir Zapolskiy unsigned long freq_bitmap; 563*fd7cb868SVladimir Zapolskiy int ret; 564*fd7cb868SVladimir Zapolskiy 565*fd7cb868SVladimir Zapolskiy if (!fwnode) 566*fd7cb868SVladimir Zapolskiy return -ENODEV; 567*fd7cb868SVladimir Zapolskiy 568*fd7cb868SVladimir Zapolskiy ep = fwnode_graph_get_next_endpoint(fwnode, NULL); 569*fd7cb868SVladimir Zapolskiy if (!ep) 570*fd7cb868SVladimir Zapolskiy return -EINVAL; 571*fd7cb868SVladimir Zapolskiy 572*fd7cb868SVladimir Zapolskiy ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); 573*fd7cb868SVladimir Zapolskiy fwnode_handle_put(ep); 574*fd7cb868SVladimir Zapolskiy if (ret) 575*fd7cb868SVladimir Zapolskiy return ret; 576*fd7cb868SVladimir Zapolskiy 577*fd7cb868SVladimir Zapolskiy ret = v4l2_link_freq_to_bitmap(ov6211->dev, bus_cfg.link_frequencies, 578*fd7cb868SVladimir Zapolskiy bus_cfg.nr_of_link_frequencies, 579*fd7cb868SVladimir Zapolskiy ov6211_link_freq_menu, 580*fd7cb868SVladimir Zapolskiy ARRAY_SIZE(ov6211_link_freq_menu), 581*fd7cb868SVladimir Zapolskiy &freq_bitmap); 582*fd7cb868SVladimir Zapolskiy 583*fd7cb868SVladimir Zapolskiy v4l2_fwnode_endpoint_free(&bus_cfg); 584*fd7cb868SVladimir Zapolskiy 585*fd7cb868SVladimir Zapolskiy return ret; 586*fd7cb868SVladimir Zapolskiy } 587*fd7cb868SVladimir Zapolskiy 588*fd7cb868SVladimir Zapolskiy static int ov6211_power_on(struct device *dev) 589*fd7cb868SVladimir Zapolskiy { 590*fd7cb868SVladimir Zapolskiy struct v4l2_subdev *sd = dev_get_drvdata(dev); 591*fd7cb868SVladimir Zapolskiy struct ov6211 *ov6211 = to_ov6211(sd); 592*fd7cb868SVladimir Zapolskiy int ret; 593*fd7cb868SVladimir Zapolskiy 594*fd7cb868SVladimir Zapolskiy ret = regulator_bulk_enable(OV6211_NUM_SUPPLIES, ov6211->supplies); 595*fd7cb868SVladimir Zapolskiy if (ret) 596*fd7cb868SVladimir Zapolskiy return ret; 597*fd7cb868SVladimir Zapolskiy 598*fd7cb868SVladimir Zapolskiy gpiod_set_value_cansleep(ov6211->reset_gpio, 0); 599*fd7cb868SVladimir Zapolskiy usleep_range(10 * USEC_PER_MSEC, 15 * USEC_PER_MSEC); 600*fd7cb868SVladimir Zapolskiy 601*fd7cb868SVladimir Zapolskiy ret = clk_prepare_enable(ov6211->xvclk); 602*fd7cb868SVladimir Zapolskiy if (ret) 603*fd7cb868SVladimir Zapolskiy goto reset_gpio; 604*fd7cb868SVladimir Zapolskiy 605*fd7cb868SVladimir Zapolskiy return 0; 606*fd7cb868SVladimir Zapolskiy 607*fd7cb868SVladimir Zapolskiy reset_gpio: 608*fd7cb868SVladimir Zapolskiy gpiod_set_value_cansleep(ov6211->reset_gpio, 1); 609*fd7cb868SVladimir Zapolskiy 610*fd7cb868SVladimir Zapolskiy regulator_bulk_disable(OV6211_NUM_SUPPLIES, ov6211->supplies); 611*fd7cb868SVladimir Zapolskiy 612*fd7cb868SVladimir Zapolskiy return ret; 613*fd7cb868SVladimir Zapolskiy } 614*fd7cb868SVladimir Zapolskiy 615*fd7cb868SVladimir Zapolskiy static int ov6211_power_off(struct device *dev) 616*fd7cb868SVladimir Zapolskiy { 617*fd7cb868SVladimir Zapolskiy struct v4l2_subdev *sd = dev_get_drvdata(dev); 618*fd7cb868SVladimir Zapolskiy struct ov6211 *ov6211 = to_ov6211(sd); 619*fd7cb868SVladimir Zapolskiy 620*fd7cb868SVladimir Zapolskiy clk_disable_unprepare(ov6211->xvclk); 621*fd7cb868SVladimir Zapolskiy 622*fd7cb868SVladimir Zapolskiy gpiod_set_value_cansleep(ov6211->reset_gpio, 1); 623*fd7cb868SVladimir Zapolskiy 624*fd7cb868SVladimir Zapolskiy regulator_bulk_disable(OV6211_NUM_SUPPLIES, ov6211->supplies); 625*fd7cb868SVladimir Zapolskiy 626*fd7cb868SVladimir Zapolskiy return 0; 627*fd7cb868SVladimir Zapolskiy } 628*fd7cb868SVladimir Zapolskiy 629*fd7cb868SVladimir Zapolskiy static int ov6211_probe(struct i2c_client *client) 630*fd7cb868SVladimir Zapolskiy { 631*fd7cb868SVladimir Zapolskiy struct ov6211 *ov6211; 632*fd7cb868SVladimir Zapolskiy unsigned long freq; 633*fd7cb868SVladimir Zapolskiy unsigned int i; 634*fd7cb868SVladimir Zapolskiy int ret; 635*fd7cb868SVladimir Zapolskiy 636*fd7cb868SVladimir Zapolskiy ov6211 = devm_kzalloc(&client->dev, sizeof(*ov6211), GFP_KERNEL); 637*fd7cb868SVladimir Zapolskiy if (!ov6211) 638*fd7cb868SVladimir Zapolskiy return -ENOMEM; 639*fd7cb868SVladimir Zapolskiy 640*fd7cb868SVladimir Zapolskiy ov6211->dev = &client->dev; 641*fd7cb868SVladimir Zapolskiy 642*fd7cb868SVladimir Zapolskiy v4l2_i2c_subdev_init(&ov6211->sd, client, &ov6211_subdev_ops); 643*fd7cb868SVladimir Zapolskiy 644*fd7cb868SVladimir Zapolskiy ov6211->regmap = devm_cci_regmap_init_i2c(client, 16); 645*fd7cb868SVladimir Zapolskiy if (IS_ERR(ov6211->regmap)) 646*fd7cb868SVladimir Zapolskiy return dev_err_probe(ov6211->dev, PTR_ERR(ov6211->regmap), 647*fd7cb868SVladimir Zapolskiy "failed to init CCI\n"); 648*fd7cb868SVladimir Zapolskiy 649*fd7cb868SVladimir Zapolskiy ov6211->xvclk = devm_v4l2_sensor_clk_get(ov6211->dev, NULL); 650*fd7cb868SVladimir Zapolskiy if (IS_ERR(ov6211->xvclk)) 651*fd7cb868SVladimir Zapolskiy return dev_err_probe(ov6211->dev, PTR_ERR(ov6211->xvclk), 652*fd7cb868SVladimir Zapolskiy "failed to get XVCLK clock\n"); 653*fd7cb868SVladimir Zapolskiy 654*fd7cb868SVladimir Zapolskiy freq = clk_get_rate(ov6211->xvclk); 655*fd7cb868SVladimir Zapolskiy if (freq && freq != OV6211_MCLK_FREQ_24MHZ) 656*fd7cb868SVladimir Zapolskiy return dev_err_probe(ov6211->dev, -EINVAL, 657*fd7cb868SVladimir Zapolskiy "XVCLK clock frequency %lu is not supported\n", 658*fd7cb868SVladimir Zapolskiy freq); 659*fd7cb868SVladimir Zapolskiy 660*fd7cb868SVladimir Zapolskiy ret = ov6211_check_hwcfg(ov6211); 661*fd7cb868SVladimir Zapolskiy if (ret) 662*fd7cb868SVladimir Zapolskiy return dev_err_probe(ov6211->dev, ret, 663*fd7cb868SVladimir Zapolskiy "failed to check HW configuration\n"); 664*fd7cb868SVladimir Zapolskiy 665*fd7cb868SVladimir Zapolskiy ov6211->reset_gpio = devm_gpiod_get_optional(ov6211->dev, "reset", 666*fd7cb868SVladimir Zapolskiy GPIOD_OUT_HIGH); 667*fd7cb868SVladimir Zapolskiy if (IS_ERR(ov6211->reset_gpio)) 668*fd7cb868SVladimir Zapolskiy return dev_err_probe(ov6211->dev, PTR_ERR(ov6211->reset_gpio), 669*fd7cb868SVladimir Zapolskiy "cannot get reset GPIO\n"); 670*fd7cb868SVladimir Zapolskiy 671*fd7cb868SVladimir Zapolskiy for (i = 0; i < OV6211_NUM_SUPPLIES; i++) 672*fd7cb868SVladimir Zapolskiy ov6211->supplies[i].supply = ov6211_supply_names[i]; 673*fd7cb868SVladimir Zapolskiy 674*fd7cb868SVladimir Zapolskiy ret = devm_regulator_bulk_get(ov6211->dev, OV6211_NUM_SUPPLIES, 675*fd7cb868SVladimir Zapolskiy ov6211->supplies); 676*fd7cb868SVladimir Zapolskiy if (ret) 677*fd7cb868SVladimir Zapolskiy return dev_err_probe(ov6211->dev, ret, 678*fd7cb868SVladimir Zapolskiy "failed to get supply regulators\n"); 679*fd7cb868SVladimir Zapolskiy 680*fd7cb868SVladimir Zapolskiy /* The sensor must be powered on to read the CHIP_ID register */ 681*fd7cb868SVladimir Zapolskiy ret = ov6211_power_on(ov6211->dev); 682*fd7cb868SVladimir Zapolskiy if (ret) 683*fd7cb868SVladimir Zapolskiy return ret; 684*fd7cb868SVladimir Zapolskiy 685*fd7cb868SVladimir Zapolskiy ret = ov6211_identify_sensor(ov6211); 686*fd7cb868SVladimir Zapolskiy if (ret) { 687*fd7cb868SVladimir Zapolskiy dev_err_probe(ov6211->dev, ret, "failed to find sensor\n"); 688*fd7cb868SVladimir Zapolskiy goto power_off; 689*fd7cb868SVladimir Zapolskiy } 690*fd7cb868SVladimir Zapolskiy 691*fd7cb868SVladimir Zapolskiy ret = ov6211_init_controls(ov6211); 692*fd7cb868SVladimir Zapolskiy if (ret) { 693*fd7cb868SVladimir Zapolskiy dev_err_probe(ov6211->dev, ret, "failed to init controls\n"); 694*fd7cb868SVladimir Zapolskiy goto power_off; 695*fd7cb868SVladimir Zapolskiy } 696*fd7cb868SVladimir Zapolskiy 697*fd7cb868SVladimir Zapolskiy ov6211->sd.state_lock = ov6211->ctrl_handler.lock; 698*fd7cb868SVladimir Zapolskiy ov6211->sd.internal_ops = &ov6211_internal_ops; 699*fd7cb868SVladimir Zapolskiy ov6211->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 700*fd7cb868SVladimir Zapolskiy ov6211->sd.entity.ops = &ov6211_subdev_entity_ops; 701*fd7cb868SVladimir Zapolskiy ov6211->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; 702*fd7cb868SVladimir Zapolskiy ov6211->pad.flags = MEDIA_PAD_FL_SOURCE; 703*fd7cb868SVladimir Zapolskiy 704*fd7cb868SVladimir Zapolskiy ret = media_entity_pads_init(&ov6211->sd.entity, 1, &ov6211->pad); 705*fd7cb868SVladimir Zapolskiy if (ret) { 706*fd7cb868SVladimir Zapolskiy dev_err_probe(ov6211->dev, ret, 707*fd7cb868SVladimir Zapolskiy "failed to init media entity pads\n"); 708*fd7cb868SVladimir Zapolskiy goto v4l2_ctrl_handler_free; 709*fd7cb868SVladimir Zapolskiy } 710*fd7cb868SVladimir Zapolskiy 711*fd7cb868SVladimir Zapolskiy ret = v4l2_subdev_init_finalize(&ov6211->sd); 712*fd7cb868SVladimir Zapolskiy if (ret < 0) { 713*fd7cb868SVladimir Zapolskiy dev_err_probe(ov6211->dev, ret, 714*fd7cb868SVladimir Zapolskiy "failed to init media entity pads\n"); 715*fd7cb868SVladimir Zapolskiy goto media_entity_cleanup; 716*fd7cb868SVladimir Zapolskiy } 717*fd7cb868SVladimir Zapolskiy 718*fd7cb868SVladimir Zapolskiy pm_runtime_set_active(ov6211->dev); 719*fd7cb868SVladimir Zapolskiy pm_runtime_enable(ov6211->dev); 720*fd7cb868SVladimir Zapolskiy 721*fd7cb868SVladimir Zapolskiy ret = v4l2_async_register_subdev_sensor(&ov6211->sd); 722*fd7cb868SVladimir Zapolskiy if (ret < 0) { 723*fd7cb868SVladimir Zapolskiy dev_err_probe(ov6211->dev, ret, 724*fd7cb868SVladimir Zapolskiy "failed to register V4L2 subdev\n"); 725*fd7cb868SVladimir Zapolskiy goto subdev_cleanup; 726*fd7cb868SVladimir Zapolskiy } 727*fd7cb868SVladimir Zapolskiy 728*fd7cb868SVladimir Zapolskiy /* Enable runtime PM and turn off the device */ 729*fd7cb868SVladimir Zapolskiy pm_runtime_idle(ov6211->dev); 730*fd7cb868SVladimir Zapolskiy pm_runtime_set_autosuspend_delay(ov6211->dev, 1000); 731*fd7cb868SVladimir Zapolskiy pm_runtime_use_autosuspend(ov6211->dev); 732*fd7cb868SVladimir Zapolskiy 733*fd7cb868SVladimir Zapolskiy return 0; 734*fd7cb868SVladimir Zapolskiy 735*fd7cb868SVladimir Zapolskiy subdev_cleanup: 736*fd7cb868SVladimir Zapolskiy v4l2_subdev_cleanup(&ov6211->sd); 737*fd7cb868SVladimir Zapolskiy pm_runtime_disable(ov6211->dev); 738*fd7cb868SVladimir Zapolskiy pm_runtime_set_suspended(ov6211->dev); 739*fd7cb868SVladimir Zapolskiy 740*fd7cb868SVladimir Zapolskiy media_entity_cleanup: 741*fd7cb868SVladimir Zapolskiy media_entity_cleanup(&ov6211->sd.entity); 742*fd7cb868SVladimir Zapolskiy 743*fd7cb868SVladimir Zapolskiy v4l2_ctrl_handler_free: 744*fd7cb868SVladimir Zapolskiy v4l2_ctrl_handler_free(ov6211->sd.ctrl_handler); 745*fd7cb868SVladimir Zapolskiy 746*fd7cb868SVladimir Zapolskiy power_off: 747*fd7cb868SVladimir Zapolskiy ov6211_power_off(ov6211->dev); 748*fd7cb868SVladimir Zapolskiy 749*fd7cb868SVladimir Zapolskiy return ret; 750*fd7cb868SVladimir Zapolskiy } 751*fd7cb868SVladimir Zapolskiy 752*fd7cb868SVladimir Zapolskiy static void ov6211_remove(struct i2c_client *client) 753*fd7cb868SVladimir Zapolskiy { 754*fd7cb868SVladimir Zapolskiy struct v4l2_subdev *sd = i2c_get_clientdata(client); 755*fd7cb868SVladimir Zapolskiy struct ov6211 *ov6211 = to_ov6211(sd); 756*fd7cb868SVladimir Zapolskiy 757*fd7cb868SVladimir Zapolskiy v4l2_async_unregister_subdev(sd); 758*fd7cb868SVladimir Zapolskiy v4l2_subdev_cleanup(sd); 759*fd7cb868SVladimir Zapolskiy media_entity_cleanup(&sd->entity); 760*fd7cb868SVladimir Zapolskiy v4l2_ctrl_handler_free(sd->ctrl_handler); 761*fd7cb868SVladimir Zapolskiy pm_runtime_disable(ov6211->dev); 762*fd7cb868SVladimir Zapolskiy 763*fd7cb868SVladimir Zapolskiy if (!pm_runtime_status_suspended(ov6211->dev)) { 764*fd7cb868SVladimir Zapolskiy ov6211_power_off(ov6211->dev); 765*fd7cb868SVladimir Zapolskiy pm_runtime_set_suspended(ov6211->dev); 766*fd7cb868SVladimir Zapolskiy } 767*fd7cb868SVladimir Zapolskiy } 768*fd7cb868SVladimir Zapolskiy 769*fd7cb868SVladimir Zapolskiy static const struct dev_pm_ops ov6211_pm_ops = { 770*fd7cb868SVladimir Zapolskiy SET_RUNTIME_PM_OPS(ov6211_power_off, ov6211_power_on, NULL) 771*fd7cb868SVladimir Zapolskiy }; 772*fd7cb868SVladimir Zapolskiy 773*fd7cb868SVladimir Zapolskiy static const struct of_device_id ov6211_of_match[] = { 774*fd7cb868SVladimir Zapolskiy { .compatible = "ovti,ov6211" }, 775*fd7cb868SVladimir Zapolskiy { /* sentinel */ } 776*fd7cb868SVladimir Zapolskiy }; 777*fd7cb868SVladimir Zapolskiy MODULE_DEVICE_TABLE(of, ov6211_of_match); 778*fd7cb868SVladimir Zapolskiy 779*fd7cb868SVladimir Zapolskiy static struct i2c_driver ov6211_i2c_driver = { 780*fd7cb868SVladimir Zapolskiy .driver = { 781*fd7cb868SVladimir Zapolskiy .name = "ov6211", 782*fd7cb868SVladimir Zapolskiy .pm = &ov6211_pm_ops, 783*fd7cb868SVladimir Zapolskiy .of_match_table = ov6211_of_match, 784*fd7cb868SVladimir Zapolskiy }, 785*fd7cb868SVladimir Zapolskiy .probe = ov6211_probe, 786*fd7cb868SVladimir Zapolskiy .remove = ov6211_remove, 787*fd7cb868SVladimir Zapolskiy }; 788*fd7cb868SVladimir Zapolskiy 789*fd7cb868SVladimir Zapolskiy module_i2c_driver(ov6211_i2c_driver); 790*fd7cb868SVladimir Zapolskiy 791*fd7cb868SVladimir Zapolskiy MODULE_AUTHOR("Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>"); 792*fd7cb868SVladimir Zapolskiy MODULE_DESCRIPTION("OmniVision OV6211 sensor driver"); 793*fd7cb868SVladimir Zapolskiy MODULE_LICENSE("GPL"); 794