1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com>
6 * Ben Collins <bcollins@ubuntu.com>
9 * John Brooks <john.brooks@bluecherry.net>
12 #include <linux/kernel.h>
13 #include <linux/delay.h>
16 #include "solo6x10-tw28.h"
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)
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)
29 static 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,
64 static 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,
99 static 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,
134 static 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,
169 #define is_tw286x(__solo, __id) (!(__solo->tw2815 & (1 << __id)))
171 static u8
tw_readbyte(struct solo_dev
*solo_dev
, int chip_id
, u8 tw6x_off
,
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
),
179 return solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
,
180 TW_CHIP_OFFSET_ADDR(chip_id
),
184 static void tw_writebyte(struct solo_dev
*solo_dev
, int chip_id
,
185 u8 tw6x_off
, u8 tw_off
, u8 val
)
187 if (is_tw286x(solo_dev
, chip_id
))
188 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
,
189 TW_CHIP_OFFSET_ADDR(chip_id
),
192 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
,
193 TW_CHIP_OFFSET_ADDR(chip_id
),
197 static void tw_write_and_verify(struct solo_dev
*solo_dev
, u8 addr
, u8 off
,
202 for (i
= 0; i
< 5; i
++) {
203 u8 rval
= solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
, addr
, off
);
208 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
, addr
, off
, val
);
209 msleep_interruptible(1);
212 /* printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n", */
213 /* addr, off, val); */
216 static int tw2865_setup(struct solo_dev
*solo_dev
, u8 dev_addr
)
218 u8 tbl_tw2865_common
[256];
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
));
225 memcpy(tbl_tw2865_common
, tbl_tw2865_ntsc_template
,
226 sizeof(tbl_tw2865_common
));
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;
246 for (i
= 0; i
< 0xff; i
++) {
247 /* Skip read only registers */
260 tw_write_and_verify(solo_dev
, dev_addr
, i
,
261 tbl_tw2865_common
[i
]);
267 static int tw2864_setup(struct solo_dev
*solo_dev
, u8 dev_addr
)
269 u8 tbl_tw2864_common
[256];
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
));
276 memcpy(tbl_tw2864_common
, tbl_tw2864_ntsc_template
,
277 sizeof(tbl_tw2864_common
));
279 if (solo_dev
->tw2865
== 0) {
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;
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;
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;
322 for (i
= 0; i
< 0xff; i
++) {
323 /* Skip read only registers */
336 tw_write_and_verify(solo_dev
, dev_addr
, i
,
337 tbl_tw2864_common
[i
]);
343 static int tw2815_setup(struct solo_dev
*solo_dev
, u8 dev_addr
)
345 u8 tbl_ntsc_tw2815_common
[] = {
346 0x00, 0xc8, 0x20, 0xd0, 0x06, 0xf0, 0x08, 0x80,
347 0x80, 0x80, 0x80, 0x02, 0x06, 0x00, 0x11,
350 u8 tbl_pal_tw2815_common
[] = {
351 0x00, 0x88, 0x20, 0xd0, 0x05, 0x20, 0x28, 0x80,
352 0x80, 0x80, 0x80, 0x82, 0x06, 0x00, 0x11,
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 */
364 u8
*tbl_tw2815_common
;
368 tbl_ntsc_tw2815_common
[0x06] = 0;
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);
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);
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);
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);
389 tbl_pal_tw2815_common
[0x06] = 0;
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);
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);
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);
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);
411 (solo_dev
->video_type
== SOLO_VO_FMT_TYPE_NTSC
) ?
412 tbl_ntsc_tw2815_common
: tbl_pal_tw2815_common
;
414 /* Dual ITU-R BT.656 format */
415 tbl_tw2815_common
[0x0d] |= 0x04;
417 /* Audio configuration */
418 tbl_tw2815_sfr
[0x62 - 0x40] &= ~(3 << 6);
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;
441 /* Output mode of R_ADATM pin (0 mixing, 1 record) */
442 /* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */
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;
448 /* Playback of right channel */
449 tbl_tw2815_sfr
[0x6c - 0x40] |= 1 << 5;
451 /* Reserved value (XXX ??) */
452 tbl_tw2815_sfr
[0x5c - 0x40] |= 1 << 5;
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;
460 /* End of audio configuration */
462 for (ch
= 0; ch
< 4; ch
++) {
463 tbl_tw2815_common
[0x0d] &= ~3;
466 tbl_tw2815_common
[0x0d] |= 0x21;
469 tbl_tw2815_common
[0x0d] |= 0x20;
472 tbl_tw2815_common
[0x0d] |= 0x23;
475 tbl_tw2815_common
[0x0d] |= 0x22;
479 for (i
= 0; i
< 0x0f; i
++) {
481 continue; /* read-only */
482 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
,
483 dev_addr
, (ch
* 0x10) + i
,
484 tbl_tw2815_common
[i
]);
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)
494 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
, dev_addr
, i
,
495 tbl_tw2815_sfr
[i
- 0x40]);
501 #define FIRST_ACTIVE_LINE 0x0008
502 #define LAST_ACTIVE_LINE 0x0102
504 static void saa712x_write_regs(struct solo_dev
*dev
, const u8
*vals
,
507 for (; start
< n
; start
++, vals
++) {
508 /* Skip read-only registers */
510 /* case 0x00 ... 0x25: */
516 solo_i2c_writebyte(dev
, SOLO_I2C_SAA
, 0x46, start
, *vals
);
520 #define SAA712x_reg7c (0x80 | ((LAST_ACTIVE_LINE & 0x100) >> 2) \
521 | ((FIRST_ACTIVE_LINE & 0x100) >> 4))
523 static void saa712x_setup(struct solo_dev
*dev
)
525 const int reg_start
= 0x26;
526 static const u8 saa7128_regs_ntsc
[] = {
530 0x59, 0x1d, 0x75, 0x3f, 0x06, 0x3f,
531 /* :0x2e XXX: read-only */
533 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
535 0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
537 0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18,
538 0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f,
540 0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06,
541 0x02, 0x80, 0x71, 0x77, 0xa7, 0x67, 0x66, 0x2e,
543 0x7b, 0x11, 0x4f, 0x1f, 0x7c, 0xf0, 0x21, 0x77,
544 0x41, 0x88, 0x41, 0x52, 0xed, 0x10, 0x10, 0x00,
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
[] = {
553 0xe1, 0x1d, 0x75, 0x3f, 0x06, 0x3f,
554 /* :0x2e XXX: read-only */
556 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
558 0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
560 0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18,
561 0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f,
563 0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06,
564 0x02, 0x80, 0x0f, 0x77, 0xa7, 0x67, 0x66, 0x2e,
566 0x7b, 0x02, 0x35, 0xcb, 0x8a, 0x09, 0x2a, 0x77,
567 0x41, 0x88, 0x41, 0x52, 0xf1, 0x10, 0x20, 0x00,
569 0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00,
570 0x00, 0x00, 0x12, 0x30,
571 SAA712x_reg7c
| 0x40, 0x00, 0xff, 0xff,
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
));
578 saa712x_write_regs(dev
, saa7128_regs_ntsc
, reg_start
,
579 sizeof(saa7128_regs_ntsc
));
582 int solo_tw28_init(struct solo_dev
*solo_dev
)
587 solo_dev
->tw28_cnt
= 0;
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);
594 switch (value
>> 3) {
596 solo_dev
->tw2865
|= 1 << i
;
597 solo_dev
->tw28_cnt
++;
601 solo_dev
->tw2864
|= 1 << i
;
602 solo_dev
->tw28_cnt
++;
605 value
= solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
,
606 TW_CHIP_OFFSET_ADDR(i
),
608 if ((value
>> 3) == 0x04) {
609 solo_dev
->tw2815
|= 1 << i
;
610 solo_dev
->tw28_cnt
++;
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");
621 saa712x_setup(solo_dev
);
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
));
629 tw2815_setup(solo_dev
, TW_CHIP_OFFSET_ADDR(i
));
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.
641 int tw28_get_video_status(struct solo_dev
*solo_dev
, u8 ch
)
645 /* Get the right chip and on-chip channel */
649 val
= tw_readbyte(solo_dev
, chip_num
, TW286x_AV_STAT_ADDR
,
650 TW_AV_STAT_ADDR
) & 0x0f;
652 return val
& (1 << ch
) ? 1 : 0;
656 /* Status of audio from up to 4 techwell chips are combined into 1 variable.
657 * See techwell datasheet for details. */
658 u16
tw28_get_audio_status(struct solo_dev
*solo_dev
)
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);
674 bool tw28_has_sharpness(struct solo_dev
*solo_dev
, u8 ch
)
676 return is_tw286x(solo_dev
, ch
/ 4);
679 int tw28_set_ctrl_val(struct solo_dev
*solo_dev
, u32 ctrl
, u8 ch
,
685 /* Get the right chip and on-chip channel */
689 if (val
> 255 || val
< 0)
693 case V4L2_CID_SHARPNESS
:
694 /* Only 286x has sharpness */
695 if (is_tw286x(solo_dev
, chip_num
)) {
696 u8 v
= solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
,
697 TW_CHIP_OFFSET_ADDR(chip_num
),
698 TW286x_SHARPNESS(chip_num
));
701 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
,
702 TW_CHIP_OFFSET_ADDR(chip_num
),
703 TW286x_SHARPNESS(chip_num
), v
);
710 if (is_tw286x(solo_dev
, chip_num
))
714 tw_writebyte(solo_dev
, chip_num
, TW286x_HUE_ADDR(ch
),
715 TW_HUE_ADDR(ch
), sval
);
719 case V4L2_CID_SATURATION
:
720 /* 286x chips have a U and V component for saturation */
721 if (is_tw286x(solo_dev
, chip_num
)) {
722 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
,
723 TW_CHIP_OFFSET_ADDR(chip_num
),
724 TW286x_SATURATIONU_ADDR(ch
), val
);
726 tw_writebyte(solo_dev
, chip_num
, TW286x_SATURATIONV_ADDR(ch
),
727 TW_SATURATION_ADDR(ch
), val
);
731 case V4L2_CID_CONTRAST
:
732 tw_writebyte(solo_dev
, chip_num
, TW286x_CONTRAST_ADDR(ch
),
733 TW_CONTRAST_ADDR(ch
), val
);
736 case V4L2_CID_BRIGHTNESS
:
737 if (is_tw286x(solo_dev
, chip_num
))
741 tw_writebyte(solo_dev
, chip_num
, TW286x_BRIGHTNESS_ADDR(ch
),
742 TW_BRIGHTNESS_ADDR(ch
), sval
);
752 int tw28_get_ctrl_val(struct solo_dev
*solo_dev
, u32 ctrl
, u8 ch
,
757 /* Get the right chip and on-chip channel */
762 case V4L2_CID_SHARPNESS
:
763 /* Only 286x has sharpness */
764 if (is_tw286x(solo_dev
, chip_num
)) {
765 rval
= solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
,
766 TW_CHIP_OFFSET_ADDR(chip_num
),
767 TW286x_SHARPNESS(chip_num
));
773 rval
= tw_readbyte(solo_dev
, chip_num
, TW286x_HUE_ADDR(ch
),
775 if (is_tw286x(solo_dev
, chip_num
))
776 *val
= (s32
)((char)rval
) + 128;
780 case V4L2_CID_SATURATION
:
781 *val
= tw_readbyte(solo_dev
, chip_num
,
782 TW286x_SATURATIONU_ADDR(ch
),
783 TW_SATURATION_ADDR(ch
));
785 case V4L2_CID_CONTRAST
:
786 *val
= tw_readbyte(solo_dev
, chip_num
,
787 TW286x_CONTRAST_ADDR(ch
),
788 TW_CONTRAST_ADDR(ch
));
790 case V4L2_CID_BRIGHTNESS
:
791 rval
= tw_readbyte(solo_dev
, chip_num
,
792 TW286x_BRIGHTNESS_ADDR(ch
),
793 TW_BRIGHTNESS_ADDR(ch
));
794 if (is_tw286x(solo_dev
, chip_num
))
795 *val
= (s32
)((char)rval
) + 128;
808 * For audio output volume, the output channel is only 1. In this case we
809 * don't need to offset TW_CHIP_OFFSET_ADDR. The TW_CHIP_OFFSET_ADDR used
810 * is the base address of the techwell chip.
812 void tw2815_Set_AudioOutVol(struct solo_dev
*solo_dev
, unsigned int u_val
)
815 unsigned int chip_num
;
817 chip_num
= (solo_dev
->nr_chans
- 1) / 4;
819 val
= tw_readbyte(solo_dev
, chip_num
, TW286x_AUDIO_OUTPUT_VOL_ADDR
,
820 TW_AUDIO_OUTPUT_VOL_ADDR
);
822 u_val
= (val
& 0x0f) | (u_val
<< 4);
824 tw_writebyte(solo_dev
, chip_num
, TW286x_AUDIO_OUTPUT_VOL_ADDR
,
825 TW_AUDIO_OUTPUT_VOL_ADDR
, u_val
);
829 u8
tw28_get_audio_gain(struct solo_dev
*solo_dev
, u8 ch
)
834 /* Get the right chip and on-chip channel */
838 val
= tw_readbyte(solo_dev
, chip_num
,
839 TW286x_AUDIO_INPUT_GAIN_ADDR(ch
),
840 TW_AUDIO_INPUT_GAIN_ADDR(ch
));
842 return (ch
% 2) ? (val
>> 4) : (val
& 0x0f);
845 void tw28_set_audio_gain(struct solo_dev
*solo_dev
, u8 ch
, u8 val
)
850 /* Get the right chip and on-chip channel */
854 old_val
= tw_readbyte(solo_dev
, chip_num
,
855 TW286x_AUDIO_INPUT_GAIN_ADDR(ch
),
856 TW_AUDIO_INPUT_GAIN_ADDR(ch
));
858 val
= (old_val
& ((ch
% 2) ? 0x0f : 0xf0)) |
859 ((ch
% 2) ? (val
<< 4) : val
);
861 tw_writebyte(solo_dev
, chip_num
, TW286x_AUDIO_INPUT_GAIN_ADDR(ch
),
862 TW_AUDIO_INPUT_GAIN_ADDR(ch
), val
);