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>
23 #include "solo6010-tw28.h"
25 /* XXX: Some of these values are masked into an 8-bit regs, and shifted
26 * around for other 8-bit regs. What are the magic bits in these values? */
27 #define DEFAULT_HDELAY_NTSC (32 - 4)
28 #define DEFAULT_HACTIVE_NTSC (720 + 16)
29 #define DEFAULT_VDELAY_NTSC (7 - 2)
30 #define DEFAULT_VACTIVE_NTSC (240 + 4)
32 #define DEFAULT_HDELAY_PAL (32 + 4)
33 #define DEFAULT_HACTIVE_PAL (864-DEFAULT_HDELAY_PAL)
34 #define DEFAULT_VDELAY_PAL (6)
35 #define DEFAULT_VACTIVE_PAL (312-DEFAULT_VDELAY_PAL)
37 static u8 tbl_tw2864_template
[] = {
38 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x00 */
39 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
40 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x10 */
41 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
42 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x20 */
43 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
44 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x30 */
45 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
46 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */
47 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
48 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
49 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
50 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
51 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
52 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */
53 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00,
54 0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
55 0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00,
56 0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */
57 0x00, 0x28, 0x44, 0x44, 0xa0, 0x88, 0x5a, 0x01,
58 0x08, 0x08, 0x08, 0x08, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */
59 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44,
60 0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */
61 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
62 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
63 0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00,
64 0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */
65 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81,
66 0x10, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
67 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
68 0x83, 0xb5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */
69 0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00,
72 static u8 tbl_tw2865_ntsc_template
[] = {
73 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */
74 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
75 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */
76 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
77 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */
78 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
79 0x00, 0xf0, 0x70, 0x48, 0x80, 0x80, 0x00, 0x02, /* 0x30 */
80 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
81 0x00, 0x00, 0x90, 0x68, 0x00, 0x38, 0x80, 0x80, /* 0x40 */
82 0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00,
83 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
84 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
85 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
86 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43,
87 0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */
88 0xE9, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80,
89 0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
90 0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00,
91 0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */
92 0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13,
93 0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1B, 0x1A, /* 0xa0 */
94 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44,
95 0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */
96 0xFF, 0xE7, 0xE9, 0xE9, 0xEB, 0xFF, 0xD6, 0xD8,
97 0xD8, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
98 0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80,
99 0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */
100 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81,
101 0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
102 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
103 0x83, 0xB5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */
104 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0,
107 static u8 tbl_tw2865_pal_template
[] = {
108 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */
109 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
110 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */
111 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
112 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */
113 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
114 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */
115 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
116 0x00, 0x94, 0x90, 0x48, 0x00, 0x38, 0x7F, 0x80, /* 0x40 */
117 0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00,
118 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
119 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
120 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
121 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43,
122 0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */
123 0xEA, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80,
124 0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
125 0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00,
126 0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */
127 0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13,
128 0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1A, 0x1A, /* 0xa0 */
129 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44,
130 0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */
131 0xFF, 0xE7, 0xE9, 0xE9, 0xE9, 0xFF, 0xD7, 0xD8,
132 0xD9, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
133 0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80,
134 0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */
135 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81,
136 0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
137 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
138 0x83, 0xB5, 0x09, 0x00, 0xA0, 0x00, 0x01, 0x20, /* 0xf0 */
139 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0,
142 #define is_tw286x(__solo, __id) (!(__solo->tw2815 & (1 << __id)))
144 static u8
tw_readbyte(struct solo6010_dev
*solo_dev
, int chip_id
, u8 tw6x_off
,
147 if (is_tw286x(solo_dev
, chip_id
))
148 return solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
,
149 TW_CHIP_OFFSET_ADDR(chip_id
),
152 return solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
,
153 TW_CHIP_OFFSET_ADDR(chip_id
),
157 static void tw_writebyte(struct solo6010_dev
*solo_dev
, int chip_id
,
158 u8 tw6x_off
, u8 tw_off
, u8 val
)
160 if (is_tw286x(solo_dev
, chip_id
))
161 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
,
162 TW_CHIP_OFFSET_ADDR(chip_id
),
165 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
,
166 TW_CHIP_OFFSET_ADDR(chip_id
),
170 static void tw_write_and_verify(struct solo6010_dev
*solo_dev
, u8 addr
, u8 off
,
175 for (i
= 0; i
< 5; i
++) {
176 u8 rval
= solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
, addr
, off
);
180 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
, addr
, off
, val
);
181 msleep_interruptible(1);
184 /* printk("solo6010/tw28: Error writing register: %02x->%02x [%02x]\n",
188 static int tw2865_setup(struct solo6010_dev
*solo_dev
, u8 dev_addr
)
190 u8 tbl_tw2865_common
[256];
193 if (solo_dev
->video_type
== SOLO_VO_FMT_TYPE_PAL
)
194 memcpy(tbl_tw2865_common
, tbl_tw2865_pal_template
,
195 sizeof(tbl_tw2865_common
));
197 memcpy(tbl_tw2865_common
, tbl_tw2865_ntsc_template
,
198 sizeof(tbl_tw2865_common
));
201 if (solo_dev
->nr_chans
== 4) {
202 tbl_tw2865_common
[0xd2] = 0x01;
203 tbl_tw2865_common
[0xcf] = 0x00;
204 } else if (solo_dev
->nr_chans
== 8) {
205 tbl_tw2865_common
[0xd2] = 0x02;
206 if (dev_addr
== TW_CHIP_OFFSET_ADDR(1))
207 tbl_tw2865_common
[0xcf] = 0x80;
208 } else if (solo_dev
->nr_chans
== 16) {
209 tbl_tw2865_common
[0xd2] = 0x03;
210 if (dev_addr
== TW_CHIP_OFFSET_ADDR(1))
211 tbl_tw2865_common
[0xcf] = 0x83;
212 else if (dev_addr
== TW_CHIP_OFFSET_ADDR(2))
213 tbl_tw2865_common
[0xcf] = 0x83;
214 else if (dev_addr
== TW_CHIP_OFFSET_ADDR(3))
215 tbl_tw2865_common
[0xcf] = 0x80;
218 for (i
= 0; i
< 0xff; i
++) {
219 /* Skip read only registers */
220 if (i
>= 0xb8 && i
<= 0xc1)
222 if ((i
& ~0x30) == 0x00 ||
223 (i
& ~0x30) == 0x0c ||
226 if (i
>= 0xc4 && i
<= 0xc7)
231 tw_write_and_verify(solo_dev
, dev_addr
, i
,
232 tbl_tw2865_common
[i
]);
238 static int tw2864_setup(struct solo6010_dev
*solo_dev
, u8 dev_addr
)
240 u8 tbl_tw2864_common
[sizeof(tbl_tw2864_template
)];
243 memcpy(tbl_tw2864_common
, tbl_tw2864_template
,
244 sizeof(tbl_tw2864_common
));
246 if (solo_dev
->tw2865
== 0) {
248 if (solo_dev
->nr_chans
== 4) {
249 tbl_tw2864_common
[0xd2] = 0x01;
250 tbl_tw2864_common
[0xcf] = 0x00;
251 } else if (solo_dev
->nr_chans
== 8) {
252 tbl_tw2864_common
[0xd2] = 0x02;
253 if (dev_addr
== TW_CHIP_OFFSET_ADDR(0))
254 tbl_tw2864_common
[0xcf] = 0x43;
255 else if (dev_addr
== TW_CHIP_OFFSET_ADDR(1))
256 tbl_tw2864_common
[0xcf] = 0x40;
257 } else if (solo_dev
->nr_chans
== 16) {
258 tbl_tw2864_common
[0xd2] = 0x03;
259 if (dev_addr
== TW_CHIP_OFFSET_ADDR(0))
260 tbl_tw2864_common
[0xcf] = 0x43;
261 else if (dev_addr
== TW_CHIP_OFFSET_ADDR(1))
262 tbl_tw2864_common
[0xcf] = 0x43;
263 else if (dev_addr
== TW_CHIP_OFFSET_ADDR(2))
264 tbl_tw2864_common
[0xcf] = 0x43;
265 else if (dev_addr
== TW_CHIP_OFFSET_ADDR(3))
266 tbl_tw2864_common
[0xcf] = 0x40;
269 /* ALINK Mode. Assumes that the first tw28xx is a
270 * 2865 and these are in cascade. */
271 for (i
= 0; i
<= 4; i
++)
272 tbl_tw2864_common
[0x08 | i
<< 4] = 0x12;
274 if (solo_dev
->nr_chans
== 8) {
275 tbl_tw2864_common
[0xd2] = 0x02;
276 if (dev_addr
== TW_CHIP_OFFSET_ADDR(1))
277 tbl_tw2864_common
[0xcf] = 0x80;
278 } else if (solo_dev
->nr_chans
== 16) {
279 tbl_tw2864_common
[0xd2] = 0x03;
280 if (dev_addr
== TW_CHIP_OFFSET_ADDR(1))
281 tbl_tw2864_common
[0xcf] = 0x83;
282 else if (dev_addr
== TW_CHIP_OFFSET_ADDR(2))
283 tbl_tw2864_common
[0xcf] = 0x83;
284 else if (dev_addr
== TW_CHIP_OFFSET_ADDR(3))
285 tbl_tw2864_common
[0xcf] = 0x80;
290 if (solo_dev
->video_type
== SOLO_VO_FMT_TYPE_PAL
) {
291 for (i
= 0; i
< 4; i
++) {
292 tbl_tw2864_common
[0x07 | (i
<< 4)] |= 0x10;
293 tbl_tw2864_common
[0x08 | (i
<< 4)] |= 0x06;
294 tbl_tw2864_common
[0x0a | (i
<< 4)] |= 0x08;
295 tbl_tw2864_common
[0x0b | (i
<< 4)] |= 0x13;
296 tbl_tw2864_common
[0x0e | (i
<< 4)] |= 0x01;
298 tbl_tw2864_common
[0x9d] = 0x90;
299 tbl_tw2864_common
[0xf3] = 0x00;
300 tbl_tw2864_common
[0xf4] = 0xa0;
303 for (i
= 0; i
< 0xff; i
++) {
304 /* Skip read only registers */
305 if (i
>= 0xb8 && i
<= 0xc1)
307 if ((i
& ~0x30) == 0x00 ||
308 (i
& ~0x30) == 0x0c ||
311 if (i
== 0x74 || i
== 0x77 || i
== 0x78 ||
312 i
== 0x79 || i
== 0x7a)
317 tw_write_and_verify(solo_dev
, dev_addr
, i
,
318 tbl_tw2864_common
[i
]);
324 static int tw2815_setup(struct solo6010_dev
*solo_dev
, u8 dev_addr
)
326 u8 tbl_ntsc_tw2815_common
[] = {
327 0x00, 0xc8, 0x20, 0xd0, 0x06, 0xf0, 0x08, 0x80,
328 0x80, 0x80, 0x80, 0x02, 0x06, 0x00, 0x11,
331 u8 tbl_pal_tw2815_common
[] = {
332 0x00, 0x88, 0x20, 0xd0, 0x05, 0x20, 0x28, 0x80,
333 0x80, 0x80, 0x80, 0x82, 0x06, 0x00, 0x11,
336 u8 tbl_tw2815_sfr
[] = {
337 0x00, 0x00, 0x00, 0xc0, 0x45, 0xa0, 0xd0, 0x2f, /* 0x00 */
338 0x64, 0x80, 0x80, 0x82, 0x82, 0x00, 0x00, 0x00,
339 0x00, 0x0f, 0x05, 0x00, 0x00, 0x80, 0x06, 0x00, /* 0x10 */
340 0x00, 0x00, 0x00, 0xff, 0x8f, 0x00, 0x00, 0x00,
341 0x88, 0x88, 0xc0, 0x00, 0x20, 0x64, 0xa8, 0xec, /* 0x20 */
342 0x31, 0x75, 0xb9, 0xfd, 0x00, 0x00, 0x88, 0x88,
343 0x88, 0x11, 0x00, 0x88, 0x88, 0x00, /* 0x30 */
345 u8
*tbl_tw2815_common
;
349 tbl_ntsc_tw2815_common
[0x06] = 0;
351 /* Horizontal Delay Control */
352 tbl_ntsc_tw2815_common
[0x02] = DEFAULT_HDELAY_NTSC
& 0xff;
353 tbl_ntsc_tw2815_common
[0x06] |= 0x03 & (DEFAULT_HDELAY_NTSC
>> 8);
355 /* Horizontal Active Control */
356 tbl_ntsc_tw2815_common
[0x03] = DEFAULT_HACTIVE_NTSC
& 0xff;
357 tbl_ntsc_tw2815_common
[0x06] |=
358 ((0x03 & (DEFAULT_HACTIVE_NTSC
>> 8)) << 2);
360 /* Vertical Delay Control */
361 tbl_ntsc_tw2815_common
[0x04] = DEFAULT_VDELAY_NTSC
& 0xff;
362 tbl_ntsc_tw2815_common
[0x06] |=
363 ((0x01 & (DEFAULT_VDELAY_NTSC
>> 8)) << 4);
365 /* Vertical Active Control */
366 tbl_ntsc_tw2815_common
[0x05] = DEFAULT_VACTIVE_NTSC
& 0xff;
367 tbl_ntsc_tw2815_common
[0x06] |=
368 ((0x01 & (DEFAULT_VACTIVE_NTSC
>> 8)) << 5);
370 tbl_pal_tw2815_common
[0x06] = 0;
372 /* Horizontal Delay Control */
373 tbl_pal_tw2815_common
[0x02] = DEFAULT_HDELAY_PAL
& 0xff;
374 tbl_pal_tw2815_common
[0x06] |= 0x03 & (DEFAULT_HDELAY_PAL
>> 8);
376 /* Horizontal Active Control */
377 tbl_pal_tw2815_common
[0x03] = DEFAULT_HACTIVE_PAL
& 0xff;
378 tbl_pal_tw2815_common
[0x06] |=
379 ((0x03 & (DEFAULT_HACTIVE_PAL
>> 8)) << 2);
381 /* Vertical Delay Control */
382 tbl_pal_tw2815_common
[0x04] = DEFAULT_VDELAY_PAL
& 0xff;
383 tbl_pal_tw2815_common
[0x06] |=
384 ((0x01 & (DEFAULT_VDELAY_PAL
>> 8)) << 4);
386 /* Vertical Active Control */
387 tbl_pal_tw2815_common
[0x05] = DEFAULT_VACTIVE_PAL
& 0xff;
388 tbl_pal_tw2815_common
[0x06] |=
389 ((0x01 & (DEFAULT_VACTIVE_PAL
>> 8)) << 5);
392 (solo_dev
->video_type
== SOLO_VO_FMT_TYPE_NTSC
) ?
393 tbl_ntsc_tw2815_common
: tbl_pal_tw2815_common
;
395 /* Dual ITU-R BT.656 format */
396 tbl_tw2815_common
[0x0d] |= 0x04;
398 /* Audio configuration */
399 tbl_tw2815_sfr
[0x62 - 0x40] &= ~(3 << 6);
401 if (solo_dev
->nr_chans
== 4) {
402 tbl_tw2815_sfr
[0x63 - 0x40] |= 1;
403 tbl_tw2815_sfr
[0x62 - 0x40] |= 3 << 6;
404 } else if (solo_dev
->nr_chans
== 8) {
405 tbl_tw2815_sfr
[0x63 - 0x40] |= 2;
406 if (dev_addr
== TW_CHIP_OFFSET_ADDR(0))
407 tbl_tw2815_sfr
[0x62 - 0x40] |= 1 << 6;
408 else if (dev_addr
== TW_CHIP_OFFSET_ADDR(1))
409 tbl_tw2815_sfr
[0x62 - 0x40] |= 2 << 6;
410 } else if (solo_dev
->nr_chans
== 16) {
411 tbl_tw2815_sfr
[0x63 - 0x40] |= 3;
412 if (dev_addr
== TW_CHIP_OFFSET_ADDR(0))
413 tbl_tw2815_sfr
[0x62 - 0x40] |= 1 << 6;
414 else if (dev_addr
== TW_CHIP_OFFSET_ADDR(1))
415 tbl_tw2815_sfr
[0x62 - 0x40] |= 0 << 6;
416 else if (dev_addr
== TW_CHIP_OFFSET_ADDR(2))
417 tbl_tw2815_sfr
[0x62 - 0x40] |= 0 << 6;
418 else if (dev_addr
== TW_CHIP_OFFSET_ADDR(3))
419 tbl_tw2815_sfr
[0x62 - 0x40] |= 2 << 6;
422 /* Output mode of R_ADATM pin (0 mixing, 1 record) */
423 /* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */
425 /* 8KHz, used to be 16KHz, but changed for remote client compat */
426 tbl_tw2815_sfr
[0x62 - 0x40] |= 0 << 2;
427 tbl_tw2815_sfr
[0x6c - 0x40] |= 0 << 2;
429 /* Playback of right channel */
430 tbl_tw2815_sfr
[0x6c - 0x40] |= 1 << 5;
432 /* Reserved value (XXX ??) */
433 tbl_tw2815_sfr
[0x5c - 0x40] |= 1 << 5;
435 /* Analog output gain and mix ratio playback on full */
436 tbl_tw2815_sfr
[0x70 - 0x40] |= 0xff;
437 /* Select playback audio and mute all except */
438 tbl_tw2815_sfr
[0x71 - 0x40] |= 0x10;
439 tbl_tw2815_sfr
[0x6d - 0x40] |= 0x0f;
441 /* End of audio configuration */
443 for (ch
= 0; ch
< 4; ch
++) {
444 tbl_tw2815_common
[0x0d] &= ~3;
447 tbl_tw2815_common
[0x0d] |= 0x21;
450 tbl_tw2815_common
[0x0d] |= 0x20;
453 tbl_tw2815_common
[0x0d] |= 0x23;
456 tbl_tw2815_common
[0x0d] |= 0x22;
460 for (i
= 0; i
< 0x0f; i
++) {
462 continue; /* read-only */
463 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
,
464 dev_addr
, (ch
* 0x10) + i
,
465 tbl_tw2815_common
[i
]);
469 for (i
= 0x40; i
< 0x76; i
++) {
470 /* Skip read-only and nop registers */
471 if (i
== 0x40 || i
== 0x59 || i
== 0x5a ||
472 i
== 0x5d || i
== 0x5e || i
== 0x5f)
475 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
, dev_addr
, i
,
476 tbl_tw2815_sfr
[i
- 0x40]);
482 #define FIRST_ACTIVE_LINE 0x0008
483 #define LAST_ACTIVE_LINE 0x0102
485 static void saa7128_setup(struct solo6010_dev
*solo_dev
)
488 unsigned char regs
[128] = {
489 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
490 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
491 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
492 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
493 0x1C, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00,
494 0x59, 0x1d, 0x75, 0x3f, 0x06, 0x3f, 0x00, 0x00,
495 0x1c, 0x33, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00,
496 0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
497 0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18,
498 0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f,
499 0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06,
500 0x02, 0x80, 0x71, 0x77, 0xa7, 0x67, 0x66, 0x2e,
501 0x7b, 0x11, 0x4f, 0x1f, 0x7c, 0xf0, 0x21, 0x77,
502 0x41, 0x88, 0x41, 0x12, 0xed, 0x10, 0x10, 0x00,
503 0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00,
504 0x00, 0x00, 0x08, 0xff, 0x80, 0x00, 0xff, 0xff,
507 regs
[0x7A] = FIRST_ACTIVE_LINE
& 0xff;
508 regs
[0x7B] = LAST_ACTIVE_LINE
& 0xff;
509 regs
[0x7C] = ((1 << 7) |
510 (((LAST_ACTIVE_LINE
>> 8) & 1) << 6) |
511 (((FIRST_ACTIVE_LINE
>> 8) & 1) << 4));
513 /* PAL: XXX: We could do a second set of regs to avoid this */
514 if (solo_dev
->video_type
!= SOLO_VO_FMT_TYPE_NTSC
) {
528 regs
[0x7A] = 0x06 + 12;
529 regs
[0x7b] = 0x24 + 12;
530 regs
[0x7c] |= 1 << 6;
533 /* First 0x25 bytes are read-only? */
534 for (i
= 0x26; i
< 128; i
++) {
535 if (i
== 0x60 || i
== 0x7D)
537 solo_i2c_writebyte(solo_dev
, SOLO_I2C_SAA
, 0x46, i
, regs
[i
]);
543 int solo_tw28_init(struct solo6010_dev
*solo_dev
)
548 /* Detect techwell chip type */
549 for (i
= 0; i
< TW_NUM_CHIP
; i
++) {
550 value
= solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
,
551 TW_CHIP_OFFSET_ADDR(i
), 0xFF);
553 switch (value
>> 3) {
555 solo_dev
->tw2865
|= 1 << i
;
556 solo_dev
->tw28_cnt
++;
559 solo_dev
->tw2864
|= 1 << i
;
560 solo_dev
->tw28_cnt
++;
563 value
= solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
,
564 TW_CHIP_OFFSET_ADDR(i
), 0x59);
565 if ((value
>> 3) == 0x04) {
566 solo_dev
->tw2815
|= 1 << i
;
567 solo_dev
->tw28_cnt
++;
572 if (!solo_dev
->tw28_cnt
)
575 saa7128_setup(solo_dev
);
577 for (i
= 0; i
< solo_dev
->tw28_cnt
; i
++) {
578 if ((solo_dev
->tw2865
& (1 << i
)))
579 tw2865_setup(solo_dev
, TW_CHIP_OFFSET_ADDR(i
));
580 else if ((solo_dev
->tw2864
& (1 << i
)))
581 tw2864_setup(solo_dev
, TW_CHIP_OFFSET_ADDR(i
));
583 tw2815_setup(solo_dev
, TW_CHIP_OFFSET_ADDR(i
));
586 dev_info(&solo_dev
->pdev
->dev
, "Initialized %d tw28xx chip%s:",
587 solo_dev
->tw28_cnt
, solo_dev
->tw28_cnt
== 1 ? "" : "s");
589 if (solo_dev
->tw2865
)
590 printk(" tw2865[%d]", hweight32(solo_dev
->tw2865
));
591 if (solo_dev
->tw2864
)
592 printk(" tw2864[%d]", hweight32(solo_dev
->tw2864
));
593 if (solo_dev
->tw2815
)
594 printk(" tw2815[%d]", hweight32(solo_dev
->tw2815
));
601 * We accessed the video status signal in the Techwell chip through
602 * iic/i2c because the video status reported by register REG_VI_STATUS1
603 * (address 0x012C) of the SOLO6010 chip doesn't give the correct video
604 * status signal values.
606 int tw28_get_video_status(struct solo6010_dev
*solo_dev
, u8 ch
)
610 /* Get the right chip and on-chip channel */
614 val
= tw_readbyte(solo_dev
, chip_num
, TW286X_AV_STAT_ADDR
,
615 TW_AV_STAT_ADDR
) & 0x0f;
617 return val
& (1 << ch
) ? 1 : 0;
621 /* Status of audio from up to 4 techwell chips are combined into 1 variable.
622 * See techwell datasheet for details. */
623 u16
tw28_get_audio_status(struct solo6010_dev
*solo_dev
)
629 for (i
= 0; i
< solo_dev
->tw28_cnt
; i
++) {
630 val
= (tw_readbyte(solo_dev
, i
, TW286X_AV_STAT_ADDR
,
631 TW_AV_STAT_ADDR
) & 0xf0) >> 4;
632 status
|= val
<< (i
* 4);
639 int tw28_set_ctrl_val(struct solo6010_dev
*solo_dev
, u32 ctrl
, u8 ch
,
645 /* Get the right chip and on-chip channel */
649 if (val
> 255 || val
< 0)
653 case V4L2_CID_SHARPNESS
:
654 /* Only 286x has sharpness */
655 if (val
> 0x0f || val
< 0)
657 if (is_tw286x(solo_dev
, chip_num
)) {
658 u8 v
= solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
,
659 TW_CHIP_OFFSET_ADDR(chip_num
),
660 TW286x_SHARPNESS(chip_num
));
663 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
,
664 TW_CHIP_OFFSET_ADDR(chip_num
),
665 TW286x_SHARPNESS(chip_num
), v
);
671 if (is_tw286x(solo_dev
, chip_num
))
675 tw_writebyte(solo_dev
, chip_num
, TW286x_HUE_ADDR(ch
),
676 TW_HUE_ADDR(ch
), sval
);
680 case V4L2_CID_SATURATION
:
681 if (is_tw286x(solo_dev
, chip_num
)) {
682 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
,
683 TW_CHIP_OFFSET_ADDR(chip_num
),
684 TW286x_SATURATIONU_ADDR(ch
), val
);
686 tw_writebyte(solo_dev
, chip_num
, TW286x_SATURATIONV_ADDR(ch
),
687 TW_SATURATION_ADDR(ch
), val
);
691 case V4L2_CID_CONTRAST
:
692 tw_writebyte(solo_dev
, chip_num
, TW286x_CONTRAST_ADDR(ch
),
693 TW_CONTRAST_ADDR(ch
), val
);
696 case V4L2_CID_BRIGHTNESS
:
697 if (is_tw286x(solo_dev
, chip_num
))
701 tw_writebyte(solo_dev
, chip_num
, TW286x_BRIGHTNESS_ADDR(ch
),
702 TW_BRIGHTNESS_ADDR(ch
), sval
);
712 int tw28_get_ctrl_val(struct solo6010_dev
*solo_dev
, u32 ctrl
, u8 ch
,
717 /* Get the right chip and on-chip channel */
722 case V4L2_CID_SHARPNESS
:
723 /* Only 286x has sharpness */
724 if (is_tw286x(solo_dev
, chip_num
)) {
725 rval
= solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
,
726 TW_CHIP_OFFSET_ADDR(chip_num
),
727 TW286x_SHARPNESS(chip_num
));
733 rval
= tw_readbyte(solo_dev
, chip_num
, TW286x_HUE_ADDR(ch
),
735 if (is_tw286x(solo_dev
, chip_num
))
736 *val
= (s32
)((char)rval
) + 128;
740 case V4L2_CID_SATURATION
:
741 *val
= tw_readbyte(solo_dev
, chip_num
,
742 TW286x_SATURATIONU_ADDR(ch
),
743 TW_SATURATION_ADDR(ch
));
745 case V4L2_CID_CONTRAST
:
746 *val
= tw_readbyte(solo_dev
, chip_num
,
747 TW286x_CONTRAST_ADDR(ch
),
748 TW_CONTRAST_ADDR(ch
));
750 case V4L2_CID_BRIGHTNESS
:
751 rval
= tw_readbyte(solo_dev
, chip_num
,
752 TW286x_BRIGHTNESS_ADDR(ch
),
753 TW_BRIGHTNESS_ADDR(ch
));
754 if (is_tw286x(solo_dev
, chip_num
))
755 *val
= (s32
)((char)rval
) + 128;
768 * For audio output volume, the output channel is only 1. In this case we
769 * don't need to offset TW_CHIP_OFFSET_ADDR. The TW_CHIP_OFFSET_ADDR used
770 * is the base address of the techwell chip.
772 void tw2815_Set_AudioOutVol(struct solo6010_dev
*solo_dev
, unsigned int u_val
)
775 unsigned int chip_num
;
777 chip_num
= (solo_dev
->nr_chans
- 1) / 4;
779 val
= tw_readbyte(solo_dev
, chip_num
, TW286x_AUDIO_OUTPUT_VOL_ADDR
,
780 TW_AUDIO_OUTPUT_VOL_ADDR
);
782 u_val
= (val
& 0x0f) | (u_val
<< 4);
784 tw_writebyte(solo_dev
, chip_num
, TW286x_AUDIO_OUTPUT_VOL_ADDR
,
785 TW_AUDIO_OUTPUT_VOL_ADDR
, u_val
);
789 u8
tw28_get_audio_gain(struct solo6010_dev
*solo_dev
, u8 ch
)
794 /* Get the right chip and on-chip channel */
798 val
= tw_readbyte(solo_dev
, chip_num
,
799 TW286x_AUDIO_INPUT_GAIN_ADDR(ch
),
800 TW_AUDIO_INPUT_GAIN_ADDR(ch
));
802 return (ch
% 2) ? (val
>> 4) : (val
& 0x0f);
805 void tw28_set_audio_gain(struct solo6010_dev
*solo_dev
, u8 ch
, u8 val
)
810 /* Get the right chip and on-chip channel */
814 old_val
= tw_readbyte(solo_dev
, chip_num
,
815 TW286x_AUDIO_INPUT_GAIN_ADDR(ch
),
816 TW_AUDIO_INPUT_GAIN_ADDR(ch
));
818 val
= (old_val
& ((ch
% 2) ? 0x0f : 0xf0)) |
819 ((ch
% 2) ? (val
<< 4) : val
);
821 tw_writebyte(solo_dev
, chip_num
, TW286x_AUDIO_INPUT_GAIN_ADDR(ch
),
822 TW_AUDIO_INPUT_GAIN_ADDR(ch
), val
);