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