10c0d06caSMauro Carvalho Chehab /* 20c0d06caSMauro Carvalho Chehab * Driver for the ov7660 sensor 30c0d06caSMauro Carvalho Chehab * 40c0d06caSMauro Carvalho Chehab * Copyright (C) 2009 Erik Andrén 50c0d06caSMauro Carvalho Chehab * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. 60c0d06caSMauro Carvalho Chehab * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br> 70c0d06caSMauro Carvalho Chehab * 80c0d06caSMauro Carvalho Chehab * Portions of code to USB interface and ALi driver software, 90c0d06caSMauro Carvalho Chehab * Copyright (c) 2006 Willem Duinker 100c0d06caSMauro Carvalho Chehab * v4l2 interface modeled after the V4L2 driver 110c0d06caSMauro Carvalho Chehab * for SN9C10x PC Camera Controllers 120c0d06caSMauro Carvalho Chehab * 130c0d06caSMauro Carvalho Chehab * This program is free software; you can redistribute it and/or 140c0d06caSMauro Carvalho Chehab * modify it under the terms of the GNU General Public License as 150c0d06caSMauro Carvalho Chehab * published by the Free Software Foundation, version 2. 160c0d06caSMauro Carvalho Chehab * 170c0d06caSMauro Carvalho Chehab */ 180c0d06caSMauro Carvalho Chehab 190c0d06caSMauro Carvalho Chehab #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 200c0d06caSMauro Carvalho Chehab 210c0d06caSMauro Carvalho Chehab #include "m5602_ov7660.h" 220c0d06caSMauro Carvalho Chehab 23c84e412fSHans de Goede static int ov7660_s_ctrl(struct v4l2_ctrl *ctrl); 24c84e412fSHans de Goede static void ov7660_dump_registers(struct sd *sd); 250c0d06caSMauro Carvalho Chehab 26123818eeSMauro Carvalho Chehab static const unsigned char preinit_ov7660[][4] = { 27123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, 28123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, 29123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, 30123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, 31123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, 32123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d}, 33123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, 34123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR, 0x03}, 35123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR, 0x03}, 36123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, 37123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, 38123818eeSMauro Carvalho Chehab 39123818eeSMauro Carvalho Chehab {SENSOR, OV7660_OFON, 0x0c}, 40123818eeSMauro Carvalho Chehab {SENSOR, OV7660_COM2, 0x11}, 41123818eeSMauro Carvalho Chehab {SENSOR, OV7660_COM7, 0x05}, 42123818eeSMauro Carvalho Chehab 43123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR, 0x01}, 44123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, 45123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, 46123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, 47123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, 48123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08}, 49123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, 50123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, 51123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, 52123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, 53123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, 54123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, 55123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, 56123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, 57123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_EN_L, 0x00} 58123818eeSMauro Carvalho Chehab }; 59123818eeSMauro Carvalho Chehab 60123818eeSMauro Carvalho Chehab static const unsigned char init_ov7660[][4] = { 61123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, 62123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, 63123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, 64123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, 65123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, 66123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d}, 67123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, 68123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR, 0x01}, 69123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR, 0x01}, 70123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, 71123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, 72123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, 73123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, 74123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, 75123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, 76123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, 77123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, 78123818eeSMauro Carvalho Chehab {SENSOR, OV7660_COM7, 0x80}, 79123818eeSMauro Carvalho Chehab {SENSOR, OV7660_CLKRC, 0x80}, 80123818eeSMauro Carvalho Chehab {SENSOR, OV7660_COM9, 0x4c}, 81123818eeSMauro Carvalho Chehab {SENSOR, OV7660_OFON, 0x43}, 82123818eeSMauro Carvalho Chehab {SENSOR, OV7660_COM12, 0x28}, 83123818eeSMauro Carvalho Chehab {SENSOR, OV7660_COM8, 0x00}, 84123818eeSMauro Carvalho Chehab {SENSOR, OV7660_COM10, 0x40}, 85123818eeSMauro Carvalho Chehab {SENSOR, OV7660_HSTART, 0x0c}, 86123818eeSMauro Carvalho Chehab {SENSOR, OV7660_HSTOP, 0x61}, 87123818eeSMauro Carvalho Chehab {SENSOR, OV7660_HREF, 0xa4}, 88123818eeSMauro Carvalho Chehab {SENSOR, OV7660_PSHFT, 0x0b}, 89123818eeSMauro Carvalho Chehab {SENSOR, OV7660_VSTART, 0x01}, 90123818eeSMauro Carvalho Chehab {SENSOR, OV7660_VSTOP, 0x7a}, 91123818eeSMauro Carvalho Chehab {SENSOR, OV7660_VSTOP, 0x00}, 92123818eeSMauro Carvalho Chehab {SENSOR, OV7660_COM7, 0x05}, 93123818eeSMauro Carvalho Chehab {SENSOR, OV7660_COM6, 0x42}, 94123818eeSMauro Carvalho Chehab {SENSOR, OV7660_BBIAS, 0x94}, 95123818eeSMauro Carvalho Chehab {SENSOR, OV7660_GbBIAS, 0x94}, 96123818eeSMauro Carvalho Chehab {SENSOR, OV7660_RSVD29, 0x94}, 97123818eeSMauro Carvalho Chehab {SENSOR, OV7660_RBIAS, 0x94}, 98123818eeSMauro Carvalho Chehab {SENSOR, OV7660_COM1, 0x00}, 99123818eeSMauro Carvalho Chehab {SENSOR, OV7660_AECH, 0x00}, 100123818eeSMauro Carvalho Chehab {SENSOR, OV7660_AECHH, 0x00}, 101123818eeSMauro Carvalho Chehab {SENSOR, OV7660_ADC, 0x05}, 102123818eeSMauro Carvalho Chehab {SENSOR, OV7660_COM13, 0x00}, 103123818eeSMauro Carvalho Chehab {SENSOR, OV7660_RSVDA1, 0x23}, 104123818eeSMauro Carvalho Chehab {SENSOR, OV7660_TSLB, 0x0d}, 105123818eeSMauro Carvalho Chehab {SENSOR, OV7660_HV, 0x80}, 106123818eeSMauro Carvalho Chehab {SENSOR, OV7660_LCC1, 0x00}, 107123818eeSMauro Carvalho Chehab {SENSOR, OV7660_LCC2, 0x00}, 108123818eeSMauro Carvalho Chehab {SENSOR, OV7660_LCC3, 0x10}, 109123818eeSMauro Carvalho Chehab {SENSOR, OV7660_LCC4, 0x40}, 110123818eeSMauro Carvalho Chehab {SENSOR, OV7660_LCC5, 0x01}, 111123818eeSMauro Carvalho Chehab 112123818eeSMauro Carvalho Chehab {SENSOR, OV7660_AECH, 0x20}, 113123818eeSMauro Carvalho Chehab {SENSOR, OV7660_COM1, 0x00}, 114123818eeSMauro Carvalho Chehab {SENSOR, OV7660_OFON, 0x0c}, 115123818eeSMauro Carvalho Chehab {SENSOR, OV7660_COM2, 0x11}, 116123818eeSMauro Carvalho Chehab {SENSOR, OV7660_COM7, 0x05}, 117123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR, 0x01}, 118123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, 119123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, 120123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, 121123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, 122123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08}, 123123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, 124123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, 125123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, 126123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, 127123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, 128123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, 129123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, 130123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, 131123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, 132123818eeSMauro Carvalho Chehab {SENSOR, OV7660_AECH, 0x5f}, 133123818eeSMauro Carvalho Chehab {SENSOR, OV7660_COM1, 0x03}, 134123818eeSMauro Carvalho Chehab {SENSOR, OV7660_OFON, 0x0c}, 135123818eeSMauro Carvalho Chehab {SENSOR, OV7660_COM2, 0x11}, 136123818eeSMauro Carvalho Chehab {SENSOR, OV7660_COM7, 0x05}, 137123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR, 0x01}, 138123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, 139123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, 140123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, 141123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, 142123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08}, 143123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, 144123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, 145123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, 146123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, 147123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, 148123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, 149123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, 150123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, 151123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, 152123818eeSMauro Carvalho Chehab 153123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06}, 154123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, 155123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, 156123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, 157123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81}, 158123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82}, 159123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SIG_INI, 0x01}, 160123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, 161123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_VSYNC_PARA, 0x08}, 162123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, 163123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, 164123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_VSYNC_PARA, 0x01}, 165123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_VSYNC_PARA, 0xec}, 166123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, 167123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, 168123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SIG_INI, 0x00}, 169123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SIG_INI, 0x02}, 170123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_HSYNC_PARA, 0x00}, 171123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_HSYNC_PARA, 0x27}, 172123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_HSYNC_PARA, 0x02}, 173123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_HSYNC_PARA, 0xa7}, 174123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SIG_INI, 0x00}, 175123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, 176123818eeSMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, 177123818eeSMauro Carvalho Chehab }; 178123818eeSMauro Carvalho Chehab 1790c0d06caSMauro Carvalho Chehab static struct v4l2_pix_format ov7660_modes[] = { 1800c0d06caSMauro Carvalho Chehab { 1810c0d06caSMauro Carvalho Chehab 640, 1820c0d06caSMauro Carvalho Chehab 480, 1830c0d06caSMauro Carvalho Chehab V4L2_PIX_FMT_SBGGR8, 1840c0d06caSMauro Carvalho Chehab V4L2_FIELD_NONE, 1850c0d06caSMauro Carvalho Chehab .sizeimage = 1860c0d06caSMauro Carvalho Chehab 640 * 480, 1870c0d06caSMauro Carvalho Chehab .bytesperline = 640, 1880c0d06caSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB, 1890c0d06caSMauro Carvalho Chehab .priv = 0 1900c0d06caSMauro Carvalho Chehab } 1910c0d06caSMauro Carvalho Chehab }; 1920c0d06caSMauro Carvalho Chehab 193c84e412fSHans de Goede static const struct v4l2_ctrl_ops ov7660_ctrl_ops = { 194c84e412fSHans de Goede .s_ctrl = ov7660_s_ctrl, 195c84e412fSHans de Goede }; 1960c0d06caSMauro Carvalho Chehab 1970c0d06caSMauro Carvalho Chehab int ov7660_probe(struct sd *sd) 1980c0d06caSMauro Carvalho Chehab { 1990c0d06caSMauro Carvalho Chehab int err = 0, i; 2000c0d06caSMauro Carvalho Chehab u8 prod_id = 0, ver_id = 0; 2010c0d06caSMauro Carvalho Chehab 2020c0d06caSMauro Carvalho Chehab if (force_sensor) { 2030c0d06caSMauro Carvalho Chehab if (force_sensor == OV7660_SENSOR) { 2040c0d06caSMauro Carvalho Chehab pr_info("Forcing an %s sensor\n", ov7660.name); 2050c0d06caSMauro Carvalho Chehab goto sensor_found; 2060c0d06caSMauro Carvalho Chehab } 2070c0d06caSMauro Carvalho Chehab /* If we want to force another sensor, 2080c0d06caSMauro Carvalho Chehab don't try to probe this one */ 2090c0d06caSMauro Carvalho Chehab return -ENODEV; 2100c0d06caSMauro Carvalho Chehab } 2110c0d06caSMauro Carvalho Chehab 2120c0d06caSMauro Carvalho Chehab /* Do the preinit */ 2130c0d06caSMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(preinit_ov7660) && !err; i++) { 2140c0d06caSMauro Carvalho Chehab u8 data[2]; 2150c0d06caSMauro Carvalho Chehab 2160c0d06caSMauro Carvalho Chehab if (preinit_ov7660[i][0] == BRIDGE) { 2170c0d06caSMauro Carvalho Chehab err = m5602_write_bridge(sd, 2180c0d06caSMauro Carvalho Chehab preinit_ov7660[i][1], 2190c0d06caSMauro Carvalho Chehab preinit_ov7660[i][2]); 2200c0d06caSMauro Carvalho Chehab } else { 2210c0d06caSMauro Carvalho Chehab data[0] = preinit_ov7660[i][2]; 2220c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, 2230c0d06caSMauro Carvalho Chehab preinit_ov7660[i][1], data, 1); 2240c0d06caSMauro Carvalho Chehab } 2250c0d06caSMauro Carvalho Chehab } 2260c0d06caSMauro Carvalho Chehab if (err < 0) 2270c0d06caSMauro Carvalho Chehab return err; 2280c0d06caSMauro Carvalho Chehab 2290c0d06caSMauro Carvalho Chehab if (m5602_read_sensor(sd, OV7660_PID, &prod_id, 1)) 2300c0d06caSMauro Carvalho Chehab return -ENODEV; 2310c0d06caSMauro Carvalho Chehab 2320c0d06caSMauro Carvalho Chehab if (m5602_read_sensor(sd, OV7660_VER, &ver_id, 1)) 2330c0d06caSMauro Carvalho Chehab return -ENODEV; 2340c0d06caSMauro Carvalho Chehab 2350c0d06caSMauro Carvalho Chehab pr_info("Sensor reported 0x%x%x\n", prod_id, ver_id); 2360c0d06caSMauro Carvalho Chehab 2370c0d06caSMauro Carvalho Chehab if ((prod_id == 0x76) && (ver_id == 0x60)) { 2380c0d06caSMauro Carvalho Chehab pr_info("Detected a ov7660 sensor\n"); 2390c0d06caSMauro Carvalho Chehab goto sensor_found; 2400c0d06caSMauro Carvalho Chehab } 2410c0d06caSMauro Carvalho Chehab return -ENODEV; 2420c0d06caSMauro Carvalho Chehab 2430c0d06caSMauro Carvalho Chehab sensor_found: 2440c0d06caSMauro Carvalho Chehab sd->gspca_dev.cam.cam_mode = ov7660_modes; 2450c0d06caSMauro Carvalho Chehab sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov7660_modes); 2460c0d06caSMauro Carvalho Chehab 2470c0d06caSMauro Carvalho Chehab return 0; 2480c0d06caSMauro Carvalho Chehab } 2490c0d06caSMauro Carvalho Chehab 2500c0d06caSMauro Carvalho Chehab int ov7660_init(struct sd *sd) 2510c0d06caSMauro Carvalho Chehab { 2529dc033f1SMauro Carvalho Chehab int i, err; 2530c0d06caSMauro Carvalho Chehab 2540c0d06caSMauro Carvalho Chehab /* Init the sensor */ 2550c0d06caSMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(init_ov7660); i++) { 2560c0d06caSMauro Carvalho Chehab u8 data[2]; 2570c0d06caSMauro Carvalho Chehab 2580c0d06caSMauro Carvalho Chehab if (init_ov7660[i][0] == BRIDGE) { 2590c0d06caSMauro Carvalho Chehab err = m5602_write_bridge(sd, 2600c0d06caSMauro Carvalho Chehab init_ov7660[i][1], 2610c0d06caSMauro Carvalho Chehab init_ov7660[i][2]); 2620c0d06caSMauro Carvalho Chehab } else { 2630c0d06caSMauro Carvalho Chehab data[0] = init_ov7660[i][2]; 2640c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, 2650c0d06caSMauro Carvalho Chehab init_ov7660[i][1], data, 1); 2660c0d06caSMauro Carvalho Chehab } 2679dc033f1SMauro Carvalho Chehab if (err < 0) 2689dc033f1SMauro Carvalho Chehab return err; 2690c0d06caSMauro Carvalho Chehab } 2700c0d06caSMauro Carvalho Chehab 2710c0d06caSMauro Carvalho Chehab if (dump_sensor) 2720c0d06caSMauro Carvalho Chehab ov7660_dump_registers(sd); 2730c0d06caSMauro Carvalho Chehab 274c84e412fSHans de Goede return 0; 275c84e412fSHans de Goede } 2760c0d06caSMauro Carvalho Chehab 277c84e412fSHans de Goede int ov7660_init_controls(struct sd *sd) 278c84e412fSHans de Goede { 279c84e412fSHans de Goede struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; 2800c0d06caSMauro Carvalho Chehab 281c84e412fSHans de Goede sd->gspca_dev.vdev.ctrl_handler = hdl; 282c84e412fSHans de Goede v4l2_ctrl_handler_init(hdl, 6); 2830c0d06caSMauro Carvalho Chehab 284c84e412fSHans de Goede v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE, 285c84e412fSHans de Goede 0, 1, 1, 1); 286c84e412fSHans de Goede v4l2_ctrl_new_std_menu(hdl, &ov7660_ctrl_ops, 287c84e412fSHans de Goede V4L2_CID_EXPOSURE_AUTO, 1, 0, V4L2_EXPOSURE_AUTO); 2880c0d06caSMauro Carvalho Chehab 289c84e412fSHans de Goede sd->autogain = v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops, 290c84e412fSHans de Goede V4L2_CID_AUTOGAIN, 0, 1, 1, 1); 291c84e412fSHans de Goede sd->gain = v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops, V4L2_CID_GAIN, 0, 292c84e412fSHans de Goede 255, 1, OV7660_DEFAULT_GAIN); 2930c0d06caSMauro Carvalho Chehab 294c84e412fSHans de Goede sd->hflip = v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops, V4L2_CID_HFLIP, 295c84e412fSHans de Goede 0, 1, 1, 0); 296c84e412fSHans de Goede sd->vflip = v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops, V4L2_CID_VFLIP, 297c84e412fSHans de Goede 0, 1, 1, 0); 298c84e412fSHans de Goede 299c84e412fSHans de Goede if (hdl->error) { 300c84e412fSHans de Goede pr_err("Could not initialize controls\n"); 301c84e412fSHans de Goede return hdl->error; 302c84e412fSHans de Goede } 303c84e412fSHans de Goede 304c84e412fSHans de Goede v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, false); 305c84e412fSHans de Goede v4l2_ctrl_cluster(2, &sd->hflip); 306c84e412fSHans de Goede 307c84e412fSHans de Goede return 0; 3080c0d06caSMauro Carvalho Chehab } 3090c0d06caSMauro Carvalho Chehab 3100c0d06caSMauro Carvalho Chehab int ov7660_start(struct sd *sd) 3110c0d06caSMauro Carvalho Chehab { 3120c0d06caSMauro Carvalho Chehab return 0; 3130c0d06caSMauro Carvalho Chehab } 3140c0d06caSMauro Carvalho Chehab 3150c0d06caSMauro Carvalho Chehab int ov7660_stop(struct sd *sd) 3160c0d06caSMauro Carvalho Chehab { 3170c0d06caSMauro Carvalho Chehab return 0; 3180c0d06caSMauro Carvalho Chehab } 3190c0d06caSMauro Carvalho Chehab 3200c0d06caSMauro Carvalho Chehab void ov7660_disconnect(struct sd *sd) 3210c0d06caSMauro Carvalho Chehab { 3220c0d06caSMauro Carvalho Chehab ov7660_stop(sd); 3230c0d06caSMauro Carvalho Chehab 3240c0d06caSMauro Carvalho Chehab sd->sensor = NULL; 3250c0d06caSMauro Carvalho Chehab } 3260c0d06caSMauro Carvalho Chehab 3270c0d06caSMauro Carvalho Chehab static int ov7660_set_gain(struct gspca_dev *gspca_dev, __s32 val) 3280c0d06caSMauro Carvalho Chehab { 3290c0d06caSMauro Carvalho Chehab int err; 330c84e412fSHans de Goede u8 i2c_data = val; 3310c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev; 3320c0d06caSMauro Carvalho Chehab 333c93396e1STheodore Kilgore PDEBUG(D_CONF, "Setting gain to %d", val); 3340c0d06caSMauro Carvalho Chehab 3350c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, OV7660_GAIN, &i2c_data, 1); 3360c0d06caSMauro Carvalho Chehab return err; 3370c0d06caSMauro Carvalho Chehab } 3380c0d06caSMauro Carvalho Chehab 3390c0d06caSMauro Carvalho Chehab static int ov7660_set_auto_white_balance(struct gspca_dev *gspca_dev, 3400c0d06caSMauro Carvalho Chehab __s32 val) 3410c0d06caSMauro Carvalho Chehab { 3420c0d06caSMauro Carvalho Chehab int err; 3430c0d06caSMauro Carvalho Chehab u8 i2c_data; 3440c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev; 3450c0d06caSMauro Carvalho Chehab 346c93396e1STheodore Kilgore PDEBUG(D_CONF, "Set auto white balance to %d", val); 3470c0d06caSMauro Carvalho Chehab 3480c0d06caSMauro Carvalho Chehab err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1); 3490c0d06caSMauro Carvalho Chehab if (err < 0) 3500c0d06caSMauro Carvalho Chehab return err; 3510c0d06caSMauro Carvalho Chehab 3520c0d06caSMauro Carvalho Chehab i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1)); 3530c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1); 3540c0d06caSMauro Carvalho Chehab 3550c0d06caSMauro Carvalho Chehab return err; 3560c0d06caSMauro Carvalho Chehab } 3570c0d06caSMauro Carvalho Chehab 3580c0d06caSMauro Carvalho Chehab static int ov7660_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val) 3590c0d06caSMauro Carvalho Chehab { 3600c0d06caSMauro Carvalho Chehab int err; 3610c0d06caSMauro Carvalho Chehab u8 i2c_data; 3620c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev; 3630c0d06caSMauro Carvalho Chehab 364c93396e1STheodore Kilgore PDEBUG(D_CONF, "Set auto gain control to %d", val); 3650c0d06caSMauro Carvalho Chehab 3660c0d06caSMauro Carvalho Chehab err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1); 3670c0d06caSMauro Carvalho Chehab if (err < 0) 3680c0d06caSMauro Carvalho Chehab return err; 3690c0d06caSMauro Carvalho Chehab 3700c0d06caSMauro Carvalho Chehab i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2)); 3710c0d06caSMauro Carvalho Chehab 3720c0d06caSMauro Carvalho Chehab return m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1); 3730c0d06caSMauro Carvalho Chehab } 3740c0d06caSMauro Carvalho Chehab 3750c0d06caSMauro Carvalho Chehab static int ov7660_set_auto_exposure(struct gspca_dev *gspca_dev, 3760c0d06caSMauro Carvalho Chehab __s32 val) 3770c0d06caSMauro Carvalho Chehab { 3780c0d06caSMauro Carvalho Chehab int err; 3790c0d06caSMauro Carvalho Chehab u8 i2c_data; 3800c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev; 3810c0d06caSMauro Carvalho Chehab 382c93396e1STheodore Kilgore PDEBUG(D_CONF, "Set auto exposure control to %d", val); 3830c0d06caSMauro Carvalho Chehab 3840c0d06caSMauro Carvalho Chehab err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1); 3850c0d06caSMauro Carvalho Chehab if (err < 0) 3860c0d06caSMauro Carvalho Chehab return err; 3870c0d06caSMauro Carvalho Chehab 388c84e412fSHans de Goede val = (val == V4L2_EXPOSURE_AUTO); 3890c0d06caSMauro Carvalho Chehab i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0)); 3900c0d06caSMauro Carvalho Chehab 3910c0d06caSMauro Carvalho Chehab return m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1); 3920c0d06caSMauro Carvalho Chehab } 3930c0d06caSMauro Carvalho Chehab 394c84e412fSHans de Goede static int ov7660_set_hvflip(struct gspca_dev *gspca_dev) 3950c0d06caSMauro Carvalho Chehab { 3960c0d06caSMauro Carvalho Chehab int err; 3970c0d06caSMauro Carvalho Chehab u8 i2c_data; 3980c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev; 3990c0d06caSMauro Carvalho Chehab 400c93396e1STheodore Kilgore PDEBUG(D_CONF, "Set hvflip to %d, %d", sd->hflip->val, sd->vflip->val); 4010c0d06caSMauro Carvalho Chehab 402c84e412fSHans de Goede i2c_data = (sd->hflip->val << 5) | (sd->vflip->val << 4); 4030c0d06caSMauro Carvalho Chehab 4040c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, OV7660_MVFP, &i2c_data, 1); 4050c0d06caSMauro Carvalho Chehab 4060c0d06caSMauro Carvalho Chehab return err; 4070c0d06caSMauro Carvalho Chehab } 4080c0d06caSMauro Carvalho Chehab 409c84e412fSHans de Goede static int ov7660_s_ctrl(struct v4l2_ctrl *ctrl) 4100c0d06caSMauro Carvalho Chehab { 411c84e412fSHans de Goede struct gspca_dev *gspca_dev = 412c84e412fSHans de Goede container_of(ctrl->handler, struct gspca_dev, ctrl_handler); 4130c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev; 4140c0d06caSMauro Carvalho Chehab int err; 4150c0d06caSMauro Carvalho Chehab 416c84e412fSHans de Goede if (!gspca_dev->streaming) 417c84e412fSHans de Goede return 0; 4180c0d06caSMauro Carvalho Chehab 419c84e412fSHans de Goede switch (ctrl->id) { 420c84e412fSHans de Goede case V4L2_CID_AUTO_WHITE_BALANCE: 421c84e412fSHans de Goede err = ov7660_set_auto_white_balance(gspca_dev, ctrl->val); 422c84e412fSHans de Goede break; 423c84e412fSHans de Goede case V4L2_CID_EXPOSURE_AUTO: 424c84e412fSHans de Goede err = ov7660_set_auto_exposure(gspca_dev, ctrl->val); 425c84e412fSHans de Goede break; 426c84e412fSHans de Goede case V4L2_CID_AUTOGAIN: 427c84e412fSHans de Goede err = ov7660_set_auto_gain(gspca_dev, ctrl->val); 428c84e412fSHans de Goede if (err || ctrl->val) 4290c0d06caSMauro Carvalho Chehab return err; 430c84e412fSHans de Goede err = ov7660_set_gain(gspca_dev, sd->gain->val); 431c84e412fSHans de Goede break; 432c84e412fSHans de Goede case V4L2_CID_HFLIP: 433c84e412fSHans de Goede err = ov7660_set_hvflip(gspca_dev); 434c84e412fSHans de Goede break; 435c84e412fSHans de Goede default: 436c84e412fSHans de Goede return -EINVAL; 437c84e412fSHans de Goede } 4380c0d06caSMauro Carvalho Chehab 4390c0d06caSMauro Carvalho Chehab return err; 4400c0d06caSMauro Carvalho Chehab } 4410c0d06caSMauro Carvalho Chehab 4420c0d06caSMauro Carvalho Chehab static void ov7660_dump_registers(struct sd *sd) 4430c0d06caSMauro Carvalho Chehab { 4440c0d06caSMauro Carvalho Chehab int address; 4450c0d06caSMauro Carvalho Chehab pr_info("Dumping the ov7660 register state\n"); 4460c0d06caSMauro Carvalho Chehab for (address = 0; address < 0xa9; address++) { 4470c0d06caSMauro Carvalho Chehab u8 value; 4480c0d06caSMauro Carvalho Chehab m5602_read_sensor(sd, address, &value, 1); 4490c0d06caSMauro Carvalho Chehab pr_info("register 0x%x contains 0x%x\n", address, value); 4500c0d06caSMauro Carvalho Chehab } 4510c0d06caSMauro Carvalho Chehab 4520c0d06caSMauro Carvalho Chehab pr_info("ov7660 register state dump complete\n"); 4530c0d06caSMauro Carvalho Chehab 4540c0d06caSMauro Carvalho Chehab pr_info("Probing for which registers that are read/write\n"); 4550c0d06caSMauro Carvalho Chehab for (address = 0; address < 0xff; address++) { 4560c0d06caSMauro Carvalho Chehab u8 old_value, ctrl_value; 4570c0d06caSMauro Carvalho Chehab u8 test_value[2] = {0xff, 0xff}; 4580c0d06caSMauro Carvalho Chehab 4590c0d06caSMauro Carvalho Chehab m5602_read_sensor(sd, address, &old_value, 1); 4600c0d06caSMauro Carvalho Chehab m5602_write_sensor(sd, address, test_value, 1); 4610c0d06caSMauro Carvalho Chehab m5602_read_sensor(sd, address, &ctrl_value, 1); 4620c0d06caSMauro Carvalho Chehab 4630c0d06caSMauro Carvalho Chehab if (ctrl_value == test_value[0]) 4640c0d06caSMauro Carvalho Chehab pr_info("register 0x%x is writeable\n", address); 4650c0d06caSMauro Carvalho Chehab else 4660c0d06caSMauro Carvalho Chehab pr_info("register 0x%x is read only\n", address); 4670c0d06caSMauro Carvalho Chehab 4680c0d06caSMauro Carvalho Chehab /* Restore original value */ 4690c0d06caSMauro Carvalho Chehab m5602_write_sensor(sd, address, &old_value, 1); 4700c0d06caSMauro Carvalho Chehab } 4710c0d06caSMauro Carvalho Chehab } 472