1/* 2 * tm6000-core.c - driver for TM5600/TM6000/TM6010 USB video capture devices 3 * 4 * Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org> 5 * 6 * Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com> 7 * - DVB-T support 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation version 2 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 */ 22 23#include <linux/module.h> 24#include <linux/kernel.h> 25#include <linux/slab.h> 26#include <linux/usb.h> 27#include <linux/i2c.h> 28#include "tm6000.h" 29#include "tm6000-regs.h" 30#include <media/v4l2-common.h> 31#include <media/tuner.h> 32 33#define USB_TIMEOUT (5 * HZ) /* ms */ 34 35int tm6000_read_write_usb(struct tm6000_core *dev, u8 req_type, u8 req, 36 u16 value, u16 index, u8 *buf, u16 len) 37{ 38 int ret, i; 39 unsigned int pipe; 40 u8 *data = NULL; 41 int delay = 5000; 42 43 if (len) { 44 data = kzalloc(len, GFP_KERNEL); 45 if (!data) 46 return -ENOMEM; 47 } 48 49 mutex_lock(&dev->usb_lock); 50 51 if (req_type & USB_DIR_IN) 52 pipe = usb_rcvctrlpipe(dev->udev, 0); 53 else { 54 pipe = usb_sndctrlpipe(dev->udev, 0); 55 memcpy(data, buf, len); 56 } 57 58 if (tm6000_debug & V4L2_DEBUG_I2C) { 59 printk(KERN_DEBUG "(dev %p, pipe %08x): ", dev->udev, pipe); 60 61 printk(KERN_CONT "%s: %02x %02x %02x %02x %02x %02x %02x %02x ", 62 (req_type & USB_DIR_IN) ? " IN" : "OUT", 63 req_type, req, value&0xff, value>>8, index&0xff, 64 index>>8, len&0xff, len>>8); 65 66 if (!(req_type & USB_DIR_IN)) { 67 printk(KERN_CONT ">>> "); 68 for (i = 0; i < len; i++) 69 printk(KERN_CONT " %02x", buf[i]); 70 printk(KERN_CONT "\n"); 71 } 72 } 73 74 ret = usb_control_msg(dev->udev, pipe, req, req_type, value, index, 75 data, len, USB_TIMEOUT); 76 77 if (req_type & USB_DIR_IN) 78 memcpy(buf, data, len); 79 80 if (tm6000_debug & V4L2_DEBUG_I2C) { 81 if (ret < 0) { 82 if (req_type & USB_DIR_IN) 83 printk(KERN_DEBUG "<<< (len=%d)\n", len); 84 85 printk(KERN_CONT "%s: Error #%d\n", __func__, ret); 86 } else if (req_type & USB_DIR_IN) { 87 printk(KERN_CONT "<<< "); 88 for (i = 0; i < len; i++) 89 printk(KERN_CONT " %02x", buf[i]); 90 printk(KERN_CONT "\n"); 91 } 92 } 93 94 kfree(data); 95 96 if (dev->quirks & TM6000_QUIRK_NO_USB_DELAY) 97 delay = 0; 98 99 if (req == REQ_16_SET_GET_I2C_WR1_RDN && !(req_type & USB_DIR_IN)) { 100 unsigned int tsleep; 101 /* Calculate delay time, 14000us for 64 bytes */ 102 tsleep = (len * 200) + 200; 103 if (tsleep < delay) 104 tsleep = delay; 105 usleep_range(tsleep, tsleep + 1000); 106 } 107 else if (delay) 108 usleep_range(delay, delay + 1000); 109 110 mutex_unlock(&dev->usb_lock); 111 return ret; 112} 113 114int tm6000_set_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index) 115{ 116 return 117 tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR, 118 req, value, index, NULL, 0); 119} 120EXPORT_SYMBOL_GPL(tm6000_set_reg); 121 122int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index) 123{ 124 int rc; 125 u8 buf[1]; 126 127 rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, 128 value, index, buf, 1); 129 130 if (rc < 0) 131 return rc; 132 133 return *buf; 134} 135EXPORT_SYMBOL_GPL(tm6000_get_reg); 136 137int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value, 138 u16 index, u16 mask) 139{ 140 int rc; 141 u8 buf[1]; 142 u8 new_index; 143 144 rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, 145 value, 0, buf, 1); 146 147 if (rc < 0) 148 return rc; 149 150 new_index = (buf[0] & ~mask) | (index & mask); 151 152 if (new_index == buf[0]) 153 return 0; 154 155 return tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR, 156 req, value, new_index, NULL, 0); 157} 158EXPORT_SYMBOL_GPL(tm6000_set_reg_mask); 159 160int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index) 161{ 162 int rc; 163 u8 buf[2]; 164 165 rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, 166 value, index, buf, 2); 167 168 if (rc < 0) 169 return rc; 170 171 return buf[1]|buf[0]<<8; 172} 173 174int tm6000_get_reg32(struct tm6000_core *dev, u8 req, u16 value, u16 index) 175{ 176 int rc; 177 u8 buf[4]; 178 179 rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, 180 value, index, buf, 4); 181 182 if (rc < 0) 183 return rc; 184 185 return buf[3] | buf[2] << 8 | buf[1] << 16 | buf[0] << 24; 186} 187 188int tm6000_i2c_reset(struct tm6000_core *dev, u16 tsleep) 189{ 190 int rc; 191 192 rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_CLK, 0); 193 if (rc < 0) 194 return rc; 195 196 msleep(tsleep); 197 198 rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_CLK, 1); 199 msleep(tsleep); 200 201 return rc; 202} 203 204void tm6000_set_fourcc_format(struct tm6000_core *dev) 205{ 206 if (dev->dev_type == TM6010) { 207 int val; 208 209 val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, 0) & 0xfc; 210 if (dev->fourcc == V4L2_PIX_FMT_UYVY) 211 tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, val); 212 else 213 tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, val | 1); 214 } else { 215 if (dev->fourcc == V4L2_PIX_FMT_UYVY) 216 tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0); 217 else 218 tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0x90); 219 } 220} 221 222static void tm6000_set_vbi(struct tm6000_core *dev) 223{ 224 /* 225 * FIXME: 226 * VBI lines and start/end are different between 60Hz and 50Hz 227 * So, it is very likely that we need to change the config to 228 * something that takes it into account, doing something different 229 * if (dev->norm & V4L2_STD_525_60) 230 */ 231 232 if (dev->dev_type == TM6010) { 233 tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); 234 tm6000_set_reg(dev, TM6010_REQ07_R41_TELETEXT_VBI_CODE1, 0x27); 235 tm6000_set_reg(dev, TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55); 236 tm6000_set_reg(dev, TM6010_REQ07_R43_VBI_DATA_TYPE_LINE7, 0x66); 237 tm6000_set_reg(dev, TM6010_REQ07_R44_VBI_DATA_TYPE_LINE8, 0x66); 238 tm6000_set_reg(dev, TM6010_REQ07_R45_VBI_DATA_TYPE_LINE9, 0x66); 239 tm6000_set_reg(dev, 240 TM6010_REQ07_R46_VBI_DATA_TYPE_LINE10, 0x66); 241 tm6000_set_reg(dev, 242 TM6010_REQ07_R47_VBI_DATA_TYPE_LINE11, 0x66); 243 tm6000_set_reg(dev, 244 TM6010_REQ07_R48_VBI_DATA_TYPE_LINE12, 0x66); 245 tm6000_set_reg(dev, 246 TM6010_REQ07_R49_VBI_DATA_TYPE_LINE13, 0x66); 247 tm6000_set_reg(dev, 248 TM6010_REQ07_R4A_VBI_DATA_TYPE_LINE14, 0x66); 249 tm6000_set_reg(dev, 250 TM6010_REQ07_R4B_VBI_DATA_TYPE_LINE15, 0x66); 251 tm6000_set_reg(dev, 252 TM6010_REQ07_R4C_VBI_DATA_TYPE_LINE16, 0x66); 253 tm6000_set_reg(dev, 254 TM6010_REQ07_R4D_VBI_DATA_TYPE_LINE17, 0x66); 255 tm6000_set_reg(dev, 256 TM6010_REQ07_R4E_VBI_DATA_TYPE_LINE18, 0x66); 257 tm6000_set_reg(dev, 258 TM6010_REQ07_R4F_VBI_DATA_TYPE_LINE19, 0x66); 259 tm6000_set_reg(dev, 260 TM6010_REQ07_R50_VBI_DATA_TYPE_LINE20, 0x66); 261 tm6000_set_reg(dev, 262 TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x66); 263 tm6000_set_reg(dev, 264 TM6010_REQ07_R52_VBI_DATA_TYPE_LINE22, 0x66); 265 tm6000_set_reg(dev, 266 TM6010_REQ07_R53_VBI_DATA_TYPE_LINE23, 0x00); 267 tm6000_set_reg(dev, 268 TM6010_REQ07_R54_VBI_DATA_TYPE_RLINES, 0x00); 269 tm6000_set_reg(dev, 270 TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01); 271 tm6000_set_reg(dev, 272 TM6010_REQ07_R56_VBI_LOOP_FILTER_I_GAIN, 0x00); 273 tm6000_set_reg(dev, 274 TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02); 275 tm6000_set_reg(dev, TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35); 276 tm6000_set_reg(dev, TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0); 277 tm6000_set_reg(dev, TM6010_REQ07_R5A_VBI_TELETEXT_DTO1, 0x11); 278 tm6000_set_reg(dev, TM6010_REQ07_R5B_VBI_TELETEXT_DTO0, 0x4c); 279 tm6000_set_reg(dev, TM6010_REQ07_R40_TELETEXT_VBI_CODE0, 0x01); 280 tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00); 281 } 282} 283 284int tm6000_init_analog_mode(struct tm6000_core *dev) 285{ 286 struct v4l2_frequency f; 287 288 if (dev->dev_type == TM6010) { 289 u8 active = TM6010_REQ07_RCC_ACTIVE_IF_AUDIO_ENABLE; 290 291 if (!dev->radio) 292 active |= TM6010_REQ07_RCC_ACTIVE_IF_VIDEO_ENABLE; 293 294 /* Enable video and audio */ 295 tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_IF, 296 active, 0x60); 297 /* Disable TS input */ 298 tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 299 0x00, 0x40); 300 } else { 301 /* Enables soft reset */ 302 tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); 303 304 if (dev->scaler) 305 /* Disable Hfilter and Enable TS Drop err */ 306 tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x20); 307 else /* Enable Hfilter and disable TS Drop err */ 308 tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x80); 309 310 tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x88); 311 tm6000_set_reg(dev, TM6000_REQ07_RDA_CLK_SEL, 0x23); 312 tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xc0); 313 tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xd8); 314 tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x06); 315 tm6000_set_reg(dev, TM6000_REQ07_RDF_PWDOWN_ACLK, 0x1f); 316 317 /* AP Software reset */ 318 tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08); 319 tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x00); 320 321 tm6000_set_fourcc_format(dev); 322 323 /* Disables soft reset */ 324 tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00); 325 } 326 msleep(20); 327 328 /* Tuner firmware can now be loaded */ 329 330 /* 331 * FIXME: This is a hack! xc3028 "sleeps" when no channel is detected 332 * for more than a few seconds. Not sure why, as this behavior does 333 * not happen on other devices with xc3028. So, I suspect that it 334 * is yet another bug at tm6000. After start sleeping, decoding 335 * doesn't start automatically. Instead, it requires some 336 * I2C commands to wake it up. As we want to have image at the 337 * beginning, we needed to add this hack. The better would be to 338 * discover some way to make tm6000 to wake up without this hack. 339 */ 340 f.frequency = dev->freq; 341 v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); 342 343 msleep(100); 344 tm6000_set_standard(dev); 345 tm6000_set_vbi(dev); 346 tm6000_set_audio_bitrate(dev, 48000); 347 348 /* switch dvb led off */ 349 if (dev->gpio.dvb_led) { 350 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, 351 dev->gpio.dvb_led, 0x01); 352 } 353 354 return 0; 355} 356 357int tm6000_init_digital_mode(struct tm6000_core *dev) 358{ 359 if (dev->dev_type == TM6010) { 360 /* Disable video and audio */ 361 tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_IF, 362 0x00, 0x60); 363 /* Enable TS input */ 364 tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 365 0x40, 0x40); 366 /* all power down, but not the digital data port */ 367 tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, 0x28); 368 tm6000_set_reg(dev, TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xfc); 369 tm6000_set_reg(dev, TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0xff); 370 } else { 371 tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08); 372 tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x00); 373 tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); 374 tm6000_set_reg(dev, TM6000_REQ07_RDF_PWDOWN_ACLK, 0x08); 375 tm6000_set_reg(dev, TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x0c); 376 tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0xff); 377 tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0xd8); 378 tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x40); 379 tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0); 380 tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x09); 381 tm6000_set_reg(dev, TM6000_REQ07_RDA_CLK_SEL, 0x37); 382 tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xd8); 383 tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xc0); 384 tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x60); 385 386 tm6000_set_reg(dev, TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x0c); 387 tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0xff); 388 tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0x08); 389 msleep(50); 390 391 tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00); 392 msleep(50); 393 tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x01); 394 msleep(50); 395 tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00); 396 msleep(100); 397 } 398 399 /* switch dvb led on */ 400 if (dev->gpio.dvb_led) { 401 tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, 402 dev->gpio.dvb_led, 0x00); 403 } 404 405 return 0; 406} 407EXPORT_SYMBOL(tm6000_init_digital_mode); 408 409struct reg_init { 410 u8 req; 411 u8 reg; 412 u8 val; 413}; 414 415/* The meaning of those initializations are unknown */ 416static struct reg_init tm6000_init_tab[] = { 417 /* REG VALUE */ 418 { TM6000_REQ07_RDF_PWDOWN_ACLK, 0x1f }, 419 { TM6010_REQ07_RFF_SOFT_RESET, 0x08 }, 420 { TM6010_REQ07_RFF_SOFT_RESET, 0x00 }, 421 { TM6010_REQ07_RD5_POWERSAVE, 0x4f }, 422 { TM6000_REQ07_RDA_CLK_SEL, 0x23 }, 423 { TM6000_REQ07_RDB_OUT_SEL, 0x08 }, 424 { TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x00 }, 425 { TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10 }, 426 { TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00 }, 427 { TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x00 }, 428 { TM6000_REQ07_REB_VADC_AADC_MODE, 0x64 }, /* 48000 bits/sample, external input */ 429 { TM6000_REQ07_REE_VADC_CTRL_SEL_CONTROL, 0xc2 }, 430 431 { TM6010_REQ07_R3F_RESET, 0x01 }, /* Start of soft reset */ 432 { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 }, 433 { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x07 }, 434 { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, 435 { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, 436 { TM6010_REQ07_R05_NOISE_THRESHOLD, 0x64 }, 437 { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01 }, 438 { TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0x82 }, 439 { TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0x36 }, 440 { TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0x50 }, 441 { TM6010_REQ07_R0C_CHROMA_AGC_CONTROL, 0x6a }, 442 { TM6010_REQ07_R11_AGC_PEAK_CONTROL, 0xc9 }, 443 { TM6010_REQ07_R12_AGC_GATE_STARTH, 0x07 }, 444 { TM6010_REQ07_R13_AGC_GATE_STARTL, 0x3b }, 445 { TM6010_REQ07_R14_AGC_GATE_WIDTH, 0x47 }, 446 { TM6010_REQ07_R15_AGC_BP_DELAY, 0x6f }, 447 { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0xcd }, 448 { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, 449 { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, 450 { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, 451 { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, 452 { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, 453 { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, 454 { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, 455 { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, 456 { TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME, 0x3c }, 457 { TM6010_REQ07_R21_HSYNC_PHASE_OFFSET, 0x3c }, 458 { TM6010_REQ07_R2D_CHROMA_BURST_END, 0x48 }, 459 { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, 460 { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, 461 { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, 462 { TM6010_REQ07_R32_VSYNC_HLOCK_MIN, 0x74 }, 463 { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, 464 { TM6010_REQ07_R34_VSYNC_AGC_MIN, 0x74 }, 465 { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, 466 { TM6010_REQ07_R36_VSYNC_VBI_MIN, 0x7a }, 467 { TM6010_REQ07_R37_VSYNC_VBI_MAX, 0x26 }, 468 { TM6010_REQ07_R38_VSYNC_THRESHOLD, 0x40 }, 469 { TM6010_REQ07_R39_VSYNC_TIME_CONSTANT, 0x0a }, 470 { TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55 }, 471 { TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x11 }, 472 { TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01 }, 473 { TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02 }, 474 { TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35 }, 475 { TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0 }, 476 { TM6010_REQ07_R80_COMB_FILTER_TRESHOLD, 0x15 }, 477 { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, 478 { TM6010_REQ07_RC1_TRESHOLD, 0xd0 }, 479 { TM6010_REQ07_RC3_HSTART1, 0x88 }, 480 { TM6010_REQ07_R3F_RESET, 0x00 }, /* End of the soft reset */ 481 { TM6010_REQ05_R18_IMASK7, 0x00 }, 482}; 483 484static struct reg_init tm6010_init_tab[] = { 485 { TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x00 }, 486 { TM6010_REQ07_RC4_HSTART0, 0xa0 }, 487 { TM6010_REQ07_RC6_HEND0, 0x40 }, 488 { TM6010_REQ07_RCA_VEND0, 0x31 }, 489 { TM6010_REQ07_RCC_ACTIVE_IF, 0xe1 }, 490 { TM6010_REQ07_RE0_DVIDEO_SOURCE, 0x03 }, 491 { TM6010_REQ07_RFE_POWER_DOWN, 0x7f }, 492 493 { TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0 }, 494 { TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4 }, 495 { TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8 }, 496 { TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x00 }, 497 { TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2 }, 498 { TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0 }, 499 { TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2 }, 500 { TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x60 }, 501 { TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc }, 502 503 { TM6010_REQ07_R3F_RESET, 0x01 }, 504 { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 }, 505 { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x07 }, 506 { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, 507 { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, 508 { TM6010_REQ07_R05_NOISE_THRESHOLD, 0x64 }, 509 { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01 }, 510 { TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0x82 }, 511 { TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0x36 }, 512 { TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0x50 }, 513 { TM6010_REQ07_R0C_CHROMA_AGC_CONTROL, 0x6a }, 514 { TM6010_REQ07_R11_AGC_PEAK_CONTROL, 0xc9 }, 515 { TM6010_REQ07_R12_AGC_GATE_STARTH, 0x07 }, 516 { TM6010_REQ07_R13_AGC_GATE_STARTL, 0x3b }, 517 { TM6010_REQ07_R14_AGC_GATE_WIDTH, 0x47 }, 518 { TM6010_REQ07_R15_AGC_BP_DELAY, 0x6f }, 519 { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0xcd }, 520 { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, 521 { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, 522 { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, 523 { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, 524 { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, 525 { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, 526 { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, 527 { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, 528 { TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME, 0x3c }, 529 { TM6010_REQ07_R21_HSYNC_PHASE_OFFSET, 0x3c }, 530 { TM6010_REQ07_R2D_CHROMA_BURST_END, 0x48 }, 531 { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, 532 { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, 533 { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, 534 { TM6010_REQ07_R32_VSYNC_HLOCK_MIN, 0x74 }, 535 { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, 536 { TM6010_REQ07_R34_VSYNC_AGC_MIN, 0x74 }, 537 { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, 538 { TM6010_REQ07_R36_VSYNC_VBI_MIN, 0x7a }, 539 { TM6010_REQ07_R37_VSYNC_VBI_MAX, 0x26 }, 540 { TM6010_REQ07_R38_VSYNC_THRESHOLD, 0x40 }, 541 { TM6010_REQ07_R39_VSYNC_TIME_CONSTANT, 0x0a }, 542 { TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55 }, 543 { TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x11 }, 544 { TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01 }, 545 { TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02 }, 546 { TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35 }, 547 { TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0 }, 548 { TM6010_REQ07_R80_COMB_FILTER_TRESHOLD, 0x15 }, 549 { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, 550 { TM6010_REQ07_RC1_TRESHOLD, 0xd0 }, 551 { TM6010_REQ07_RC3_HSTART1, 0x88 }, 552 { TM6010_REQ07_R3F_RESET, 0x00 }, 553 554 { TM6010_REQ05_R18_IMASK7, 0x00 }, 555 556 { TM6010_REQ07_RDC_IR_LEADER1, 0xaa }, 557 { TM6010_REQ07_RDD_IR_LEADER0, 0x30 }, 558 { TM6010_REQ07_RDE_IR_PULSE_CNT1, 0x20 }, 559 { TM6010_REQ07_RDF_IR_PULSE_CNT0, 0xd0 }, 560 { REQ_04_EN_DISABLE_MCU_INT, 0x02, 0x00 }, 561 { TM6010_REQ07_RD8_IR, 0x0f }, 562 563 /* set remote wakeup key:any key wakeup */ 564 { TM6010_REQ07_RE5_REMOTE_WAKEUP, 0xfe }, 565 { TM6010_REQ07_RDA_IR_WAKEUP_SEL, 0xff }, 566}; 567 568int tm6000_init(struct tm6000_core *dev) 569{ 570 int board, rc = 0, i, size; 571 struct reg_init *tab; 572 573 /* Check board revision */ 574 board = tm6000_get_reg32(dev, REQ_40_GET_VERSION, 0, 0); 575 if (board >= 0) { 576 switch (board & 0xff) { 577 case 0xf3: 578 printk(KERN_INFO "Found tm6000\n"); 579 if (dev->dev_type != TM6000) 580 dev->dev_type = TM6000; 581 break; 582 case 0xf4: 583 printk(KERN_INFO "Found tm6010\n"); 584 if (dev->dev_type != TM6010) 585 dev->dev_type = TM6010; 586 break; 587 default: 588 printk(KERN_INFO "Unknown board version = 0x%08x\n", board); 589 } 590 } else 591 printk(KERN_ERR "Error %i while retrieving board version\n", board); 592 593 if (dev->dev_type == TM6010) { 594 tab = tm6010_init_tab; 595 size = ARRAY_SIZE(tm6010_init_tab); 596 } else { 597 tab = tm6000_init_tab; 598 size = ARRAY_SIZE(tm6000_init_tab); 599 } 600 601 /* Load board's initialization table */ 602 for (i = 0; i < size; i++) { 603 rc = tm6000_set_reg(dev, tab[i].req, tab[i].reg, tab[i].val); 604 if (rc < 0) { 605 printk(KERN_ERR "Error %i while setting req %d, " 606 "reg %d to value %d\n", rc, 607 tab[i].req, tab[i].reg, tab[i].val); 608 return rc; 609 } 610 } 611 612 msleep(5); /* Just to be conservative */ 613 614 rc = tm6000_cards_setup(dev); 615 616 return rc; 617} 618 619 620int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate) 621{ 622 int val = 0; 623 u8 areg_f0 = 0x60; /* ADC MCLK = 250 Fs */ 624 u8 areg_0a = 0x91; /* SIF 48KHz */ 625 626 switch (bitrate) { 627 case 48000: 628 areg_f0 = 0x60; /* ADC MCLK = 250 Fs */ 629 areg_0a = 0x91; /* SIF 48KHz */ 630 dev->audio_bitrate = bitrate; 631 break; 632 case 32000: 633 areg_f0 = 0x00; /* ADC MCLK = 375 Fs */ 634 areg_0a = 0x90; /* SIF 32KHz */ 635 dev->audio_bitrate = bitrate; 636 break; 637 default: 638 return -EINVAL; 639 } 640 641 642 /* enable I2S, if we use sif or external I2S device */ 643 if (dev->dev_type == TM6010) { 644 val = tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, areg_0a); 645 if (val < 0) 646 return val; 647 648 val = tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 649 areg_f0, 0xf0); 650 if (val < 0) 651 return val; 652 } else { 653 val = tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 654 areg_f0, 0xf0); 655 if (val < 0) 656 return val; 657 } 658 return 0; 659} 660EXPORT_SYMBOL_GPL(tm6000_set_audio_bitrate); 661 662int tm6000_set_audio_rinput(struct tm6000_core *dev) 663{ 664 if (dev->dev_type == TM6010) { 665 /* Audio crossbar setting, default SIF1 */ 666 u8 areg_f0; 667 u8 areg_07 = 0x10; 668 669 switch (dev->rinput.amux) { 670 case TM6000_AMUX_SIF1: 671 case TM6000_AMUX_SIF2: 672 areg_f0 = 0x03; 673 areg_07 = 0x30; 674 break; 675 case TM6000_AMUX_ADC1: 676 areg_f0 = 0x00; 677 break; 678 case TM6000_AMUX_ADC2: 679 areg_f0 = 0x08; 680 break; 681 case TM6000_AMUX_I2S: 682 areg_f0 = 0x04; 683 break; 684 default: 685 printk(KERN_INFO "%s: audio input dosn't support\n", 686 dev->name); 687 return 0; 688 break; 689 } 690 /* Set audio input crossbar */ 691 tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 692 areg_f0, 0x0f); 693 /* Mux overflow workaround */ 694 tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, 695 areg_07, 0xf0); 696 } else { 697 u8 areg_eb; 698 /* Audio setting, default LINE1 */ 699 switch (dev->rinput.amux) { 700 case TM6000_AMUX_ADC1: 701 areg_eb = 0x00; 702 break; 703 case TM6000_AMUX_ADC2: 704 areg_eb = 0x04; 705 break; 706 default: 707 printk(KERN_INFO "%s: audio input dosn't support\n", 708 dev->name); 709 return 0; 710 break; 711 } 712 /* Set audio input */ 713 tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 714 areg_eb, 0x0f); 715 } 716 return 0; 717} 718 719static void tm6010_set_mute_sif(struct tm6000_core *dev, u8 mute) 720{ 721 u8 mute_reg = 0; 722 723 if (mute) 724 mute_reg = 0x08; 725 726 tm6000_set_reg_mask(dev, TM6010_REQ08_R0A_A_I2S_MOD, mute_reg, 0x08); 727} 728 729static void tm6010_set_mute_adc(struct tm6000_core *dev, u8 mute) 730{ 731 u8 mute_reg = 0; 732 733 if (mute) 734 mute_reg = 0x20; 735 736 if (dev->dev_type == TM6010) { 737 tm6000_set_reg_mask(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL, 738 mute_reg, 0x20); 739 tm6000_set_reg_mask(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL, 740 mute_reg, 0x20); 741 } else { 742 tm6000_set_reg_mask(dev, TM6000_REQ07_REC_VADC_AADC_LVOL, 743 mute_reg, 0x20); 744 tm6000_set_reg_mask(dev, TM6000_REQ07_RED_VADC_AADC_RVOL, 745 mute_reg, 0x20); 746 } 747} 748 749int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute) 750{ 751 enum tm6000_mux mux; 752 753 if (dev->radio) 754 mux = dev->rinput.amux; 755 else 756 mux = dev->vinput[dev->input].amux; 757 758 switch (mux) { 759 case TM6000_AMUX_SIF1: 760 case TM6000_AMUX_SIF2: 761 if (dev->dev_type == TM6010) 762 tm6010_set_mute_sif(dev, mute); 763 else { 764 printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has" 765 " SIF audio inputs. Please check the %s" 766 " configuration.\n", dev->name); 767 return -EINVAL; 768 } 769 break; 770 case TM6000_AMUX_ADC1: 771 case TM6000_AMUX_ADC2: 772 tm6010_set_mute_adc(dev, mute); 773 break; 774 default: 775 return -EINVAL; 776 break; 777 } 778 return 0; 779} 780 781static void tm6010_set_volume_sif(struct tm6000_core *dev, int vol) 782{ 783 u8 vol_reg; 784 785 vol_reg = vol & 0x0F; 786 787 if (vol < 0) 788 vol_reg |= 0x40; 789 790 tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, vol_reg); 791 tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, vol_reg); 792} 793 794static void tm6010_set_volume_adc(struct tm6000_core *dev, int vol) 795{ 796 u8 vol_reg; 797 798 vol_reg = (vol + 0x10) & 0x1f; 799 800 if (dev->dev_type == TM6010) { 801 tm6000_set_reg(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL, vol_reg); 802 tm6000_set_reg(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL, vol_reg); 803 } else { 804 tm6000_set_reg(dev, TM6000_REQ07_REC_VADC_AADC_LVOL, vol_reg); 805 tm6000_set_reg(dev, TM6000_REQ07_RED_VADC_AADC_RVOL, vol_reg); 806 } 807} 808 809void tm6000_set_volume(struct tm6000_core *dev, int vol) 810{ 811 enum tm6000_mux mux; 812 813 if (dev->radio) { 814 mux = dev->rinput.amux; 815 vol += 8; /* Offset to 0 dB */ 816 } else 817 mux = dev->vinput[dev->input].amux; 818 819 switch (mux) { 820 case TM6000_AMUX_SIF1: 821 case TM6000_AMUX_SIF2: 822 if (dev->dev_type == TM6010) 823 tm6010_set_volume_sif(dev, vol); 824 else 825 printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has" 826 " SIF audio inputs. Please check the %s" 827 " configuration.\n", dev->name); 828 break; 829 case TM6000_AMUX_ADC1: 830 case TM6000_AMUX_ADC2: 831 tm6010_set_volume_adc(dev, vol); 832 break; 833 default: 834 break; 835 } 836} 837 838static LIST_HEAD(tm6000_devlist); 839static DEFINE_MUTEX(tm6000_devlist_mutex); 840 841/* 842 * tm6000_realease_resource() 843 */ 844 845void tm6000_remove_from_devlist(struct tm6000_core *dev) 846{ 847 mutex_lock(&tm6000_devlist_mutex); 848 list_del(&dev->devlist); 849 mutex_unlock(&tm6000_devlist_mutex); 850}; 851 852void tm6000_add_into_devlist(struct tm6000_core *dev) 853{ 854 mutex_lock(&tm6000_devlist_mutex); 855 list_add_tail(&dev->devlist, &tm6000_devlist); 856 mutex_unlock(&tm6000_devlist_mutex); 857}; 858 859/* 860 * Extension interface 861 */ 862 863static LIST_HEAD(tm6000_extension_devlist); 864 865int tm6000_call_fillbuf(struct tm6000_core *dev, enum tm6000_ops_type type, 866 char *buf, int size) 867{ 868 struct tm6000_ops *ops = NULL; 869 870 /* FIXME: tm6000_extension_devlist_lock should be a spinlock */ 871 872 if (!list_empty(&tm6000_extension_devlist)) { 873 list_for_each_entry(ops, &tm6000_extension_devlist, next) { 874 if (ops->fillbuf && ops->type == type) 875 ops->fillbuf(dev, buf, size); 876 } 877 } 878 879 return 0; 880} 881 882int tm6000_register_extension(struct tm6000_ops *ops) 883{ 884 struct tm6000_core *dev = NULL; 885 886 mutex_lock(&tm6000_devlist_mutex); 887 list_add_tail(&ops->next, &tm6000_extension_devlist); 888 list_for_each_entry(dev, &tm6000_devlist, devlist) { 889 ops->init(dev); 890 printk(KERN_INFO "%s: Initialized (%s) extension\n", 891 dev->name, ops->name); 892 } 893 mutex_unlock(&tm6000_devlist_mutex); 894 return 0; 895} 896EXPORT_SYMBOL(tm6000_register_extension); 897 898void tm6000_unregister_extension(struct tm6000_ops *ops) 899{ 900 struct tm6000_core *dev = NULL; 901 902 mutex_lock(&tm6000_devlist_mutex); 903 list_for_each_entry(dev, &tm6000_devlist, devlist) 904 ops->fini(dev); 905 906 printk(KERN_INFO "tm6000: Remove (%s) extension\n", ops->name); 907 list_del(&ops->next); 908 mutex_unlock(&tm6000_devlist_mutex); 909} 910EXPORT_SYMBOL(tm6000_unregister_extension); 911 912void tm6000_init_extension(struct tm6000_core *dev) 913{ 914 struct tm6000_ops *ops = NULL; 915 916 mutex_lock(&tm6000_devlist_mutex); 917 if (!list_empty(&tm6000_extension_devlist)) { 918 list_for_each_entry(ops, &tm6000_extension_devlist, next) { 919 if (ops->init) 920 ops->init(dev); 921 } 922 } 923 mutex_unlock(&tm6000_devlist_mutex); 924} 925 926void tm6000_close_extension(struct tm6000_core *dev) 927{ 928 struct tm6000_ops *ops = NULL; 929 930 mutex_lock(&tm6000_devlist_mutex); 931 if (!list_empty(&tm6000_extension_devlist)) { 932 list_for_each_entry(ops, &tm6000_extension_devlist, next) { 933 if (ops->fini) 934 ops->fini(dev); 935 } 936 } 937 mutex_unlock(&tm6000_devlist_mutex); 938} 939