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