1/*************************************************************************** 2 * Plug-in for OV7630 image sensor connected to the SN9C1xx PC Camera * 3 * Controllers * 4 * * 5 * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * 6 * * 7 * This program is free software; you can redistribute it and/or modify * 8 * it under the terms of the GNU General Public License as published by * 9 * the Free Software Foundation; either version 2 of the License, or * 10 * (at your option) any later version. * 11 * * 12 * This program is distributed in the hope that it will be useful, * 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 * GNU General Public License for more details. * 16 * * 17 * You should have received a copy of the GNU General Public License * 18 * along with this program; if not, write to the Free Software * 19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * 20 ***************************************************************************/ 21 22#include "sn9c102_sensor.h" 23 24 25static int ov7630_init(struct sn9c102_device* cam) 26{ 27 int err = 0; 28 29 switch (sn9c102_get_bridge(cam)) { 30 case BRIDGE_SN9C101: 31 case BRIDGE_SN9C102: 32 err = sn9c102_write_const_regs(cam, {0x00, 0x14}, {0x60, 0x17}, 33 {0x0f, 0x18}, {0x50, 0x19}); 34 35 err += sn9c102_i2c_write(cam, 0x12, 0x8d); 36 err += sn9c102_i2c_write(cam, 0x12, 0x0d); 37 err += sn9c102_i2c_write(cam, 0x11, 0x00); 38 err += sn9c102_i2c_write(cam, 0x15, 0x35); 39 err += sn9c102_i2c_write(cam, 0x16, 0x03); 40 err += sn9c102_i2c_write(cam, 0x17, 0x1c); 41 err += sn9c102_i2c_write(cam, 0x18, 0xbd); 42 err += sn9c102_i2c_write(cam, 0x19, 0x06); 43 err += sn9c102_i2c_write(cam, 0x1a, 0xf6); 44 err += sn9c102_i2c_write(cam, 0x1b, 0x04); 45 err += sn9c102_i2c_write(cam, 0x20, 0x44); 46 err += sn9c102_i2c_write(cam, 0x23, 0xee); 47 err += sn9c102_i2c_write(cam, 0x26, 0xa0); 48 err += sn9c102_i2c_write(cam, 0x27, 0x9a); 49 err += sn9c102_i2c_write(cam, 0x28, 0x20); 50 err += sn9c102_i2c_write(cam, 0x29, 0x30); 51 err += sn9c102_i2c_write(cam, 0x2f, 0x3d); 52 err += sn9c102_i2c_write(cam, 0x30, 0x24); 53 err += sn9c102_i2c_write(cam, 0x32, 0x86); 54 err += sn9c102_i2c_write(cam, 0x60, 0xa9); 55 err += sn9c102_i2c_write(cam, 0x61, 0x42); 56 err += sn9c102_i2c_write(cam, 0x65, 0x00); 57 err += sn9c102_i2c_write(cam, 0x69, 0x38); 58 err += sn9c102_i2c_write(cam, 0x6f, 0x88); 59 err += sn9c102_i2c_write(cam, 0x70, 0x0b); 60 err += sn9c102_i2c_write(cam, 0x71, 0x00); 61 err += sn9c102_i2c_write(cam, 0x74, 0x21); 62 err += sn9c102_i2c_write(cam, 0x7d, 0xf7); 63 break; 64 case BRIDGE_SN9C103: 65 err = sn9c102_write_const_regs(cam, {0x00, 0x02}, {0x00, 0x03}, 66 {0x1a, 0x04}, {0x20, 0x05}, 67 {0x20, 0x06}, {0x20, 0x07}, 68 {0x03, 0x10}, {0x0a, 0x14}, 69 {0x60, 0x17}, {0x0f, 0x18}, 70 {0x50, 0x19}, {0x1d, 0x1a}, 71 {0x10, 0x1b}, {0x02, 0x1c}, 72 {0x03, 0x1d}, {0x0f, 0x1e}, 73 {0x0c, 0x1f}, {0x00, 0x20}, 74 {0x10, 0x21}, {0x20, 0x22}, 75 {0x30, 0x23}, {0x40, 0x24}, 76 {0x50, 0x25}, {0x60, 0x26}, 77 {0x70, 0x27}, {0x80, 0x28}, 78 {0x90, 0x29}, {0xa0, 0x2a}, 79 {0xb0, 0x2b}, {0xc0, 0x2c}, 80 {0xd0, 0x2d}, {0xe0, 0x2e}, 81 {0xf0, 0x2f}, {0xff, 0x30}); 82 83 err += sn9c102_i2c_write(cam, 0x12, 0x8d); 84 err += sn9c102_i2c_write(cam, 0x12, 0x0d); 85 err += sn9c102_i2c_write(cam, 0x15, 0x34); 86 err += sn9c102_i2c_write(cam, 0x11, 0x01); 87 err += sn9c102_i2c_write(cam, 0x1b, 0x04); 88 err += sn9c102_i2c_write(cam, 0x20, 0x44); 89 err += sn9c102_i2c_write(cam, 0x23, 0xee); 90 err += sn9c102_i2c_write(cam, 0x26, 0xa0); 91 err += sn9c102_i2c_write(cam, 0x27, 0x9a); 92 err += sn9c102_i2c_write(cam, 0x28, 0x20); 93 err += sn9c102_i2c_write(cam, 0x29, 0x30); 94 err += sn9c102_i2c_write(cam, 0x2f, 0x3d); 95 err += sn9c102_i2c_write(cam, 0x30, 0x24); 96 err += sn9c102_i2c_write(cam, 0x32, 0x86); 97 err += sn9c102_i2c_write(cam, 0x60, 0xa9); 98 err += sn9c102_i2c_write(cam, 0x61, 0x42); 99 err += sn9c102_i2c_write(cam, 0x65, 0x00); 100 err += sn9c102_i2c_write(cam, 0x69, 0x38); 101 err += sn9c102_i2c_write(cam, 0x6f, 0x88); 102 err += sn9c102_i2c_write(cam, 0x70, 0x0b); 103 err += sn9c102_i2c_write(cam, 0x71, 0x00); 104 err += sn9c102_i2c_write(cam, 0x74, 0x21); 105 err += sn9c102_i2c_write(cam, 0x7d, 0xf7); 106 break; 107 default: 108 break; 109 } 110 111 return err; 112} 113 114 115static int ov7630_get_ctrl(struct sn9c102_device* cam, 116 struct v4l2_control* ctrl) 117{ 118 int err = 0; 119 120 switch (ctrl->id) { 121 case V4L2_CID_EXPOSURE: 122 if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0) 123 return -EIO; 124 break; 125 case V4L2_CID_RED_BALANCE: 126 ctrl->value = sn9c102_pread_reg(cam, 0x07); 127 break; 128 case V4L2_CID_BLUE_BALANCE: 129 ctrl->value = sn9c102_pread_reg(cam, 0x06); 130 break; 131 case SN9C102_V4L2_CID_GREEN_BALANCE: 132 ctrl->value = sn9c102_pread_reg(cam, 0x05); 133 break; 134 case V4L2_CID_GAIN: 135 if ((ctrl->value = sn9c102_i2c_read(cam, 0x00)) < 0) 136 return -EIO; 137 ctrl->value &= 0x3f; 138 break; 139 case V4L2_CID_DO_WHITE_BALANCE: 140 if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) 141 return -EIO; 142 ctrl->value &= 0x3f; 143 break; 144 case V4L2_CID_WHITENESS: 145 if ((ctrl->value = sn9c102_i2c_read(cam, 0x0d)) < 0) 146 return -EIO; 147 ctrl->value &= 0x3f; 148 break; 149 case V4L2_CID_AUTOGAIN: 150 if ((ctrl->value = sn9c102_i2c_read(cam, 0x13)) < 0) 151 return -EIO; 152 ctrl->value &= 0x01; 153 break; 154 case V4L2_CID_VFLIP: 155 if ((ctrl->value = sn9c102_i2c_read(cam, 0x75)) < 0) 156 return -EIO; 157 ctrl->value = (ctrl->value & 0x80) ? 1 : 0; 158 break; 159 case SN9C102_V4L2_CID_GAMMA: 160 if ((ctrl->value = sn9c102_i2c_read(cam, 0x14)) < 0) 161 return -EIO; 162 ctrl->value = (ctrl->value & 0x02) ? 1 : 0; 163 break; 164 case SN9C102_V4L2_CID_BAND_FILTER: 165 if ((ctrl->value = sn9c102_i2c_read(cam, 0x2d)) < 0) 166 return -EIO; 167 ctrl->value = (ctrl->value & 0x02) ? 1 : 0; 168 break; 169 default: 170 return -EINVAL; 171 } 172 173 return err ? -EIO : 0; 174} 175 176 177static int ov7630_set_ctrl(struct sn9c102_device* cam, 178 const struct v4l2_control* ctrl) 179{ 180 int err = 0; 181 182 switch (ctrl->id) { 183 case V4L2_CID_EXPOSURE: 184 err += sn9c102_i2c_write(cam, 0x10, ctrl->value); 185 break; 186 case V4L2_CID_RED_BALANCE: 187 err += sn9c102_write_reg(cam, ctrl->value, 0x07); 188 break; 189 case V4L2_CID_BLUE_BALANCE: 190 err += sn9c102_write_reg(cam, ctrl->value, 0x06); 191 break; 192 case SN9C102_V4L2_CID_GREEN_BALANCE: 193 err += sn9c102_write_reg(cam, ctrl->value, 0x05); 194 break; 195 case V4L2_CID_GAIN: 196 err += sn9c102_i2c_write(cam, 0x00, ctrl->value); 197 break; 198 case V4L2_CID_DO_WHITE_BALANCE: 199 err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); 200 break; 201 case V4L2_CID_WHITENESS: 202 err += sn9c102_i2c_write(cam, 0x0d, ctrl->value); 203 break; 204 case V4L2_CID_AUTOGAIN: 205 err += sn9c102_i2c_write(cam, 0x13, ctrl->value | 206 (ctrl->value << 1)); 207 break; 208 case V4L2_CID_VFLIP: 209 err += sn9c102_i2c_write(cam, 0x75, 0x0e | (ctrl->value << 7)); 210 break; 211 case SN9C102_V4L2_CID_GAMMA: 212 err += sn9c102_i2c_write(cam, 0x14, ctrl->value << 2); 213 break; 214 case SN9C102_V4L2_CID_BAND_FILTER: 215 err += sn9c102_i2c_write(cam, 0x2d, ctrl->value << 2); 216 break; 217 default: 218 return -EINVAL; 219 } 220 221 return err ? -EIO : 0; 222} 223 224 225static int ov7630_set_crop(struct sn9c102_device* cam, 226 const struct v4l2_rect* rect) 227{ 228 struct sn9c102_sensor* s = sn9c102_get_sensor(cam); 229 int err = 0; 230 u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1, 231 v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; 232 233 err += sn9c102_write_reg(cam, h_start, 0x12); 234 err += sn9c102_write_reg(cam, v_start, 0x13); 235 236 return err; 237} 238 239 240static int ov7630_set_pix_format(struct sn9c102_device* cam, 241 const struct v4l2_pix_format* pix) 242{ 243 int err = 0; 244 245 if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) 246 err += sn9c102_write_reg(cam, 0x20, 0x19); 247 else 248 err += sn9c102_write_reg(cam, 0x50, 0x19); 249 250 return err; 251} 252 253 254static const struct sn9c102_sensor ov7630 = { 255 .name = "OV7630", 256 .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", 257 .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, 258 .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, 259 .frequency = SN9C102_I2C_100KHZ, 260 .interface = SN9C102_I2C_2WIRES, 261 .i2c_slave_id = 0x21, 262 .init = &ov7630_init, 263 .qctrl = { 264 { 265 .id = V4L2_CID_GAIN, 266 .type = V4L2_CTRL_TYPE_INTEGER, 267 .name = "global gain", 268 .minimum = 0x00, 269 .maximum = 0x3f, 270 .step = 0x01, 271 .default_value = 0x14, 272 .flags = 0, 273 }, 274 { 275 .id = V4L2_CID_EXPOSURE, 276 .type = V4L2_CTRL_TYPE_INTEGER, 277 .name = "exposure", 278 .minimum = 0x00, 279 .maximum = 0xff, 280 .step = 0x01, 281 .default_value = 0x60, 282 .flags = 0, 283 }, 284 { 285 .id = V4L2_CID_WHITENESS, 286 .type = V4L2_CTRL_TYPE_INTEGER, 287 .name = "white balance background: red", 288 .minimum = 0x00, 289 .maximum = 0x3f, 290 .step = 0x01, 291 .default_value = 0x20, 292 .flags = 0, 293 }, 294 { 295 .id = V4L2_CID_DO_WHITE_BALANCE, 296 .type = V4L2_CTRL_TYPE_INTEGER, 297 .name = "white balance background: blue", 298 .minimum = 0x00, 299 .maximum = 0x3f, 300 .step = 0x01, 301 .default_value = 0x20, 302 .flags = 0, 303 }, 304 { 305 .id = V4L2_CID_RED_BALANCE, 306 .type = V4L2_CTRL_TYPE_INTEGER, 307 .name = "red balance", 308 .minimum = 0x00, 309 .maximum = 0x7f, 310 .step = 0x01, 311 .default_value = 0x20, 312 .flags = 0, 313 }, 314 { 315 .id = V4L2_CID_BLUE_BALANCE, 316 .type = V4L2_CTRL_TYPE_INTEGER, 317 .name = "blue balance", 318 .minimum = 0x00, 319 .maximum = 0x7f, 320 .step = 0x01, 321 .default_value = 0x20, 322 .flags = 0, 323 }, 324 { 325 .id = V4L2_CID_AUTOGAIN, 326 .type = V4L2_CTRL_TYPE_BOOLEAN, 327 .name = "auto adjust", 328 .minimum = 0x00, 329 .maximum = 0x01, 330 .step = 0x01, 331 .default_value = 0x00, 332 .flags = 0, 333 }, 334 { 335 .id = V4L2_CID_VFLIP, 336 .type = V4L2_CTRL_TYPE_BOOLEAN, 337 .name = "vertical flip", 338 .minimum = 0x00, 339 .maximum = 0x01, 340 .step = 0x01, 341 .default_value = 0x01, 342 .flags = 0, 343 }, 344 { 345 .id = SN9C102_V4L2_CID_GREEN_BALANCE, 346 .type = V4L2_CTRL_TYPE_INTEGER, 347 .name = "green balance", 348 .minimum = 0x00, 349 .maximum = 0x7f, 350 .step = 0x01, 351 .default_value = 0x20, 352 .flags = 0, 353 }, 354 { 355 .id = SN9C102_V4L2_CID_BAND_FILTER, 356 .type = V4L2_CTRL_TYPE_BOOLEAN, 357 .name = "band filter", 358 .minimum = 0x00, 359 .maximum = 0x01, 360 .step = 0x01, 361 .default_value = 0x00, 362 .flags = 0, 363 }, 364 { 365 .id = SN9C102_V4L2_CID_GAMMA, 366 .type = V4L2_CTRL_TYPE_BOOLEAN, 367 .name = "rgb gamma", 368 .minimum = 0x00, 369 .maximum = 0x01, 370 .step = 0x01, 371 .default_value = 0x00, 372 .flags = 0, 373 }, 374 }, 375 .get_ctrl = &ov7630_get_ctrl, 376 .set_ctrl = &ov7630_set_ctrl, 377 .cropcap = { 378 .bounds = { 379 .left = 0, 380 .top = 0, 381 .width = 640, 382 .height = 480, 383 }, 384 .defrect = { 385 .left = 0, 386 .top = 0, 387 .width = 640, 388 .height = 480, 389 }, 390 }, 391 .set_crop = &ov7630_set_crop, 392 .pix_format = { 393 .width = 640, 394 .height = 480, 395 .pixelformat = V4L2_PIX_FMT_SN9C10X, 396 .priv = 8, 397 }, 398 .set_pix_format = &ov7630_set_pix_format 399}; 400 401 402int sn9c102_probe_ov7630(struct sn9c102_device* cam) 403{ 404 int pid, ver, err = 0; 405 406 switch (sn9c102_get_bridge(cam)) { 407 case BRIDGE_SN9C101: 408 case BRIDGE_SN9C102: 409 err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, 410 {0x28, 0x17}); 411 break; 412 case BRIDGE_SN9C103: /* do _not_ change anything! */ 413 err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x42, 0x01}, 414 {0x28, 0x17}, {0x44, 0x02}); 415 pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a); 416 if (err || pid < 0) /* try a different initialization */ 417 err += sn9c102_write_const_regs(cam, {0x01, 0x01}, 418 {0x00, 0x01}); 419 break; 420 default: 421 break; 422 } 423 424 pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a); 425 ver = sn9c102_i2c_try_read(cam, &ov7630, 0x0b); 426 if (err || pid < 0 || ver < 0) 427 return -EIO; 428 if (pid != 0x76 || ver != 0x31) 429 return -ENODEV; 430 sn9c102_attach_sensor(cam, &ov7630); 431 432 return 0; 433} 434