760 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Driver for the s5k4aa sensor4 *5 * Copyright (C) 2008 Erik Andrén6 * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.7 * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>8 *9 * Portions of code to USB interface and ALi driver software,10 * Copyright (c) 2006 Willem Duinker11 * v4l2 interface modeled after the V4L2 driver12 * for SN9C10x PC Camera Controllers13 */14 15#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt16 17#include "m5602_s5k4aa.h"18 19static const unsigned char preinit_s5k4aa[][4] = {20 {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},21 {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},22 {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},23 {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},24 {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},25 {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},26 {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},27 28 {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},29 {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},30 {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},31 {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},32 {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},33 {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},34 {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},35 {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},36 {BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00},37 {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},38 {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},39 {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},40 {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},41 {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},42 {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},43 {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},44 45 {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},46 {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},47 {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},48 {BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00},49 {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},50 {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},51 {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},52 {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},53 {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},54 {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},55 {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},56 {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},57 {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},58 59 {SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00}60};61 62static const unsigned char init_s5k4aa[][4] = {63 {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},64 {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},65 {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},66 {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},67 {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},68 {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},69 {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},70 71 {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},72 {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},73 {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},74 {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},75 {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},76 {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},77 {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},78 {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},79 {BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00},80 {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},81 {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},82 {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},83 {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},84 {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},85 {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},86 {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},87 88 {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},89 {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},90 {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},91 {BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00},92 {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},93 {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},94 {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},95 {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},96 {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},97 {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},98 {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},99 {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},100 {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},101 102 {SENSOR, S5K4AA_PAGE_MAP, 0x07, 0x00},103 {SENSOR, 0x36, 0x01, 0x00},104 {SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00},105 {SENSOR, 0x7b, 0xff, 0x00},106 {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},107 {SENSOR, 0x0c, 0x05, 0x00},108 {SENSOR, 0x02, 0x0e, 0x00},109 {SENSOR, S5K4AA_READ_MODE, 0xa0, 0x00},110 {SENSOR, 0x37, 0x00, 0x00},111};112 113static const unsigned char VGA_s5k4aa[][4] = {114 {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},115 {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},116 {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},117 {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},118 {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},119 {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},120 {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},121 {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},122 {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},123 {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},124 {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},125 /* VSYNC_PARA, VSYNC_PARA : img height 480 = 0x01e0 */126 {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},127 {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0, 0x00},128 {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},129 {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},130 {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},131 {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},132 {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},133 {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},134 /* HSYNC_PARA, HSYNC_PARA : img width 640 = 0x0280 */135 {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},136 {BRIDGE, M5602_XB_HSYNC_PARA, 0x80, 0x00},137 {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},138 {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},139 {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */140 141 {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},142 {SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP | S5K4AA_RM_ROW_SKIP_2X143 | S5K4AA_RM_COL_SKIP_2X, 0x00},144 /* 0x37 : Fix image stability when light is too bright and improves145 * image quality in 640x480, but worsens it in 1280x1024 */146 {SENSOR, 0x37, 0x01, 0x00},147 /* ROWSTART_HI, ROWSTART_LO : 10 + (1024-960)/2 = 42 = 0x002a */148 {SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00},149 {SENSOR, S5K4AA_ROWSTART_LO, 0x29, 0x00},150 {SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00},151 {SENSOR, S5K4AA_COLSTART_LO, 0x0c, 0x00},152 /* window_height_hi, window_height_lo : 960 = 0x03c0 */153 {SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x03, 0x00},154 {SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0xc0, 0x00},155 /* window_width_hi, window_width_lo : 1280 = 0x0500 */156 {SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00},157 {SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00},158 {SENSOR, S5K4AA_H_BLANK_HI__, 0x00, 0x00},159 {SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00}, /* helps to sync... */160 {SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00},161 {SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00},162 {SENSOR, 0x11, 0x04, 0x00},163 {SENSOR, 0x12, 0xc3, 0x00},164 {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},165 {SENSOR, 0x02, 0x0e, 0x00},166};167 168static const unsigned char SXGA_s5k4aa[][4] = {169 {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},170 {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},171 {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},172 {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},173 {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},174 {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},175 {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},176 {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},177 {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},178 {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},179 {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},180 /* VSYNC_PARA, VSYNC_PARA : img height 1024 = 0x0400 */181 {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},182 {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},183 {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},184 {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},185 {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},186 {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},187 {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},188 {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},189 /* HSYNC_PARA, HSYNC_PARA : img width 1280 = 0x0500 */190 {BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00},191 {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},192 {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},193 {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},194 {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */195 196 {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},197 {SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP, 0x00},198 {SENSOR, 0x37, 0x01, 0x00},199 {SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00},200 {SENSOR, S5K4AA_ROWSTART_LO, 0x09, 0x00},201 {SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00},202 {SENSOR, S5K4AA_COLSTART_LO, 0x0a, 0x00},203 {SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x04, 0x00},204 {SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0x00, 0x00},205 {SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00},206 {SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00},207 {SENSOR, S5K4AA_H_BLANK_HI__, 0x01, 0x00},208 {SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00},209 {SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00},210 {SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00},211 {SENSOR, 0x11, 0x04, 0x00},212 {SENSOR, 0x12, 0xc3, 0x00},213 {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},214 {SENSOR, 0x02, 0x0e, 0x00},215};216 217 218static int s5k4aa_s_ctrl(struct v4l2_ctrl *ctrl);219static void s5k4aa_dump_registers(struct sd *sd);220 221static const struct v4l2_ctrl_ops s5k4aa_ctrl_ops = {222 .s_ctrl = s5k4aa_s_ctrl,223};224 225static226 const227 struct dmi_system_id s5k4aa_vflip_dmi_table[] = {228 {229 .ident = "BRUNEINIT",230 .matches = {231 DMI_MATCH(DMI_SYS_VENDOR, "BRUNENIT"),232 DMI_MATCH(DMI_PRODUCT_NAME, "BRUNENIT"),233 DMI_MATCH(DMI_BOARD_VERSION, "00030D0000000001")234 }235 }, {236 .ident = "Fujitsu-Siemens Amilo Xa 2528",237 .matches = {238 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),239 DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xa 2528")240 }241 }, {242 .ident = "Fujitsu-Siemens Amilo Xi 2428",243 .matches = {244 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),245 DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2428")246 }247 }, {248 .ident = "Fujitsu-Siemens Amilo Xi 2528",249 .matches = {250 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),251 DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2528")252 }253 }, {254 .ident = "Fujitsu-Siemens Amilo Xi 2550",255 .matches = {256 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),257 DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2550")258 }259 }, {260 .ident = "Fujitsu-Siemens Amilo Pa 2548",261 .matches = {262 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),263 DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 2548")264 }265 }, {266 .ident = "Fujitsu-Siemens Amilo Pi 2530",267 .matches = {268 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),269 DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pi 2530")270 }271 }, {272 .ident = "MSI GX700",273 .matches = {274 DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),275 DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),276 DMI_MATCH(DMI_BIOS_DATE, "12/02/2008")277 }278 }, {279 .ident = "MSI GX700",280 .matches = {281 DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),282 DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),283 DMI_MATCH(DMI_BIOS_DATE, "07/26/2007")284 }285 }, {286 .ident = "MSI GX700",287 .matches = {288 DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),289 DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),290 DMI_MATCH(DMI_BIOS_DATE, "07/19/2007")291 }292 }, {293 .ident = "MSI GX700/GX705/EX700",294 .matches = {295 DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),296 DMI_MATCH(DMI_PRODUCT_NAME, "GX700/GX705/EX700")297 }298 }, {299 .ident = "MSI L735",300 .matches = {301 DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),302 DMI_MATCH(DMI_PRODUCT_NAME, "MS-1717X")303 }304 }, {305 .ident = "Lenovo Y300",306 .matches = {307 DMI_MATCH(DMI_SYS_VENDOR, "L3000 Y300"),308 DMI_MATCH(DMI_PRODUCT_NAME, "Y300")309 }310 },311 { }312};313 314static struct v4l2_pix_format s5k4aa_modes[] = {315 {316 640,317 480,318 V4L2_PIX_FMT_SBGGR8,319 V4L2_FIELD_NONE,320 .sizeimage =321 640 * 480,322 .bytesperline = 640,323 .colorspace = V4L2_COLORSPACE_SRGB,324 .priv = 0325 },326 {327 1280,328 1024,329 V4L2_PIX_FMT_SBGGR8,330 V4L2_FIELD_NONE,331 .sizeimage =332 1280 * 1024,333 .bytesperline = 1280,334 .colorspace = V4L2_COLORSPACE_SRGB,335 .priv = 0336 }337};338 339int s5k4aa_probe(struct sd *sd)340{341 u8 prod_id[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};342 const u8 expected_prod_id[6] = {0x00, 0x10, 0x00, 0x4b, 0x33, 0x75};343 struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;344 int i, err = 0;345 346 if (force_sensor) {347 if (force_sensor == S5K4AA_SENSOR) {348 pr_info("Forcing a %s sensor\n", s5k4aa.name);349 goto sensor_found;350 }351 /* If we want to force another sensor, don't try to probe this352 * one */353 return -ENODEV;354 }355 356 gspca_dbg(gspca_dev, D_PROBE, "Probing for a s5k4aa sensor\n");357 358 /* Preinit the sensor */359 for (i = 0; i < ARRAY_SIZE(preinit_s5k4aa) && !err; i++) {360 u8 data[2] = {0x00, 0x00};361 362 switch (preinit_s5k4aa[i][0]) {363 case BRIDGE:364 err = m5602_write_bridge(sd,365 preinit_s5k4aa[i][1],366 preinit_s5k4aa[i][2]);367 break;368 369 case SENSOR:370 data[0] = preinit_s5k4aa[i][2];371 err = m5602_write_sensor(sd,372 preinit_s5k4aa[i][1],373 data, 1);374 break;375 376 case SENSOR_LONG:377 data[0] = preinit_s5k4aa[i][2];378 data[1] = preinit_s5k4aa[i][3];379 err = m5602_write_sensor(sd,380 preinit_s5k4aa[i][1],381 data, 2);382 break;383 default:384 pr_info("Invalid stream command, exiting init\n");385 return -EINVAL;386 }387 }388 389 /* Test some registers, but we don't know their exact meaning yet */390 if (m5602_read_sensor(sd, 0x00, prod_id, 2))391 return -ENODEV;392 if (m5602_read_sensor(sd, 0x02, prod_id+2, 2))393 return -ENODEV;394 if (m5602_read_sensor(sd, 0x04, prod_id+4, 2))395 return -ENODEV;396 397 if (memcmp(prod_id, expected_prod_id, sizeof(prod_id)))398 return -ENODEV;399 else400 pr_info("Detected a s5k4aa sensor\n");401 402sensor_found:403 sd->gspca_dev.cam.cam_mode = s5k4aa_modes;404 sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k4aa_modes);405 406 return 0;407}408 409int s5k4aa_start(struct sd *sd)410{411 int i, err = 0;412 u8 data[2];413 struct cam *cam = &sd->gspca_dev.cam;414 struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;415 416 switch (cam->cam_mode[sd->gspca_dev.curr_mode].width) {417 case 1280:418 gspca_dbg(gspca_dev, D_CONF, "Configuring camera for SXGA mode\n");419 420 for (i = 0; i < ARRAY_SIZE(SXGA_s5k4aa); i++) {421 switch (SXGA_s5k4aa[i][0]) {422 case BRIDGE:423 err = m5602_write_bridge(sd,424 SXGA_s5k4aa[i][1],425 SXGA_s5k4aa[i][2]);426 break;427 428 case SENSOR:429 data[0] = SXGA_s5k4aa[i][2];430 err = m5602_write_sensor(sd,431 SXGA_s5k4aa[i][1],432 data, 1);433 break;434 435 case SENSOR_LONG:436 data[0] = SXGA_s5k4aa[i][2];437 data[1] = SXGA_s5k4aa[i][3];438 err = m5602_write_sensor(sd,439 SXGA_s5k4aa[i][1],440 data, 2);441 break;442 443 default:444 pr_err("Invalid stream command, exiting init\n");445 return -EINVAL;446 }447 }448 break;449 450 case 640:451 gspca_dbg(gspca_dev, D_CONF, "Configuring camera for VGA mode\n");452 453 for (i = 0; i < ARRAY_SIZE(VGA_s5k4aa); i++) {454 switch (VGA_s5k4aa[i][0]) {455 case BRIDGE:456 err = m5602_write_bridge(sd,457 VGA_s5k4aa[i][1],458 VGA_s5k4aa[i][2]);459 break;460 461 case SENSOR:462 data[0] = VGA_s5k4aa[i][2];463 err = m5602_write_sensor(sd,464 VGA_s5k4aa[i][1],465 data, 1);466 break;467 468 case SENSOR_LONG:469 data[0] = VGA_s5k4aa[i][2];470 data[1] = VGA_s5k4aa[i][3];471 err = m5602_write_sensor(sd,472 VGA_s5k4aa[i][1],473 data, 2);474 break;475 476 default:477 pr_err("Invalid stream command, exiting init\n");478 return -EINVAL;479 }480 }481 break;482 }483 if (err < 0)484 return err;485 486 return 0;487}488 489int s5k4aa_init(struct sd *sd)490{491 int i, err = 0;492 493 for (i = 0; i < ARRAY_SIZE(init_s5k4aa) && !err; i++) {494 u8 data[2] = {0x00, 0x00};495 496 switch (init_s5k4aa[i][0]) {497 case BRIDGE:498 err = m5602_write_bridge(sd,499 init_s5k4aa[i][1],500 init_s5k4aa[i][2]);501 break;502 503 case SENSOR:504 data[0] = init_s5k4aa[i][2];505 err = m5602_write_sensor(sd,506 init_s5k4aa[i][1], data, 1);507 break;508 509 case SENSOR_LONG:510 data[0] = init_s5k4aa[i][2];511 data[1] = init_s5k4aa[i][3];512 err = m5602_write_sensor(sd,513 init_s5k4aa[i][1], data, 2);514 break;515 default:516 pr_info("Invalid stream command, exiting init\n");517 return -EINVAL;518 }519 }520 521 if (dump_sensor)522 s5k4aa_dump_registers(sd);523 524 return err;525}526 527int s5k4aa_init_controls(struct sd *sd)528{529 struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;530 531 sd->gspca_dev.vdev.ctrl_handler = hdl;532 v4l2_ctrl_handler_init(hdl, 6);533 534 v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_BRIGHTNESS,535 0, 0x1f, 1, S5K4AA_DEFAULT_BRIGHTNESS);536 537 v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_EXPOSURE,538 13, 0xfff, 1, 0x100);539 540 v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_GAIN,541 0, 127, 1, S5K4AA_DEFAULT_GAIN);542 543 v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_SHARPNESS,544 0, 1, 1, 1);545 546 sd->hflip = v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_HFLIP,547 0, 1, 1, 0);548 sd->vflip = v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_VFLIP,549 0, 1, 1, 0);550 551 if (hdl->error) {552 pr_err("Could not initialize controls\n");553 return hdl->error;554 }555 556 v4l2_ctrl_cluster(2, &sd->hflip);557 558 return 0;559}560 561static int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val)562{563 struct sd *sd = (struct sd *) gspca_dev;564 u8 data = S5K4AA_PAGE_MAP_2;565 int err;566 567 gspca_dbg(gspca_dev, D_CONF, "Set exposure to %d\n", val);568 err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);569 if (err < 0)570 return err;571 data = (val >> 8) & 0xff;572 err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);573 if (err < 0)574 return err;575 data = val & 0xff;576 err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);577 578 return err;579}580 581static int s5k4aa_set_hvflip(struct gspca_dev *gspca_dev)582{583 struct sd *sd = (struct sd *) gspca_dev;584 u8 data = S5K4AA_PAGE_MAP_2;585 int err;586 int hflip = sd->hflip->val;587 int vflip = sd->vflip->val;588 589 gspca_dbg(gspca_dev, D_CONF, "Set hvflip %d %d\n", hflip, vflip);590 err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);591 if (err < 0)592 return err;593 594 err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1);595 if (err < 0)596 return err;597 598 if (dmi_check_system(s5k4aa_vflip_dmi_table)) {599 hflip = !hflip;600 vflip = !vflip;601 }602 603 data = (data & 0x7f) | (vflip << 7) | (hflip << 6);604 err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);605 if (err < 0)606 return err;607 608 err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);609 if (err < 0)610 return err;611 if (hflip)612 data &= 0xfe;613 else614 data |= 0x01;615 err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);616 if (err < 0)617 return err;618 619 err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);620 if (err < 0)621 return err;622 if (vflip)623 data &= 0xfe;624 else625 data |= 0x01;626 err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);627 if (err < 0)628 return err;629 630 return 0;631}632 633static int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val)634{635 struct sd *sd = (struct sd *) gspca_dev;636 u8 data = S5K4AA_PAGE_MAP_2;637 int err;638 639 gspca_dbg(gspca_dev, D_CONF, "Set gain to %d\n", val);640 err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);641 if (err < 0)642 return err;643 644 data = val & 0xff;645 err = m5602_write_sensor(sd, S5K4AA_GAIN, &data, 1);646 647 return err;648}649 650static int s5k4aa_set_brightness(struct gspca_dev *gspca_dev, __s32 val)651{652 struct sd *sd = (struct sd *) gspca_dev;653 u8 data = S5K4AA_PAGE_MAP_2;654 int err;655 656 gspca_dbg(gspca_dev, D_CONF, "Set brightness to %d\n", val);657 err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);658 if (err < 0)659 return err;660 661 data = val & 0xff;662 return m5602_write_sensor(sd, S5K4AA_BRIGHTNESS, &data, 1);663}664 665static int s5k4aa_set_noise(struct gspca_dev *gspca_dev, __s32 val)666{667 struct sd *sd = (struct sd *) gspca_dev;668 u8 data = S5K4AA_PAGE_MAP_2;669 int err;670 671 gspca_dbg(gspca_dev, D_CONF, "Set noise to %d\n", val);672 err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);673 if (err < 0)674 return err;675 676 data = val & 0x01;677 return m5602_write_sensor(sd, S5K4AA_NOISE_SUPP, &data, 1);678}679 680static int s5k4aa_s_ctrl(struct v4l2_ctrl *ctrl)681{682 struct gspca_dev *gspca_dev =683 container_of(ctrl->handler, struct gspca_dev, ctrl_handler);684 int err;685 686 if (!gspca_dev->streaming)687 return 0;688 689 switch (ctrl->id) {690 case V4L2_CID_BRIGHTNESS:691 err = s5k4aa_set_brightness(gspca_dev, ctrl->val);692 break;693 case V4L2_CID_EXPOSURE:694 err = s5k4aa_set_exposure(gspca_dev, ctrl->val);695 break;696 case V4L2_CID_GAIN:697 err = s5k4aa_set_gain(gspca_dev, ctrl->val);698 break;699 case V4L2_CID_SHARPNESS:700 err = s5k4aa_set_noise(gspca_dev, ctrl->val);701 break;702 case V4L2_CID_HFLIP:703 err = s5k4aa_set_hvflip(gspca_dev);704 break;705 default:706 return -EINVAL;707 }708 709 return err;710}711 712void s5k4aa_disconnect(struct sd *sd)713{714 sd->sensor = NULL;715}716 717static void s5k4aa_dump_registers(struct sd *sd)718{719 int address;720 u8 page, old_page;721 m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);722 for (page = 0; page < 16; page++) {723 m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);724 pr_info("Dumping the s5k4aa register state for page 0x%x\n",725 page);726 for (address = 0; address <= 0xff; address++) {727 u8 value = 0;728 m5602_read_sensor(sd, address, &value, 1);729 pr_info("register 0x%x contains 0x%x\n",730 address, value);731 }732 }733 pr_info("s5k4aa register state dump complete\n");734 735 for (page = 0; page < 16; page++) {736 m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);737 pr_info("Probing for which registers that are read/write for page 0x%x\n",738 page);739 for (address = 0; address <= 0xff; address++) {740 u8 old_value, ctrl_value, test_value = 0xff;741 742 m5602_read_sensor(sd, address, &old_value, 1);743 m5602_write_sensor(sd, address, &test_value, 1);744 m5602_read_sensor(sd, address, &ctrl_value, 1);745 746 if (ctrl_value == test_value)747 pr_info("register 0x%x is writeable\n",748 address);749 else750 pr_info("register 0x%x is read only\n",751 address);752 753 /* Restore original value */754 m5602_write_sensor(sd, address, &old_value, 1);755 }756 }757 pr_info("Read/write register probing complete\n");758 m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);759}760