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