linux/drivers/staging/tm6000/tm6000-core.c
<<
>>
Prefs
   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
  42        if (len)
  43                data = kzalloc(len, GFP_KERNEL);
  44
  45
  46        if (req_type & USB_DIR_IN)
  47                pipe = usb_rcvctrlpipe(dev->udev, 0);
  48        else {
  49                pipe = usb_sndctrlpipe(dev->udev, 0);
  50                memcpy(data, buf, len);
  51        }
  52
  53        if (tm6000_debug & V4L2_DEBUG_I2C) {
  54                printk("(dev %p, pipe %08x): ", dev->udev, pipe);
  55
  56                printk("%s: %02x %02x %02x %02x %02x %02x %02x %02x ",
  57                        (req_type & USB_DIR_IN) ? " IN" : "OUT",
  58                        req_type, req, value&0xff, value>>8, index&0xff,
  59                        index>>8, len&0xff, len>>8);
  60
  61                if (!(req_type & USB_DIR_IN)) {
  62                        printk(">>> ");
  63                        for (i = 0; i < len; i++)
  64                                printk(" %02x", buf[i]);
  65                printk("\n");
  66                }
  67        }
  68
  69        ret = usb_control_msg(dev->udev, pipe, req, req_type, value, index,
  70                              data, len, USB_TIMEOUT);
  71
  72        if (req_type &  USB_DIR_IN)
  73                memcpy(buf, data, len);
  74
  75        if (tm6000_debug & V4L2_DEBUG_I2C) {
  76                if (ret < 0) {
  77                        if (req_type &  USB_DIR_IN)
  78                                printk("<<< (len=%d)\n", len);
  79
  80                        printk("%s: Error #%d\n", __FUNCTION__, ret);
  81                } else if (req_type &  USB_DIR_IN) {
  82                        printk("<<< ");
  83                        for (i = 0; i < len; i++)
  84                                printk(" %02x", buf[i]);
  85                        printk("\n");
  86                }
  87        }
  88
  89        kfree(data);
  90
  91        msleep(5);
  92
  93        return ret;
  94}
  95
  96int tm6000_set_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index)
  97{
  98        return
  99                tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR,
 100                                      req, value, index, NULL, 0);
 101}
 102EXPORT_SYMBOL_GPL(tm6000_set_reg);
 103
 104int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index)
 105{
 106        int rc;
 107        u8 buf[1];
 108
 109        rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req,
 110                                        value, index, buf, 1);
 111
 112        if (rc < 0)
 113                return rc;
 114
 115        return *buf;
 116}
 117EXPORT_SYMBOL_GPL(tm6000_get_reg);
 118
 119int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index)
 120{
 121        int rc;
 122        u8 buf[2];
 123
 124        rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req,
 125                                        value, index, buf, 2);
 126
 127        if (rc < 0)
 128                return rc;
 129
 130        return buf[1]|buf[0]<<8;
 131}
 132
 133int tm6000_get_reg32(struct tm6000_core *dev, u8 req, u16 value, u16 index)
 134{
 135        int rc;
 136        u8 buf[4];
 137
 138        rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req,
 139                                        value, index, buf, 4);
 140
 141        if (rc < 0)
 142                return rc;
 143
 144        return buf[3] | buf[2] << 8 | buf[1] << 16 | buf[0] << 24;
 145}
 146
 147int tm6000_i2c_reset(struct tm6000_core *dev, u16 tsleep)
 148{
 149        int rc;
 150
 151        rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_CLK, 0);
 152        if (rc < 0)
 153                return rc;
 154
 155        msleep(tsleep);
 156
 157        rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_CLK, 1);
 158        msleep(tsleep);
 159
 160        return rc;
 161}
 162
 163void tm6000_set_fourcc_format(struct tm6000_core *dev)
 164{
 165        if (dev->dev_type == TM6010) {
 166                int val;
 167
 168                val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0) & 0xfc;
 169                if (dev->fourcc == V4L2_PIX_FMT_UYVY)
 170                        tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val);
 171                else
 172                        tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val | 1);
 173        } else {
 174                if (dev->fourcc == V4L2_PIX_FMT_UYVY)
 175                        tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0);
 176                else
 177                        tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0x90);
 178        }
 179}
 180
 181static void tm6000_set_vbi(struct tm6000_core *dev)
 182{
 183        /*
 184         * FIXME:
 185         * VBI lines and start/end are different between 60Hz and 50Hz
 186         * So, it is very likely that we need to change the config to
 187         * something that takes it into account, doing something different
 188         * if (dev->norm & V4L2_STD_525_60)
 189         */
 190
 191        if (dev->dev_type == TM6010) {
 192                tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01);
 193                tm6000_set_reg(dev, TM6010_REQ07_R41_TELETEXT_VBI_CODE1, 0x27);
 194                tm6000_set_reg(dev, TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55);
 195                tm6000_set_reg(dev, TM6010_REQ07_R43_VBI_DATA_TYPE_LINE7, 0x66);
 196                tm6000_set_reg(dev, TM6010_REQ07_R44_VBI_DATA_TYPE_LINE8, 0x66);
 197                tm6000_set_reg(dev, TM6010_REQ07_R45_VBI_DATA_TYPE_LINE9, 0x66);
 198                tm6000_set_reg(dev,
 199                        TM6010_REQ07_R46_VBI_DATA_TYPE_LINE10, 0x66);
 200                tm6000_set_reg(dev,
 201                        TM6010_REQ07_R47_VBI_DATA_TYPE_LINE11, 0x66);
 202                tm6000_set_reg(dev,
 203                        TM6010_REQ07_R48_VBI_DATA_TYPE_LINE12, 0x66);
 204                tm6000_set_reg(dev,
 205                        TM6010_REQ07_R49_VBI_DATA_TYPE_LINE13, 0x66);
 206                tm6000_set_reg(dev,
 207                        TM6010_REQ07_R4A_VBI_DATA_TYPE_LINE14, 0x66);
 208                tm6000_set_reg(dev,
 209                        TM6010_REQ07_R4B_VBI_DATA_TYPE_LINE15, 0x66);
 210                tm6000_set_reg(dev,
 211                        TM6010_REQ07_R4C_VBI_DATA_TYPE_LINE16, 0x66);
 212                tm6000_set_reg(dev,
 213                        TM6010_REQ07_R4D_VBI_DATA_TYPE_LINE17, 0x66);
 214                tm6000_set_reg(dev,
 215                        TM6010_REQ07_R4E_VBI_DATA_TYPE_LINE18, 0x66);
 216                tm6000_set_reg(dev,
 217                        TM6010_REQ07_R4F_VBI_DATA_TYPE_LINE19, 0x66);
 218                tm6000_set_reg(dev,
 219                        TM6010_REQ07_R50_VBI_DATA_TYPE_LINE20, 0x66);
 220                tm6000_set_reg(dev,
 221                        TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x66);
 222                tm6000_set_reg(dev,
 223                        TM6010_REQ07_R52_VBI_DATA_TYPE_LINE22, 0x66);
 224                tm6000_set_reg(dev,
 225                        TM6010_REQ07_R53_VBI_DATA_TYPE_LINE23, 0x00);
 226                tm6000_set_reg(dev,
 227                        TM6010_REQ07_R54_VBI_DATA_TYPE_RLINES, 0x00);
 228                tm6000_set_reg(dev,
 229                        TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01);
 230                tm6000_set_reg(dev,
 231                        TM6010_REQ07_R56_VBI_LOOP_FILTER_I_GAIN, 0x00);
 232                tm6000_set_reg(dev,
 233                        TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02);
 234                tm6000_set_reg(dev, TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35);
 235                tm6000_set_reg(dev, TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0);
 236                tm6000_set_reg(dev, TM6010_REQ07_R5A_VBI_TELETEXT_DTO1, 0x11);
 237                tm6000_set_reg(dev, TM6010_REQ07_R5B_VBI_TELETEXT_DTO0, 0x4c);
 238                tm6000_set_reg(dev, TM6010_REQ07_R40_TELETEXT_VBI_CODE0, 0x01);
 239                tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00);
 240        }
 241}
 242
 243int tm6000_init_analog_mode(struct tm6000_core *dev)
 244{
 245        struct v4l2_frequency f;
 246
 247        if (dev->dev_type == TM6010) {
 248                int val;
 249
 250                /* Enable video */
 251                val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0);
 252                val |= 0x60;
 253                tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val);
 254                val = tm6000_get_reg(dev,
 255                        TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0);
 256                val &= ~0x40;
 257                tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, val);
 258
 259                tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc);
 260
 261        } else {
 262                /* Enables soft reset */
 263                tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01);
 264
 265                if (dev->scaler)
 266                        tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x20);
 267                else    /* Enable Hfilter and disable TS Drop err */
 268                        tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x80);
 269
 270                tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x88);
 271                tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_WAKEUP_SEL, 0x23);
 272                tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xc0);
 273                tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xd8);
 274                tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x06);
 275                tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_PULSE_CNT0, 0x1f);
 276
 277                /* AP Software reset */
 278                tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08);
 279                tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x00);
 280
 281                tm6000_set_fourcc_format(dev);
 282
 283                /* Disables soft reset */
 284                tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00);
 285
 286                /* E3: Select input 0 - TV tuner */
 287                tm6000_set_reg(dev, TM6010_REQ07_RE3_OUT_SEL1, 0x00);
 288                tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xeb, 0x60);
 289
 290                /* This controls input */
 291                tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_2, 0x0);
 292                tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_3, 0x01);
 293        }
 294        msleep(20);
 295
 296        /* Tuner firmware can now be loaded */
 297
 298        /*
 299         * FIXME: This is a hack! xc3028 "sleeps" when no channel is detected
 300         * for more than a few seconds. Not sure why, as this behavior does
 301         * not happen on other devices with xc3028. So, I suspect that it
 302         * is yet another bug at tm6000. After start sleeping, decoding 
 303         * doesn't start automatically. Instead, it requires some
 304         * I2C commands to wake it up. As we want to have image at the
 305         * beginning, we needed to add this hack. The better would be to
 306         * discover some way to make tm6000 to wake up without this hack.
 307         */
 308        f.frequency = dev->freq;
 309        v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
 310
 311        msleep(100);
 312        tm6000_set_standard(dev, &dev->norm);
 313        tm6000_set_vbi(dev);
 314        tm6000_set_audio_bitrate(dev, 48000);
 315
 316        /* switch dvb led off */
 317        if (dev->gpio.dvb_led) {
 318                tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
 319                        dev->gpio.dvb_led, 0x01);
 320        }
 321
 322        return 0;
 323}
 324
 325int tm6000_init_digital_mode(struct tm6000_core *dev)
 326{
 327        if (dev->dev_type == TM6010) {
 328                int val;
 329                u8 buf[2];
 330
 331                /* digital init */
 332                val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0);
 333                val &= ~0x60;
 334                tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val);
 335                val = tm6000_get_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0);
 336                val |= 0x40;
 337                tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, val);
 338                tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, 0x28);
 339                tm6000_set_reg(dev, TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xfc);
 340                tm6000_set_reg(dev, TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0xff);
 341                tm6000_read_write_usb(dev, 0xc0, 0x0e, 0x00c2, 0x0008, buf, 2);
 342                printk(KERN_INFO"buf %#x %#x\n", buf[0], buf[1]);
 343        } else  {
 344                tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08);
 345                tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x00);
 346                tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01);
 347                tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_PULSE_CNT0, 0x08);
 348                tm6000_set_reg(dev, TM6010_REQ07_RE2_OUT_SEL2, 0x0c);
 349                tm6000_set_reg(dev, TM6010_REQ07_RE8_TYPESEL_MOS_I2S, 0xff);
 350                tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0x00eb, 0xd8);
 351                tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x40);
 352                tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0);
 353                tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x09);
 354                tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_WAKEUP_SEL, 0x37);
 355                tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xd8);
 356                tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xc0);
 357                tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x60);
 358
 359                tm6000_set_reg(dev, TM6010_REQ07_RE2_OUT_SEL2, 0x0c);
 360                tm6000_set_reg(dev, TM6010_REQ07_RE8_TYPESEL_MOS_I2S, 0xff);
 361                tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0x00eb, 0x08);
 362                msleep(50);
 363
 364                tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00);
 365                msleep(50);
 366                tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x01);
 367                msleep(50);
 368                tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00);
 369                msleep(100);
 370        }
 371
 372        /* switch dvb led on */
 373        if (dev->gpio.dvb_led) {
 374                tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
 375                        dev->gpio.dvb_led, 0x00);
 376        }
 377
 378        return 0;
 379}
 380EXPORT_SYMBOL(tm6000_init_digital_mode);
 381
 382struct reg_init {
 383        u8 req;
 384        u8 reg;
 385        u8 val;
 386};
 387
 388/* The meaning of those initializations are unknown */
 389struct reg_init tm6000_init_tab[] = {
 390        /* REG  VALUE */
 391        { TM6010_REQ07_RD8_IR_PULSE_CNT0, 0x1f },
 392        { TM6010_REQ07_RFF_SOFT_RESET, 0x08 },
 393        { TM6010_REQ07_RFF_SOFT_RESET, 0x00 },
 394        { TM6010_REQ07_RD5_POWERSAVE, 0x4f },
 395        { TM6010_REQ07_RD8_IR_WAKEUP_SEL, 0x23 },
 396        { TM6010_REQ07_RD8_IR_WAKEUP_ADD, 0x08 },
 397        { TM6010_REQ07_RE2_OUT_SEL2, 0x00 },
 398        { TM6010_REQ07_RE3_OUT_SEL1, 0x10 },
 399        { TM6010_REQ07_RE5_REMOTE_WAKEUP, 0x00 },
 400        { TM6010_REQ07_RE8_TYPESEL_MOS_I2S, 0x00 },
 401        { REQ_07_SET_GET_AVREG,  0xeb, 0x64 },          /* 48000 bits/sample, external input */
 402        { REQ_07_SET_GET_AVREG,  0xee, 0xc2 },
 403        { TM6010_REQ07_R3F_RESET, 0x01 },               /* Start of soft reset */
 404        { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 },
 405        { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x07 },
 406        { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
 407        { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 },
 408        { TM6010_REQ07_R05_NOISE_THRESHOLD, 0x64 },
 409        { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01 },
 410        { TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0x82 },
 411        { TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0x36 },
 412        { TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0x50 },
 413        { TM6010_REQ07_R0C_CHROMA_AGC_CONTROL, 0x6a },
 414        { TM6010_REQ07_R11_AGC_PEAK_CONTROL, 0xc9 },
 415        { TM6010_REQ07_R12_AGC_GATE_STARTH, 0x07 },
 416        { TM6010_REQ07_R13_AGC_GATE_STARTL, 0x3b },
 417        { TM6010_REQ07_R14_AGC_GATE_WIDTH, 0x47 },
 418        { TM6010_REQ07_R15_AGC_BP_DELAY, 0x6f },
 419        { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0xcd },
 420        { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e },
 421        { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b },
 422        { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 },
 423        { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 },
 424        { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
 425        { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
 426        { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
 427        { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
 428        { TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME, 0x3c },
 429        { TM6010_REQ07_R21_HSYNC_PHASE_OFFSET, 0x3c },
 430        { TM6010_REQ07_R2D_CHROMA_BURST_END, 0x48 },
 431        { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 },
 432        { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 },
 433        { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 },
 434        { TM6010_REQ07_R32_VSYNC_HLOCK_MIN, 0x74 },
 435        { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c },
 436        { TM6010_REQ07_R34_VSYNC_AGC_MIN, 0x74 },
 437        { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
 438        { TM6010_REQ07_R36_VSYNC_VBI_MIN, 0x7a },
 439        { TM6010_REQ07_R37_VSYNC_VBI_MAX, 0x26 },
 440        { TM6010_REQ07_R38_VSYNC_THRESHOLD, 0x40 },
 441        { TM6010_REQ07_R39_VSYNC_TIME_CONSTANT, 0x0a },
 442        { TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55 },
 443        { TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x11 },
 444        { TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01 },
 445        { TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02 },
 446        { TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35 },
 447        { TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0 },
 448        { TM6010_REQ07_R80_COMB_FILTER_TRESHOLD, 0x15 },
 449        { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 },
 450        { TM6010_REQ07_RC1_TRESHOLD, 0xd0 },
 451        { TM6010_REQ07_RC3_HSTART1, 0x88 },
 452        { TM6010_REQ07_R3F_RESET, 0x00 },               /* End of the soft reset */
 453        { TM6010_REQ05_R18_IMASK7, 0x00 },
 454};
 455
 456struct reg_init tm6010_init_tab[] = {
 457        { TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x00 },
 458        { TM6010_REQ07_RC4_HSTART0, 0xa0 },
 459        { TM6010_REQ07_RC6_HEND0, 0x40 },
 460        { TM6010_REQ07_RCA_VEND0, 0x31 },
 461        { TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0xe1 },
 462        { TM6010_REQ07_RE0_DVIDEO_SOURCE, 0x03 },
 463        { TM6010_REQ07_RFE_POWER_DOWN, 0x7f },
 464
 465        { TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0 },
 466        { TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4 },
 467        { TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8 },
 468        { TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x00 },
 469        { TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2 },
 470        { TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0 },
 471        { TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2 },
 472        { TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x60 },
 473        { TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc },
 474
 475        { TM6010_REQ07_R3F_RESET, 0x01 },
 476        { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 },
 477        { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x07 },
 478        { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
 479        { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 },
 480        { TM6010_REQ07_R05_NOISE_THRESHOLD, 0x64 },
 481        { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01 },
 482        { TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0x82 },
 483        { TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0x36 },
 484        { TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0x50 },
 485        { TM6010_REQ07_R0C_CHROMA_AGC_CONTROL, 0x6a },
 486        { TM6010_REQ07_R11_AGC_PEAK_CONTROL, 0xc9 },
 487        { TM6010_REQ07_R12_AGC_GATE_STARTH, 0x07 },
 488        { TM6010_REQ07_R13_AGC_GATE_STARTL, 0x3b },
 489        { TM6010_REQ07_R14_AGC_GATE_WIDTH, 0x47 },
 490        { TM6010_REQ07_R15_AGC_BP_DELAY, 0x6f },
 491        { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0xcd },
 492        { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e },
 493        { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b },
 494        { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 },
 495        { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 },
 496        { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
 497        { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
 498        { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
 499        { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
 500        { TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME, 0x3c },
 501        { TM6010_REQ07_R21_HSYNC_PHASE_OFFSET, 0x3c },
 502        { TM6010_REQ07_R2D_CHROMA_BURST_END, 0x48 },
 503        { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 },
 504        { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 },
 505        { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 },
 506        { TM6010_REQ07_R32_VSYNC_HLOCK_MIN, 0x74 },
 507        { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c },
 508        { TM6010_REQ07_R34_VSYNC_AGC_MIN, 0x74 },
 509        { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
 510        { TM6010_REQ07_R36_VSYNC_VBI_MIN, 0x7a },
 511        { TM6010_REQ07_R37_VSYNC_VBI_MAX, 0x26 },
 512        { TM6010_REQ07_R38_VSYNC_THRESHOLD, 0x40 },
 513        { TM6010_REQ07_R39_VSYNC_TIME_CONSTANT, 0x0a },
 514        { TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55 },
 515        { TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x11 },
 516        { TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01 },
 517        { TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02 },
 518        { TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35 },
 519        { TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0 },
 520        { TM6010_REQ07_R80_COMB_FILTER_TRESHOLD, 0x15 },
 521        { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 },
 522        { TM6010_REQ07_RC1_TRESHOLD, 0xd0 },
 523        { TM6010_REQ07_RC3_HSTART1, 0x88 },
 524        { TM6010_REQ07_R3F_RESET, 0x00 },
 525
 526        { TM6010_REQ05_R18_IMASK7, 0x00 },
 527
 528        { TM6010_REQ07_RD8_IR_LEADER1, 0xaa },
 529        { TM6010_REQ07_RD8_IR_LEADER0, 0x30 },
 530        { TM6010_REQ07_RD8_IR_PULSE_CNT1, 0x20 },
 531        { TM6010_REQ07_RD8_IR_PULSE_CNT0, 0xd0 },
 532        { REQ_04_EN_DISABLE_MCU_INT, 0x02, 0x00 },
 533        { TM6010_REQ07_RD8_IR, 0x2f },
 534
 535        /* set remote wakeup key:any key wakeup */
 536        { TM6010_REQ07_RE5_REMOTE_WAKEUP,  0xfe },
 537        { TM6010_REQ07_RD8_IR_WAKEUP_SEL,  0xff },
 538};
 539
 540int tm6000_init(struct tm6000_core *dev)
 541{
 542        int board, rc = 0, i, size;
 543        struct reg_init *tab;
 544
 545        /* Check board revision */
 546        board = tm6000_get_reg32(dev, REQ_40_GET_VERSION, 0, 0);
 547        if (board >= 0) {
 548                switch (board & 0xff) {
 549                case 0xf3:
 550                        printk(KERN_INFO "Found tm6000\n");
 551                        if (dev->dev_type != TM6000)
 552                                dev->dev_type = TM6000;
 553                        break;
 554                case 0xf4:
 555                        printk(KERN_INFO "Found tm6010\n");
 556                        if (dev->dev_type != TM6010)
 557                                dev->dev_type = TM6010;
 558                        break;
 559                default:
 560                        printk(KERN_INFO "Unknown board version = 0x%08x\n", board);
 561                }
 562        } else
 563                printk(KERN_ERR "Error %i while retrieving board version\n", board);
 564
 565        if (dev->dev_type == TM6010) {
 566                tab = tm6010_init_tab;
 567                size = ARRAY_SIZE(tm6010_init_tab);
 568        } else {
 569                tab = tm6000_init_tab;
 570                size = ARRAY_SIZE(tm6000_init_tab);
 571        }
 572
 573        /* Load board's initialization table */
 574        for (i = 0; i < size; i++) {
 575                rc = tm6000_set_reg(dev, tab[i].req, tab[i].reg, tab[i].val);
 576                if (rc < 0) {
 577                        printk(KERN_ERR "Error %i while setting req %d, "
 578                                        "reg %d to value %d\n", rc,
 579                                        tab[i].req, tab[i].reg, tab[i].val);
 580                        return rc;
 581                }
 582        }
 583
 584        msleep(5); /* Just to be conservative */
 585
 586        rc = tm6000_cards_setup(dev);
 587
 588        return rc;
 589}
 590
 591int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate)
 592{
 593        int val;
 594
 595        if (dev->dev_type == TM6010) {
 596                val = tm6000_get_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0);
 597                if (val < 0)
 598                        return val;
 599                val = (val & 0xf0) | 0x1; /* 48 kHz, not muted */
 600                val = tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, val);
 601                if (val < 0)
 602                        return val;
 603        }
 604
 605        val = tm6000_get_reg(dev, REQ_07_SET_GET_AVREG, 0xeb, 0x0);
 606        if (val < 0)
 607                return val;
 608
 609        val &= 0x0f;            /* Preserve the audio input control bits */
 610        switch (bitrate) {
 611        case 44100:
 612                val |= 0xd0;
 613                dev->audio_bitrate = bitrate;
 614                break;
 615        case 48000:
 616                val |= 0x60;
 617                dev->audio_bitrate = bitrate;
 618                break;
 619        }
 620        val = tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xeb, val);
 621
 622        return val;
 623}
 624EXPORT_SYMBOL_GPL(tm6000_set_audio_bitrate);
 625
 626static LIST_HEAD(tm6000_devlist);
 627static DEFINE_MUTEX(tm6000_devlist_mutex);
 628
 629/*
 630 * tm6000_realease_resource()
 631 */
 632
 633void tm6000_remove_from_devlist(struct tm6000_core *dev)
 634{
 635        mutex_lock(&tm6000_devlist_mutex);
 636        list_del(&dev->devlist);
 637        mutex_unlock(&tm6000_devlist_mutex);
 638};
 639
 640void tm6000_add_into_devlist(struct tm6000_core *dev)
 641{
 642        mutex_lock(&tm6000_devlist_mutex);
 643        list_add_tail(&dev->devlist, &tm6000_devlist);
 644        mutex_unlock(&tm6000_devlist_mutex);
 645};
 646
 647/*
 648 * Extension interface
 649 */
 650
 651static LIST_HEAD(tm6000_extension_devlist);
 652
 653int tm6000_call_fillbuf(struct tm6000_core *dev, enum tm6000_ops_type type,
 654                        char *buf, int size)
 655{
 656        struct tm6000_ops *ops = NULL;
 657
 658        /* FIXME: tm6000_extension_devlist_lock should be a spinlock */
 659
 660        if (!list_empty(&tm6000_extension_devlist)) {
 661                list_for_each_entry(ops, &tm6000_extension_devlist, next) {
 662                        if (ops->fillbuf && ops->type == type)
 663                                ops->fillbuf(dev, buf, size);
 664                }
 665        }
 666
 667        return 0;
 668}
 669
 670int tm6000_register_extension(struct tm6000_ops *ops)
 671{
 672        struct tm6000_core *dev = NULL;
 673
 674        mutex_lock(&tm6000_devlist_mutex);
 675        list_add_tail(&ops->next, &tm6000_extension_devlist);
 676        list_for_each_entry(dev, &tm6000_devlist, devlist) {
 677                ops->init(dev);
 678                printk(KERN_INFO "%s: Initialized (%s) extension\n",
 679                       dev->name, ops->name);
 680        }
 681        mutex_unlock(&tm6000_devlist_mutex);
 682        return 0;
 683}
 684EXPORT_SYMBOL(tm6000_register_extension);
 685
 686void tm6000_unregister_extension(struct tm6000_ops *ops)
 687{
 688        struct tm6000_core *dev = NULL;
 689
 690        mutex_lock(&tm6000_devlist_mutex);
 691        list_for_each_entry(dev, &tm6000_devlist, devlist)
 692                ops->fini(dev);
 693
 694        printk(KERN_INFO "tm6000: Remove (%s) extension\n", ops->name);
 695        list_del(&ops->next);
 696        mutex_unlock(&tm6000_devlist_mutex);
 697}
 698EXPORT_SYMBOL(tm6000_unregister_extension);
 699
 700void tm6000_init_extension(struct tm6000_core *dev)
 701{
 702        struct tm6000_ops *ops = NULL;
 703
 704        mutex_lock(&tm6000_devlist_mutex);
 705        if (!list_empty(&tm6000_extension_devlist)) {
 706                list_for_each_entry(ops, &tm6000_extension_devlist, next) {
 707                        if (ops->init)
 708                                ops->init(dev);
 709                }
 710        }
 711        mutex_unlock(&tm6000_devlist_mutex);
 712}
 713
 714void tm6000_close_extension(struct tm6000_core *dev)
 715{
 716        struct tm6000_ops *ops = NULL;
 717
 718        mutex_lock(&tm6000_devlist_mutex);
 719        if (!list_empty(&tm6000_extension_devlist)) {
 720                list_for_each_entry(ops, &tm6000_extension_devlist, next) {
 721                        if (ops->fini)
 722                                ops->fini(dev);
 723                }
 724        }
 725        mutex_unlock(&tm6000_devlist_mutex);
 726}
 727