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