b4446b910e351484748fdb56988835d693f97a22
[pandora-kernel.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 #define is_tw286x(__solo, __id) (!(__solo->tw2815 & (1 << __id)))
73
74 static u8 tw_readbyte(struct solo6010_dev *solo_dev, int chip_id, u8 tw6x_off,
75                       u8 tw_off)
76 {
77         if (is_tw286x(solo_dev, chip_id))
78                 return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
79                                          TW_CHIP_OFFSET_ADDR(chip_id),
80                                          tw6x_off);
81         else
82                 return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
83                                          TW_CHIP_OFFSET_ADDR(chip_id),
84                                          tw_off);
85 }
86
87 static void tw_writebyte(struct solo6010_dev *solo_dev, int chip_id,
88                          u8 tw6x_off, u8 tw_off, u8 val)
89 {
90         if (is_tw286x(solo_dev, chip_id))
91                 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
92                                    TW_CHIP_OFFSET_ADDR(chip_id),
93                                    tw6x_off, val);
94         else
95                 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
96                                    TW_CHIP_OFFSET_ADDR(chip_id),
97                                    tw_off, val);
98 }
99
100 static void tw_write_and_verify(struct solo6010_dev *solo_dev, u8 addr, u8 off,
101                                 u8 val)
102 {
103         int i;
104
105         for (i = 0; i < 5; i++) {
106                 u8 rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, addr, off);
107                 if (rval == val)
108                         return;
109
110                 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, addr, off, val);
111                 msleep_interruptible(1);
112         }
113
114         printk("solo6010/tw28: Error writing register: %02x->%02x [%02x]\n",
115                 addr, off, val);
116 }
117
118 static int tw2864_setup(struct solo6010_dev *solo_dev, u8 dev_addr)
119 {
120         u8 tbl_tw2864_common[256];
121         int i;
122
123         memcpy(tbl_tw2864_common, tbl_tw2864_template,
124                sizeof(tbl_tw2864_common));
125
126         /* IRQ Mode */
127         if (solo_dev->nr_chans == 4) {
128                 tbl_tw2864_common[0xd2] = 0x01;
129                 tbl_tw2864_common[0xcf] = 0x00;
130         } else if (solo_dev->nr_chans == 8) {
131                 tbl_tw2864_common[0xd2] = 0x02;
132                 if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
133                         tbl_tw2864_common[0xcf] = 0x43;
134                 else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
135                         tbl_tw2864_common[0xcf] = 0x40;
136         } else if (solo_dev->nr_chans == 16) {
137                 tbl_tw2864_common[0xd2] = 0x03;
138                 if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
139                         tbl_tw2864_common[0xcf] = 0x43;
140                 else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
141                         tbl_tw2864_common[0xcf] = 0x43;
142                 else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
143                         tbl_tw2864_common[0xcf] = 0x43;
144                 else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
145                         tbl_tw2864_common[0xcf] = 0x40;
146         }
147
148         /* NTSC or PAL */
149         if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL) {
150                 for (i = 0; i < 4; i++) {
151                         tbl_tw2864_common[0x07 | (i << 4)] |= 0x10;
152                         tbl_tw2864_common[0x08 | (i << 4)] |= 0x06;
153                         tbl_tw2864_common[0x0a | (i << 4)] |= 0x08;
154                         tbl_tw2864_common[0x0b | (i << 4)] |= 0x13;
155                         tbl_tw2864_common[0x0e | (i << 4)] |= 0x01;
156                 }
157                 tbl_tw2864_common[0x9d] = 0x90;
158                 tbl_tw2864_common[0xf3] = 0x00;
159                 tbl_tw2864_common[0xf4] = 0xa0;
160         }
161
162         for (i = 0; i < 0xff; i++) {
163                 /* Skip read only registers */
164                 if (i >= 0xb8 && i <= 0xc1 )
165                         continue;
166                 if ((i & ~0x30) == 0x00 ||
167                     (i & ~0x30) == 0x0c ||
168                     (i & ~0x30) == 0x0d)
169                         continue;
170                 if (i == 0x74 || i == 0x77 || i == 0x78 ||
171                     i == 0x79 || i == 0x7a)
172                         continue;
173                 if (i == 0xfd)
174                         continue;
175
176                 tw_write_and_verify(solo_dev, dev_addr, i,
177                                     tbl_tw2864_common[i]);
178         }
179
180         return 0;
181 }
182
183 static int tw2815_setup(struct solo6010_dev *solo_dev, u8 dev_addr)
184 {
185         u8 tbl_ntsc_tw2815_common[] = {
186                 0x00, 0xc8, 0x20, 0xd0, 0x06, 0xf0, 0x08, 0x80,
187                 0x80, 0x80, 0x80, 0x02, 0x06, 0x00, 0x11,
188         };
189
190         u8 tbl_pal_tw2815_common[] = {
191                 0x00, 0x88, 0x20, 0xd0, 0x05, 0x20, 0x28, 0x80,
192                 0x80, 0x80, 0x80, 0x82, 0x06, 0x00, 0x11,
193         };
194
195         u8 tbl_tw2815_sfr[] = {
196                 0x00, 0x00, 0x00, 0xc0, 0x45, 0xa0, 0xd0, 0x2f, // 0x00
197                 0x64, 0x80, 0x80, 0x82, 0x82, 0x00, 0x00, 0x00,
198                 0x00, 0x0f, 0x05, 0x00, 0x00, 0x80, 0x06, 0x00, // 0x10
199                 0x00, 0x00, 0x00, 0xff, 0x8f, 0x00, 0x00, 0x00,
200                 0x88, 0x88, 0xc0, 0x00, 0x20, 0x64, 0xa8, 0xec, // 0x20
201                 0x31, 0x75, 0xb9, 0xfd, 0x00, 0x00, 0x88, 0x88,
202                 0x88, 0x11, 0x00, 0x88, 0x88, 0x00,             // 0x30
203         };
204         u8 *tbl_tw2815_common;
205         int i;
206         int ch;
207
208         tbl_ntsc_tw2815_common[0x06] = 0;
209
210         /* Horizontal Delay Control */
211         tbl_ntsc_tw2815_common[0x02] = DEFAULT_HDELAY_NTSC & 0xff;
212         tbl_ntsc_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_NTSC >> 8);
213
214         /* Horizontal Active Control */
215         tbl_ntsc_tw2815_common[0x03] = DEFAULT_HACTIVE_NTSC & 0xff;
216         tbl_ntsc_tw2815_common[0x06] |=
217                 ((0x03 & (DEFAULT_HACTIVE_NTSC >> 8)) << 2);
218
219         /* Vertical Delay Control */
220         tbl_ntsc_tw2815_common[0x04] = DEFAULT_VDELAY_NTSC & 0xff;
221         tbl_ntsc_tw2815_common[0x06] |=
222                 ((0x01 & (DEFAULT_VDELAY_NTSC >> 8)) << 4);
223
224         /* Vertical Active Control */
225         tbl_ntsc_tw2815_common[0x05] = DEFAULT_VACTIVE_NTSC & 0xff;
226         tbl_ntsc_tw2815_common[0x06] |=
227                 ((0x01 & (DEFAULT_VACTIVE_NTSC >> 8)) << 5);
228
229         tbl_pal_tw2815_common[0x06] = 0;
230
231         /* Horizontal Delay Control */
232         tbl_pal_tw2815_common[0x02] = DEFAULT_HDELAY_PAL & 0xff;
233         tbl_pal_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_PAL >> 8);
234
235         /* Horizontal Active Control */
236         tbl_pal_tw2815_common[0x03] = DEFAULT_HACTIVE_PAL & 0xff;
237         tbl_pal_tw2815_common[0x06] |=
238                 ((0x03 & (DEFAULT_HACTIVE_PAL >> 8)) << 2);
239
240         /* Vertical Delay Control */
241         tbl_pal_tw2815_common[0x04] = DEFAULT_VDELAY_PAL & 0xff;
242         tbl_pal_tw2815_common[0x06] |=
243                 ((0x01 & (DEFAULT_VDELAY_PAL >> 8)) << 4);
244
245         /* Vertical Active Control */
246         tbl_pal_tw2815_common[0x05] = DEFAULT_VACTIVE_PAL & 0xff;
247         tbl_pal_tw2815_common[0x06] |=
248                 ((0x01 & (DEFAULT_VACTIVE_PAL >> 8)) << 5);
249
250         tbl_tw2815_common =
251             (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) ?
252              tbl_ntsc_tw2815_common : tbl_pal_tw2815_common;
253
254         /* Dual ITU-R BT.656 format */
255         tbl_tw2815_common[0x0d] |= 0x04;
256
257         /* Audio configuration */
258         tbl_tw2815_sfr[0x62 - 0x40] &= ~(3 << 6);
259
260         if (solo_dev->nr_chans == 4) {
261                 tbl_tw2815_sfr[0x63 - 0x40] |= 1;
262                 tbl_tw2815_sfr[0x62 - 0x40] |= 3 << 6;
263         } else if (solo_dev->nr_chans == 8) {
264                 tbl_tw2815_sfr[0x63 - 0x40] |= 2;
265                 if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
266                         tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6;
267                 else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
268                         tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6;
269         } else if (solo_dev->nr_chans == 16) {
270                 tbl_tw2815_sfr[0x63 - 0x40] |= 3;
271                 if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
272                         tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6;
273                 else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
274                         tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6;
275                 else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
276                         tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6;
277                 else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
278                         tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6;
279         }
280
281         /* Output mode of R_ADATM pin (0 mixing, 1 record) */
282         /* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */
283
284         /* 8KHz, used to be 16KHz, but changed for remote client compat */
285         tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 2;
286         tbl_tw2815_sfr[0x6c - 0x40] |= 0 << 2;
287
288         /* Playback of right channel */
289         tbl_tw2815_sfr[0x6c - 0x40] |= 1 << 5;
290
291         /* Reserved value (XXX ??) */
292         tbl_tw2815_sfr[0x5c - 0x40] |= 1 << 5;
293
294         /* Analog output gain and mix ratio playback on full */
295         tbl_tw2815_sfr[0x70 - 0x40] |= 0xff;
296         /* Select playback audio and mute all except */
297         tbl_tw2815_sfr[0x71 - 0x40] |= 0x10;
298         tbl_tw2815_sfr[0x6d - 0x40] |= 0x0f;
299
300         /* End of audio configuration */
301
302         for (ch = 0; ch < 4; ch++) {
303                 tbl_tw2815_common[0x0d] &= ~3;
304                 switch (ch) {
305                 case 0:
306                         tbl_tw2815_common[0x0d] |= 0x21;
307                         break;
308                 case 1:
309                         tbl_tw2815_common[0x0d] |= 0x20;
310                         break;
311                 case 2:
312                         tbl_tw2815_common[0x0d] |= 0x23;
313                         break;
314                 case 3:
315                         tbl_tw2815_common[0x0d] |= 0x22;
316                         break;
317                 }
318
319                 for (i = 0; i < 0x0f; i++) {
320                         if (i == 0x00)
321                                 continue;       // read-only
322                         solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
323                                            dev_addr, (ch * 0x10) + i,
324                                            tbl_tw2815_common[i]);
325                 }
326         }
327
328         for (i = 0x40; i < 0x76; i++) {
329                 /* Skip read-only and nop registers */
330                 if (i == 0x40 || i == 0x59 || i == 0x5a ||
331                     i == 0x5d || i == 0x5e || i == 0x5f)
332                         continue;
333
334                 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, dev_addr, i,
335                                        tbl_tw2815_sfr[i - 0x40]);
336         }
337
338         return 0;
339 }
340
341 #define FIRST_ACTIVE_LINE       0x0008
342 #define LAST_ACTIVE_LINE        0x0102
343
344 static void saa7128_setup(struct solo6010_dev *solo_dev)
345 {
346         int i;
347         unsigned char regs[128] = {
348                 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
349                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
350                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
351                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
352                 0x1C, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00,
353                 0x59, 0x1d, 0x75, 0x3f, 0x06, 0x3f, 0x00, 0x00,
354                 0x1c, 0x33, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00,
355                 0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
356                 0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18,
357                 0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f,
358                 0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06,
359                 0x02, 0x80, 0x71, 0x77, 0xa7, 0x67, 0x66, 0x2e,
360                 0x7b, 0x11, 0x4f, 0x1f, 0x7c, 0xf0, 0x21, 0x77,
361                 0x41, 0x88, 0x41, 0x12, 0xed, 0x10, 0x10, 0x00,
362                 0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00,
363                 0x00, 0x00, 0x08, 0xff, 0x80, 0x00, 0xff, 0xff,
364         };
365
366         regs[0x7A] = FIRST_ACTIVE_LINE & 0xff;
367         regs[0x7B] = LAST_ACTIVE_LINE & 0xff;
368         regs[0x7C] = ((1 << 7) |
369                         (((LAST_ACTIVE_LINE >> 8) & 1) << 6) |
370                         (((FIRST_ACTIVE_LINE >> 8) & 1) << 4));
371
372         /* PAL: XXX: We could do a second set of regs to avoid this */
373         if (solo_dev->video_type != SOLO_VO_FMT_TYPE_NTSC) {
374                 regs[0x28] = 0xE1;
375
376                 regs[0x5A] = 0x0F;
377                 regs[0x61] = 0x02;
378                 regs[0x62] = 0x35;
379                 regs[0x63] = 0xCB;
380                 regs[0x64] = 0x8A;
381                 regs[0x65] = 0x09;
382                 regs[0x66] = 0x2A;
383
384                 regs[0x6C] = 0xf1;
385                 regs[0x6E] = 0x20;
386
387                 regs[0x7A] = 0x06 + 12;
388                 regs[0x7b] = 0x24 + 12;
389                 regs[0x7c] |= 1 << 6;
390         }
391
392         /* First 0x25 bytes are read-only? */
393         for (i = 0x26; i < 128; i++) {
394                 if (i == 0x60 || i == 0x7D)
395                         continue;
396                 solo_i2c_writebyte(solo_dev, SOLO_I2C_SAA, 0x46, i, regs[i]);
397         }
398
399         return;
400 }
401
402 int solo_tw28_init(struct solo6010_dev *solo_dev)
403 {
404         int i;
405         u8 value;
406
407         /* Detect techwell chip type */
408         for (i = 0; i < TW_NUM_CHIP; i++) {
409                 value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
410                                           TW_CHIP_OFFSET_ADDR(i), 0xFF);
411
412                 switch (value >> 3) {
413                 case 0x18:
414                         printk("solo6010: 2865 support not enabled\n");
415                         return -EINVAL;
416                         break;
417                 case 0x0c:
418                         solo_dev->tw2864 |= 1 << i;
419                         solo_dev->tw28_cnt++;
420                         break;
421                 default:
422                         value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
423                                                   TW_CHIP_OFFSET_ADDR(i), 0x59);
424                         if ((value >> 3) == 0x04) {
425                                 solo_dev->tw2815 |= 1 << i;
426                                 solo_dev->tw28_cnt++;
427                         }
428                 }
429         }
430
431         if (!solo_dev->tw28_cnt)
432                 return -EINVAL;
433
434         saa7128_setup(solo_dev);
435
436         for (i = 0; i < solo_dev->tw28_cnt; i++) {
437                 if ((solo_dev->tw2864 & (1 << i)))
438                         tw2864_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
439                 else
440                         tw2815_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
441         }
442
443         dev_info(&solo_dev->pdev->dev, "Initialized %d tw28xx chip%s:",
444                  solo_dev->tw28_cnt, solo_dev->tw28_cnt == 1 ? "" : "s");
445
446         if (solo_dev->tw2864)
447                 printk(" tw2864[%d]", hweight32(solo_dev->tw2864));
448         if (solo_dev->tw2815)
449                 printk(" tw2815[%d]", hweight32(solo_dev->tw2815));
450         printk("\n");
451
452         return 0;
453 }
454
455 /* 
456  * We accessed the video status signal in the Techwell chip through
457  * iic/i2c because the video status reported by register REG_VI_STATUS1
458  * (address 0x012C) of the SOLO6010 chip doesn't give the correct video
459  * status signal values.
460  */
461 int tw28_get_video_status(struct solo6010_dev *solo_dev, u8 ch)
462 {
463         u8 val, chip_num;
464
465         /* Get the right chip and on-chip channel */
466         chip_num = ch / 4;
467         ch %= 4;
468
469         val = tw_readbyte(solo_dev, chip_num, TW286X_AV_STAT_ADDR,
470                           TW_AV_STAT_ADDR) & 0x0f;
471
472         return val & (1 << ch) ? 1 : 0;
473 }
474
475 #if 0
476 /* Status of audio from up to 4 techwell chips are combined into 1 variable.
477  * See techwell datasheet for details. */
478 u16 tw28_get_audio_status(struct solo6010_dev *solo_dev)
479 {
480         u8 val;
481         u16 status = 0;
482         int i;
483
484         for (i = 0; i < solo_dev->tw28_cnt; i++) {
485                 val = (tw_readbyte(solo_dev, i, TW286X_AV_STAT_ADDR,
486                                    TW_AV_STAT_ADDR) & 0xf0) >> 4;
487                 status |= val << (i * 4);
488         }
489
490         return status;
491 }
492 #endif
493
494 int tw28_set_ctrl_val(struct solo6010_dev *solo_dev, u32 ctrl, u8 ch,
495                       s32 val)
496 {
497         char sval;
498         u8 chip_num;
499
500         /* Get the right chip and on-chip channel */
501         chip_num = ch / 4;
502         ch %= 4;
503
504         if (val > 255 || val < 0)
505                 return -ERANGE;
506
507         switch (ctrl) {
508         case V4L2_CID_SHARPNESS:
509                 /* Only 286x has sharpness */
510                 if (val > 0x0f || val < 0)
511                         return -ERANGE;
512                 if (is_tw286x(solo_dev, chip_num)) {
513                         u8 v = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
514                                                  TW_CHIP_OFFSET_ADDR(chip_num),
515                                                  TW286x_SHARPNESS(chip_num));
516                         v &= 0xf0;
517                         v |= val;
518                         solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
519                                            TW_CHIP_OFFSET_ADDR(chip_num),
520                                            TW286x_SHARPNESS(chip_num), v);
521                 } else if (val != 0)
522                         return -ERANGE;
523                 break;
524
525         case V4L2_CID_HUE:
526                 if (is_tw286x(solo_dev, chip_num))
527                         sval = val - 128;
528                 else
529                         sval = (char)val;
530                 tw_writebyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
531                              TW_HUE_ADDR(ch), sval);
532
533                 break;
534
535         case V4L2_CID_SATURATION:
536                 if (is_tw286x(solo_dev, chip_num)) {
537                         solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
538                                            TW_CHIP_OFFSET_ADDR(chip_num),
539                                            TW286x_SATURATIONU_ADDR(ch), val);
540                 }
541                 tw_writebyte(solo_dev, chip_num, TW286x_SATURATIONV_ADDR(ch),
542                              TW_SATURATION_ADDR(ch), val);
543
544                 break;
545
546         case V4L2_CID_CONTRAST:
547                 tw_writebyte(solo_dev, chip_num, TW286x_CONTRAST_ADDR(ch),
548                              TW_CONTRAST_ADDR(ch), val);
549                 break;
550
551         case V4L2_CID_BRIGHTNESS:
552                 if (is_tw286x(solo_dev, chip_num))
553                         sval = val - 128;
554                 else
555                         sval = (char)val;
556                 tw_writebyte(solo_dev, chip_num, TW286x_BRIGHTNESS_ADDR(ch),
557                              TW_BRIGHTNESS_ADDR(ch), sval);
558
559                 break;
560         default:
561                 return -EINVAL;
562         }
563
564         return 0;
565 }
566
567 int tw28_get_ctrl_val(struct solo6010_dev *solo_dev, u32 ctrl, u8 ch,
568                       s32 *val)
569 {
570         u8 rval, chip_num;
571
572         /* Get the right chip and on-chip channel */
573         chip_num = ch / 4;
574         ch %= 4;
575
576         switch (ctrl) {
577         case V4L2_CID_SHARPNESS:
578                 /* Only 286x has sharpness */
579                 if (is_tw286x(solo_dev, chip_num)) {
580                         rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
581                                                  TW_CHIP_OFFSET_ADDR(chip_num),
582                                                  TW286x_SHARPNESS(chip_num));
583                         *val = rval & 0x0f;
584                 } else
585                         *val = 0;
586                 break;
587         case V4L2_CID_HUE:
588                 rval = tw_readbyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
589                                    TW_HUE_ADDR(ch));
590                 if (is_tw286x(solo_dev, chip_num))
591                         *val = (s32)((char)rval) + 128;
592                 else
593                         *val = rval;
594                 break;
595         case V4L2_CID_SATURATION:
596                 *val = tw_readbyte(solo_dev, chip_num,
597                                    TW286x_SATURATIONU_ADDR(ch),
598                                    TW_SATURATION_ADDR(ch));
599                 break;
600         case V4L2_CID_CONTRAST:
601                 *val = tw_readbyte(solo_dev, chip_num,
602                                    TW286x_CONTRAST_ADDR(ch),
603                                    TW_CONTRAST_ADDR(ch));
604                 break;
605         case V4L2_CID_BRIGHTNESS:
606                 rval = tw_readbyte(solo_dev, chip_num,
607                                    TW286x_BRIGHTNESS_ADDR(ch),
608                                    TW_BRIGHTNESS_ADDR(ch));
609                 if (is_tw286x(solo_dev, chip_num)) 
610                         *val = (s32)((char)rval) + 128;
611                 else
612                         *val = rval;
613                 break;
614         default:
615                 return -EINVAL;
616         }
617
618         return 0;
619 }
620
621 #if 0
622 /*
623  * For audio output volume, the output channel is only 1. In this case we
624  * don't need to offset TW_CHIP_OFFSET_ADDR. The TW_CHIP_OFFSET_ADDR used
625  * is the base address of the techwell chip.
626  */
627 void tw2815_Set_AudioOutVol(struct solo6010_dev *solo_dev, unsigned int u_val)
628 {
629         unsigned int val;
630         unsigned int chip_num;
631
632         chip_num = (solo_dev->nr_chans - 1) / 4;
633
634         val = tw_readbyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
635                           TW_AUDIO_OUTPUT_VOL_ADDR);
636
637         u_val = (val & 0x0f) | (u_val << 4);
638
639         tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
640                      TW_AUDIO_OUTPUT_VOL_ADDR, u_val);
641 }
642 #endif
643
644 u8 tw28_get_audio_gain(struct solo6010_dev *solo_dev, u8 ch)
645 {
646         u8 val;
647         u8 chip_num;
648
649         /* Get the right chip and on-chip channel */
650         chip_num = ch / 4;
651         ch %= 4;
652
653         val = tw_readbyte(solo_dev, chip_num,
654                           TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
655                           TW_AUDIO_INPUT_GAIN_ADDR(ch));
656
657         return (ch % 2) ? (val >> 4) : (val & 0x0f);
658 }
659
660 void tw28_set_audio_gain(struct solo6010_dev *solo_dev, u8 ch, u8 val)
661 {
662         u8 old_val;
663         u8 chip_num;
664
665         /* Get the right chip and on-chip channel */
666         chip_num = ch / 4;
667         ch %= 4;
668
669         old_val = tw_readbyte(solo_dev, chip_num,
670                               TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
671                               TW_AUDIO_INPUT_GAIN_ADDR(ch));
672
673         val = (old_val & ((ch % 2) ? 0x0f : 0xf0)) |
674                 ((ch % 2) ? (val << 4) : val);
675
676         tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
677                      TW_AUDIO_INPUT_GAIN_ADDR(ch), val);
678 }