diff options
author | Patrice Chotard <patrice.chotard@sfr.fr> | 2012-07-31 14:34:29 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-08-11 22:27:00 -0400 |
commit | 397e972350c42cbaf3228fe2eec23fecf6a69903 (patch) | |
tree | b7bda2ae1529872ca24259636e4f3745e2b826fb /drivers/media/dvb | |
parent | 5fb67074c6657edc34867cba78255b6f5b505f12 (diff) |
[media] ngene: add support for Terratec Cynergy 2400i Dual DVB-T
This code is based on ngene initial check-in (dae52d009fc950b5c209260d50fcc000f5becd3c)
Signed-off-by: Patrice Chotard <patricechotard@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/dvb')
-rw-r--r-- | drivers/media/dvb/ngene/ngene-cards.c | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/drivers/media/dvb/ngene/ngene-cards.c b/drivers/media/dvb/ngene/ngene-cards.c index 72ee8de02260..a6cd6959ad19 100644 --- a/drivers/media/dvb/ngene/ngene-cards.c +++ b/drivers/media/dvb/ngene/ngene-cards.c | |||
@@ -42,6 +42,8 @@ | |||
42 | #include "mt2131.h" | 42 | #include "mt2131.h" |
43 | #include "tda18271c2dd.h" | 43 | #include "tda18271c2dd.h" |
44 | #include "drxk.h" | 44 | #include "drxk.h" |
45 | #include "drxd.h" | ||
46 | #include "dvb-pll.h" | ||
45 | 47 | ||
46 | 48 | ||
47 | /****************************************************************************/ | 49 | /****************************************************************************/ |
@@ -313,6 +315,235 @@ static int demod_attach_lg330x(struct ngene_channel *chan) | |||
313 | return (chan->fe) ? 0 : -ENODEV; | 315 | return (chan->fe) ? 0 : -ENODEV; |
314 | } | 316 | } |
315 | 317 | ||
318 | static int demod_attach_drxd(struct ngene_channel *chan) | ||
319 | { | ||
320 | struct drxd_config *feconf; | ||
321 | |||
322 | feconf = chan->dev->card_info->fe_config[chan->number]; | ||
323 | |||
324 | chan->fe = dvb_attach(drxd_attach, feconf, chan, | ||
325 | &chan->i2c_adapter, &chan->dev->pci_dev->dev); | ||
326 | if (!chan->fe) { | ||
327 | pr_err("No DRXD found!\n"); | ||
328 | return -ENODEV; | ||
329 | } | ||
330 | |||
331 | if (!dvb_attach(dvb_pll_attach, chan->fe, feconf->pll_address, | ||
332 | &chan->i2c_adapter, | ||
333 | feconf->pll_type)) { | ||
334 | pr_err("No pll(%d) found!\n", feconf->pll_type); | ||
335 | return -ENODEV; | ||
336 | } | ||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | /****************************************************************************/ | ||
341 | /* EEPROM TAGS **************************************************************/ | ||
342 | /****************************************************************************/ | ||
343 | |||
344 | #define MICNG_EE_START 0x0100 | ||
345 | #define MICNG_EE_END 0x0FF0 | ||
346 | |||
347 | #define MICNG_EETAG_END0 0x0000 | ||
348 | #define MICNG_EETAG_END1 0xFFFF | ||
349 | |||
350 | /* 0x0001 - 0x000F reserved for housekeeping */ | ||
351 | /* 0xFFFF - 0xFFFE reserved for housekeeping */ | ||
352 | |||
353 | /* Micronas assigned tags | ||
354 | EEProm tags for hardware support */ | ||
355 | |||
356 | #define MICNG_EETAG_DRXD1_OSCDEVIATION 0x1000 /* 2 Bytes data */ | ||
357 | #define MICNG_EETAG_DRXD2_OSCDEVIATION 0x1001 /* 2 Bytes data */ | ||
358 | |||
359 | #define MICNG_EETAG_MT2060_1_1STIF 0x1100 /* 2 Bytes data */ | ||
360 | #define MICNG_EETAG_MT2060_2_1STIF 0x1101 /* 2 Bytes data */ | ||
361 | |||
362 | /* Tag range for OEMs */ | ||
363 | |||
364 | #define MICNG_EETAG_OEM_FIRST 0xC000 | ||
365 | #define MICNG_EETAG_OEM_LAST 0xFFEF | ||
366 | |||
367 | static int i2c_write_eeprom(struct i2c_adapter *adapter, | ||
368 | u8 adr, u16 reg, u8 data) | ||
369 | { | ||
370 | u8 m[3] = {(reg >> 8), (reg & 0xff), data}; | ||
371 | struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, | ||
372 | .len = sizeof(m)}; | ||
373 | |||
374 | if (i2c_transfer(adapter, &msg, 1) != 1) { | ||
375 | pr_err(DEVICE_NAME ": Error writing EEPROM!\n"); | ||
376 | return -EIO; | ||
377 | } | ||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | static int i2c_read_eeprom(struct i2c_adapter *adapter, | ||
382 | u8 adr, u16 reg, u8 *data, int len) | ||
383 | { | ||
384 | u8 msg[2] = {(reg >> 8), (reg & 0xff)}; | ||
385 | struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, | ||
386 | .buf = msg, .len = 2 }, | ||
387 | {.addr = adr, .flags = I2C_M_RD, | ||
388 | .buf = data, .len = len} }; | ||
389 | |||
390 | if (i2c_transfer(adapter, msgs, 2) != 2) { | ||
391 | pr_err(DEVICE_NAME ": Error reading EEPROM\n"); | ||
392 | return -EIO; | ||
393 | } | ||
394 | return 0; | ||
395 | } | ||
396 | |||
397 | static int ReadEEProm(struct i2c_adapter *adapter, | ||
398 | u16 Tag, u32 MaxLen, u8 *data, u32 *pLength) | ||
399 | { | ||
400 | int status = 0; | ||
401 | u16 Addr = MICNG_EE_START, Length, tag = 0; | ||
402 | u8 EETag[3]; | ||
403 | |||
404 | while (Addr + sizeof(u16) + 1 < MICNG_EE_END) { | ||
405 | if (i2c_read_eeprom(adapter, 0x50, Addr, EETag, sizeof(EETag))) | ||
406 | return -1; | ||
407 | tag = (EETag[0] << 8) | EETag[1]; | ||
408 | if (tag == MICNG_EETAG_END0 || tag == MICNG_EETAG_END1) | ||
409 | return -1; | ||
410 | if (tag == Tag) | ||
411 | break; | ||
412 | Addr += sizeof(u16) + 1 + EETag[2]; | ||
413 | } | ||
414 | if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) { | ||
415 | pr_err(DEVICE_NAME | ||
416 | ": Reached EOEE @ Tag = %04x Length = %3d\n", | ||
417 | tag, EETag[2]); | ||
418 | return -1; | ||
419 | } | ||
420 | Length = EETag[2]; | ||
421 | if (Length > MaxLen) | ||
422 | Length = (u16) MaxLen; | ||
423 | if (Length > 0) { | ||
424 | Addr += sizeof(u16) + 1; | ||
425 | status = i2c_read_eeprom(adapter, 0x50, Addr, data, Length); | ||
426 | if (!status) { | ||
427 | *pLength = EETag[2]; | ||
428 | if (Length < EETag[2]) | ||
429 | ; /*status=STATUS_BUFFER_OVERFLOW; */ | ||
430 | } | ||
431 | } | ||
432 | return status; | ||
433 | } | ||
434 | |||
435 | static int WriteEEProm(struct i2c_adapter *adapter, | ||
436 | u16 Tag, u32 Length, u8 *data) | ||
437 | { | ||
438 | int status = 0; | ||
439 | u16 Addr = MICNG_EE_START; | ||
440 | u8 EETag[3]; | ||
441 | u16 tag = 0; | ||
442 | int retry, i; | ||
443 | |||
444 | while (Addr + sizeof(u16) + 1 < MICNG_EE_END) { | ||
445 | if (i2c_read_eeprom(adapter, 0x50, Addr, EETag, sizeof(EETag))) | ||
446 | return -1; | ||
447 | tag = (EETag[0] << 8) | EETag[1]; | ||
448 | if (tag == MICNG_EETAG_END0 || tag == MICNG_EETAG_END1) | ||
449 | return -1; | ||
450 | if (tag == Tag) | ||
451 | break; | ||
452 | Addr += sizeof(u16) + 1 + EETag[2]; | ||
453 | } | ||
454 | if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) { | ||
455 | pr_err(DEVICE_NAME | ||
456 | ": Reached EOEE @ Tag = %04x Length = %3d\n", | ||
457 | tag, EETag[2]); | ||
458 | return -1; | ||
459 | } | ||
460 | |||
461 | if (Length > EETag[2]) | ||
462 | return -EINVAL; | ||
463 | /* Note: We write the data one byte at a time to avoid | ||
464 | issues with page sizes. (which are different for | ||
465 | each manufacture and eeprom size) | ||
466 | */ | ||
467 | Addr += sizeof(u16) + 1; | ||
468 | for (i = 0; i < Length; i++, Addr++) { | ||
469 | status = i2c_write_eeprom(adapter, 0x50, Addr, data[i]); | ||
470 | |||
471 | if (status) | ||
472 | break; | ||
473 | |||
474 | /* Poll for finishing write cycle */ | ||
475 | retry = 10; | ||
476 | while (retry) { | ||
477 | u8 Tmp; | ||
478 | |||
479 | msleep(50); | ||
480 | status = i2c_read_eeprom(adapter, 0x50, Addr, &Tmp, 1); | ||
481 | if (status) | ||
482 | break; | ||
483 | if (Tmp != data[i]) | ||
484 | pr_err(DEVICE_NAME | ||
485 | "eeprom write error\n"); | ||
486 | retry -= 1; | ||
487 | } | ||
488 | if (status) { | ||
489 | pr_err(DEVICE_NAME | ||
490 | ": Timeout polling eeprom\n"); | ||
491 | break; | ||
492 | } | ||
493 | } | ||
494 | return status; | ||
495 | } | ||
496 | |||
497 | static int eeprom_read_ushort(struct i2c_adapter *adapter, u16 tag, u16 *data) | ||
498 | { | ||
499 | int stat; | ||
500 | u8 buf[2]; | ||
501 | u32 len = 0; | ||
502 | |||
503 | stat = ReadEEProm(adapter, tag, 2, buf, &len); | ||
504 | if (stat) | ||
505 | return stat; | ||
506 | if (len != 2) | ||
507 | return -EINVAL; | ||
508 | |||
509 | *data = (buf[0] << 8) | buf[1]; | ||
510 | return 0; | ||
511 | } | ||
512 | |||
513 | static int eeprom_write_ushort(struct i2c_adapter *adapter, u16 tag, u16 data) | ||
514 | { | ||
515 | int stat; | ||
516 | u8 buf[2]; | ||
517 | |||
518 | buf[0] = data >> 8; | ||
519 | buf[1] = data & 0xff; | ||
520 | stat = WriteEEProm(adapter, tag, 2, buf); | ||
521 | if (stat) | ||
522 | return stat; | ||
523 | return 0; | ||
524 | } | ||
525 | |||
526 | static s16 osc_deviation(void *priv, s16 deviation, int flag) | ||
527 | { | ||
528 | struct ngene_channel *chan = priv; | ||
529 | struct i2c_adapter *adap = &chan->i2c_adapter; | ||
530 | u16 data = 0; | ||
531 | |||
532 | if (flag) { | ||
533 | data = (u16) deviation; | ||
534 | pr_info(DEVICE_NAME ": write deviation %d\n", | ||
535 | deviation); | ||
536 | eeprom_write_ushort(adap, 0x1000 + chan->number, data); | ||
537 | } else { | ||
538 | if (eeprom_read_ushort(adap, 0x1000 + chan->number, &data)) | ||
539 | data = 0; | ||
540 | pr_info(DEVICE_NAME ": read deviation %d\n", | ||
541 | (s16) data); | ||
542 | } | ||
543 | |||
544 | return (s16) data; | ||
545 | } | ||
546 | |||
316 | /****************************************************************************/ | 547 | /****************************************************************************/ |
317 | /* Switch control (I2C gates, etc.) *****************************************/ | 548 | /* Switch control (I2C gates, etc.) *****************************************/ |
318 | /****************************************************************************/ | 549 | /****************************************************************************/ |
@@ -464,6 +695,37 @@ static struct ngene_info ngene_info_m780 = { | |||
464 | .fw_version = 15, | 695 | .fw_version = 15, |
465 | }; | 696 | }; |
466 | 697 | ||
698 | static struct drxd_config fe_terratec_dvbt_0 = { | ||
699 | .index = 0, | ||
700 | .demod_address = 0x70, | ||
701 | .demod_revision = 0xa2, | ||
702 | .demoda_address = 0x00, | ||
703 | .pll_address = 0x60, | ||
704 | .pll_type = DVB_PLL_THOMSON_DTT7520X, | ||
705 | .clock = 20000, | ||
706 | .osc_deviation = osc_deviation, | ||
707 | }; | ||
708 | |||
709 | static struct drxd_config fe_terratec_dvbt_1 = { | ||
710 | .index = 1, | ||
711 | .demod_address = 0x71, | ||
712 | .demod_revision = 0xa2, | ||
713 | .demoda_address = 0x00, | ||
714 | .pll_address = 0x60, | ||
715 | .pll_type = DVB_PLL_THOMSON_DTT7520X, | ||
716 | .clock = 20000, | ||
717 | .osc_deviation = osc_deviation, | ||
718 | }; | ||
719 | |||
720 | static struct ngene_info ngene_info_terratec = { | ||
721 | .type = NGENE_TERRATEC, | ||
722 | .name = "Terratec Integra/Cinergy2400i Dual DVB-T", | ||
723 | .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, | ||
724 | .demod_attach = {demod_attach_drxd, demod_attach_drxd}, | ||
725 | .fe_config = {&fe_terratec_dvbt_0, &fe_terratec_dvbt_1}, | ||
726 | .i2c_access = 1, | ||
727 | }; | ||
728 | |||
467 | /****************************************************************************/ | 729 | /****************************************************************************/ |
468 | 730 | ||
469 | 731 | ||
@@ -488,6 +750,7 @@ static const struct pci_device_id ngene_id_tbl[] __devinitdata = { | |||
488 | NGENE_ID(0x18c3, 0xdd10, ngene_info_duoFlex), | 750 | NGENE_ID(0x18c3, 0xdd10, ngene_info_duoFlex), |
489 | NGENE_ID(0x18c3, 0xdd20, ngene_info_duoFlex), | 751 | NGENE_ID(0x18c3, 0xdd20, ngene_info_duoFlex), |
490 | NGENE_ID(0x1461, 0x062e, ngene_info_m780), | 752 | NGENE_ID(0x1461, 0x062e, ngene_info_m780), |
753 | NGENE_ID(0x153b, 0x1167, ngene_info_terratec), | ||
491 | {0} | 754 | {0} |
492 | }; | 755 | }; |
493 | MODULE_DEVICE_TABLE(pci, ngene_id_tbl); | 756 | MODULE_DEVICE_TABLE(pci, ngene_id_tbl); |