saa7110.c revision 85ede69fec2d26789f5782a364fb3a93ebf0b546
1/* 2 * saa7110 - Philips SAA7110(A) video decoder driver 3 * 4 * Copyright (C) 1998 Pauline Middelink <middelin@polyware.nl> 5 * 6 * Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net> 7 * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx> 8 * - some corrections for Pinnacle Systems Inc. DC10plus card. 9 * 10 * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net> 11 * - moved over to linux>=2.4.x i2c protocol (1/1/2003) 12 * 13 * This program is free software; you can redistribute it and/or modify 14 * it under the terms of the GNU General Public License as published by 15 * the Free Software Foundation; either version 2 of the License, or 16 * (at your option) any later version. 17 * 18 * This program is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU General Public License for more details. 22 * 23 * You should have received a copy of the GNU General Public License 24 * along with this program; if not, write to the Free Software 25 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 26 */ 27 28#include <linux/module.h> 29#include <linux/init.h> 30#include <linux/types.h> 31#include <linux/delay.h> 32#include <linux/slab.h> 33#include <linux/wait.h> 34#include <asm/uaccess.h> 35#include <linux/i2c.h> 36#include <linux/videodev.h> 37#include <linux/video_decoder.h> 38#include <media/v4l2-common.h> 39#include <media/v4l2-i2c-drv-legacy.h> 40 41MODULE_DESCRIPTION("Philips SAA7110 video decoder driver"); 42MODULE_AUTHOR("Pauline Middelink"); 43MODULE_LICENSE("GPL"); 44 45static int debug; 46module_param(debug, int, 0); 47MODULE_PARM_DESC(debug, "Debug level (0-1)"); 48 49#define SAA7110_MAX_INPUT 9 /* 6 CVBS, 3 SVHS */ 50#define SAA7110_MAX_OUTPUT 0 /* its a decoder only */ 51 52#define SAA7110_NR_REG 0x35 53 54struct saa7110 { 55 u8 reg[SAA7110_NR_REG]; 56 57 int norm; 58 int input; 59 int enable; 60 int bright; 61 int contrast; 62 int hue; 63 int sat; 64 65 wait_queue_head_t wq; 66}; 67 68/* ----------------------------------------------------------------------- */ 69/* I2C support functions */ 70/* ----------------------------------------------------------------------- */ 71 72static int saa7110_write(struct i2c_client *client, u8 reg, u8 value) 73{ 74 struct saa7110 *decoder = i2c_get_clientdata(client); 75 76 decoder->reg[reg] = value; 77 return i2c_smbus_write_byte_data(client, reg, value); 78} 79 80static int saa7110_write_block(struct i2c_client *client, const u8 *data, unsigned int len) 81{ 82 int ret = -1; 83 u8 reg = *data; /* first register to write to */ 84 85 /* Sanity check */ 86 if (reg + (len - 1) > SAA7110_NR_REG) 87 return ret; 88 89 /* the saa7110 has an autoincrement function, use it if 90 * the adapter understands raw I2C */ 91 if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { 92 struct saa7110 *decoder = i2c_get_clientdata(client); 93 94 ret = i2c_master_send(client, data, len); 95 96 /* Cache the written data */ 97 memcpy(decoder->reg + reg, data + 1, len - 1); 98 } else { 99 for (++data, --len; len; len--) { 100 ret = saa7110_write(client, reg++, *data++); 101 if (ret < 0) 102 break; 103 } 104 } 105 106 return ret; 107} 108 109static inline int saa7110_read(struct i2c_client *client) 110{ 111 return i2c_smbus_read_byte(client); 112} 113 114/* ----------------------------------------------------------------------- */ 115/* SAA7110 functions */ 116/* ----------------------------------------------------------------------- */ 117 118#define FRESP_06H_COMPST 0x03 //0x13 119#define FRESP_06H_SVIDEO 0x83 //0xC0 120 121 122static int saa7110_selmux(struct i2c_client *client, int chan) 123{ 124 static const unsigned char modes[9][8] = { 125 /* mode 0 */ 126 {FRESP_06H_COMPST, 0xD9, 0x17, 0x40, 0x03, 127 0x44, 0x75, 0x16}, 128 /* mode 1 */ 129 {FRESP_06H_COMPST, 0xD8, 0x17, 0x40, 0x03, 130 0x44, 0x75, 0x16}, 131 /* mode 2 */ 132 {FRESP_06H_COMPST, 0xBA, 0x07, 0x91, 0x03, 133 0x60, 0xB5, 0x05}, 134 /* mode 3 */ 135 {FRESP_06H_COMPST, 0xB8, 0x07, 0x91, 0x03, 136 0x60, 0xB5, 0x05}, 137 /* mode 4 */ 138 {FRESP_06H_COMPST, 0x7C, 0x07, 0xD2, 0x83, 139 0x60, 0xB5, 0x03}, 140 /* mode 5 */ 141 {FRESP_06H_COMPST, 0x78, 0x07, 0xD2, 0x83, 142 0x60, 0xB5, 0x03}, 143 /* mode 6 */ 144 {FRESP_06H_SVIDEO, 0x59, 0x17, 0x42, 0xA3, 145 0x44, 0x75, 0x12}, 146 /* mode 7 */ 147 {FRESP_06H_SVIDEO, 0x9A, 0x17, 0xB1, 0x13, 148 0x60, 0xB5, 0x14}, 149 /* mode 8 */ 150 {FRESP_06H_SVIDEO, 0x3C, 0x27, 0xC1, 0x23, 151 0x44, 0x75, 0x21} 152 }; 153 struct saa7110 *decoder = i2c_get_clientdata(client); 154 const unsigned char *ptr = modes[chan]; 155 156 saa7110_write(client, 0x06, ptr[0]); /* Luminance control */ 157 saa7110_write(client, 0x20, ptr[1]); /* Analog Control #1 */ 158 saa7110_write(client, 0x21, ptr[2]); /* Analog Control #2 */ 159 saa7110_write(client, 0x22, ptr[3]); /* Mixer Control #1 */ 160 saa7110_write(client, 0x2C, ptr[4]); /* Mixer Control #2 */ 161 saa7110_write(client, 0x30, ptr[5]); /* ADCs gain control */ 162 saa7110_write(client, 0x31, ptr[6]); /* Mixer Control #3 */ 163 saa7110_write(client, 0x21, ptr[7]); /* Analog Control #2 */ 164 decoder->input = chan; 165 166 return 0; 167} 168 169static const unsigned char initseq[1 + SAA7110_NR_REG] = { 170 0, 0x4C, 0x3C, 0x0D, 0xEF, 0xBD, 0xF2, 0x03, 0x00, 171 /* 0x08 */ 0xF8, 0xF8, 0x60, 0x60, 0x00, 0x86, 0x18, 0x90, 172 /* 0x10 */ 0x00, 0x59, 0x40, 0x46, 0x42, 0x1A, 0xFF, 0xDA, 173 /* 0x18 */ 0xF2, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 174 /* 0x20 */ 0xD9, 0x16, 0x40, 0x41, 0x80, 0x41, 0x80, 0x4F, 175 /* 0x28 */ 0xFE, 0x01, 0xCF, 0x0F, 0x03, 0x01, 0x03, 0x0C, 176 /* 0x30 */ 0x44, 0x71, 0x02, 0x8C, 0x02 177}; 178 179static int determine_norm(struct i2c_client *client) 180{ 181 DEFINE_WAIT(wait); 182 struct saa7110 *decoder = i2c_get_clientdata(client); 183 int status; 184 185 /* mode changed, start automatic detection */ 186 saa7110_write_block(client, initseq, sizeof(initseq)); 187 saa7110_selmux(client, decoder->input); 188 prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE); 189 schedule_timeout(msecs_to_jiffies(250)); 190 finish_wait(&decoder->wq, &wait); 191 status = saa7110_read(client); 192 if (status & 0x40) { 193 v4l_dbg(1, debug, client, "status=0x%02x (no signal)\n", status); 194 return decoder->norm; // no change 195 } 196 if ((status & 3) == 0) { 197 saa7110_write(client, 0x06, 0x83); 198 if (status & 0x20) { 199 v4l_dbg(1, debug, client, "status=0x%02x (NTSC/no color)\n", status); 200 //saa7110_write(client,0x2E,0x81); 201 return VIDEO_MODE_NTSC; 202 } 203 v4l_dbg(1, debug, client, "status=0x%02x (PAL/no color)\n", status); 204 //saa7110_write(client,0x2E,0x9A); 205 return VIDEO_MODE_PAL; 206 } 207 //saa7110_write(client,0x06,0x03); 208 if (status & 0x20) { /* 60Hz */ 209 v4l_dbg(1, debug, client, "status=0x%02x (NTSC)\n", status); 210 saa7110_write(client, 0x0D, 0x86); 211 saa7110_write(client, 0x0F, 0x50); 212 saa7110_write(client, 0x11, 0x2C); 213 //saa7110_write(client,0x2E,0x81); 214 return VIDEO_MODE_NTSC; 215 } 216 217 /* 50Hz -> PAL/SECAM */ 218 saa7110_write(client, 0x0D, 0x86); 219 saa7110_write(client, 0x0F, 0x10); 220 saa7110_write(client, 0x11, 0x59); 221 //saa7110_write(client,0x2E,0x9A); 222 223 prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE); 224 schedule_timeout(msecs_to_jiffies(250)); 225 finish_wait(&decoder->wq, &wait); 226 227 status = saa7110_read(client); 228 if ((status & 0x03) == 0x01) { 229 v4l_dbg(1, debug, client, "status=0x%02x (SECAM)\n", status); 230 saa7110_write(client, 0x0D, 0x87); 231 return VIDEO_MODE_SECAM; 232 } 233 v4l_dbg(1, debug, client, "status=0x%02x (PAL)\n", status); 234 return VIDEO_MODE_PAL; 235} 236 237static int 238saa7110_command (struct i2c_client *client, 239 unsigned int cmd, 240 void *arg) 241{ 242 struct saa7110 *decoder = i2c_get_clientdata(client); 243 int v; 244 245 switch (cmd) { 246 case 0: 247 //saa7110_write_block(client, initseq, sizeof(initseq)); 248 break; 249 250 case DECODER_GET_CAPABILITIES: 251 { 252 struct video_decoder_capability *dc = arg; 253 254 dc->flags = 255 VIDEO_DECODER_PAL | VIDEO_DECODER_NTSC | 256 VIDEO_DECODER_SECAM | VIDEO_DECODER_AUTO; 257 dc->inputs = SAA7110_MAX_INPUT; 258 dc->outputs = SAA7110_MAX_OUTPUT; 259 break; 260 } 261 262 case DECODER_GET_STATUS: 263 { 264 int status; 265 int res = 0; 266 267 status = saa7110_read(client); 268 v4l_dbg(1, debug, client, "status=0x%02x norm=%d\n", 269 status, decoder->norm); 270 if (!(status & 0x40)) 271 res |= DECODER_STATUS_GOOD; 272 if (status & 0x03) 273 res |= DECODER_STATUS_COLOR; 274 275 switch (decoder->norm) { 276 case VIDEO_MODE_NTSC: 277 res |= DECODER_STATUS_NTSC; 278 break; 279 case VIDEO_MODE_PAL: 280 res |= DECODER_STATUS_PAL; 281 break; 282 case VIDEO_MODE_SECAM: 283 res |= DECODER_STATUS_SECAM; 284 break; 285 } 286 *(int *) arg = res; 287 break; 288 } 289 290 case DECODER_SET_NORM: 291 v = *(int *) arg; 292 if (decoder->norm != v) { 293 decoder->norm = v; 294 //saa7110_write(client, 0x06, 0x03); 295 switch (v) { 296 case VIDEO_MODE_NTSC: 297 saa7110_write(client, 0x0D, 0x86); 298 saa7110_write(client, 0x0F, 0x50); 299 saa7110_write(client, 0x11, 0x2C); 300 //saa7110_write(client, 0x2E, 0x81); 301 v4l_dbg(1, debug, client, "switched to NTSC\n"); 302 break; 303 case VIDEO_MODE_PAL: 304 saa7110_write(client, 0x0D, 0x86); 305 saa7110_write(client, 0x0F, 0x10); 306 saa7110_write(client, 0x11, 0x59); 307 //saa7110_write(client, 0x2E, 0x9A); 308 v4l_dbg(1, debug, client, "switched to PAL\n"); 309 break; 310 case VIDEO_MODE_SECAM: 311 saa7110_write(client, 0x0D, 0x87); 312 saa7110_write(client, 0x0F, 0x10); 313 saa7110_write(client, 0x11, 0x59); 314 //saa7110_write(client, 0x2E, 0x9A); 315 v4l_dbg(1, debug, client, "switched to SECAM\n"); 316 break; 317 case VIDEO_MODE_AUTO: 318 v4l_dbg(1, debug, client, "switched to AUTO\n"); 319 decoder->norm = determine_norm(client); 320 *(int *) arg = decoder->norm; 321 break; 322 default: 323 return -EPERM; 324 } 325 } 326 break; 327 328 case DECODER_SET_INPUT: 329 v = *(int *) arg; 330 if (v < 0 || v > SAA7110_MAX_INPUT) { 331 v4l_dbg(1, debug, client, "input=%d not available\n", v); 332 return -EINVAL; 333 } 334 if (decoder->input != v) { 335 saa7110_selmux(client, v); 336 v4l_dbg(1, debug, client, "switched to input=%d\n", v); 337 } 338 break; 339 340 case DECODER_SET_OUTPUT: 341 v = *(int *) arg; 342 /* not much choice of outputs */ 343 if (v != 0) 344 return -EINVAL; 345 break; 346 347 case DECODER_ENABLE_OUTPUT: 348 v = *(int *) arg; 349 if (decoder->enable != v) { 350 decoder->enable = v; 351 saa7110_write(client, 0x0E, v ? 0x18 : 0x80); 352 v4l_dbg(1, debug, client, "YUV %s\n", v ? "on" : "off"); 353 } 354 break; 355 356 case DECODER_SET_PICTURE: 357 { 358 struct video_picture *pic = arg; 359 360 if (decoder->bright != pic->brightness) { 361 /* We want 0 to 255 we get 0-65535 */ 362 decoder->bright = pic->brightness; 363 saa7110_write(client, 0x19, decoder->bright >> 8); 364 } 365 if (decoder->contrast != pic->contrast) { 366 /* We want 0 to 127 we get 0-65535 */ 367 decoder->contrast = pic->contrast; 368 saa7110_write(client, 0x13, 369 decoder->contrast >> 9); 370 } 371 if (decoder->sat != pic->colour) { 372 /* We want 0 to 127 we get 0-65535 */ 373 decoder->sat = pic->colour; 374 saa7110_write(client, 0x12, decoder->sat >> 9); 375 } 376 if (decoder->hue != pic->hue) { 377 /* We want -128 to 127 we get 0-65535 */ 378 decoder->hue = pic->hue; 379 saa7110_write(client, 0x07, 380 (decoder->hue >> 8) - 128); 381 } 382 break; 383 } 384 385 case DECODER_DUMP: 386 if (!debug) 387 break; 388 for (v = 0; v < SAA7110_NR_REG; v += 16) { 389 int j; 390 v4l_dbg(1, debug, client, "%02x:", v); 391 for (j = 0; j < 16 && v + j < SAA7110_NR_REG; j++) 392 printk(KERN_CONT " %02x", decoder->reg[v + j]); 393 printk(KERN_CONT "\n"); 394 } 395 break; 396 397 default: 398 v4l_dbg(1, debug, client, "unknown command %08x\n", cmd); 399 return -EINVAL; 400 } 401 return 0; 402} 403 404/* ----------------------------------------------------------------------- */ 405 406/* 407 * Generic i2c probe 408 * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' 409 */ 410 411static unsigned short normal_i2c[] = { 0x9c >> 1, 0x9e >> 1, I2C_CLIENT_END }; 412 413I2C_CLIENT_INSMOD; 414 415static int saa7110_probe(struct i2c_client *client, 416 const struct i2c_device_id *id) 417{ 418 struct saa7110 *decoder; 419 int rv; 420 421 /* Check if the adapter supports the needed features */ 422 if (!i2c_check_functionality(client->adapter, 423 I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) 424 return -ENODEV; 425 426 v4l_info(client, "chip found @ 0x%x (%s)\n", 427 client->addr << 1, client->adapter->name); 428 429 decoder = kzalloc(sizeof(struct saa7110), GFP_KERNEL); 430 if (!decoder) 431 return -ENOMEM; 432 decoder->norm = VIDEO_MODE_PAL; 433 decoder->input = 0; 434 decoder->enable = 1; 435 decoder->bright = 32768; 436 decoder->contrast = 32768; 437 decoder->hue = 32768; 438 decoder->sat = 32768; 439 init_waitqueue_head(&decoder->wq); 440 i2c_set_clientdata(client, decoder); 441 442 rv = saa7110_write_block(client, initseq, sizeof(initseq)); 443 if (rv < 0) { 444 v4l_dbg(1, debug, client, "init status %d\n", rv); 445 } else { 446 int ver, status; 447 saa7110_write(client, 0x21, 0x10); 448 saa7110_write(client, 0x0e, 0x18); 449 saa7110_write(client, 0x0D, 0x04); 450 ver = saa7110_read(client); 451 saa7110_write(client, 0x0D, 0x06); 452 //mdelay(150); 453 status = saa7110_read(client); 454 v4l_dbg(1, debug, client, "version %x, status=0x%02x\n", 455 ver, status); 456 saa7110_write(client, 0x0D, 0x86); 457 saa7110_write(client, 0x0F, 0x10); 458 saa7110_write(client, 0x11, 0x59); 459 //saa7110_write(client, 0x2E, 0x9A); 460 } 461 462 //saa7110_selmux(client,0); 463 //determine_norm(client); 464 /* setup and implicit mode 0 select has been performed */ 465 466 return 0; 467} 468 469static int saa7110_remove(struct i2c_client *client) 470{ 471 kfree(i2c_get_clientdata(client)); 472 return 0; 473} 474 475/* ----------------------------------------------------------------------- */ 476 477static const struct i2c_device_id saa7110_id[] = { 478 { "saa7110", 0 }, 479 { } 480}; 481MODULE_DEVICE_TABLE(i2c, saa7110_id); 482 483static struct v4l2_i2c_driver_data v4l2_i2c_data = { 484 .name = "saa7110", 485 .driverid = I2C_DRIVERID_SAA7110, 486 .command = saa7110_command, 487 .probe = saa7110_probe, 488 .remove = saa7110_remove, 489 .id_table = saa7110_id, 490}; 491