785 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2 3/*4 * Driver for the ov9650 sensor5 *6 * Copyright (C) 2008 Erik Andrén7 * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.8 * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>9 *10 * Portions of code to USB interface and ALi driver software,11 * Copyright (c) 2006 Willem Duinker12 * v4l2 interface modeled after the V4L2 driver13 * for SN9C10x PC Camera Controllers14 */15 16#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt17 18#include "m5602_ov9650.h"19 20static int ov9650_s_ctrl(struct v4l2_ctrl *ctrl);21static void ov9650_dump_registers(struct sd *sd);22 23static const unsigned char preinit_ov9650[][3] = {24 /* [INITCAM] */25 {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},26 {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},27 {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},28 {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},29 {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},30 {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},31 32 {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08},33 {BRIDGE, M5602_XB_GPIO_DIR, 0x05},34 {BRIDGE, M5602_XB_GPIO_DAT, 0x04},35 {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},36 {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},37 {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},38 {BRIDGE, M5602_XB_GPIO_DAT, 0x00},39 {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a},40 /* Reset chip */41 {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},42 /* Enable double clock */43 {SENSOR, OV9650_CLKRC, 0x80},44 /* Do something out of spec with the power */45 {SENSOR, OV9650_OFON, 0x40}46};47 48static const unsigned char init_ov9650[][3] = {49 /* [INITCAM] */50 {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},51 {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},52 {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},53 {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},54 {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},55 {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},56 57 {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08},58 {BRIDGE, M5602_XB_GPIO_DIR, 0x05},59 {BRIDGE, M5602_XB_GPIO_DAT, 0x04},60 {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},61 {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},62 {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},63 {BRIDGE, M5602_XB_GPIO_DAT, 0x00},64 {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a},65 66 /* Reset chip */67 {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},68 /* One extra reset is needed in order to make the sensor behave69 properly when resuming from ram, could be a timing issue */70 {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},71 72 /* Enable double clock */73 {SENSOR, OV9650_CLKRC, 0x80},74 /* Do something out of spec with the power */75 {SENSOR, OV9650_OFON, 0x40},76 77 /* Set fast AGC/AEC algorithm with unlimited step size */78 {SENSOR, OV9650_COM8, OV9650_FAST_AGC_AEC |79 OV9650_AEC_UNLIM_STEP_SIZE},80 81 {SENSOR, OV9650_CHLF, 0x10},82 {SENSOR, OV9650_ARBLM, 0xbf},83 {SENSOR, OV9650_ACOM38, 0x81},84 /* Turn off color matrix coefficient double option */85 {SENSOR, OV9650_COM16, 0x00},86 /* Enable color matrix for RGB/YUV, Delay Y channel,87 set output Y/UV delay to 1 */88 {SENSOR, OV9650_COM13, 0x19},89 /* Enable digital BLC, Set output mode to U Y V Y */90 {SENSOR, OV9650_TSLB, 0x0c},91 /* Limit the AGC/AEC stable upper region */92 {SENSOR, OV9650_COM24, 0x00},93 /* Enable HREF and some out of spec things */94 {SENSOR, OV9650_COM12, 0x73},95 /* Set all DBLC offset signs to positive and96 do some out of spec stuff */97 {SENSOR, OV9650_DBLC1, 0xdf},98 {SENSOR, OV9650_COM21, 0x06},99 {SENSOR, OV9650_RSVD35, 0x91},100 /* Necessary, no camera stream without it */101 {SENSOR, OV9650_RSVD16, 0x06},102 {SENSOR, OV9650_RSVD94, 0x99},103 {SENSOR, OV9650_RSVD95, 0x99},104 {SENSOR, OV9650_RSVD96, 0x04},105 /* Enable full range output */106 {SENSOR, OV9650_COM15, 0x0},107 /* Enable HREF at optical black, enable ADBLC bias,108 enable ADBLC, reset timings at format change */109 {SENSOR, OV9650_COM6, 0x4b},110 /* Subtract 32 from the B channel bias */111 {SENSOR, OV9650_BBIAS, 0xa0},112 /* Subtract 32 from the Gb channel bias */113 {SENSOR, OV9650_GbBIAS, 0xa0},114 /* Do not bypass the analog BLC and to some out of spec stuff */115 {SENSOR, OV9650_Gr_COM, 0x00},116 /* Subtract 32 from the R channel bias */117 {SENSOR, OV9650_RBIAS, 0xa0},118 /* Subtract 32 from the R channel bias */119 {SENSOR, OV9650_RBIAS, 0x0},120 {SENSOR, OV9650_COM26, 0x80},121 {SENSOR, OV9650_ACOMA9, 0x98},122 /* Set the AGC/AEC stable region upper limit */123 {SENSOR, OV9650_AEW, 0x68},124 /* Set the AGC/AEC stable region lower limit */125 {SENSOR, OV9650_AEB, 0x5c},126 /* Set the high and low limit nibbles to 3 */127 {SENSOR, OV9650_VPT, 0xc3},128 /* Set the Automatic Gain Ceiling (AGC) to 128x,129 drop VSYNC at frame drop,130 limit exposure timing,131 drop frame when the AEC step is larger than the exposure gap */132 {SENSOR, OV9650_COM9, 0x6e},133 /* Set VSYNC negative, Set RESET to SLHS (slave mode horizontal sync)134 and set PWDN to SLVS (slave mode vertical sync) */135 {SENSOR, OV9650_COM10, 0x42},136 /* Set horizontal column start high to default value */137 {SENSOR, OV9650_HSTART, 0x1a}, /* 210 */138 /* Set horizontal column end */139 {SENSOR, OV9650_HSTOP, 0xbf}, /* 1534 */140 /* Complementing register to the two writes above */141 {SENSOR, OV9650_HREF, 0xb2},142 /* Set vertical row start high bits */143 {SENSOR, OV9650_VSTRT, 0x02},144 /* Set vertical row end low bits */145 {SENSOR, OV9650_VSTOP, 0x7e},146 /* Set complementing vertical frame control */147 {SENSOR, OV9650_VREF, 0x10},148 {SENSOR, OV9650_ADC, 0x04},149 {SENSOR, OV9650_HV, 0x40},150 151 /* Enable denoise, and white-pixel erase */152 {SENSOR, OV9650_COM22, OV9650_DENOISE_ENABLE |153 OV9650_WHITE_PIXEL_ENABLE |154 OV9650_WHITE_PIXEL_OPTION},155 156 /* Enable VARIOPIXEL */157 {SENSOR, OV9650_COM3, OV9650_VARIOPIXEL},158 {SENSOR, OV9650_COM4, OV9650_QVGA_VARIOPIXEL},159 160 /* Put the sensor in soft sleep mode */161 {SENSOR, OV9650_COM2, OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X},162};163 164static const unsigned char res_init_ov9650[][3] = {165 {SENSOR, OV9650_COM2, OV9650_OUTPUT_DRIVE_2X},166 167 {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x82},168 {BRIDGE, M5602_XB_LINE_OF_FRAME_L, 0x00},169 {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},170 {BRIDGE, M5602_XB_PIX_OF_LINE_L, 0x00},171 {BRIDGE, M5602_XB_SIG_INI, 0x01}172};173 174/* Vertically and horizontally flips the image if matched, needed for machines175 where the sensor is mounted upside down */176static177 const178 struct dmi_system_id ov9650_flip_dmi_table[] = {179 {180 .ident = "ASUS A6Ja",181 .matches = {182 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),183 DMI_MATCH(DMI_PRODUCT_NAME, "A6J")184 }185 },186 {187 .ident = "ASUS A6JC",188 .matches = {189 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),190 DMI_MATCH(DMI_PRODUCT_NAME, "A6JC")191 }192 },193 {194 .ident = "ASUS A6K",195 .matches = {196 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),197 DMI_MATCH(DMI_PRODUCT_NAME, "A6K")198 }199 },200 {201 .ident = "ASUS A6Kt",202 .matches = {203 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),204 DMI_MATCH(DMI_PRODUCT_NAME, "A6Kt")205 }206 },207 {208 .ident = "ASUS A6VA",209 .matches = {210 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),211 DMI_MATCH(DMI_PRODUCT_NAME, "A6VA")212 }213 },214 {215 216 .ident = "ASUS A6VC",217 .matches = {218 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),219 DMI_MATCH(DMI_PRODUCT_NAME, "A6VC")220 }221 },222 {223 .ident = "ASUS A6VM",224 .matches = {225 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),226 DMI_MATCH(DMI_PRODUCT_NAME, "A6VM")227 }228 },229 {230 .ident = "ASUS A7V",231 .matches = {232 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),233 DMI_MATCH(DMI_PRODUCT_NAME, "A7V")234 }235 },236 {237 .ident = "Alienware Aurora m9700",238 .matches = {239 DMI_MATCH(DMI_SYS_VENDOR, "alienware"),240 DMI_MATCH(DMI_PRODUCT_NAME, "Aurora m9700")241 }242 },243 {}244};245 246static struct v4l2_pix_format ov9650_modes[] = {247 {248 176,249 144,250 V4L2_PIX_FMT_SBGGR8,251 V4L2_FIELD_NONE,252 .sizeimage =253 176 * 144,254 .bytesperline = 176,255 .colorspace = V4L2_COLORSPACE_SRGB,256 .priv = 9257 }, {258 320,259 240,260 V4L2_PIX_FMT_SBGGR8,261 V4L2_FIELD_NONE,262 .sizeimage =263 320 * 240,264 .bytesperline = 320,265 .colorspace = V4L2_COLORSPACE_SRGB,266 .priv = 8267 }, {268 352,269 288,270 V4L2_PIX_FMT_SBGGR8,271 V4L2_FIELD_NONE,272 .sizeimage =273 352 * 288,274 .bytesperline = 352,275 .colorspace = V4L2_COLORSPACE_SRGB,276 .priv = 9277 }, {278 640,279 480,280 V4L2_PIX_FMT_SBGGR8,281 V4L2_FIELD_NONE,282 .sizeimage =283 640 * 480,284 .bytesperline = 640,285 .colorspace = V4L2_COLORSPACE_SRGB,286 .priv = 9287 }288};289 290static const struct v4l2_ctrl_ops ov9650_ctrl_ops = {291 .s_ctrl = ov9650_s_ctrl,292};293 294int ov9650_probe(struct sd *sd)295{296 int err = 0;297 u8 prod_id = 0, ver_id = 0, i;298 struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;299 300 if (force_sensor) {301 if (force_sensor == OV9650_SENSOR) {302 pr_info("Forcing an %s sensor\n", ov9650.name);303 goto sensor_found;304 }305 /* If we want to force another sensor,306 don't try to probe this one */307 return -ENODEV;308 }309 310 gspca_dbg(gspca_dev, D_PROBE, "Probing for an ov9650 sensor\n");311 312 /* Run the pre-init before probing the sensor */313 for (i = 0; i < ARRAY_SIZE(preinit_ov9650) && !err; i++) {314 u8 data = preinit_ov9650[i][2];315 if (preinit_ov9650[i][0] == SENSOR)316 err = m5602_write_sensor(sd,317 preinit_ov9650[i][1], &data, 1);318 else319 err = m5602_write_bridge(sd,320 preinit_ov9650[i][1], data);321 }322 323 if (err < 0)324 return err;325 326 if (m5602_read_sensor(sd, OV9650_PID, &prod_id, 1))327 return -ENODEV;328 329 if (m5602_read_sensor(sd, OV9650_VER, &ver_id, 1))330 return -ENODEV;331 332 if ((prod_id == 0x96) && (ver_id == 0x52)) {333 pr_info("Detected an ov9650 sensor\n");334 goto sensor_found;335 }336 return -ENODEV;337 338sensor_found:339 sd->gspca_dev.cam.cam_mode = ov9650_modes;340 sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov9650_modes);341 342 return 0;343}344 345int ov9650_init(struct sd *sd)346{347 int i, err = 0;348 u8 data;349 350 if (dump_sensor)351 ov9650_dump_registers(sd);352 353 for (i = 0; i < ARRAY_SIZE(init_ov9650) && !err; i++) {354 data = init_ov9650[i][2];355 if (init_ov9650[i][0] == SENSOR)356 err = m5602_write_sensor(sd, init_ov9650[i][1],357 &data, 1);358 else359 err = m5602_write_bridge(sd, init_ov9650[i][1], data);360 }361 362 return 0;363}364 365int ov9650_init_controls(struct sd *sd)366{367 struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;368 369 sd->gspca_dev.vdev.ctrl_handler = hdl;370 v4l2_ctrl_handler_init(hdl, 9);371 372 sd->auto_white_bal = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops,373 V4L2_CID_AUTO_WHITE_BALANCE,374 0, 1, 1, 1);375 sd->red_bal = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops,376 V4L2_CID_RED_BALANCE, 0, 255, 1,377 RED_GAIN_DEFAULT);378 sd->blue_bal = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops,379 V4L2_CID_BLUE_BALANCE, 0, 255, 1,380 BLUE_GAIN_DEFAULT);381 382 sd->autoexpo = v4l2_ctrl_new_std_menu(hdl, &ov9650_ctrl_ops,383 V4L2_CID_EXPOSURE_AUTO, 1, 0, V4L2_EXPOSURE_AUTO);384 sd->expo = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_EXPOSURE,385 0, 0x1ff, 4, EXPOSURE_DEFAULT);386 387 sd->autogain = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops,388 V4L2_CID_AUTOGAIN, 0, 1, 1, 1);389 sd->gain = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_GAIN, 0,390 0x3ff, 1, GAIN_DEFAULT);391 392 sd->hflip = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_HFLIP,393 0, 1, 1, 0);394 sd->vflip = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_VFLIP,395 0, 1, 1, 0);396 397 if (hdl->error) {398 pr_err("Could not initialize controls\n");399 return hdl->error;400 }401 402 v4l2_ctrl_auto_cluster(3, &sd->auto_white_bal, 0, false);403 v4l2_ctrl_auto_cluster(2, &sd->autoexpo, 0, false);404 v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, false);405 v4l2_ctrl_cluster(2, &sd->hflip);406 407 return 0;408}409 410int ov9650_start(struct sd *sd)411{412 u8 data;413 int i, err = 0;414 struct cam *cam = &sd->gspca_dev.cam;415 416 int width = cam->cam_mode[sd->gspca_dev.curr_mode].width;417 int height = cam->cam_mode[sd->gspca_dev.curr_mode].height;418 int ver_offs = cam->cam_mode[sd->gspca_dev.curr_mode].priv;419 int hor_offs = OV9650_LEFT_OFFSET;420 struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;421 422 if ((!dmi_check_system(ov9650_flip_dmi_table) &&423 sd->vflip->val) ||424 (dmi_check_system(ov9650_flip_dmi_table) &&425 !sd->vflip->val))426 ver_offs--;427 428 if (width <= 320)429 hor_offs /= 2;430 431 /* Synthesize the vsync/hsync setup */432 for (i = 0; i < ARRAY_SIZE(res_init_ov9650) && !err; i++) {433 if (res_init_ov9650[i][0] == BRIDGE)434 err = m5602_write_bridge(sd, res_init_ov9650[i][1],435 res_init_ov9650[i][2]);436 else if (res_init_ov9650[i][0] == SENSOR) {437 data = res_init_ov9650[i][2];438 err = m5602_write_sensor(sd,439 res_init_ov9650[i][1], &data, 1);440 }441 }442 if (err < 0)443 return err;444 445 err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA,446 ((ver_offs >> 8) & 0xff));447 if (err < 0)448 return err;449 450 err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (ver_offs & 0xff));451 if (err < 0)452 return err;453 454 err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);455 if (err < 0)456 return err;457 458 err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff);459 if (err < 0)460 return err;461 462 err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff));463 if (err < 0)464 return err;465 466 for (i = 0; i < 2 && !err; i++)467 err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);468 if (err < 0)469 return err;470 471 err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);472 if (err < 0)473 return err;474 475 err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 2);476 if (err < 0)477 return err;478 479 err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,480 (hor_offs >> 8) & 0xff);481 if (err < 0)482 return err;483 484 err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, hor_offs & 0xff);485 if (err < 0)486 return err;487 488 err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,489 ((width + hor_offs) >> 8) & 0xff);490 if (err < 0)491 return err;492 493 err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,494 ((width + hor_offs) & 0xff));495 if (err < 0)496 return err;497 498 err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);499 if (err < 0)500 return err;501 502 switch (width) {503 case 640:504 gspca_dbg(gspca_dev, D_CONF, "Configuring camera for VGA mode\n");505 506 data = OV9650_VGA_SELECT | OV9650_RGB_SELECT |507 OV9650_RAW_RGB_SELECT;508 err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);509 break;510 511 case 352:512 gspca_dbg(gspca_dev, D_CONF, "Configuring camera for CIF mode\n");513 514 data = OV9650_CIF_SELECT | OV9650_RGB_SELECT |515 OV9650_RAW_RGB_SELECT;516 err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);517 break;518 519 case 320:520 gspca_dbg(gspca_dev, D_CONF, "Configuring camera for QVGA mode\n");521 522 data = OV9650_QVGA_SELECT | OV9650_RGB_SELECT |523 OV9650_RAW_RGB_SELECT;524 err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);525 break;526 527 case 176:528 gspca_dbg(gspca_dev, D_CONF, "Configuring camera for QCIF mode\n");529 530 data = OV9650_QCIF_SELECT | OV9650_RGB_SELECT |531 OV9650_RAW_RGB_SELECT;532 err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);533 break;534 }535 return err;536}537 538int ov9650_stop(struct sd *sd)539{540 u8 data = OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X;541 return m5602_write_sensor(sd, OV9650_COM2, &data, 1);542}543 544void ov9650_disconnect(struct sd *sd)545{546 ov9650_stop(sd);547 548 sd->sensor = NULL;549}550 551static int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val)552{553 struct sd *sd = (struct sd *) gspca_dev;554 u8 i2c_data;555 int err;556 557 gspca_dbg(gspca_dev, D_CONF, "Set exposure to %d\n", val);558 559 /* The 6 MSBs */560 i2c_data = (val >> 10) & 0x3f;561 err = m5602_write_sensor(sd, OV9650_AECHM,562 &i2c_data, 1);563 if (err < 0)564 return err;565 566 /* The 8 middle bits */567 i2c_data = (val >> 2) & 0xff;568 err = m5602_write_sensor(sd, OV9650_AECH,569 &i2c_data, 1);570 if (err < 0)571 return err;572 573 /* The 2 LSBs */574 i2c_data = val & 0x03;575 err = m5602_write_sensor(sd, OV9650_COM1, &i2c_data, 1);576 return err;577}578 579static int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val)580{581 int err;582 u8 i2c_data;583 struct sd *sd = (struct sd *) gspca_dev;584 585 gspca_dbg(gspca_dev, D_CONF, "Setting gain to %d\n", val);586 587 /* The 2 MSB */588 /* Read the OV9650_VREF register first to avoid589 corrupting the VREF high and low bits */590 err = m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1);591 if (err < 0)592 return err;593 594 /* Mask away all uninteresting bits */595 i2c_data = ((val & 0x0300) >> 2) |596 (i2c_data & 0x3f);597 err = m5602_write_sensor(sd, OV9650_VREF, &i2c_data, 1);598 if (err < 0)599 return err;600 601 /* The 8 LSBs */602 i2c_data = val & 0xff;603 err = m5602_write_sensor(sd, OV9650_GAIN, &i2c_data, 1);604 return err;605}606 607static int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)608{609 int err;610 u8 i2c_data;611 struct sd *sd = (struct sd *) gspca_dev;612 613 gspca_dbg(gspca_dev, D_CONF, "Set red gain to %d\n", val);614 615 i2c_data = val & 0xff;616 err = m5602_write_sensor(sd, OV9650_RED, &i2c_data, 1);617 return err;618}619 620static int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)621{622 int err;623 u8 i2c_data;624 struct sd *sd = (struct sd *) gspca_dev;625 626 gspca_dbg(gspca_dev, D_CONF, "Set blue gain to %d\n", val);627 628 i2c_data = val & 0xff;629 err = m5602_write_sensor(sd, OV9650_BLUE, &i2c_data, 1);630 return err;631}632 633static int ov9650_set_hvflip(struct gspca_dev *gspca_dev)634{635 int err;636 u8 i2c_data;637 struct sd *sd = (struct sd *) gspca_dev;638 int hflip = sd->hflip->val;639 int vflip = sd->vflip->val;640 641 gspca_dbg(gspca_dev, D_CONF, "Set hvflip to %d %d\n", hflip, vflip);642 643 if (dmi_check_system(ov9650_flip_dmi_table))644 vflip = !vflip;645 646 i2c_data = (hflip << 5) | (vflip << 4);647 err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);648 if (err < 0)649 return err;650 651 /* When vflip is toggled we need to readjust the bridge hsync/vsync */652 if (gspca_dev->streaming)653 err = ov9650_start(sd);654 655 return err;656}657 658static int ov9650_set_auto_exposure(struct gspca_dev *gspca_dev,659 __s32 val)660{661 int err;662 u8 i2c_data;663 struct sd *sd = (struct sd *) gspca_dev;664 665 gspca_dbg(gspca_dev, D_CONF, "Set auto exposure control to %d\n", val);666 667 err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);668 if (err < 0)669 return err;670 671 val = (val == V4L2_EXPOSURE_AUTO);672 i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0));673 674 return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);675}676 677static int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev,678 __s32 val)679{680 int err;681 u8 i2c_data;682 struct sd *sd = (struct sd *) gspca_dev;683 684 gspca_dbg(gspca_dev, D_CONF, "Set auto white balance to %d\n", val);685 686 err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);687 if (err < 0)688 return err;689 690 i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1));691 err = m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);692 693 return err;694}695 696static int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val)697{698 int err;699 u8 i2c_data;700 struct sd *sd = (struct sd *) gspca_dev;701 702 gspca_dbg(gspca_dev, D_CONF, "Set auto gain control to %d\n", val);703 704 err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);705 if (err < 0)706 return err;707 708 i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2));709 710 return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);711}712 713static int ov9650_s_ctrl(struct v4l2_ctrl *ctrl)714{715 struct gspca_dev *gspca_dev =716 container_of(ctrl->handler, struct gspca_dev, ctrl_handler);717 struct sd *sd = (struct sd *) gspca_dev;718 int err;719 720 if (!gspca_dev->streaming)721 return 0;722 723 switch (ctrl->id) {724 case V4L2_CID_AUTO_WHITE_BALANCE:725 err = ov9650_set_auto_white_balance(gspca_dev, ctrl->val);726 if (err || ctrl->val)727 return err;728 err = ov9650_set_red_balance(gspca_dev, sd->red_bal->val);729 if (err)730 return err;731 err = ov9650_set_blue_balance(gspca_dev, sd->blue_bal->val);732 break;733 case V4L2_CID_EXPOSURE_AUTO:734 err = ov9650_set_auto_exposure(gspca_dev, ctrl->val);735 if (err || ctrl->val == V4L2_EXPOSURE_AUTO)736 return err;737 err = ov9650_set_exposure(gspca_dev, sd->expo->val);738 break;739 case V4L2_CID_AUTOGAIN:740 err = ov9650_set_auto_gain(gspca_dev, ctrl->val);741 if (err || ctrl->val)742 return err;743 err = ov9650_set_gain(gspca_dev, sd->gain->val);744 break;745 case V4L2_CID_HFLIP:746 err = ov9650_set_hvflip(gspca_dev);747 break;748 default:749 return -EINVAL;750 }751 752 return err;753}754 755static void ov9650_dump_registers(struct sd *sd)756{757 int address;758 pr_info("Dumping the ov9650 register state\n");759 for (address = 0; address < 0xa9; address++) {760 u8 value;761 m5602_read_sensor(sd, address, &value, 1);762 pr_info("register 0x%x contains 0x%x\n", address, value);763 }764 765 pr_info("ov9650 register state dump complete\n");766 767 pr_info("Probing for which registers that are read/write\n");768 for (address = 0; address < 0xff; address++) {769 u8 old_value, ctrl_value;770 u8 test_value[2] = {0xff, 0xff};771 772 m5602_read_sensor(sd, address, &old_value, 1);773 m5602_write_sensor(sd, address, test_value, 1);774 m5602_read_sensor(sd, address, &ctrl_value, 1);775 776 if (ctrl_value == test_value[0])777 pr_info("register 0x%x is writeable\n", address);778 else779 pr_info("register 0x%x is read only\n", address);780 781 /* Restore original value */782 m5602_write_sensor(sd, address, &old_value, 1);783 }784}785