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