Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

media: i2c: Add OmniVision OV6211 image sensor driver

OmniVision OV6211 is a monochrome image sensor, which produces frames in
8/10-bit raw output format and supports 400x400, 200x200 and 100x100
output image resolution modes.

Signed-off-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>

authored by

Vladimir Zapolskiy and committed by
Mauro Carvalho Chehab
fd7cb868 9364790e

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