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 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
++;
609 solo_dev
->tw2864
|= 1 << i
;
610 solo_dev
->tw28_cnt
++;
613 value
= solo_i2c_readbyte(solo_dev
, SOLO_I2C_TW
,
614 TW_CHIP_OFFSET_ADDR(i
),
616 if ((value
>> 3) == 0x04) {
617 solo_dev
->tw2815
|= 1 << i
;
618 solo_dev
->tw28_cnt
++;
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");
629 saa712x_setup(solo_dev
);
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
));
637 tw2815_setup(solo_dev
, TW_CHIP_OFFSET_ADDR(i
));
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.
649 int tw28_get_video_status(struct solo_dev
*solo_dev
, u8 ch
)
653 /* Get the right chip and on-chip channel */
657 val
= tw_readbyte(solo_dev
, chip_num
, TW286x_AV_STAT_ADDR
,
658 TW_AV_STAT_ADDR
) & 0x0f;
660 return val
& (1 << ch
) ? 1 : 0;
664 /* Status of audio from up to 4 techwell chips are combined into 1 variable.
665 * See techwell datasheet for details. */
666 u16
tw28_get_audio_status(struct solo_dev
*solo_dev
)
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);
682 bool tw28_has_sharpness(struct solo_dev
*solo_dev
, u8 ch
)
684 return is_tw286x(solo_dev
, ch
/ 4);
687 int tw28_set_ctrl_val(struct solo_dev
*solo_dev
, u32 ctrl
, u8 ch
,
693 /* Get the right chip and on-chip channel */
697 if (val
> 255 || val
< 0)
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
));
709 solo_i2c_writebyte(solo_dev
, SOLO_I2C_TW
,
710 TW_CHIP_OFFSET_ADDR(chip_num
),
711 TW286x_SHARPNESS(chip_num
), v
);
718 if (is_tw286x(solo_dev
, chip_num
))
722 tw_writebyte(solo_dev
, chip_num
, TW286x_HUE_ADDR(ch
),
723 TW_HUE_ADDR(ch
), sval
);
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
);
734 tw_writebyte(solo_dev
, chip_num
, TW286x_SATURATIONV_ADDR(ch
),
735 TW_SATURATION_ADDR(ch
), val
);
739 case V4L2_CID_CONTRAST
:
740 tw_writebyte(solo_dev
, chip_num
, TW286x_CONTRAST_ADDR(ch
),
741 TW_CONTRAST_ADDR(ch
), val
);
744 case V4L2_CID_BRIGHTNESS
:
745 if (is_tw286x(solo_dev
, chip_num
))
749 tw_writebyte(solo_dev
, chip_num
, TW286x_BRIGHTNESS_ADDR(ch
),
750 TW_BRIGHTNESS_ADDR(ch
), sval
);
760 int tw28_get_ctrl_val(struct solo_dev
*solo_dev
, u32 ctrl
, u8 ch
,
765 /* Get the right chip and on-chip channel */
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
));
781 rval
= tw_readbyte(solo_dev
, chip_num
, TW286x_HUE_ADDR(ch
),
783 if (is_tw286x(solo_dev
, chip_num
))
784 *val
= (s32
)((char)rval
) + 128;
788 case V4L2_CID_SATURATION
:
789 *val
= tw_readbyte(solo_dev
, chip_num
,
790 TW286x_SATURATIONU_ADDR(ch
),
791 TW_SATURATION_ADDR(ch
));
793 case V4L2_CID_CONTRAST
:
794 *val
= tw_readbyte(solo_dev
, chip_num
,
795 TW286x_CONTRAST_ADDR(ch
),
796 TW_CONTRAST_ADDR(ch
));
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;
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.
820 void tw2815_Set_AudioOutVol(struct solo_dev
*solo_dev
, unsigned int u_val
)
823 unsigned int chip_num
;
825 chip_num
= (solo_dev
->nr_chans
- 1) / 4;
827 val
= tw_readbyte(solo_dev
, chip_num
, TW286x_AUDIO_OUTPUT_VOL_ADDR
,
828 TW_AUDIO_OUTPUT_VOL_ADDR
);
830 u_val
= (val
& 0x0f) | (u_val
<< 4);
832 tw_writebyte(solo_dev
, chip_num
, TW286x_AUDIO_OUTPUT_VOL_ADDR
,
833 TW_AUDIO_OUTPUT_VOL_ADDR
, u_val
);
837 u8
tw28_get_audio_gain(struct solo_dev
*solo_dev
, u8 ch
)
842 /* Get the right chip and on-chip channel */
846 val
= tw_readbyte(solo_dev
, chip_num
,
847 TW286x_AUDIO_INPUT_GAIN_ADDR(ch
),
848 TW_AUDIO_INPUT_GAIN_ADDR(ch
));
850 return (ch
% 2) ? (val
>> 4) : (val
& 0x0f);
853 void tw28_set_audio_gain(struct solo_dev
*solo_dev
, u8 ch
, u8 val
)
858 /* Get the right chip and on-chip channel */
862 old_val
= tw_readbyte(solo_dev
, chip_num
,
863 TW286x_AUDIO_INPUT_GAIN_ADDR(ch
),
864 TW_AUDIO_INPUT_GAIN_ADDR(ch
));
866 val
= (old_val
& ((ch
% 2) ? 0x0f : 0xf0)) |
867 ((ch
% 2) ? (val
<< 4) : val
);
869 tw_writebyte(solo_dev
, chip_num
, TW286x_AUDIO_INPUT_GAIN_ADDR(ch
),
870 TW_AUDIO_INPUT_GAIN_ADDR(ch
), val
);