2 * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
3 * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
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.
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.
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.
20 #include <linux/kernel.h>
24 /* XXX: Some of these values are masked into an 8-bit regs, and shifted
25 * around for other 8-bit regs. What are the magic bits in these values? */
26 #define DEFAULT_HDELAY_NTSC (32 - 4)
27 #define DEFAULT_HACTIVE_NTSC (720 + 16)
28 #define DEFAULT_VDELAY_NTSC (7 - 2)
29 #define DEFAULT_VACTIVE_NTSC (240 + 4)
31 #define DEFAULT_HDELAY_PAL (32 + 4)
32 #define DEFAULT_HACTIVE_PAL (864-DEFAULT_HDELAY_PAL)
33 #define DEFAULT_VDELAY_PAL (6)
34 #define DEFAULT_VACTIVE_PAL (312-DEFAULT_VDELAY_PAL)
36 static u8 tbl_tw2864_template
[] = {
37 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x00 */
38 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
39 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x10 */
40 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
41 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x20 */
42 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
43 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x30 */
44 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
45 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */
46 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
47 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
48 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
49 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
50 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
51 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */
52 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00,
53 0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
54 0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00,
55 0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */
56 0x00, 0x28, 0x44, 0x44, 0xa0, 0x88, 0x5a, 0x01,
57 0x08, 0x08, 0x08, 0x08, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */
58 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44,
59 0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */
60 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
61 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
62 0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00,
63 0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */
64 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81,
65 0x10, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
66 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
67 0x83, 0xb5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */
68 0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00,
71 static u8 tbl_tw2865_ntsc_template
[] = {
72 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */
73 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
74 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */
75 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
76 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */
77 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
78 0x00, 0xf0, 0x70, 0x48, 0x80, 0x80, 0x00, 0x02, /* 0x30 */
79 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
80 0x00, 0x00, 0x90, 0x68, 0x00, 0x38, 0x80, 0x80, /* 0x40 */
81 0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00,
82 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
83 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
84 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
85 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43,
86 0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */
87 0xE9, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80,
88 0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
89 0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00,
90 0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */
91 0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13,
92 0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1B, 0x1A, /* 0xa0 */
93 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44,
94 0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */
95 0xFF, 0xE7, 0xE9, 0xE9, 0xEB, 0xFF, 0xD6, 0xD8,
96 0xD8, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
97 0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80,
98 0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */
99 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81,
100 0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
101 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
102 0x83, 0xB5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */
103 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0,
106 static u8 tbl_tw2865_pal_template
[] = {
107 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */
108 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
109 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */
110 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
111 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */
112 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
113 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */
114 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
115 0x00, 0x94, 0x90, 0x48, 0x00, 0x38, 0x7F, 0x80, /* 0x40 */
116 0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00,
117 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
118 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
119 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
120 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43,
121 0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */
122 0xEA, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80,
123 0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
124 0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00,
125 0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */
126 0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13,
127 0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1A, 0x1A, /* 0xa0 */
128 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44,
129 0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */
130 0xFF, 0xE7, 0xE9, 0xE9, 0xE9, 0xFF, 0xD7, 0xD8,
131 0xD9, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
132 0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80,
133 0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */
134 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81,
135 0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
136 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
137 0x83, 0xB5, 0x09, 0x00, 0xA0, 0x00, 0x01, 0x20, /* 0xf0 */
138 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0,
141 #define is_tw286x(__solo, __id) (!(__solo->tw2815 & (1 << __id)))
143 static u8
tw_readbyte(struct solo_dev
*solo_dev
, int chip_id
, u8 tw6x_off
,
146 if (is_tw286x(solo_dev
, chip_id
))
147 return solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
,
148 TW_CHIP_OFFSET_ADDR(chip_id
),
151 return solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
,
152 TW_CHIP_OFFSET_ADDR(chip_id
),
156 static void tw_writebyte(struct solo_dev
*solo_dev
, int chip_id
,
157 u8 tw6x_off
, u8 tw_off
, u8 val
)
159 if (is_tw286x(solo_dev
, chip_id
))
160 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
,
161 TW_CHIP_OFFSET_ADDR(chip_id
),
164 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
,
165 TW_CHIP_OFFSET_ADDR(chip_id
),
169 static void tw_write_and_verify(struct solo_dev
*solo_dev
, u8 addr
, u8 off
,
174 for (i
= 0; i
< 5; i
++) {
175 u8 rval
= solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
, addr
, off
);
179 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
, addr
, off
, val
);
180 msleep_interruptible(1);
183 /* printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n",
187 static int tw2865_setup(struct solo_dev
*solo_dev
, u8 dev_addr
)
189 u8 tbl_tw2865_common
[256];
192 if (solo_dev
->video_type
== SOLO_VO_FMT_TYPE_PAL
)
193 memcpy(tbl_tw2865_common
, tbl_tw2865_pal_template
,
194 sizeof(tbl_tw2865_common
));
196 memcpy(tbl_tw2865_common
, tbl_tw2865_ntsc_template
,
197 sizeof(tbl_tw2865_common
));
200 if (solo_dev
->nr_chans
== 4) {
201 tbl_tw2865_common
[0xd2] = 0x01;
202 tbl_tw2865_common
[0xcf] = 0x00;
203 } else if (solo_dev
->nr_chans
== 8) {
204 tbl_tw2865_common
[0xd2] = 0x02;
205 if (dev_addr
== TW_CHIP_OFFSET_ADDR(1))
206 tbl_tw2865_common
[0xcf] = 0x80;
207 } else if (solo_dev
->nr_chans
== 16) {
208 tbl_tw2865_common
[0xd2] = 0x03;
209 if (dev_addr
== TW_CHIP_OFFSET_ADDR(1))
210 tbl_tw2865_common
[0xcf] = 0x83;
211 else if (dev_addr
== TW_CHIP_OFFSET_ADDR(2))
212 tbl_tw2865_common
[0xcf] = 0x83;
213 else if (dev_addr
== TW_CHIP_OFFSET_ADDR(3))
214 tbl_tw2865_common
[0xcf] = 0x80;
217 for (i
= 0; i
< 0xff; i
++) {
218 /* Skip read only registers */
219 if (i
>= 0xb8 && i
<= 0xc1)
221 if ((i
& ~0x30) == 0x00 ||
222 (i
& ~0x30) == 0x0c ||
225 if (i
>= 0xc4 && i
<= 0xc7)
230 tw_write_and_verify(solo_dev
, dev_addr
, i
,
231 tbl_tw2865_common
[i
]);
237 static int tw2864_setup(struct solo_dev
*solo_dev
, u8 dev_addr
)
239 u8 tbl_tw2864_common
[sizeof(tbl_tw2864_template
)];
242 memcpy(tbl_tw2864_common
, tbl_tw2864_template
,
243 sizeof(tbl_tw2864_common
));
245 if (solo_dev
->tw2865
== 0) {
247 if (solo_dev
->nr_chans
== 4) {
248 tbl_tw2864_common
[0xd2] = 0x01;
249 tbl_tw2864_common
[0xcf] = 0x00;
250 } else if (solo_dev
->nr_chans
== 8) {
251 tbl_tw2864_common
[0xd2] = 0x02;
252 if (dev_addr
== TW_CHIP_OFFSET_ADDR(0))
253 tbl_tw2864_common
[0xcf] = 0x43;
254 else if (dev_addr
== TW_CHIP_OFFSET_ADDR(1))
255 tbl_tw2864_common
[0xcf] = 0x40;
256 } else if (solo_dev
->nr_chans
== 16) {
257 tbl_tw2864_common
[0xd2] = 0x03;
258 if (dev_addr
== TW_CHIP_OFFSET_ADDR(0))
259 tbl_tw2864_common
[0xcf] = 0x43;
260 else if (dev_addr
== TW_CHIP_OFFSET_ADDR(1))
261 tbl_tw2864_common
[0xcf] = 0x43;
262 else if (dev_addr
== TW_CHIP_OFFSET_ADDR(2))
263 tbl_tw2864_common
[0xcf] = 0x43;
264 else if (dev_addr
== TW_CHIP_OFFSET_ADDR(3))
265 tbl_tw2864_common
[0xcf] = 0x40;
268 /* ALINK Mode. Assumes that the first tw28xx is a
269 * 2865 and these are in cascade. */
270 for (i
= 0; i
<= 4; i
++)
271 tbl_tw2864_common
[0x08 | i
<< 4] = 0x12;
273 if (solo_dev
->nr_chans
== 8) {
274 tbl_tw2864_common
[0xd2] = 0x02;
275 if (dev_addr
== TW_CHIP_OFFSET_ADDR(1))
276 tbl_tw2864_common
[0xcf] = 0x80;
277 } else if (solo_dev
->nr_chans
== 16) {
278 tbl_tw2864_common
[0xd2] = 0x03;
279 if (dev_addr
== TW_CHIP_OFFSET_ADDR(1))
280 tbl_tw2864_common
[0xcf] = 0x83;
281 else if (dev_addr
== TW_CHIP_OFFSET_ADDR(2))
282 tbl_tw2864_common
[0xcf] = 0x83;
283 else if (dev_addr
== TW_CHIP_OFFSET_ADDR(3))
284 tbl_tw2864_common
[0xcf] = 0x80;
289 if (solo_dev
->video_type
== SOLO_VO_FMT_TYPE_PAL
) {
290 for (i
= 0; i
< 4; i
++) {
291 tbl_tw2864_common
[0x07 | (i
<< 4)] |= 0x10;
292 tbl_tw2864_common
[0x08 | (i
<< 4)] |= 0x06;
293 tbl_tw2864_common
[0x0a | (i
<< 4)] |= 0x08;
294 tbl_tw2864_common
[0x0b | (i
<< 4)] |= 0x13;
295 tbl_tw2864_common
[0x0e | (i
<< 4)] |= 0x01;
297 tbl_tw2864_common
[0x9d] = 0x90;
298 tbl_tw2864_common
[0xf3] = 0x00;
299 tbl_tw2864_common
[0xf4] = 0xa0;
302 for (i
= 0; i
< 0xff; i
++) {
303 /* Skip read only registers */
304 if (i
>= 0xb8 && i
<= 0xc1)
306 if ((i
& ~0x30) == 0x00 ||
307 (i
& ~0x30) == 0x0c ||
310 if (i
== 0x74 || i
== 0x77 || i
== 0x78 ||
311 i
== 0x79 || i
== 0x7a)
316 tw_write_and_verify(solo_dev
, dev_addr
, i
,
317 tbl_tw2864_common
[i
]);
323 static int tw2815_setup(struct solo_dev
*solo_dev
, u8 dev_addr
)
325 u8 tbl_ntsc_tw2815_common
[] = {
326 0x00, 0xc8, 0x20, 0xd0, 0x06, 0xf0, 0x08, 0x80,
327 0x80, 0x80, 0x80, 0x02, 0x06, 0x00, 0x11,
330 u8 tbl_pal_tw2815_common
[] = {
331 0x00, 0x88, 0x20, 0xd0, 0x05, 0x20, 0x28, 0x80,
332 0x80, 0x80, 0x80, 0x82, 0x06, 0x00, 0x11,
335 u8 tbl_tw2815_sfr
[] = {
336 0x00, 0x00, 0x00, 0xc0, 0x45, 0xa0, 0xd0, 0x2f, /* 0x00 */
337 0x64, 0x80, 0x80, 0x82, 0x82, 0x00, 0x00, 0x00,
338 0x00, 0x0f, 0x05, 0x00, 0x00, 0x80, 0x06, 0x00, /* 0x10 */
339 0x00, 0x00, 0x00, 0xff, 0x8f, 0x00, 0x00, 0x00,
340 0x88, 0x88, 0xc0, 0x00, 0x20, 0x64, 0xa8, 0xec, /* 0x20 */
341 0x31, 0x75, 0xb9, 0xfd, 0x00, 0x00, 0x88, 0x88,
342 0x88, 0x11, 0x00, 0x88, 0x88, 0x00, /* 0x30 */
344 u8
*tbl_tw2815_common
;
348 tbl_ntsc_tw2815_common
[0x06] = 0;
350 /* Horizontal Delay Control */
351 tbl_ntsc_tw2815_common
[0x02] = DEFAULT_HDELAY_NTSC
& 0xff;
352 tbl_ntsc_tw2815_common
[0x06] |= 0x03 & (DEFAULT_HDELAY_NTSC
>> 8);
354 /* Horizontal Active Control */
355 tbl_ntsc_tw2815_common
[0x03] = DEFAULT_HACTIVE_NTSC
& 0xff;
356 tbl_ntsc_tw2815_common
[0x06] |=
357 ((0x03 & (DEFAULT_HACTIVE_NTSC
>> 8)) << 2);
359 /* Vertical Delay Control */
360 tbl_ntsc_tw2815_common
[0x04] = DEFAULT_VDELAY_NTSC
& 0xff;
361 tbl_ntsc_tw2815_common
[0x06] |=
362 ((0x01 & (DEFAULT_VDELAY_NTSC
>> 8)) << 4);
364 /* Vertical Active Control */
365 tbl_ntsc_tw2815_common
[0x05] = DEFAULT_VACTIVE_NTSC
& 0xff;
366 tbl_ntsc_tw2815_common
[0x06] |=
367 ((0x01 & (DEFAULT_VACTIVE_NTSC
>> 8)) << 5);
369 tbl_pal_tw2815_common
[0x06] = 0;
371 /* Horizontal Delay Control */
372 tbl_pal_tw2815_common
[0x02] = DEFAULT_HDELAY_PAL
& 0xff;
373 tbl_pal_tw2815_common
[0x06] |= 0x03 & (DEFAULT_HDELAY_PAL
>> 8);
375 /* Horizontal Active Control */
376 tbl_pal_tw2815_common
[0x03] = DEFAULT_HACTIVE_PAL
& 0xff;
377 tbl_pal_tw2815_common
[0x06] |=
378 ((0x03 & (DEFAULT_HACTIVE_PAL
>> 8)) << 2);
380 /* Vertical Delay Control */
381 tbl_pal_tw2815_common
[0x04] = DEFAULT_VDELAY_PAL
& 0xff;
382 tbl_pal_tw2815_common
[0x06] |=
383 ((0x01 & (DEFAULT_VDELAY_PAL
>> 8)) << 4);
385 /* Vertical Active Control */
386 tbl_pal_tw2815_common
[0x05] = DEFAULT_VACTIVE_PAL
& 0xff;
387 tbl_pal_tw2815_common
[0x06] |=
388 ((0x01 & (DEFAULT_VACTIVE_PAL
>> 8)) << 5);
391 (solo_dev
->video_type
== SOLO_VO_FMT_TYPE_NTSC
) ?
392 tbl_ntsc_tw2815_common
: tbl_pal_tw2815_common
;
394 /* Dual ITU-R BT.656 format */
395 tbl_tw2815_common
[0x0d] |= 0x04;
397 /* Audio configuration */
398 tbl_tw2815_sfr
[0x62 - 0x40] &= ~(3 << 6);
400 if (solo_dev
->nr_chans
== 4) {
401 tbl_tw2815_sfr
[0x63 - 0x40] |= 1;
402 tbl_tw2815_sfr
[0x62 - 0x40] |= 3 << 6;
403 } else if (solo_dev
->nr_chans
== 8) {
404 tbl_tw2815_sfr
[0x63 - 0x40] |= 2;
405 if (dev_addr
== TW_CHIP_OFFSET_ADDR(0))
406 tbl_tw2815_sfr
[0x62 - 0x40] |= 1 << 6;
407 else if (dev_addr
== TW_CHIP_OFFSET_ADDR(1))
408 tbl_tw2815_sfr
[0x62 - 0x40] |= 2 << 6;
409 } else if (solo_dev
->nr_chans
== 16) {
410 tbl_tw2815_sfr
[0x63 - 0x40] |= 3;
411 if (dev_addr
== TW_CHIP_OFFSET_ADDR(0))
412 tbl_tw2815_sfr
[0x62 - 0x40] |= 1 << 6;
413 else if (dev_addr
== TW_CHIP_OFFSET_ADDR(1))
414 tbl_tw2815_sfr
[0x62 - 0x40] |= 0 << 6;
415 else if (dev_addr
== TW_CHIP_OFFSET_ADDR(2))
416 tbl_tw2815_sfr
[0x62 - 0x40] |= 0 << 6;
417 else if (dev_addr
== TW_CHIP_OFFSET_ADDR(3))
418 tbl_tw2815_sfr
[0x62 - 0x40] |= 2 << 6;
421 /* Output mode of R_ADATM pin (0 mixing, 1 record) */
422 /* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */
424 /* 8KHz, used to be 16KHz, but changed for remote client compat */
425 tbl_tw2815_sfr
[0x62 - 0x40] |= 0 << 2;
426 tbl_tw2815_sfr
[0x6c - 0x40] |= 0 << 2;
428 /* Playback of right channel */
429 tbl_tw2815_sfr
[0x6c - 0x40] |= 1 << 5;
431 /* Reserved value (XXX ??) */
432 tbl_tw2815_sfr
[0x5c - 0x40] |= 1 << 5;
434 /* Analog output gain and mix ratio playback on full */
435 tbl_tw2815_sfr
[0x70 - 0x40] |= 0xff;
436 /* Select playback audio and mute all except */
437 tbl_tw2815_sfr
[0x71 - 0x40] |= 0x10;
438 tbl_tw2815_sfr
[0x6d - 0x40] |= 0x0f;
440 /* End of audio configuration */
442 for (ch
= 0; ch
< 4; ch
++) {
443 tbl_tw2815_common
[0x0d] &= ~3;
446 tbl_tw2815_common
[0x0d] |= 0x21;
449 tbl_tw2815_common
[0x0d] |= 0x20;
452 tbl_tw2815_common
[0x0d] |= 0x23;
455 tbl_tw2815_common
[0x0d] |= 0x22;
459 for (i
= 0; i
< 0x0f; i
++) {
461 continue; /* read-only */
462 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
,
463 dev_addr
, (ch
* 0x10) + i
,
464 tbl_tw2815_common
[i
]);
468 for (i
= 0x40; i
< 0x76; i
++) {
469 /* Skip read-only and nop registers */
470 if (i
== 0x40 || i
== 0x59 || i
== 0x5a ||
471 i
== 0x5d || i
== 0x5e || i
== 0x5f)
474 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
, dev_addr
, i
,
475 tbl_tw2815_sfr
[i
- 0x40]);
481 #define FIRST_ACTIVE_LINE 0x0008
482 #define LAST_ACTIVE_LINE 0x0102
484 static void saa7128_setup(struct solo_dev
*solo_dev
)
487 unsigned char regs
[128] = {
488 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
489 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
490 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
491 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
492 0x1C, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00,
493 0x59, 0x1d, 0x75, 0x3f, 0x06, 0x3f, 0x00, 0x00,
494 0x1c, 0x33, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00,
495 0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
496 0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18,
497 0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f,
498 0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06,
499 0x02, 0x80, 0x71, 0x77, 0xa7, 0x67, 0x66, 0x2e,
500 0x7b, 0x11, 0x4f, 0x1f, 0x7c, 0xf0, 0x21, 0x77,
501 0x41, 0x88, 0x41, 0x12, 0xed, 0x10, 0x10, 0x00,
502 0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00,
503 0x00, 0x00, 0x08, 0xff, 0x80, 0x00, 0xff, 0xff,
506 regs
[0x7A] = FIRST_ACTIVE_LINE
& 0xff;
507 regs
[0x7B] = LAST_ACTIVE_LINE
& 0xff;
508 regs
[0x7C] = ((1 << 7) |
509 (((LAST_ACTIVE_LINE
>> 8) & 1) << 6) |
510 (((FIRST_ACTIVE_LINE
>> 8) & 1) << 4));
512 /* PAL: XXX: We could do a second set of regs to avoid this */
513 if (solo_dev
->video_type
!= SOLO_VO_FMT_TYPE_NTSC
) {
527 regs
[0x7A] = 0x06 + 12;
528 regs
[0x7b] = 0x24 + 12;
529 regs
[0x7c] |= 1 << 6;
532 /* First 0x25 bytes are read-only? */
533 for (i
= 0x26; i
< 128; i
++) {
534 if (i
== 0x60 || i
== 0x7D)
536 solo_i2c_writebyte(solo_dev
, SOLO_I2C_SAA
, 0x46, i
, regs
[i
]);
542 int solo_tw28_init(struct solo_dev
*solo_dev
)
547 /* Detect techwell chip type */
548 for (i
= 0; i
< TW_NUM_CHIP
; i
++) {
549 value
= solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
,
550 TW_CHIP_OFFSET_ADDR(i
), 0xFF);
552 switch (value
>> 3) {
554 solo_dev
->tw2865
|= 1 << i
;
555 solo_dev
->tw28_cnt
++;
558 solo_dev
->tw2864
|= 1 << i
;
559 solo_dev
->tw28_cnt
++;
562 value
= solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
,
563 TW_CHIP_OFFSET_ADDR(i
), 0x59);
564 if ((value
>> 3) == 0x04) {
565 solo_dev
->tw2815
|= 1 << i
;
566 solo_dev
->tw28_cnt
++;
571 if (!solo_dev
->tw28_cnt
)
574 saa7128_setup(solo_dev
);
576 for (i
= 0; i
< solo_dev
->tw28_cnt
; i
++) {
577 if ((solo_dev
->tw2865
& (1 << i
)))
578 tw2865_setup(solo_dev
, TW_CHIP_OFFSET_ADDR(i
));
579 else if ((solo_dev
->tw2864
& (1 << i
)))
580 tw2864_setup(solo_dev
, TW_CHIP_OFFSET_ADDR(i
));
582 tw2815_setup(solo_dev
, TW_CHIP_OFFSET_ADDR(i
));
585 dev_info(&solo_dev
->pdev
->dev
, "Initialized %d tw28xx chip%s:",
586 solo_dev
->tw28_cnt
, solo_dev
->tw28_cnt
== 1 ? "" : "s");
588 if (solo_dev
->tw2865
)
589 printk(" tw2865[%d]", hweight32(solo_dev
->tw2865
));
590 if (solo_dev
->tw2864
)
591 printk(" tw2864[%d]", hweight32(solo_dev
->tw2864
));
592 if (solo_dev
->tw2815
)
593 printk(" tw2815[%d]", hweight32(solo_dev
->tw2815
));
600 * We accessed the video status signal in the Techwell chip through
601 * iic/i2c because the video status reported by register REG_VI_STATUS1
602 * (address 0x012C) of the SOLO6010 chip doesn't give the correct video
603 * status signal values.
605 int tw28_get_video_status(struct solo_dev
*solo_dev
, u8 ch
)
609 /* Get the right chip and on-chip channel */
613 val
= tw_readbyte(solo_dev
, chip_num
, TW286X_AV_STAT_ADDR
,
614 TW_AV_STAT_ADDR
) & 0x0f;
616 return val
& (1 << ch
) ? 1 : 0;
620 /* Status of audio from up to 4 techwell chips are combined into 1 variable.
621 * See techwell datasheet for details. */
622 u16
tw28_get_audio_status(struct solo_dev
*solo_dev
)
628 for (i
= 0; i
< solo_dev
->tw28_cnt
; i
++) {
629 val
= (tw_readbyte(solo_dev
, i
, TW286X_AV_STAT_ADDR
,
630 TW_AV_STAT_ADDR
) & 0xf0) >> 4;
631 status
|= val
<< (i
* 4);
638 int tw28_set_ctrl_val(struct solo_dev
*solo_dev
, u32 ctrl
, u8 ch
, s32 val
)
643 /* Get the right chip and on-chip channel */
647 if (val
> 255 || val
< 0)
651 case V4L2_CID_SHARPNESS
:
652 /* Only 286x has sharpness */
653 if (val
> 0x0f || val
< 0)
655 if (is_tw286x(solo_dev
, chip_num
)) {
656 u8 v
= solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
,
657 TW_CHIP_OFFSET_ADDR(chip_num
),
658 TW286x_SHARPNESS(chip_num
));
661 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
,
662 TW_CHIP_OFFSET_ADDR(chip_num
),
663 TW286x_SHARPNESS(chip_num
), v
);
669 if (is_tw286x(solo_dev
, chip_num
))
673 tw_writebyte(solo_dev
, chip_num
, TW286x_HUE_ADDR(ch
),
674 TW_HUE_ADDR(ch
), sval
);
678 case V4L2_CID_SATURATION
:
679 if (is_tw286x(solo_dev
, chip_num
)) {
680 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
,
681 TW_CHIP_OFFSET_ADDR(chip_num
),
682 TW286x_SATURATIONU_ADDR(ch
), val
);
684 tw_writebyte(solo_dev
, chip_num
, TW286x_SATURATIONV_ADDR(ch
),
685 TW_SATURATION_ADDR(ch
), val
);
689 case V4L2_CID_CONTRAST
:
690 tw_writebyte(solo_dev
, chip_num
, TW286x_CONTRAST_ADDR(ch
),
691 TW_CONTRAST_ADDR(ch
), val
);
694 case V4L2_CID_BRIGHTNESS
:
695 if (is_tw286x(solo_dev
, chip_num
))
699 tw_writebyte(solo_dev
, chip_num
, TW286x_BRIGHTNESS_ADDR(ch
),
700 TW_BRIGHTNESS_ADDR(ch
), sval
);
710 int tw28_get_ctrl_val(struct solo_dev
*solo_dev
, u32 ctrl
, u8 ch
,
715 /* Get the right chip and on-chip channel */
720 case V4L2_CID_SHARPNESS
:
721 /* Only 286x has sharpness */
722 if (is_tw286x(solo_dev
, chip_num
)) {
723 rval
= solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
,
724 TW_CHIP_OFFSET_ADDR(chip_num
),
725 TW286x_SHARPNESS(chip_num
));
731 rval
= tw_readbyte(solo_dev
, chip_num
, TW286x_HUE_ADDR(ch
),
733 if (is_tw286x(solo_dev
, chip_num
))
734 *val
= (s32
)((char)rval
) + 128;
738 case V4L2_CID_SATURATION
:
739 *val
= tw_readbyte(solo_dev
, chip_num
,
740 TW286x_SATURATIONU_ADDR(ch
),
741 TW_SATURATION_ADDR(ch
));
743 case V4L2_CID_CONTRAST
:
744 *val
= tw_readbyte(solo_dev
, chip_num
,
745 TW286x_CONTRAST_ADDR(ch
),
746 TW_CONTRAST_ADDR(ch
));
748 case V4L2_CID_BRIGHTNESS
:
749 rval
= tw_readbyte(solo_dev
, chip_num
,
750 TW286x_BRIGHTNESS_ADDR(ch
),
751 TW_BRIGHTNESS_ADDR(ch
));
752 if (is_tw286x(solo_dev
, chip_num
))
753 *val
= (s32
)((char)rval
) + 128;
766 * For audio output volume, the output channel is only 1. In this case we
767 * don't need to offset TW_CHIP_OFFSET_ADDR. The TW_CHIP_OFFSET_ADDR used
768 * is the base address of the techwell chip.
770 void tw2815_Set_AudioOutVol(struct solo_dev
*solo_dev
, unsigned int u_val
)
773 unsigned int chip_num
;
775 chip_num
= (solo_dev
->nr_chans
- 1) / 4;
777 val
= tw_readbyte(solo_dev
, chip_num
, TW286x_AUDIO_OUTPUT_VOL_ADDR
,
778 TW_AUDIO_OUTPUT_VOL_ADDR
);
780 u_val
= (val
& 0x0f) | (u_val
<< 4);
782 tw_writebyte(solo_dev
, chip_num
, TW286x_AUDIO_OUTPUT_VOL_ADDR
,
783 TW_AUDIO_OUTPUT_VOL_ADDR
, u_val
);
787 u8
tw28_get_audio_gain(struct solo_dev
*solo_dev
, u8 ch
)
792 /* Get the right chip and on-chip channel */
796 val
= tw_readbyte(solo_dev
, chip_num
,
797 TW286x_AUDIO_INPUT_GAIN_ADDR(ch
),
798 TW_AUDIO_INPUT_GAIN_ADDR(ch
));
800 return (ch
% 2) ? (val
>> 4) : (val
& 0x0f);
803 void tw28_set_audio_gain(struct solo_dev
*solo_dev
, u8 ch
, u8 val
)
808 /* Get the right chip and on-chip channel */
812 old_val
= tw_readbyte(solo_dev
, chip_num
,
813 TW286x_AUDIO_INPUT_GAIN_ADDR(ch
),
814 TW_AUDIO_INPUT_GAIN_ADDR(ch
));
816 val
= (old_val
& ((ch
% 2) ? 0x0f : 0xf0)) |
817 ((ch
% 2) ? (val
<< 4) : val
);
819 tw_writebyte(solo_dev
, chip_num
, TW286x_AUDIO_INPUT_GAIN_ADDR(ch
),
820 TW_AUDIO_INPUT_GAIN_ADDR(ch
), val
);