blob: 7d53d58aafa183338097a27a2740cfd24b9ee567 [file] [log] [blame]
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -03001/* tuner-xc2028
2 *
3 * Copyright (c) 2007 Mauro Carvalho Chehab (mchehab@infradead.org)
Michel Ludwig701672e2007-07-18 10:29:10 -03004 * Copyright (c) 2007 Michel Ludwig (michel.ludwig@gmail.com)
5 * - frontend interface
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -03006 * This code is placed under the terms of the GNU General Public License v2
7 */
8
9#include <linux/i2c.h>
10#include <asm/div64.h>
11#include <linux/firmware.h>
12#include <linux/videodev.h>
13#include <linux/delay.h>
Michel Ludwig701672e2007-07-18 10:29:10 -030014#include <media/tuner.h>
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -030015#include <linux/mutex.h>
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030016#include "tuner-i2c.h"
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030017#include "tuner-xc2028.h"
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -030018#include "tuner-xc2028-types.h"
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030019
Michel Ludwig701672e2007-07-18 10:29:10 -030020#include <linux/dvb/frontend.h>
21#include "dvb_frontend.h"
22
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030023#define PREFIX "xc2028 "
24
25static LIST_HEAD(xc2028_list);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -030026/* struct for storing firmware table */
27struct firmware_description {
28 unsigned int type;
29 v4l2_std_id id;
30 unsigned char *ptr;
31 unsigned int size;
32};
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030033
34struct xc2028_data {
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030035 struct list_head xc2028_list;
36 struct tuner_i2c_props i2c_props;
37 int (*tuner_callback) (void *dev,
38 int command, int arg);
39 struct device *dev;
40 void *video_dev;
41 int count;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -030042 __u32 frequency;
43
44 struct firmware_description *firm;
45 int firm_size;
46
47 __u16 version;
48
49 struct xc2028_ctrl ctrl;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030050
Michel Ludwig701672e2007-07-18 10:29:10 -030051 v4l2_std_id firm_type; /* video stds supported
52 by current firmware */
53 fe_bandwidth_t bandwidth; /* Firmware bandwidth:
54 6M, 7M or 8M */
55 int need_load_generic; /* The generic firmware
56 were loaded? */
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -030057
58 int max_len; /* Max firmware chunk */
59
Michel Ludwig701672e2007-07-18 10:29:10 -030060 enum tuner_mode mode;
61 struct i2c_client *i2c_client;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030062
63 struct mutex lock;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030064};
65
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030066#define i2c_send(rc, priv, buf, size) \
67if (size != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buf, size))) \
68 tuner_info("i2c output error: rc = %d (should be %d)\n", \
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030069 rc, (int)size);
70
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030071#define i2c_rcv(rc, priv, buf, size) \
72if (size != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buf, size))) \
73 tuner_info("i2c input error: rc = %d (should be %d)\n", \
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030074 rc, (int)size);
75
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030076#define send_seq(priv, data...) \
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030077{ int rc; \
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030078 static u8 _val[] = data; \
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030079 if (sizeof(_val) != \
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030080 (rc = tuner_i2c_xfer_send (&priv->i2c_props, \
81 _val, sizeof(_val)))) { \
82 tuner_info("Error on line %d: %d\n",__LINE__,rc); \
83 return -EINVAL; \
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030084 } \
85 msleep (10); \
86}
87
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030088static int xc2028_get_reg(struct xc2028_data *priv, u16 reg)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030089{
90 int rc;
91 unsigned char buf[1];
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030092
93 tuner_info("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030094
95 buf[0]= reg;
96
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030097 i2c_send(rc, priv, buf, sizeof(buf));
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030098 if (rc<0)
99 return rc;
100
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300101 i2c_rcv(rc, priv, buf, 2);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300102 if (rc<0)
103 return rc;
104
105 return (buf[1])|(buf[0]<<8);
106}
107
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300108static void free_firmware (struct xc2028_data *priv)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300109{
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300110 int i;
111
112 if (!priv->firm)
113 return;
114
115 for (i=0;i<priv->firm_size;i++) {
116 if (priv->firm[i].ptr)
117 kfree(priv->firm[i].ptr);
118 }
119 kfree(priv->firm);
120
121 priv->firm=NULL;
122 priv->need_load_generic = 1;
123}
124
125static int load_all_firmwares (struct dvb_frontend *fe)
126{
127 struct xc2028_data *priv = fe->tuner_priv;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300128 const struct firmware *fw=NULL;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300129 unsigned char *p, *endp;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300130 int rc=0, n, n_array;
131 char name[33];
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300132
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300133 tuner_info("%s called\n", __FUNCTION__);
134
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300135 tuner_info("Loading firmware %s\n", priv->ctrl.fname);
136 rc = request_firmware(&fw, priv->ctrl.fname, priv->dev);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300137 if (rc < 0) {
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300138 if (rc==-ENOENT)
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300139 tuner_info("Error: firmware %s not found.\n",
140 priv->ctrl.fname);
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300141 else
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300142 tuner_info("Error %d while requesting firmware %s \n",
143 rc, priv->ctrl.fname);
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300144
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300145 return rc;
146 }
147 p=fw->data;
148 endp=p+fw->size;
149
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300150 if(fw->size<sizeof(name)-1+2) {
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300151 tuner_info("Error: firmware size is zero!\n");
152 rc=-EINVAL;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300153 goto done;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300154 }
155
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300156 memcpy(name,p,sizeof(name)-1);
157 name[sizeof(name)-1]=0;
158 p+=sizeof(name)-1;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300159
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300160 priv->version = le16_to_cpu(*(__u16 *)p);
161 p += 2;
162
163 tuner_info("firmware: %s, ver %d.%d\n", name,
164 priv->version>>8, priv->version&0xff);
165
166 if (p+2>endp)
167 goto corrupt;
168
169 n_array = le16_to_cpu(*(__u16 *)p);
170 p += 2;
171
172 tuner_info("there are %d firmwares at %s\n", n_array, priv->ctrl.fname);
173
174 priv->firm=kzalloc(sizeof(*priv->firm)*n_array,GFP_KERNEL);
175
176 if (!fw) {
177 tuner_info("Not enough memory for loading firmware.\n");
178 rc=-ENOMEM;
179 goto done;
180 }
181
182 priv->firm_size = n_array;
183 n=-1;
184 while (p<endp) {
185 __u32 type, size;
186 v4l2_std_id id;
187
188 n++;
189 if (n >= n_array) {
190 tuner_info("Too much firmwares at the file\n");
191 goto corrupt;
192 }
193
194 /* Checks if there's enough bytes to read */
195 if (p+sizeof(type)+sizeof(id)+sizeof(size)>endp) {
196 tuner_info("Lost firmware!\n");
197 goto corrupt;
198 }
199
200 type = le32_to_cpu(*(__u32 *)p);
201 p += sizeof(type);
202
203 id = le64_to_cpu(*(v4l2_std_id *)p);
204 p += sizeof(id);
205
206 size = le32_to_cpu(*(v4l2_std_id *)p);
207 p += sizeof(size);
208
209 if ((!size)||(size+p>endp)) {
210 tuner_info("Firmware type %x, id %lx corrupt\n",
211 type, (unsigned long) id);
212 goto corrupt;
213 }
214
215 priv->firm[n].ptr=kzalloc(size,GFP_KERNEL);
216 if (!priv->firm[n].ptr) {
217 tuner_info("Not enough memory.\n");
218 rc=-ENOMEM;
219 goto err;
220 }
221 tuner_info("Loading firmware type %x, id %lx, size=%d.\n",
222 type, (unsigned long) id, size);
223
224 memcpy(priv->firm[n].ptr, p, size);
225 priv->firm[n].type = type;
226 priv->firm[n].id = id;
227 priv->firm[n].size = size;
228
229 p += size;
230 }
231
232 if (n+1 != priv->firm_size) {
233 tuner_info("Firmware file is incomplete!\n");
234 goto corrupt;
235 }
236
237 goto done;
238
239corrupt:
240 rc=-EINVAL;
241 tuner_info("Error: firmware file is corrupted!\n");
242
243err:
244 tuner_info("Releasing loaded firmware file.\n");
245
246 free_firmware(priv);
247
248done:
249 release_firmware(fw);
250 tuner_info("Firmware files loaded.\n");
251
252 return rc;
253}
254
255static int load_firmware (struct dvb_frontend *fe, unsigned int type,
256 v4l2_std_id *id)
257{
258 struct xc2028_data *priv = fe->tuner_priv;
259 int i, rc;
260 unsigned char *p, *endp, buf[priv->max_len];
261
262 tuner_info("%s called\n", __FUNCTION__);
263
264 if (!priv->firm) {
265 printk (KERN_ERR PREFIX "Error! firmware not loaded\n");
266 return -EINVAL;
267 }
268
269 if ((type == 0) && (*id == 0))
270 *id=V4L2_STD_PAL;
271
272 /* Seek for exact match */
273 for (i=0;i<priv->firm_size;i++) {
274 if ( (type == priv->firm[i].type) &&
275 (*id == priv->firm[i].id))
276 goto found;
277 }
278
279 /* Seek for generic video standard match */
280 for (i=0;i<priv->firm_size;i++) {
281 if ( (type == priv->firm[i].type) && (*id & priv->firm[i].id))
282 goto found;
283 }
284
285 /*FIXME: Would make sense to seek for type "hint" match ? */
286
287 tuner_info ("Can't find firmware for type=%x, id=%lx\n", type,
288 (long int)*id);
289 return -EINVAL;
290
291found:
292 *id = priv->firm[i].id;
293 tuner_info ("Found firmware for type=%x, id=%lx\n", type,
294 (long int)*id);
295
296 p = priv->firm[i].ptr;
297
298 if (!p) {
299 printk(KERN_ERR PREFIX "Firmware pointer were freed!");
300 return -EINVAL;
301 }
302 endp = p+priv->firm[i].size;
303
304 while (p<endp) {
305 __u16 size;
306
307 /* Checks if there's enough bytes to read */
308 if (p+sizeof(size)>endp) {
309 tuner_info("missing bytes\n");
310 return -EINVAL;
311 }
312
313
314 size = le16_to_cpu(*(__u16 *)p);
315 p += sizeof(size);
316
317 if (size == 0xffff)
318 return 0;
319
320 if (!size) {
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300321 /* Special callback command received */
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300322 rc = priv->tuner_callback(priv->video_dev,
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300323 XC2028_TUNER_RESET, 0);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300324 if (rc<0) {
325 tuner_info("Error at RESET code %d\n",
326 (*p)&0x7f);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300327 return -EINVAL;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300328 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300329 continue;
330 }
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300331
332 /* Checks for a sleep command */
333 if (size & 0x8000) {
334 msleep (size & 0x7fff);
335 continue;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300336 }
337
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300338 if ((size + p > endp)) {
339 tuner_info("missing bytes: need %d, have %d\n",
340 size, (int)(endp-p));
341 return -EINVAL;
342 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300343
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300344 buf[0] = *p;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300345 p++;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300346 size--;
347
348 /* Sends message chunks */
349 while (size>0) {
350 int len = (size<priv->max_len-1)?size:priv->max_len-1;
351
352 memcpy(buf+1, p, len);
353
354 i2c_send(rc, priv, buf, len+1);
355 if (rc<0) {
356 tuner_info("%d returned from send\n",rc);
357 return -EINVAL;
358 }
359
360 p += len;
361 size -= len;
362 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300363 }
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300364 return -EINVAL;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300365}
366
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300367static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode,
368 v4l2_std_id std,
Michel Ludwig701672e2007-07-18 10:29:10 -0300369 fe_bandwidth_t bandwidth)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300370{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300371 struct xc2028_data *priv = fe->tuner_priv;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300372 int rc, version;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300373 v4l2_std_id std0=0;
374 unsigned int type0=0,type=0;
375 int change_digital_bandwidth;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300376
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300377 tuner_info("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300378
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300379 if (!priv->firm) {
380 if (!priv->ctrl.fname)
381 return -EINVAL;
382
383 rc=load_all_firmwares(fe);
384 if (rc<0)
385 return rc;
386 }
387
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300388 tuner_info( "I am in mode %u and I should switch to mode %i\n",
389 priv->mode, new_mode);
Michel Ludwig701672e2007-07-18 10:29:10 -0300390
391 /* first of all, determine whether we have switched the mode */
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300392 if(new_mode != priv->mode) {
393 priv->mode = new_mode;
394 priv->need_load_generic = 1;
Michel Ludwig701672e2007-07-18 10:29:10 -0300395 }
396
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300397 change_digital_bandwidth = (priv->mode == T_DIGITAL_TV
398 && bandwidth != priv->bandwidth) ? 1 : 0;
399 tuner_info("old bandwidth %u, new bandwidth %u\n", priv->bandwidth,
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300400 bandwidth);
Michel Ludwig701672e2007-07-18 10:29:10 -0300401
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300402 if (priv->need_load_generic) {
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300403 /* Reset is needed before loading firmware */
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300404 rc = priv->tuner_callback(priv->video_dev,
405 XC2028_TUNER_RESET, 0);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300406 if (rc<0)
407 return rc;
408
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300409 type0=BASE;
410
411 if (priv->ctrl.type == XC2028_FIRM_MTS)
412 type0 |= MTS;
413
414 if (priv->bandwidth==8)
415 type0 |= F8MHZ;
416
417 /* FIXME: How to load FM and FM|INPUT1 firmwares? */
418
419 rc = load_firmware(fe, type0, &std0);
420 if (rc<0) {
421 tuner_info("Error %d while loading generic firmware\n",
422 rc);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300423 return rc;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300424 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300425
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300426 priv->need_load_generic=0;
427 priv->firm_type=0;
428 if(priv->mode == T_DIGITAL_TV) {
Michel Ludwig701672e2007-07-18 10:29:10 -0300429 change_digital_bandwidth=1;
430 }
431 }
432
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300433 tuner_info("I should change bandwidth %u\n",
Michel Ludwig701672e2007-07-18 10:29:10 -0300434 change_digital_bandwidth);
435
436 if (change_digital_bandwidth) {
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300437
438 /*FIXME: Should allow selecting between D2620 and D2633 */
439 type |= D2620;
440
441 /* FIXME: When should select a DTV78 firmware?
442 */
Michel Ludwig701672e2007-07-18 10:29:10 -0300443 switch(bandwidth) {
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300444 case BANDWIDTH_8_MHZ:
445 type |= DTV8;
446 break;
447 case BANDWIDTH_7_MHZ:
448 type |= DTV7;
449 break;
450 case BANDWIDTH_6_MHZ:
451 /* FIXME: Should allow select also ATSC */
452 type |= DTV6_QAM;
Michel Ludwig701672e2007-07-18 10:29:10 -0300453 break;
454
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300455 default:
456 tuner_info("error: bandwidth not supported.\n");
Michel Ludwig701672e2007-07-18 10:29:10 -0300457 };
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300458 priv->bandwidth = bandwidth;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300459 }
460
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300461 /* Load INIT1, if needed */
462 tuner_info("Trying to load init1 firmware\n");
463 type0 = BASE | INIT1 | priv->ctrl.type;
464 if (priv->ctrl.type == XC2028_FIRM_MTS)
465 type0 |= MTS;
466
467 /* FIXME: Should handle errors - if INIT1 found */
468 rc = load_firmware(fe, type0, &std0);
469
470 /* FIXME: Should add support for FM radio
471 */
472
473 if (priv->ctrl.type == XC2028_FIRM_MTS)
474 type |= MTS;
475
476 tuner_info("firmware standard to load: %08lx\n",(unsigned long) std);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300477 if (priv->firm_type & std) {
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300478 tuner_info("no need to load a std-specific firmware.\n");
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300479 return 0;
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300480 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300481
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300482 rc = load_firmware(fe, type, &std);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300483 if (rc<0)
484 return rc;
485
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300486 version = xc2028_get_reg(priv, 0x4);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300487 tuner_info("Firmware version is %d.%d\n",
488 (version>>4)&0x0f,(version)&0x0f);
489
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300490 priv->firm_type=std;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300491
492 return 0;
493}
494
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300495static int xc2028_signal(struct dvb_frontend *fe, u16 *strength)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300496{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300497 struct xc2028_data *priv = fe->tuner_priv;
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300498 int frq_lock, signal=0;
499
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300500 tuner_info("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300501
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300502 mutex_lock(&priv->lock);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300503
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300504 *strength = 0;
505
506 frq_lock = xc2028_get_reg(priv, 0x2);
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300507 if (frq_lock<=0)
508 goto ret;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300509
510 /* Frequency is locked. Return signal quality */
511
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300512 signal = xc2028_get_reg(priv, 0x40);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300513
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300514 if(signal<=0) {
515 signal=frq_lock;
516 }
517
518ret:
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300519 mutex_unlock(&priv->lock);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300520
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300521 *strength = signal;
522
523 return 0;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300524}
525
526#define DIV 15625
527
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300528static int generic_set_tv_freq(struct dvb_frontend *fe, u32 freq /* in Hz */,
529 enum tuner_mode new_mode,
530 v4l2_std_id std,
531 fe_bandwidth_t bandwidth)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300532{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300533 struct xc2028_data *priv = fe->tuner_priv;
534 int rc=-EINVAL;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300535 unsigned char buf[5];
Michel Ludwig701672e2007-07-18 10:29:10 -0300536 u32 div, offset = 0;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300537
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300538 tuner_info("%s called\n", __FUNCTION__);
539
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300540 mutex_lock(&priv->lock);
541
Mauro Carvalho Chehabd4e76682007-07-18 23:14:25 -0300542 /* HACK: It seems that specific firmware need to be reloaded
543 when freq is changed */
Michel Ludwig701672e2007-07-18 10:29:10 -0300544
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300545 priv->firm_type=0;
Michel Ludwig701672e2007-07-18 10:29:10 -0300546
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300547 /* Reset GPIO 1 */
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300548 rc = priv->tuner_callback(priv->video_dev, XC2028_TUNER_RESET, 0);
549 if (rc<0)
550 goto ret;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300551
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300552 msleep(10);
553 tuner_info("should set frequency %d kHz)\n", freq / 1000);
554
555 if (check_firmware(fe, new_mode, std, bandwidth)<0)
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300556 goto ret;
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300557
Mauro Carvalho Chehabd4e76682007-07-18 23:14:25 -0300558 if(new_mode == T_DIGITAL_TV)
559 offset = 2750000;
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300560
Mauro Carvalho Chehabd4e76682007-07-18 23:14:25 -0300561 div = (freq - offset + DIV/2)/DIV;
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300562
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300563 /* CMD= Set frequency */
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300564
565 if (priv->version<0x0202) {
566 send_seq(priv, {0x00, 0x02, 0x00, 0x00});
567 } else {
568 send_seq(priv, {0x80, 0x02, 0x00, 0x00});
569 }
570
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300571 rc = priv->tuner_callback(priv->video_dev, XC2028_RESET_CLK, 1);
572 if (rc<0)
573 goto ret;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300574
575 msleep(10);
Michel Ludwig701672e2007-07-18 10:29:10 -0300576
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300577 buf[0]= 0xff & (div>>24);
578 buf[1]= 0xff & (div>>16);
579 buf[2]= 0xff & (div>>8);
580 buf[3]= 0xff & (div);
581 buf[4]= 0;
582
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300583 i2c_send(rc, priv, buf, sizeof(buf));
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300584 if (rc<0)
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300585 goto ret;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300586 msleep(100);
587
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300588 priv->frequency=freq;
589
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300590 printk("divider= %02x %02x %02x %02x (freq=%d.%02d)\n",
591 buf[1],buf[2],buf[3],buf[4],
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300592 freq / 1000000, (freq%1000000)/10000);
593
594 rc=0;
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300595
596ret:
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300597 mutex_unlock(&priv->lock);
598
599 return rc;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300600}
601
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300602static int xc2028_set_tv_freq(struct dvb_frontend *fe,
603 struct analog_parameters *p)
Michel Ludwig701672e2007-07-18 10:29:10 -0300604{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300605 struct xc2028_data *priv = fe->tuner_priv;
Michel Ludwig701672e2007-07-18 10:29:10 -0300606
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300607 tuner_info("%s called\n", __FUNCTION__);
608
609 return generic_set_tv_freq(fe, 62500l*p->frequency, T_ANALOG_TV,
610 p->std,
611 BANDWIDTH_8_MHZ /* NOT USED */);
Michel Ludwig701672e2007-07-18 10:29:10 -0300612}
613
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300614static int xc2028_set_params(struct dvb_frontend *fe,
Michel Ludwig701672e2007-07-18 10:29:10 -0300615 struct dvb_frontend_parameters *p)
616{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300617 struct xc2028_data *priv = fe->tuner_priv;
Michel Ludwig701672e2007-07-18 10:29:10 -0300618
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300619 tuner_info("%s called\n", __FUNCTION__);
Michel Ludwig701672e2007-07-18 10:29:10 -0300620
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300621 /* FIXME: Only OFDM implemented */
622 if (fe->ops.info.type != FE_OFDM) {
623 tuner_info ("DTV type not implemented.\n");
624 return -EINVAL;
625 }
Michel Ludwig701672e2007-07-18 10:29:10 -0300626
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300627 return generic_set_tv_freq(fe, p->frequency, T_DIGITAL_TV,
628 0, /* NOT USED */
629 p->u.ofdm.bandwidth);
630
Michel Ludwig701672e2007-07-18 10:29:10 -0300631}
632
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300633static int xc2028_dvb_release(struct dvb_frontend *fe)
Michel Ludwig701672e2007-07-18 10:29:10 -0300634{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300635 struct xc2028_data *priv = fe->tuner_priv;
Michel Ludwig701672e2007-07-18 10:29:10 -0300636
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300637 tuner_info("%s called\n", __FUNCTION__);
638
639 priv->count--;
640
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300641 if (!priv->count) {
642 if (priv->ctrl.fname)
643 kfree(priv->ctrl.fname);
644
645 free_firmware(priv);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300646 kfree (priv);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300647 }
Michel Ludwig701672e2007-07-18 10:29:10 -0300648
649 return 0;
650}
651
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300652static int xc2028_get_frequency(struct dvb_frontend *fe, u32 *frequency)
Michel Ludwig701672e2007-07-18 10:29:10 -0300653{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300654 struct xc2028_data *priv = fe->tuner_priv;
655
656 tuner_info("%s called\n", __FUNCTION__);
657
658 *frequency = priv->frequency;
Michel Ludwig701672e2007-07-18 10:29:10 -0300659
660 return 0;
661}
662
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300663static int xc2028_set_config (struct dvb_frontend *fe, void *priv_cfg)
664{
665 struct xc2028_data *priv = fe->tuner_priv;
666 struct xc2028_ctrl *p = priv_cfg;
667
668 tuner_info("%s called\n", __FUNCTION__);
669
670 priv->ctrl.type = p->type;
671
672 if (p->fname) {
673 if (priv->ctrl.fname)
674 kfree(priv->ctrl.fname);
675
676 priv->ctrl.fname = kmalloc(strlen(p->fname)+1, GFP_KERNEL);
677 if (!priv->ctrl.fname)
678 return -ENOMEM;
679
680 free_firmware(priv);
681 strcpy(priv->ctrl.fname, p->fname);
682 }
683
684 tuner_info("%s OK\n", __FUNCTION__);
685
686 return 0;
687}
688
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300689static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = {
Michel Ludwig701672e2007-07-18 10:29:10 -0300690 .info = {
691 .name = "Xceive XC3028",
692 .frequency_min = 42000000,
693 .frequency_max = 864000000,
694 .frequency_step = 50000,
695 },
696
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300697 .set_config = xc2028_set_config,
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300698 .set_analog_params = xc2028_set_tv_freq,
699 .release = xc2028_dvb_release,
700 .get_frequency = xc2028_get_frequency,
701 .get_rf_strength = xc2028_signal,
702 .set_params = xc2028_set_params,
Michel Ludwig701672e2007-07-18 10:29:10 -0300703
704// int (*sleep)(struct dvb_frontend *fe);
Michel Ludwig701672e2007-07-18 10:29:10 -0300705// int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth);
Michel Ludwig701672e2007-07-18 10:29:10 -0300706// int (*get_status)(struct dvb_frontend *fe, u32 *status);
707};
708
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300709int xc2028_attach(struct dvb_frontend *fe, struct i2c_adapter* i2c_adap,
710 u8 i2c_addr, struct device *dev, void *video_dev,
711 int (*tuner_callback) (void *dev, int command,int arg))
Michel Ludwig701672e2007-07-18 10:29:10 -0300712{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300713 struct xc2028_data *priv;
Michel Ludwig701672e2007-07-18 10:29:10 -0300714
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300715 printk( KERN_INFO PREFIX "Xcv2028/3028 init called!\n");
716
717 if (NULL == dev)
718 return -ENODEV;
719
720 if (NULL == video_dev)
721 return -ENODEV;
722
723 if (!tuner_callback) {
724 printk( KERN_ERR PREFIX "No tuner callback!\n");
725 return -EINVAL;
726 }
727
728 list_for_each_entry(priv, &xc2028_list, xc2028_list) {
729 if (priv->dev == dev) {
730 dev = NULL;
731 priv->count++;
732 }
733 }
734
735 if (dev) {
736 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
737 if (priv == NULL)
738 return -ENOMEM;
739
740 fe->tuner_priv = priv;
741
742 priv->bandwidth=BANDWIDTH_6_MHZ;
743 priv->need_load_generic=1;
744 priv->mode = T_UNINITIALIZED;
745 priv->i2c_props.addr = i2c_addr;
746 priv->i2c_props.adap = i2c_adap;
747 priv->dev = dev;
748 priv->video_dev = video_dev;
749 priv->tuner_callback = tuner_callback;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300750 priv->max_len = 13;
751
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300752
753 mutex_init(&priv->lock);
754
755 list_add_tail(&priv->xc2028_list,&xc2028_list);
756 }
757
758 memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops,
759 sizeof(xc2028_dvb_tuner_ops));
760
761 tuner_info("type set to %s\n", "XCeive xc2028/xc3028 tuner");
Michel Ludwig701672e2007-07-18 10:29:10 -0300762
763 return 0;
764}
765
766EXPORT_SYMBOL(xc2028_attach);
767
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300768MODULE_DESCRIPTION("Xceive xc2028/xc3028 tuner driver");
769MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
770MODULE_LICENSE("GPL");