2 * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
3 * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
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.
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.
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.
20 #include <linux/kernel.h>
23 #include "solo6010-tw28.h"
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)
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)
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,
72 #define is_tw286x(__solo, __id) (!(__solo->tw2815 & (1 << __id)))
74 static u8 tw_readbyte(struct solo6010_dev *solo_dev, int chip_id, u8 tw6x_off,
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),
82 return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
83 TW_CHIP_OFFSET_ADDR(chip_id),
87 static void tw_writebyte(struct solo6010_dev *solo_dev, int chip_id,
88 u8 tw6x_off, u8 tw_off, u8 val)
90 if (is_tw286x(solo_dev, chip_id))
91 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
92 TW_CHIP_OFFSET_ADDR(chip_id),
95 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
96 TW_CHIP_OFFSET_ADDR(chip_id),
100 static void tw_write_and_verify(struct solo6010_dev *solo_dev, u8 addr, u8 off,
105 for (i = 0; i < 5; i++) {
106 u8 rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, addr, off);
110 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, addr, off, val);
111 msleep_interruptible(1);
114 printk("solo6010/tw28: Error writing register: %02x->%02x [%02x]\n",
118 static int tw2864_setup(struct solo6010_dev *solo_dev, u8 dev_addr)
120 u8 tbl_tw2864_common[256];
123 memcpy(tbl_tw2864_common, tbl_tw2864_template,
124 sizeof(tbl_tw2864_common));
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;
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;
157 tbl_tw2864_common[0x9d] = 0x90;
158 tbl_tw2864_common[0xf3] = 0x00;
159 tbl_tw2864_common[0xf4] = 0xa0;
162 for (i = 0; i < 0xff; i++) {
163 /* Skip read only registers */
164 if (i >= 0xb8 && i <= 0xc1 )
166 if ((i & ~0x30) == 0x00 ||
167 (i & ~0x30) == 0x0c ||
170 if (i == 0x74 || i == 0x77 || i == 0x78 ||
171 i == 0x79 || i == 0x7a)
176 tw_write_and_verify(solo_dev, dev_addr, i,
177 tbl_tw2864_common[i]);
183 static int tw2815_setup(struct solo6010_dev *solo_dev, u8 dev_addr)
185 u8 tbl_ntsc_tw2815_common[] = {
186 0x00, 0xc8, 0x20, 0xd0, 0x06, 0xf0, 0x08, 0x80,
187 0x80, 0x80, 0x80, 0x02, 0x06, 0x00, 0x11,
190 u8 tbl_pal_tw2815_common[] = {
191 0x00, 0x88, 0x20, 0xd0, 0x05, 0x20, 0x28, 0x80,
192 0x80, 0x80, 0x80, 0x82, 0x06, 0x00, 0x11,
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
204 u8 *tbl_tw2815_common;
208 tbl_ntsc_tw2815_common[0x06] = 0;
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);
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);
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);
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);
229 tbl_pal_tw2815_common[0x06] = 0;
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);
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);
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);
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);
251 (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) ?
252 tbl_ntsc_tw2815_common : tbl_pal_tw2815_common;
254 /* Dual ITU-R BT.656 format */
255 tbl_tw2815_common[0x0d] |= 0x04;
257 /* Audio configuration */
258 tbl_tw2815_sfr[0x62 - 0x40] &= ~(3 << 6);
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;
281 /* Output mode of R_ADATM pin (0 mixing, 1 record) */
282 /* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */
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;
288 /* Playback of right channel */
289 tbl_tw2815_sfr[0x6c - 0x40] |= 1 << 5;
291 /* Reserved value (XXX ??) */
292 tbl_tw2815_sfr[0x5c - 0x40] |= 1 << 5;
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;
300 /* End of audio configuration */
302 for (ch = 0; ch < 4; ch++) {
303 tbl_tw2815_common[0x0d] &= ~3;
306 tbl_tw2815_common[0x0d] |= 0x21;
309 tbl_tw2815_common[0x0d] |= 0x20;
312 tbl_tw2815_common[0x0d] |= 0x23;
315 tbl_tw2815_common[0x0d] |= 0x22;
319 for (i = 0; i < 0x0f; i++) {
321 continue; // read-only
322 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
323 dev_addr, (ch * 0x10) + i,
324 tbl_tw2815_common[i]);
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)
334 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, dev_addr, i,
335 tbl_tw2815_sfr[i - 0x40]);
341 #define FIRST_ACTIVE_LINE 0x0008
342 #define LAST_ACTIVE_LINE 0x0102
344 static void saa7128_setup(struct solo6010_dev *solo_dev)
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,
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));
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) {
387 regs[0x7A] = 0x06 + 12;
388 regs[0x7b] = 0x24 + 12;
389 regs[0x7c] |= 1 << 6;
392 /* First 0x25 bytes are read-only? */
393 for (i = 0x26; i < 128; i++) {
394 if (i == 0x60 || i == 0x7D)
396 solo_i2c_writebyte(solo_dev, SOLO_I2C_SAA, 0x46, i, regs[i]);
402 int solo_tw28_init(struct solo6010_dev *solo_dev)
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);
412 switch (value >> 3) {
414 printk("solo6010: 2865 support not enabled\n");
418 solo_dev->tw2864 |= 1 << i;
419 solo_dev->tw28_cnt++;
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++;
431 if (!solo_dev->tw28_cnt)
434 saa7128_setup(solo_dev);
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));
440 tw2815_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
443 dev_info(&solo_dev->pdev->dev, "Initialized %d tw28xx chip%s:",
444 solo_dev->tw28_cnt, solo_dev->tw28_cnt == 1 ? "" : "s");
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));
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.
461 int tw28_get_video_status(struct solo6010_dev *solo_dev, u8 ch)
465 /* Get the right chip and on-chip channel */
469 val = tw_readbyte(solo_dev, chip_num, TW286X_AV_STAT_ADDR,
470 TW_AV_STAT_ADDR) & 0x0f;
472 return val & (1 << ch) ? 1 : 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)
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);
494 int tw28_set_ctrl_val(struct solo6010_dev *solo_dev, u32 ctrl, u8 ch,
500 /* Get the right chip and on-chip channel */
504 if (val > 255 || val < 0)
508 case V4L2_CID_SHARPNESS:
509 /* Only 286x has sharpness */
510 if (val > 0x0f || val < 0)
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));
518 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
519 TW_CHIP_OFFSET_ADDR(chip_num),
520 TW286x_SHARPNESS(chip_num), v);
526 if (is_tw286x(solo_dev, chip_num))
530 tw_writebyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
531 TW_HUE_ADDR(ch), sval);
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);
541 tw_writebyte(solo_dev, chip_num, TW286x_SATURATIONV_ADDR(ch),
542 TW_SATURATION_ADDR(ch), val);
546 case V4L2_CID_CONTRAST:
547 tw_writebyte(solo_dev, chip_num, TW286x_CONTRAST_ADDR(ch),
548 TW_CONTRAST_ADDR(ch), val);
551 case V4L2_CID_BRIGHTNESS:
552 if (is_tw286x(solo_dev, chip_num))
556 tw_writebyte(solo_dev, chip_num, TW286x_BRIGHTNESS_ADDR(ch),
557 TW_BRIGHTNESS_ADDR(ch), sval);
567 int tw28_get_ctrl_val(struct solo6010_dev *solo_dev, u32 ctrl, u8 ch,
572 /* Get the right chip and on-chip channel */
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));
588 rval = tw_readbyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
590 if (is_tw286x(solo_dev, chip_num))
591 *val = (s32)((char)rval) + 128;
595 case V4L2_CID_SATURATION:
596 *val = tw_readbyte(solo_dev, chip_num,
597 TW286x_SATURATIONU_ADDR(ch),
598 TW_SATURATION_ADDR(ch));
600 case V4L2_CID_CONTRAST:
601 *val = tw_readbyte(solo_dev, chip_num,
602 TW286x_CONTRAST_ADDR(ch),
603 TW_CONTRAST_ADDR(ch));
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;
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.
627 void tw2815_Set_AudioOutVol(struct solo6010_dev *solo_dev, unsigned int u_val)
630 unsigned int chip_num;
632 chip_num = (solo_dev->nr_chans - 1) / 4;
634 val = tw_readbyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
635 TW_AUDIO_OUTPUT_VOL_ADDR);
637 u_val = (val & 0x0f) | (u_val << 4);
639 tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
640 TW_AUDIO_OUTPUT_VOL_ADDR, u_val);
644 u8 tw28_get_audio_gain(struct solo6010_dev *solo_dev, u8 ch)
649 /* Get the right chip and on-chip channel */
653 val = tw_readbyte(solo_dev, chip_num,
654 TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
655 TW_AUDIO_INPUT_GAIN_ADDR(ch));
657 return (ch % 2) ? (val >> 4) : (val & 0x0f);
660 void tw28_set_audio_gain(struct solo6010_dev *solo_dev, u8 ch, u8 val)
665 /* Get the right chip and on-chip channel */
669 old_val = tw_readbyte(solo_dev, chip_num,
670 TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
671 TW_AUDIO_INPUT_GAIN_ADDR(ch));
673 val = (old_val & ((ch % 2) ? 0x0f : 0xf0)) |
674 ((ch % 2) ? (val << 4) : val);
676 tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
677 TW_AUDIO_INPUT_GAIN_ADDR(ch), val);