Path: blob/master/drivers/media/video/gspca/m5602/m5602_po1030.c
17988 views
/*1* Driver for the po1030 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_po1030.h"1920static int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);21static int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val);22static int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val);23static int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val);24static int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val);25static int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val);26static int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val);27static int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val);28static int po1030_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val);29static int po1030_set_green_balance(struct gspca_dev *gspca_dev, __s32 val);30static int po1030_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);31static int po1030_set_hflip(struct gspca_dev *gspca_dev, __s32 val);32static int po1030_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);33static int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val);34static int po1030_set_auto_white_balance(struct gspca_dev *gspca_dev,35__s32 val);36static int po1030_get_auto_white_balance(struct gspca_dev *gspca_dev,37__s32 *val);38static int po1030_set_auto_exposure(struct gspca_dev *gspca_dev,39__s32 val);40static int po1030_get_auto_exposure(struct gspca_dev *gspca_dev,41__s32 *val);4243static struct v4l2_pix_format po1030_modes[] = {44{45640,46480,47V4L2_PIX_FMT_SBGGR8,48V4L2_FIELD_NONE,49.sizeimage = 640 * 480,50.bytesperline = 640,51.colorspace = V4L2_COLORSPACE_SRGB,52.priv = 253}54};5556static const struct ctrl po1030_ctrls[] = {57#define GAIN_IDX 058{59{60.id = V4L2_CID_GAIN,61.type = V4L2_CTRL_TYPE_INTEGER,62.name = "gain",63.minimum = 0x00,64.maximum = 0x4f,65.step = 0x1,66.default_value = PO1030_GLOBAL_GAIN_DEFAULT,67.flags = V4L2_CTRL_FLAG_SLIDER68},69.set = po1030_set_gain,70.get = po1030_get_gain71},72#define EXPOSURE_IDX 173{74{75.id = V4L2_CID_EXPOSURE,76.type = V4L2_CTRL_TYPE_INTEGER,77.name = "exposure",78.minimum = 0x00,79.maximum = 0x02ff,80.step = 0x1,81.default_value = PO1030_EXPOSURE_DEFAULT,82.flags = V4L2_CTRL_FLAG_SLIDER83},84.set = po1030_set_exposure,85.get = po1030_get_exposure86},87#define RED_BALANCE_IDX 288{89{90.id = V4L2_CID_RED_BALANCE,91.type = V4L2_CTRL_TYPE_INTEGER,92.name = "red balance",93.minimum = 0x00,94.maximum = 0xff,95.step = 0x1,96.default_value = PO1030_RED_GAIN_DEFAULT,97.flags = V4L2_CTRL_FLAG_SLIDER98},99.set = po1030_set_red_balance,100.get = po1030_get_red_balance101},102#define BLUE_BALANCE_IDX 3103{104{105.id = V4L2_CID_BLUE_BALANCE,106.type = V4L2_CTRL_TYPE_INTEGER,107.name = "blue balance",108.minimum = 0x00,109.maximum = 0xff,110.step = 0x1,111.default_value = PO1030_BLUE_GAIN_DEFAULT,112.flags = V4L2_CTRL_FLAG_SLIDER113},114.set = po1030_set_blue_balance,115.get = po1030_get_blue_balance116},117#define HFLIP_IDX 4118{119{120.id = V4L2_CID_HFLIP,121.type = V4L2_CTRL_TYPE_BOOLEAN,122.name = "horizontal flip",123.minimum = 0,124.maximum = 1,125.step = 1,126.default_value = 0,127},128.set = po1030_set_hflip,129.get = po1030_get_hflip130},131#define VFLIP_IDX 5132{133{134.id = V4L2_CID_VFLIP,135.type = V4L2_CTRL_TYPE_BOOLEAN,136.name = "vertical flip",137.minimum = 0,138.maximum = 1,139.step = 1,140.default_value = 0,141},142.set = po1030_set_vflip,143.get = po1030_get_vflip144},145#define AUTO_WHITE_BALANCE_IDX 6146{147{148.id = V4L2_CID_AUTO_WHITE_BALANCE,149.type = V4L2_CTRL_TYPE_BOOLEAN,150.name = "auto white balance",151.minimum = 0,152.maximum = 1,153.step = 1,154.default_value = 0,155},156.set = po1030_set_auto_white_balance,157.get = po1030_get_auto_white_balance158},159#define AUTO_EXPOSURE_IDX 7160{161{162.id = V4L2_CID_EXPOSURE_AUTO,163.type = V4L2_CTRL_TYPE_BOOLEAN,164.name = "auto exposure",165.minimum = 0,166.maximum = 1,167.step = 1,168.default_value = 0,169},170.set = po1030_set_auto_exposure,171.get = po1030_get_auto_exposure172},173#define GREEN_BALANCE_IDX 8174{175{176.id = M5602_V4L2_CID_GREEN_BALANCE,177.type = V4L2_CTRL_TYPE_INTEGER,178.name = "green balance",179.minimum = 0x00,180.maximum = 0xff,181.step = 0x1,182.default_value = PO1030_GREEN_GAIN_DEFAULT,183.flags = V4L2_CTRL_FLAG_SLIDER184},185.set = po1030_set_green_balance,186.get = po1030_get_green_balance187},188};189190static void po1030_dump_registers(struct sd *sd);191192int po1030_probe(struct sd *sd)193{194u8 dev_id_h = 0, i;195s32 *sensor_settings;196197if (force_sensor) {198if (force_sensor == PO1030_SENSOR) {199info("Forcing a %s sensor", po1030.name);200goto sensor_found;201}202/* If we want to force another sensor, don't try to probe this203* one */204return -ENODEV;205}206207PDEBUG(D_PROBE, "Probing for a po1030 sensor");208209/* Run the pre-init to actually probe the unit */210for (i = 0; i < ARRAY_SIZE(preinit_po1030); i++) {211u8 data = preinit_po1030[i][2];212if (preinit_po1030[i][0] == SENSOR)213m5602_write_sensor(sd,214preinit_po1030[i][1], &data, 1);215else216m5602_write_bridge(sd, preinit_po1030[i][1], data);217}218219if (m5602_read_sensor(sd, PO1030_DEVID_H, &dev_id_h, 1))220return -ENODEV;221222if (dev_id_h == 0x30) {223info("Detected a po1030 sensor");224goto sensor_found;225}226return -ENODEV;227228sensor_found:229sensor_settings = kmalloc(230ARRAY_SIZE(po1030_ctrls) * sizeof(s32), GFP_KERNEL);231if (!sensor_settings)232return -ENOMEM;233234sd->gspca_dev.cam.cam_mode = po1030_modes;235sd->gspca_dev.cam.nmodes = ARRAY_SIZE(po1030_modes);236sd->desc->ctrls = po1030_ctrls;237sd->desc->nctrls = ARRAY_SIZE(po1030_ctrls);238239for (i = 0; i < ARRAY_SIZE(po1030_ctrls); i++)240sensor_settings[i] = po1030_ctrls[i].qctrl.default_value;241sd->sensor_priv = sensor_settings;242243return 0;244}245246int po1030_init(struct sd *sd)247{248s32 *sensor_settings = sd->sensor_priv;249int i, err = 0;250251/* Init the sensor */252for (i = 0; i < ARRAY_SIZE(init_po1030) && !err; i++) {253u8 data[2] = {0x00, 0x00};254255switch (init_po1030[i][0]) {256case BRIDGE:257err = m5602_write_bridge(sd,258init_po1030[i][1],259init_po1030[i][2]);260break;261262case SENSOR:263data[0] = init_po1030[i][2];264err = m5602_write_sensor(sd,265init_po1030[i][1], data, 1);266break;267268default:269info("Invalid stream command, exiting init");270return -EINVAL;271}272}273if (err < 0)274return err;275276if (dump_sensor)277po1030_dump_registers(sd);278279err = po1030_set_exposure(&sd->gspca_dev,280sensor_settings[EXPOSURE_IDX]);281if (err < 0)282return err;283284err = po1030_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);285if (err < 0)286return err;287288err = po1030_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]);289if (err < 0)290return err;291292err = po1030_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]);293if (err < 0)294return err;295296err = po1030_set_red_balance(&sd->gspca_dev,297sensor_settings[RED_BALANCE_IDX]);298if (err < 0)299return err;300301err = po1030_set_blue_balance(&sd->gspca_dev,302sensor_settings[BLUE_BALANCE_IDX]);303if (err < 0)304return err;305306err = po1030_set_green_balance(&sd->gspca_dev,307sensor_settings[GREEN_BALANCE_IDX]);308if (err < 0)309return err;310311err = po1030_set_auto_white_balance(&sd->gspca_dev,312sensor_settings[AUTO_WHITE_BALANCE_IDX]);313if (err < 0)314return err;315316err = po1030_set_auto_exposure(&sd->gspca_dev,317sensor_settings[AUTO_EXPOSURE_IDX]);318return err;319}320321int po1030_start(struct sd *sd)322{323struct cam *cam = &sd->gspca_dev.cam;324int i, err = 0;325int width = cam->cam_mode[sd->gspca_dev.curr_mode].width;326int height = cam->cam_mode[sd->gspca_dev.curr_mode].height;327int ver_offs = cam->cam_mode[sd->gspca_dev.curr_mode].priv;328u8 data;329330switch (width) {331case 320:332data = PO1030_SUBSAMPLING;333err = m5602_write_sensor(sd, PO1030_CONTROL3, &data, 1);334if (err < 0)335return err;336337data = ((width + 3) >> 8) & 0xff;338err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_H, &data, 1);339if (err < 0)340return err;341342data = (width + 3) & 0xff;343err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_L, &data, 1);344if (err < 0)345return err;346347data = ((height + 1) >> 8) & 0xff;348err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_H, &data, 1);349if (err < 0)350return err;351352data = (height + 1) & 0xff;353err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_L, &data, 1);354355height += 6;356width -= 1;357break;358359case 640:360data = 0;361err = m5602_write_sensor(sd, PO1030_CONTROL3, &data, 1);362if (err < 0)363return err;364365data = ((width + 7) >> 8) & 0xff;366err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_H, &data, 1);367if (err < 0)368return err;369370data = (width + 7) & 0xff;371err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_L, &data, 1);372if (err < 0)373return err;374375data = ((height + 3) >> 8) & 0xff;376err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_H, &data, 1);377if (err < 0)378return err;379380data = (height + 3) & 0xff;381err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_L, &data, 1);382383height += 12;384width -= 2;385break;386}387err = m5602_write_bridge(sd, M5602_XB_SENSOR_TYPE, 0x0c);388if (err < 0)389return err;390391err = m5602_write_bridge(sd, M5602_XB_LINE_OF_FRAME_H, 0x81);392if (err < 0)393return err;394395err = m5602_write_bridge(sd, M5602_XB_PIX_OF_LINE_H, 0x82);396if (err < 0)397return err;398399err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0x01);400if (err < 0)401return err;402403err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA,404((ver_offs >> 8) & 0xff));405if (err < 0)406return err;407408err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (ver_offs & 0xff));409if (err < 0)410return err;411412for (i = 0; i < 2 && !err; i++)413err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);414if (err < 0)415return err;416417err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff);418if (err < 0)419return err;420421err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff));422if (err < 0)423return err;424425for (i = 0; i < 2 && !err; i++)426err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);427428for (i = 0; i < 2 && !err; i++)429err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);430431for (i = 0; i < 2 && !err; i++)432err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, 0);433if (err < 0)434return err;435436err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, (width >> 8) & 0xff);437if (err < 0)438return err;439440err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, (width & 0xff));441if (err < 0)442return err;443444err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);445return err;446}447448static int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)449{450struct sd *sd = (struct sd *) gspca_dev;451s32 *sensor_settings = sd->sensor_priv;452453*val = sensor_settings[EXPOSURE_IDX];454PDEBUG(D_V4L2, "Exposure read as %d", *val);455return 0;456}457458static int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val)459{460struct sd *sd = (struct sd *) gspca_dev;461s32 *sensor_settings = sd->sensor_priv;462u8 i2c_data;463int err;464465sensor_settings[EXPOSURE_IDX] = val;466PDEBUG(D_V4L2, "Set exposure to %d", val & 0xffff);467468i2c_data = ((val & 0xff00) >> 8);469PDEBUG(D_V4L2, "Set exposure to high byte to 0x%x",470i2c_data);471472err = m5602_write_sensor(sd, PO1030_INTEGLINES_H,473&i2c_data, 1);474if (err < 0)475return err;476477i2c_data = (val & 0xff);478PDEBUG(D_V4L2, "Set exposure to low byte to 0x%x",479i2c_data);480err = m5602_write_sensor(sd, PO1030_INTEGLINES_M,481&i2c_data, 1);482483return err;484}485486static int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val)487{488struct sd *sd = (struct sd *) gspca_dev;489s32 *sensor_settings = sd->sensor_priv;490491*val = sensor_settings[GAIN_IDX];492PDEBUG(D_V4L2, "Read global gain %d", *val);493return 0;494}495496static int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val)497{498struct sd *sd = (struct sd *) gspca_dev;499s32 *sensor_settings = sd->sensor_priv;500u8 i2c_data;501int err;502503sensor_settings[GAIN_IDX] = val;504505i2c_data = val & 0xff;506PDEBUG(D_V4L2, "Set global gain to %d", i2c_data);507err = m5602_write_sensor(sd, PO1030_GLOBALGAIN,508&i2c_data, 1);509return err;510}511512static int po1030_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)513{514struct sd *sd = (struct sd *) gspca_dev;515s32 *sensor_settings = sd->sensor_priv;516517*val = sensor_settings[HFLIP_IDX];518PDEBUG(D_V4L2, "Read hflip %d", *val);519520return 0;521}522523static int po1030_set_hflip(struct gspca_dev *gspca_dev, __s32 val)524{525struct sd *sd = (struct sd *) gspca_dev;526s32 *sensor_settings = sd->sensor_priv;527u8 i2c_data;528int err;529530sensor_settings[HFLIP_IDX] = val;531532PDEBUG(D_V4L2, "Set hflip %d", val);533err = m5602_read_sensor(sd, PO1030_CONTROL2, &i2c_data, 1);534if (err < 0)535return err;536537i2c_data = (0x7f & i2c_data) | ((val & 0x01) << 7);538539err = m5602_write_sensor(sd, PO1030_CONTROL2,540&i2c_data, 1);541542return err;543}544545static int po1030_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)546{547struct sd *sd = (struct sd *) gspca_dev;548s32 *sensor_settings = sd->sensor_priv;549550*val = sensor_settings[VFLIP_IDX];551PDEBUG(D_V4L2, "Read vflip %d", *val);552553return 0;554}555556static int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val)557{558struct sd *sd = (struct sd *) gspca_dev;559s32 *sensor_settings = sd->sensor_priv;560u8 i2c_data;561int err;562563sensor_settings[VFLIP_IDX] = val;564565PDEBUG(D_V4L2, "Set vflip %d", val);566err = m5602_read_sensor(sd, PO1030_CONTROL2, &i2c_data, 1);567if (err < 0)568return err;569570i2c_data = (i2c_data & 0xbf) | ((val & 0x01) << 6);571572err = m5602_write_sensor(sd, PO1030_CONTROL2,573&i2c_data, 1);574575return err;576}577578static int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)579{580struct sd *sd = (struct sd *) gspca_dev;581s32 *sensor_settings = sd->sensor_priv;582583*val = sensor_settings[RED_BALANCE_IDX];584PDEBUG(D_V4L2, "Read red gain %d", *val);585return 0;586}587588static int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)589{590struct sd *sd = (struct sd *) gspca_dev;591s32 *sensor_settings = sd->sensor_priv;592u8 i2c_data;593int err;594595sensor_settings[RED_BALANCE_IDX] = val;596597i2c_data = val & 0xff;598PDEBUG(D_V4L2, "Set red gain to %d", i2c_data);599err = m5602_write_sensor(sd, PO1030_RED_GAIN,600&i2c_data, 1);601return err;602}603604static int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)605{606struct sd *sd = (struct sd *) gspca_dev;607s32 *sensor_settings = sd->sensor_priv;608609*val = sensor_settings[BLUE_BALANCE_IDX];610PDEBUG(D_V4L2, "Read blue gain %d", *val);611612return 0;613}614615static int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)616{617struct sd *sd = (struct sd *) gspca_dev;618s32 *sensor_settings = sd->sensor_priv;619u8 i2c_data;620int err;621622sensor_settings[BLUE_BALANCE_IDX] = val;623624i2c_data = val & 0xff;625PDEBUG(D_V4L2, "Set blue gain to %d", i2c_data);626err = m5602_write_sensor(sd, PO1030_BLUE_GAIN,627&i2c_data, 1);628629return err;630}631632static int po1030_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val)633{634struct sd *sd = (struct sd *) gspca_dev;635s32 *sensor_settings = sd->sensor_priv;636637*val = sensor_settings[GREEN_BALANCE_IDX];638PDEBUG(D_V4L2, "Read green gain %d", *val);639640return 0;641}642643static int po1030_set_green_balance(struct gspca_dev *gspca_dev, __s32 val)644{645struct sd *sd = (struct sd *) gspca_dev;646s32 *sensor_settings = sd->sensor_priv;647u8 i2c_data;648int err;649650sensor_settings[GREEN_BALANCE_IDX] = val;651i2c_data = val & 0xff;652PDEBUG(D_V4L2, "Set green gain to %d", i2c_data);653654err = m5602_write_sensor(sd, PO1030_GREEN_1_GAIN,655&i2c_data, 1);656if (err < 0)657return err;658659return m5602_write_sensor(sd, PO1030_GREEN_2_GAIN,660&i2c_data, 1);661}662663static int po1030_get_auto_white_balance(struct gspca_dev *gspca_dev,664__s32 *val)665{666struct sd *sd = (struct sd *) gspca_dev;667s32 *sensor_settings = sd->sensor_priv;668669*val = sensor_settings[AUTO_WHITE_BALANCE_IDX];670PDEBUG(D_V4L2, "Auto white balancing is %d", *val);671672return 0;673}674675static int po1030_set_auto_white_balance(struct gspca_dev *gspca_dev,676__s32 val)677{678struct sd *sd = (struct sd *) gspca_dev;679s32 *sensor_settings = sd->sensor_priv;680u8 i2c_data;681int err;682683sensor_settings[AUTO_WHITE_BALANCE_IDX] = val;684685err = m5602_read_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1);686if (err < 0)687return err;688689PDEBUG(D_V4L2, "Set auto white balance to %d", val);690i2c_data = (i2c_data & 0xfe) | (val & 0x01);691err = m5602_write_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1);692return err;693}694695static int po1030_get_auto_exposure(struct gspca_dev *gspca_dev,696__s32 *val)697{698struct sd *sd = (struct sd *) gspca_dev;699s32 *sensor_settings = sd->sensor_priv;700701*val = sensor_settings[AUTO_EXPOSURE_IDX];702PDEBUG(D_V4L2, "Auto exposure is %d", *val);703return 0;704}705706static int po1030_set_auto_exposure(struct gspca_dev *gspca_dev,707__s32 val)708{709struct sd *sd = (struct sd *) gspca_dev;710s32 *sensor_settings = sd->sensor_priv;711u8 i2c_data;712int err;713714sensor_settings[AUTO_EXPOSURE_IDX] = val;715err = m5602_read_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1);716if (err < 0)717return err;718719PDEBUG(D_V4L2, "Set auto exposure to %d", val);720i2c_data = (i2c_data & 0xfd) | ((val & 0x01) << 1);721return m5602_write_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1);722}723724void po1030_disconnect(struct sd *sd)725{726sd->sensor = NULL;727kfree(sd->sensor_priv);728}729730static void po1030_dump_registers(struct sd *sd)731{732int address;733u8 value = 0;734735info("Dumping the po1030 sensor core registers");736for (address = 0; address < 0x7f; address++) {737m5602_read_sensor(sd, address, &value, 1);738info("register 0x%x contains 0x%x",739address, value);740}741742info("po1030 register state dump complete");743744info("Probing for which registers that are read/write");745for (address = 0; address < 0xff; address++) {746u8 old_value, ctrl_value;747u8 test_value[2] = {0xff, 0xff};748749m5602_read_sensor(sd, address, &old_value, 1);750m5602_write_sensor(sd, address, test_value, 1);751m5602_read_sensor(sd, address, &ctrl_value, 1);752753if (ctrl_value == test_value[0])754info("register 0x%x is writeable", address);755else756info("register 0x%x is read only", address);757758/* Restore original value */759m5602_write_sensor(sd, address, &old_value, 1);760}761}762763764