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#include "solo6x10.h"
22#include "tw28.h"
23
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)
30
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)
35
36static 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,
69};
70
71static 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,
104};
105
106static 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,
139};
140
141#define is_tw286x(__solo, __id) (!(__solo->tw2815 & (1 << __id)))
142
143static u8 tw_readbyte(struct solo_dev *solo_dev, int chip_id, u8 tw6x_off,
144		      u8 tw_off)
145{
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),
149					 tw6x_off);
150	else
151		return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
152					 TW_CHIP_OFFSET_ADDR(chip_id),
153					 tw_off);
154}
155
156static void tw_writebyte(struct solo_dev *solo_dev, int chip_id,
157			 u8 tw6x_off, u8 tw_off, u8 val)
158{
159	if (is_tw286x(solo_dev, chip_id))
160		solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
161				   TW_CHIP_OFFSET_ADDR(chip_id),
162				   tw6x_off, val);
163	else
164		solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
165				   TW_CHIP_OFFSET_ADDR(chip_id),
166				   tw_off, val);
167}
168
169static void tw_write_and_verify(struct solo_dev *solo_dev, u8 addr, u8 off,
170				u8 val)
171{
172	int i;
173
174	for (i = 0; i < 5; i++) {
175		u8 rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, addr, off);
176		if (rval == val)
177			return;
178
179		solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, addr, off, val);
180		msleep_interruptible(1);
181	}
182
183/*	printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n",
184		addr, off, val); */
185}
186
187static int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr)
188{
189	u8 tbl_tw2865_common[256];
190	int i;
191
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));
195	else
196		memcpy(tbl_tw2865_common, tbl_tw2865_ntsc_template,
197		       sizeof(tbl_tw2865_common));
198
199	/* ALINK Mode */
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;
215	}
216
217	for (i = 0; i < 0xff; i++) {
218		/* Skip read only registers */
219		if (i >= 0xb8 && i <= 0xc1)
220			continue;
221		if ((i & ~0x30) == 0x00 ||
222		    (i & ~0x30) == 0x0c ||
223		    (i & ~0x30) == 0x0d)
224			continue;
225		if (i >= 0xc4 && i <= 0xc7)
226			continue;
227		if (i == 0xfd)
228			continue;
229
230		tw_write_and_verify(solo_dev, dev_addr, i,
231				    tbl_tw2865_common[i]);
232	}
233
234	return 0;
235}
236
237static int tw2864_setup(struct solo_dev *solo_dev, u8 dev_addr)
238{
239	u8 tbl_tw2864_common[sizeof(tbl_tw2864_template)];
240	int i;
241
242	memcpy(tbl_tw2864_common, tbl_tw2864_template,
243	       sizeof(tbl_tw2864_common));
244
245	if (solo_dev->tw2865 == 0) {
246		/* IRQ Mode */
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;
266		}
267	} else {
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;
272
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;
285		}
286	}
287
288	/* NTSC or PAL */
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;
296		}
297		tbl_tw2864_common[0x9d] = 0x90;
298		tbl_tw2864_common[0xf3] = 0x00;
299		tbl_tw2864_common[0xf4] = 0xa0;
300	}
301
302	for (i = 0; i < 0xff; i++) {
303		/* Skip read only registers */
304		if (i >= 0xb8 && i <= 0xc1)
305			continue;
306		if ((i & ~0x30) == 0x00 ||
307		    (i & ~0x30) == 0x0c ||
308		    (i & ~0x30) == 0x0d)
309			continue;
310		if (i == 0x74 || i == 0x77 || i == 0x78 ||
311		    i == 0x79 || i == 0x7a)
312			continue;
313		if (i == 0xfd)
314			continue;
315
316		tw_write_and_verify(solo_dev, dev_addr, i,
317				    tbl_tw2864_common[i]);
318	}
319
320	return 0;
321}
322
323static int tw2815_setup(struct solo_dev *solo_dev, u8 dev_addr)
324{
325	u8 tbl_ntsc_tw2815_common[] = {
326		0x00, 0xc8, 0x20, 0xd0, 0x06, 0xf0, 0x08, 0x80,
327		0x80, 0x80, 0x80, 0x02, 0x06, 0x00, 0x11,
328	};
329
330	u8 tbl_pal_tw2815_common[] = {
331		0x00, 0x88, 0x20, 0xd0, 0x05, 0x20, 0x28, 0x80,
332		0x80, 0x80, 0x80, 0x82, 0x06, 0x00, 0x11,
333	};
334
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 */
343	};
344	u8 *tbl_tw2815_common;
345	int i;
346	int ch;
347
348	tbl_ntsc_tw2815_common[0x06] = 0;
349
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);
353
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);
358
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);
363
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);
368
369	tbl_pal_tw2815_common[0x06] = 0;
370
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);
374
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);
379
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);
384
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);
389
390	tbl_tw2815_common =
391	    (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) ?
392	     tbl_ntsc_tw2815_common : tbl_pal_tw2815_common;
393
394	/* Dual ITU-R BT.656 format */
395	tbl_tw2815_common[0x0d] |= 0x04;
396
397	/* Audio configuration */
398	tbl_tw2815_sfr[0x62 - 0x40] &= ~(3 << 6);
399
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;
419	}
420
421	/* Output mode of R_ADATM pin (0 mixing, 1 record) */
422	/* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */
423
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;
427
428	/* Playback of right channel */
429	tbl_tw2815_sfr[0x6c - 0x40] |= 1 << 5;
430
431	/* Reserved value (XXX ??) */
432	tbl_tw2815_sfr[0x5c - 0x40] |= 1 << 5;
433
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;
439
440	/* End of audio configuration */
441
442	for (ch = 0; ch < 4; ch++) {
443		tbl_tw2815_common[0x0d] &= ~3;
444		switch (ch) {
445		case 0:
446			tbl_tw2815_common[0x0d] |= 0x21;
447			break;
448		case 1:
449			tbl_tw2815_common[0x0d] |= 0x20;
450			break;
451		case 2:
452			tbl_tw2815_common[0x0d] |= 0x23;
453			break;
454		case 3:
455			tbl_tw2815_common[0x0d] |= 0x22;
456			break;
457		}
458
459		for (i = 0; i < 0x0f; i++) {
460			if (i == 0x00)
461				continue;	/* read-only */
462			solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
463					   dev_addr, (ch * 0x10) + i,
464					   tbl_tw2815_common[i]);
465		}
466	}
467
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)
472			continue;
473
474		solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, dev_addr, i,
475				       tbl_tw2815_sfr[i - 0x40]);
476	}
477
478	return 0;
479}
480
481#define FIRST_ACTIVE_LINE	0x0008
482#define LAST_ACTIVE_LINE	0x0102
483
484static void saa7128_setup(struct solo_dev *solo_dev)
485{
486	int i;
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,
504	};
505
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));
511
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) {
514		regs[0x28] = 0xE1;
515
516		regs[0x5A] = 0x0F;
517		regs[0x61] = 0x02;
518		regs[0x62] = 0x35;
519		regs[0x63] = 0xCB;
520		regs[0x64] = 0x8A;
521		regs[0x65] = 0x09;
522		regs[0x66] = 0x2A;
523
524		regs[0x6C] = 0xf1;
525		regs[0x6E] = 0x20;
526
527		regs[0x7A] = 0x06 + 12;
528		regs[0x7b] = 0x24 + 12;
529		regs[0x7c] |= 1 << 6;
530	}
531
532	/* First 0x25 bytes are read-only? */
533	for (i = 0x26; i < 128; i++) {
534		if (i == 0x60 || i == 0x7D)
535			continue;
536		solo_i2c_writebyte(solo_dev, SOLO_I2C_SAA, 0x46, i, regs[i]);
537	}
538
539	return;
540}
541
542int solo_tw28_init(struct solo_dev *solo_dev)
543{
544	int i;
545	u8 value;
546
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);
551
552		switch (value >> 3) {
553		case 0x18:
554			solo_dev->tw2865 |= 1 << i;
555			solo_dev->tw28_cnt++;
556			break;
557		case 0x0c:
558			solo_dev->tw2864 |= 1 << i;
559			solo_dev->tw28_cnt++;
560			break;
561		default:
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++;
567			}
568		}
569	}
570
571	if (!solo_dev->tw28_cnt)
572		return -EINVAL;
573
574	saa7128_setup(solo_dev);
575
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));
581		else
582			tw2815_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
583	}
584
585	dev_info(&solo_dev->pdev->dev, "Initialized %d tw28xx chip%s:",
586		 solo_dev->tw28_cnt, solo_dev->tw28_cnt == 1 ? "" : "s");
587
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));
594	printk("\n");
595
596	return 0;
597}
598
599/*
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.
604 */
605int tw28_get_video_status(struct solo_dev *solo_dev, u8 ch)
606{
607	u8 val, chip_num;
608
609	/* Get the right chip and on-chip channel */
610	chip_num = ch / 4;
611	ch %= 4;
612
613	val = tw_readbyte(solo_dev, chip_num, TW286X_AV_STAT_ADDR,
614			  TW_AV_STAT_ADDR) & 0x0f;
615
616	return val & (1 << ch) ? 1 : 0;
617}
618
619#if 0
620/* Status of audio from up to 4 techwell chips are combined into 1 variable.
621 * See techwell datasheet for details. */
622u16 tw28_get_audio_status(struct solo_dev *solo_dev)
623{
624	u8 val;
625	u16 status = 0;
626	int i;
627
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);
632	}
633
634	return status;
635}
636#endif
637
638int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 val)
639{
640	char sval;
641	u8 chip_num;
642
643	/* Get the right chip and on-chip channel */
644	chip_num = ch / 4;
645	ch %= 4;
646
647	if (val > 255 || val < 0)
648		return -ERANGE;
649
650	switch (ctrl) {
651	case V4L2_CID_SHARPNESS:
652		/* Only 286x has sharpness */
653		if (val > 0x0f || val < 0)
654			return -ERANGE;
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));
659			v &= 0xf0;
660			v |= val;
661			solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
662					   TW_CHIP_OFFSET_ADDR(chip_num),
663					   TW286x_SHARPNESS(chip_num), v);
664		} else if (val != 0)
665			return -ERANGE;
666		break;
667
668	case V4L2_CID_HUE:
669		if (is_tw286x(solo_dev, chip_num))
670			sval = val - 128;
671		else
672			sval = (char)val;
673		tw_writebyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
674			     TW_HUE_ADDR(ch), sval);
675
676		break;
677
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);
683		}
684		tw_writebyte(solo_dev, chip_num, TW286x_SATURATIONV_ADDR(ch),
685			     TW_SATURATION_ADDR(ch), val);
686
687		break;
688
689	case V4L2_CID_CONTRAST:
690		tw_writebyte(solo_dev, chip_num, TW286x_CONTRAST_ADDR(ch),
691			     TW_CONTRAST_ADDR(ch), val);
692		break;
693
694	case V4L2_CID_BRIGHTNESS:
695		if (is_tw286x(solo_dev, chip_num))
696			sval = val - 128;
697		else
698			sval = (char)val;
699		tw_writebyte(solo_dev, chip_num, TW286x_BRIGHTNESS_ADDR(ch),
700			     TW_BRIGHTNESS_ADDR(ch), sval);
701
702		break;
703	default:
704		return -EINVAL;
705	}
706
707	return 0;
708}
709
710int tw28_get_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch,
711		      s32 *val)
712{
713	u8 rval, chip_num;
714
715	/* Get the right chip and on-chip channel */
716	chip_num = ch / 4;
717	ch %= 4;
718
719	switch (ctrl) {
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));
726			*val = rval & 0x0f;
727		} else
728			*val = 0;
729		break;
730	case V4L2_CID_HUE:
731		rval = tw_readbyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
732				   TW_HUE_ADDR(ch));
733		if (is_tw286x(solo_dev, chip_num))
734			*val = (s32)((char)rval) + 128;
735		else
736			*val = rval;
737		break;
738	case V4L2_CID_SATURATION:
739		*val = tw_readbyte(solo_dev, chip_num,
740				   TW286x_SATURATIONU_ADDR(ch),
741				   TW_SATURATION_ADDR(ch));
742		break;
743	case V4L2_CID_CONTRAST:
744		*val = tw_readbyte(solo_dev, chip_num,
745				   TW286x_CONTRAST_ADDR(ch),
746				   TW_CONTRAST_ADDR(ch));
747		break;
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;
754		else
755			*val = rval;
756		break;
757	default:
758		return -EINVAL;
759	}
760
761	return 0;
762}
763
764#if 0
765/*
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.
769 */
770void tw2815_Set_AudioOutVol(struct solo_dev *solo_dev, unsigned int u_val)
771{
772	unsigned int val;
773	unsigned int chip_num;
774
775	chip_num = (solo_dev->nr_chans - 1) / 4;
776
777	val = tw_readbyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
778			  TW_AUDIO_OUTPUT_VOL_ADDR);
779
780	u_val = (val & 0x0f) | (u_val << 4);
781
782	tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
783		     TW_AUDIO_OUTPUT_VOL_ADDR, u_val);
784}
785#endif
786
787u8 tw28_get_audio_gain(struct solo_dev *solo_dev, u8 ch)
788{
789	u8 val;
790	u8 chip_num;
791
792	/* Get the right chip and on-chip channel */
793	chip_num = ch / 4;
794	ch %= 4;
795
796	val = tw_readbyte(solo_dev, chip_num,
797			  TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
798			  TW_AUDIO_INPUT_GAIN_ADDR(ch));
799
800	return (ch % 2) ? (val >> 4) : (val & 0x0f);
801}
802
803void tw28_set_audio_gain(struct solo_dev *solo_dev, u8 ch, u8 val)
804{
805	u8 old_val;
806	u8 chip_num;
807
808	/* Get the right chip and on-chip channel */
809	chip_num = ch / 4;
810	ch %= 4;
811
812	old_val = tw_readbyte(solo_dev, chip_num,
813			      TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
814			      TW_AUDIO_INPUT_GAIN_ADDR(ch));
815
816	val = (old_val & ((ch % 2) ? 0x0f : 0xf0)) |
817		((ch % 2) ? (val << 4) : val);
818
819	tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
820		     TW_AUDIO_INPUT_GAIN_ADDR(ch), val);
821}
822