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