2 * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
5 * Ben Collins <bcollins@ubuntu.com>
8 * John Brooks <john.brooks@bluecherry.net>
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.
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.
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.
25 #include <linux/kernel.h>
26 #include <linux/delay.h>
29 #include "solo6x10-tw28.h"
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)
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)
42 static 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,
77 static 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,
112 static 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,
147 static 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,
182 #define is_tw286x(__solo, __id) (!(__solo->tw2815 & (1 << __id)))
184 static u8 tw_readbyte(struct solo_dev *solo_dev, int chip_id, u8 tw6x_off,
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),
192 return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
193 TW_CHIP_OFFSET_ADDR(chip_id),
197 static void tw_writebyte(struct solo_dev *solo_dev, int chip_id,
198 u8 tw6x_off, u8 tw_off, u8 val)
200 if (is_tw286x(solo_dev, chip_id))
201 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
202 TW_CHIP_OFFSET_ADDR(chip_id),
205 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
206 TW_CHIP_OFFSET_ADDR(chip_id),
210 static void tw_write_and_verify(struct solo_dev *solo_dev, u8 addr, u8 off,
215 for (i = 0; i < 5; i++) {
216 u8 rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, addr, off);
220 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, addr, off, val);
221 msleep_interruptible(1);
224 /* printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n", */
225 /* addr, off, val); */
228 static int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr)
230 u8 tbl_tw2865_common[256];
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));
237 memcpy(tbl_tw2865_common, tbl_tw2865_ntsc_template,
238 sizeof(tbl_tw2865_common));
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;
258 for (i = 0; i < 0xff; i++) {
259 /* Skip read only registers */
272 tw_write_and_verify(solo_dev, dev_addr, i,
273 tbl_tw2865_common[i]);
279 static int tw2864_setup(struct solo_dev *solo_dev, u8 dev_addr)
281 u8 tbl_tw2864_common[256];
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));
288 memcpy(tbl_tw2864_common, tbl_tw2864_ntsc_template,
289 sizeof(tbl_tw2864_common));
291 if (solo_dev->tw2865 == 0) {
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;
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;
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;
334 for (i = 0; i < 0xff; i++) {
335 /* Skip read only registers */
348 tw_write_and_verify(solo_dev, dev_addr, i,
349 tbl_tw2864_common[i]);
355 static int tw2815_setup(struct solo_dev *solo_dev, u8 dev_addr)
357 u8 tbl_ntsc_tw2815_common[] = {
358 0x00, 0xc8, 0x20, 0xd0, 0x06, 0xf0, 0x08, 0x80,
359 0x80, 0x80, 0x80, 0x02, 0x06, 0x00, 0x11,
362 u8 tbl_pal_tw2815_common[] = {
363 0x00, 0x88, 0x20, 0xd0, 0x05, 0x20, 0x28, 0x80,
364 0x80, 0x80, 0x80, 0x82, 0x06, 0x00, 0x11,
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 */
376 u8 *tbl_tw2815_common;
380 tbl_ntsc_tw2815_common[0x06] = 0;
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);
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);
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);
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);
401 tbl_pal_tw2815_common[0x06] = 0;
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);
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);
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);
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);
423 (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) ?
424 tbl_ntsc_tw2815_common : tbl_pal_tw2815_common;
426 /* Dual ITU-R BT.656 format */
427 tbl_tw2815_common[0x0d] |= 0x04;
429 /* Audio configuration */
430 tbl_tw2815_sfr[0x62 - 0x40] &= ~(3 << 6);
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;
453 /* Output mode of R_ADATM pin (0 mixing, 1 record) */
454 /* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */
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;
460 /* Playback of right channel */
461 tbl_tw2815_sfr[0x6c - 0x40] |= 1 << 5;
463 /* Reserved value (XXX ??) */
464 tbl_tw2815_sfr[0x5c - 0x40] |= 1 << 5;
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;
472 /* End of audio configuration */
474 for (ch = 0; ch < 4; ch++) {
475 tbl_tw2815_common[0x0d] &= ~3;
478 tbl_tw2815_common[0x0d] |= 0x21;
481 tbl_tw2815_common[0x0d] |= 0x20;
484 tbl_tw2815_common[0x0d] |= 0x23;
487 tbl_tw2815_common[0x0d] |= 0x22;
491 for (i = 0; i < 0x0f; i++) {
493 continue; /* read-only */
494 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
495 dev_addr, (ch * 0x10) + i,
496 tbl_tw2815_common[i]);
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)
506 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, dev_addr, i,
507 tbl_tw2815_sfr[i - 0x40]);
513 #define FIRST_ACTIVE_LINE 0x0008
514 #define LAST_ACTIVE_LINE 0x0102
516 static void saa7128_setup(struct solo_dev *solo_dev)
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,
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));
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) {
559 regs[0x7A] = 0x06 + 12;
560 regs[0x7b] = 0x24 + 12;
561 regs[0x7c] |= 1 << 6;
564 /* First 0x25 bytes are read-only? */
565 for (i = 0x26; i < 128; i++) {
566 if (i == 0x60 || i == 0x7D)
568 solo_i2c_writebyte(solo_dev, SOLO_I2C_SAA, 0x46, i, regs[i]);
574 int solo_tw28_init(struct solo_dev *solo_dev)
579 solo_dev->tw28_cnt = 0;
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);
586 switch (value >> 3) {
588 solo_dev->tw2865 |= 1 << i;
589 solo_dev->tw28_cnt++;
592 solo_dev->tw2864 |= 1 << i;
593 solo_dev->tw28_cnt++;
596 value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
597 TW_CHIP_OFFSET_ADDR(i),
599 if ((value >> 3) == 0x04) {
600 solo_dev->tw2815 |= 1 << i;
601 solo_dev->tw28_cnt++;
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");
612 saa7128_setup(solo_dev);
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));
620 tw2815_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
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.
632 int tw28_get_video_status(struct solo_dev *solo_dev, u8 ch)
636 /* Get the right chip and on-chip channel */
640 val = tw_readbyte(solo_dev, chip_num, TW286x_AV_STAT_ADDR,
641 TW_AV_STAT_ADDR) & 0x0f;
643 return val & (1 << ch) ? 1 : 0;
647 /* Status of audio from up to 4 techwell chips are combined into 1 variable.
648 * See techwell datasheet for details. */
649 u16 tw28_get_audio_status(struct solo_dev *solo_dev)
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);
665 bool tw28_has_sharpness(struct solo_dev *solo_dev, u8 ch)
667 return is_tw286x(solo_dev, ch / 4);
670 int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch,
676 /* Get the right chip and on-chip channel */
680 if (val > 255 || val < 0)
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));
692 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
693 TW_CHIP_OFFSET_ADDR(chip_num),
694 TW286x_SHARPNESS(chip_num), v);
701 if (is_tw286x(solo_dev, chip_num))
705 tw_writebyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
706 TW_HUE_ADDR(ch), sval);
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);
717 tw_writebyte(solo_dev, chip_num, TW286x_SATURATIONV_ADDR(ch),
718 TW_SATURATION_ADDR(ch), val);
722 case V4L2_CID_CONTRAST:
723 tw_writebyte(solo_dev, chip_num, TW286x_CONTRAST_ADDR(ch),
724 TW_CONTRAST_ADDR(ch), val);
727 case V4L2_CID_BRIGHTNESS:
728 if (is_tw286x(solo_dev, chip_num))
732 tw_writebyte(solo_dev, chip_num, TW286x_BRIGHTNESS_ADDR(ch),
733 TW_BRIGHTNESS_ADDR(ch), sval);
743 int tw28_get_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch,
748 /* Get the right chip and on-chip channel */
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));
764 rval = tw_readbyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
766 if (is_tw286x(solo_dev, chip_num))
767 *val = (s32)((char)rval) + 128;
771 case V4L2_CID_SATURATION:
772 *val = tw_readbyte(solo_dev, chip_num,
773 TW286x_SATURATIONU_ADDR(ch),
774 TW_SATURATION_ADDR(ch));
776 case V4L2_CID_CONTRAST:
777 *val = tw_readbyte(solo_dev, chip_num,
778 TW286x_CONTRAST_ADDR(ch),
779 TW_CONTRAST_ADDR(ch));
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;
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.
803 void tw2815_Set_AudioOutVol(struct solo_dev *solo_dev, unsigned int u_val)
806 unsigned int chip_num;
808 chip_num = (solo_dev->nr_chans - 1) / 4;
810 val = tw_readbyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
811 TW_AUDIO_OUTPUT_VOL_ADDR);
813 u_val = (val & 0x0f) | (u_val << 4);
815 tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
816 TW_AUDIO_OUTPUT_VOL_ADDR, u_val);
820 u8 tw28_get_audio_gain(struct solo_dev *solo_dev, u8 ch)
825 /* Get the right chip and on-chip channel */
829 val = tw_readbyte(solo_dev, chip_num,
830 TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
831 TW_AUDIO_INPUT_GAIN_ADDR(ch));
833 return (ch % 2) ? (val >> 4) : (val & 0x0f);
836 void tw28_set_audio_gain(struct solo_dev *solo_dev, u8 ch, u8 val)
841 /* Get the right chip and on-chip channel */
845 old_val = tw_readbyte(solo_dev, chip_num,
846 TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
847 TW_AUDIO_INPUT_GAIN_ADDR(ch));
849 val = (old_val & ((ch % 2) ? 0x0f : 0xf0)) |
850 ((ch % 2) ? (val << 4) : val);
852 tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
853 TW_AUDIO_INPUT_GAIN_ADDR(ch), val);