File Index
Files and directories
Sign in
FILE

solo6x10-tw28.c

File preview with syntax highlighting.

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