1*a10e763bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
20c0d06caSMauro Carvalho Chehab /*
30c0d06caSMauro Carvalho Chehab  * Driver for the s5k4aa sensor
40c0d06caSMauro Carvalho Chehab  *
50c0d06caSMauro Carvalho Chehab  * Copyright (C) 2008 Erik Andrén
60c0d06caSMauro Carvalho Chehab  * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
70c0d06caSMauro Carvalho Chehab  * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
80c0d06caSMauro Carvalho Chehab  *
90c0d06caSMauro Carvalho Chehab  * Portions of code to USB interface and ALi driver software,
100c0d06caSMauro Carvalho Chehab  * Copyright (c) 2006 Willem Duinker
110c0d06caSMauro Carvalho Chehab  * v4l2 interface modeled after the V4L2 driver
120c0d06caSMauro Carvalho Chehab  * for SN9C10x PC Camera Controllers
130c0d06caSMauro Carvalho Chehab  */
140c0d06caSMauro Carvalho Chehab 
150c0d06caSMauro Carvalho Chehab #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
160c0d06caSMauro Carvalho Chehab 
170c0d06caSMauro Carvalho Chehab #include "m5602_s5k4aa.h"
180c0d06caSMauro Carvalho Chehab 
190eed95b2SMauro Carvalho Chehab static const unsigned char preinit_s5k4aa[][4] = {
200eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
210eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
220eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
230eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
240eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
250eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
260eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
270eed95b2SMauro Carvalho Chehab 
280eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
290eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
300eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
310eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
320eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
330eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
340eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
350eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
360eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00},
370eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
380eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
390eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
400eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
410eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
420eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
430eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
440eed95b2SMauro Carvalho Chehab 
450eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
460eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
470eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
480eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00},
490eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
500eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
510eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
520eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
530eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
540eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
550eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
560eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
570eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
580eed95b2SMauro Carvalho Chehab 
590eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00}
600eed95b2SMauro Carvalho Chehab };
610eed95b2SMauro Carvalho Chehab 
620eed95b2SMauro Carvalho Chehab static const unsigned char init_s5k4aa[][4] = {
630eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
640eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
650eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
660eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
670eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
680eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
690eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
700eed95b2SMauro Carvalho Chehab 
710eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
720eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
730eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
740eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
750eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
760eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
770eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
780eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
790eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00},
800eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
810eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
820eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
830eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
840eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
850eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
860eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
870eed95b2SMauro Carvalho Chehab 
880eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
890eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
900eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
910eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00},
920eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
930eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
940eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
950eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
960eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
970eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
980eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
990eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
1000eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
1010eed95b2SMauro Carvalho Chehab 
1020eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_PAGE_MAP, 0x07, 0x00},
1030eed95b2SMauro Carvalho Chehab 	{SENSOR, 0x36, 0x01, 0x00},
1040eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00},
1050eed95b2SMauro Carvalho Chehab 	{SENSOR, 0x7b, 0xff, 0x00},
1060eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
1070eed95b2SMauro Carvalho Chehab 	{SENSOR, 0x0c, 0x05, 0x00},
1080eed95b2SMauro Carvalho Chehab 	{SENSOR, 0x02, 0x0e, 0x00},
1090eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_READ_MODE, 0xa0, 0x00},
1100eed95b2SMauro Carvalho Chehab 	{SENSOR, 0x37, 0x00, 0x00},
1110eed95b2SMauro Carvalho Chehab };
1120eed95b2SMauro Carvalho Chehab 
1130eed95b2SMauro Carvalho Chehab static const unsigned char VGA_s5k4aa[][4] = {
1140eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
1150eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
1160eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
1170eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
1180eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
1190eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
1200eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
1210eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
1220eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
1230eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
1240eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
1250eed95b2SMauro Carvalho Chehab 	/* VSYNC_PARA, VSYNC_PARA : img height 480 = 0x01e0 */
1260eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
1270eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_VSYNC_PARA, 0xe0, 0x00},
1280eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
1290eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
1300eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
1310eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
1320eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
1330eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
1340eed95b2SMauro Carvalho Chehab 	/* HSYNC_PARA, HSYNC_PARA : img width 640 = 0x0280 */
1350eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
1360eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_HSYNC_PARA, 0x80, 0x00},
1370eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
1380eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
1390eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */
1400eed95b2SMauro Carvalho Chehab 
1410eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
1420eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP | S5K4AA_RM_ROW_SKIP_2X
1430eed95b2SMauro Carvalho Chehab 		| S5K4AA_RM_COL_SKIP_2X, 0x00},
1440eed95b2SMauro Carvalho Chehab 	/* 0x37 : Fix image stability when light is too bright and improves
1450eed95b2SMauro Carvalho Chehab 	 * image quality in 640x480, but worsens it in 1280x1024 */
1460eed95b2SMauro Carvalho Chehab 	{SENSOR, 0x37, 0x01, 0x00},
1470eed95b2SMauro Carvalho Chehab 	/* ROWSTART_HI, ROWSTART_LO : 10 + (1024-960)/2 = 42 = 0x002a */
1480eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00},
1490eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_ROWSTART_LO, 0x29, 0x00},
1500eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00},
1510eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_COLSTART_LO, 0x0c, 0x00},
1520eed95b2SMauro Carvalho Chehab 	/* window_height_hi, window_height_lo : 960 = 0x03c0 */
1530eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x03, 0x00},
1540eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0xc0, 0x00},
1550eed95b2SMauro Carvalho Chehab 	/* window_width_hi, window_width_lo : 1280 = 0x0500 */
1560eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00},
1570eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00},
1580eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_H_BLANK_HI__, 0x00, 0x00},
1590eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00}, /* helps to sync... */
1600eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00},
1610eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00},
1620eed95b2SMauro Carvalho Chehab 	{SENSOR, 0x11, 0x04, 0x00},
1630eed95b2SMauro Carvalho Chehab 	{SENSOR, 0x12, 0xc3, 0x00},
1640eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
1650eed95b2SMauro Carvalho Chehab 	{SENSOR, 0x02, 0x0e, 0x00},
1660eed95b2SMauro Carvalho Chehab };
1670eed95b2SMauro Carvalho Chehab 
1680eed95b2SMauro Carvalho Chehab static const unsigned char SXGA_s5k4aa[][4] = {
1690eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
1700eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
1710eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
1720eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
1730eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
1740eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
1750eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
1760eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
1770eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
1780eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
1790eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
1800eed95b2SMauro Carvalho Chehab 	/* VSYNC_PARA, VSYNC_PARA : img height 1024 = 0x0400 */
1810eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
1820eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
1830eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
1840eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
1850eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
1860eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
1870eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
1880eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
1890eed95b2SMauro Carvalho Chehab 	/* HSYNC_PARA, HSYNC_PARA : img width 1280 = 0x0500 */
1900eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00},
1910eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
1920eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
1930eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
1940eed95b2SMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */
1950eed95b2SMauro Carvalho Chehab 
1960eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
1970eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP, 0x00},
1980eed95b2SMauro Carvalho Chehab 	{SENSOR, 0x37, 0x01, 0x00},
1990eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00},
2000eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_ROWSTART_LO, 0x09, 0x00},
2010eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00},
2020eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_COLSTART_LO, 0x0a, 0x00},
2030eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x04, 0x00},
2040eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0x00, 0x00},
2050eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00},
2060eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00},
2070eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_H_BLANK_HI__, 0x01, 0x00},
2080eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00},
2090eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00},
2100eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00},
2110eed95b2SMauro Carvalho Chehab 	{SENSOR, 0x11, 0x04, 0x00},
2120eed95b2SMauro Carvalho Chehab 	{SENSOR, 0x12, 0xc3, 0x00},
2130eed95b2SMauro Carvalho Chehab 	{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
2140eed95b2SMauro Carvalho Chehab 	{SENSOR, 0x02, 0x0e, 0x00},
2150eed95b2SMauro Carvalho Chehab };
2160eed95b2SMauro Carvalho Chehab 
2170eed95b2SMauro Carvalho Chehab 
218c84e412fSHans de Goede static int s5k4aa_s_ctrl(struct v4l2_ctrl *ctrl);
219c84e412fSHans de Goede static void s5k4aa_dump_registers(struct sd *sd);
220c84e412fSHans de Goede 
221c84e412fSHans de Goede static const struct v4l2_ctrl_ops s5k4aa_ctrl_ops = {
222c84e412fSHans de Goede 	.s_ctrl = s5k4aa_s_ctrl,
223c84e412fSHans de Goede };
2240c0d06caSMauro Carvalho Chehab 
2250c0d06caSMauro Carvalho Chehab static
2260c0d06caSMauro Carvalho Chehab     const
2270c0d06caSMauro Carvalho Chehab 	struct dmi_system_id s5k4aa_vflip_dmi_table[] = {
2280c0d06caSMauro Carvalho Chehab 	{
2290c0d06caSMauro Carvalho Chehab 		.ident = "BRUNEINIT",
2300c0d06caSMauro Carvalho Chehab 		.matches = {
2310c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_SYS_VENDOR, "BRUNENIT"),
2320c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_PRODUCT_NAME, "BRUNENIT"),
2330c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_BOARD_VERSION, "00030D0000000001")
2340c0d06caSMauro Carvalho Chehab 		}
2350c0d06caSMauro Carvalho Chehab 	}, {
2360c0d06caSMauro Carvalho Chehab 		.ident = "Fujitsu-Siemens Amilo Xa 2528",
2370c0d06caSMauro Carvalho Chehab 		.matches = {
2380c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
2390c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xa 2528")
2400c0d06caSMauro Carvalho Chehab 		}
2410c0d06caSMauro Carvalho Chehab 	}, {
2420c0d06caSMauro Carvalho Chehab 		.ident = "Fujitsu-Siemens Amilo Xi 2428",
2430c0d06caSMauro Carvalho Chehab 		.matches = {
2440c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
2450c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2428")
2460c0d06caSMauro Carvalho Chehab 		}
2470c0d06caSMauro Carvalho Chehab 	}, {
2480c0d06caSMauro Carvalho Chehab 		.ident = "Fujitsu-Siemens Amilo Xi 2528",
2490c0d06caSMauro Carvalho Chehab 		.matches = {
2500c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
2510c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2528")
2520c0d06caSMauro Carvalho Chehab 		}
2530c0d06caSMauro Carvalho Chehab 	}, {
2540c0d06caSMauro Carvalho Chehab 		.ident = "Fujitsu-Siemens Amilo Xi 2550",
2550c0d06caSMauro Carvalho Chehab 		.matches = {
2560c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
2570c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2550")
2580c0d06caSMauro Carvalho Chehab 		}
2590c0d06caSMauro Carvalho Chehab 	}, {
2600c0d06caSMauro Carvalho Chehab 		.ident = "Fujitsu-Siemens Amilo Pa 2548",
2610c0d06caSMauro Carvalho Chehab 		.matches = {
2620c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
2630c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 2548")
2640c0d06caSMauro Carvalho Chehab 		}
2650c0d06caSMauro Carvalho Chehab 	}, {
26668425935SGregor Jasny 		.ident = "Fujitsu-Siemens Amilo Pi 2530",
26768425935SGregor Jasny 		.matches = {
26868425935SGregor Jasny 			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
26968425935SGregor Jasny 			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pi 2530")
27068425935SGregor Jasny 		}
27168425935SGregor Jasny 	}, {
2720c0d06caSMauro Carvalho Chehab 		.ident = "MSI GX700",
2730c0d06caSMauro Carvalho Chehab 		.matches = {
2740c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
2750c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),
2760c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_BIOS_DATE, "12/02/2008")
2770c0d06caSMauro Carvalho Chehab 		}
2780c0d06caSMauro Carvalho Chehab 	}, {
2790c0d06caSMauro Carvalho Chehab 		.ident = "MSI GX700",
2800c0d06caSMauro Carvalho Chehab 		.matches = {
2810c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
2820c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),
2830c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_BIOS_DATE, "07/26/2007")
2840c0d06caSMauro Carvalho Chehab 		}
2850c0d06caSMauro Carvalho Chehab 	}, {
2860c0d06caSMauro Carvalho Chehab 		.ident = "MSI GX700",
2870c0d06caSMauro Carvalho Chehab 		.matches = {
2880c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
2890c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),
2900c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_BIOS_DATE, "07/19/2007")
2910c0d06caSMauro Carvalho Chehab 		}
2920c0d06caSMauro Carvalho Chehab 	}, {
2930c0d06caSMauro Carvalho Chehab 		.ident = "MSI GX700/GX705/EX700",
2940c0d06caSMauro Carvalho Chehab 		.matches = {
2950c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
2960c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_PRODUCT_NAME, "GX700/GX705/EX700")
2970c0d06caSMauro Carvalho Chehab 		}
2980c0d06caSMauro Carvalho Chehab 	}, {
2990c0d06caSMauro Carvalho Chehab 		.ident = "MSI L735",
3000c0d06caSMauro Carvalho Chehab 		.matches = {
3010c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
3020c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_PRODUCT_NAME, "MS-1717X")
3030c0d06caSMauro Carvalho Chehab 		}
3040c0d06caSMauro Carvalho Chehab 	}, {
3050c0d06caSMauro Carvalho Chehab 		.ident = "Lenovo Y300",
3060c0d06caSMauro Carvalho Chehab 		.matches = {
3070c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_SYS_VENDOR, "L3000 Y300"),
3080c0d06caSMauro Carvalho Chehab 			DMI_MATCH(DMI_PRODUCT_NAME, "Y300")
3090c0d06caSMauro Carvalho Chehab 		}
3100c0d06caSMauro Carvalho Chehab 	},
3110c0d06caSMauro Carvalho Chehab 	{ }
3120c0d06caSMauro Carvalho Chehab };
3130c0d06caSMauro Carvalho Chehab 
3140c0d06caSMauro Carvalho Chehab static struct v4l2_pix_format s5k4aa_modes[] = {
3150c0d06caSMauro Carvalho Chehab 	{
3160c0d06caSMauro Carvalho Chehab 		640,
3170c0d06caSMauro Carvalho Chehab 		480,
3180c0d06caSMauro Carvalho Chehab 		V4L2_PIX_FMT_SBGGR8,
3190c0d06caSMauro Carvalho Chehab 		V4L2_FIELD_NONE,
3200c0d06caSMauro Carvalho Chehab 		.sizeimage =
3210c0d06caSMauro Carvalho Chehab 			640 * 480,
3220c0d06caSMauro Carvalho Chehab 		.bytesperline = 640,
3230c0d06caSMauro Carvalho Chehab 		.colorspace = V4L2_COLORSPACE_SRGB,
3240c0d06caSMauro Carvalho Chehab 		.priv = 0
3250c0d06caSMauro Carvalho Chehab 	},
3260c0d06caSMauro Carvalho Chehab 	{
3270c0d06caSMauro Carvalho Chehab 		1280,
3280c0d06caSMauro Carvalho Chehab 		1024,
3290c0d06caSMauro Carvalho Chehab 		V4L2_PIX_FMT_SBGGR8,
3300c0d06caSMauro Carvalho Chehab 		V4L2_FIELD_NONE,
3310c0d06caSMauro Carvalho Chehab 		.sizeimage =
3320c0d06caSMauro Carvalho Chehab 			1280 * 1024,
3330c0d06caSMauro Carvalho Chehab 		.bytesperline = 1280,
3340c0d06caSMauro Carvalho Chehab 		.colorspace = V4L2_COLORSPACE_SRGB,
3350c0d06caSMauro Carvalho Chehab 		.priv = 0
3360c0d06caSMauro Carvalho Chehab 	}
3370c0d06caSMauro Carvalho Chehab };
3380c0d06caSMauro Carvalho Chehab 
s5k4aa_probe(struct sd * sd)3390c0d06caSMauro Carvalho Chehab int s5k4aa_probe(struct sd *sd)
3400c0d06caSMauro Carvalho Chehab {
3410c0d06caSMauro Carvalho Chehab 	u8 prod_id[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
3420c0d06caSMauro Carvalho Chehab 	const u8 expected_prod_id[6] = {0x00, 0x10, 0x00, 0x4b, 0x33, 0x75};
343c93396e1STheodore Kilgore 	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
3440c0d06caSMauro Carvalho Chehab 	int i, err = 0;
3450c0d06caSMauro Carvalho Chehab 
3460c0d06caSMauro Carvalho Chehab 	if (force_sensor) {
3470c0d06caSMauro Carvalho Chehab 		if (force_sensor == S5K4AA_SENSOR) {
3480c0d06caSMauro Carvalho Chehab 			pr_info("Forcing a %s sensor\n", s5k4aa.name);
3490c0d06caSMauro Carvalho Chehab 			goto sensor_found;
3500c0d06caSMauro Carvalho Chehab 		}
3510c0d06caSMauro Carvalho Chehab 		/* If we want to force another sensor, don't try to probe this
3520c0d06caSMauro Carvalho Chehab 		 * one */
3530c0d06caSMauro Carvalho Chehab 		return -ENODEV;
3540c0d06caSMauro Carvalho Chehab 	}
3550c0d06caSMauro Carvalho Chehab 
35637d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_PROBE, "Probing for a s5k4aa sensor\n");
3570c0d06caSMauro Carvalho Chehab 
3580c0d06caSMauro Carvalho Chehab 	/* Preinit the sensor */
3590c0d06caSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(preinit_s5k4aa) && !err; i++) {
3600c0d06caSMauro Carvalho Chehab 		u8 data[2] = {0x00, 0x00};
3610c0d06caSMauro Carvalho Chehab 
3620c0d06caSMauro Carvalho Chehab 		switch (preinit_s5k4aa[i][0]) {
3630c0d06caSMauro Carvalho Chehab 		case BRIDGE:
3640c0d06caSMauro Carvalho Chehab 			err = m5602_write_bridge(sd,
3650c0d06caSMauro Carvalho Chehab 						 preinit_s5k4aa[i][1],
3660c0d06caSMauro Carvalho Chehab 						 preinit_s5k4aa[i][2]);
3670c0d06caSMauro Carvalho Chehab 			break;
3680c0d06caSMauro Carvalho Chehab 
3690c0d06caSMauro Carvalho Chehab 		case SENSOR:
3700c0d06caSMauro Carvalho Chehab 			data[0] = preinit_s5k4aa[i][2];
3710c0d06caSMauro Carvalho Chehab 			err = m5602_write_sensor(sd,
3720c0d06caSMauro Carvalho Chehab 						  preinit_s5k4aa[i][1],
3730c0d06caSMauro Carvalho Chehab 						  data, 1);
3740c0d06caSMauro Carvalho Chehab 			break;
3750c0d06caSMauro Carvalho Chehab 
3760c0d06caSMauro Carvalho Chehab 		case SENSOR_LONG:
3770c0d06caSMauro Carvalho Chehab 			data[0] = preinit_s5k4aa[i][2];
3780c0d06caSMauro Carvalho Chehab 			data[1] = preinit_s5k4aa[i][3];
3790c0d06caSMauro Carvalho Chehab 			err = m5602_write_sensor(sd,
3800c0d06caSMauro Carvalho Chehab 						  preinit_s5k4aa[i][1],
3810c0d06caSMauro Carvalho Chehab 						  data, 2);
3820c0d06caSMauro Carvalho Chehab 			break;
3830c0d06caSMauro Carvalho Chehab 		default:
3840c0d06caSMauro Carvalho Chehab 			pr_info("Invalid stream command, exiting init\n");
3850c0d06caSMauro Carvalho Chehab 			return -EINVAL;
3860c0d06caSMauro Carvalho Chehab 		}
3870c0d06caSMauro Carvalho Chehab 	}
3880c0d06caSMauro Carvalho Chehab 
3890c0d06caSMauro Carvalho Chehab 	/* Test some registers, but we don't know their exact meaning yet */
3900c0d06caSMauro Carvalho Chehab 	if (m5602_read_sensor(sd, 0x00, prod_id, 2))
3910c0d06caSMauro Carvalho Chehab 		return -ENODEV;
3920c0d06caSMauro Carvalho Chehab 	if (m5602_read_sensor(sd, 0x02, prod_id+2, 2))
3930c0d06caSMauro Carvalho Chehab 		return -ENODEV;
3940c0d06caSMauro Carvalho Chehab 	if (m5602_read_sensor(sd, 0x04, prod_id+4, 2))
3950c0d06caSMauro Carvalho Chehab 		return -ENODEV;
3960c0d06caSMauro Carvalho Chehab 
3970c0d06caSMauro Carvalho Chehab 	if (memcmp(prod_id, expected_prod_id, sizeof(prod_id)))
3980c0d06caSMauro Carvalho Chehab 		return -ENODEV;
3990c0d06caSMauro Carvalho Chehab 	else
4000c0d06caSMauro Carvalho Chehab 		pr_info("Detected a s5k4aa sensor\n");
4010c0d06caSMauro Carvalho Chehab 
4020c0d06caSMauro Carvalho Chehab sensor_found:
4030c0d06caSMauro Carvalho Chehab 	sd->gspca_dev.cam.cam_mode = s5k4aa_modes;
4040c0d06caSMauro Carvalho Chehab 	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k4aa_modes);
4050c0d06caSMauro Carvalho Chehab 
4060c0d06caSMauro Carvalho Chehab 	return 0;
4070c0d06caSMauro Carvalho Chehab }
4080c0d06caSMauro Carvalho Chehab 
s5k4aa_start(struct sd * sd)4090c0d06caSMauro Carvalho Chehab int s5k4aa_start(struct sd *sd)
4100c0d06caSMauro Carvalho Chehab {
4110c0d06caSMauro Carvalho Chehab 	int i, err = 0;
4120c0d06caSMauro Carvalho Chehab 	u8 data[2];
4130c0d06caSMauro Carvalho Chehab 	struct cam *cam = &sd->gspca_dev.cam;
414c93396e1STheodore Kilgore 	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
4150c0d06caSMauro Carvalho Chehab 
4160c0d06caSMauro Carvalho Chehab 	switch (cam->cam_mode[sd->gspca_dev.curr_mode].width) {
4170c0d06caSMauro Carvalho Chehab 	case 1280:
41837d5efb0SJoe Perches 		gspca_dbg(gspca_dev, D_CONF, "Configuring camera for SXGA mode\n");
4190c0d06caSMauro Carvalho Chehab 
4200c0d06caSMauro Carvalho Chehab 		for (i = 0; i < ARRAY_SIZE(SXGA_s5k4aa); i++) {
4210c0d06caSMauro Carvalho Chehab 			switch (SXGA_s5k4aa[i][0]) {
4220c0d06caSMauro Carvalho Chehab 			case BRIDGE:
4230c0d06caSMauro Carvalho Chehab 				err = m5602_write_bridge(sd,
4240c0d06caSMauro Carvalho Chehab 						 SXGA_s5k4aa[i][1],
4250c0d06caSMauro Carvalho Chehab 						 SXGA_s5k4aa[i][2]);
4260c0d06caSMauro Carvalho Chehab 			break;
4270c0d06caSMauro Carvalho Chehab 
4280c0d06caSMauro Carvalho Chehab 			case SENSOR:
4290c0d06caSMauro Carvalho Chehab 				data[0] = SXGA_s5k4aa[i][2];
4300c0d06caSMauro Carvalho Chehab 				err = m5602_write_sensor(sd,
4310c0d06caSMauro Carvalho Chehab 						 SXGA_s5k4aa[i][1],
4320c0d06caSMauro Carvalho Chehab 						 data, 1);
4330c0d06caSMauro Carvalho Chehab 			break;
4340c0d06caSMauro Carvalho Chehab 
4350c0d06caSMauro Carvalho Chehab 			case SENSOR_LONG:
4360c0d06caSMauro Carvalho Chehab 				data[0] = SXGA_s5k4aa[i][2];
4370c0d06caSMauro Carvalho Chehab 				data[1] = SXGA_s5k4aa[i][3];
4380c0d06caSMauro Carvalho Chehab 				err = m5602_write_sensor(sd,
4390c0d06caSMauro Carvalho Chehab 						  SXGA_s5k4aa[i][1],
4400c0d06caSMauro Carvalho Chehab 						  data, 2);
4410c0d06caSMauro Carvalho Chehab 			break;
4420c0d06caSMauro Carvalho Chehab 
4430c0d06caSMauro Carvalho Chehab 			default:
4440c0d06caSMauro Carvalho Chehab 				pr_err("Invalid stream command, exiting init\n");
4450c0d06caSMauro Carvalho Chehab 				return -EINVAL;
4460c0d06caSMauro Carvalho Chehab 			}
4470c0d06caSMauro Carvalho Chehab 		}
4480c0d06caSMauro Carvalho Chehab 		break;
4490c0d06caSMauro Carvalho Chehab 
4500c0d06caSMauro Carvalho Chehab 	case 640:
45137d5efb0SJoe Perches 		gspca_dbg(gspca_dev, D_CONF, "Configuring camera for VGA mode\n");
4520c0d06caSMauro Carvalho Chehab 
4530c0d06caSMauro Carvalho Chehab 		for (i = 0; i < ARRAY_SIZE(VGA_s5k4aa); i++) {
4540c0d06caSMauro Carvalho Chehab 			switch (VGA_s5k4aa[i][0]) {
4550c0d06caSMauro Carvalho Chehab 			case BRIDGE:
4560c0d06caSMauro Carvalho Chehab 				err = m5602_write_bridge(sd,
4570c0d06caSMauro Carvalho Chehab 						 VGA_s5k4aa[i][1],
4580c0d06caSMauro Carvalho Chehab 						 VGA_s5k4aa[i][2]);
4590c0d06caSMauro Carvalho Chehab 			break;
4600c0d06caSMauro Carvalho Chehab 
4610c0d06caSMauro Carvalho Chehab 			case SENSOR:
4620c0d06caSMauro Carvalho Chehab 				data[0] = VGA_s5k4aa[i][2];
4630c0d06caSMauro Carvalho Chehab 				err = m5602_write_sensor(sd,
4640c0d06caSMauro Carvalho Chehab 						 VGA_s5k4aa[i][1],
4650c0d06caSMauro Carvalho Chehab 						 data, 1);
4660c0d06caSMauro Carvalho Chehab 			break;
4670c0d06caSMauro Carvalho Chehab 
4680c0d06caSMauro Carvalho Chehab 			case SENSOR_LONG:
4690c0d06caSMauro Carvalho Chehab 				data[0] = VGA_s5k4aa[i][2];
4700c0d06caSMauro Carvalho Chehab 				data[1] = VGA_s5k4aa[i][3];
4710c0d06caSMauro Carvalho Chehab 				err = m5602_write_sensor(sd,
4720c0d06caSMauro Carvalho Chehab 						  VGA_s5k4aa[i][1],
4730c0d06caSMauro Carvalho Chehab 						  data, 2);
4740c0d06caSMauro Carvalho Chehab 			break;
4750c0d06caSMauro Carvalho Chehab 
4760c0d06caSMauro Carvalho Chehab 			default:
4770c0d06caSMauro Carvalho Chehab 				pr_err("Invalid stream command, exiting init\n");
4780c0d06caSMauro Carvalho Chehab 				return -EINVAL;
4790c0d06caSMauro Carvalho Chehab 			}
4800c0d06caSMauro Carvalho Chehab 		}
4810c0d06caSMauro Carvalho Chehab 		break;
4820c0d06caSMauro Carvalho Chehab 	}
4830c0d06caSMauro Carvalho Chehab 	if (err < 0)
4840c0d06caSMauro Carvalho Chehab 		return err;
4850c0d06caSMauro Carvalho Chehab 
486c84e412fSHans de Goede 	return 0;
4870c0d06caSMauro Carvalho Chehab }
4880c0d06caSMauro Carvalho Chehab 
s5k4aa_init(struct sd * sd)4890c0d06caSMauro Carvalho Chehab int s5k4aa_init(struct sd *sd)
4900c0d06caSMauro Carvalho Chehab {
4910c0d06caSMauro Carvalho Chehab 	int i, err = 0;
4920c0d06caSMauro Carvalho Chehab 
4930c0d06caSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(init_s5k4aa) && !err; i++) {
4940c0d06caSMauro Carvalho Chehab 		u8 data[2] = {0x00, 0x00};
4950c0d06caSMauro Carvalho Chehab 
4960c0d06caSMauro Carvalho Chehab 		switch (init_s5k4aa[i][0]) {
4970c0d06caSMauro Carvalho Chehab 		case BRIDGE:
4980c0d06caSMauro Carvalho Chehab 			err = m5602_write_bridge(sd,
4990c0d06caSMauro Carvalho Chehab 				init_s5k4aa[i][1],
5000c0d06caSMauro Carvalho Chehab 				init_s5k4aa[i][2]);
5010c0d06caSMauro Carvalho Chehab 			break;
5020c0d06caSMauro Carvalho Chehab 
5030c0d06caSMauro Carvalho Chehab 		case SENSOR:
5040c0d06caSMauro Carvalho Chehab 			data[0] = init_s5k4aa[i][2];
5050c0d06caSMauro Carvalho Chehab 			err = m5602_write_sensor(sd,
5060c0d06caSMauro Carvalho Chehab 				init_s5k4aa[i][1], data, 1);
5070c0d06caSMauro Carvalho Chehab 			break;
5080c0d06caSMauro Carvalho Chehab 
5090c0d06caSMauro Carvalho Chehab 		case SENSOR_LONG:
5100c0d06caSMauro Carvalho Chehab 			data[0] = init_s5k4aa[i][2];
5110c0d06caSMauro Carvalho Chehab 			data[1] = init_s5k4aa[i][3];
5120c0d06caSMauro Carvalho Chehab 			err = m5602_write_sensor(sd,
5130c0d06caSMauro Carvalho Chehab 				init_s5k4aa[i][1], data, 2);
5140c0d06caSMauro Carvalho Chehab 			break;
5150c0d06caSMauro Carvalho Chehab 		default:
5160c0d06caSMauro Carvalho Chehab 			pr_info("Invalid stream command, exiting init\n");
5170c0d06caSMauro Carvalho Chehab 			return -EINVAL;
5180c0d06caSMauro Carvalho Chehab 		}
5190c0d06caSMauro Carvalho Chehab 	}
5200c0d06caSMauro Carvalho Chehab 
5210c0d06caSMauro Carvalho Chehab 	if (dump_sensor)
5220c0d06caSMauro Carvalho Chehab 		s5k4aa_dump_registers(sd);
5230c0d06caSMauro Carvalho Chehab 
5240c0d06caSMauro Carvalho Chehab 	return err;
5250c0d06caSMauro Carvalho Chehab }
5260c0d06caSMauro Carvalho Chehab 
s5k4aa_init_controls(struct sd * sd)527c84e412fSHans de Goede int s5k4aa_init_controls(struct sd *sd)
5280c0d06caSMauro Carvalho Chehab {
529c84e412fSHans de Goede 	struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
5300c0d06caSMauro Carvalho Chehab 
531c84e412fSHans de Goede 	sd->gspca_dev.vdev.ctrl_handler = hdl;
532c84e412fSHans de Goede 	v4l2_ctrl_handler_init(hdl, 6);
533c84e412fSHans de Goede 
534c84e412fSHans de Goede 	v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_BRIGHTNESS,
535c84e412fSHans de Goede 			  0, 0x1f, 1, S5K4AA_DEFAULT_BRIGHTNESS);
536c84e412fSHans de Goede 
537c84e412fSHans de Goede 	v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_EXPOSURE,
538c84e412fSHans de Goede 			  13, 0xfff, 1, 0x100);
539c84e412fSHans de Goede 
540c84e412fSHans de Goede 	v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_GAIN,
541c84e412fSHans de Goede 			  0, 127, 1, S5K4AA_DEFAULT_GAIN);
542c84e412fSHans de Goede 
543c84e412fSHans de Goede 	v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_SHARPNESS,
544c84e412fSHans de Goede 			  0, 1, 1, 1);
545c84e412fSHans de Goede 
546c84e412fSHans de Goede 	sd->hflip = v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_HFLIP,
547c84e412fSHans de Goede 				      0, 1, 1, 0);
548c84e412fSHans de Goede 	sd->vflip = v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_VFLIP,
549c84e412fSHans de Goede 				      0, 1, 1, 0);
550c84e412fSHans de Goede 
551c84e412fSHans de Goede 	if (hdl->error) {
552c84e412fSHans de Goede 		pr_err("Could not initialize controls\n");
553c84e412fSHans de Goede 		return hdl->error;
554c84e412fSHans de Goede 	}
555c84e412fSHans de Goede 
556c84e412fSHans de Goede 	v4l2_ctrl_cluster(2, &sd->hflip);
5570c0d06caSMauro Carvalho Chehab 
5580c0d06caSMauro Carvalho Chehab 	return 0;
5590c0d06caSMauro Carvalho Chehab }
5600c0d06caSMauro Carvalho Chehab 
s5k4aa_set_exposure(struct gspca_dev * gspca_dev,__s32 val)5610c0d06caSMauro Carvalho Chehab static int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
5620c0d06caSMauro Carvalho Chehab {
5630c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
5640c0d06caSMauro Carvalho Chehab 	u8 data = S5K4AA_PAGE_MAP_2;
5650c0d06caSMauro Carvalho Chehab 	int err;
5660c0d06caSMauro Carvalho Chehab 
56737d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_CONF, "Set exposure to %d\n", val);
5680c0d06caSMauro Carvalho Chehab 	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
5690c0d06caSMauro Carvalho Chehab 	if (err < 0)
5700c0d06caSMauro Carvalho Chehab 		return err;
5710c0d06caSMauro Carvalho Chehab 	data = (val >> 8) & 0xff;
5720c0d06caSMauro Carvalho Chehab 	err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);
5730c0d06caSMauro Carvalho Chehab 	if (err < 0)
5740c0d06caSMauro Carvalho Chehab 		return err;
5750c0d06caSMauro Carvalho Chehab 	data = val & 0xff;
5760c0d06caSMauro Carvalho Chehab 	err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);
5770c0d06caSMauro Carvalho Chehab 
5780c0d06caSMauro Carvalho Chehab 	return err;
5790c0d06caSMauro Carvalho Chehab }
5800c0d06caSMauro Carvalho Chehab 
s5k4aa_set_hvflip(struct gspca_dev * gspca_dev)581c84e412fSHans de Goede static int s5k4aa_set_hvflip(struct gspca_dev *gspca_dev)
5820c0d06caSMauro Carvalho Chehab {
5830c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
5840c0d06caSMauro Carvalho Chehab 	u8 data = S5K4AA_PAGE_MAP_2;
5850c0d06caSMauro Carvalho Chehab 	int err;
586c84e412fSHans de Goede 	int hflip = sd->hflip->val;
587c84e412fSHans de Goede 	int vflip = sd->vflip->val;
5880c0d06caSMauro Carvalho Chehab 
58937d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_CONF, "Set hvflip %d %d\n", hflip, vflip);
5900c0d06caSMauro Carvalho Chehab 	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
5910c0d06caSMauro Carvalho Chehab 	if (err < 0)
5920c0d06caSMauro Carvalho Chehab 		return err;
5930c0d06caSMauro Carvalho Chehab 
5940c0d06caSMauro Carvalho Chehab 	err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1);
5950c0d06caSMauro Carvalho Chehab 	if (err < 0)
5960c0d06caSMauro Carvalho Chehab 		return err;
5970c0d06caSMauro Carvalho Chehab 
598c84e412fSHans de Goede 	if (dmi_check_system(s5k4aa_vflip_dmi_table)) {
599c84e412fSHans de Goede 		hflip = !hflip;
600c84e412fSHans de Goede 		vflip = !vflip;
6010c0d06caSMauro Carvalho Chehab 	}
6020c0d06caSMauro Carvalho Chehab 
603c84e412fSHans de Goede 	data = (data & 0x7f) | (vflip << 7) | (hflip << 6);
6040c0d06caSMauro Carvalho Chehab 	err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
6050c0d06caSMauro Carvalho Chehab 	if (err < 0)
6060c0d06caSMauro Carvalho Chehab 		return err;
6070c0d06caSMauro Carvalho Chehab 
6080c0d06caSMauro Carvalho Chehab 	err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
6090c0d06caSMauro Carvalho Chehab 	if (err < 0)
6100c0d06caSMauro Carvalho Chehab 		return err;
611c84e412fSHans de Goede 	if (hflip)
6120c0d06caSMauro Carvalho Chehab 		data &= 0xfe;
6130c0d06caSMauro Carvalho Chehab 	else
6140c0d06caSMauro Carvalho Chehab 		data |= 0x01;
6150c0d06caSMauro Carvalho Chehab 	err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
616c84e412fSHans de Goede 	if (err < 0)
6170c0d06caSMauro Carvalho Chehab 		return err;
6180c0d06caSMauro Carvalho Chehab 
619c84e412fSHans de Goede 	err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
620c84e412fSHans de Goede 	if (err < 0)
621c84e412fSHans de Goede 		return err;
622c84e412fSHans de Goede 	if (vflip)
623c84e412fSHans de Goede 		data &= 0xfe;
624c84e412fSHans de Goede 	else
625c84e412fSHans de Goede 		data |= 0x01;
626c84e412fSHans de Goede 	err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
627c84e412fSHans de Goede 	if (err < 0)
628c84e412fSHans de Goede 		return err;
6290c0d06caSMauro Carvalho Chehab 
6300c0d06caSMauro Carvalho Chehab 	return 0;
6310c0d06caSMauro Carvalho Chehab }
6320c0d06caSMauro Carvalho Chehab 
s5k4aa_set_gain(struct gspca_dev * gspca_dev,__s32 val)6330c0d06caSMauro Carvalho Chehab static int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val)
6340c0d06caSMauro Carvalho Chehab {
6350c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
6360c0d06caSMauro Carvalho Chehab 	u8 data = S5K4AA_PAGE_MAP_2;
6370c0d06caSMauro Carvalho Chehab 	int err;
6380c0d06caSMauro Carvalho Chehab 
63937d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_CONF, "Set gain to %d\n", val);
6400c0d06caSMauro Carvalho Chehab 	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
6410c0d06caSMauro Carvalho Chehab 	if (err < 0)
6420c0d06caSMauro Carvalho Chehab 		return err;
6430c0d06caSMauro Carvalho Chehab 
6440c0d06caSMauro Carvalho Chehab 	data = val & 0xff;
6450c0d06caSMauro Carvalho Chehab 	err = m5602_write_sensor(sd, S5K4AA_GAIN, &data, 1);
6460c0d06caSMauro Carvalho Chehab 
6470c0d06caSMauro Carvalho Chehab 	return err;
6480c0d06caSMauro Carvalho Chehab }
6490c0d06caSMauro Carvalho Chehab 
s5k4aa_set_brightness(struct gspca_dev * gspca_dev,__s32 val)6500c0d06caSMauro Carvalho Chehab static int s5k4aa_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
6510c0d06caSMauro Carvalho Chehab {
6520c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
6530c0d06caSMauro Carvalho Chehab 	u8 data = S5K4AA_PAGE_MAP_2;
6540c0d06caSMauro Carvalho Chehab 	int err;
6550c0d06caSMauro Carvalho Chehab 
65637d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_CONF, "Set brightness to %d\n", val);
6570c0d06caSMauro Carvalho Chehab 	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
6580c0d06caSMauro Carvalho Chehab 	if (err < 0)
6590c0d06caSMauro Carvalho Chehab 		return err;
6600c0d06caSMauro Carvalho Chehab 
6610c0d06caSMauro Carvalho Chehab 	data = val & 0xff;
6620c0d06caSMauro Carvalho Chehab 	return m5602_write_sensor(sd, S5K4AA_BRIGHTNESS, &data, 1);
6630c0d06caSMauro Carvalho Chehab }
6640c0d06caSMauro Carvalho Chehab 
s5k4aa_set_noise(struct gspca_dev * gspca_dev,__s32 val)6650c0d06caSMauro Carvalho Chehab static int s5k4aa_set_noise(struct gspca_dev *gspca_dev, __s32 val)
6660c0d06caSMauro Carvalho Chehab {
6670c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
6680c0d06caSMauro Carvalho Chehab 	u8 data = S5K4AA_PAGE_MAP_2;
6690c0d06caSMauro Carvalho Chehab 	int err;
6700c0d06caSMauro Carvalho Chehab 
67137d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_CONF, "Set noise to %d\n", val);
6720c0d06caSMauro Carvalho Chehab 	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
6730c0d06caSMauro Carvalho Chehab 	if (err < 0)
6740c0d06caSMauro Carvalho Chehab 		return err;
6750c0d06caSMauro Carvalho Chehab 
6760c0d06caSMauro Carvalho Chehab 	data = val & 0x01;
6770c0d06caSMauro Carvalho Chehab 	return m5602_write_sensor(sd, S5K4AA_NOISE_SUPP, &data, 1);
6780c0d06caSMauro Carvalho Chehab }
6790c0d06caSMauro Carvalho Chehab 
s5k4aa_s_ctrl(struct v4l2_ctrl * ctrl)680c84e412fSHans de Goede static int s5k4aa_s_ctrl(struct v4l2_ctrl *ctrl)
681c84e412fSHans de Goede {
682c84e412fSHans de Goede 	struct gspca_dev *gspca_dev =
683c84e412fSHans de Goede 		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
684c84e412fSHans de Goede 	int err;
685c84e412fSHans de Goede 
686c84e412fSHans de Goede 	if (!gspca_dev->streaming)
687c84e412fSHans de Goede 		return 0;
688c84e412fSHans de Goede 
689c84e412fSHans de Goede 	switch (ctrl->id) {
690c84e412fSHans de Goede 	case V4L2_CID_BRIGHTNESS:
691c84e412fSHans de Goede 		err = s5k4aa_set_brightness(gspca_dev, ctrl->val);
692c84e412fSHans de Goede 		break;
693c84e412fSHans de Goede 	case V4L2_CID_EXPOSURE:
694c84e412fSHans de Goede 		err = s5k4aa_set_exposure(gspca_dev, ctrl->val);
695c84e412fSHans de Goede 		break;
696c84e412fSHans de Goede 	case V4L2_CID_GAIN:
697c84e412fSHans de Goede 		err = s5k4aa_set_gain(gspca_dev, ctrl->val);
698c84e412fSHans de Goede 		break;
699c84e412fSHans de Goede 	case V4L2_CID_SHARPNESS:
700c84e412fSHans de Goede 		err = s5k4aa_set_noise(gspca_dev, ctrl->val);
701c84e412fSHans de Goede 		break;
702c84e412fSHans de Goede 	case V4L2_CID_HFLIP:
703c84e412fSHans de Goede 		err = s5k4aa_set_hvflip(gspca_dev);
704c84e412fSHans de Goede 		break;
705c84e412fSHans de Goede 	default:
706c84e412fSHans de Goede 		return -EINVAL;
707c84e412fSHans de Goede 	}
708c84e412fSHans de Goede 
709c84e412fSHans de Goede 	return err;
710c84e412fSHans de Goede }
711c84e412fSHans de Goede 
s5k4aa_disconnect(struct sd * sd)7120c0d06caSMauro Carvalho Chehab void s5k4aa_disconnect(struct sd *sd)
7130c0d06caSMauro Carvalho Chehab {
7140c0d06caSMauro Carvalho Chehab 	sd->sensor = NULL;
7150c0d06caSMauro Carvalho Chehab }
7160c0d06caSMauro Carvalho Chehab 
s5k4aa_dump_registers(struct sd * sd)7170c0d06caSMauro Carvalho Chehab static void s5k4aa_dump_registers(struct sd *sd)
7180c0d06caSMauro Carvalho Chehab {
7190c0d06caSMauro Carvalho Chehab 	int address;
7200c0d06caSMauro Carvalho Chehab 	u8 page, old_page;
7210c0d06caSMauro Carvalho Chehab 	m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
7220c0d06caSMauro Carvalho Chehab 	for (page = 0; page < 16; page++) {
7230c0d06caSMauro Carvalho Chehab 		m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
7240c0d06caSMauro Carvalho Chehab 		pr_info("Dumping the s5k4aa register state for page 0x%x\n",
7250c0d06caSMauro Carvalho Chehab 			page);
7260c0d06caSMauro Carvalho Chehab 		for (address = 0; address <= 0xff; address++) {
7270c0d06caSMauro Carvalho Chehab 			u8 value = 0;
7280c0d06caSMauro Carvalho Chehab 			m5602_read_sensor(sd, address, &value, 1);
7290c0d06caSMauro Carvalho Chehab 			pr_info("register 0x%x contains 0x%x\n",
7300c0d06caSMauro Carvalho Chehab 				address, value);
7310c0d06caSMauro Carvalho Chehab 		}
7320c0d06caSMauro Carvalho Chehab 	}
7330c0d06caSMauro Carvalho Chehab 	pr_info("s5k4aa register state dump complete\n");
7340c0d06caSMauro Carvalho Chehab 
7350c0d06caSMauro Carvalho Chehab 	for (page = 0; page < 16; page++) {
7360c0d06caSMauro Carvalho Chehab 		m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
7370c0d06caSMauro Carvalho Chehab 		pr_info("Probing for which registers that are read/write for page 0x%x\n",
7380c0d06caSMauro Carvalho Chehab 			page);
7390c0d06caSMauro Carvalho Chehab 		for (address = 0; address <= 0xff; address++) {
7400c0d06caSMauro Carvalho Chehab 			u8 old_value, ctrl_value, test_value = 0xff;
7410c0d06caSMauro Carvalho Chehab 
7420c0d06caSMauro Carvalho Chehab 			m5602_read_sensor(sd, address, &old_value, 1);
7430c0d06caSMauro Carvalho Chehab 			m5602_write_sensor(sd, address, &test_value, 1);
7440c0d06caSMauro Carvalho Chehab 			m5602_read_sensor(sd, address, &ctrl_value, 1);
7450c0d06caSMauro Carvalho Chehab 
7460c0d06caSMauro Carvalho Chehab 			if (ctrl_value == test_value)
7470c0d06caSMauro Carvalho Chehab 				pr_info("register 0x%x is writeable\n",
7480c0d06caSMauro Carvalho Chehab 					address);
7490c0d06caSMauro Carvalho Chehab 			else
7500c0d06caSMauro Carvalho Chehab 				pr_info("register 0x%x is read only\n",
7510c0d06caSMauro Carvalho Chehab 					address);
7520c0d06caSMauro Carvalho Chehab 
7530c0d06caSMauro Carvalho Chehab 			/* Restore original value */
7540c0d06caSMauro Carvalho Chehab 			m5602_write_sensor(sd, address, &old_value, 1);
7550c0d06caSMauro Carvalho Chehab 		}
7560c0d06caSMauro Carvalho Chehab 	}
7570c0d06caSMauro Carvalho Chehab 	pr_info("Read/write register probing complete\n");
7580c0d06caSMauro Carvalho Chehab 	m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
7590c0d06caSMauro Carvalho Chehab }
760