2 * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
5 * Ben Collins <bcollins@ubuntu.com>
8 * John Brooks <john.brooks@bluecherry.net>
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.
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.
21 #include <linux/kernel.h>
22 #include <linux/delay.h>
25 #include "solo6x10-tw28.h"
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)
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)
38 static 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,
73 static 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,
108 static 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,
143 static 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,
178 #define is_tw286x(__solo, __id) (!(__solo->tw2815 & (1 << __id)))
180 static u8
tw_readbyte(struct solo_dev
*solo_dev
, int chip_id
, u8 tw6x_off
,
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
),
188 return solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
,
189 TW_CHIP_OFFSET_ADDR(chip_id
),
193 static void tw_writebyte(struct solo_dev
*solo_dev
, int chip_id
,
194 u8 tw6x_off
, u8 tw_off
, u8 val
)
196 if (is_tw286x(solo_dev
, chip_id
))
197 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
,
198 TW_CHIP_OFFSET_ADDR(chip_id
),
201 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
,
202 TW_CHIP_OFFSET_ADDR(chip_id
),
206 static void tw_write_and_verify(struct solo_dev
*solo_dev
, u8 addr
, u8 off
,
211 for (i
= 0; i
< 5; i
++) {
212 u8 rval
= solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
, addr
, off
);
217 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
, addr
, off
, val
);
218 msleep_interruptible(1);
221 /* printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n", */
222 /* addr, off, val); */
225 static int tw2865_setup(struct solo_dev
*solo_dev
, u8 dev_addr
)
227 u8 tbl_tw2865_common
[256];
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
));
234 memcpy(tbl_tw2865_common
, tbl_tw2865_ntsc_template
,
235 sizeof(tbl_tw2865_common
));
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;
255 for (i
= 0; i
< 0xff; i
++) {
256 /* Skip read only registers */
269 tw_write_and_verify(solo_dev
, dev_addr
, i
,
270 tbl_tw2865_common
[i
]);
276 static int tw2864_setup(struct solo_dev
*solo_dev
, u8 dev_addr
)
278 u8 tbl_tw2864_common
[256];
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
));
285 memcpy(tbl_tw2864_common
, tbl_tw2864_ntsc_template
,
286 sizeof(tbl_tw2864_common
));
288 if (solo_dev
->tw2865
== 0) {
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;
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;
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;
331 for (i
= 0; i
< 0xff; i
++) {
332 /* Skip read only registers */
345 tw_write_and_verify(solo_dev
, dev_addr
, i
,
346 tbl_tw2864_common
[i
]);
352 static int tw2815_setup(struct solo_dev
*solo_dev
, u8 dev_addr
)
354 u8 tbl_ntsc_tw2815_common
[] = {
355 0x00, 0xc8, 0x20, 0xd0, 0x06, 0xf0, 0x08, 0x80,
356 0x80, 0x80, 0x80, 0x02, 0x06, 0x00, 0x11,
359 u8 tbl_pal_tw2815_common
[] = {
360 0x00, 0x88, 0x20, 0xd0, 0x05, 0x20, 0x28, 0x80,
361 0x80, 0x80, 0x80, 0x82, 0x06, 0x00, 0x11,
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 */
373 u8
*tbl_tw2815_common
;
377 tbl_ntsc_tw2815_common
[0x06] = 0;
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);
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);
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);
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);
398 tbl_pal_tw2815_common
[0x06] = 0;
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);
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);
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);
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);
420 (solo_dev
->video_type
== SOLO_VO_FMT_TYPE_NTSC
) ?
421 tbl_ntsc_tw2815_common
: tbl_pal_tw2815_common
;
423 /* Dual ITU-R BT.656 format */
424 tbl_tw2815_common
[0x0d] |= 0x04;
426 /* Audio configuration */
427 tbl_tw2815_sfr
[0x62 - 0x40] &= ~(3 << 6);
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;
450 /* Output mode of R_ADATM pin (0 mixing, 1 record) */
451 /* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */
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;
457 /* Playback of right channel */
458 tbl_tw2815_sfr
[0x6c - 0x40] |= 1 << 5;
460 /* Reserved value (XXX ??) */
461 tbl_tw2815_sfr
[0x5c - 0x40] |= 1 << 5;
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;
469 /* End of audio configuration */
471 for (ch
= 0; ch
< 4; ch
++) {
472 tbl_tw2815_common
[0x0d] &= ~3;
475 tbl_tw2815_common
[0x0d] |= 0x21;
478 tbl_tw2815_common
[0x0d] |= 0x20;
481 tbl_tw2815_common
[0x0d] |= 0x23;
484 tbl_tw2815_common
[0x0d] |= 0x22;
488 for (i
= 0; i
< 0x0f; i
++) {
490 continue; /* read-only */
491 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
,
492 dev_addr
, (ch
* 0x10) + i
,
493 tbl_tw2815_common
[i
]);
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)
503 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
, dev_addr
, i
,
504 tbl_tw2815_sfr
[i
- 0x40]);
510 #define FIRST_ACTIVE_LINE 0x0008
511 #define LAST_ACTIVE_LINE 0x0102
513 static void saa712x_write_regs(struct solo_dev
*dev
, const u8
*vals
,
516 for (; start
< n
; start
++, vals
++) {
517 /* Skip read-only registers */
519 /* case 0x00 ... 0x25: */
525 solo_i2c_writebyte(dev
, SOLO_I2C_SAA
, 0x46, start
, *vals
);
529 #define SAA712x_reg7c (0x80 | ((LAST_ACTIVE_LINE & 0x100) >> 2) \
530 | ((FIRST_ACTIVE_LINE & 0x100) >> 4))
532 static void saa712x_setup(struct solo_dev
*dev
)
534 const int reg_start
= 0x26;
535 static const u8 saa7128_regs_ntsc
[] = {
539 0x59, 0x1d, 0x75, 0x3f, 0x06, 0x3f,
540 /* :0x2e XXX: read-only */
542 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
544 0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
546 0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18,
547 0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f,
549 0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06,
550 0x02, 0x80, 0x71, 0x77, 0xa7, 0x67, 0x66, 0x2e,
552 0x7b, 0x11, 0x4f, 0x1f, 0x7c, 0xf0, 0x21, 0x77,
553 0x41, 0x88, 0x41, 0x52, 0xed, 0x10, 0x10, 0x00,
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
[] = {
562 0xe1, 0x1d, 0x75, 0x3f, 0x06, 0x3f,
563 /* :0x2e XXX: read-only */
565 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
567 0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
569 0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18,
570 0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f,
572 0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06,
573 0x02, 0x80, 0x0f, 0x77, 0xa7, 0x67, 0x66, 0x2e,
575 0x7b, 0x02, 0x35, 0xcb, 0x8a, 0x09, 0x2a, 0x77,
576 0x41, 0x88, 0x41, 0x52, 0xf1, 0x10, 0x20, 0x00,
578 0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00,
579 0x00, 0x00, 0x12, 0x30,
580 SAA712x_reg7c
| 0x40, 0x00, 0xff, 0xff,
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
));
587 saa712x_write_regs(dev
, saa7128_regs_ntsc
, reg_start
,
588 sizeof(saa7128_regs_ntsc
));
591 int solo_tw28_init(struct solo_dev
*solo_dev
)
596 solo_dev
->tw28_cnt
= 0;
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);
603 switch (value
>> 3) {
605 solo_dev
->tw2865
|= 1 << i
;
606 solo_dev
->tw28_cnt
++;
610 solo_dev
->tw2864
|= 1 << i
;
611 solo_dev
->tw28_cnt
++;
614 value
= solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
,
615 TW_CHIP_OFFSET_ADDR(i
),
617 if ((value
>> 3) == 0x04) {
618 solo_dev
->tw2815
|= 1 << i
;
619 solo_dev
->tw28_cnt
++;
624 if (solo_dev
->tw28_cnt
!= (solo_dev
->nr_chans
>> 2)) {
625 dev_err(&solo_dev
->pdev
->dev
,
626 "Could not initialize any techwell chips\n");
630 saa712x_setup(solo_dev
);
632 for (i
= 0; i
< solo_dev
->tw28_cnt
; i
++) {
633 if ((solo_dev
->tw2865
& (1 << i
)))
634 tw2865_setup(solo_dev
, TW_CHIP_OFFSET_ADDR(i
));
635 else if ((solo_dev
->tw2864
& (1 << i
)))
636 tw2864_setup(solo_dev
, TW_CHIP_OFFSET_ADDR(i
));
638 tw2815_setup(solo_dev
, TW_CHIP_OFFSET_ADDR(i
));
645 * We accessed the video status signal in the Techwell chip through
646 * iic/i2c because the video status reported by register REG_VI_STATUS1
647 * (address 0x012C) of the SOLO6010 chip doesn't give the correct video
648 * status signal values.
650 int tw28_get_video_status(struct solo_dev
*solo_dev
, u8 ch
)
654 /* Get the right chip and on-chip channel */
658 val
= tw_readbyte(solo_dev
, chip_num
, TW286x_AV_STAT_ADDR
,
659 TW_AV_STAT_ADDR
) & 0x0f;
661 return val
& (1 << ch
) ? 1 : 0;
665 /* Status of audio from up to 4 techwell chips are combined into 1 variable.
666 * See techwell datasheet for details. */
667 u16
tw28_get_audio_status(struct solo_dev
*solo_dev
)
673 for (i
= 0; i
< solo_dev
->tw28_cnt
; i
++) {
674 val
= (tw_readbyte(solo_dev
, i
, TW286x_AV_STAT_ADDR
,
675 TW_AV_STAT_ADDR
) & 0xf0) >> 4;
676 status
|= val
<< (i
* 4);
683 bool tw28_has_sharpness(struct solo_dev
*solo_dev
, u8 ch
)
685 return is_tw286x(solo_dev
, ch
/ 4);
688 int tw28_set_ctrl_val(struct solo_dev
*solo_dev
, u32 ctrl
, u8 ch
,
694 /* Get the right chip and on-chip channel */
698 if (val
> 255 || val
< 0)
702 case V4L2_CID_SHARPNESS
:
703 /* Only 286x has sharpness */
704 if (is_tw286x(solo_dev
, chip_num
)) {
705 u8 v
= solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
,
706 TW_CHIP_OFFSET_ADDR(chip_num
),
707 TW286x_SHARPNESS(chip_num
));
710 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
,
711 TW_CHIP_OFFSET_ADDR(chip_num
),
712 TW286x_SHARPNESS(chip_num
), v
);
719 if (is_tw286x(solo_dev
, chip_num
))
723 tw_writebyte(solo_dev
, chip_num
, TW286x_HUE_ADDR(ch
),
724 TW_HUE_ADDR(ch
), sval
);
728 case V4L2_CID_SATURATION
:
729 /* 286x chips have a U and V component for saturation */
730 if (is_tw286x(solo_dev
, chip_num
)) {
731 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
,
732 TW_CHIP_OFFSET_ADDR(chip_num
),
733 TW286x_SATURATIONU_ADDR(ch
), val
);
735 tw_writebyte(solo_dev
, chip_num
, TW286x_SATURATIONV_ADDR(ch
),
736 TW_SATURATION_ADDR(ch
), val
);
740 case V4L2_CID_CONTRAST
:
741 tw_writebyte(solo_dev
, chip_num
, TW286x_CONTRAST_ADDR(ch
),
742 TW_CONTRAST_ADDR(ch
), val
);
745 case V4L2_CID_BRIGHTNESS
:
746 if (is_tw286x(solo_dev
, chip_num
))
750 tw_writebyte(solo_dev
, chip_num
, TW286x_BRIGHTNESS_ADDR(ch
),
751 TW_BRIGHTNESS_ADDR(ch
), sval
);
761 int tw28_get_ctrl_val(struct solo_dev
*solo_dev
, u32 ctrl
, u8 ch
,
766 /* Get the right chip and on-chip channel */
771 case V4L2_CID_SHARPNESS
:
772 /* Only 286x has sharpness */
773 if (is_tw286x(solo_dev
, chip_num
)) {
774 rval
= solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
,
775 TW_CHIP_OFFSET_ADDR(chip_num
),
776 TW286x_SHARPNESS(chip_num
));
782 rval
= tw_readbyte(solo_dev
, chip_num
, TW286x_HUE_ADDR(ch
),
784 if (is_tw286x(solo_dev
, chip_num
))
785 *val
= (s32
)((char)rval
) + 128;
789 case V4L2_CID_SATURATION
:
790 *val
= tw_readbyte(solo_dev
, chip_num
,
791 TW286x_SATURATIONU_ADDR(ch
),
792 TW_SATURATION_ADDR(ch
));
794 case V4L2_CID_CONTRAST
:
795 *val
= tw_readbyte(solo_dev
, chip_num
,
796 TW286x_CONTRAST_ADDR(ch
),
797 TW_CONTRAST_ADDR(ch
));
799 case V4L2_CID_BRIGHTNESS
:
800 rval
= tw_readbyte(solo_dev
, chip_num
,
801 TW286x_BRIGHTNESS_ADDR(ch
),
802 TW_BRIGHTNESS_ADDR(ch
));
803 if (is_tw286x(solo_dev
, chip_num
))
804 *val
= (s32
)((char)rval
) + 128;
817 * For audio output volume, the output channel is only 1. In this case we
818 * don't need to offset TW_CHIP_OFFSET_ADDR. The TW_CHIP_OFFSET_ADDR used
819 * is the base address of the techwell chip.
821 void tw2815_Set_AudioOutVol(struct solo_dev
*solo_dev
, unsigned int u_val
)
824 unsigned int chip_num
;
826 chip_num
= (solo_dev
->nr_chans
- 1) / 4;
828 val
= tw_readbyte(solo_dev
, chip_num
, TW286x_AUDIO_OUTPUT_VOL_ADDR
,
829 TW_AUDIO_OUTPUT_VOL_ADDR
);
831 u_val
= (val
& 0x0f) | (u_val
<< 4);
833 tw_writebyte(solo_dev
, chip_num
, TW286x_AUDIO_OUTPUT_VOL_ADDR
,
834 TW_AUDIO_OUTPUT_VOL_ADDR
, u_val
);
838 u8
tw28_get_audio_gain(struct solo_dev
*solo_dev
, u8 ch
)
843 /* Get the right chip and on-chip channel */
847 val
= tw_readbyte(solo_dev
, chip_num
,
848 TW286x_AUDIO_INPUT_GAIN_ADDR(ch
),
849 TW_AUDIO_INPUT_GAIN_ADDR(ch
));
851 return (ch
% 2) ? (val
>> 4) : (val
& 0x0f);
854 void tw28_set_audio_gain(struct solo_dev
*solo_dev
, u8 ch
, u8 val
)
859 /* Get the right chip and on-chip channel */
863 old_val
= tw_readbyte(solo_dev
, chip_num
,
864 TW286x_AUDIO_INPUT_GAIN_ADDR(ch
),
865 TW_AUDIO_INPUT_GAIN_ADDR(ch
));
867 val
= (old_val
& ((ch
% 2) ? 0x0f : 0xf0)) |
868 ((ch
% 2) ? (val
<< 4) : val
);
870 tw_writebyte(solo_dev
, chip_num
, TW286x_AUDIO_INPUT_GAIN_ADDR(ch
),
871 TW_AUDIO_INPUT_GAIN_ADDR(ch
), val
);