aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@infradead.org>2007-10-02 10:57:03 -0400
committerMauro Carvalho Chehab <mchehab@infradead.org>2008-01-25 16:01:02 -0500
commit6cb45879dca84c667996d65a12880db6705a2b0e (patch)
tree1548d262b2bcf68eeaf07d5ec5fa33af5e0a11af /drivers/media/video
parent12466577853b0d057f4416f4c7020e544f3a4209 (diff)
V4L/DVB (6423): Add tuner-xc2028 driver
Add support for Xceive XC2028/XC3028 tuner driver Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media/video')
-rw-r--r--drivers/media/video/Kconfig12
-rw-r--r--drivers/media/video/Makefile4
-rw-r--r--drivers/media/video/tuner-core.c5
-rw-r--r--drivers/media/video/tuner-driver.h2
-rw-r--r--drivers/media/video/tuner-xc2028.c391
-rw-r--r--drivers/media/video/tuner-xc2028.h9
6 files changed, 423 insertions, 0 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index ea5be3711a7d..454846355e93 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -505,6 +505,18 @@ config TUNER_3036
505 Say Y here to include support for Philips SAB3036 compatible tuners. 505 Say Y here to include support for Philips SAB3036 compatible tuners.
506 If in doubt, say N. 506 If in doubt, say N.
507 507
508config TUNER_XC2028
509 tristate "Xceive xc2028 support for tm5600/tm6000 driver"
510 depends on I2C
511 select VIDEO_TUNER
512 help
513 Say Y here to include support for Xceive xc2028 tuner. This is
514 required on a few tm5600/tm6000 designs. You should notice
515 that this module currently works only with the special
516 firmware versions used on those Trident chips.
517
518 If in doubt, say N.
519
508config VIDEO_VINO 520config VIDEO_VINO
509 tristate "SGI Vino Video For Linux (EXPERIMENTAL)" 521 tristate "SGI Vino Video For Linux (EXPERIMENTAL)"
510 depends on I2C && SGI_IP22 && EXPERIMENTAL && VIDEO_V4L2 522 depends on I2C && SGI_IP22 && EXPERIMENTAL && VIDEO_V4L2
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index b5a064163e03..1fd775e0a0b0 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -6,6 +6,10 @@ zr36067-objs := zoran_procfs.o zoran_device.o \
6 zoran_driver.o zoran_card.o 6 zoran_driver.o zoran_card.o
7tuner-objs := tuner-core.o tuner-types.o tda9887.o 7tuner-objs := tuner-core.o tuner-types.o tda9887.o
8 8
9ifneq ($(CONFIG_TUNER_XC2028),)
10 tuner-objs += tuner-xc2028.o
11endif
12
9msp3400-objs := msp3400-driver.o msp3400-kthreads.o 13msp3400-objs := msp3400-driver.o msp3400-kthreads.o
10 14
11obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o compat_ioctl32.o \ 15obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o compat_ioctl32.o \
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
index d1d6c664bb09..ce817a17ccf1 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/video/tuner-core.c
@@ -193,6 +193,8 @@ static void set_freq(struct i2c_client *c, unsigned long freq)
193 set_tv_freq(c, freq); 193 set_tv_freq(c, freq);
194 t->tv_freq = freq; 194 t->tv_freq = freq;
195 break; 195 break;
196 default:
197 tuner_dbg("freq set: unknown mode: 0x%04x!\n",t->mode);
196 } 198 }
197} 199}
198 200
@@ -320,6 +322,9 @@ static void set_type(struct i2c_client *c, unsigned int type,
320 i2c_master_send(c,buffer,4); 322 i2c_master_send(c,buffer,4);
321 attach_simple_tuner(t); 323 attach_simple_tuner(t);
322 break; 324 break;
325 case TUNER_XC2028:
326 xc2028_tuner_init(c);
327 break;
323 case TUNER_TDA9887: 328 case TUNER_TDA9887:
324 tda9887_tuner_init(t); 329 tda9887_tuner_init(t);
325 break; 330 break;
diff --git a/drivers/media/video/tuner-driver.h b/drivers/media/video/tuner-driver.h
index 28a10da76d12..5e733bc8362d 100644
--- a/drivers/media/video/tuner-driver.h
+++ b/drivers/media/video/tuner-driver.h
@@ -70,6 +70,8 @@ struct tuner {
70 struct tuner_operations ops; 70 struct tuner_operations ops;
71}; 71};
72 72
73extern int xc2028_tuner_init(struct i2c_client *c);
74
73/* ------------------------------------------------------------------------ */ 75/* ------------------------------------------------------------------------ */
74 76
75extern int tda9887_tuner_init(struct tuner *t); 77extern int tda9887_tuner_init(struct tuner *t);
diff --git a/drivers/media/video/tuner-xc2028.c b/drivers/media/video/tuner-xc2028.c
new file mode 100644
index 000000000000..cfcc1fe68c5c
--- /dev/null
+++ b/drivers/media/video/tuner-xc2028.c
@@ -0,0 +1,391 @@
1/* tuner-xc2028
2 *
3 * Copyright (c) 2007 Mauro Carvalho Chehab (mchehab@infradead.org)
4 * This code is placed under the terms of the GNU General Public License v2
5 */
6
7#include <linux/i2c.h>
8#include <asm/div64.h>
9#include <linux/firmware.h>
10#include <linux/videodev.h>
11#include <linux/delay.h>
12#include "tuner-driver.h"
13#include "tuner-xc2028.h"
14
15/* Firmwares used on tm5600/tm6000 + xc2028/xc3028 */
16static const char *firmware_6M = "tm6000_xc3028_DTV_6M.fw";
17static const char *firmware_8M = "tm6000_xc3028_78M.fw";
18static const char *firmware_DK = "tm6000_xc3028_DK_PAL_MTS.fw";
19static const char *firmware_MN = "tm6000_xc3028_MN_BTSC.fw";
20
21struct xc2028_data {
22 v4l2_std_id firm_type; /* video stds supported by current firmware */
23 int bandwidth; /* Firmware bandwidth: 6M, 7M or 8M */
24 int need_load_generic; /* The generic firmware were loaded? */
25};
26
27#define i2c_send(rc,c,buf,size) \
28if (size != (rc = i2c_master_send(c, buf, size))) \
29 tuner_warn("i2c output error: rc = %d (should be %d)\n", \
30 rc, (int)size);
31
32#define i2c_rcv(rc,c,buf,size) \
33if (size != (rc = i2c_master_recv(c, buf, size))) \
34 tuner_warn("i2c input error: rc = %d (should be %d)\n", \
35 rc, (int)size);
36
37#define send_seq(c, data...) \
38{ int rc; \
39 const static u8 _val[] = data; \
40 if (sizeof(_val) != \
41 (rc = i2c_master_send \
42 (c, _val, sizeof(_val)))) { \
43 printk(KERN_ERR "Error on line %d: %d\n",__LINE__,rc); \
44 return; \
45 } \
46 msleep (10); \
47}
48
49static int xc2028_get_reg(struct i2c_client *c, u16 reg)
50{
51 int rc;
52 unsigned char buf[1];
53 struct tuner *t = i2c_get_clientdata(c);
54
55 buf[0]= reg;
56
57 i2c_send(rc, c, buf, sizeof(buf));
58 if (rc<0)
59 return rc;
60
61 if (t->tuner_callback) {
62 rc = t->tuner_callback( c->adapter->algo_data,
63 XC2028_RESET_CLK, 0);
64 if (rc<0)
65 return rc;
66 }
67
68 i2c_rcv(rc, c, buf, 2);
69 if (rc<0)
70 return rc;
71
72 return (buf[1])|(buf[0]<<8);
73}
74
75static int load_firmware (struct i2c_client *c, const char *name)
76{
77 const struct firmware *fw=NULL;
78 struct tuner *t = i2c_get_clientdata(c);
79 unsigned char *p, *endp;
80 int len=0, rc=0;
81 static const char firmware_ver[] = "tm6000/xcv v1";
82
83 tuner_info("Loading firmware %s\n", name);
84 rc = request_firmware(&fw, name, &c->dev);
85 if (rc < 0) {
86 tuner_info("Error %d while requesting firmware\n", rc);
87 return rc;
88 }
89 p=fw->data;
90 endp=p+fw->size;
91
92 if(fw->size==0) {
93 tuner_info("Error: firmware size is zero!\n");
94 rc=-EINVAL;
95 goto err;
96 }
97 if (fw->size<sizeof(firmware_ver)-1) {
98 /* Firmware is incorrect */
99 tuner_info("Error: firmware size is less than header (%d<%d)!\n",
100 (int)fw->size,(int)sizeof(firmware_ver)-1);
101 rc=-EINVAL;
102 goto err;
103 }
104
105 if (memcmp(p,firmware_ver,sizeof(firmware_ver)-1)) {
106 /* Firmware is incorrect */
107 tuner_info("Error: firmware is not for tm5600/6000 + Xcv2028/3028!\n");
108 rc=-EINVAL;
109 goto err;
110 }
111 p+=sizeof(firmware_ver)-1;
112
113 while(p<endp) {
114 if ((*p) & 0x80) {
115 /* Special callback command received */
116 rc = t->tuner_callback(c->adapter->algo_data,
117 XC2028_TUNER_RESET, (*p)&0x7f);
118 if (rc<0) {
119 tuner_info("Error at RESET code %d\n",
120 (*p)&0x7f);
121 goto err;
122 }
123 p++;
124 continue;
125 }
126 len=*p;
127 p++;
128 if (p+len+1>endp) {
129 /* Firmware is incorrect */
130 tuner_info("Error: firmware is truncated!\n");
131 rc=-EINVAL;
132 goto err;
133 }
134 if (len<=0) {
135 tuner_info("Error: firmware file is corrupted!\n");
136 rc=-EINVAL;
137 goto err;
138 }
139
140 i2c_send(rc, c, p, len);
141 if (rc<0)
142 goto err;
143 p+=len;
144
145 if (*p)
146 msleep(*p);
147 p++;
148 }
149
150
151err:
152 release_firmware(fw);
153
154 return rc;
155}
156
157static int check_firmware(struct i2c_client *c)
158{
159 int rc, version;
160 struct tuner *t = i2c_get_clientdata(c);
161 struct xc2028_data *xc2028 = t->priv;
162 const char *name;
163
164 if (!t->tuner_callback) {
165 printk(KERN_ERR "xc2028: need tuner_callback to load firmware\n");
166 return -EINVAL;
167 }
168
169 if (xc2028->need_load_generic) {
170 if (xc2028->bandwidth==6)
171 name = firmware_6M;
172 else
173 name = firmware_8M;
174
175 /* Reset is needed before loading firmware */
176 rc = t->tuner_callback(c->adapter->algo_data,
177 XC2028_TUNER_RESET, 0);
178 if (rc<0)
179 return rc;
180
181 rc = load_firmware(c,name);
182 if (rc<0)
183 return rc;
184
185 xc2028->need_load_generic=0;
186 xc2028->firm_type=0;
187 }
188
189 if (xc2028->firm_type & t->std)
190 return 0;
191
192 if (t->std & V4L2_STD_MN)
193 name=firmware_MN;
194 else
195 name=firmware_DK;
196
197 rc = load_firmware(c,name);
198 if (rc<0)
199 return rc;
200
201 version = xc2028_get_reg(c, 0x4);
202 tuner_info("Firmware version is %d.%d\n",
203 (version>>4)&0x0f,(version)&0x0f);
204
205 xc2028->firm_type=t->std;
206
207 return 0;
208}
209
210static int xc2028_signal(struct i2c_client *c)
211{
212 int lock, signal;
213
214 if (check_firmware(c)<0)
215 return 0;
216
217 lock = xc2028_get_reg(c, 0x2);
218 if (lock<=0)
219 return lock;
220
221 /* Frequency is locked. Return signal quality */
222
223 signal = xc2028_get_reg(c, 0x40);
224
225 if(signal<=0)
226 return lock;
227
228 return signal;
229}
230
231#define DIV 15625
232
233static void set_tv_freq(struct i2c_client *c, unsigned int freq)
234{
235 int rc;
236 unsigned char buf[5];
237 struct tuner *t = i2c_get_clientdata(c);
238 unsigned long div = (freq*62500l+DIV/2)/DIV;
239
240 if (check_firmware(c)<0)
241 return;
242
243 /* Reset GPIO 1 */
244 if (t->tuner_callback) {
245 rc = t->tuner_callback( c->adapter->algo_data,
246 XC2028_TUNER_RESET, 0);
247 if (rc<0)
248 return;
249 }
250 msleep(10);
251
252 send_seq (c, {0x12, 0x39});
253 send_seq (c, {0x0c, 0x80, 0xf0, 0xf7, 0x3e, 0x75, 0xc1, 0x8a, 0xe4});
254 send_seq (c, {0x0c, 0x02, 0x00});
255 send_seq (c, {0x05, 0x0f, 0xee, 0xaa, 0x5f, 0xea, 0x90});
256 send_seq (c, {0x06, 0x00, 0x0a, 0x4d, 0x8c, 0xf2, 0xd8, 0xcf, 0x30});
257 send_seq (c, {0x06, 0x79, 0x9f});
258 send_seq (c, {0x0b, 0x0d, 0xa4, 0x6c});
259 send_seq (c, {0x0a, 0x01, 0x67, 0x24, 0x40, 0x08, 0xc3, 0x20, 0x10});
260 send_seq (c, {0x0a, 0x64, 0x3c, 0xfa, 0xf7, 0xe1, 0x0c, 0x2c});
261 send_seq (c, {0x09, 0x0b});
262 send_seq (c, {0x10, 0x13});
263 send_seq (c, {0x16, 0x12});
264 send_seq (c, {0x1f, 0x02});
265 send_seq (c, {0x21, 0x02});
266 send_seq (c, {0x01, 0x02});
267 send_seq (c, {0x2b, 0x10});
268 send_seq (c, {0x02, 0x02});
269 send_seq (c, {0x02, 0x03});
270 send_seq (c, {0x00, 0x8c});
271
272 send_seq (c, {0x00, 0x01, 0x00, 0x00});
273 send_seq (c, {0x00, 0xcc, 0x20, 0x06});
274 send_seq (c, {0x2b, 0x1a});
275 send_seq (c, {0x2b, 0x1b});
276 send_seq (c, {0x14, 0x01, 0x1b, 0x19, 0xb5, 0x29, 0xab, 0x09, 0x55});
277 send_seq (c, {0x14, 0x44, 0x05, 0x65});
278 send_seq (c, {0x13, 0x18, 0x08, 0x00, 0x00, 0x6c, 0x18, 0x16, 0x8c});
279 send_seq (c, {0x13, 0x49, 0x2a, 0xab});
280 send_seq (c, {0x0d, 0x01, 0x4b, 0x03, 0x97, 0x55, 0xc7, 0xd7, 0x00});
281 send_seq (c, {0x0d, 0xa1, 0xeb, 0x8f, 0x5c});
282 send_seq (c, {0x1a, 0x00, 0x00, 0x16, 0x8a, 0x40, 0x00, 0x00, 0x00, 0x20});
283 send_seq (c, {0x2d, 0x01});
284 send_seq (c, {0x18, 0x00});
285 send_seq (c, {0x1b, 0x0d, 0x86, 0x51, 0xd2, 0x35, 0xa4, 0x92, 0xa5});
286 send_seq (c, {0x1b, 0xb5, 0x25, 0x65});
287 send_seq (c, {0x1d, 0x00});
288 send_seq (c, {0x0f, 0x00, 0x29, 0x56, 0xb0, 0x00, 0xb6});
289 send_seq (c, {0x20, 0x00});
290 send_seq (c, {0x1e, 0x09, 0x02, 0x5b, 0x6c, 0x00, 0x4b, 0x81, 0x56});
291 send_seq (c, {0x1e, 0x46, 0x69, 0x0b});
292 send_seq (c, {0x22, 0x32});
293 send_seq (c, {0x23, 0x0a});
294 send_seq (c, {0x25, 0x00, 0x09, 0x90, 0x09, 0x06, 0x64, 0x02, 0x41});
295 send_seq (c, {0x26, 0xcc});
296 send_seq (c, {0x29, 0x40});
297 send_seq (c, {0x21, 0x03});
298 send_seq (c, {0x00, 0x8c});
299 send_seq (c, {0x00, 0x00, 0x00, 0x00});
300
301 /* CMD= Set frequency */
302 send_seq(c, {0x00, 0x02, 0x00, 0x00});
303 if (t->tuner_callback) {
304 rc = t->tuner_callback( c->adapter->algo_data,
305 XC2028_RESET_CLK, 1);
306 if (rc<0)
307 return;
308 }
309
310 msleep(10);
311// send_seq(c, {0x00, 0x00, 0x10, 0xd0, 0x00});
312// msleep(100);
313 buf[0]= 0xff & (div>>24);
314 buf[1]= 0xff & (div>>16);
315 buf[2]= 0xff & (div>>8);
316 buf[3]= 0xff & (div);
317 buf[4]= 0;
318
319 i2c_send(rc, c, buf, sizeof(buf));
320 if (rc<0)
321 return;
322 msleep(100);
323
324 printk("divider= %02x %02x %02x %02x (freq=%d.%02d)\n",
325 buf[1],buf[2],buf[3],buf[4],
326 freq / 16, freq % 16 * 100 / 16);
327// printk("signal=%d\n",xc2028_signal(c));
328}
329
330
331static void xc2028_release(struct i2c_client *c)
332{
333 struct tuner *t = i2c_get_clientdata(c);
334
335 kfree(t->priv);
336 t->priv = NULL;
337}
338
339static struct tuner_operations tea5767_tuner_ops = {
340 .set_tv_freq = set_tv_freq,
341 .has_signal = xc2028_signal,
342 .release = xc2028_release,
343// .is_stereo = xc2028_stereo,
344};
345
346
347static int init=0;
348
349int xc2028_tuner_init(struct i2c_client *c)
350{
351 struct tuner *t = i2c_get_clientdata(c);
352 int version = xc2028_get_reg(c, 0x4);
353 int prd_id = xc2028_get_reg(c, 0x8);
354 struct xc2028_data *xc2028;
355
356 if (init) {
357 printk (KERN_ERR "Module already initialized!\n");
358 return 0;
359 }
360 init++;
361
362 xc2028 = kzalloc(sizeof(*xc2028), GFP_KERNEL);
363 if (!xc2028)
364 return -ENOMEM;
365 t->priv = xc2028;
366
367#ifdef HACK
368 xc2028->firm_type=1;
369 xc2028->bandwidth=6;
370#endif
371 xc2028->bandwidth=6;
372 xc2028->need_load_generic=1;
373
374 /* FIXME: Check where t->priv will be freed */
375
376 if (version<0)
377 version=0;
378
379 if (prd_id<0)
380 prd_id=0;
381
382 strlcpy(c->name, "xc2028", sizeof(c->name));
383 tuner_info("type set to %d (%s, hw ver=%d.%d, fw ver=%d.%d, id=0x%04x)\n",
384 t->type, c->name,
385 (version>>12)&0x0f,(version>>8)&0x0f,
386 (version>>4)&0x0f,(version)&0x0f, prd_id);
387
388 memcpy(&t->ops, &tea5767_tuner_ops, sizeof(struct tuner_operations));
389
390 return 0;
391}
diff --git a/drivers/media/video/tuner-xc2028.h b/drivers/media/video/tuner-xc2028.h
new file mode 100644
index 000000000000..7c540c3871bf
--- /dev/null
+++ b/drivers/media/video/tuner-xc2028.h
@@ -0,0 +1,9 @@
1/* tuner-xc2028
2 *
3 * Copyright (c) 2007 Mauro Carvalho Chehab (mchehab@infradead.org)
4 * This code is placed under the terms of the GNU General Public License v2
5 */
6
7/* xc2028 commands for callback */
8#define XC2028_TUNER_RESET 0
9#define XC2028_RESET_CLK 1