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