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