xref: /linux/drivers/media/pci/solo6x10/solo6x10-tw28.c (revision 4b4193256c8d3bc3a5397b5cd9494c2ad386317d)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
228cae868SHans Verkuil /*
3*cf293a4fSAlexander A. Klimov  * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com>
428cae868SHans Verkuil  *
528cae868SHans Verkuil  * Original author:
628cae868SHans Verkuil  * Ben Collins <bcollins@ubuntu.com>
728cae868SHans Verkuil  *
828cae868SHans Verkuil  * Additional work by:
928cae868SHans Verkuil  * John Brooks <john.brooks@bluecherry.net>
1028cae868SHans Verkuil  */
1128cae868SHans Verkuil 
1228cae868SHans Verkuil #include <linux/kernel.h>
1328cae868SHans Verkuil #include <linux/delay.h>
1428cae868SHans Verkuil 
1528cae868SHans Verkuil #include "solo6x10.h"
1628cae868SHans Verkuil #include "solo6x10-tw28.h"
1728cae868SHans Verkuil 
1828cae868SHans Verkuil #define DEFAULT_HDELAY_NTSC		(32 - 8)
1928cae868SHans Verkuil #define DEFAULT_HACTIVE_NTSC		(720 + 16)
2028cae868SHans Verkuil #define DEFAULT_VDELAY_NTSC		(7 - 2)
2128cae868SHans Verkuil #define DEFAULT_VACTIVE_NTSC		(240 + 4)
2228cae868SHans Verkuil 
2328cae868SHans Verkuil #define DEFAULT_HDELAY_PAL		(32 + 4)
2428cae868SHans Verkuil #define DEFAULT_HACTIVE_PAL		(864-DEFAULT_HDELAY_PAL)
2528cae868SHans Verkuil #define DEFAULT_VDELAY_PAL		(6)
2628cae868SHans Verkuil #define DEFAULT_VACTIVE_PAL		(312-DEFAULT_VDELAY_PAL)
2728cae868SHans Verkuil 
2828cae868SHans Verkuil 
2928cae868SHans Verkuil static const u8 tbl_tw2864_ntsc_template[] = {
3028cae868SHans Verkuil 	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */
3128cae868SHans Verkuil 	0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
3228cae868SHans Verkuil 	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */
3328cae868SHans Verkuil 	0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
3428cae868SHans Verkuil 	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */
3528cae868SHans Verkuil 	0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
3628cae868SHans Verkuil 	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x30 */
3728cae868SHans Verkuil 	0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
3828cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */
3928cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
4028cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
4128cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
4228cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
4328cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
4428cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */
4528cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00,
4628cae868SHans Verkuil 	0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
4728cae868SHans Verkuil 	0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00,
4828cae868SHans Verkuil 	0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */
4928cae868SHans Verkuil 	0x00, 0x28, 0x44, 0x44, 0xa0, 0x88, 0x5a, 0x01,
5028cae868SHans Verkuil 	0x08, 0x08, 0x08, 0x08, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */
5128cae868SHans Verkuil 	0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44,
5228cae868SHans Verkuil 	0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */
5328cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
5428cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
5528cae868SHans Verkuil 	0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00,
5628cae868SHans Verkuil 	0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */
5728cae868SHans Verkuil 	0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81,
5828cae868SHans Verkuil 	0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
5928cae868SHans Verkuil 	0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
6028cae868SHans Verkuil 	0x83, 0xb5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */
6128cae868SHans Verkuil 	0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00,
6228cae868SHans Verkuil };
6328cae868SHans Verkuil 
6428cae868SHans Verkuil static const u8 tbl_tw2864_pal_template[] = {
6528cae868SHans Verkuil 	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */
6628cae868SHans Verkuil 	0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
6728cae868SHans Verkuil 	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */
6828cae868SHans Verkuil 	0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
6928cae868SHans Verkuil 	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */
7028cae868SHans Verkuil 	0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
7128cae868SHans Verkuil 	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */
7228cae868SHans Verkuil 	0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
7328cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */
7428cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7528cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
7628cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7728cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
7828cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7928cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */
8028cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00,
8128cae868SHans Verkuil 	0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
8228cae868SHans Verkuil 	0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00,
8328cae868SHans Verkuil 	0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */
8428cae868SHans Verkuil 	0x00, 0x28, 0x44, 0x44, 0xa0, 0x90, 0x5a, 0x01,
8528cae868SHans Verkuil 	0x0a, 0x0a, 0x0a, 0x0a, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */
8628cae868SHans Verkuil 	0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44,
8728cae868SHans Verkuil 	0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */
8828cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8928cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
9028cae868SHans Verkuil 	0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00,
9128cae868SHans Verkuil 	0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */
9228cae868SHans Verkuil 	0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81,
9328cae868SHans Verkuil 	0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
9428cae868SHans Verkuil 	0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
9528cae868SHans Verkuil 	0x83, 0xb5, 0x09, 0x00, 0xa0, 0x00, 0x01, 0x20, /* 0xf0 */
9628cae868SHans Verkuil 	0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00,
9728cae868SHans Verkuil };
9828cae868SHans Verkuil 
9928cae868SHans Verkuil static const u8 tbl_tw2865_ntsc_template[] = {
10028cae868SHans Verkuil 	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */
10128cae868SHans Verkuil 	0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
10228cae868SHans Verkuil 	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */
10328cae868SHans Verkuil 	0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
10428cae868SHans Verkuil 	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */
10528cae868SHans Verkuil 	0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
10628cae868SHans Verkuil 	0x00, 0xf0, 0x70, 0x48, 0x80, 0x80, 0x00, 0x02, /* 0x30 */
10728cae868SHans Verkuil 	0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
10828cae868SHans Verkuil 	0x00, 0x00, 0x90, 0x68, 0x00, 0x38, 0x80, 0x80, /* 0x40 */
10928cae868SHans Verkuil 	0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00,
11028cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
11128cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
11228cae868SHans Verkuil 	0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
11328cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43,
11428cae868SHans Verkuil 	0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */
11528cae868SHans Verkuil 	0xE9, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80,
11628cae868SHans Verkuil 	0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
11728cae868SHans Verkuil 	0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00,
11828cae868SHans Verkuil 	0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */
11928cae868SHans Verkuil 	0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13,
12028cae868SHans Verkuil 	0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1B, 0x1A, /* 0xa0 */
12128cae868SHans Verkuil 	0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44,
12228cae868SHans Verkuil 	0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */
12328cae868SHans Verkuil 	0xFF, 0xE7, 0xE9, 0xE9, 0xEB, 0xFF, 0xD6, 0xD8,
12428cae868SHans Verkuil 	0xD8, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
12528cae868SHans Verkuil 	0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80,
12628cae868SHans Verkuil 	0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */
12728cae868SHans Verkuil 	0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81,
12828cae868SHans Verkuil 	0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
12928cae868SHans Verkuil 	0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
13028cae868SHans Verkuil 	0x83, 0xB5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */
13128cae868SHans Verkuil 	0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0,
13228cae868SHans Verkuil };
13328cae868SHans Verkuil 
13428cae868SHans Verkuil static const u8 tbl_tw2865_pal_template[] = {
13528cae868SHans Verkuil 	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */
13628cae868SHans Verkuil 	0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
13728cae868SHans Verkuil 	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */
13828cae868SHans Verkuil 	0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
13928cae868SHans Verkuil 	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */
14028cae868SHans Verkuil 	0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
14128cae868SHans Verkuil 	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */
14228cae868SHans Verkuil 	0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
14328cae868SHans Verkuil 	0x00, 0x94, 0x90, 0x48, 0x00, 0x38, 0x7F, 0x80, /* 0x40 */
14428cae868SHans Verkuil 	0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00,
14528cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
14628cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
14728cae868SHans Verkuil 	0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
14828cae868SHans Verkuil 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43,
14928cae868SHans Verkuil 	0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */
15028cae868SHans Verkuil 	0xEA, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80,
15128cae868SHans Verkuil 	0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
15228cae868SHans Verkuil 	0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00,
15328cae868SHans Verkuil 	0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */
15428cae868SHans Verkuil 	0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13,
15528cae868SHans Verkuil 	0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1A, 0x1A, /* 0xa0 */
15628cae868SHans Verkuil 	0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44,
15728cae868SHans Verkuil 	0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */
15828cae868SHans Verkuil 	0xFF, 0xE7, 0xE9, 0xE9, 0xE9, 0xFF, 0xD7, 0xD8,
15928cae868SHans Verkuil 	0xD9, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
16028cae868SHans Verkuil 	0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80,
16128cae868SHans Verkuil 	0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */
16228cae868SHans Verkuil 	0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81,
16328cae868SHans Verkuil 	0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
16428cae868SHans Verkuil 	0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
16528cae868SHans Verkuil 	0x83, 0xB5, 0x09, 0x00, 0xA0, 0x00, 0x01, 0x20, /* 0xf0 */
16628cae868SHans Verkuil 	0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0,
16728cae868SHans Verkuil };
16828cae868SHans Verkuil 
16928cae868SHans Verkuil #define is_tw286x(__solo, __id) (!(__solo->tw2815 & (1 << __id)))
17028cae868SHans Verkuil 
tw_readbyte(struct solo_dev * solo_dev,int chip_id,u8 tw6x_off,u8 tw_off)17128cae868SHans Verkuil static u8 tw_readbyte(struct solo_dev *solo_dev, int chip_id, u8 tw6x_off,
17228cae868SHans Verkuil 		      u8 tw_off)
17328cae868SHans Verkuil {
17428cae868SHans Verkuil 	if (is_tw286x(solo_dev, chip_id))
17528cae868SHans Verkuil 		return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
17628cae868SHans Verkuil 					 TW_CHIP_OFFSET_ADDR(chip_id),
17728cae868SHans Verkuil 					 tw6x_off);
17828cae868SHans Verkuil 	else
17928cae868SHans Verkuil 		return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
18028cae868SHans Verkuil 					 TW_CHIP_OFFSET_ADDR(chip_id),
18128cae868SHans Verkuil 					 tw_off);
18228cae868SHans Verkuil }
18328cae868SHans Verkuil 
tw_writebyte(struct solo_dev * solo_dev,int chip_id,u8 tw6x_off,u8 tw_off,u8 val)18428cae868SHans Verkuil static void tw_writebyte(struct solo_dev *solo_dev, int chip_id,
18528cae868SHans Verkuil 			 u8 tw6x_off, u8 tw_off, u8 val)
18628cae868SHans Verkuil {
18728cae868SHans Verkuil 	if (is_tw286x(solo_dev, chip_id))
18828cae868SHans Verkuil 		solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
18928cae868SHans Verkuil 				   TW_CHIP_OFFSET_ADDR(chip_id),
19028cae868SHans Verkuil 				   tw6x_off, val);
19128cae868SHans Verkuil 	else
19228cae868SHans Verkuil 		solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
19328cae868SHans Verkuil 				   TW_CHIP_OFFSET_ADDR(chip_id),
19428cae868SHans Verkuil 				   tw_off, val);
19528cae868SHans Verkuil }
19628cae868SHans Verkuil 
tw_write_and_verify(struct solo_dev * solo_dev,u8 addr,u8 off,u8 val)19728cae868SHans Verkuil static void tw_write_and_verify(struct solo_dev *solo_dev, u8 addr, u8 off,
19828cae868SHans Verkuil 				u8 val)
19928cae868SHans Verkuil {
20028cae868SHans Verkuil 	int i;
20128cae868SHans Verkuil 
20228cae868SHans Verkuil 	for (i = 0; i < 5; i++) {
20328cae868SHans Verkuil 		u8 rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, addr, off);
20428cae868SHans Verkuil 
20528cae868SHans Verkuil 		if (rval == val)
20628cae868SHans Verkuil 			return;
20728cae868SHans Verkuil 
20828cae868SHans Verkuil 		solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, addr, off, val);
20928cae868SHans Verkuil 		msleep_interruptible(1);
21028cae868SHans Verkuil 	}
21128cae868SHans Verkuil 
21228cae868SHans Verkuil /*	printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n", */
21328cae868SHans Verkuil /*		addr, off, val); */
21428cae868SHans Verkuil }
21528cae868SHans Verkuil 
tw2865_setup(struct solo_dev * solo_dev,u8 dev_addr)21628cae868SHans Verkuil static int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr)
21728cae868SHans Verkuil {
21828cae868SHans Verkuil 	u8 tbl_tw2865_common[256];
21928cae868SHans Verkuil 	int i;
22028cae868SHans Verkuil 
22128cae868SHans Verkuil 	if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL)
22228cae868SHans Verkuil 		memcpy(tbl_tw2865_common, tbl_tw2865_pal_template,
22328cae868SHans Verkuil 		       sizeof(tbl_tw2865_common));
22428cae868SHans Verkuil 	else
22528cae868SHans Verkuil 		memcpy(tbl_tw2865_common, tbl_tw2865_ntsc_template,
22628cae868SHans Verkuil 		       sizeof(tbl_tw2865_common));
22728cae868SHans Verkuil 
22828cae868SHans Verkuil 	/* ALINK Mode */
22928cae868SHans Verkuil 	if (solo_dev->nr_chans == 4) {
23028cae868SHans Verkuil 		tbl_tw2865_common[0xd2] = 0x01;
23128cae868SHans Verkuil 		tbl_tw2865_common[0xcf] = 0x00;
23228cae868SHans Verkuil 	} else if (solo_dev->nr_chans == 8) {
23328cae868SHans Verkuil 		tbl_tw2865_common[0xd2] = 0x02;
23428cae868SHans Verkuil 		if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
23528cae868SHans Verkuil 			tbl_tw2865_common[0xcf] = 0x80;
23628cae868SHans Verkuil 	} else if (solo_dev->nr_chans == 16) {
23728cae868SHans Verkuil 		tbl_tw2865_common[0xd2] = 0x03;
23828cae868SHans Verkuil 		if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
23928cae868SHans Verkuil 			tbl_tw2865_common[0xcf] = 0x83;
24028cae868SHans Verkuil 		else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
24128cae868SHans Verkuil 			tbl_tw2865_common[0xcf] = 0x83;
24228cae868SHans Verkuil 		else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
24328cae868SHans Verkuil 			tbl_tw2865_common[0xcf] = 0x80;
24428cae868SHans Verkuil 	}
24528cae868SHans Verkuil 
24628cae868SHans Verkuil 	for (i = 0; i < 0xff; i++) {
24728cae868SHans Verkuil 		/* Skip read only registers */
24828cae868SHans Verkuil 		switch (i) {
24928cae868SHans Verkuil 		case 0xb8 ... 0xc1:
25028cae868SHans Verkuil 		case 0xc4 ... 0xc7:
25128cae868SHans Verkuil 		case 0xfd:
25228cae868SHans Verkuil 			continue;
25328cae868SHans Verkuil 		}
25428cae868SHans Verkuil 		switch (i & ~0x30) {
25528cae868SHans Verkuil 		case 0x00:
25628cae868SHans Verkuil 		case 0x0c ... 0x0d:
25728cae868SHans Verkuil 			continue;
25828cae868SHans Verkuil 		}
25928cae868SHans Verkuil 
26028cae868SHans Verkuil 		tw_write_and_verify(solo_dev, dev_addr, i,
26128cae868SHans Verkuil 				    tbl_tw2865_common[i]);
26228cae868SHans Verkuil 	}
26328cae868SHans Verkuil 
26428cae868SHans Verkuil 	return 0;
26528cae868SHans Verkuil }
26628cae868SHans Verkuil 
tw2864_setup(struct solo_dev * solo_dev,u8 dev_addr)26728cae868SHans Verkuil static int tw2864_setup(struct solo_dev *solo_dev, u8 dev_addr)
26828cae868SHans Verkuil {
26928cae868SHans Verkuil 	u8 tbl_tw2864_common[256];
27028cae868SHans Verkuil 	int i;
27128cae868SHans Verkuil 
27228cae868SHans Verkuil 	if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL)
27328cae868SHans Verkuil 		memcpy(tbl_tw2864_common, tbl_tw2864_pal_template,
27428cae868SHans Verkuil 		       sizeof(tbl_tw2864_common));
27528cae868SHans Verkuil 	else
27628cae868SHans Verkuil 		memcpy(tbl_tw2864_common, tbl_tw2864_ntsc_template,
27728cae868SHans Verkuil 		       sizeof(tbl_tw2864_common));
27828cae868SHans Verkuil 
27928cae868SHans Verkuil 	if (solo_dev->tw2865 == 0) {
28028cae868SHans Verkuil 		/* IRQ Mode */
28128cae868SHans Verkuil 		if (solo_dev->nr_chans == 4) {
28228cae868SHans Verkuil 			tbl_tw2864_common[0xd2] = 0x01;
28328cae868SHans Verkuil 			tbl_tw2864_common[0xcf] = 0x00;
28428cae868SHans Verkuil 		} else if (solo_dev->nr_chans == 8) {
28528cae868SHans Verkuil 			tbl_tw2864_common[0xd2] = 0x02;
28628cae868SHans Verkuil 			if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
28728cae868SHans Verkuil 				tbl_tw2864_common[0xcf] = 0x43;
28828cae868SHans Verkuil 			else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
28928cae868SHans Verkuil 				tbl_tw2864_common[0xcf] = 0x40;
29028cae868SHans Verkuil 		} else if (solo_dev->nr_chans == 16) {
29128cae868SHans Verkuil 			tbl_tw2864_common[0xd2] = 0x03;
29228cae868SHans Verkuil 			if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
29328cae868SHans Verkuil 				tbl_tw2864_common[0xcf] = 0x43;
29428cae868SHans Verkuil 			else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
29528cae868SHans Verkuil 				tbl_tw2864_common[0xcf] = 0x43;
29628cae868SHans Verkuil 			else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
29728cae868SHans Verkuil 				tbl_tw2864_common[0xcf] = 0x43;
29828cae868SHans Verkuil 			else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
29928cae868SHans Verkuil 				tbl_tw2864_common[0xcf] = 0x40;
30028cae868SHans Verkuil 		}
30128cae868SHans Verkuil 	} else {
30228cae868SHans Verkuil 		/* ALINK Mode. Assumes that the first tw28xx is a
30328cae868SHans Verkuil 		 * 2865 and these are in cascade. */
30428cae868SHans Verkuil 		for (i = 0; i <= 4; i++)
30528cae868SHans Verkuil 			tbl_tw2864_common[0x08 | i << 4] = 0x12;
30628cae868SHans Verkuil 
30728cae868SHans Verkuil 		if (solo_dev->nr_chans == 8) {
30828cae868SHans Verkuil 			tbl_tw2864_common[0xd2] = 0x02;
30928cae868SHans Verkuil 			if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
31028cae868SHans Verkuil 				tbl_tw2864_common[0xcf] = 0x80;
31128cae868SHans Verkuil 		} else if (solo_dev->nr_chans == 16) {
31228cae868SHans Verkuil 			tbl_tw2864_common[0xd2] = 0x03;
31328cae868SHans Verkuil 			if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
31428cae868SHans Verkuil 				tbl_tw2864_common[0xcf] = 0x83;
31528cae868SHans Verkuil 			else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
31628cae868SHans Verkuil 				tbl_tw2864_common[0xcf] = 0x83;
31728cae868SHans Verkuil 			else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
31828cae868SHans Verkuil 				tbl_tw2864_common[0xcf] = 0x80;
31928cae868SHans Verkuil 		}
32028cae868SHans Verkuil 	}
32128cae868SHans Verkuil 
32228cae868SHans Verkuil 	for (i = 0; i < 0xff; i++) {
32328cae868SHans Verkuil 		/* Skip read only registers */
32428cae868SHans Verkuil 		switch (i) {
32528cae868SHans Verkuil 		case 0xb8 ... 0xc1:
32628cae868SHans Verkuil 		case 0xfd:
32728cae868SHans Verkuil 			continue;
32828cae868SHans Verkuil 		}
32928cae868SHans Verkuil 		switch (i & ~0x30) {
33028cae868SHans Verkuil 		case 0x00:
33128cae868SHans Verkuil 		case 0x0c:
33228cae868SHans Verkuil 		case 0x0d:
33328cae868SHans Verkuil 			continue;
33428cae868SHans Verkuil 		}
33528cae868SHans Verkuil 
33628cae868SHans Verkuil 		tw_write_and_verify(solo_dev, dev_addr, i,
33728cae868SHans Verkuil 				    tbl_tw2864_common[i]);
33828cae868SHans Verkuil 	}
33928cae868SHans Verkuil 
34028cae868SHans Verkuil 	return 0;
34128cae868SHans Verkuil }
34228cae868SHans Verkuil 
tw2815_setup(struct solo_dev * solo_dev,u8 dev_addr)34328cae868SHans Verkuil static int tw2815_setup(struct solo_dev *solo_dev, u8 dev_addr)
34428cae868SHans Verkuil {
34528cae868SHans Verkuil 	u8 tbl_ntsc_tw2815_common[] = {
34628cae868SHans Verkuil 		0x00, 0xc8, 0x20, 0xd0, 0x06, 0xf0, 0x08, 0x80,
34728cae868SHans Verkuil 		0x80, 0x80, 0x80, 0x02, 0x06, 0x00, 0x11,
34828cae868SHans Verkuil 	};
34928cae868SHans Verkuil 
35028cae868SHans Verkuil 	u8 tbl_pal_tw2815_common[] = {
35128cae868SHans Verkuil 		0x00, 0x88, 0x20, 0xd0, 0x05, 0x20, 0x28, 0x80,
35228cae868SHans Verkuil 		0x80, 0x80, 0x80, 0x82, 0x06, 0x00, 0x11,
35328cae868SHans Verkuil 	};
35428cae868SHans Verkuil 
35528cae868SHans Verkuil 	u8 tbl_tw2815_sfr[] = {
35628cae868SHans Verkuil 		0x00, 0x00, 0x00, 0xc0, 0x45, 0xa0, 0xd0, 0x2f, /* 0x00 */
35728cae868SHans Verkuil 		0x64, 0x80, 0x80, 0x82, 0x82, 0x00, 0x00, 0x00,
35828cae868SHans Verkuil 		0x00, 0x0f, 0x05, 0x00, 0x00, 0x80, 0x06, 0x00, /* 0x10 */
35928cae868SHans Verkuil 		0x00, 0x00, 0x00, 0xff, 0x8f, 0x00, 0x00, 0x00,
36028cae868SHans Verkuil 		0x88, 0x88, 0xc0, 0x00, 0x20, 0x64, 0xa8, 0xec, /* 0x20 */
36128cae868SHans Verkuil 		0x31, 0x75, 0xb9, 0xfd, 0x00, 0x00, 0x88, 0x88,
36228cae868SHans Verkuil 		0x88, 0x11, 0x00, 0x88, 0x88, 0x00,		/* 0x30 */
36328cae868SHans Verkuil 	};
36428cae868SHans Verkuil 	u8 *tbl_tw2815_common;
36528cae868SHans Verkuil 	int i;
36628cae868SHans Verkuil 	int ch;
36728cae868SHans Verkuil 
36828cae868SHans Verkuil 	tbl_ntsc_tw2815_common[0x06] = 0;
36928cae868SHans Verkuil 
37028cae868SHans Verkuil 	/* Horizontal Delay Control */
37128cae868SHans Verkuil 	tbl_ntsc_tw2815_common[0x02] = DEFAULT_HDELAY_NTSC & 0xff;
37228cae868SHans Verkuil 	tbl_ntsc_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_NTSC >> 8);
37328cae868SHans Verkuil 
37428cae868SHans Verkuil 	/* Horizontal Active Control */
37528cae868SHans Verkuil 	tbl_ntsc_tw2815_common[0x03] = DEFAULT_HACTIVE_NTSC & 0xff;
37628cae868SHans Verkuil 	tbl_ntsc_tw2815_common[0x06] |=
37728cae868SHans Verkuil 		((0x03 & (DEFAULT_HACTIVE_NTSC >> 8)) << 2);
37828cae868SHans Verkuil 
37928cae868SHans Verkuil 	/* Vertical Delay Control */
38028cae868SHans Verkuil 	tbl_ntsc_tw2815_common[0x04] = DEFAULT_VDELAY_NTSC & 0xff;
38128cae868SHans Verkuil 	tbl_ntsc_tw2815_common[0x06] |=
38228cae868SHans Verkuil 		((0x01 & (DEFAULT_VDELAY_NTSC >> 8)) << 4);
38328cae868SHans Verkuil 
38428cae868SHans Verkuil 	/* Vertical Active Control */
38528cae868SHans Verkuil 	tbl_ntsc_tw2815_common[0x05] = DEFAULT_VACTIVE_NTSC & 0xff;
38628cae868SHans Verkuil 	tbl_ntsc_tw2815_common[0x06] |=
38728cae868SHans Verkuil 		((0x01 & (DEFAULT_VACTIVE_NTSC >> 8)) << 5);
38828cae868SHans Verkuil 
38928cae868SHans Verkuil 	tbl_pal_tw2815_common[0x06] = 0;
39028cae868SHans Verkuil 
39128cae868SHans Verkuil 	/* Horizontal Delay Control */
39228cae868SHans Verkuil 	tbl_pal_tw2815_common[0x02] = DEFAULT_HDELAY_PAL & 0xff;
39328cae868SHans Verkuil 	tbl_pal_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_PAL >> 8);
39428cae868SHans Verkuil 
39528cae868SHans Verkuil 	/* Horizontal Active Control */
39628cae868SHans Verkuil 	tbl_pal_tw2815_common[0x03] = DEFAULT_HACTIVE_PAL & 0xff;
39728cae868SHans Verkuil 	tbl_pal_tw2815_common[0x06] |=
39828cae868SHans Verkuil 		((0x03 & (DEFAULT_HACTIVE_PAL >> 8)) << 2);
39928cae868SHans Verkuil 
40028cae868SHans Verkuil 	/* Vertical Delay Control */
40128cae868SHans Verkuil 	tbl_pal_tw2815_common[0x04] = DEFAULT_VDELAY_PAL & 0xff;
40228cae868SHans Verkuil 	tbl_pal_tw2815_common[0x06] |=
40328cae868SHans Verkuil 		((0x01 & (DEFAULT_VDELAY_PAL >> 8)) << 4);
40428cae868SHans Verkuil 
40528cae868SHans Verkuil 	/* Vertical Active Control */
40628cae868SHans Verkuil 	tbl_pal_tw2815_common[0x05] = DEFAULT_VACTIVE_PAL & 0xff;
40728cae868SHans Verkuil 	tbl_pal_tw2815_common[0x06] |=
40828cae868SHans Verkuil 		((0x01 & (DEFAULT_VACTIVE_PAL >> 8)) << 5);
40928cae868SHans Verkuil 
41028cae868SHans Verkuil 	tbl_tw2815_common =
41128cae868SHans Verkuil 	    (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) ?
41228cae868SHans Verkuil 	     tbl_ntsc_tw2815_common : tbl_pal_tw2815_common;
41328cae868SHans Verkuil 
41428cae868SHans Verkuil 	/* Dual ITU-R BT.656 format */
41528cae868SHans Verkuil 	tbl_tw2815_common[0x0d] |= 0x04;
41628cae868SHans Verkuil 
41728cae868SHans Verkuil 	/* Audio configuration */
41828cae868SHans Verkuil 	tbl_tw2815_sfr[0x62 - 0x40] &= ~(3 << 6);
41928cae868SHans Verkuil 
42028cae868SHans Verkuil 	if (solo_dev->nr_chans == 4) {
42128cae868SHans Verkuil 		tbl_tw2815_sfr[0x63 - 0x40] |= 1;
42228cae868SHans Verkuil 		tbl_tw2815_sfr[0x62 - 0x40] |= 3 << 6;
42328cae868SHans Verkuil 	} else if (solo_dev->nr_chans == 8) {
42428cae868SHans Verkuil 		tbl_tw2815_sfr[0x63 - 0x40] |= 2;
42528cae868SHans Verkuil 		if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
42628cae868SHans Verkuil 			tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6;
42728cae868SHans Verkuil 		else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
42828cae868SHans Verkuil 			tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6;
42928cae868SHans Verkuil 	} else if (solo_dev->nr_chans == 16) {
43028cae868SHans Verkuil 		tbl_tw2815_sfr[0x63 - 0x40] |= 3;
43128cae868SHans Verkuil 		if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
43228cae868SHans Verkuil 			tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6;
43328cae868SHans Verkuil 		else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
43428cae868SHans Verkuil 			tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6;
43528cae868SHans Verkuil 		else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
43628cae868SHans Verkuil 			tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6;
43728cae868SHans Verkuil 		else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
43828cae868SHans Verkuil 			tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6;
43928cae868SHans Verkuil 	}
44028cae868SHans Verkuil 
44128cae868SHans Verkuil 	/* Output mode of R_ADATM pin (0 mixing, 1 record) */
44228cae868SHans Verkuil 	/* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */
44328cae868SHans Verkuil 
44428cae868SHans Verkuil 	/* 8KHz, used to be 16KHz, but changed for remote client compat */
44528cae868SHans Verkuil 	tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 2;
44628cae868SHans Verkuil 	tbl_tw2815_sfr[0x6c - 0x40] |= 0 << 2;
44728cae868SHans Verkuil 
44828cae868SHans Verkuil 	/* Playback of right channel */
44928cae868SHans Verkuil 	tbl_tw2815_sfr[0x6c - 0x40] |= 1 << 5;
45028cae868SHans Verkuil 
45128cae868SHans Verkuil 	/* Reserved value (XXX ??) */
45228cae868SHans Verkuil 	tbl_tw2815_sfr[0x5c - 0x40] |= 1 << 5;
45328cae868SHans Verkuil 
45428cae868SHans Verkuil 	/* Analog output gain and mix ratio playback on full */
45528cae868SHans Verkuil 	tbl_tw2815_sfr[0x70 - 0x40] |= 0xff;
45628cae868SHans Verkuil 	/* Select playback audio and mute all except */
45728cae868SHans Verkuil 	tbl_tw2815_sfr[0x71 - 0x40] |= 0x10;
45828cae868SHans Verkuil 	tbl_tw2815_sfr[0x6d - 0x40] |= 0x0f;
45928cae868SHans Verkuil 
46028cae868SHans Verkuil 	/* End of audio configuration */
46128cae868SHans Verkuil 
46228cae868SHans Verkuil 	for (ch = 0; ch < 4; ch++) {
46328cae868SHans Verkuil 		tbl_tw2815_common[0x0d] &= ~3;
46428cae868SHans Verkuil 		switch (ch) {
46528cae868SHans Verkuil 		case 0:
46628cae868SHans Verkuil 			tbl_tw2815_common[0x0d] |= 0x21;
46728cae868SHans Verkuil 			break;
46828cae868SHans Verkuil 		case 1:
46928cae868SHans Verkuil 			tbl_tw2815_common[0x0d] |= 0x20;
47028cae868SHans Verkuil 			break;
47128cae868SHans Verkuil 		case 2:
47228cae868SHans Verkuil 			tbl_tw2815_common[0x0d] |= 0x23;
47328cae868SHans Verkuil 			break;
47428cae868SHans Verkuil 		case 3:
47528cae868SHans Verkuil 			tbl_tw2815_common[0x0d] |= 0x22;
47628cae868SHans Verkuil 			break;
47728cae868SHans Verkuil 		}
47828cae868SHans Verkuil 
47928cae868SHans Verkuil 		for (i = 0; i < 0x0f; i++) {
48028cae868SHans Verkuil 			if (i == 0x00)
48128cae868SHans Verkuil 				continue;	/* read-only */
48228cae868SHans Verkuil 			solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
48328cae868SHans Verkuil 					   dev_addr, (ch * 0x10) + i,
48428cae868SHans Verkuil 					   tbl_tw2815_common[i]);
48528cae868SHans Verkuil 		}
48628cae868SHans Verkuil 	}
48728cae868SHans Verkuil 
48828cae868SHans Verkuil 	for (i = 0x40; i < 0x76; i++) {
48928cae868SHans Verkuil 		/* Skip read-only and nop registers */
49028cae868SHans Verkuil 		if (i == 0x40 || i == 0x59 || i == 0x5a ||
49128cae868SHans Verkuil 		    i == 0x5d || i == 0x5e || i == 0x5f)
49228cae868SHans Verkuil 			continue;
49328cae868SHans Verkuil 
49428cae868SHans Verkuil 		solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, dev_addr, i,
49528cae868SHans Verkuil 				       tbl_tw2815_sfr[i - 0x40]);
49628cae868SHans Verkuil 	}
49728cae868SHans Verkuil 
49828cae868SHans Verkuil 	return 0;
49928cae868SHans Verkuil }
50028cae868SHans Verkuil 
50128cae868SHans Verkuil #define FIRST_ACTIVE_LINE	0x0008
50228cae868SHans Verkuil #define LAST_ACTIVE_LINE	0x0102
50328cae868SHans Verkuil 
saa712x_write_regs(struct solo_dev * dev,const u8 * vals,int start,int n)504af29ba97SIsmael Luceno static void saa712x_write_regs(struct solo_dev *dev, const u8 *vals,
50528cae868SHans Verkuil 		int start, int n)
50628cae868SHans Verkuil {
50728cae868SHans Verkuil 	for (; start < n; start++, vals++) {
50828cae868SHans Verkuil 		/* Skip read-only registers */
50928cae868SHans Verkuil 		switch (start) {
51028cae868SHans Verkuil 		/* case 0x00 ... 0x25: */
51128cae868SHans Verkuil 		case 0x2e ... 0x37:
51228cae868SHans Verkuil 		case 0x60:
51328cae868SHans Verkuil 		case 0x7d:
51428cae868SHans Verkuil 			continue;
51528cae868SHans Verkuil 		}
51628cae868SHans Verkuil 		solo_i2c_writebyte(dev, SOLO_I2C_SAA, 0x46, start, *vals);
51728cae868SHans Verkuil 	}
51828cae868SHans Verkuil }
51928cae868SHans Verkuil 
52028cae868SHans Verkuil #define SAA712x_reg7c (0x80 | ((LAST_ACTIVE_LINE & 0x100) >> 2) \
52128cae868SHans Verkuil 		| ((FIRST_ACTIVE_LINE & 0x100) >> 4))
52228cae868SHans Verkuil 
saa712x_setup(struct solo_dev * dev)52328cae868SHans Verkuil static void saa712x_setup(struct solo_dev *dev)
52428cae868SHans Verkuil {
52528cae868SHans Verkuil 	const int reg_start = 0x26;
526d7552a1eSColin Ian King 	static const u8 saa7128_regs_ntsc[] = {
52728cae868SHans Verkuil 	/* :0x26 */
52828cae868SHans Verkuil 		0x0d, 0x00,
52928cae868SHans Verkuil 	/* :0x28 */
53028cae868SHans Verkuil 		0x59, 0x1d, 0x75, 0x3f, 0x06, 0x3f,
53128cae868SHans Verkuil 	/* :0x2e XXX: read-only */
53228cae868SHans Verkuil 		0x00, 0x00,
53328cae868SHans Verkuil 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
53428cae868SHans Verkuil 	/* :0x38 */
53528cae868SHans Verkuil 		0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
53628cae868SHans Verkuil 	/* :0x40 */
53728cae868SHans Verkuil 		0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18,
53828cae868SHans Verkuil 		0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f,
53928cae868SHans Verkuil 	/* :0x50 */
54028cae868SHans Verkuil 		0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06,
54128cae868SHans Verkuil 		0x02, 0x80, 0x71, 0x77, 0xa7, 0x67, 0x66, 0x2e,
54228cae868SHans Verkuil 	/* :0x60 */
54328cae868SHans Verkuil 		0x7b, 0x11, 0x4f, 0x1f, 0x7c, 0xf0, 0x21, 0x77,
54428cae868SHans Verkuil 		0x41, 0x88, 0x41, 0x52, 0xed, 0x10, 0x10, 0x00,
54528cae868SHans Verkuil 	/* :0x70 */
54628cae868SHans Verkuil 		0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00,
54728cae868SHans Verkuil 		0x00, 0x00, FIRST_ACTIVE_LINE, LAST_ACTIVE_LINE & 0xff,
54828cae868SHans Verkuil 		SAA712x_reg7c, 0x00, 0xff, 0xff,
54928cae868SHans Verkuil 	}, saa7128_regs_pal[] = {
55028cae868SHans Verkuil 	/* :0x26 */
55128cae868SHans Verkuil 		0x0d, 0x00,
55228cae868SHans Verkuil 	/* :0x28 */
55328cae868SHans Verkuil 		0xe1, 0x1d, 0x75, 0x3f, 0x06, 0x3f,
55428cae868SHans Verkuil 	/* :0x2e XXX: read-only */
55528cae868SHans Verkuil 		0x00, 0x00,
55628cae868SHans Verkuil 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
55728cae868SHans Verkuil 	/* :0x38 */
55828cae868SHans Verkuil 		0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
55928cae868SHans Verkuil 	/* :0x40 */
56028cae868SHans Verkuil 		0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18,
56128cae868SHans Verkuil 		0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f,
56228cae868SHans Verkuil 	/* :0x50 */
56328cae868SHans Verkuil 		0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06,
56428cae868SHans Verkuil 		0x02, 0x80, 0x0f, 0x77, 0xa7, 0x67, 0x66, 0x2e,
56528cae868SHans Verkuil 	/* :0x60 */
56628cae868SHans Verkuil 		0x7b, 0x02, 0x35, 0xcb, 0x8a, 0x09, 0x2a, 0x77,
56728cae868SHans Verkuil 		0x41, 0x88, 0x41, 0x52, 0xf1, 0x10, 0x20, 0x00,
56828cae868SHans Verkuil 	/* :0x70 */
56928cae868SHans Verkuil 		0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00,
57028cae868SHans Verkuil 		0x00, 0x00, 0x12, 0x30,
57128cae868SHans Verkuil 		SAA712x_reg7c | 0x40, 0x00, 0xff, 0xff,
57228cae868SHans Verkuil 	};
57328cae868SHans Verkuil 
57428cae868SHans Verkuil 	if (dev->video_type == SOLO_VO_FMT_TYPE_PAL)
57528cae868SHans Verkuil 		saa712x_write_regs(dev, saa7128_regs_pal, reg_start,
57628cae868SHans Verkuil 				sizeof(saa7128_regs_pal));
57728cae868SHans Verkuil 	else
57828cae868SHans Verkuil 		saa712x_write_regs(dev, saa7128_regs_ntsc, reg_start,
57928cae868SHans Verkuil 				sizeof(saa7128_regs_ntsc));
58028cae868SHans Verkuil }
58128cae868SHans Verkuil 
solo_tw28_init(struct solo_dev * solo_dev)58228cae868SHans Verkuil int solo_tw28_init(struct solo_dev *solo_dev)
58328cae868SHans Verkuil {
58428cae868SHans Verkuil 	int i;
58528cae868SHans Verkuil 	u8 value;
58628cae868SHans Verkuil 
58728cae868SHans Verkuil 	solo_dev->tw28_cnt = 0;
58828cae868SHans Verkuil 
58928cae868SHans Verkuil 	/* Detect techwell chip type(s) */
59028cae868SHans Verkuil 	for (i = 0; i < solo_dev->nr_chans / 4; i++) {
59128cae868SHans Verkuil 		value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
59228cae868SHans Verkuil 					  TW_CHIP_OFFSET_ADDR(i), 0xFF);
59328cae868SHans Verkuil 
59428cae868SHans Verkuil 		switch (value >> 3) {
59528cae868SHans Verkuil 		case 0x18:
59628cae868SHans Verkuil 			solo_dev->tw2865 |= 1 << i;
59728cae868SHans Verkuil 			solo_dev->tw28_cnt++;
59828cae868SHans Verkuil 			break;
59928cae868SHans Verkuil 		case 0x0c:
6003361d54eSAnton Sviridenko 		case 0x0d:
60128cae868SHans Verkuil 			solo_dev->tw2864 |= 1 << i;
60228cae868SHans Verkuil 			solo_dev->tw28_cnt++;
60328cae868SHans Verkuil 			break;
60428cae868SHans Verkuil 		default:
60528cae868SHans Verkuil 			value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
60628cae868SHans Verkuil 						  TW_CHIP_OFFSET_ADDR(i),
60728cae868SHans Verkuil 						  0x59);
60828cae868SHans Verkuil 			if ((value >> 3) == 0x04) {
60928cae868SHans Verkuil 				solo_dev->tw2815 |= 1 << i;
61028cae868SHans Verkuil 				solo_dev->tw28_cnt++;
61128cae868SHans Verkuil 			}
61228cae868SHans Verkuil 		}
61328cae868SHans Verkuil 	}
61428cae868SHans Verkuil 
61528cae868SHans Verkuil 	if (solo_dev->tw28_cnt != (solo_dev->nr_chans >> 2)) {
61628cae868SHans Verkuil 		dev_err(&solo_dev->pdev->dev,
61728cae868SHans Verkuil 			"Could not initialize any techwell chips\n");
61828cae868SHans Verkuil 		return -EINVAL;
61928cae868SHans Verkuil 	}
62028cae868SHans Verkuil 
62128cae868SHans Verkuil 	saa712x_setup(solo_dev);
62228cae868SHans Verkuil 
62328cae868SHans Verkuil 	for (i = 0; i < solo_dev->tw28_cnt; i++) {
62428cae868SHans Verkuil 		if ((solo_dev->tw2865 & (1 << i)))
62528cae868SHans Verkuil 			tw2865_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
62628cae868SHans Verkuil 		else if ((solo_dev->tw2864 & (1 << i)))
62728cae868SHans Verkuil 			tw2864_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
62828cae868SHans Verkuil 		else
62928cae868SHans Verkuil 			tw2815_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
63028cae868SHans Verkuil 	}
63128cae868SHans Verkuil 
63228cae868SHans Verkuil 	return 0;
63328cae868SHans Verkuil }
63428cae868SHans Verkuil 
63528cae868SHans Verkuil /*
63628cae868SHans Verkuil  * We accessed the video status signal in the Techwell chip through
63728cae868SHans Verkuil  * iic/i2c because the video status reported by register REG_VI_STATUS1
63828cae868SHans Verkuil  * (address 0x012C) of the SOLO6010 chip doesn't give the correct video
63928cae868SHans Verkuil  * status signal values.
64028cae868SHans Verkuil  */
tw28_get_video_status(struct solo_dev * solo_dev,u8 ch)64128cae868SHans Verkuil int tw28_get_video_status(struct solo_dev *solo_dev, u8 ch)
64228cae868SHans Verkuil {
64328cae868SHans Verkuil 	u8 val, chip_num;
64428cae868SHans Verkuil 
64528cae868SHans Verkuil 	/* Get the right chip and on-chip channel */
64628cae868SHans Verkuil 	chip_num = ch / 4;
64728cae868SHans Verkuil 	ch %= 4;
64828cae868SHans Verkuil 
64928cae868SHans Verkuil 	val = tw_readbyte(solo_dev, chip_num, TW286x_AV_STAT_ADDR,
65028cae868SHans Verkuil 			  TW_AV_STAT_ADDR) & 0x0f;
65128cae868SHans Verkuil 
65228cae868SHans Verkuil 	return val & (1 << ch) ? 1 : 0;
65328cae868SHans Verkuil }
65428cae868SHans Verkuil 
65528cae868SHans Verkuil #if 0
65628cae868SHans Verkuil /* Status of audio from up to 4 techwell chips are combined into 1 variable.
65728cae868SHans Verkuil  * See techwell datasheet for details. */
65828cae868SHans Verkuil u16 tw28_get_audio_status(struct solo_dev *solo_dev)
65928cae868SHans Verkuil {
66028cae868SHans Verkuil 	u8 val;
66128cae868SHans Verkuil 	u16 status = 0;
66228cae868SHans Verkuil 	int i;
66328cae868SHans Verkuil 
66428cae868SHans Verkuil 	for (i = 0; i < solo_dev->tw28_cnt; i++) {
66528cae868SHans Verkuil 		val = (tw_readbyte(solo_dev, i, TW286x_AV_STAT_ADDR,
66628cae868SHans Verkuil 				   TW_AV_STAT_ADDR) & 0xf0) >> 4;
66728cae868SHans Verkuil 		status |= val << (i * 4);
66828cae868SHans Verkuil 	}
66928cae868SHans Verkuil 
67028cae868SHans Verkuil 	return status;
67128cae868SHans Verkuil }
67228cae868SHans Verkuil #endif
67328cae868SHans Verkuil 
tw28_has_sharpness(struct solo_dev * solo_dev,u8 ch)67428cae868SHans Verkuil bool tw28_has_sharpness(struct solo_dev *solo_dev, u8 ch)
67528cae868SHans Verkuil {
67628cae868SHans Verkuil 	return is_tw286x(solo_dev, ch / 4);
67728cae868SHans Verkuil }
67828cae868SHans Verkuil 
tw28_set_ctrl_val(struct solo_dev * solo_dev,u32 ctrl,u8 ch,s32 val)67928cae868SHans Verkuil int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch,
68028cae868SHans Verkuil 		      s32 val)
68128cae868SHans Verkuil {
68228cae868SHans Verkuil 	char sval;
68328cae868SHans Verkuil 	u8 chip_num;
68428cae868SHans Verkuil 
68528cae868SHans Verkuil 	/* Get the right chip and on-chip channel */
68628cae868SHans Verkuil 	chip_num = ch / 4;
68728cae868SHans Verkuil 	ch %= 4;
68828cae868SHans Verkuil 
68928cae868SHans Verkuil 	if (val > 255 || val < 0)
69028cae868SHans Verkuil 		return -ERANGE;
69128cae868SHans Verkuil 
69228cae868SHans Verkuil 	switch (ctrl) {
69328cae868SHans Verkuil 	case V4L2_CID_SHARPNESS:
69428cae868SHans Verkuil 		/* Only 286x has sharpness */
69528cae868SHans Verkuil 		if (is_tw286x(solo_dev, chip_num)) {
69628cae868SHans Verkuil 			u8 v = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
69728cae868SHans Verkuil 						 TW_CHIP_OFFSET_ADDR(chip_num),
69828cae868SHans Verkuil 						 TW286x_SHARPNESS(chip_num));
69928cae868SHans Verkuil 			v &= 0xf0;
70028cae868SHans Verkuil 			v |= val;
70128cae868SHans Verkuil 			solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
70228cae868SHans Verkuil 					   TW_CHIP_OFFSET_ADDR(chip_num),
70328cae868SHans Verkuil 					   TW286x_SHARPNESS(chip_num), v);
70428cae868SHans Verkuil 		} else {
70528cae868SHans Verkuil 			return -EINVAL;
70628cae868SHans Verkuil 		}
70728cae868SHans Verkuil 		break;
70828cae868SHans Verkuil 
70928cae868SHans Verkuil 	case V4L2_CID_HUE:
71028cae868SHans Verkuil 		if (is_tw286x(solo_dev, chip_num))
71128cae868SHans Verkuil 			sval = val - 128;
71228cae868SHans Verkuil 		else
71328cae868SHans Verkuil 			sval = (char)val;
71428cae868SHans Verkuil 		tw_writebyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
71528cae868SHans Verkuil 			     TW_HUE_ADDR(ch), sval);
71628cae868SHans Verkuil 
71728cae868SHans Verkuil 		break;
71828cae868SHans Verkuil 
71928cae868SHans Verkuil 	case V4L2_CID_SATURATION:
72028cae868SHans Verkuil 		/* 286x chips have a U and V component for saturation */
72128cae868SHans Verkuil 		if (is_tw286x(solo_dev, chip_num)) {
72228cae868SHans Verkuil 			solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
72328cae868SHans Verkuil 					   TW_CHIP_OFFSET_ADDR(chip_num),
72428cae868SHans Verkuil 					   TW286x_SATURATIONU_ADDR(ch), val);
72528cae868SHans Verkuil 		}
72628cae868SHans Verkuil 		tw_writebyte(solo_dev, chip_num, TW286x_SATURATIONV_ADDR(ch),
72728cae868SHans Verkuil 			     TW_SATURATION_ADDR(ch), val);
72828cae868SHans Verkuil 
72928cae868SHans Verkuil 		break;
73028cae868SHans Verkuil 
73128cae868SHans Verkuil 	case V4L2_CID_CONTRAST:
73228cae868SHans Verkuil 		tw_writebyte(solo_dev, chip_num, TW286x_CONTRAST_ADDR(ch),
73328cae868SHans Verkuil 			     TW_CONTRAST_ADDR(ch), val);
73428cae868SHans Verkuil 		break;
73528cae868SHans Verkuil 
73628cae868SHans Verkuil 	case V4L2_CID_BRIGHTNESS:
73728cae868SHans Verkuil 		if (is_tw286x(solo_dev, chip_num))
73828cae868SHans Verkuil 			sval = val - 128;
73928cae868SHans Verkuil 		else
74028cae868SHans Verkuil 			sval = (char)val;
74128cae868SHans Verkuil 		tw_writebyte(solo_dev, chip_num, TW286x_BRIGHTNESS_ADDR(ch),
74228cae868SHans Verkuil 			     TW_BRIGHTNESS_ADDR(ch), sval);
74328cae868SHans Verkuil 
74428cae868SHans Verkuil 		break;
74528cae868SHans Verkuil 	default:
74628cae868SHans Verkuil 		return -EINVAL;
74728cae868SHans Verkuil 	}
74828cae868SHans Verkuil 
74928cae868SHans Verkuil 	return 0;
75028cae868SHans Verkuil }
75128cae868SHans Verkuil 
tw28_get_ctrl_val(struct solo_dev * solo_dev,u32 ctrl,u8 ch,s32 * val)75228cae868SHans Verkuil int tw28_get_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch,
75328cae868SHans Verkuil 		      s32 *val)
75428cae868SHans Verkuil {
75528cae868SHans Verkuil 	u8 rval, chip_num;
75628cae868SHans Verkuil 
75728cae868SHans Verkuil 	/* Get the right chip and on-chip channel */
75828cae868SHans Verkuil 	chip_num = ch / 4;
75928cae868SHans Verkuil 	ch %= 4;
76028cae868SHans Verkuil 
76128cae868SHans Verkuil 	switch (ctrl) {
76228cae868SHans Verkuil 	case V4L2_CID_SHARPNESS:
76328cae868SHans Verkuil 		/* Only 286x has sharpness */
76428cae868SHans Verkuil 		if (is_tw286x(solo_dev, chip_num)) {
76528cae868SHans Verkuil 			rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
76628cae868SHans Verkuil 						 TW_CHIP_OFFSET_ADDR(chip_num),
76728cae868SHans Verkuil 						 TW286x_SHARPNESS(chip_num));
76828cae868SHans Verkuil 			*val = rval & 0x0f;
76928cae868SHans Verkuil 		} else
77028cae868SHans Verkuil 			*val = 0;
77128cae868SHans Verkuil 		break;
77228cae868SHans Verkuil 	case V4L2_CID_HUE:
77328cae868SHans Verkuil 		rval = tw_readbyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
77428cae868SHans Verkuil 				   TW_HUE_ADDR(ch));
77528cae868SHans Verkuil 		if (is_tw286x(solo_dev, chip_num))
77628cae868SHans Verkuil 			*val = (s32)((char)rval) + 128;
77728cae868SHans Verkuil 		else
77828cae868SHans Verkuil 			*val = rval;
77928cae868SHans Verkuil 		break;
78028cae868SHans Verkuil 	case V4L2_CID_SATURATION:
78128cae868SHans Verkuil 		*val = tw_readbyte(solo_dev, chip_num,
78228cae868SHans Verkuil 				   TW286x_SATURATIONU_ADDR(ch),
78328cae868SHans Verkuil 				   TW_SATURATION_ADDR(ch));
78428cae868SHans Verkuil 		break;
78528cae868SHans Verkuil 	case V4L2_CID_CONTRAST:
78628cae868SHans Verkuil 		*val = tw_readbyte(solo_dev, chip_num,
78728cae868SHans Verkuil 				   TW286x_CONTRAST_ADDR(ch),
78828cae868SHans Verkuil 				   TW_CONTRAST_ADDR(ch));
78928cae868SHans Verkuil 		break;
79028cae868SHans Verkuil 	case V4L2_CID_BRIGHTNESS:
79128cae868SHans Verkuil 		rval = tw_readbyte(solo_dev, chip_num,
79228cae868SHans Verkuil 				   TW286x_BRIGHTNESS_ADDR(ch),
79328cae868SHans Verkuil 				   TW_BRIGHTNESS_ADDR(ch));
79428cae868SHans Verkuil 		if (is_tw286x(solo_dev, chip_num))
79528cae868SHans Verkuil 			*val = (s32)((char)rval) + 128;
79628cae868SHans Verkuil 		else
79728cae868SHans Verkuil 			*val = rval;
79828cae868SHans Verkuil 		break;
79928cae868SHans Verkuil 	default:
80028cae868SHans Verkuil 		return -EINVAL;
80128cae868SHans Verkuil 	}
80228cae868SHans Verkuil 
80328cae868SHans Verkuil 	return 0;
80428cae868SHans Verkuil }
80528cae868SHans Verkuil 
80628cae868SHans Verkuil #if 0
80728cae868SHans Verkuil /*
80828cae868SHans Verkuil  * For audio output volume, the output channel is only 1. In this case we
80928cae868SHans Verkuil  * don't need to offset TW_CHIP_OFFSET_ADDR. The TW_CHIP_OFFSET_ADDR used
81028cae868SHans Verkuil  * is the base address of the techwell chip.
81128cae868SHans Verkuil  */
81228cae868SHans Verkuil void tw2815_Set_AudioOutVol(struct solo_dev *solo_dev, unsigned int u_val)
81328cae868SHans Verkuil {
81428cae868SHans Verkuil 	unsigned int val;
81528cae868SHans Verkuil 	unsigned int chip_num;
81628cae868SHans Verkuil 
81728cae868SHans Verkuil 	chip_num = (solo_dev->nr_chans - 1) / 4;
81828cae868SHans Verkuil 
81928cae868SHans Verkuil 	val = tw_readbyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
82028cae868SHans Verkuil 			  TW_AUDIO_OUTPUT_VOL_ADDR);
82128cae868SHans Verkuil 
82228cae868SHans Verkuil 	u_val = (val & 0x0f) | (u_val << 4);
82328cae868SHans Verkuil 
82428cae868SHans Verkuil 	tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
82528cae868SHans Verkuil 		     TW_AUDIO_OUTPUT_VOL_ADDR, u_val);
82628cae868SHans Verkuil }
82728cae868SHans Verkuil #endif
82828cae868SHans Verkuil 
tw28_get_audio_gain(struct solo_dev * solo_dev,u8 ch)82928cae868SHans Verkuil u8 tw28_get_audio_gain(struct solo_dev *solo_dev, u8 ch)
83028cae868SHans Verkuil {
83128cae868SHans Verkuil 	u8 val;
83228cae868SHans Verkuil 	u8 chip_num;
83328cae868SHans Verkuil 
83428cae868SHans Verkuil 	/* Get the right chip and on-chip channel */
83528cae868SHans Verkuil 	chip_num = ch / 4;
83628cae868SHans Verkuil 	ch %= 4;
83728cae868SHans Verkuil 
83828cae868SHans Verkuil 	val = tw_readbyte(solo_dev, chip_num,
83928cae868SHans Verkuil 			  TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
84028cae868SHans Verkuil 			  TW_AUDIO_INPUT_GAIN_ADDR(ch));
84128cae868SHans Verkuil 
84228cae868SHans Verkuil 	return (ch % 2) ? (val >> 4) : (val & 0x0f);
84328cae868SHans Verkuil }
84428cae868SHans Verkuil 
tw28_set_audio_gain(struct solo_dev * solo_dev,u8 ch,u8 val)84528cae868SHans Verkuil void tw28_set_audio_gain(struct solo_dev *solo_dev, u8 ch, u8 val)
84628cae868SHans Verkuil {
84728cae868SHans Verkuil 	u8 old_val;
84828cae868SHans Verkuil 	u8 chip_num;
84928cae868SHans Verkuil 
85028cae868SHans Verkuil 	/* Get the right chip and on-chip channel */
85128cae868SHans Verkuil 	chip_num = ch / 4;
85228cae868SHans Verkuil 	ch %= 4;
85328cae868SHans Verkuil 
85428cae868SHans Verkuil 	old_val = tw_readbyte(solo_dev, chip_num,
85528cae868SHans Verkuil 			      TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
85628cae868SHans Verkuil 			      TW_AUDIO_INPUT_GAIN_ADDR(ch));
85728cae868SHans Verkuil 
85828cae868SHans Verkuil 	val = (old_val & ((ch % 2) ? 0x0f : 0xf0)) |
85928cae868SHans Verkuil 		((ch % 2) ? (val << 4) : val);
86028cae868SHans Verkuil 
86128cae868SHans Verkuil 	tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
86228cae868SHans Verkuil 		     TW_AUDIO_INPUT_GAIN_ADDR(ch), val);
86328cae868SHans Verkuil }
864