2 * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
5 * Ben Collins <bcollins@ubuntu.com>
8 * John Brooks <john.brooks@bluecherry.net>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 #include <linux/kernel.h>
26 #include <linux/delay.h>
29 #include "solo6x10-tw28.h"
31 #define DEFAULT_HDELAY_NTSC (32 - 8)
32 #define DEFAULT_HACTIVE_NTSC (720 + 16)
33 #define DEFAULT_VDELAY_NTSC (7 - 2)
34 #define DEFAULT_VACTIVE_NTSC (240 + 4)
36 #define DEFAULT_HDELAY_PAL (32 + 4)
37 #define DEFAULT_HACTIVE_PAL (864-DEFAULT_HDELAY_PAL)
38 #define DEFAULT_VDELAY_PAL (6)
39 #define DEFAULT_VACTIVE_PAL (312-DEFAULT_VDELAY_PAL)
42 static const u8 tbl_tw2864_ntsc_template[] = {
43 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */
44 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
45 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */
46 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
47 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */
48 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
49 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x30 */
50 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
51 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */
52 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
53 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
54 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
55 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
56 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
57 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */
58 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00,
59 0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
60 0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00,
61 0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */
62 0x00, 0x28, 0x44, 0x44, 0xa0, 0x88, 0x5a, 0x01,
63 0x08, 0x08, 0x08, 0x08, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */
64 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44,
65 0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */
66 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
67 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
68 0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00,
69 0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */
70 0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81,
71 0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
72 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
73 0x83, 0xb5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */
74 0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00,
77 static const u8 tbl_tw2864_pal_template[] = {
78 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */
79 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
80 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */
81 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
82 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */
83 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
84 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */
85 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
86 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */
87 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
88 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
89 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
90 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
91 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
92 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */
93 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00,
94 0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
95 0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00,
96 0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */
97 0x00, 0x28, 0x44, 0x44, 0xa0, 0x90, 0x5a, 0x01,
98 0x0a, 0x0a, 0x0a, 0x0a, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */
99 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44,
100 0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */
101 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
102 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
103 0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00,
104 0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */
105 0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81,
106 0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
107 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
108 0x83, 0xb5, 0x09, 0x00, 0xa0, 0x00, 0x01, 0x20, /* 0xf0 */
109 0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00,
112 static const u8 tbl_tw2865_ntsc_template[] = {
113 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */
114 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
115 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */
116 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
117 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */
118 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
119 0x00, 0xf0, 0x70, 0x48, 0x80, 0x80, 0x00, 0x02, /* 0x30 */
120 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
121 0x00, 0x00, 0x90, 0x68, 0x00, 0x38, 0x80, 0x80, /* 0x40 */
122 0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00,
123 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
124 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
125 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
126 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43,
127 0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */
128 0xE9, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80,
129 0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
130 0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00,
131 0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */
132 0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13,
133 0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1B, 0x1A, /* 0xa0 */
134 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44,
135 0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */
136 0xFF, 0xE7, 0xE9, 0xE9, 0xEB, 0xFF, 0xD6, 0xD8,
137 0xD8, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
138 0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80,
139 0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */
140 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81,
141 0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
142 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
143 0x83, 0xB5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */
144 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0,
147 static const u8 tbl_tw2865_pal_template[] = {
148 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */
149 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
150 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */
151 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
152 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */
153 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
154 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */
155 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
156 0x00, 0x94, 0x90, 0x48, 0x00, 0x38, 0x7F, 0x80, /* 0x40 */
157 0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00,
158 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
159 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
160 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
161 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43,
162 0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */
163 0xEA, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80,
164 0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
165 0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00,
166 0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */
167 0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13,
168 0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1A, 0x1A, /* 0xa0 */
169 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44,
170 0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */
171 0xFF, 0xE7, 0xE9, 0xE9, 0xE9, 0xFF, 0xD7, 0xD8,
172 0xD9, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
173 0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80,
174 0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */
175 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81,
176 0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
177 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
178 0x83, 0xB5, 0x09, 0x00, 0xA0, 0x00, 0x01, 0x20, /* 0xf0 */
179 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0,
182 #define is_tw286x(__solo, __id) (!(__solo->tw2815 & (1 << __id)))
184 static u8 tw_readbyte(struct solo_dev *solo_dev, int chip_id, u8 tw6x_off,
187 if (is_tw286x(solo_dev, chip_id))
188 return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
189 TW_CHIP_OFFSET_ADDR(chip_id),
192 return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
193 TW_CHIP_OFFSET_ADDR(chip_id),
197 static void tw_writebyte(struct solo_dev *solo_dev, int chip_id,
198 u8 tw6x_off, u8 tw_off, u8 val)
200 if (is_tw286x(solo_dev, chip_id))
201 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
202 TW_CHIP_OFFSET_ADDR(chip_id),
205 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
206 TW_CHIP_OFFSET_ADDR(chip_id),
210 static void tw_write_and_verify(struct solo_dev *solo_dev, u8 addr, u8 off,
215 for (i = 0; i < 5; i++) {
216 u8 rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, addr, off);
220 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, addr, off, val);
221 msleep_interruptible(1);
224 /* printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n", */
225 /* addr, off, val); */
228 static int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr)
230 u8 tbl_tw2865_common[256];
233 if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL)
234 memcpy(tbl_tw2865_common, tbl_tw2865_pal_template,
235 sizeof(tbl_tw2865_common));
237 memcpy(tbl_tw2865_common, tbl_tw2865_ntsc_template,
238 sizeof(tbl_tw2865_common));
241 if (solo_dev->nr_chans == 4) {
242 tbl_tw2865_common[0xd2] = 0x01;
243 tbl_tw2865_common[0xcf] = 0x00;
244 } else if (solo_dev->nr_chans == 8) {
245 tbl_tw2865_common[0xd2] = 0x02;
246 if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
247 tbl_tw2865_common[0xcf] = 0x80;
248 } else if (solo_dev->nr_chans == 16) {
249 tbl_tw2865_common[0xd2] = 0x03;
250 if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
251 tbl_tw2865_common[0xcf] = 0x83;
252 else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
253 tbl_tw2865_common[0xcf] = 0x83;
254 else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
255 tbl_tw2865_common[0xcf] = 0x80;
258 for (i = 0; i < 0xff; i++) {
259 /* Skip read only registers */
272 tw_write_and_verify(solo_dev, dev_addr, i,
273 tbl_tw2865_common[i]);
279 static int tw2864_setup(struct solo_dev *solo_dev, u8 dev_addr)
281 u8 tbl_tw2864_common[256];
284 if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL)
285 memcpy(tbl_tw2864_common, tbl_tw2864_pal_template,
286 sizeof(tbl_tw2864_common));
288 memcpy(tbl_tw2864_common, tbl_tw2864_ntsc_template,
289 sizeof(tbl_tw2864_common));
291 if (solo_dev->tw2865 == 0) {
293 if (solo_dev->nr_chans == 4) {
294 tbl_tw2864_common[0xd2] = 0x01;
295 tbl_tw2864_common[0xcf] = 0x00;
296 } else if (solo_dev->nr_chans == 8) {
297 tbl_tw2864_common[0xd2] = 0x02;
298 if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
299 tbl_tw2864_common[0xcf] = 0x43;
300 else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
301 tbl_tw2864_common[0xcf] = 0x40;
302 } else if (solo_dev->nr_chans == 16) {
303 tbl_tw2864_common[0xd2] = 0x03;
304 if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
305 tbl_tw2864_common[0xcf] = 0x43;
306 else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
307 tbl_tw2864_common[0xcf] = 0x43;
308 else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
309 tbl_tw2864_common[0xcf] = 0x43;
310 else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
311 tbl_tw2864_common[0xcf] = 0x40;
314 /* ALINK Mode. Assumes that the first tw28xx is a
315 * 2865 and these are in cascade. */
316 for (i = 0; i <= 4; i++)
317 tbl_tw2864_common[0x08 | i << 4] = 0x12;
319 if (solo_dev->nr_chans == 8) {
320 tbl_tw2864_common[0xd2] = 0x02;
321 if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
322 tbl_tw2864_common[0xcf] = 0x80;
323 } else if (solo_dev->nr_chans == 16) {
324 tbl_tw2864_common[0xd2] = 0x03;
325 if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
326 tbl_tw2864_common[0xcf] = 0x83;
327 else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
328 tbl_tw2864_common[0xcf] = 0x83;
329 else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
330 tbl_tw2864_common[0xcf] = 0x80;
334 for (i = 0; i < 0xff; i++) {
335 /* Skip read only registers */
348 tw_write_and_verify(solo_dev, dev_addr, i,
349 tbl_tw2864_common[i]);
355 static int tw2815_setup(struct solo_dev *solo_dev, u8 dev_addr)
357 u8 tbl_ntsc_tw2815_common[] = {
358 0x00, 0xc8, 0x20, 0xd0, 0x06, 0xf0, 0x08, 0x80,
359 0x80, 0x80, 0x80, 0x02, 0x06, 0x00, 0x11,
362 u8 tbl_pal_tw2815_common[] = {
363 0x00, 0x88, 0x20, 0xd0, 0x05, 0x20, 0x28, 0x80,
364 0x80, 0x80, 0x80, 0x82, 0x06, 0x00, 0x11,
367 u8 tbl_tw2815_sfr[] = {
368 0x00, 0x00, 0x00, 0xc0, 0x45, 0xa0, 0xd0, 0x2f, /* 0x00 */
369 0x64, 0x80, 0x80, 0x82, 0x82, 0x00, 0x00, 0x00,
370 0x00, 0x0f, 0x05, 0x00, 0x00, 0x80, 0x06, 0x00, /* 0x10 */
371 0x00, 0x00, 0x00, 0xff, 0x8f, 0x00, 0x00, 0x00,
372 0x88, 0x88, 0xc0, 0x00, 0x20, 0x64, 0xa8, 0xec, /* 0x20 */
373 0x31, 0x75, 0xb9, 0xfd, 0x00, 0x00, 0x88, 0x88,
374 0x88, 0x11, 0x00, 0x88, 0x88, 0x00, /* 0x30 */
376 u8 *tbl_tw2815_common;
380 tbl_ntsc_tw2815_common[0x06] = 0;
382 /* Horizontal Delay Control */
383 tbl_ntsc_tw2815_common[0x02] = DEFAULT_HDELAY_NTSC & 0xff;
384 tbl_ntsc_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_NTSC >> 8);
386 /* Horizontal Active Control */
387 tbl_ntsc_tw2815_common[0x03] = DEFAULT_HACTIVE_NTSC & 0xff;
388 tbl_ntsc_tw2815_common[0x06] |=
389 ((0x03 & (DEFAULT_HACTIVE_NTSC >> 8)) << 2);
391 /* Vertical Delay Control */
392 tbl_ntsc_tw2815_common[0x04] = DEFAULT_VDELAY_NTSC & 0xff;
393 tbl_ntsc_tw2815_common[0x06] |=
394 ((0x01 & (DEFAULT_VDELAY_NTSC >> 8)) << 4);
396 /* Vertical Active Control */
397 tbl_ntsc_tw2815_common[0x05] = DEFAULT_VACTIVE_NTSC & 0xff;
398 tbl_ntsc_tw2815_common[0x06] |=
399 ((0x01 & (DEFAULT_VACTIVE_NTSC >> 8)) << 5);
401 tbl_pal_tw2815_common[0x06] = 0;
403 /* Horizontal Delay Control */
404 tbl_pal_tw2815_common[0x02] = DEFAULT_HDELAY_PAL & 0xff;
405 tbl_pal_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_PAL >> 8);
407 /* Horizontal Active Control */
408 tbl_pal_tw2815_common[0x03] = DEFAULT_HACTIVE_PAL & 0xff;
409 tbl_pal_tw2815_common[0x06] |=
410 ((0x03 & (DEFAULT_HACTIVE_PAL >> 8)) << 2);
412 /* Vertical Delay Control */
413 tbl_pal_tw2815_common[0x04] = DEFAULT_VDELAY_PAL & 0xff;
414 tbl_pal_tw2815_common[0x06] |=
415 ((0x01 & (DEFAULT_VDELAY_PAL >> 8)) << 4);
417 /* Vertical Active Control */
418 tbl_pal_tw2815_common[0x05] = DEFAULT_VACTIVE_PAL & 0xff;
419 tbl_pal_tw2815_common[0x06] |=
420 ((0x01 & (DEFAULT_VACTIVE_PAL >> 8)) << 5);
423 (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) ?
424 tbl_ntsc_tw2815_common : tbl_pal_tw2815_common;
426 /* Dual ITU-R BT.656 format */
427 tbl_tw2815_common[0x0d] |= 0x04;
429 /* Audio configuration */
430 tbl_tw2815_sfr[0x62 - 0x40] &= ~(3 << 6);
432 if (solo_dev->nr_chans == 4) {
433 tbl_tw2815_sfr[0x63 - 0x40] |= 1;
434 tbl_tw2815_sfr[0x62 - 0x40] |= 3 << 6;
435 } else if (solo_dev->nr_chans == 8) {
436 tbl_tw2815_sfr[0x63 - 0x40] |= 2;
437 if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
438 tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6;
439 else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
440 tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6;
441 } else if (solo_dev->nr_chans == 16) {
442 tbl_tw2815_sfr[0x63 - 0x40] |= 3;
443 if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
444 tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6;
445 else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
446 tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6;
447 else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
448 tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6;
449 else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
450 tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6;
453 /* Output mode of R_ADATM pin (0 mixing, 1 record) */
454 /* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */
456 /* 8KHz, used to be 16KHz, but changed for remote client compat */
457 tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 2;
458 tbl_tw2815_sfr[0x6c - 0x40] |= 0 << 2;
460 /* Playback of right channel */
461 tbl_tw2815_sfr[0x6c - 0x40] |= 1 << 5;
463 /* Reserved value (XXX ??) */
464 tbl_tw2815_sfr[0x5c - 0x40] |= 1 << 5;
466 /* Analog output gain and mix ratio playback on full */
467 tbl_tw2815_sfr[0x70 - 0x40] |= 0xff;
468 /* Select playback audio and mute all except */
469 tbl_tw2815_sfr[0x71 - 0x40] |= 0x10;
470 tbl_tw2815_sfr[0x6d - 0x40] |= 0x0f;
472 /* End of audio configuration */
474 for (ch = 0; ch < 4; ch++) {
475 tbl_tw2815_common[0x0d] &= ~3;
478 tbl_tw2815_common[0x0d] |= 0x21;
481 tbl_tw2815_common[0x0d] |= 0x20;
484 tbl_tw2815_common[0x0d] |= 0x23;
487 tbl_tw2815_common[0x0d] |= 0x22;
491 for (i = 0; i < 0x0f; i++) {
493 continue; /* read-only */
494 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
495 dev_addr, (ch * 0x10) + i,
496 tbl_tw2815_common[i]);
500 for (i = 0x40; i < 0x76; i++) {
501 /* Skip read-only and nop registers */
502 if (i == 0x40 || i == 0x59 || i == 0x5a ||
503 i == 0x5d || i == 0x5e || i == 0x5f)
506 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, dev_addr, i,
507 tbl_tw2815_sfr[i - 0x40]);
513 #define FIRST_ACTIVE_LINE 0x0008
514 #define LAST_ACTIVE_LINE 0x0102
516 static void saa712x_write_regs(struct solo_dev *dev, const uint8_t *vals,
519 for (;start < n; start++, vals++) {
520 /* Skip read-only registers */
522 /* case 0x00 ... 0x25: */
528 solo_i2c_writebyte(dev, SOLO_I2C_SAA, 0x46, start, *vals);
532 #define SAA712x_reg7c (0x80 | ((LAST_ACTIVE_LINE & 0x100) >> 2) \
533 | ((FIRST_ACTIVE_LINE & 0x100) >> 4))
535 static void saa712x_setup(struct solo_dev *dev)
537 const int reg_start = 0x26;
538 const uint8_t saa7128_regs_ntsc[] = {
542 0x59, 0x1d, 0x75, 0x3f, 0x06, 0x3f,
543 /* :0x2e XXX: read-only */
545 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
547 0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
549 0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18,
550 0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f,
552 0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06,
553 0x02, 0x80, 0x71, 0x77, 0xa7, 0x67, 0x66, 0x2e,
555 0x7b, 0x11, 0x4f, 0x1f, 0x7c, 0xf0, 0x21, 0x77,
556 0x41, 0x88, 0x41, 0x52, 0xed, 0x10, 0x10, 0x00,
558 0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00,
559 0x00, 0x00, FIRST_ACTIVE_LINE, LAST_ACTIVE_LINE & 0xff,
560 SAA712x_reg7c, 0x00, 0xff, 0xff,
561 }, saa7128_regs_pal[] = {
565 0xe1, 0x1d, 0x75, 0x3f, 0x06, 0x3f,
566 /* :0x2e XXX: read-only */
568 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
570 0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
572 0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18,
573 0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f,
575 0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06,
576 0x02, 0x80, 0x0f, 0x77, 0xa7, 0x67, 0x66, 0x2e,
578 0x7b, 0x02, 0x35, 0xcb, 0x8a, 0x09, 0x2a, 0x77,
579 0x41, 0x88, 0x41, 0x52, 0xf1, 0x10, 0x20, 0x00,
581 0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00,
582 0x00, 0x00, 0x12, 0x30,
583 SAA712x_reg7c | 0x40, 0x00, 0xff, 0xff,
586 if (dev->video_type == SOLO_VO_FMT_TYPE_PAL)
587 saa712x_write_regs(dev, saa7128_regs_pal, reg_start,
588 sizeof(saa7128_regs_pal));
590 saa712x_write_regs(dev, saa7128_regs_ntsc, reg_start,
591 sizeof(saa7128_regs_ntsc));
594 int solo_tw28_init(struct solo_dev *solo_dev)
599 solo_dev->tw28_cnt = 0;
601 /* Detect techwell chip type(s) */
602 for (i = 0; i < solo_dev->nr_chans / 4; i++) {
603 value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
604 TW_CHIP_OFFSET_ADDR(i), 0xFF);
606 switch (value >> 3) {
608 solo_dev->tw2865 |= 1 << i;
609 solo_dev->tw28_cnt++;
612 solo_dev->tw2864 |= 1 << i;
613 solo_dev->tw28_cnt++;
616 value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
617 TW_CHIP_OFFSET_ADDR(i),
619 if ((value >> 3) == 0x04) {
620 solo_dev->tw2815 |= 1 << i;
621 solo_dev->tw28_cnt++;
626 if (solo_dev->tw28_cnt != (solo_dev->nr_chans >> 2)) {
627 dev_err(&solo_dev->pdev->dev,
628 "Could not initialize any techwell chips\n");
632 saa712x_setup(solo_dev);
634 for (i = 0; i < solo_dev->tw28_cnt; i++) {
635 if ((solo_dev->tw2865 & (1 << i)))
636 tw2865_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
637 else if ((solo_dev->tw2864 & (1 << i)))
638 tw2864_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
640 tw2815_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
647 * We accessed the video status signal in the Techwell chip through
648 * iic/i2c because the video status reported by register REG_VI_STATUS1
649 * (address 0x012C) of the SOLO6010 chip doesn't give the correct video
650 * status signal values.
652 int tw28_get_video_status(struct solo_dev *solo_dev, u8 ch)
656 /* Get the right chip and on-chip channel */
660 val = tw_readbyte(solo_dev, chip_num, TW286x_AV_STAT_ADDR,
661 TW_AV_STAT_ADDR) & 0x0f;
663 return val & (1 << ch) ? 1 : 0;
667 /* Status of audio from up to 4 techwell chips are combined into 1 variable.
668 * See techwell datasheet for details. */
669 u16 tw28_get_audio_status(struct solo_dev *solo_dev)
675 for (i = 0; i < solo_dev->tw28_cnt; i++) {
676 val = (tw_readbyte(solo_dev, i, TW286x_AV_STAT_ADDR,
677 TW_AV_STAT_ADDR) & 0xf0) >> 4;
678 status |= val << (i * 4);
685 bool tw28_has_sharpness(struct solo_dev *solo_dev, u8 ch)
687 return is_tw286x(solo_dev, ch / 4);
690 int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch,
696 /* Get the right chip and on-chip channel */
700 if (val > 255 || val < 0)
704 case V4L2_CID_SHARPNESS:
705 /* Only 286x has sharpness */
706 if (is_tw286x(solo_dev, chip_num)) {
707 u8 v = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
708 TW_CHIP_OFFSET_ADDR(chip_num),
709 TW286x_SHARPNESS(chip_num));
712 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
713 TW_CHIP_OFFSET_ADDR(chip_num),
714 TW286x_SHARPNESS(chip_num), v);
721 if (is_tw286x(solo_dev, chip_num))
725 tw_writebyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
726 TW_HUE_ADDR(ch), sval);
730 case V4L2_CID_SATURATION:
731 /* 286x chips have a U and V component for saturation */
732 if (is_tw286x(solo_dev, chip_num)) {
733 solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
734 TW_CHIP_OFFSET_ADDR(chip_num),
735 TW286x_SATURATIONU_ADDR(ch), val);
737 tw_writebyte(solo_dev, chip_num, TW286x_SATURATIONV_ADDR(ch),
738 TW_SATURATION_ADDR(ch), val);
742 case V4L2_CID_CONTRAST:
743 tw_writebyte(solo_dev, chip_num, TW286x_CONTRAST_ADDR(ch),
744 TW_CONTRAST_ADDR(ch), val);
747 case V4L2_CID_BRIGHTNESS:
748 if (is_tw286x(solo_dev, chip_num))
752 tw_writebyte(solo_dev, chip_num, TW286x_BRIGHTNESS_ADDR(ch),
753 TW_BRIGHTNESS_ADDR(ch), sval);
763 int tw28_get_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch,
768 /* Get the right chip and on-chip channel */
773 case V4L2_CID_SHARPNESS:
774 /* Only 286x has sharpness */
775 if (is_tw286x(solo_dev, chip_num)) {
776 rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
777 TW_CHIP_OFFSET_ADDR(chip_num),
778 TW286x_SHARPNESS(chip_num));
784 rval = tw_readbyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
786 if (is_tw286x(solo_dev, chip_num))
787 *val = (s32)((char)rval) + 128;
791 case V4L2_CID_SATURATION:
792 *val = tw_readbyte(solo_dev, chip_num,
793 TW286x_SATURATIONU_ADDR(ch),
794 TW_SATURATION_ADDR(ch));
796 case V4L2_CID_CONTRAST:
797 *val = tw_readbyte(solo_dev, chip_num,
798 TW286x_CONTRAST_ADDR(ch),
799 TW_CONTRAST_ADDR(ch));
801 case V4L2_CID_BRIGHTNESS:
802 rval = tw_readbyte(solo_dev, chip_num,
803 TW286x_BRIGHTNESS_ADDR(ch),
804 TW_BRIGHTNESS_ADDR(ch));
805 if (is_tw286x(solo_dev, chip_num))
806 *val = (s32)((char)rval) + 128;
819 * For audio output volume, the output channel is only 1. In this case we
820 * don't need to offset TW_CHIP_OFFSET_ADDR. The TW_CHIP_OFFSET_ADDR used
821 * is the base address of the techwell chip.
823 void tw2815_Set_AudioOutVol(struct solo_dev *solo_dev, unsigned int u_val)
826 unsigned int chip_num;
828 chip_num = (solo_dev->nr_chans - 1) / 4;
830 val = tw_readbyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
831 TW_AUDIO_OUTPUT_VOL_ADDR);
833 u_val = (val & 0x0f) | (u_val << 4);
835 tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
836 TW_AUDIO_OUTPUT_VOL_ADDR, u_val);
840 u8 tw28_get_audio_gain(struct solo_dev *solo_dev, u8 ch)
845 /* Get the right chip and on-chip channel */
849 val = tw_readbyte(solo_dev, chip_num,
850 TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
851 TW_AUDIO_INPUT_GAIN_ADDR(ch));
853 return (ch % 2) ? (val >> 4) : (val & 0x0f);
856 void tw28_set_audio_gain(struct solo_dev *solo_dev, u8 ch, u8 val)
861 /* Get the right chip and on-chip channel */
865 old_val = tw_readbyte(solo_dev, chip_num,
866 TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
867 TW_AUDIO_INPUT_GAIN_ADDR(ch));
869 val = (old_val & ((ch % 2) ? 0x0f : 0xf0)) |
870 ((ch % 2) ? (val << 4) : val);
872 tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
873 TW_AUDIO_INPUT_GAIN_ADDR(ch), val);