| xj | b04a402 | 2021-11-25 15:01:52 +0800 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 | 
|  | 2 | // Copyright (c) 2017 Microchip Corporation. | 
|  | 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/regmap.h> | 
|  | 11 | #include <media/v4l2-ctrls.h> | 
|  | 12 | #include <media/v4l2-event.h> | 
|  | 13 | #include <media/v4l2-image-sizes.h> | 
|  | 14 | #include <media/v4l2-subdev.h> | 
|  | 15 |  | 
|  | 16 | #define REG_OUTSIZE_LSB 0x34 | 
|  | 17 |  | 
|  | 18 | /* OV7740 register tables */ | 
|  | 19 | #define REG_GAIN	0x00	/* Gain lower 8 bits (rest in vref) */ | 
|  | 20 | #define REG_BGAIN	0x01	/* blue gain */ | 
|  | 21 | #define REG_RGAIN	0x02	/* red gain */ | 
|  | 22 | #define REG_GGAIN	0x03	/* green gain */ | 
|  | 23 | #define REG_REG04	0x04	/* analog setting, dont change*/ | 
|  | 24 | #define REG_BAVG	0x05	/* b channel average */ | 
|  | 25 | #define REG_GAVG	0x06	/* g channel average */ | 
|  | 26 | #define REG_RAVG	0x07	/* r channel average */ | 
|  | 27 |  | 
|  | 28 | #define REG_REG0C	0x0C	/* filp enable */ | 
|  | 29 | #define REG0C_IMG_FLIP		0x80 | 
|  | 30 | #define REG0C_IMG_MIRROR	0x40 | 
|  | 31 |  | 
|  | 32 | #define REG_REG0E	0x0E	/* blc line */ | 
|  | 33 | #define REG_HAEC	0x0F	/* auto exposure cntrl */ | 
|  | 34 | #define REG_AEC		0x10	/* auto exposure cntrl */ | 
|  | 35 |  | 
|  | 36 | #define REG_CLK		0x11	/* Clock control */ | 
|  | 37 | #define REG_REG55	0x55	/* Clock PLL DIV/PreDiv */ | 
|  | 38 |  | 
|  | 39 | #define REG_REG12	0x12 | 
|  | 40 |  | 
|  | 41 | #define REG_REG13	0x13	/* auto/manual AGC, AEC, Write Balance*/ | 
|  | 42 | #define REG13_AEC_EN	0x01 | 
|  | 43 | #define REG13_AGC_EN	0x04 | 
|  | 44 |  | 
|  | 45 | #define REG_REG14	0x14 | 
|  | 46 | #define REG_CTRL15	0x15 | 
|  | 47 | #define REG15_GAIN_MSB	0x03 | 
|  | 48 |  | 
|  | 49 | #define REG_REG16	0x16 | 
|  | 50 |  | 
|  | 51 | #define REG_MIDH	0x1C	/* manufacture id byte */ | 
|  | 52 | #define REG_MIDL	0x1D	/* manufacture id byre */ | 
|  | 53 | #define REG_PIDH	0x0A	/* Product ID MSB */ | 
|  | 54 | #define REG_PIDL	0x0B	/* Product ID LSB */ | 
|  | 55 |  | 
|  | 56 | #define REG_84		0x84	/* lots of stuff */ | 
|  | 57 | #define REG_REG38	0x38	/* sub-addr */ | 
|  | 58 |  | 
|  | 59 | #define REG_AHSTART	0x17	/* Horiz start high bits */ | 
|  | 60 | #define REG_AHSIZE	0x18 | 
|  | 61 | #define REG_AVSTART	0x19	/* Vert start high bits */ | 
|  | 62 | #define REG_AVSIZE	0x1A | 
|  | 63 | #define REG_PSHFT	0x1b	/* Pixel delay after HREF */ | 
|  | 64 |  | 
|  | 65 | #define REG_HOUTSIZE	0x31 | 
|  | 66 | #define REG_VOUTSIZE	0x32 | 
|  | 67 | #define REG_HVSIZEOFF	0x33 | 
|  | 68 | #define REG_REG34	0x34	/* DSP output size H/V LSB*/ | 
|  | 69 |  | 
|  | 70 | #define REG_ISP_CTRL00	0x80 | 
|  | 71 | #define ISPCTRL00_AWB_EN	0x10 | 
|  | 72 | #define ISPCTRL00_AWB_GAIN_EN	0x04 | 
|  | 73 |  | 
|  | 74 | #define	REG_YGAIN	0xE2	/* ygain for contrast control */ | 
|  | 75 |  | 
|  | 76 | #define	REG_YBRIGHT	  0xE3 | 
|  | 77 | #define	REG_SGNSET	  0xE4 | 
|  | 78 | #define	SGNSET_YBRIGHT_MASK	  0x08 | 
|  | 79 |  | 
|  | 80 | #define REG_USAT	0xDD | 
|  | 81 | #define REG_VSAT	0xDE | 
|  | 82 |  | 
|  | 83 |  | 
|  | 84 | struct ov7740 { | 
|  | 85 | struct v4l2_subdev subdev; | 
|  | 86 | #if defined(CONFIG_MEDIA_CONTROLLER) | 
|  | 87 | struct media_pad pad; | 
|  | 88 | #endif | 
|  | 89 | struct v4l2_mbus_framefmt format; | 
|  | 90 | const struct ov7740_pixfmt *fmt;  /* Current format */ | 
|  | 91 | const struct ov7740_framesize *frmsize; | 
|  | 92 | struct regmap *regmap; | 
|  | 93 | struct clk *xvclk; | 
|  | 94 | struct v4l2_ctrl_handler ctrl_handler; | 
|  | 95 | struct { | 
|  | 96 | /* gain cluster */ | 
|  | 97 | struct v4l2_ctrl *auto_gain; | 
|  | 98 | struct v4l2_ctrl *gain; | 
|  | 99 | }; | 
|  | 100 | struct { | 
|  | 101 | struct v4l2_ctrl *auto_wb; | 
|  | 102 | struct v4l2_ctrl *blue_balance; | 
|  | 103 | struct v4l2_ctrl *red_balance; | 
|  | 104 | }; | 
|  | 105 | struct { | 
|  | 106 | struct v4l2_ctrl *hflip; | 
|  | 107 | struct v4l2_ctrl *vflip; | 
|  | 108 | }; | 
|  | 109 | struct { | 
|  | 110 | /* exposure cluster */ | 
|  | 111 | struct v4l2_ctrl *auto_exposure; | 
|  | 112 | struct v4l2_ctrl *exposure; | 
|  | 113 | }; | 
|  | 114 | struct { | 
|  | 115 | /* saturation/hue cluster */ | 
|  | 116 | struct v4l2_ctrl *saturation; | 
|  | 117 | struct v4l2_ctrl *hue; | 
|  | 118 | }; | 
|  | 119 | struct v4l2_ctrl *brightness; | 
|  | 120 | struct v4l2_ctrl *contrast; | 
|  | 121 |  | 
|  | 122 | struct mutex mutex;	/* To serialize asynchronus callbacks */ | 
|  | 123 | bool streaming;		/* Streaming on/off */ | 
|  | 124 |  | 
|  | 125 | struct gpio_desc *resetb_gpio; | 
|  | 126 | struct gpio_desc *pwdn_gpio; | 
|  | 127 | }; | 
|  | 128 |  | 
|  | 129 | struct ov7740_pixfmt { | 
|  | 130 | u32 mbus_code; | 
|  | 131 | enum v4l2_colorspace colorspace; | 
|  | 132 | const struct reg_sequence *regs; | 
|  | 133 | u32 reg_num; | 
|  | 134 | }; | 
|  | 135 |  | 
|  | 136 | struct ov7740_framesize { | 
|  | 137 | u16 width; | 
|  | 138 | u16 height; | 
|  | 139 | const struct reg_sequence *regs; | 
|  | 140 | u32 reg_num; | 
|  | 141 | }; | 
|  | 142 |  | 
|  | 143 | static const struct reg_sequence ov7740_vga[] = { | 
|  | 144 | {0x55, 0x40}, | 
|  | 145 | {0x11, 0x02}, | 
|  | 146 |  | 
|  | 147 | {0xd5, 0x10}, | 
|  | 148 | {0x0c, 0x12}, | 
|  | 149 | {0x0d, 0x34}, | 
|  | 150 | {0x17, 0x25}, | 
|  | 151 | {0x18, 0xa0}, | 
|  | 152 | {0x19, 0x03}, | 
|  | 153 | {0x1a, 0xf0}, | 
|  | 154 | {0x1b, 0x89}, | 
|  | 155 | {0x22, 0x03}, | 
|  | 156 | {0x29, 0x18}, | 
|  | 157 | {0x2b, 0xf8}, | 
|  | 158 | {0x2c, 0x01}, | 
|  | 159 | {REG_HOUTSIZE, 0xa0}, | 
|  | 160 | {REG_VOUTSIZE, 0xf0}, | 
|  | 161 | {0x33, 0xc4}, | 
|  | 162 | {REG_OUTSIZE_LSB, 0x0}, | 
|  | 163 | {0x35, 0x05}, | 
|  | 164 | {0x04, 0x60}, | 
|  | 165 | {0x27, 0x80}, | 
|  | 166 | {0x3d, 0x0f}, | 
|  | 167 | {0x3e, 0x80}, | 
|  | 168 | {0x3f, 0x40}, | 
|  | 169 | {0x40, 0x7f}, | 
|  | 170 | {0x41, 0x6a}, | 
|  | 171 | {0x42, 0x29}, | 
|  | 172 | {0x44, 0x22}, | 
|  | 173 | {0x45, 0x41}, | 
|  | 174 | {0x47, 0x02}, | 
|  | 175 | {0x49, 0x64}, | 
|  | 176 | {0x4a, 0xa1}, | 
|  | 177 | {0x4b, 0x40}, | 
|  | 178 | {0x4c, 0x1a}, | 
|  | 179 | {0x4d, 0x50}, | 
|  | 180 | {0x4e, 0x13}, | 
|  | 181 | {0x64, 0x00}, | 
|  | 182 | {0x67, 0x88}, | 
|  | 183 | {0x68, 0x1a}, | 
|  | 184 |  | 
|  | 185 | {0x14, 0x28}, | 
|  | 186 | {0x24, 0x3c}, | 
|  | 187 | {0x25, 0x30}, | 
|  | 188 | {0x26, 0x72}, | 
|  | 189 | {0x50, 0x97}, | 
|  | 190 | {0x51, 0x1f}, | 
|  | 191 | {0x52, 0x00}, | 
|  | 192 | {0x53, 0x00}, | 
|  | 193 | {0x20, 0x00}, | 
|  | 194 | {0x21, 0xcf}, | 
|  | 195 | {0x50, 0x4b}, | 
|  | 196 | {0x38, 0x14}, | 
|  | 197 | {0xe9, 0x00}, | 
|  | 198 | {0x56, 0x55}, | 
|  | 199 | {0x57, 0xff}, | 
|  | 200 | {0x58, 0xff}, | 
|  | 201 | {0x59, 0xff}, | 
|  | 202 | {0x5f, 0x04}, | 
|  | 203 | {0xec, 0x00}, | 
|  | 204 | {0x13, 0xff}, | 
|  | 205 |  | 
|  | 206 | {0x81, 0x3f}, | 
|  | 207 | {0x82, 0x32}, | 
|  | 208 | {0x38, 0x11}, | 
|  | 209 | {0x84, 0x70}, | 
|  | 210 | {0x85, 0x00}, | 
|  | 211 | {0x86, 0x03}, | 
|  | 212 | {0x87, 0x01}, | 
|  | 213 | {0x88, 0x05}, | 
|  | 214 | {0x89, 0x30}, | 
|  | 215 | {0x8d, 0x30}, | 
|  | 216 | {0x8f, 0x85}, | 
|  | 217 | {0x93, 0x30}, | 
|  | 218 | {0x95, 0x85}, | 
|  | 219 | {0x99, 0x30}, | 
|  | 220 | {0x9b, 0x85}, | 
|  | 221 |  | 
|  | 222 | {0x9c, 0x08}, | 
|  | 223 | {0x9d, 0x12}, | 
|  | 224 | {0x9e, 0x23}, | 
|  | 225 | {0x9f, 0x45}, | 
|  | 226 | {0xa0, 0x55}, | 
|  | 227 | {0xa1, 0x64}, | 
|  | 228 | {0xa2, 0x72}, | 
|  | 229 | {0xa3, 0x7f}, | 
|  | 230 | {0xa4, 0x8b}, | 
|  | 231 | {0xa5, 0x95}, | 
|  | 232 | {0xa6, 0xa7}, | 
|  | 233 | {0xa7, 0xb5}, | 
|  | 234 | {0xa8, 0xcb}, | 
|  | 235 | {0xa9, 0xdd}, | 
|  | 236 | {0xaa, 0xec}, | 
|  | 237 | {0xab, 0x1a}, | 
|  | 238 |  | 
|  | 239 | {0xce, 0x78}, | 
|  | 240 | {0xcf, 0x6e}, | 
|  | 241 | {0xd0, 0x0a}, | 
|  | 242 | {0xd1, 0x0c}, | 
|  | 243 | {0xd2, 0x84}, | 
|  | 244 | {0xd3, 0x90}, | 
|  | 245 | {0xd4, 0x1e}, | 
|  | 246 |  | 
|  | 247 | {0x5a, 0x24}, | 
|  | 248 | {0x5b, 0x1f}, | 
|  | 249 | {0x5c, 0x88}, | 
|  | 250 | {0x5d, 0x60}, | 
|  | 251 |  | 
|  | 252 | {0xac, 0x6e}, | 
|  | 253 | {0xbe, 0xff}, | 
|  | 254 | {0xbf, 0x00}, | 
|  | 255 |  | 
|  | 256 | {0x0f, 0x1d}, | 
|  | 257 | {0x0f, 0x1f}, | 
|  | 258 | }; | 
|  | 259 |  | 
|  | 260 | static const struct ov7740_framesize ov7740_framesizes[] = { | 
|  | 261 | { | 
|  | 262 | .width		= VGA_WIDTH, | 
|  | 263 | .height		= VGA_HEIGHT, | 
|  | 264 | .regs		= ov7740_vga, | 
|  | 265 | .reg_num	= ARRAY_SIZE(ov7740_vga), | 
|  | 266 | }, | 
|  | 267 | }; | 
|  | 268 |  | 
|  | 269 | #ifdef CONFIG_VIDEO_ADV_DEBUG | 
|  | 270 | static int ov7740_get_register(struct v4l2_subdev *sd, | 
|  | 271 | struct v4l2_dbg_register *reg) | 
|  | 272 | { | 
|  | 273 | struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); | 
|  | 274 | struct regmap *regmap = ov7740->regmap; | 
|  | 275 | unsigned int val = 0; | 
|  | 276 | int ret; | 
|  | 277 |  | 
|  | 278 | ret = regmap_read(regmap, reg->reg & 0xff, &val); | 
|  | 279 | reg->val = val; | 
|  | 280 | reg->size = 1; | 
|  | 281 |  | 
|  | 282 | return ret; | 
|  | 283 | } | 
|  | 284 |  | 
|  | 285 | static int ov7740_set_register(struct v4l2_subdev *sd, | 
|  | 286 | const struct v4l2_dbg_register *reg) | 
|  | 287 | { | 
|  | 288 | struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); | 
|  | 289 | struct regmap *regmap = ov7740->regmap; | 
|  | 290 |  | 
|  | 291 | regmap_write(regmap, reg->reg & 0xff, reg->val & 0xff); | 
|  | 292 |  | 
|  | 293 | return 0; | 
|  | 294 | } | 
|  | 295 | #endif | 
|  | 296 |  | 
|  | 297 | static int ov7740_set_power(struct ov7740 *ov7740, int on) | 
|  | 298 | { | 
|  | 299 | int ret; | 
|  | 300 |  | 
|  | 301 | if (on) { | 
|  | 302 | ret = clk_prepare_enable(ov7740->xvclk); | 
|  | 303 | if (ret) | 
|  | 304 | return ret; | 
|  | 305 |  | 
|  | 306 | if (ov7740->pwdn_gpio) | 
|  | 307 | gpiod_direction_output(ov7740->pwdn_gpio, 0); | 
|  | 308 |  | 
|  | 309 | if (ov7740->resetb_gpio) { | 
|  | 310 | gpiod_set_value(ov7740->resetb_gpio, 1); | 
|  | 311 | usleep_range(500, 1000); | 
|  | 312 | gpiod_set_value(ov7740->resetb_gpio, 0); | 
|  | 313 | usleep_range(3000, 5000); | 
|  | 314 | } | 
|  | 315 | } else { | 
|  | 316 | clk_disable_unprepare(ov7740->xvclk); | 
|  | 317 |  | 
|  | 318 | if (ov7740->pwdn_gpio) | 
|  | 319 | gpiod_direction_output(ov7740->pwdn_gpio, 0); | 
|  | 320 | } | 
|  | 321 |  | 
|  | 322 | return 0; | 
|  | 323 | } | 
|  | 324 |  | 
|  | 325 | static struct v4l2_subdev_core_ops ov7740_subdev_core_ops = { | 
|  | 326 | .log_status = v4l2_ctrl_subdev_log_status, | 
|  | 327 | #ifdef CONFIG_VIDEO_ADV_DEBUG | 
|  | 328 | .g_register = ov7740_get_register, | 
|  | 329 | .s_register = ov7740_set_register, | 
|  | 330 | #endif | 
|  | 331 | .subscribe_event = v4l2_ctrl_subdev_subscribe_event, | 
|  | 332 | .unsubscribe_event = v4l2_event_subdev_unsubscribe, | 
|  | 333 | }; | 
|  | 334 |  | 
|  | 335 | static int ov7740_set_white_balance(struct ov7740 *ov7740, int awb) | 
|  | 336 | { | 
|  | 337 | struct regmap *regmap = ov7740->regmap; | 
|  | 338 | unsigned int value; | 
|  | 339 | int ret; | 
|  | 340 |  | 
|  | 341 | ret = regmap_read(regmap, REG_ISP_CTRL00, &value); | 
|  | 342 | if (!ret) { | 
|  | 343 | if (awb) | 
|  | 344 | value |= (ISPCTRL00_AWB_EN | ISPCTRL00_AWB_GAIN_EN); | 
|  | 345 | else | 
|  | 346 | value &= ~(ISPCTRL00_AWB_EN | ISPCTRL00_AWB_GAIN_EN); | 
|  | 347 | ret = regmap_write(regmap, REG_ISP_CTRL00, value); | 
|  | 348 | if (ret) | 
|  | 349 | return ret; | 
|  | 350 | } | 
|  | 351 |  | 
|  | 352 | if (!awb) { | 
|  | 353 | ret = regmap_write(regmap, REG_BGAIN, | 
|  | 354 | ov7740->blue_balance->val); | 
|  | 355 | if (ret) | 
|  | 356 | return ret; | 
|  | 357 |  | 
|  | 358 | ret = regmap_write(regmap, REG_RGAIN, ov7740->red_balance->val); | 
|  | 359 | if (ret) | 
|  | 360 | return ret; | 
|  | 361 | } | 
|  | 362 |  | 
|  | 363 | return 0; | 
|  | 364 | } | 
|  | 365 |  | 
|  | 366 | static int ov7740_set_saturation(struct regmap *regmap, int value) | 
|  | 367 | { | 
|  | 368 | int ret; | 
|  | 369 |  | 
|  | 370 | ret = regmap_write(regmap, REG_USAT, (unsigned char)value); | 
|  | 371 | if (ret) | 
|  | 372 | return ret; | 
|  | 373 |  | 
|  | 374 | return regmap_write(regmap, REG_VSAT, (unsigned char)value); | 
|  | 375 | } | 
|  | 376 |  | 
|  | 377 | static int ov7740_set_gain(struct regmap *regmap, int value) | 
|  | 378 | { | 
|  | 379 | int ret; | 
|  | 380 |  | 
|  | 381 | ret = regmap_write(regmap, REG_GAIN, value & 0xff); | 
|  | 382 | if (ret) | 
|  | 383 | return ret; | 
|  | 384 |  | 
|  | 385 | ret = regmap_update_bits(regmap, REG_CTRL15, | 
|  | 386 | REG15_GAIN_MSB, (value >> 8) & 0x3); | 
|  | 387 | if (!ret) | 
|  | 388 | ret = regmap_update_bits(regmap, REG_REG13, REG13_AGC_EN, 0); | 
|  | 389 |  | 
|  | 390 | return ret; | 
|  | 391 | } | 
|  | 392 |  | 
|  | 393 | static int ov7740_set_autogain(struct regmap *regmap, int value) | 
|  | 394 | { | 
|  | 395 | unsigned int reg; | 
|  | 396 | int ret; | 
|  | 397 |  | 
|  | 398 | ret = regmap_read(regmap, REG_REG13, ®); | 
|  | 399 | if (ret) | 
|  | 400 | return ret; | 
|  | 401 | if (value) | 
|  | 402 | reg |= REG13_AGC_EN; | 
|  | 403 | else | 
|  | 404 | reg &= ~REG13_AGC_EN; | 
|  | 405 | return regmap_write(regmap, REG_REG13, reg); | 
|  | 406 | } | 
|  | 407 |  | 
|  | 408 | static int ov7740_set_brightness(struct regmap *regmap, int value) | 
|  | 409 | { | 
|  | 410 | /* Turn off AEC/AGC */ | 
|  | 411 | regmap_update_bits(regmap, REG_REG13, REG13_AEC_EN, 0); | 
|  | 412 | regmap_update_bits(regmap, REG_REG13, REG13_AGC_EN, 0); | 
|  | 413 |  | 
|  | 414 | if (value >= 0) { | 
|  | 415 | regmap_write(regmap, REG_YBRIGHT, (unsigned char)value); | 
|  | 416 | regmap_update_bits(regmap, REG_SGNSET, SGNSET_YBRIGHT_MASK, 0); | 
|  | 417 | } else{ | 
|  | 418 | regmap_write(regmap, REG_YBRIGHT, (unsigned char)(-value)); | 
|  | 419 | regmap_update_bits(regmap, REG_SGNSET, SGNSET_YBRIGHT_MASK, 1); | 
|  | 420 | } | 
|  | 421 |  | 
|  | 422 | return 0; | 
|  | 423 | } | 
|  | 424 |  | 
|  | 425 | static int ov7740_set_contrast(struct regmap *regmap, int value) | 
|  | 426 | { | 
|  | 427 | return regmap_write(regmap, REG_YGAIN, (unsigned char)value); | 
|  | 428 | } | 
|  | 429 |  | 
|  | 430 | static int ov7740_get_gain(struct ov7740 *ov7740, struct v4l2_ctrl *ctrl) | 
|  | 431 | { | 
|  | 432 | struct regmap *regmap = ov7740->regmap; | 
|  | 433 | unsigned int value0, value1; | 
|  | 434 | int ret; | 
|  | 435 |  | 
|  | 436 | if (!ctrl->val) | 
|  | 437 | return 0; | 
|  | 438 |  | 
|  | 439 | ret = regmap_read(regmap, REG_GAIN, &value0); | 
|  | 440 | if (ret) | 
|  | 441 | return ret; | 
|  | 442 | ret = regmap_read(regmap, REG_CTRL15, &value1); | 
|  | 443 | if (ret) | 
|  | 444 | return ret; | 
|  | 445 |  | 
|  | 446 | ov7740->gain->val = (value1 << 8) | (value0 & 0xff); | 
|  | 447 |  | 
|  | 448 | return 0; | 
|  | 449 | } | 
|  | 450 |  | 
|  | 451 | static int ov7740_set_exp(struct regmap *regmap, int value) | 
|  | 452 | { | 
|  | 453 | int ret; | 
|  | 454 |  | 
|  | 455 | /* Turn off AEC/AGC */ | 
|  | 456 | ret = regmap_update_bits(regmap, REG_REG13, | 
|  | 457 | REG13_AEC_EN | REG13_AGC_EN, 0); | 
|  | 458 | if (ret) | 
|  | 459 | return ret; | 
|  | 460 |  | 
|  | 461 | ret = regmap_write(regmap, REG_AEC, (unsigned char)value); | 
|  | 462 | if (ret) | 
|  | 463 | return ret; | 
|  | 464 |  | 
|  | 465 | return regmap_write(regmap, REG_HAEC, (unsigned char)(value >> 8)); | 
|  | 466 | } | 
|  | 467 |  | 
|  | 468 | static int ov7740_set_autoexp(struct regmap *regmap, | 
|  | 469 | enum v4l2_exposure_auto_type value) | 
|  | 470 | { | 
|  | 471 | unsigned int reg; | 
|  | 472 | int ret; | 
|  | 473 |  | 
|  | 474 | ret = regmap_read(regmap, REG_REG13, ®); | 
|  | 475 | if (!ret) { | 
|  | 476 | if (value == V4L2_EXPOSURE_AUTO) | 
|  | 477 | reg |= (REG13_AEC_EN | REG13_AGC_EN); | 
|  | 478 | else | 
|  | 479 | reg &= ~(REG13_AEC_EN | REG13_AGC_EN); | 
|  | 480 | ret = regmap_write(regmap, REG_REG13, reg); | 
|  | 481 | } | 
|  | 482 |  | 
|  | 483 | return ret; | 
|  | 484 | } | 
|  | 485 |  | 
|  | 486 |  | 
|  | 487 | static int ov7740_get_volatile_ctrl(struct v4l2_ctrl *ctrl) | 
|  | 488 | { | 
|  | 489 | struct ov7740 *ov7740 = container_of(ctrl->handler, | 
|  | 490 | struct ov7740, ctrl_handler); | 
|  | 491 | int ret; | 
|  | 492 |  | 
|  | 493 | switch (ctrl->id) { | 
|  | 494 | case V4L2_CID_AUTOGAIN: | 
|  | 495 | ret = ov7740_get_gain(ov7740, ctrl); | 
|  | 496 | break; | 
|  | 497 | default: | 
|  | 498 | ret = -EINVAL; | 
|  | 499 | break; | 
|  | 500 | } | 
|  | 501 | return ret; | 
|  | 502 | } | 
|  | 503 |  | 
|  | 504 | static int ov7740_set_ctrl(struct v4l2_ctrl *ctrl) | 
|  | 505 | { | 
|  | 506 | struct ov7740 *ov7740 = container_of(ctrl->handler, | 
|  | 507 | struct ov7740, ctrl_handler); | 
|  | 508 | struct i2c_client *client = v4l2_get_subdevdata(&ov7740->subdev); | 
|  | 509 | struct regmap *regmap = ov7740->regmap; | 
|  | 510 | int ret; | 
|  | 511 | u8 val = 0; | 
|  | 512 |  | 
|  | 513 | if (!pm_runtime_get_if_in_use(&client->dev)) | 
|  | 514 | return 0; | 
|  | 515 |  | 
|  | 516 | switch (ctrl->id) { | 
|  | 517 | case V4L2_CID_AUTO_WHITE_BALANCE: | 
|  | 518 | ret = ov7740_set_white_balance(ov7740, ctrl->val); | 
|  | 519 | break; | 
|  | 520 | case V4L2_CID_SATURATION: | 
|  | 521 | ret = ov7740_set_saturation(regmap, ctrl->val); | 
|  | 522 | break; | 
|  | 523 | case V4L2_CID_BRIGHTNESS: | 
|  | 524 | ret = ov7740_set_brightness(regmap, ctrl->val); | 
|  | 525 | break; | 
|  | 526 | case V4L2_CID_CONTRAST: | 
|  | 527 | ret = ov7740_set_contrast(regmap, ctrl->val); | 
|  | 528 | break; | 
|  | 529 | case V4L2_CID_VFLIP: | 
|  | 530 | ret = regmap_update_bits(regmap, REG_REG0C, | 
|  | 531 | REG0C_IMG_FLIP, val); | 
|  | 532 | break; | 
|  | 533 | case V4L2_CID_HFLIP: | 
|  | 534 | val = ctrl->val ? REG0C_IMG_MIRROR : 0x00; | 
|  | 535 | ret = regmap_update_bits(regmap, REG_REG0C, | 
|  | 536 | REG0C_IMG_MIRROR, val); | 
|  | 537 | break; | 
|  | 538 | case V4L2_CID_AUTOGAIN: | 
|  | 539 | if (!ctrl->val) | 
|  | 540 | return ov7740_set_gain(regmap, ov7740->gain->val); | 
|  | 541 |  | 
|  | 542 | ret = ov7740_set_autogain(regmap, ctrl->val); | 
|  | 543 | break; | 
|  | 544 |  | 
|  | 545 | case V4L2_CID_EXPOSURE_AUTO: | 
|  | 546 | if (ctrl->val == V4L2_EXPOSURE_MANUAL) | 
|  | 547 | return ov7740_set_exp(regmap, ov7740->exposure->val); | 
|  | 548 |  | 
|  | 549 | ret = ov7740_set_autoexp(regmap, ctrl->val); | 
|  | 550 | break; | 
|  | 551 | default: | 
|  | 552 | ret = -EINVAL; | 
|  | 553 | break; | 
|  | 554 | } | 
|  | 555 |  | 
|  | 556 | pm_runtime_put(&client->dev); | 
|  | 557 |  | 
|  | 558 | return ret; | 
|  | 559 | } | 
|  | 560 |  | 
|  | 561 | static const struct v4l2_ctrl_ops ov7740_ctrl_ops = { | 
|  | 562 | .g_volatile_ctrl = ov7740_get_volatile_ctrl, | 
|  | 563 | .s_ctrl = ov7740_set_ctrl, | 
|  | 564 | }; | 
|  | 565 |  | 
|  | 566 | static int ov7740_start_streaming(struct ov7740 *ov7740) | 
|  | 567 | { | 
|  | 568 | int ret; | 
|  | 569 |  | 
|  | 570 | if (ov7740->fmt) { | 
|  | 571 | ret = regmap_multi_reg_write(ov7740->regmap, | 
|  | 572 | ov7740->fmt->regs, | 
|  | 573 | ov7740->fmt->reg_num); | 
|  | 574 | if (ret) | 
|  | 575 | return ret; | 
|  | 576 | } | 
|  | 577 |  | 
|  | 578 | if (ov7740->frmsize) { | 
|  | 579 | ret = regmap_multi_reg_write(ov7740->regmap, | 
|  | 580 | ov7740->frmsize->regs, | 
|  | 581 | ov7740->frmsize->reg_num); | 
|  | 582 | if (ret) | 
|  | 583 | return ret; | 
|  | 584 | } | 
|  | 585 |  | 
|  | 586 | return __v4l2_ctrl_handler_setup(ov7740->subdev.ctrl_handler); | 
|  | 587 | } | 
|  | 588 |  | 
|  | 589 | static int ov7740_set_stream(struct v4l2_subdev *sd, int enable) | 
|  | 590 | { | 
|  | 591 | struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); | 
|  | 592 | struct i2c_client *client = v4l2_get_subdevdata(sd); | 
|  | 593 | int ret = 0; | 
|  | 594 |  | 
|  | 595 | mutex_lock(&ov7740->mutex); | 
|  | 596 | if (ov7740->streaming == enable) { | 
|  | 597 | mutex_unlock(&ov7740->mutex); | 
|  | 598 | return 0; | 
|  | 599 | } | 
|  | 600 |  | 
|  | 601 | if (enable) { | 
|  | 602 | ret = pm_runtime_get_sync(&client->dev); | 
|  | 603 | if (ret < 0) { | 
|  | 604 | pm_runtime_put_noidle(&client->dev); | 
|  | 605 | goto err_unlock; | 
|  | 606 | } | 
|  | 607 |  | 
|  | 608 | ret = ov7740_start_streaming(ov7740); | 
|  | 609 | if (ret) | 
|  | 610 | goto err_rpm_put; | 
|  | 611 | } else { | 
|  | 612 | pm_runtime_put(&client->dev); | 
|  | 613 | } | 
|  | 614 |  | 
|  | 615 | ov7740->streaming = enable; | 
|  | 616 |  | 
|  | 617 | mutex_unlock(&ov7740->mutex); | 
|  | 618 | return ret; | 
|  | 619 |  | 
|  | 620 | err_rpm_put: | 
|  | 621 | pm_runtime_put(&client->dev); | 
|  | 622 | err_unlock: | 
|  | 623 | mutex_unlock(&ov7740->mutex); | 
|  | 624 | return ret; | 
|  | 625 | } | 
|  | 626 |  | 
|  | 627 | static int ov7740_g_frame_interval(struct v4l2_subdev *sd, | 
|  | 628 | struct v4l2_subdev_frame_interval *ival) | 
|  | 629 | { | 
|  | 630 | struct v4l2_fract *tpf = &ival->interval; | 
|  | 631 |  | 
|  | 632 |  | 
|  | 633 | tpf->numerator = 1; | 
|  | 634 | tpf->denominator = 60; | 
|  | 635 |  | 
|  | 636 | return 0; | 
|  | 637 | } | 
|  | 638 |  | 
|  | 639 | static int ov7740_s_frame_interval(struct v4l2_subdev *sd, | 
|  | 640 | struct v4l2_subdev_frame_interval *ival) | 
|  | 641 | { | 
|  | 642 | struct v4l2_fract *tpf = &ival->interval; | 
|  | 643 |  | 
|  | 644 |  | 
|  | 645 | tpf->numerator = 1; | 
|  | 646 | tpf->denominator = 60; | 
|  | 647 |  | 
|  | 648 | return 0; | 
|  | 649 | } | 
|  | 650 |  | 
|  | 651 | static struct v4l2_subdev_video_ops ov7740_subdev_video_ops = { | 
|  | 652 | .s_stream = ov7740_set_stream, | 
|  | 653 | .s_frame_interval = ov7740_s_frame_interval, | 
|  | 654 | .g_frame_interval = ov7740_g_frame_interval, | 
|  | 655 | }; | 
|  | 656 |  | 
|  | 657 | static const struct reg_sequence ov7740_format_yuyv[] = { | 
|  | 658 | {0x12, 0x00}, | 
|  | 659 | {0x36, 0x3f}, | 
|  | 660 | {0x80, 0x7f}, | 
|  | 661 | {0x83, 0x01}, | 
|  | 662 | }; | 
|  | 663 |  | 
|  | 664 | static const struct reg_sequence ov7740_format_bggr8[] = { | 
|  | 665 | {0x36, 0x2f}, | 
|  | 666 | {0x80, 0x01}, | 
|  | 667 | {0x83, 0x04}, | 
|  | 668 | }; | 
|  | 669 |  | 
|  | 670 | static const struct ov7740_pixfmt ov7740_formats[] = { | 
|  | 671 | { | 
|  | 672 | .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, | 
|  | 673 | .colorspace = V4L2_COLORSPACE_SRGB, | 
|  | 674 | .regs = ov7740_format_yuyv, | 
|  | 675 | .reg_num = ARRAY_SIZE(ov7740_format_yuyv), | 
|  | 676 | }, | 
|  | 677 | { | 
|  | 678 | .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, | 
|  | 679 | .colorspace = V4L2_COLORSPACE_SRGB, | 
|  | 680 | .regs = ov7740_format_bggr8, | 
|  | 681 | .reg_num = ARRAY_SIZE(ov7740_format_bggr8), | 
|  | 682 | } | 
|  | 683 | }; | 
|  | 684 | #define N_OV7740_FMTS ARRAY_SIZE(ov7740_formats) | 
|  | 685 |  | 
|  | 686 | static int ov7740_enum_mbus_code(struct v4l2_subdev *sd, | 
|  | 687 | struct v4l2_subdev_pad_config *cfg, | 
|  | 688 | struct v4l2_subdev_mbus_code_enum *code) | 
|  | 689 | { | 
|  | 690 | if (code->pad || code->index >= N_OV7740_FMTS) | 
|  | 691 | return -EINVAL; | 
|  | 692 |  | 
|  | 693 | code->code = ov7740_formats[code->index].mbus_code; | 
|  | 694 |  | 
|  | 695 | return 0; | 
|  | 696 | } | 
|  | 697 |  | 
|  | 698 | static int ov7740_enum_frame_interval(struct v4l2_subdev *sd, | 
|  | 699 | struct v4l2_subdev_pad_config *cfg, | 
|  | 700 | struct v4l2_subdev_frame_interval_enum *fie) | 
|  | 701 | { | 
|  | 702 | if (fie->pad) | 
|  | 703 | return -EINVAL; | 
|  | 704 |  | 
|  | 705 | if (fie->index >= 1) | 
|  | 706 | return -EINVAL; | 
|  | 707 |  | 
|  | 708 | if ((fie->width != VGA_WIDTH) || (fie->height != VGA_HEIGHT)) | 
|  | 709 | return -EINVAL; | 
|  | 710 |  | 
|  | 711 | fie->interval.numerator = 1; | 
|  | 712 | fie->interval.denominator = 60; | 
|  | 713 |  | 
|  | 714 | return 0; | 
|  | 715 | } | 
|  | 716 |  | 
|  | 717 | static int ov7740_enum_frame_size(struct v4l2_subdev *sd, | 
|  | 718 | struct v4l2_subdev_pad_config *cfg, | 
|  | 719 | struct v4l2_subdev_frame_size_enum *fse) | 
|  | 720 | { | 
|  | 721 | if (fse->pad) | 
|  | 722 | return -EINVAL; | 
|  | 723 |  | 
|  | 724 | if (fse->index > 0) | 
|  | 725 | return -EINVAL; | 
|  | 726 |  | 
|  | 727 | fse->min_width = fse->max_width = VGA_WIDTH; | 
|  | 728 | fse->min_height = fse->max_height = VGA_HEIGHT; | 
|  | 729 |  | 
|  | 730 | return 0; | 
|  | 731 | } | 
|  | 732 |  | 
|  | 733 | static int ov7740_try_fmt_internal(struct v4l2_subdev *sd, | 
|  | 734 | struct v4l2_mbus_framefmt *fmt, | 
|  | 735 | const struct ov7740_pixfmt **ret_fmt, | 
|  | 736 | const struct ov7740_framesize **ret_frmsize) | 
|  | 737 | { | 
|  | 738 | struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); | 
|  | 739 | const struct ov7740_framesize *fsize = &ov7740_framesizes[0]; | 
|  | 740 | int index, i; | 
|  | 741 |  | 
|  | 742 | for (index = 0; index < N_OV7740_FMTS; index++) { | 
|  | 743 | if (ov7740_formats[index].mbus_code == fmt->code) | 
|  | 744 | break; | 
|  | 745 | } | 
|  | 746 | if (index >= N_OV7740_FMTS) { | 
|  | 747 | /* default to first format */ | 
|  | 748 | index = 0; | 
|  | 749 | fmt->code = ov7740_formats[0].mbus_code; | 
|  | 750 | } | 
|  | 751 | if (ret_fmt != NULL) | 
|  | 752 | *ret_fmt = ov7740_formats + index; | 
|  | 753 |  | 
|  | 754 | for (i = 0; i < ARRAY_SIZE(ov7740_framesizes); i++) { | 
|  | 755 | if ((fsize->width >= fmt->width) && | 
|  | 756 | (fsize->height >= fmt->height)) { | 
|  | 757 | fmt->width = fsize->width; | 
|  | 758 | fmt->height = fsize->height; | 
|  | 759 | break; | 
|  | 760 | } | 
|  | 761 |  | 
|  | 762 | fsize++; | 
|  | 763 | } | 
|  | 764 | if (i >= ARRAY_SIZE(ov7740_framesizes)) { | 
|  | 765 | fsize = &ov7740_framesizes[0]; | 
|  | 766 | fmt->width = fsize->width; | 
|  | 767 | fmt->height = fsize->height; | 
|  | 768 | } | 
|  | 769 | if (ret_frmsize != NULL) | 
|  | 770 | *ret_frmsize = fsize; | 
|  | 771 |  | 
|  | 772 | fmt->field = V4L2_FIELD_NONE; | 
|  | 773 | fmt->colorspace = ov7740_formats[index].colorspace; | 
|  | 774 |  | 
|  | 775 | ov7740->format = *fmt; | 
|  | 776 |  | 
|  | 777 | return 0; | 
|  | 778 | } | 
|  | 779 |  | 
|  | 780 | static int ov7740_set_fmt(struct v4l2_subdev *sd, | 
|  | 781 | struct v4l2_subdev_pad_config *cfg, | 
|  | 782 | struct v4l2_subdev_format *format) | 
|  | 783 | { | 
|  | 784 | struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); | 
|  | 785 | const struct ov7740_pixfmt *ovfmt; | 
|  | 786 | const struct ov7740_framesize *fsize; | 
|  | 787 | #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API | 
|  | 788 | struct v4l2_mbus_framefmt *mbus_fmt; | 
|  | 789 | #endif | 
|  | 790 | int ret; | 
|  | 791 |  | 
|  | 792 | mutex_lock(&ov7740->mutex); | 
|  | 793 | if (format->pad) { | 
|  | 794 | ret = -EINVAL; | 
|  | 795 | goto error; | 
|  | 796 | } | 
|  | 797 |  | 
|  | 798 | if (format->which == V4L2_SUBDEV_FORMAT_TRY) { | 
|  | 799 | ret = ov7740_try_fmt_internal(sd, &format->format, NULL, NULL); | 
|  | 800 | if (ret) | 
|  | 801 | goto error; | 
|  | 802 | #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API | 
|  | 803 | mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); | 
|  | 804 | *mbus_fmt = format->format; | 
|  | 805 |  | 
|  | 806 | mutex_unlock(&ov7740->mutex); | 
|  | 807 | return 0; | 
|  | 808 | #else | 
|  | 809 | ret = -ENOTTY; | 
|  | 810 | goto error; | 
|  | 811 | #endif | 
|  | 812 | } | 
|  | 813 |  | 
|  | 814 | ret = ov7740_try_fmt_internal(sd, &format->format, &ovfmt, &fsize); | 
|  | 815 | if (ret) | 
|  | 816 | goto error; | 
|  | 817 |  | 
|  | 818 | ov7740->fmt = ovfmt; | 
|  | 819 | ov7740->frmsize = fsize; | 
|  | 820 |  | 
|  | 821 | mutex_unlock(&ov7740->mutex); | 
|  | 822 | return 0; | 
|  | 823 |  | 
|  | 824 | error: | 
|  | 825 | mutex_unlock(&ov7740->mutex); | 
|  | 826 | return ret; | 
|  | 827 | } | 
|  | 828 |  | 
|  | 829 | static int ov7740_get_fmt(struct v4l2_subdev *sd, | 
|  | 830 | struct v4l2_subdev_pad_config *cfg, | 
|  | 831 | struct v4l2_subdev_format *format) | 
|  | 832 | { | 
|  | 833 | struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); | 
|  | 834 | #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API | 
|  | 835 | struct v4l2_mbus_framefmt *mbus_fmt; | 
|  | 836 | #endif | 
|  | 837 | int ret = 0; | 
|  | 838 |  | 
|  | 839 | mutex_lock(&ov7740->mutex); | 
|  | 840 | if (format->which == V4L2_SUBDEV_FORMAT_TRY) { | 
|  | 841 | #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API | 
|  | 842 | mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); | 
|  | 843 | format->format = *mbus_fmt; | 
|  | 844 | ret = 0; | 
|  | 845 | #else | 
|  | 846 | ret = -ENOTTY; | 
|  | 847 | #endif | 
|  | 848 | } else { | 
|  | 849 | format->format = ov7740->format; | 
|  | 850 | } | 
|  | 851 | mutex_unlock(&ov7740->mutex); | 
|  | 852 |  | 
|  | 853 | return ret; | 
|  | 854 | } | 
|  | 855 |  | 
|  | 856 | static const struct v4l2_subdev_pad_ops ov7740_subdev_pad_ops = { | 
|  | 857 | .enum_frame_interval = ov7740_enum_frame_interval, | 
|  | 858 | .enum_frame_size = ov7740_enum_frame_size, | 
|  | 859 | .enum_mbus_code = ov7740_enum_mbus_code, | 
|  | 860 | .get_fmt = ov7740_get_fmt, | 
|  | 861 | .set_fmt = ov7740_set_fmt, | 
|  | 862 | }; | 
|  | 863 |  | 
|  | 864 | static const struct v4l2_subdev_ops ov7740_subdev_ops = { | 
|  | 865 | .core	= &ov7740_subdev_core_ops, | 
|  | 866 | .video	= &ov7740_subdev_video_ops, | 
|  | 867 | .pad	= &ov7740_subdev_pad_ops, | 
|  | 868 | }; | 
|  | 869 |  | 
|  | 870 | static void ov7740_get_default_format(struct v4l2_subdev *sd, | 
|  | 871 | struct v4l2_mbus_framefmt *format) | 
|  | 872 | { | 
|  | 873 | struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); | 
|  | 874 |  | 
|  | 875 | format->width = ov7740->frmsize->width; | 
|  | 876 | format->height = ov7740->frmsize->height; | 
|  | 877 | format->colorspace = ov7740->fmt->colorspace; | 
|  | 878 | format->code = ov7740->fmt->mbus_code; | 
|  | 879 | format->field = V4L2_FIELD_NONE; | 
|  | 880 | } | 
|  | 881 |  | 
|  | 882 | #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API | 
|  | 883 | static int ov7740_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | 
|  | 884 | { | 
|  | 885 | struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); | 
|  | 886 | struct v4l2_mbus_framefmt *format = | 
|  | 887 | v4l2_subdev_get_try_format(sd, fh->pad, 0); | 
|  | 888 |  | 
|  | 889 | mutex_lock(&ov7740->mutex); | 
|  | 890 | ov7740_get_default_format(sd, format); | 
|  | 891 | mutex_unlock(&ov7740->mutex); | 
|  | 892 |  | 
|  | 893 | return 0; | 
|  | 894 | } | 
|  | 895 |  | 
|  | 896 | static const struct v4l2_subdev_internal_ops ov7740_subdev_internal_ops = { | 
|  | 897 | .open = ov7740_open, | 
|  | 898 | }; | 
|  | 899 | #endif | 
|  | 900 |  | 
|  | 901 | static int ov7740_probe_dt(struct i2c_client *client, | 
|  | 902 | struct ov7740 *ov7740) | 
|  | 903 | { | 
|  | 904 | ov7740->resetb_gpio = devm_gpiod_get_optional(&client->dev, "reset", | 
|  | 905 | GPIOD_OUT_HIGH); | 
|  | 906 | if (IS_ERR(ov7740->resetb_gpio)) { | 
|  | 907 | dev_info(&client->dev, "can't get %s GPIO\n", "reset"); | 
|  | 908 | return PTR_ERR(ov7740->resetb_gpio); | 
|  | 909 | } | 
|  | 910 |  | 
|  | 911 | ov7740->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown", | 
|  | 912 | GPIOD_OUT_LOW); | 
|  | 913 | if (IS_ERR(ov7740->pwdn_gpio)) { | 
|  | 914 | dev_info(&client->dev, "can't get %s GPIO\n", "powerdown"); | 
|  | 915 | return PTR_ERR(ov7740->pwdn_gpio); | 
|  | 916 | } | 
|  | 917 |  | 
|  | 918 | return 0; | 
|  | 919 | } | 
|  | 920 |  | 
|  | 921 | static int ov7740_detect(struct ov7740 *ov7740) | 
|  | 922 | { | 
|  | 923 | struct regmap *regmap = ov7740->regmap; | 
|  | 924 | unsigned int midh, midl, pidh, pidl; | 
|  | 925 | int ret; | 
|  | 926 |  | 
|  | 927 | ret = regmap_read(regmap, REG_MIDH, &midh); | 
|  | 928 | if (ret) | 
|  | 929 | return ret; | 
|  | 930 | if (midh != 0x7f) | 
|  | 931 | return -ENODEV; | 
|  | 932 |  | 
|  | 933 | ret = regmap_read(regmap, REG_MIDL, &midl); | 
|  | 934 | if (ret) | 
|  | 935 | return ret; | 
|  | 936 | if (midl != 0xa2) | 
|  | 937 | return -ENODEV; | 
|  | 938 |  | 
|  | 939 | ret = regmap_read(regmap, REG_PIDH, &pidh); | 
|  | 940 | if (ret) | 
|  | 941 | return ret; | 
|  | 942 | if (pidh != 0x77) | 
|  | 943 | return -ENODEV; | 
|  | 944 |  | 
|  | 945 | ret = regmap_read(regmap, REG_PIDL, &pidl); | 
|  | 946 | if (ret) | 
|  | 947 | return ret; | 
|  | 948 | if ((pidl != 0x40) && (pidl != 0x41) && (pidl != 0x42)) | 
|  | 949 | return -ENODEV; | 
|  | 950 |  | 
|  | 951 | return 0; | 
|  | 952 | } | 
|  | 953 |  | 
|  | 954 | static int ov7740_init_controls(struct ov7740 *ov7740) | 
|  | 955 | { | 
|  | 956 | struct i2c_client *client = v4l2_get_subdevdata(&ov7740->subdev); | 
|  | 957 | struct v4l2_ctrl_handler *ctrl_hdlr = &ov7740->ctrl_handler; | 
|  | 958 | int ret; | 
|  | 959 |  | 
|  | 960 | ret = v4l2_ctrl_handler_init(ctrl_hdlr, 12); | 
|  | 961 | if (ret < 0) | 
|  | 962 | return ret; | 
|  | 963 |  | 
|  | 964 | ctrl_hdlr->lock = &ov7740->mutex; | 
|  | 965 | ov7740->auto_wb = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops, | 
|  | 966 | V4L2_CID_AUTO_WHITE_BALANCE, | 
|  | 967 | 0, 1, 1, 1); | 
|  | 968 | ov7740->blue_balance = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops, | 
|  | 969 | V4L2_CID_BLUE_BALANCE, | 
|  | 970 | 0, 0xff, 1, 0x80); | 
|  | 971 | ov7740->red_balance = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops, | 
|  | 972 | V4L2_CID_RED_BALANCE, | 
|  | 973 | 0, 0xff, 1, 0x80); | 
|  | 974 |  | 
|  | 975 | ov7740->brightness = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops, | 
|  | 976 | V4L2_CID_BRIGHTNESS, | 
|  | 977 | -255, 255, 1, 0); | 
|  | 978 | ov7740->contrast = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops, | 
|  | 979 | V4L2_CID_CONTRAST, | 
|  | 980 | 0, 127, 1, 0x20); | 
|  | 981 | ov7740->saturation = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops, | 
|  | 982 | V4L2_CID_SATURATION, 0, 256, 1, 0x80); | 
|  | 983 | ov7740->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops, | 
|  | 984 | V4L2_CID_HFLIP, 0, 1, 1, 0); | 
|  | 985 | ov7740->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops, | 
|  | 986 | V4L2_CID_VFLIP, 0, 1, 1, 0); | 
|  | 987 |  | 
|  | 988 | ov7740->gain = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops, | 
|  | 989 | V4L2_CID_GAIN, 0, 1023, 1, 500); | 
|  | 990 | if (ov7740->gain) | 
|  | 991 | ov7740->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; | 
|  | 992 |  | 
|  | 993 | ov7740->auto_gain = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops, | 
|  | 994 | V4L2_CID_AUTOGAIN, 0, 1, 1, 1); | 
|  | 995 |  | 
|  | 996 | ov7740->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops, | 
|  | 997 | V4L2_CID_EXPOSURE, 0, 65535, 1, 500); | 
|  | 998 | if (ov7740->exposure) | 
|  | 999 | ov7740->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; | 
|  | 1000 |  | 
|  | 1001 | ov7740->auto_exposure = v4l2_ctrl_new_std_menu(ctrl_hdlr, | 
|  | 1002 | &ov7740_ctrl_ops, | 
|  | 1003 | V4L2_CID_EXPOSURE_AUTO, | 
|  | 1004 | V4L2_EXPOSURE_MANUAL, 0, | 
|  | 1005 | V4L2_EXPOSURE_AUTO); | 
|  | 1006 |  | 
|  | 1007 | v4l2_ctrl_auto_cluster(3, &ov7740->auto_wb, 0, false); | 
|  | 1008 | v4l2_ctrl_auto_cluster(2, &ov7740->auto_gain, 0, true); | 
|  | 1009 | v4l2_ctrl_auto_cluster(2, &ov7740->auto_exposure, | 
|  | 1010 | V4L2_EXPOSURE_MANUAL, false); | 
|  | 1011 | v4l2_ctrl_cluster(2, &ov7740->hflip); | 
|  | 1012 |  | 
|  | 1013 | if (ctrl_hdlr->error) { | 
|  | 1014 | ret = ctrl_hdlr->error; | 
|  | 1015 | dev_err(&client->dev, "controls initialisation failed (%d)\n", | 
|  | 1016 | ret); | 
|  | 1017 | goto error; | 
|  | 1018 | } | 
|  | 1019 |  | 
|  | 1020 | ret = v4l2_ctrl_handler_setup(ctrl_hdlr); | 
|  | 1021 | if (ret) { | 
|  | 1022 | dev_err(&client->dev, "%s control init failed (%d)\n", | 
|  | 1023 | __func__, ret); | 
|  | 1024 | goto error; | 
|  | 1025 | } | 
|  | 1026 |  | 
|  | 1027 | ov7740->subdev.ctrl_handler = ctrl_hdlr; | 
|  | 1028 | return 0; | 
|  | 1029 |  | 
|  | 1030 | error: | 
|  | 1031 | v4l2_ctrl_handler_free(ctrl_hdlr); | 
|  | 1032 | mutex_destroy(&ov7740->mutex); | 
|  | 1033 | return ret; | 
|  | 1034 | } | 
|  | 1035 |  | 
|  | 1036 | static void ov7740_free_controls(struct ov7740 *ov7740) | 
|  | 1037 | { | 
|  | 1038 | v4l2_ctrl_handler_free(ov7740->subdev.ctrl_handler); | 
|  | 1039 | mutex_destroy(&ov7740->mutex); | 
|  | 1040 | } | 
|  | 1041 |  | 
|  | 1042 | #define OV7740_MAX_REGISTER     0xff | 
|  | 1043 | static const struct regmap_config ov7740_regmap_config = { | 
|  | 1044 | .reg_bits	= 8, | 
|  | 1045 | .val_bits	= 8, | 
|  | 1046 | .max_register	= OV7740_MAX_REGISTER, | 
|  | 1047 | }; | 
|  | 1048 |  | 
|  | 1049 | static int ov7740_probe(struct i2c_client *client, | 
|  | 1050 | const struct i2c_device_id *id) | 
|  | 1051 | { | 
|  | 1052 | struct ov7740 *ov7740; | 
|  | 1053 | struct v4l2_subdev *sd; | 
|  | 1054 | int ret; | 
|  | 1055 |  | 
|  | 1056 | if (!i2c_check_functionality(client->adapter, | 
|  | 1057 | I2C_FUNC_SMBUS_BYTE_DATA)) { | 
|  | 1058 | dev_err(&client->dev, | 
|  | 1059 | "OV7740: I2C-Adapter doesn't support SMBUS\n"); | 
|  | 1060 | return -EIO; | 
|  | 1061 | } | 
|  | 1062 |  | 
|  | 1063 | ov7740 = devm_kzalloc(&client->dev, sizeof(*ov7740), GFP_KERNEL); | 
|  | 1064 | if (!ov7740) | 
|  | 1065 | return -ENOMEM; | 
|  | 1066 |  | 
|  | 1067 | ov7740->xvclk = devm_clk_get(&client->dev, "xvclk"); | 
|  | 1068 | if (IS_ERR(ov7740->xvclk)) { | 
|  | 1069 | ret = PTR_ERR(ov7740->xvclk); | 
|  | 1070 | dev_err(&client->dev, | 
|  | 1071 | "OV7740: fail to get xvclk: %d\n", ret); | 
|  | 1072 | return ret; | 
|  | 1073 | } | 
|  | 1074 |  | 
|  | 1075 | ret = ov7740_probe_dt(client, ov7740); | 
|  | 1076 | if (ret) | 
|  | 1077 | return ret; | 
|  | 1078 |  | 
|  | 1079 | ov7740->regmap = devm_regmap_init_i2c(client, &ov7740_regmap_config); | 
|  | 1080 | if (IS_ERR(ov7740->regmap)) { | 
|  | 1081 | ret = PTR_ERR(ov7740->regmap); | 
|  | 1082 | dev_err(&client->dev, "Failed to allocate register map: %d\n", | 
|  | 1083 | ret); | 
|  | 1084 | return ret; | 
|  | 1085 | } | 
|  | 1086 |  | 
|  | 1087 | sd = &ov7740->subdev; | 
|  | 1088 | client->flags |= I2C_CLIENT_SCCB; | 
|  | 1089 | v4l2_i2c_subdev_init(sd, client, &ov7740_subdev_ops); | 
|  | 1090 |  | 
|  | 1091 | #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API | 
|  | 1092 | sd->internal_ops = &ov7740_subdev_internal_ops; | 
|  | 1093 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; | 
|  | 1094 | #endif | 
|  | 1095 |  | 
|  | 1096 | #if defined(CONFIG_MEDIA_CONTROLLER) | 
|  | 1097 | ov7740->pad.flags = MEDIA_PAD_FL_SOURCE; | 
|  | 1098 | sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; | 
|  | 1099 | ret = media_entity_pads_init(&sd->entity, 1, &ov7740->pad); | 
|  | 1100 | if (ret) | 
|  | 1101 | return ret; | 
|  | 1102 | #endif | 
|  | 1103 |  | 
|  | 1104 | ret = ov7740_set_power(ov7740, 1); | 
|  | 1105 | if (ret) | 
|  | 1106 | return ret; | 
|  | 1107 |  | 
|  | 1108 | pm_runtime_set_active(&client->dev); | 
|  | 1109 | pm_runtime_enable(&client->dev); | 
|  | 1110 |  | 
|  | 1111 | ret = ov7740_detect(ov7740); | 
|  | 1112 | if (ret) | 
|  | 1113 | goto error_detect; | 
|  | 1114 |  | 
|  | 1115 | mutex_init(&ov7740->mutex); | 
|  | 1116 |  | 
|  | 1117 | ret = ov7740_init_controls(ov7740); | 
|  | 1118 | if (ret) | 
|  | 1119 | goto error_init_controls; | 
|  | 1120 |  | 
|  | 1121 | v4l_info(client, "chip found @ 0x%02x (%s)\n", | 
|  | 1122 | client->addr << 1, client->adapter->name); | 
|  | 1123 |  | 
|  | 1124 | ov7740->fmt = &ov7740_formats[0]; | 
|  | 1125 | ov7740->frmsize = &ov7740_framesizes[0]; | 
|  | 1126 |  | 
|  | 1127 | ov7740_get_default_format(sd, &ov7740->format); | 
|  | 1128 |  | 
|  | 1129 | ret = v4l2_async_register_subdev(sd); | 
|  | 1130 | if (ret) | 
|  | 1131 | goto error_async_register; | 
|  | 1132 |  | 
|  | 1133 | pm_runtime_idle(&client->dev); | 
|  | 1134 |  | 
|  | 1135 | return 0; | 
|  | 1136 |  | 
|  | 1137 | error_async_register: | 
|  | 1138 | v4l2_ctrl_handler_free(ov7740->subdev.ctrl_handler); | 
|  | 1139 | error_init_controls: | 
|  | 1140 | ov7740_free_controls(ov7740); | 
|  | 1141 | error_detect: | 
|  | 1142 | pm_runtime_disable(&client->dev); | 
|  | 1143 | pm_runtime_set_suspended(&client->dev); | 
|  | 1144 | ov7740_set_power(ov7740, 0); | 
|  | 1145 | media_entity_cleanup(&ov7740->subdev.entity); | 
|  | 1146 |  | 
|  | 1147 | return ret; | 
|  | 1148 | } | 
|  | 1149 |  | 
|  | 1150 | static int ov7740_remove(struct i2c_client *client) | 
|  | 1151 | { | 
|  | 1152 | struct v4l2_subdev *sd = i2c_get_clientdata(client); | 
|  | 1153 | struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); | 
|  | 1154 |  | 
|  | 1155 | mutex_destroy(&ov7740->mutex); | 
|  | 1156 | v4l2_ctrl_handler_free(ov7740->subdev.ctrl_handler); | 
|  | 1157 | media_entity_cleanup(&ov7740->subdev.entity); | 
|  | 1158 | v4l2_async_unregister_subdev(sd); | 
|  | 1159 | ov7740_free_controls(ov7740); | 
|  | 1160 |  | 
|  | 1161 | pm_runtime_get_sync(&client->dev); | 
|  | 1162 | pm_runtime_disable(&client->dev); | 
|  | 1163 | pm_runtime_set_suspended(&client->dev); | 
|  | 1164 | pm_runtime_put_noidle(&client->dev); | 
|  | 1165 |  | 
|  | 1166 | ov7740_set_power(ov7740, 0); | 
|  | 1167 | return 0; | 
|  | 1168 | } | 
|  | 1169 |  | 
|  | 1170 | static int __maybe_unused ov7740_runtime_suspend(struct device *dev) | 
|  | 1171 | { | 
|  | 1172 | struct i2c_client *client = to_i2c_client(dev); | 
|  | 1173 | struct v4l2_subdev *sd = i2c_get_clientdata(client); | 
|  | 1174 | struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); | 
|  | 1175 |  | 
|  | 1176 | ov7740_set_power(ov7740, 0); | 
|  | 1177 |  | 
|  | 1178 | return 0; | 
|  | 1179 | } | 
|  | 1180 |  | 
|  | 1181 | static int __maybe_unused ov7740_runtime_resume(struct device *dev) | 
|  | 1182 | { | 
|  | 1183 | struct i2c_client *client = to_i2c_client(dev); | 
|  | 1184 | struct v4l2_subdev *sd = i2c_get_clientdata(client); | 
|  | 1185 | struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); | 
|  | 1186 |  | 
|  | 1187 | return ov7740_set_power(ov7740, 1); | 
|  | 1188 | } | 
|  | 1189 |  | 
|  | 1190 | static const struct i2c_device_id ov7740_id[] = { | 
|  | 1191 | { "ov7740", 0 }, | 
|  | 1192 | { /* sentinel */ } | 
|  | 1193 | }; | 
|  | 1194 | MODULE_DEVICE_TABLE(i2c, ov7740_id); | 
|  | 1195 |  | 
|  | 1196 | static const struct dev_pm_ops ov7740_pm_ops = { | 
|  | 1197 | SET_RUNTIME_PM_OPS(ov7740_runtime_suspend, ov7740_runtime_resume, NULL) | 
|  | 1198 | }; | 
|  | 1199 |  | 
|  | 1200 | static const struct of_device_id ov7740_of_match[] = { | 
|  | 1201 | {.compatible = "ovti,ov7740", }, | 
|  | 1202 | { /* sentinel */ }, | 
|  | 1203 | }; | 
|  | 1204 | MODULE_DEVICE_TABLE(of, ov7740_of_match); | 
|  | 1205 |  | 
|  | 1206 | static struct i2c_driver ov7740_i2c_driver = { | 
|  | 1207 | .driver = { | 
|  | 1208 | .name = "ov7740", | 
|  | 1209 | .pm = &ov7740_pm_ops, | 
|  | 1210 | .of_match_table = of_match_ptr(ov7740_of_match), | 
|  | 1211 | }, | 
|  | 1212 | .probe    = ov7740_probe, | 
|  | 1213 | .remove   = ov7740_remove, | 
|  | 1214 | .id_table = ov7740_id, | 
|  | 1215 | }; | 
|  | 1216 | module_i2c_driver(ov7740_i2c_driver); | 
|  | 1217 |  | 
|  | 1218 | MODULE_DESCRIPTION("The V4L2 driver for Omnivision 7740 sensor"); | 
|  | 1219 | MODULE_AUTHOR("Songjun Wu <songjun.wu@atmel.com>"); | 
|  | 1220 | MODULE_LICENSE("GPL v2"); |