ALSA: oxygen: do not show chip revision in card longname
[firewire-audio.git] / drivers / staging / solo6x10 / solo6010-tw28.c
blob0159c8392436b85705487c13f2adb9c2dd3e866f
1 /*
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>
22 #include "solo6010.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,
145 u8 tw_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),
150 tw6x_off);
151 else
152 return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
153 TW_CHIP_OFFSET_ADDR(chip_id),
154 tw_off);
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),
163 tw6x_off, val);
164 else
165 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
166 TW_CHIP_OFFSET_ADDR(chip_id),
167 tw_off, val);
170 static void tw_write_and_verify(struct solo6010_dev *solo_dev, u8 addr, u8 off,
171 u8 val)
173 int i;
175 for (i = 0; i < 5; i++) {
176 u8 rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, addr, off);
177 if (rval == val)
178 return;
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",
185 // addr, off, val);
188 static int tw2865_setup(struct solo6010_dev *solo_dev, u8 dev_addr)
190 u8 tbl_tw2865_common[256];
191 int i;
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));
196 else
197 memcpy(tbl_tw2865_common, tbl_tw2865_ntsc_template,
198 sizeof(tbl_tw2865_common));
200 /* ALINK Mode */
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 )
221 continue;
222 if ((i & ~0x30) == 0x00 ||
223 (i & ~0x30) == 0x0c ||
224 (i & ~0x30) == 0x0d)
225 continue;
226 if (i >= 0xc4 && i <= 0xc7)
227 continue;
228 if (i == 0xfd)
229 continue;
231 tw_write_and_verify(solo_dev, dev_addr, i,
232 tbl_tw2865_common[i]);
235 return 0;
238 static int tw2864_setup(struct solo6010_dev *solo_dev, u8 dev_addr)
240 u8 tbl_tw2864_common[sizeof(tbl_tw2864_template)];
241 int i;
243 memcpy(tbl_tw2864_common, tbl_tw2864_template,
244 sizeof(tbl_tw2864_common));
246 if (solo_dev->tw2865 == 0) {
247 /* IRQ Mode */
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;
268 } else {
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;
289 /* NTSC or PAL */
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 )
306 continue;
307 if ((i & ~0x30) == 0x00 ||
308 (i & ~0x30) == 0x0c ||
309 (i & ~0x30) == 0x0d)
310 continue;
311 if (i == 0x74 || i == 0x77 || i == 0x78 ||
312 i == 0x79 || i == 0x7a)
313 continue;
314 if (i == 0xfd)
315 continue;
317 tw_write_and_verify(solo_dev, dev_addr, i,
318 tbl_tw2864_common[i]);
321 return 0;
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;
346 int i;
347 int ch;
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);
391 tbl_tw2815_common =
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;
445 switch (ch) {
446 case 0:
447 tbl_tw2815_common[0x0d] |= 0x21;
448 break;
449 case 1:
450 tbl_tw2815_common[0x0d] |= 0x20;
451 break;
452 case 2:
453 tbl_tw2815_common[0x0d] |= 0x23;
454 break;
455 case 3:
456 tbl_tw2815_common[0x0d] |= 0x22;
457 break;
460 for (i = 0; i < 0x0f; i++) {
461 if (i == 0x00)
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)
473 continue;
475 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, dev_addr, i,
476 tbl_tw2815_sfr[i - 0x40]);
479 return 0;
482 #define FIRST_ACTIVE_LINE 0x0008
483 #define LAST_ACTIVE_LINE 0x0102
485 static void saa7128_setup(struct solo6010_dev *solo_dev)
487 int i;
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) {
515 regs[0x28] = 0xE1;
517 regs[0x5A] = 0x0F;
518 regs[0x61] = 0x02;
519 regs[0x62] = 0x35;
520 regs[0x63] = 0xCB;
521 regs[0x64] = 0x8A;
522 regs[0x65] = 0x09;
523 regs[0x66] = 0x2A;
525 regs[0x6C] = 0xf1;
526 regs[0x6E] = 0x20;
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)
536 continue;
537 solo_i2c_writebyte(solo_dev, SOLO_I2C_SAA, 0x46, i, regs[i]);
540 return;
543 int solo_tw28_init(struct solo6010_dev *solo_dev)
545 int i;
546 u8 value;
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) {
554 case 0x18:
555 solo_dev->tw2865 |= 1 << i;
556 solo_dev->tw28_cnt++;
557 break;
558 case 0x0c:
559 solo_dev->tw2864 |= 1 << i;
560 solo_dev->tw28_cnt++;
561 break;
562 default:
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)
573 return -EINVAL;
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));
582 else
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));
595 printk("\n");
597 return 0;
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)
608 u8 val, chip_num;
610 /* Get the right chip and on-chip channel */
611 chip_num = ch / 4;
612 ch %= 4;
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;
620 #if 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)
625 u8 val;
626 u16 status = 0;
627 int i;
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);
635 return status;
637 #endif
639 int tw28_set_ctrl_val(struct solo6010_dev *solo_dev, u32 ctrl, u8 ch,
640 s32 val)
642 char sval;
643 u8 chip_num;
645 /* Get the right chip and on-chip channel */
646 chip_num = ch / 4;
647 ch %= 4;
649 if (val > 255 || val < 0)
650 return -ERANGE;
652 switch (ctrl) {
653 case V4L2_CID_SHARPNESS:
654 /* Only 286x has sharpness */
655 if (val > 0x0f || val < 0)
656 return -ERANGE;
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));
661 v &= 0xf0;
662 v |= val;
663 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
664 TW_CHIP_OFFSET_ADDR(chip_num),
665 TW286x_SHARPNESS(chip_num), v);
666 } else if (val != 0)
667 return -ERANGE;
668 break;
670 case V4L2_CID_HUE:
671 if (is_tw286x(solo_dev, chip_num))
672 sval = val - 128;
673 else
674 sval = (char)val;
675 tw_writebyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
676 TW_HUE_ADDR(ch), sval);
678 break;
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);
689 break;
691 case V4L2_CID_CONTRAST:
692 tw_writebyte(solo_dev, chip_num, TW286x_CONTRAST_ADDR(ch),
693 TW_CONTRAST_ADDR(ch), val);
694 break;
696 case V4L2_CID_BRIGHTNESS:
697 if (is_tw286x(solo_dev, chip_num))
698 sval = val - 128;
699 else
700 sval = (char)val;
701 tw_writebyte(solo_dev, chip_num, TW286x_BRIGHTNESS_ADDR(ch),
702 TW_BRIGHTNESS_ADDR(ch), sval);
704 break;
705 default:
706 return -EINVAL;
709 return 0;
712 int tw28_get_ctrl_val(struct solo6010_dev *solo_dev, u32 ctrl, u8 ch,
713 s32 *val)
715 u8 rval, chip_num;
717 /* Get the right chip and on-chip channel */
718 chip_num = ch / 4;
719 ch %= 4;
721 switch (ctrl) {
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));
728 *val = rval & 0x0f;
729 } else
730 *val = 0;
731 break;
732 case V4L2_CID_HUE:
733 rval = tw_readbyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
734 TW_HUE_ADDR(ch));
735 if (is_tw286x(solo_dev, chip_num))
736 *val = (s32)((char)rval) + 128;
737 else
738 *val = rval;
739 break;
740 case V4L2_CID_SATURATION:
741 *val = tw_readbyte(solo_dev, chip_num,
742 TW286x_SATURATIONU_ADDR(ch),
743 TW_SATURATION_ADDR(ch));
744 break;
745 case V4L2_CID_CONTRAST:
746 *val = tw_readbyte(solo_dev, chip_num,
747 TW286x_CONTRAST_ADDR(ch),
748 TW_CONTRAST_ADDR(ch));
749 break;
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;
756 else
757 *val = rval;
758 break;
759 default:
760 return -EINVAL;
763 return 0;
766 #if 0
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)
774 unsigned int 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);
787 #endif
789 u8 tw28_get_audio_gain(struct solo6010_dev *solo_dev, u8 ch)
791 u8 val;
792 u8 chip_num;
794 /* Get the right chip and on-chip channel */
795 chip_num = ch / 4;
796 ch %= 4;
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)
807 u8 old_val;
808 u8 chip_num;
810 /* Get the right chip and on-chip channel */
811 chip_num = ch / 4;
812 ch %= 4;
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);