linux/drivers/staging/media/solo6x10/solo6x10-tw28.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
   3 *
   4 * Original author:
   5 * Ben Collins <bcollins@ubuntu.com>
   6 *
   7 * Additional work by:
   8 * John Brooks <john.brooks@bluecherry.net>
   9 *
  10 * This program is free software; you can redistribute it and/or modify
  11 * it under the terms of the GNU General Public License as published by
  12 * the Free Software Foundation; either version 2 of the License, or
  13 * (at your option) any later version.
  14 *
  15 * This program is distributed in the hope that it will be useful,
  16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18 * GNU General Public License for more details.
  19 *
  20 * You should have received a copy of the GNU General Public License
  21 * along with this program; if not, write to the Free Software
  22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  23 */
  24
  25#include <linux/kernel.h>
  26#include <linux/delay.h>
  27
  28#include "solo6x10.h"
  29#include "solo6x10-tw28.h"
  30
  31#define DEFAULT_HDELAY_NTSC             (32 - 8)
  32#define DEFAULT_HACTIVE_NTSC            (720 + 16)
  33#define DEFAULT_VDELAY_NTSC             (7 - 2)
  34#define DEFAULT_VACTIVE_NTSC            (240 + 4)
  35
  36#define DEFAULT_HDELAY_PAL              (32 + 4)
  37#define DEFAULT_HACTIVE_PAL             (864-DEFAULT_HDELAY_PAL)
  38#define DEFAULT_VDELAY_PAL              (6)
  39#define DEFAULT_VACTIVE_PAL             (312-DEFAULT_VDELAY_PAL)
  40
  41
  42static const u8 tbl_tw2864_ntsc_template[] = {
  43        0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */
  44        0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
  45        0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */
  46        0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
  47        0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */
  48        0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
  49        0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x30 */
  50        0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
  51        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */
  52        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  53        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
  54        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  55        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
  56        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  57        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */
  58        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00,
  59        0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
  60        0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00,
  61        0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */
  62        0x00, 0x28, 0x44, 0x44, 0xa0, 0x88, 0x5a, 0x01,
  63        0x08, 0x08, 0x08, 0x08, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */
  64        0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44,
  65        0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */
  66        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  67        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
  68        0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00,
  69        0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */
  70        0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81,
  71        0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
  72        0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
  73        0x83, 0xb5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */
  74        0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00,
  75};
  76
  77static const u8 tbl_tw2864_pal_template[] = {
  78        0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */
  79        0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
  80        0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */
  81        0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
  82        0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */
  83        0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
  84        0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */
  85        0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
  86        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */
  87        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  88        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
  89        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  90        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
  91        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  92        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */
  93        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00,
  94        0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
  95        0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00,
  96        0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */
  97        0x00, 0x28, 0x44, 0x44, 0xa0, 0x90, 0x5a, 0x01,
  98        0x0a, 0x0a, 0x0a, 0x0a, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */
  99        0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44,
 100        0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */
 101        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 102        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
 103        0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00,
 104        0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */
 105        0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81,
 106        0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
 107        0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
 108        0x83, 0xb5, 0x09, 0x00, 0xa0, 0x00, 0x01, 0x20, /* 0xf0 */
 109        0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00,
 110};
 111
 112static const u8 tbl_tw2865_ntsc_template[] = {
 113        0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */
 114        0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
 115        0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */
 116        0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
 117        0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */
 118        0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
 119        0x00, 0xf0, 0x70, 0x48, 0x80, 0x80, 0x00, 0x02, /* 0x30 */
 120        0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
 121        0x00, 0x00, 0x90, 0x68, 0x00, 0x38, 0x80, 0x80, /* 0x40 */
 122        0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00,
 123        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
 124        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 125        0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
 126        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43,
 127        0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */
 128        0xE9, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80,
 129        0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
 130        0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00,
 131        0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */
 132        0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13,
 133        0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1B, 0x1A, /* 0xa0 */
 134        0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44,
 135        0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */
 136        0xFF, 0xE7, 0xE9, 0xE9, 0xEB, 0xFF, 0xD6, 0xD8,
 137        0xD8, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
 138        0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80,
 139        0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */
 140        0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81,
 141        0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
 142        0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
 143        0x83, 0xB5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */
 144        0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0,
 145};
 146
 147static const u8 tbl_tw2865_pal_template[] = {
 148        0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */
 149        0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
 150        0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */
 151        0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
 152        0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */
 153        0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
 154        0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */
 155        0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
 156        0x00, 0x94, 0x90, 0x48, 0x00, 0x38, 0x7F, 0x80, /* 0x40 */
 157        0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00,
 158        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
 159        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 160        0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
 161        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43,
 162        0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */
 163        0xEA, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80,
 164        0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
 165        0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00,
 166        0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */
 167        0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13,
 168        0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1A, 0x1A, /* 0xa0 */
 169        0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44,
 170        0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */
 171        0xFF, 0xE7, 0xE9, 0xE9, 0xE9, 0xFF, 0xD7, 0xD8,
 172        0xD9, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
 173        0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80,
 174        0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */
 175        0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81,
 176        0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
 177        0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
 178        0x83, 0xB5, 0x09, 0x00, 0xA0, 0x00, 0x01, 0x20, /* 0xf0 */
 179        0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0,
 180};
 181
 182#define is_tw286x(__solo, __id) (!(__solo->tw2815 & (1 << __id)))
 183
 184static u8 tw_readbyte(struct solo_dev *solo_dev, int chip_id, u8 tw6x_off,
 185                      u8 tw_off)
 186{
 187        if (is_tw286x(solo_dev, chip_id))
 188                return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
 189                                         TW_CHIP_OFFSET_ADDR(chip_id),
 190                                         tw6x_off);
 191        else
 192                return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
 193                                         TW_CHIP_OFFSET_ADDR(chip_id),
 194                                         tw_off);
 195}
 196
 197static void tw_writebyte(struct solo_dev *solo_dev, int chip_id,
 198                         u8 tw6x_off, u8 tw_off, u8 val)
 199{
 200        if (is_tw286x(solo_dev, chip_id))
 201                solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
 202                                   TW_CHIP_OFFSET_ADDR(chip_id),
 203                                   tw6x_off, val);
 204        else
 205                solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
 206                                   TW_CHIP_OFFSET_ADDR(chip_id),
 207                                   tw_off, val);
 208}
 209
 210static void tw_write_and_verify(struct solo_dev *solo_dev, u8 addr, u8 off,
 211                                u8 val)
 212{
 213        int i;
 214
 215        for (i = 0; i < 5; i++) {
 216                u8 rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, addr, off);
 217                if (rval == val)
 218                        return;
 219
 220                solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, addr, off, val);
 221                msleep_interruptible(1);
 222        }
 223
 224/*      printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n", */
 225/*              addr, off, val); */
 226}
 227
 228static int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr)
 229{
 230        u8 tbl_tw2865_common[256];
 231        int i;
 232
 233        if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL)
 234                memcpy(tbl_tw2865_common, tbl_tw2865_pal_template,
 235                       sizeof(tbl_tw2865_common));
 236        else
 237                memcpy(tbl_tw2865_common, tbl_tw2865_ntsc_template,
 238                       sizeof(tbl_tw2865_common));
 239
 240        /* ALINK Mode */
 241        if (solo_dev->nr_chans == 4) {
 242                tbl_tw2865_common[0xd2] = 0x01;
 243                tbl_tw2865_common[0xcf] = 0x00;
 244        } else if (solo_dev->nr_chans == 8) {
 245                tbl_tw2865_common[0xd2] = 0x02;
 246                if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
 247                        tbl_tw2865_common[0xcf] = 0x80;
 248        } else if (solo_dev->nr_chans == 16) {
 249                tbl_tw2865_common[0xd2] = 0x03;
 250                if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
 251                        tbl_tw2865_common[0xcf] = 0x83;
 252                else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
 253                        tbl_tw2865_common[0xcf] = 0x83;
 254                else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
 255                        tbl_tw2865_common[0xcf] = 0x80;
 256        }
 257
 258        for (i = 0; i < 0xff; i++) {
 259                /* Skip read only registers */
 260                switch (i) {
 261                case 0xb8 ... 0xc1:
 262                case 0xc4 ... 0xc7:
 263                case 0xfd:
 264                        continue;
 265                }
 266                switch (i & ~0x30) {
 267                case 0x00:
 268                case 0x0c ... 0x0d:
 269                        continue;
 270                }
 271
 272                tw_write_and_verify(solo_dev, dev_addr, i,
 273                                    tbl_tw2865_common[i]);
 274        }
 275
 276        return 0;
 277}
 278
 279static int tw2864_setup(struct solo_dev *solo_dev, u8 dev_addr)
 280{
 281        u8 tbl_tw2864_common[256];
 282        int i;
 283
 284        if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL)
 285                memcpy(tbl_tw2864_common, tbl_tw2864_pal_template,
 286                       sizeof(tbl_tw2864_common));
 287        else
 288                memcpy(tbl_tw2864_common, tbl_tw2864_ntsc_template,
 289                       sizeof(tbl_tw2864_common));
 290
 291        if (solo_dev->tw2865 == 0) {
 292                /* IRQ Mode */
 293                if (solo_dev->nr_chans == 4) {
 294                        tbl_tw2864_common[0xd2] = 0x01;
 295                        tbl_tw2864_common[0xcf] = 0x00;
 296                } else if (solo_dev->nr_chans == 8) {
 297                        tbl_tw2864_common[0xd2] = 0x02;
 298                        if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
 299                                tbl_tw2864_common[0xcf] = 0x43;
 300                        else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
 301                                tbl_tw2864_common[0xcf] = 0x40;
 302                } else if (solo_dev->nr_chans == 16) {
 303                        tbl_tw2864_common[0xd2] = 0x03;
 304                        if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
 305                                tbl_tw2864_common[0xcf] = 0x43;
 306                        else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
 307                                tbl_tw2864_common[0xcf] = 0x43;
 308                        else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
 309                                tbl_tw2864_common[0xcf] = 0x43;
 310                        else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
 311                                tbl_tw2864_common[0xcf] = 0x40;
 312                }
 313        } else {
 314                /* ALINK Mode. Assumes that the first tw28xx is a
 315                 * 2865 and these are in cascade. */
 316                for (i = 0; i <= 4; i++)
 317                        tbl_tw2864_common[0x08 | i << 4] = 0x12;
 318
 319                if (solo_dev->nr_chans == 8) {
 320                        tbl_tw2864_common[0xd2] = 0x02;
 321                        if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
 322                                tbl_tw2864_common[0xcf] = 0x80;
 323                } else if (solo_dev->nr_chans == 16) {
 324                        tbl_tw2864_common[0xd2] = 0x03;
 325                        if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
 326                                tbl_tw2864_common[0xcf] = 0x83;
 327                        else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
 328                                tbl_tw2864_common[0xcf] = 0x83;
 329                        else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
 330                                tbl_tw2864_common[0xcf] = 0x80;
 331                }
 332        }
 333
 334        for (i = 0; i < 0xff; i++) {
 335                /* Skip read only registers */
 336                switch (i) {
 337                case 0xb8 ... 0xc1:
 338                case 0xfd:
 339                        continue;
 340                }
 341                switch (i & ~0x30) {
 342                case 0x00:
 343                case 0x0c:
 344                case 0x0d:
 345                        continue;
 346                }
 347
 348                tw_write_and_verify(solo_dev, dev_addr, i,
 349                                    tbl_tw2864_common[i]);
 350        }
 351
 352        return 0;
 353}
 354
 355static int tw2815_setup(struct solo_dev *solo_dev, u8 dev_addr)
 356{
 357        u8 tbl_ntsc_tw2815_common[] = {
 358                0x00, 0xc8, 0x20, 0xd0, 0x06, 0xf0, 0x08, 0x80,
 359                0x80, 0x80, 0x80, 0x02, 0x06, 0x00, 0x11,
 360        };
 361
 362        u8 tbl_pal_tw2815_common[] = {
 363                0x00, 0x88, 0x20, 0xd0, 0x05, 0x20, 0x28, 0x80,
 364                0x80, 0x80, 0x80, 0x82, 0x06, 0x00, 0x11,
 365        };
 366
 367        u8 tbl_tw2815_sfr[] = {
 368                0x00, 0x00, 0x00, 0xc0, 0x45, 0xa0, 0xd0, 0x2f, /* 0x00 */
 369                0x64, 0x80, 0x80, 0x82, 0x82, 0x00, 0x00, 0x00,
 370                0x00, 0x0f, 0x05, 0x00, 0x00, 0x80, 0x06, 0x00, /* 0x10 */
 371                0x00, 0x00, 0x00, 0xff, 0x8f, 0x00, 0x00, 0x00,
 372                0x88, 0x88, 0xc0, 0x00, 0x20, 0x64, 0xa8, 0xec, /* 0x20 */
 373                0x31, 0x75, 0xb9, 0xfd, 0x00, 0x00, 0x88, 0x88,
 374                0x88, 0x11, 0x00, 0x88, 0x88, 0x00,             /* 0x30 */
 375        };
 376        u8 *tbl_tw2815_common;
 377        int i;
 378        int ch;
 379
 380        tbl_ntsc_tw2815_common[0x06] = 0;
 381
 382        /* Horizontal Delay Control */
 383        tbl_ntsc_tw2815_common[0x02] = DEFAULT_HDELAY_NTSC & 0xff;
 384        tbl_ntsc_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_NTSC >> 8);
 385
 386        /* Horizontal Active Control */
 387        tbl_ntsc_tw2815_common[0x03] = DEFAULT_HACTIVE_NTSC & 0xff;
 388        tbl_ntsc_tw2815_common[0x06] |=
 389                ((0x03 & (DEFAULT_HACTIVE_NTSC >> 8)) << 2);
 390
 391        /* Vertical Delay Control */
 392        tbl_ntsc_tw2815_common[0x04] = DEFAULT_VDELAY_NTSC & 0xff;
 393        tbl_ntsc_tw2815_common[0x06] |=
 394                ((0x01 & (DEFAULT_VDELAY_NTSC >> 8)) << 4);
 395
 396        /* Vertical Active Control */
 397        tbl_ntsc_tw2815_common[0x05] = DEFAULT_VACTIVE_NTSC & 0xff;
 398        tbl_ntsc_tw2815_common[0x06] |=
 399                ((0x01 & (DEFAULT_VACTIVE_NTSC >> 8)) << 5);
 400
 401        tbl_pal_tw2815_common[0x06] = 0;
 402
 403        /* Horizontal Delay Control */
 404        tbl_pal_tw2815_common[0x02] = DEFAULT_HDELAY_PAL & 0xff;
 405        tbl_pal_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_PAL >> 8);
 406
 407        /* Horizontal Active Control */
 408        tbl_pal_tw2815_common[0x03] = DEFAULT_HACTIVE_PAL & 0xff;
 409        tbl_pal_tw2815_common[0x06] |=
 410                ((0x03 & (DEFAULT_HACTIVE_PAL >> 8)) << 2);
 411
 412        /* Vertical Delay Control */
 413        tbl_pal_tw2815_common[0x04] = DEFAULT_VDELAY_PAL & 0xff;
 414        tbl_pal_tw2815_common[0x06] |=
 415                ((0x01 & (DEFAULT_VDELAY_PAL >> 8)) << 4);
 416
 417        /* Vertical Active Control */
 418        tbl_pal_tw2815_common[0x05] = DEFAULT_VACTIVE_PAL & 0xff;
 419        tbl_pal_tw2815_common[0x06] |=
 420                ((0x01 & (DEFAULT_VACTIVE_PAL >> 8)) << 5);
 421
 422        tbl_tw2815_common =
 423            (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) ?
 424             tbl_ntsc_tw2815_common : tbl_pal_tw2815_common;
 425
 426        /* Dual ITU-R BT.656 format */
 427        tbl_tw2815_common[0x0d] |= 0x04;
 428
 429        /* Audio configuration */
 430        tbl_tw2815_sfr[0x62 - 0x40] &= ~(3 << 6);
 431
 432        if (solo_dev->nr_chans == 4) {
 433                tbl_tw2815_sfr[0x63 - 0x40] |= 1;
 434                tbl_tw2815_sfr[0x62 - 0x40] |= 3 << 6;
 435        } else if (solo_dev->nr_chans == 8) {
 436                tbl_tw2815_sfr[0x63 - 0x40] |= 2;
 437                if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
 438                        tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6;
 439                else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
 440                        tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6;
 441        } else if (solo_dev->nr_chans == 16) {
 442                tbl_tw2815_sfr[0x63 - 0x40] |= 3;
 443                if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
 444                        tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6;
 445                else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
 446                        tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6;
 447                else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
 448                        tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6;
 449                else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
 450                        tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6;
 451        }
 452
 453        /* Output mode of R_ADATM pin (0 mixing, 1 record) */
 454        /* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */
 455
 456        /* 8KHz, used to be 16KHz, but changed for remote client compat */
 457        tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 2;
 458        tbl_tw2815_sfr[0x6c - 0x40] |= 0 << 2;
 459
 460        /* Playback of right channel */
 461        tbl_tw2815_sfr[0x6c - 0x40] |= 1 << 5;
 462
 463        /* Reserved value (XXX ??) */
 464        tbl_tw2815_sfr[0x5c - 0x40] |= 1 << 5;
 465
 466        /* Analog output gain and mix ratio playback on full */
 467        tbl_tw2815_sfr[0x70 - 0x40] |= 0xff;
 468        /* Select playback audio and mute all except */
 469        tbl_tw2815_sfr[0x71 - 0x40] |= 0x10;
 470        tbl_tw2815_sfr[0x6d - 0x40] |= 0x0f;
 471
 472        /* End of audio configuration */
 473
 474        for (ch = 0; ch < 4; ch++) {
 475                tbl_tw2815_common[0x0d] &= ~3;
 476                switch (ch) {
 477                case 0:
 478                        tbl_tw2815_common[0x0d] |= 0x21;
 479                        break;
 480                case 1:
 481                        tbl_tw2815_common[0x0d] |= 0x20;
 482                        break;
 483                case 2:
 484                        tbl_tw2815_common[0x0d] |= 0x23;
 485                        break;
 486                case 3:
 487                        tbl_tw2815_common[0x0d] |= 0x22;
 488                        break;
 489                }
 490
 491                for (i = 0; i < 0x0f; i++) {
 492                        if (i == 0x00)
 493                                continue;       /* read-only */
 494                        solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
 495                                           dev_addr, (ch * 0x10) + i,
 496                                           tbl_tw2815_common[i]);
 497                }
 498        }
 499
 500        for (i = 0x40; i < 0x76; i++) {
 501                /* Skip read-only and nop registers */
 502                if (i == 0x40 || i == 0x59 || i == 0x5a ||
 503                    i == 0x5d || i == 0x5e || i == 0x5f)
 504                        continue;
 505
 506                solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, dev_addr, i,
 507                                       tbl_tw2815_sfr[i - 0x40]);
 508        }
 509
 510        return 0;
 511}
 512
 513#define FIRST_ACTIVE_LINE       0x0008
 514#define LAST_ACTIVE_LINE        0x0102
 515
 516static void saa7128_setup(struct solo_dev *solo_dev)
 517{
 518        int i;
 519        unsigned char regs[128] = {
 520                0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
 521                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 522                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 523                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 524                0x1C, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00,
 525                0x59, 0x1d, 0x75, 0x3f, 0x06, 0x3f, 0x00, 0x00,
 526                0x1c, 0x33, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00,
 527                0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
 528                0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18,
 529                0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f,
 530                0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06,
 531                0x02, 0x80, 0x71, 0x77, 0xa7, 0x67, 0x66, 0x2e,
 532                0x7b, 0x11, 0x4f, 0x1f, 0x7c, 0xf0, 0x21, 0x77,
 533                0x41, 0x88, 0x41, 0x12, 0xed, 0x10, 0x10, 0x00,
 534                0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00,
 535                0x00, 0x00, 0x08, 0xff, 0x80, 0x00, 0xff, 0xff,
 536        };
 537
 538        regs[0x7A] = FIRST_ACTIVE_LINE & 0xff;
 539        regs[0x7B] = LAST_ACTIVE_LINE & 0xff;
 540        regs[0x7C] = ((1 << 7) |
 541                        (((LAST_ACTIVE_LINE >> 8) & 1) << 6) |
 542                        (((FIRST_ACTIVE_LINE >> 8) & 1) << 4));
 543
 544        /* PAL: XXX: We could do a second set of regs to avoid this */
 545        if (solo_dev->video_type != SOLO_VO_FMT_TYPE_NTSC) {
 546                regs[0x28] = 0xE1;
 547
 548                regs[0x5A] = 0x0F;
 549                regs[0x61] = 0x02;
 550                regs[0x62] = 0x35;
 551                regs[0x63] = 0xCB;
 552                regs[0x64] = 0x8A;
 553                regs[0x65] = 0x09;
 554                regs[0x66] = 0x2A;
 555
 556                regs[0x6C] = 0xf1;
 557                regs[0x6E] = 0x20;
 558
 559                regs[0x7A] = 0x06 + 12;
 560                regs[0x7b] = 0x24 + 12;
 561                regs[0x7c] |= 1 << 6;
 562        }
 563
 564        /* First 0x25 bytes are read-only? */
 565        for (i = 0x26; i < 128; i++) {
 566                if (i == 0x60 || i == 0x7D)
 567                        continue;
 568                solo_i2c_writebyte(solo_dev, SOLO_I2C_SAA, 0x46, i, regs[i]);
 569        }
 570
 571        return;
 572}
 573
 574int solo_tw28_init(struct solo_dev *solo_dev)
 575{
 576        int i;
 577        u8 value;
 578
 579        solo_dev->tw28_cnt = 0;
 580
 581        /* Detect techwell chip type(s) */
 582        for (i = 0; i < solo_dev->nr_chans / 4; i++) {
 583                value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
 584                                          TW_CHIP_OFFSET_ADDR(i), 0xFF);
 585
 586                switch (value >> 3) {
 587                case 0x18:
 588                        solo_dev->tw2865 |= 1 << i;
 589                        solo_dev->tw28_cnt++;
 590                        break;
 591                case 0x0c:
 592                        solo_dev->tw2864 |= 1 << i;
 593                        solo_dev->tw28_cnt++;
 594                        break;
 595                default:
 596                        value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
 597                                                  TW_CHIP_OFFSET_ADDR(i),
 598                                                  0x59);
 599                        if ((value >> 3) == 0x04) {
 600                                solo_dev->tw2815 |= 1 << i;
 601                                solo_dev->tw28_cnt++;
 602                        }
 603                }
 604        }
 605
 606        if (solo_dev->tw28_cnt != (solo_dev->nr_chans >> 2)) {
 607                dev_err(&solo_dev->pdev->dev,
 608                        "Could not initialize any techwell chips\n");
 609                return -EINVAL;
 610        }
 611
 612        saa7128_setup(solo_dev);
 613
 614        for (i = 0; i < solo_dev->tw28_cnt; i++) {
 615                if ((solo_dev->tw2865 & (1 << i)))
 616                        tw2865_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
 617                else if ((solo_dev->tw2864 & (1 << i)))
 618                        tw2864_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
 619                else
 620                        tw2815_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
 621        }
 622
 623        return 0;
 624}
 625
 626/*
 627 * We accessed the video status signal in the Techwell chip through
 628 * iic/i2c because the video status reported by register REG_VI_STATUS1
 629 * (address 0x012C) of the SOLO6010 chip doesn't give the correct video
 630 * status signal values.
 631 */
 632int tw28_get_video_status(struct solo_dev *solo_dev, u8 ch)
 633{
 634        u8 val, chip_num;
 635
 636        /* Get the right chip and on-chip channel */
 637        chip_num = ch / 4;
 638        ch %= 4;
 639
 640        val = tw_readbyte(solo_dev, chip_num, TW286x_AV_STAT_ADDR,
 641                          TW_AV_STAT_ADDR) & 0x0f;
 642
 643        return val & (1 << ch) ? 1 : 0;
 644}
 645
 646#if 0
 647/* Status of audio from up to 4 techwell chips are combined into 1 variable.
 648 * See techwell datasheet for details. */
 649u16 tw28_get_audio_status(struct solo_dev *solo_dev)
 650{
 651        u8 val;
 652        u16 status = 0;
 653        int i;
 654
 655        for (i = 0; i < solo_dev->tw28_cnt; i++) {
 656                val = (tw_readbyte(solo_dev, i, TW286x_AV_STAT_ADDR,
 657                                   TW_AV_STAT_ADDR) & 0xf0) >> 4;
 658                status |= val << (i * 4);
 659        }
 660
 661        return status;
 662}
 663#endif
 664
 665bool tw28_has_sharpness(struct solo_dev *solo_dev, u8 ch)
 666{
 667        return is_tw286x(solo_dev, ch / 4);
 668}
 669
 670int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch,
 671                      s32 val)
 672{
 673        char sval;
 674        u8 chip_num;
 675
 676        /* Get the right chip and on-chip channel */
 677        chip_num = ch / 4;
 678        ch %= 4;
 679
 680        if (val > 255 || val < 0)
 681                return -ERANGE;
 682
 683        switch (ctrl) {
 684        case V4L2_CID_SHARPNESS:
 685                /* Only 286x has sharpness */
 686                if (is_tw286x(solo_dev, chip_num)) {
 687                        u8 v = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
 688                                                 TW_CHIP_OFFSET_ADDR(chip_num),
 689                                                 TW286x_SHARPNESS(chip_num));
 690                        v &= 0xf0;
 691                        v |= val;
 692                        solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
 693                                           TW_CHIP_OFFSET_ADDR(chip_num),
 694                                           TW286x_SHARPNESS(chip_num), v);
 695                } else {
 696                        return -EINVAL;
 697                }
 698                break;
 699
 700        case V4L2_CID_HUE:
 701                if (is_tw286x(solo_dev, chip_num))
 702                        sval = val - 128;
 703                else
 704                        sval = (char)val;
 705                tw_writebyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
 706                             TW_HUE_ADDR(ch), sval);
 707
 708                break;
 709
 710        case V4L2_CID_SATURATION:
 711                /* 286x chips have a U and V component for saturation */
 712                if (is_tw286x(solo_dev, chip_num)) {
 713                        solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
 714                                           TW_CHIP_OFFSET_ADDR(chip_num),
 715                                           TW286x_SATURATIONU_ADDR(ch), val);
 716                }
 717                tw_writebyte(solo_dev, chip_num, TW286x_SATURATIONV_ADDR(ch),
 718                             TW_SATURATION_ADDR(ch), val);
 719
 720                break;
 721
 722        case V4L2_CID_CONTRAST:
 723                tw_writebyte(solo_dev, chip_num, TW286x_CONTRAST_ADDR(ch),
 724                             TW_CONTRAST_ADDR(ch), val);
 725                break;
 726
 727        case V4L2_CID_BRIGHTNESS:
 728                if (is_tw286x(solo_dev, chip_num))
 729                        sval = val - 128;
 730                else
 731                        sval = (char)val;
 732                tw_writebyte(solo_dev, chip_num, TW286x_BRIGHTNESS_ADDR(ch),
 733                             TW_BRIGHTNESS_ADDR(ch), sval);
 734
 735                break;
 736        default:
 737                return -EINVAL;
 738        }
 739
 740        return 0;
 741}
 742
 743int tw28_get_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch,
 744                      s32 *val)
 745{
 746        u8 rval, chip_num;
 747
 748        /* Get the right chip and on-chip channel */
 749        chip_num = ch / 4;
 750        ch %= 4;
 751
 752        switch (ctrl) {
 753        case V4L2_CID_SHARPNESS:
 754                /* Only 286x has sharpness */
 755                if (is_tw286x(solo_dev, chip_num)) {
 756                        rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
 757                                                 TW_CHIP_OFFSET_ADDR(chip_num),
 758                                                 TW286x_SHARPNESS(chip_num));
 759                        *val = rval & 0x0f;
 760                } else
 761                        *val = 0;
 762                break;
 763        case V4L2_CID_HUE:
 764                rval = tw_readbyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
 765                                   TW_HUE_ADDR(ch));
 766                if (is_tw286x(solo_dev, chip_num))
 767                        *val = (s32)((char)rval) + 128;
 768                else
 769                        *val = rval;
 770                break;
 771        case V4L2_CID_SATURATION:
 772                *val = tw_readbyte(solo_dev, chip_num,
 773                                   TW286x_SATURATIONU_ADDR(ch),
 774                                   TW_SATURATION_ADDR(ch));
 775                break;
 776        case V4L2_CID_CONTRAST:
 777                *val = tw_readbyte(solo_dev, chip_num,
 778                                   TW286x_CONTRAST_ADDR(ch),
 779                                   TW_CONTRAST_ADDR(ch));
 780                break;
 781        case V4L2_CID_BRIGHTNESS:
 782                rval = tw_readbyte(solo_dev, chip_num,
 783                                   TW286x_BRIGHTNESS_ADDR(ch),
 784                                   TW_BRIGHTNESS_ADDR(ch));
 785                if (is_tw286x(solo_dev, chip_num))
 786                        *val = (s32)((char)rval) + 128;
 787                else
 788                        *val = rval;
 789                break;
 790        default:
 791                return -EINVAL;
 792        }
 793
 794        return 0;
 795}
 796
 797#if 0
 798/*
 799 * For audio output volume, the output channel is only 1. In this case we
 800 * don't need to offset TW_CHIP_OFFSET_ADDR. The TW_CHIP_OFFSET_ADDR used
 801 * is the base address of the techwell chip.
 802 */
 803void tw2815_Set_AudioOutVol(struct solo_dev *solo_dev, unsigned int u_val)
 804{
 805        unsigned int val;
 806        unsigned int chip_num;
 807
 808        chip_num = (solo_dev->nr_chans - 1) / 4;
 809
 810        val = tw_readbyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
 811                          TW_AUDIO_OUTPUT_VOL_ADDR);
 812
 813        u_val = (val & 0x0f) | (u_val << 4);
 814
 815        tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
 816                     TW_AUDIO_OUTPUT_VOL_ADDR, u_val);
 817}
 818#endif
 819
 820u8 tw28_get_audio_gain(struct solo_dev *solo_dev, u8 ch)
 821{
 822        u8 val;
 823        u8 chip_num;
 824
 825        /* Get the right chip and on-chip channel */
 826        chip_num = ch / 4;
 827        ch %= 4;
 828
 829        val = tw_readbyte(solo_dev, chip_num,
 830                          TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
 831                          TW_AUDIO_INPUT_GAIN_ADDR(ch));
 832
 833        return (ch % 2) ? (val >> 4) : (val & 0x0f);
 834}
 835
 836void tw28_set_audio_gain(struct solo_dev *solo_dev, u8 ch, u8 val)
 837{
 838        u8 old_val;
 839        u8 chip_num;
 840
 841        /* Get the right chip and on-chip channel */
 842        chip_num = ch / 4;
 843        ch %= 4;
 844
 845        old_val = tw_readbyte(solo_dev, chip_num,
 846                              TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
 847                              TW_AUDIO_INPUT_GAIN_ADDR(ch));
 848
 849        val = (old_val & ((ch % 2) ? 0x0f : 0xf0)) |
 850                ((ch % 2) ? (val << 4) : val);
 851
 852        tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
 853                     TW_AUDIO_INPUT_GAIN_ADDR(ch), val);
 854}
 855