Path: blob/master/drivers/media/video/gspca/m5602/m5602_ov9650.c
17980 views
/*1* Driver for the ov9650 sensor2*3* Copyright (C) 2008 Erik Andrén4* Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.5* Copyright (C) 2005 m5603x Linux Driver Project <[email protected]>6*7* Portions of code to USB interface and ALi driver software,8* Copyright (c) 2006 Willem Duinker9* v4l2 interface modeled after the V4L2 driver10* for SN9C10x PC Camera Controllers11*12* This program is free software; you can redistribute it and/or13* modify it under the terms of the GNU General Public License as14* published by the Free Software Foundation, version 2.15*16*/1718#include "m5602_ov9650.h"1920static int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val);21static int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);22static int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val);23static int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val);24static int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val);25static int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val);26static int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val);27static int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val);28static int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);29static int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val);30static int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);31static int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val);32static int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev,33__s32 *val);34static int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev,35__s32 val);36static int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val);37static int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val);38static int ov9650_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val);39static int ov9650_set_auto_exposure(struct gspca_dev *gspca_dev, __s32 val);4041/* Vertically and horizontally flips the image if matched, needed for machines42where the sensor is mounted upside down */43static44const45struct dmi_system_id ov9650_flip_dmi_table[] = {46{47.ident = "ASUS A6Ja",48.matches = {49DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),50DMI_MATCH(DMI_PRODUCT_NAME, "A6J")51}52},53{54.ident = "ASUS A6JC",55.matches = {56DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),57DMI_MATCH(DMI_PRODUCT_NAME, "A6JC")58}59},60{61.ident = "ASUS A6K",62.matches = {63DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),64DMI_MATCH(DMI_PRODUCT_NAME, "A6K")65}66},67{68.ident = "ASUS A6Kt",69.matches = {70DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),71DMI_MATCH(DMI_PRODUCT_NAME, "A6Kt")72}73},74{75.ident = "ASUS A6VA",76.matches = {77DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),78DMI_MATCH(DMI_PRODUCT_NAME, "A6VA")79}80},81{8283.ident = "ASUS A6VC",84.matches = {85DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),86DMI_MATCH(DMI_PRODUCT_NAME, "A6VC")87}88},89{90.ident = "ASUS A6VM",91.matches = {92DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),93DMI_MATCH(DMI_PRODUCT_NAME, "A6VM")94}95},96{97.ident = "ASUS A7V",98.matches = {99DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),100DMI_MATCH(DMI_PRODUCT_NAME, "A7V")101}102},103{104.ident = "Alienware Aurora m9700",105.matches = {106DMI_MATCH(DMI_SYS_VENDOR, "alienware"),107DMI_MATCH(DMI_PRODUCT_NAME, "Aurora m9700")108}109},110{}111};112113static const struct ctrl ov9650_ctrls[] = {114#define EXPOSURE_IDX 0115{116{117.id = V4L2_CID_EXPOSURE,118.type = V4L2_CTRL_TYPE_INTEGER,119.name = "exposure",120.minimum = 0x00,121.maximum = 0x1ff,122.step = 0x4,123.default_value = EXPOSURE_DEFAULT,124.flags = V4L2_CTRL_FLAG_SLIDER125},126.set = ov9650_set_exposure,127.get = ov9650_get_exposure128},129#define GAIN_IDX 1130{131{132.id = V4L2_CID_GAIN,133.type = V4L2_CTRL_TYPE_INTEGER,134.name = "gain",135.minimum = 0x00,136.maximum = 0x3ff,137.step = 0x1,138.default_value = GAIN_DEFAULT,139.flags = V4L2_CTRL_FLAG_SLIDER140},141.set = ov9650_set_gain,142.get = ov9650_get_gain143},144#define RED_BALANCE_IDX 2145{146{147.id = V4L2_CID_RED_BALANCE,148.type = V4L2_CTRL_TYPE_INTEGER,149.name = "red balance",150.minimum = 0x00,151.maximum = 0xff,152.step = 0x1,153.default_value = RED_GAIN_DEFAULT,154.flags = V4L2_CTRL_FLAG_SLIDER155},156.set = ov9650_set_red_balance,157.get = ov9650_get_red_balance158},159#define BLUE_BALANCE_IDX 3160{161{162.id = V4L2_CID_BLUE_BALANCE,163.type = V4L2_CTRL_TYPE_INTEGER,164.name = "blue balance",165.minimum = 0x00,166.maximum = 0xff,167.step = 0x1,168.default_value = BLUE_GAIN_DEFAULT,169.flags = V4L2_CTRL_FLAG_SLIDER170},171.set = ov9650_set_blue_balance,172.get = ov9650_get_blue_balance173},174#define HFLIP_IDX 4175{176{177.id = V4L2_CID_HFLIP,178.type = V4L2_CTRL_TYPE_BOOLEAN,179.name = "horizontal flip",180.minimum = 0,181.maximum = 1,182.step = 1,183.default_value = 0184},185.set = ov9650_set_hflip,186.get = ov9650_get_hflip187},188#define VFLIP_IDX 5189{190{191.id = V4L2_CID_VFLIP,192.type = V4L2_CTRL_TYPE_BOOLEAN,193.name = "vertical flip",194.minimum = 0,195.maximum = 1,196.step = 1,197.default_value = 0198},199.set = ov9650_set_vflip,200.get = ov9650_get_vflip201},202#define AUTO_WHITE_BALANCE_IDX 6203{204{205.id = V4L2_CID_AUTO_WHITE_BALANCE,206.type = V4L2_CTRL_TYPE_BOOLEAN,207.name = "auto white balance",208.minimum = 0,209.maximum = 1,210.step = 1,211.default_value = 1212},213.set = ov9650_set_auto_white_balance,214.get = ov9650_get_auto_white_balance215},216#define AUTO_GAIN_CTRL_IDX 7217{218{219.id = V4L2_CID_AUTOGAIN,220.type = V4L2_CTRL_TYPE_BOOLEAN,221.name = "auto gain control",222.minimum = 0,223.maximum = 1,224.step = 1,225.default_value = 1226},227.set = ov9650_set_auto_gain,228.get = ov9650_get_auto_gain229},230#define AUTO_EXPOSURE_IDX 8231{232{233.id = V4L2_CID_EXPOSURE_AUTO,234.type = V4L2_CTRL_TYPE_BOOLEAN,235.name = "auto exposure",236.minimum = 0,237.maximum = 1,238.step = 1,239.default_value = 1240},241.set = ov9650_set_auto_exposure,242.get = ov9650_get_auto_exposure243}244245};246247static struct v4l2_pix_format ov9650_modes[] = {248{249176,250144,251V4L2_PIX_FMT_SBGGR8,252V4L2_FIELD_NONE,253.sizeimage =254176 * 144,255.bytesperline = 176,256.colorspace = V4L2_COLORSPACE_SRGB,257.priv = 9258}, {259320,260240,261V4L2_PIX_FMT_SBGGR8,262V4L2_FIELD_NONE,263.sizeimage =264320 * 240,265.bytesperline = 320,266.colorspace = V4L2_COLORSPACE_SRGB,267.priv = 8268}, {269352,270288,271V4L2_PIX_FMT_SBGGR8,272V4L2_FIELD_NONE,273.sizeimage =274352 * 288,275.bytesperline = 352,276.colorspace = V4L2_COLORSPACE_SRGB,277.priv = 9278}, {279640,280480,281V4L2_PIX_FMT_SBGGR8,282V4L2_FIELD_NONE,283.sizeimage =284640 * 480,285.bytesperline = 640,286.colorspace = V4L2_COLORSPACE_SRGB,287.priv = 9288}289};290291static void ov9650_dump_registers(struct sd *sd);292293int ov9650_probe(struct sd *sd)294{295int err = 0;296u8 prod_id = 0, ver_id = 0, i;297s32 *sensor_settings;298299if (force_sensor) {300if (force_sensor == OV9650_SENSOR) {301info("Forcing an %s sensor", ov9650.name);302goto sensor_found;303}304/* If we want to force another sensor,305don't try to probe this one */306return -ENODEV;307}308309PDEBUG(D_PROBE, "Probing for an ov9650 sensor");310311/* Run the pre-init before probing the sensor */312for (i = 0; i < ARRAY_SIZE(preinit_ov9650) && !err; i++) {313u8 data = preinit_ov9650[i][2];314if (preinit_ov9650[i][0] == SENSOR)315err = m5602_write_sensor(sd,316preinit_ov9650[i][1], &data, 1);317else318err = m5602_write_bridge(sd,319preinit_ov9650[i][1], data);320}321322if (err < 0)323return err;324325if (m5602_read_sensor(sd, OV9650_PID, &prod_id, 1))326return -ENODEV;327328if (m5602_read_sensor(sd, OV9650_VER, &ver_id, 1))329return -ENODEV;330331if ((prod_id == 0x96) && (ver_id == 0x52)) {332info("Detected an ov9650 sensor");333goto sensor_found;334}335return -ENODEV;336337sensor_found:338sensor_settings = kmalloc(339ARRAY_SIZE(ov9650_ctrls) * sizeof(s32), GFP_KERNEL);340if (!sensor_settings)341return -ENOMEM;342343sd->gspca_dev.cam.cam_mode = ov9650_modes;344sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov9650_modes);345sd->desc->ctrls = ov9650_ctrls;346sd->desc->nctrls = ARRAY_SIZE(ov9650_ctrls);347348for (i = 0; i < ARRAY_SIZE(ov9650_ctrls); i++)349sensor_settings[i] = ov9650_ctrls[i].qctrl.default_value;350sd->sensor_priv = sensor_settings;351return 0;352}353354int ov9650_init(struct sd *sd)355{356int i, err = 0;357u8 data;358s32 *sensor_settings = sd->sensor_priv;359360if (dump_sensor)361ov9650_dump_registers(sd);362363for (i = 0; i < ARRAY_SIZE(init_ov9650) && !err; i++) {364data = init_ov9650[i][2];365if (init_ov9650[i][0] == SENSOR)366err = m5602_write_sensor(sd, init_ov9650[i][1],367&data, 1);368else369err = m5602_write_bridge(sd, init_ov9650[i][1], data);370}371372err = ov9650_set_exposure(&sd->gspca_dev,373sensor_settings[EXPOSURE_IDX]);374if (err < 0)375return err;376377err = ov9650_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);378if (err < 0)379return err;380381err = ov9650_set_red_balance(&sd->gspca_dev,382sensor_settings[RED_BALANCE_IDX]);383if (err < 0)384return err;385386err = ov9650_set_blue_balance(&sd->gspca_dev,387sensor_settings[BLUE_BALANCE_IDX]);388if (err < 0)389return err;390391err = ov9650_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]);392if (err < 0)393return err;394395err = ov9650_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]);396if (err < 0)397return err;398399err = ov9650_set_auto_exposure(&sd->gspca_dev,400sensor_settings[AUTO_EXPOSURE_IDX]);401if (err < 0)402return err;403404err = ov9650_set_auto_white_balance(&sd->gspca_dev,405sensor_settings[AUTO_WHITE_BALANCE_IDX]);406if (err < 0)407return err;408409err = ov9650_set_auto_gain(&sd->gspca_dev,410sensor_settings[AUTO_GAIN_CTRL_IDX]);411return err;412}413414int ov9650_start(struct sd *sd)415{416u8 data;417int i, err = 0;418struct cam *cam = &sd->gspca_dev.cam;419s32 *sensor_settings = sd->sensor_priv;420421int width = cam->cam_mode[sd->gspca_dev.curr_mode].width;422int height = cam->cam_mode[sd->gspca_dev.curr_mode].height;423int ver_offs = cam->cam_mode[sd->gspca_dev.curr_mode].priv;424int hor_offs = OV9650_LEFT_OFFSET;425426if ((!dmi_check_system(ov9650_flip_dmi_table) &&427sensor_settings[VFLIP_IDX]) ||428(dmi_check_system(ov9650_flip_dmi_table) &&429!sensor_settings[VFLIP_IDX]))430ver_offs--;431432if (width <= 320)433hor_offs /= 2;434435/* Synthesize the vsync/hsync setup */436for (i = 0; i < ARRAY_SIZE(res_init_ov9650) && !err; i++) {437if (res_init_ov9650[i][0] == BRIDGE)438err = m5602_write_bridge(sd, res_init_ov9650[i][1],439res_init_ov9650[i][2]);440else if (res_init_ov9650[i][0] == SENSOR) {441data = res_init_ov9650[i][2];442err = m5602_write_sensor(sd,443res_init_ov9650[i][1], &data, 1);444}445}446if (err < 0)447return err;448449err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA,450((ver_offs >> 8) & 0xff));451if (err < 0)452return err;453454err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (ver_offs & 0xff));455if (err < 0)456return err;457458err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);459if (err < 0)460return err;461462err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff);463if (err < 0)464return err;465466err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff));467if (err < 0)468return err;469470for (i = 0; i < 2 && !err; i++)471err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);472if (err < 0)473return err;474475err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);476if (err < 0)477return err;478479err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 2);480if (err < 0)481return err;482483err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,484(hor_offs >> 8) & 0xff);485if (err < 0)486return err;487488err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, hor_offs & 0xff);489if (err < 0)490return err;491492err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,493((width + hor_offs) >> 8) & 0xff);494if (err < 0)495return err;496497err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,498((width + hor_offs) & 0xff));499if (err < 0)500return err;501502err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);503if (err < 0)504return err;505506switch (width) {507case 640:508PDEBUG(D_V4L2, "Configuring camera for VGA mode");509510data = OV9650_VGA_SELECT | OV9650_RGB_SELECT |511OV9650_RAW_RGB_SELECT;512err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);513break;514515case 352:516PDEBUG(D_V4L2, "Configuring camera for CIF mode");517518data = OV9650_CIF_SELECT | OV9650_RGB_SELECT |519OV9650_RAW_RGB_SELECT;520err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);521break;522523case 320:524PDEBUG(D_V4L2, "Configuring camera for QVGA mode");525526data = OV9650_QVGA_SELECT | OV9650_RGB_SELECT |527OV9650_RAW_RGB_SELECT;528err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);529break;530531case 176:532PDEBUG(D_V4L2, "Configuring camera for QCIF mode");533534data = OV9650_QCIF_SELECT | OV9650_RGB_SELECT |535OV9650_RAW_RGB_SELECT;536err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);537break;538}539return err;540}541542int ov9650_stop(struct sd *sd)543{544u8 data = OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X;545return m5602_write_sensor(sd, OV9650_COM2, &data, 1);546}547548void ov9650_disconnect(struct sd *sd)549{550ov9650_stop(sd);551552sd->sensor = NULL;553kfree(sd->sensor_priv);554}555556static int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)557{558struct sd *sd = (struct sd *) gspca_dev;559s32 *sensor_settings = sd->sensor_priv;560561*val = sensor_settings[EXPOSURE_IDX];562PDEBUG(D_V4L2, "Read exposure %d", *val);563return 0;564}565566static int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val)567{568struct sd *sd = (struct sd *) gspca_dev;569s32 *sensor_settings = sd->sensor_priv;570u8 i2c_data;571int err;572573PDEBUG(D_V4L2, "Set exposure to %d", val);574575sensor_settings[EXPOSURE_IDX] = val;576/* The 6 MSBs */577i2c_data = (val >> 10) & 0x3f;578err = m5602_write_sensor(sd, OV9650_AECHM,579&i2c_data, 1);580if (err < 0)581return err;582583/* The 8 middle bits */584i2c_data = (val >> 2) & 0xff;585err = m5602_write_sensor(sd, OV9650_AECH,586&i2c_data, 1);587if (err < 0)588return err;589590/* The 2 LSBs */591i2c_data = val & 0x03;592err = m5602_write_sensor(sd, OV9650_COM1, &i2c_data, 1);593return err;594}595596static int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val)597{598struct sd *sd = (struct sd *) gspca_dev;599s32 *sensor_settings = sd->sensor_priv;600601*val = sensor_settings[GAIN_IDX];602PDEBUG(D_V4L2, "Read gain %d", *val);603return 0;604}605606static int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val)607{608int err;609u8 i2c_data;610struct sd *sd = (struct sd *) gspca_dev;611s32 *sensor_settings = sd->sensor_priv;612613PDEBUG(D_V4L2, "Setting gain to %d", val);614615sensor_settings[GAIN_IDX] = val;616617/* The 2 MSB */618/* Read the OV9650_VREF register first to avoid619corrupting the VREF high and low bits */620err = m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1);621if (err < 0)622return err;623624/* Mask away all uninteresting bits */625i2c_data = ((val & 0x0300) >> 2) |626(i2c_data & 0x3f);627err = m5602_write_sensor(sd, OV9650_VREF, &i2c_data, 1);628if (err < 0)629return err;630631/* The 8 LSBs */632i2c_data = val & 0xff;633err = m5602_write_sensor(sd, OV9650_GAIN, &i2c_data, 1);634return err;635}636637static int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)638{639struct sd *sd = (struct sd *) gspca_dev;640s32 *sensor_settings = sd->sensor_priv;641642*val = sensor_settings[RED_BALANCE_IDX];643PDEBUG(D_V4L2, "Read red gain %d", *val);644return 0;645}646647static int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)648{649int err;650u8 i2c_data;651struct sd *sd = (struct sd *) gspca_dev;652s32 *sensor_settings = sd->sensor_priv;653654PDEBUG(D_V4L2, "Set red gain to %d", val);655656sensor_settings[RED_BALANCE_IDX] = val;657658i2c_data = val & 0xff;659err = m5602_write_sensor(sd, OV9650_RED, &i2c_data, 1);660return err;661}662663static int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)664{665struct sd *sd = (struct sd *) gspca_dev;666s32 *sensor_settings = sd->sensor_priv;667668*val = sensor_settings[BLUE_BALANCE_IDX];669PDEBUG(D_V4L2, "Read blue gain %d", *val);670671return 0;672}673674static int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)675{676int err;677u8 i2c_data;678struct sd *sd = (struct sd *) gspca_dev;679s32 *sensor_settings = sd->sensor_priv;680681PDEBUG(D_V4L2, "Set blue gain to %d", val);682683sensor_settings[BLUE_BALANCE_IDX] = val;684685i2c_data = val & 0xff;686err = m5602_write_sensor(sd, OV9650_BLUE, &i2c_data, 1);687return err;688}689690static int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)691{692struct sd *sd = (struct sd *) gspca_dev;693s32 *sensor_settings = sd->sensor_priv;694695*val = sensor_settings[HFLIP_IDX];696PDEBUG(D_V4L2, "Read horizontal flip %d", *val);697return 0;698}699700static int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val)701{702int err;703u8 i2c_data;704struct sd *sd = (struct sd *) gspca_dev;705s32 *sensor_settings = sd->sensor_priv;706707PDEBUG(D_V4L2, "Set horizontal flip to %d", val);708709sensor_settings[HFLIP_IDX] = val;710711if (!dmi_check_system(ov9650_flip_dmi_table))712i2c_data = ((val & 0x01) << 5) |713(sensor_settings[VFLIP_IDX] << 4);714else715i2c_data = ((val & 0x01) << 5) |716(!sensor_settings[VFLIP_IDX] << 4);717718err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);719720return err;721}722723static int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)724{725struct sd *sd = (struct sd *) gspca_dev;726s32 *sensor_settings = sd->sensor_priv;727728*val = sensor_settings[VFLIP_IDX];729PDEBUG(D_V4L2, "Read vertical flip %d", *val);730731return 0;732}733734static int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val)735{736int err;737u8 i2c_data;738struct sd *sd = (struct sd *) gspca_dev;739s32 *sensor_settings = sd->sensor_priv;740741PDEBUG(D_V4L2, "Set vertical flip to %d", val);742sensor_settings[VFLIP_IDX] = val;743744if (dmi_check_system(ov9650_flip_dmi_table))745val = !val;746747i2c_data = ((val & 0x01) << 4) | (sensor_settings[VFLIP_IDX] << 5);748err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);749if (err < 0)750return err;751752/* When vflip is toggled we need to readjust the bridge hsync/vsync */753if (gspca_dev->streaming)754err = ov9650_start(sd);755756return err;757}758759static int ov9650_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val)760{761struct sd *sd = (struct sd *) gspca_dev;762s32 *sensor_settings = sd->sensor_priv;763764*val = sensor_settings[AUTO_EXPOSURE_IDX];765PDEBUG(D_V4L2, "Read auto exposure control %d", *val);766return 0;767}768769static int ov9650_set_auto_exposure(struct gspca_dev *gspca_dev,770__s32 val)771{772int err;773u8 i2c_data;774struct sd *sd = (struct sd *) gspca_dev;775s32 *sensor_settings = sd->sensor_priv;776777PDEBUG(D_V4L2, "Set auto exposure control to %d", val);778779sensor_settings[AUTO_EXPOSURE_IDX] = val;780err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);781if (err < 0)782return err;783784i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0));785786return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);787}788789static int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev,790__s32 *val)791{792struct sd *sd = (struct sd *) gspca_dev;793s32 *sensor_settings = sd->sensor_priv;794795*val = sensor_settings[AUTO_WHITE_BALANCE_IDX];796return 0;797}798799static int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev,800__s32 val)801{802int err;803u8 i2c_data;804struct sd *sd = (struct sd *) gspca_dev;805s32 *sensor_settings = sd->sensor_priv;806807PDEBUG(D_V4L2, "Set auto white balance to %d", val);808809sensor_settings[AUTO_WHITE_BALANCE_IDX] = val;810err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);811if (err < 0)812return err;813814i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1));815err = m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);816817return err;818}819820static int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val)821{822struct sd *sd = (struct sd *) gspca_dev;823s32 *sensor_settings = sd->sensor_priv;824825*val = sensor_settings[AUTO_GAIN_CTRL_IDX];826PDEBUG(D_V4L2, "Read auto gain control %d", *val);827return 0;828}829830static int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val)831{832int err;833u8 i2c_data;834struct sd *sd = (struct sd *) gspca_dev;835s32 *sensor_settings = sd->sensor_priv;836837PDEBUG(D_V4L2, "Set auto gain control to %d", val);838839sensor_settings[AUTO_GAIN_CTRL_IDX] = val;840err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);841if (err < 0)842return err;843844i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2));845846return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);847}848849static void ov9650_dump_registers(struct sd *sd)850{851int address;852info("Dumping the ov9650 register state");853for (address = 0; address < 0xa9; address++) {854u8 value;855m5602_read_sensor(sd, address, &value, 1);856info("register 0x%x contains 0x%x",857address, value);858}859860info("ov9650 register state dump complete");861862info("Probing for which registers that are read/write");863for (address = 0; address < 0xff; address++) {864u8 old_value, ctrl_value;865u8 test_value[2] = {0xff, 0xff};866867m5602_read_sensor(sd, address, &old_value, 1);868m5602_write_sensor(sd, address, test_value, 1);869m5602_read_sensor(sd, address, &ctrl_value, 1);870871if (ctrl_value == test_value[0])872info("register 0x%x is writeable", address);873else874info("register 0x%x is read only", address);875876/* Restore original value */877m5602_write_sensor(sd, address, &old_value, 1);878}879}880881882