Path: blob/master/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
17900 views
/*1* Driver for the s5k4aa 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_s5k4aa.h"1920static int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);21static int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val);22static int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);23static int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val);24static int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);25static int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val);26static int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val);27static int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val);28static int s5k4aa_get_noise(struct gspca_dev *gspca_dev, __s32 *val);29static int s5k4aa_set_noise(struct gspca_dev *gspca_dev, __s32 val);30static int s5k4aa_get_brightness(struct gspca_dev *gspca_dev, __s32 *val);31static int s5k4aa_set_brightness(struct gspca_dev *gspca_dev, __s32 val);3233static34const35struct dmi_system_id s5k4aa_vflip_dmi_table[] = {36{37.ident = "BRUNEINIT",38.matches = {39DMI_MATCH(DMI_SYS_VENDOR, "BRUNENIT"),40DMI_MATCH(DMI_PRODUCT_NAME, "BRUNENIT"),41DMI_MATCH(DMI_BOARD_VERSION, "00030D0000000001")42}43}, {44.ident = "Fujitsu-Siemens Amilo Xa 2528",45.matches = {46DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),47DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xa 2528")48}49}, {50.ident = "Fujitsu-Siemens Amilo Xi 2428",51.matches = {52DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),53DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2428")54}55}, {56.ident = "Fujitsu-Siemens Amilo Xi 2528",57.matches = {58DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),59DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2528")60}61}, {62.ident = "Fujitsu-Siemens Amilo Xi 2550",63.matches = {64DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),65DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2550")66}67}, {68.ident = "Fujitsu-Siemens Amilo Pa 2548",69.matches = {70DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),71DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 2548")72}73}, {74.ident = "MSI GX700",75.matches = {76DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),77DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),78DMI_MATCH(DMI_BIOS_DATE, "12/02/2008")79}80}, {81.ident = "MSI GX700",82.matches = {83DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),84DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),85DMI_MATCH(DMI_BIOS_DATE, "07/26/2007")86}87}, {88.ident = "MSI GX700",89.matches = {90DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),91DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),92DMI_MATCH(DMI_BIOS_DATE, "07/19/2007")93}94}, {95.ident = "MSI GX700/GX705/EX700",96.matches = {97DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),98DMI_MATCH(DMI_PRODUCT_NAME, "GX700/GX705/EX700")99}100}, {101.ident = "MSI L735",102.matches = {103DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),104DMI_MATCH(DMI_PRODUCT_NAME, "MS-1717X")105}106}, {107.ident = "Lenovo Y300",108.matches = {109DMI_MATCH(DMI_SYS_VENDOR, "L3000 Y300"),110DMI_MATCH(DMI_PRODUCT_NAME, "Y300")111}112},113{ }114};115116static struct v4l2_pix_format s5k4aa_modes[] = {117{118640,119480,120V4L2_PIX_FMT_SBGGR8,121V4L2_FIELD_NONE,122.sizeimage =123640 * 480,124.bytesperline = 640,125.colorspace = V4L2_COLORSPACE_SRGB,126.priv = 0127},128{1291280,1301024,131V4L2_PIX_FMT_SBGGR8,132V4L2_FIELD_NONE,133.sizeimage =1341280 * 1024,135.bytesperline = 1280,136.colorspace = V4L2_COLORSPACE_SRGB,137.priv = 0138}139};140141static const struct ctrl s5k4aa_ctrls[] = {142#define VFLIP_IDX 0143{144{145.id = V4L2_CID_VFLIP,146.type = V4L2_CTRL_TYPE_BOOLEAN,147.name = "vertical flip",148.minimum = 0,149.maximum = 1,150.step = 1,151.default_value = 0152},153.set = s5k4aa_set_vflip,154.get = s5k4aa_get_vflip155},156#define HFLIP_IDX 1157{158{159.id = V4L2_CID_HFLIP,160.type = V4L2_CTRL_TYPE_BOOLEAN,161.name = "horizontal flip",162.minimum = 0,163.maximum = 1,164.step = 1,165.default_value = 0166},167.set = s5k4aa_set_hflip,168.get = s5k4aa_get_hflip169},170#define GAIN_IDX 2171{172{173.id = V4L2_CID_GAIN,174.type = V4L2_CTRL_TYPE_INTEGER,175.name = "Gain",176.minimum = 0,177.maximum = 127,178.step = 1,179.default_value = S5K4AA_DEFAULT_GAIN,180.flags = V4L2_CTRL_FLAG_SLIDER181},182.set = s5k4aa_set_gain,183.get = s5k4aa_get_gain184},185#define EXPOSURE_IDX 3186{187{188.id = V4L2_CID_EXPOSURE,189.type = V4L2_CTRL_TYPE_INTEGER,190.name = "Exposure",191.minimum = 13,192.maximum = 0xfff,193.step = 1,194.default_value = 0x100,195.flags = V4L2_CTRL_FLAG_SLIDER196},197.set = s5k4aa_set_exposure,198.get = s5k4aa_get_exposure199},200#define NOISE_SUPP_IDX 4201{202{203.id = V4L2_CID_PRIVATE_BASE,204.type = V4L2_CTRL_TYPE_BOOLEAN,205.name = "Noise suppression (smoothing)",206.minimum = 0,207.maximum = 1,208.step = 1,209.default_value = 1,210},211.set = s5k4aa_set_noise,212.get = s5k4aa_get_noise213},214#define BRIGHTNESS_IDX 5215{216{217.id = V4L2_CID_BRIGHTNESS,218.type = V4L2_CTRL_TYPE_INTEGER,219.name = "Brightness",220.minimum = 0,221.maximum = 0x1f,222.step = 1,223.default_value = S5K4AA_DEFAULT_BRIGHTNESS,224},225.set = s5k4aa_set_brightness,226.get = s5k4aa_get_brightness227},228229};230231static void s5k4aa_dump_registers(struct sd *sd);232233int s5k4aa_probe(struct sd *sd)234{235u8 prod_id[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};236const u8 expected_prod_id[6] = {0x00, 0x10, 0x00, 0x4b, 0x33, 0x75};237int i, err = 0;238s32 *sensor_settings;239240if (force_sensor) {241if (force_sensor == S5K4AA_SENSOR) {242info("Forcing a %s sensor", s5k4aa.name);243goto sensor_found;244}245/* If we want to force another sensor, don't try to probe this246* one */247return -ENODEV;248}249250PDEBUG(D_PROBE, "Probing for a s5k4aa sensor");251252/* Preinit the sensor */253for (i = 0; i < ARRAY_SIZE(preinit_s5k4aa) && !err; i++) {254u8 data[2] = {0x00, 0x00};255256switch (preinit_s5k4aa[i][0]) {257case BRIDGE:258err = m5602_write_bridge(sd,259preinit_s5k4aa[i][1],260preinit_s5k4aa[i][2]);261break;262263case SENSOR:264data[0] = preinit_s5k4aa[i][2];265err = m5602_write_sensor(sd,266preinit_s5k4aa[i][1],267data, 1);268break;269270case SENSOR_LONG:271data[0] = preinit_s5k4aa[i][2];272data[1] = preinit_s5k4aa[i][3];273err = m5602_write_sensor(sd,274preinit_s5k4aa[i][1],275data, 2);276break;277default:278info("Invalid stream command, exiting init");279return -EINVAL;280}281}282283/* Test some registers, but we don't know their exact meaning yet */284if (m5602_read_sensor(sd, 0x00, prod_id, 2))285return -ENODEV;286if (m5602_read_sensor(sd, 0x02, prod_id+2, 2))287return -ENODEV;288if (m5602_read_sensor(sd, 0x04, prod_id+4, 2))289return -ENODEV;290291if (memcmp(prod_id, expected_prod_id, sizeof(prod_id)))292return -ENODEV;293else294info("Detected a s5k4aa sensor");295296sensor_found:297sensor_settings = kmalloc(298ARRAY_SIZE(s5k4aa_ctrls) * sizeof(s32), GFP_KERNEL);299if (!sensor_settings)300return -ENOMEM;301302sd->gspca_dev.cam.cam_mode = s5k4aa_modes;303sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k4aa_modes);304sd->desc->ctrls = s5k4aa_ctrls;305sd->desc->nctrls = ARRAY_SIZE(s5k4aa_ctrls);306307for (i = 0; i < ARRAY_SIZE(s5k4aa_ctrls); i++)308sensor_settings[i] = s5k4aa_ctrls[i].qctrl.default_value;309sd->sensor_priv = sensor_settings;310311return 0;312}313314int s5k4aa_start(struct sd *sd)315{316int i, err = 0;317u8 data[2];318struct cam *cam = &sd->gspca_dev.cam;319s32 *sensor_settings = sd->sensor_priv;320321switch (cam->cam_mode[sd->gspca_dev.curr_mode].width) {322case 1280:323PDEBUG(D_V4L2, "Configuring camera for SXGA mode");324325for (i = 0; i < ARRAY_SIZE(SXGA_s5k4aa); i++) {326switch (SXGA_s5k4aa[i][0]) {327case BRIDGE:328err = m5602_write_bridge(sd,329SXGA_s5k4aa[i][1],330SXGA_s5k4aa[i][2]);331break;332333case SENSOR:334data[0] = SXGA_s5k4aa[i][2];335err = m5602_write_sensor(sd,336SXGA_s5k4aa[i][1],337data, 1);338break;339340case SENSOR_LONG:341data[0] = SXGA_s5k4aa[i][2];342data[1] = SXGA_s5k4aa[i][3];343err = m5602_write_sensor(sd,344SXGA_s5k4aa[i][1],345data, 2);346break;347348default:349err("Invalid stream command, exiting init");350return -EINVAL;351}352}353err = s5k4aa_set_noise(&sd->gspca_dev, 0);354if (err < 0)355return err;356break;357358case 640:359PDEBUG(D_V4L2, "Configuring camera for VGA mode");360361for (i = 0; i < ARRAY_SIZE(VGA_s5k4aa); i++) {362switch (VGA_s5k4aa[i][0]) {363case BRIDGE:364err = m5602_write_bridge(sd,365VGA_s5k4aa[i][1],366VGA_s5k4aa[i][2]);367break;368369case SENSOR:370data[0] = VGA_s5k4aa[i][2];371err = m5602_write_sensor(sd,372VGA_s5k4aa[i][1],373data, 1);374break;375376case SENSOR_LONG:377data[0] = VGA_s5k4aa[i][2];378data[1] = VGA_s5k4aa[i][3];379err = m5602_write_sensor(sd,380VGA_s5k4aa[i][1],381data, 2);382break;383384default:385err("Invalid stream command, exiting init");386return -EINVAL;387}388}389err = s5k4aa_set_noise(&sd->gspca_dev, 1);390if (err < 0)391return err;392break;393}394if (err < 0)395return err;396397err = s5k4aa_set_exposure(&sd->gspca_dev,398sensor_settings[EXPOSURE_IDX]);399if (err < 0)400return err;401402err = s5k4aa_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);403if (err < 0)404return err;405406err = s5k4aa_set_brightness(&sd->gspca_dev,407sensor_settings[BRIGHTNESS_IDX]);408if (err < 0)409return err;410411err = s5k4aa_set_noise(&sd->gspca_dev, sensor_settings[NOISE_SUPP_IDX]);412if (err < 0)413return err;414415err = s5k4aa_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]);416if (err < 0)417return err;418419return s5k4aa_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]);420}421422int s5k4aa_init(struct sd *sd)423{424int i, err = 0;425426for (i = 0; i < ARRAY_SIZE(init_s5k4aa) && !err; i++) {427u8 data[2] = {0x00, 0x00};428429switch (init_s5k4aa[i][0]) {430case BRIDGE:431err = m5602_write_bridge(sd,432init_s5k4aa[i][1],433init_s5k4aa[i][2]);434break;435436case SENSOR:437data[0] = init_s5k4aa[i][2];438err = m5602_write_sensor(sd,439init_s5k4aa[i][1], data, 1);440break;441442case SENSOR_LONG:443data[0] = init_s5k4aa[i][2];444data[1] = init_s5k4aa[i][3];445err = m5602_write_sensor(sd,446init_s5k4aa[i][1], data, 2);447break;448default:449info("Invalid stream command, exiting init");450return -EINVAL;451}452}453454if (dump_sensor)455s5k4aa_dump_registers(sd);456457return err;458}459460static int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)461{462struct sd *sd = (struct sd *) gspca_dev;463s32 *sensor_settings = sd->sensor_priv;464465*val = sensor_settings[EXPOSURE_IDX];466PDEBUG(D_V4L2, "Read exposure %d", *val);467468return 0;469}470471static int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val)472{473struct sd *sd = (struct sd *) gspca_dev;474s32 *sensor_settings = sd->sensor_priv;475u8 data = S5K4AA_PAGE_MAP_2;476int err;477478sensor_settings[EXPOSURE_IDX] = val;479PDEBUG(D_V4L2, "Set exposure to %d", val);480err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);481if (err < 0)482return err;483data = (val >> 8) & 0xff;484err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);485if (err < 0)486return err;487data = val & 0xff;488err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);489490return err;491}492493static int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)494{495struct sd *sd = (struct sd *) gspca_dev;496s32 *sensor_settings = sd->sensor_priv;497498*val = sensor_settings[VFLIP_IDX];499PDEBUG(D_V4L2, "Read vertical flip %d", *val);500501return 0;502}503504static int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val)505{506struct sd *sd = (struct sd *) gspca_dev;507s32 *sensor_settings = sd->sensor_priv;508u8 data = S5K4AA_PAGE_MAP_2;509int err;510511sensor_settings[VFLIP_IDX] = val;512513PDEBUG(D_V4L2, "Set vertical flip to %d", val);514err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);515if (err < 0)516return err;517518err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1);519if (err < 0)520return err;521522if (dmi_check_system(s5k4aa_vflip_dmi_table))523val = !val;524525data = ((data & ~S5K4AA_RM_V_FLIP) | ((val & 0x01) << 7));526err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);527if (err < 0)528return err;529530err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);531if (err < 0)532return err;533if (val)534data &= 0xfe;535else536data |= 0x01;537err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);538return err;539}540541static int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)542{543struct sd *sd = (struct sd *) gspca_dev;544s32 *sensor_settings = sd->sensor_priv;545546*val = sensor_settings[HFLIP_IDX];547PDEBUG(D_V4L2, "Read horizontal flip %d", *val);548549return 0;550}551552static int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val)553{554struct sd *sd = (struct sd *) gspca_dev;555s32 *sensor_settings = sd->sensor_priv;556u8 data = S5K4AA_PAGE_MAP_2;557int err;558559sensor_settings[HFLIP_IDX] = val;560561PDEBUG(D_V4L2, "Set horizontal flip to %d", val);562err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);563if (err < 0)564return err;565566err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1);567if (err < 0)568return err;569570if (dmi_check_system(s5k4aa_vflip_dmi_table))571val = !val;572573data = ((data & ~S5K4AA_RM_H_FLIP) | ((val & 0x01) << 6));574err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);575if (err < 0)576return err;577578err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);579if (err < 0)580return err;581if (val)582data &= 0xfe;583else584data |= 0x01;585err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);586return err;587}588589static int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val)590{591struct sd *sd = (struct sd *) gspca_dev;592s32 *sensor_settings = sd->sensor_priv;593594*val = sensor_settings[GAIN_IDX];595PDEBUG(D_V4L2, "Read gain %d", *val);596return 0;597}598599static int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val)600{601struct sd *sd = (struct sd *) gspca_dev;602s32 *sensor_settings = sd->sensor_priv;603u8 data = S5K4AA_PAGE_MAP_2;604int err;605606sensor_settings[GAIN_IDX] = val;607608PDEBUG(D_V4L2, "Set gain to %d", val);609err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);610if (err < 0)611return err;612613data = val & 0xff;614err = m5602_write_sensor(sd, S5K4AA_GAIN, &data, 1);615616return err;617}618619static int s5k4aa_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)620{621struct sd *sd = (struct sd *) gspca_dev;622s32 *sensor_settings = sd->sensor_priv;623624*val = sensor_settings[BRIGHTNESS_IDX];625PDEBUG(D_V4L2, "Read brightness %d", *val);626return 0;627}628629static int s5k4aa_set_brightness(struct gspca_dev *gspca_dev, __s32 val)630{631struct sd *sd = (struct sd *) gspca_dev;632s32 *sensor_settings = sd->sensor_priv;633u8 data = S5K4AA_PAGE_MAP_2;634int err;635636sensor_settings[BRIGHTNESS_IDX] = val;637638PDEBUG(D_V4L2, "Set brightness to %d", val);639err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);640if (err < 0)641return err;642643data = val & 0xff;644return m5602_write_sensor(sd, S5K4AA_BRIGHTNESS, &data, 1);645}646647static int s5k4aa_get_noise(struct gspca_dev *gspca_dev, __s32 *val)648{649struct sd *sd = (struct sd *) gspca_dev;650s32 *sensor_settings = sd->sensor_priv;651652*val = sensor_settings[NOISE_SUPP_IDX];653PDEBUG(D_V4L2, "Read noise %d", *val);654return 0;655}656657static int s5k4aa_set_noise(struct gspca_dev *gspca_dev, __s32 val)658{659struct sd *sd = (struct sd *) gspca_dev;660s32 *sensor_settings = sd->sensor_priv;661u8 data = S5K4AA_PAGE_MAP_2;662int err;663664sensor_settings[NOISE_SUPP_IDX] = val;665666PDEBUG(D_V4L2, "Set noise to %d", val);667err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);668if (err < 0)669return err;670671data = val & 0x01;672return m5602_write_sensor(sd, S5K4AA_NOISE_SUPP, &data, 1);673}674675void s5k4aa_disconnect(struct sd *sd)676{677sd->sensor = NULL;678kfree(sd->sensor_priv);679}680681static void s5k4aa_dump_registers(struct sd *sd)682{683int address;684u8 page, old_page;685m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);686for (page = 0; page < 16; page++) {687m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);688info("Dumping the s5k4aa register state for page 0x%x", page);689for (address = 0; address <= 0xff; address++) {690u8 value = 0;691m5602_read_sensor(sd, address, &value, 1);692info("register 0x%x contains 0x%x",693address, value);694}695}696info("s5k4aa register state dump complete");697698for (page = 0; page < 16; page++) {699m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);700info("Probing for which registers that are "701"read/write for page 0x%x", page);702for (address = 0; address <= 0xff; address++) {703u8 old_value, ctrl_value, test_value = 0xff;704705m5602_read_sensor(sd, address, &old_value, 1);706m5602_write_sensor(sd, address, &test_value, 1);707m5602_read_sensor(sd, address, &ctrl_value, 1);708709if (ctrl_value == test_value)710info("register 0x%x is writeable", address);711else712info("register 0x%x is read only", address);713714/* Restore original value */715m5602_write_sensor(sd, address, &old_value, 1);716}717}718info("Read/write register probing complete");719m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);720}721722723