brintos

brintos / linux-shallow public Read only

0
0
Text · 20.8 KiB · c022265 Raw
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