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