|
26 | 26 | #define FREQ_OFFSET_LOW_SYM_RATE 3000
|
27 | 27 |
|
28 | 28 | struct ts2020_priv {
|
| 29 | + struct dvb_frontend *fe; |
29 | 30 | /* i2c details */
|
30 | 31 | int i2c_address;
|
31 | 32 | struct i2c_adapter *i2c;
|
@@ -428,6 +429,7 @@ struct dvb_frontend *ts2020_attach(struct dvb_frontend *fe,
|
428 | 429 | priv->clk_out = config->clk_out;
|
429 | 430 | priv->clk_out_div = config->clk_out_div;
|
430 | 431 | priv->frequency_div = config->frequency_div;
|
| 432 | + priv->fe = fe; |
431 | 433 | fe->tuner_priv = priv;
|
432 | 434 |
|
433 | 435 | if (!priv->frequency_div)
|
@@ -463,6 +465,165 @@ struct dvb_frontend *ts2020_attach(struct dvb_frontend *fe,
|
463 | 465 | }
|
464 | 466 | EXPORT_SYMBOL(ts2020_attach);
|
465 | 467 |
|
| 468 | +static int ts2020_probe(struct i2c_client *client, |
| 469 | + const struct i2c_device_id *id) |
| 470 | +{ |
| 471 | + struct ts2020_config *pdata = client->dev.platform_data; |
| 472 | + struct dvb_frontend *fe = pdata->fe; |
| 473 | + struct ts2020_priv *dev; |
| 474 | + int ret; |
| 475 | + u8 u8tmp; |
| 476 | + unsigned int utmp; |
| 477 | + char *chip_str; |
| 478 | + |
| 479 | + dev = kzalloc(sizeof(*dev), GFP_KERNEL); |
| 480 | + if (!dev) { |
| 481 | + ret = -ENOMEM; |
| 482 | + goto err; |
| 483 | + } |
| 484 | + |
| 485 | + dev->i2c = client->adapter; |
| 486 | + dev->i2c_address = client->addr; |
| 487 | + dev->clk_out = pdata->clk_out; |
| 488 | + dev->clk_out_div = pdata->clk_out_div; |
| 489 | + dev->frequency_div = pdata->frequency_div; |
| 490 | + dev->fe = fe; |
| 491 | + fe->tuner_priv = dev; |
| 492 | + |
| 493 | + /* check if the tuner is there */ |
| 494 | + ret = ts2020_readreg(fe, 0x00); |
| 495 | + if (ret < 0) |
| 496 | + goto err; |
| 497 | + utmp = ret; |
| 498 | + |
| 499 | + if ((utmp & 0x03) == 0x00) { |
| 500 | + ret = ts2020_writereg(fe, 0x00, 0x01); |
| 501 | + if (ret) |
| 502 | + goto err; |
| 503 | + |
| 504 | + usleep_range(2000, 50000); |
| 505 | + } |
| 506 | + |
| 507 | + ret = ts2020_writereg(fe, 0x00, 0x03); |
| 508 | + if (ret) |
| 509 | + goto err; |
| 510 | + |
| 511 | + usleep_range(2000, 50000); |
| 512 | + |
| 513 | + ret = ts2020_readreg(fe, 0x00); |
| 514 | + if (ret < 0) |
| 515 | + goto err; |
| 516 | + utmp = ret; |
| 517 | + |
| 518 | + dev_dbg(&client->dev, "chip_id=%02x\n", utmp); |
| 519 | + |
| 520 | + switch (utmp) { |
| 521 | + case 0x01: |
| 522 | + case 0x41: |
| 523 | + case 0x81: |
| 524 | + dev->tuner = TS2020_M88TS2020; |
| 525 | + chip_str = "TS2020"; |
| 526 | + if (!dev->frequency_div) |
| 527 | + dev->frequency_div = 1060000; |
| 528 | + break; |
| 529 | + case 0xc3: |
| 530 | + case 0x83: |
| 531 | + dev->tuner = TS2020_M88TS2022; |
| 532 | + chip_str = "TS2022"; |
| 533 | + if (!dev->frequency_div) |
| 534 | + dev->frequency_div = 1103000; |
| 535 | + break; |
| 536 | + default: |
| 537 | + ret = -ENODEV; |
| 538 | + goto err; |
| 539 | + } |
| 540 | + |
| 541 | + if (dev->tuner == TS2020_M88TS2022) { |
| 542 | + switch (dev->clk_out) { |
| 543 | + case TS2020_CLK_OUT_DISABLED: |
| 544 | + u8tmp = 0x60; |
| 545 | + break; |
| 546 | + case TS2020_CLK_OUT_ENABLED: |
| 547 | + u8tmp = 0x70; |
| 548 | + ret = ts2020_writereg(fe, 0x05, dev->clk_out_div); |
| 549 | + if (ret) |
| 550 | + goto err; |
| 551 | + break; |
| 552 | + case TS2020_CLK_OUT_ENABLED_XTALOUT: |
| 553 | + u8tmp = 0x6c; |
| 554 | + break; |
| 555 | + default: |
| 556 | + ret = -EINVAL; |
| 557 | + goto err; |
| 558 | + } |
| 559 | + |
| 560 | + ret = ts2020_writereg(fe, 0x42, u8tmp); |
| 561 | + if (ret) |
| 562 | + goto err; |
| 563 | + |
| 564 | + if (dev->loop_through) |
| 565 | + u8tmp = 0xec; |
| 566 | + else |
| 567 | + u8tmp = 0x6c; |
| 568 | + |
| 569 | + ret = ts2020_writereg(fe, 0x62, u8tmp); |
| 570 | + if (ret) |
| 571 | + goto err; |
| 572 | + } |
| 573 | + |
| 574 | + /* sleep */ |
| 575 | + ret = ts2020_writereg(fe, 0x00, 0x00); |
| 576 | + if (ret) |
| 577 | + goto err; |
| 578 | + |
| 579 | + dev_info(&client->dev, |
| 580 | + "Montage Technology %s successfully identified\n", chip_str); |
| 581 | + |
| 582 | + memcpy(&fe->ops.tuner_ops, &ts2020_tuner_ops, |
| 583 | + sizeof(struct dvb_tuner_ops)); |
| 584 | + fe->ops.tuner_ops.release = NULL; |
| 585 | + |
| 586 | + i2c_set_clientdata(client, dev); |
| 587 | + return 0; |
| 588 | +err: |
| 589 | + dev_dbg(&client->dev, "failed=%d\n", ret); |
| 590 | + kfree(dev); |
| 591 | + return ret; |
| 592 | +} |
| 593 | + |
| 594 | +static int ts2020_remove(struct i2c_client *client) |
| 595 | +{ |
| 596 | + struct ts2020_priv *dev = i2c_get_clientdata(client); |
| 597 | + struct dvb_frontend *fe = dev->fe; |
| 598 | + |
| 599 | + dev_dbg(&client->dev, "\n"); |
| 600 | + |
| 601 | + memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops)); |
| 602 | + fe->tuner_priv = NULL; |
| 603 | + kfree(dev); |
| 604 | + |
| 605 | + return 0; |
| 606 | +} |
| 607 | + |
| 608 | +static const struct i2c_device_id ts2020_id_table[] = { |
| 609 | + {"ts2020", 0}, |
| 610 | + {"ts2022", 0}, |
| 611 | + {} |
| 612 | +}; |
| 613 | +MODULE_DEVICE_TABLE(i2c, ts2020_id_table); |
| 614 | + |
| 615 | +static struct i2c_driver ts2020_driver = { |
| 616 | + .driver = { |
| 617 | + .owner = THIS_MODULE, |
| 618 | + .name = "ts2020", |
| 619 | + }, |
| 620 | + .probe = ts2020_probe, |
| 621 | + .remove = ts2020_remove, |
| 622 | + .id_table = ts2020_id_table, |
| 623 | +}; |
| 624 | + |
| 625 | +module_i2c_driver(ts2020_driver); |
| 626 | + |
466 | 627 | MODULE_AUTHOR( "Konstantin Dimitrov <[email protected]>");
|
467 | 628 | MODULE_DESCRIPTION("Montage Technology TS2020 - Silicon tuner driver module");
|
468 | 629 | MODULE_LICENSE("GPL");
|
0 commit comments