.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * Silicon Labs Si2146/2147/2148/2157/2158 silicon tuner driver |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 2014 Antti Palosaari <crope@iki.fi> |
---|
5 | | - * |
---|
6 | | - * This program is free software; you can redistribute it and/or modify |
---|
7 | | - * it under the terms of the GNU General Public License as published by |
---|
8 | | - * the Free Software Foundation; either version 2 of the License, or |
---|
9 | | - * (at your option) any later version. |
---|
10 | | - * |
---|
11 | | - * This program is distributed in the hope that it will be useful, |
---|
12 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
13 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
14 | | - * GNU General Public License for more details. |
---|
15 | 6 | */ |
---|
16 | 7 | |
---|
17 | 8 | #include "si2157_priv.h" |
---|
18 | 9 | |
---|
19 | 10 | static const struct dvb_tuner_ops si2157_ops; |
---|
| 11 | + |
---|
| 12 | +static int tuner_lock_debug; |
---|
| 13 | +module_param(tuner_lock_debug, int, 0644); |
---|
| 14 | +MODULE_PARM_DESC(tuner_lock_debug, "if set, signal lock is briefly waited on after setting params"); |
---|
20 | 15 | |
---|
21 | 16 | /* execute firmware command */ |
---|
22 | 17 | static int si2157_cmd_execute(struct i2c_client *client, struct si2157_cmd *cmd) |
---|
.. | .. |
---|
56 | 51 | break; |
---|
57 | 52 | } |
---|
58 | 53 | |
---|
59 | | - dev_dbg(&client->dev, "cmd execution took %d ms\n", |
---|
60 | | - jiffies_to_msecs(jiffies) - |
---|
61 | | - (jiffies_to_msecs(timeout) - TIMEOUT)); |
---|
| 54 | + dev_dbg(&client->dev, "cmd execution took %d ms, status=%x\n", |
---|
| 55 | + jiffies_to_msecs(jiffies) - |
---|
| 56 | + (jiffies_to_msecs(timeout) - TIMEOUT), |
---|
| 57 | + cmd->args[0]); |
---|
62 | 58 | |
---|
63 | 59 | if (!((cmd->args[0] >> 7) & 0x01)) { |
---|
64 | 60 | ret = -ETIMEDOUT; |
---|
| 61 | + goto err_mutex_unlock; |
---|
| 62 | + } |
---|
| 63 | + /* check error status bit */ |
---|
| 64 | + if (cmd->args[0] & 0x40) { |
---|
| 65 | + ret = -EAGAIN; |
---|
65 | 66 | goto err_mutex_unlock; |
---|
66 | 67 | } |
---|
67 | 68 | } |
---|
.. | .. |
---|
114 | 115 | } |
---|
115 | 116 | cmd.rlen = 1; |
---|
116 | 117 | ret = si2157_cmd_execute(client, &cmd); |
---|
117 | | - if (ret) |
---|
| 118 | + if (ret && (dev->chiptype != SI2157_CHIPTYPE_SI2141 || ret != -EAGAIN)) |
---|
118 | 119 | goto err; |
---|
119 | 120 | |
---|
120 | 121 | /* Si2141 needs a second command before it answers the revision query */ |
---|
.. | .. |
---|
124 | 125 | ret = si2157_cmd_execute(client, &cmd); |
---|
125 | 126 | if (ret) |
---|
126 | 127 | goto err; |
---|
| 128 | + } |
---|
| 129 | + |
---|
| 130 | + if (dev->dont_load_firmware) { |
---|
| 131 | + dev_info(&client->dev, "device is buggy, skipping firmware download\n"); |
---|
| 132 | + goto skip_fw_download; |
---|
127 | 133 | } |
---|
128 | 134 | |
---|
129 | 135 | /* query chip revision */ |
---|
.. | .. |
---|
137 | 143 | chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 | cmd.args[3] << 8 | |
---|
138 | 144 | cmd.args[4] << 0; |
---|
139 | 145 | |
---|
| 146 | + #define SI2177_A30 ('A' << 24 | 77 << 16 | '3' << 8 | '0' << 0) |
---|
140 | 147 | #define SI2158_A20 ('A' << 24 | 58 << 16 | '2' << 8 | '0' << 0) |
---|
141 | 148 | #define SI2148_A20 ('A' << 24 | 48 << 16 | '2' << 8 | '0' << 0) |
---|
142 | 149 | #define SI2157_A30 ('A' << 24 | 57 << 16 | '3' << 8 | '0' << 0) |
---|
.. | .. |
---|
151 | 158 | break; |
---|
152 | 159 | case SI2141_A10: |
---|
153 | 160 | fw_name = SI2141_A10_FIRMWARE; |
---|
| 161 | + break; |
---|
| 162 | + case SI2177_A30: |
---|
| 163 | + fw_name = SI2157_A30_FIRMWARE; |
---|
154 | 164 | break; |
---|
155 | 165 | case SI2157_A30: |
---|
156 | 166 | case SI2147_A30: |
---|
.. | .. |
---|
229 | 239 | |
---|
230 | 240 | dev_info(&client->dev, "firmware version: %c.%c.%d\n", |
---|
231 | 241 | cmd.args[6], cmd.args[7], cmd.args[8]); |
---|
| 242 | + |
---|
| 243 | + /* enable tuner status flags */ |
---|
| 244 | + memcpy(cmd.args, "\x14\x00\x01\x05\x01\x00", 6); |
---|
| 245 | + cmd.wlen = 6; |
---|
| 246 | + cmd.rlen = 1; |
---|
| 247 | + ret = si2157_cmd_execute(client, &cmd); |
---|
| 248 | + if (ret) |
---|
| 249 | + goto err; |
---|
| 250 | + |
---|
| 251 | + memcpy(cmd.args, "\x14\x00\x01\x06\x01\x00", 6); |
---|
| 252 | + cmd.wlen = 6; |
---|
| 253 | + cmd.rlen = 1; |
---|
| 254 | + ret = si2157_cmd_execute(client, &cmd); |
---|
| 255 | + if (ret) |
---|
| 256 | + goto err; |
---|
| 257 | + |
---|
| 258 | + memcpy(cmd.args, "\x14\x00\x01\x07\x01\x00", 6); |
---|
| 259 | + cmd.wlen = 6; |
---|
| 260 | + cmd.rlen = 1; |
---|
| 261 | + ret = si2157_cmd_execute(client, &cmd); |
---|
| 262 | + if (ret) |
---|
| 263 | + goto err; |
---|
232 | 264 | warm: |
---|
233 | 265 | /* init statistics in order signal app which are supported */ |
---|
234 | 266 | c->strength.len = 1; |
---|
.. | .. |
---|
270 | 302 | return 0; |
---|
271 | 303 | err: |
---|
272 | 304 | dev_dbg(&client->dev, "failed=%d\n", ret); |
---|
| 305 | + return ret; |
---|
| 306 | +} |
---|
| 307 | + |
---|
| 308 | +static int si2157_tune_wait(struct i2c_client *client, u8 is_digital) |
---|
| 309 | +{ |
---|
| 310 | +#define TUN_TIMEOUT 40 |
---|
| 311 | +#define DIG_TIMEOUT 30 |
---|
| 312 | +#define ANALOG_TIMEOUT 150 |
---|
| 313 | + struct si2157_dev *dev = i2c_get_clientdata(client); |
---|
| 314 | + int ret; |
---|
| 315 | + unsigned long timeout; |
---|
| 316 | + unsigned long start_time; |
---|
| 317 | + u8 wait_status; |
---|
| 318 | + u8 tune_lock_mask; |
---|
| 319 | + |
---|
| 320 | + if (is_digital) |
---|
| 321 | + tune_lock_mask = 0x04; |
---|
| 322 | + else |
---|
| 323 | + tune_lock_mask = 0x02; |
---|
| 324 | + |
---|
| 325 | + mutex_lock(&dev->i2c_mutex); |
---|
| 326 | + |
---|
| 327 | + /* wait tuner command complete */ |
---|
| 328 | + start_time = jiffies; |
---|
| 329 | + timeout = start_time + msecs_to_jiffies(TUN_TIMEOUT); |
---|
| 330 | + while (1) { |
---|
| 331 | + ret = i2c_master_recv(client, &wait_status, |
---|
| 332 | + sizeof(wait_status)); |
---|
| 333 | + if (ret < 0) { |
---|
| 334 | + goto err_mutex_unlock; |
---|
| 335 | + } else if (ret != sizeof(wait_status)) { |
---|
| 336 | + ret = -EREMOTEIO; |
---|
| 337 | + goto err_mutex_unlock; |
---|
| 338 | + } |
---|
| 339 | + |
---|
| 340 | + if (time_after(jiffies, timeout)) |
---|
| 341 | + break; |
---|
| 342 | + |
---|
| 343 | + /* tuner done? */ |
---|
| 344 | + if ((wait_status & 0x81) == 0x81) |
---|
| 345 | + break; |
---|
| 346 | + usleep_range(5000, 10000); |
---|
| 347 | + } |
---|
| 348 | + |
---|
| 349 | + dev_dbg(&client->dev, "tuning took %d ms, status=0x%x\n", |
---|
| 350 | + jiffies_to_msecs(jiffies) - jiffies_to_msecs(start_time), |
---|
| 351 | + wait_status); |
---|
| 352 | + |
---|
| 353 | + /* if we tuned ok, wait a bit for tuner lock */ |
---|
| 354 | + if (tuner_lock_debug && (wait_status & 0x81) == 0x81) { |
---|
| 355 | + if (is_digital) |
---|
| 356 | + timeout = jiffies + msecs_to_jiffies(DIG_TIMEOUT); |
---|
| 357 | + else |
---|
| 358 | + timeout = jiffies + msecs_to_jiffies(ANALOG_TIMEOUT); |
---|
| 359 | + |
---|
| 360 | + while (!time_after(jiffies, timeout)) { |
---|
| 361 | + ret = i2c_master_recv(client, &wait_status, |
---|
| 362 | + sizeof(wait_status)); |
---|
| 363 | + if (ret < 0) { |
---|
| 364 | + goto err_mutex_unlock; |
---|
| 365 | + } else if (ret != sizeof(wait_status)) { |
---|
| 366 | + ret = -EREMOTEIO; |
---|
| 367 | + goto err_mutex_unlock; |
---|
| 368 | + } |
---|
| 369 | + |
---|
| 370 | + /* tuner locked? */ |
---|
| 371 | + if (wait_status & tune_lock_mask) |
---|
| 372 | + break; |
---|
| 373 | + usleep_range(5000, 10000); |
---|
| 374 | + } |
---|
| 375 | + |
---|
| 376 | + dev_dbg(&client->dev, "tuning+lock took %d ms, status=0x%x\n", |
---|
| 377 | + jiffies_to_msecs(jiffies) - jiffies_to_msecs(start_time), |
---|
| 378 | + wait_status); |
---|
| 379 | + } |
---|
| 380 | + |
---|
| 381 | + if ((wait_status & 0xc0) != 0x80) { |
---|
| 382 | + ret = -ETIMEDOUT; |
---|
| 383 | + goto err_mutex_unlock; |
---|
| 384 | + } |
---|
| 385 | + |
---|
| 386 | + mutex_unlock(&dev->i2c_mutex); |
---|
| 387 | + return 0; |
---|
| 388 | + |
---|
| 389 | +err_mutex_unlock: |
---|
| 390 | + mutex_unlock(&dev->i2c_mutex); |
---|
| 391 | + dev_err(&client->dev, "failed=%d\n", ret); |
---|
273 | 392 | return ret; |
---|
274 | 393 | } |
---|
275 | 394 | |
---|
.. | .. |
---|
343 | 462 | if (ret) |
---|
344 | 463 | goto err; |
---|
345 | 464 | |
---|
346 | | - /* set if frequency if needed */ |
---|
| 465 | + /* set digital if frequency if needed */ |
---|
347 | 466 | if (if_frequency != dev->if_frequency) { |
---|
348 | 467 | memcpy(cmd.args, "\x14\x00\x06\x07", 4); |
---|
349 | 468 | cmd.args[4] = (if_frequency / 1000) & 0xff; |
---|
.. | .. |
---|
357 | 476 | dev->if_frequency = if_frequency; |
---|
358 | 477 | } |
---|
359 | 478 | |
---|
360 | | - /* set frequency */ |
---|
| 479 | + /* set digital frequency */ |
---|
361 | 480 | memcpy(cmd.args, "\x41\x00\x00\x00\x00\x00\x00\x00", 8); |
---|
362 | 481 | cmd.args[4] = (c->frequency >> 0) & 0xff; |
---|
363 | 482 | cmd.args[5] = (c->frequency >> 8) & 0xff; |
---|
.. | .. |
---|
369 | 488 | if (ret) |
---|
370 | 489 | goto err; |
---|
371 | 490 | |
---|
| 491 | + dev->bandwidth = bandwidth; |
---|
| 492 | + dev->frequency = c->frequency; |
---|
| 493 | + |
---|
| 494 | + si2157_tune_wait(client, 1); /* wait to complete, ignore any errors */ |
---|
| 495 | + |
---|
372 | 496 | return 0; |
---|
373 | 497 | err: |
---|
| 498 | + dev->bandwidth = 0; |
---|
| 499 | + dev->frequency = 0; |
---|
| 500 | + dev->if_frequency = 0; |
---|
374 | 501 | dev_dbg(&client->dev, "failed=%d\n", ret); |
---|
375 | 502 | return ret; |
---|
| 503 | +} |
---|
| 504 | + |
---|
| 505 | +static int si2157_set_analog_params(struct dvb_frontend *fe, |
---|
| 506 | + struct analog_parameters *params) |
---|
| 507 | +{ |
---|
| 508 | + struct i2c_client *client = fe->tuner_priv; |
---|
| 509 | + struct si2157_dev *dev = i2c_get_clientdata(client); |
---|
| 510 | + char *std; /* for debugging */ |
---|
| 511 | + int ret; |
---|
| 512 | + struct si2157_cmd cmd; |
---|
| 513 | + u32 bandwidth = 0; |
---|
| 514 | + u32 if_frequency = 0; |
---|
| 515 | + u32 freq = 0; |
---|
| 516 | + u64 tmp_lval = 0; |
---|
| 517 | + u8 system = 0; |
---|
| 518 | + u8 color = 0; /* 0=NTSC/PAL, 0x10=SECAM */ |
---|
| 519 | + u8 invert_analog = 1; /* analog tuner spectrum; 0=normal, 1=inverted */ |
---|
| 520 | + |
---|
| 521 | + if (dev->chiptype != SI2157_CHIPTYPE_SI2157) { |
---|
| 522 | + dev_info(&client->dev, "Analog tuning not supported for chiptype=%u\n", |
---|
| 523 | + dev->chiptype); |
---|
| 524 | + ret = -EINVAL; |
---|
| 525 | + goto err; |
---|
| 526 | + } |
---|
| 527 | + |
---|
| 528 | + if (!dev->active) |
---|
| 529 | + si2157_init(fe); |
---|
| 530 | + |
---|
| 531 | + if (!dev->active) { |
---|
| 532 | + ret = -EAGAIN; |
---|
| 533 | + goto err; |
---|
| 534 | + } |
---|
| 535 | + if (params->mode == V4L2_TUNER_RADIO) { |
---|
| 536 | + /* |
---|
| 537 | + * std = "fm"; |
---|
| 538 | + * bandwidth = 1700000; //best can do for FM, AGC will be a mess though |
---|
| 539 | + * if_frequency = 1250000; //HVR-225x(saa7164), HVR-12xx(cx23885) |
---|
| 540 | + * if_frequency = 6600000; //HVR-9xx(cx231xx) |
---|
| 541 | + * if_frequency = 5500000; //HVR-19xx(pvrusb2) |
---|
| 542 | + */ |
---|
| 543 | + dev_err(&client->dev, "si2157 does not currently support FM radio\n"); |
---|
| 544 | + ret = -EINVAL; |
---|
| 545 | + goto err; |
---|
| 546 | + } |
---|
| 547 | + tmp_lval = params->frequency * 625LL; |
---|
| 548 | + do_div(tmp_lval, 10); /* convert to HZ */ |
---|
| 549 | + freq = (u32)tmp_lval; |
---|
| 550 | + |
---|
| 551 | + if (freq < 1000000) /* is freq in KHz */ |
---|
| 552 | + freq = freq * 1000; |
---|
| 553 | + dev->frequency = freq; |
---|
| 554 | + |
---|
| 555 | + /* if_frequency values based on tda187271C2 */ |
---|
| 556 | + if (params->std & (V4L2_STD_B | V4L2_STD_GH)) { |
---|
| 557 | + if (freq >= 470000000) { |
---|
| 558 | + std = "palGH"; |
---|
| 559 | + bandwidth = 8000000; |
---|
| 560 | + if_frequency = 6000000; |
---|
| 561 | + system = 1; |
---|
| 562 | + if (params->std & |
---|
| 563 | + (V4L2_STD_SECAM_G | V4L2_STD_SECAM_H)) { |
---|
| 564 | + std = "secamGH"; |
---|
| 565 | + color = 0x10; |
---|
| 566 | + } |
---|
| 567 | + } else { |
---|
| 568 | + std = "palB"; |
---|
| 569 | + bandwidth = 7000000; |
---|
| 570 | + if_frequency = 6000000; |
---|
| 571 | + system = 0; |
---|
| 572 | + if (params->std & V4L2_STD_SECAM_B) { |
---|
| 573 | + std = "secamB"; |
---|
| 574 | + color = 0x10; |
---|
| 575 | + } |
---|
| 576 | + } |
---|
| 577 | + } else if (params->std & V4L2_STD_MN) { |
---|
| 578 | + std = "MN"; |
---|
| 579 | + bandwidth = 6000000; |
---|
| 580 | + if_frequency = 5400000; |
---|
| 581 | + system = 2; |
---|
| 582 | + } else if (params->std & V4L2_STD_PAL_I) { |
---|
| 583 | + std = "palI"; |
---|
| 584 | + bandwidth = 8000000; |
---|
| 585 | + if_frequency = 7250000; /* TODO: does not work yet */ |
---|
| 586 | + system = 4; |
---|
| 587 | + } else if (params->std & V4L2_STD_DK) { |
---|
| 588 | + std = "palDK"; |
---|
| 589 | + bandwidth = 8000000; |
---|
| 590 | + if_frequency = 6900000; /* TODO: does not work yet */ |
---|
| 591 | + system = 5; |
---|
| 592 | + if (params->std & V4L2_STD_SECAM_DK) { |
---|
| 593 | + std = "secamDK"; |
---|
| 594 | + color = 0x10; |
---|
| 595 | + } |
---|
| 596 | + } else if (params->std & V4L2_STD_SECAM_L) { |
---|
| 597 | + std = "secamL"; |
---|
| 598 | + bandwidth = 8000000; |
---|
| 599 | + if_frequency = 6750000; /* TODO: untested */ |
---|
| 600 | + system = 6; |
---|
| 601 | + color = 0x10; |
---|
| 602 | + } else if (params->std & V4L2_STD_SECAM_LC) { |
---|
| 603 | + std = "secamL'"; |
---|
| 604 | + bandwidth = 7000000; |
---|
| 605 | + if_frequency = 1250000; /* TODO: untested */ |
---|
| 606 | + system = 7; |
---|
| 607 | + color = 0x10; |
---|
| 608 | + } else { |
---|
| 609 | + std = "unknown"; |
---|
| 610 | + } |
---|
| 611 | + /* calc channel center freq */ |
---|
| 612 | + freq = freq - 1250000 + (bandwidth / 2); |
---|
| 613 | + |
---|
| 614 | + dev_dbg(&client->dev, |
---|
| 615 | + "mode=%d system=%u std='%s' params->frequency=%u center freq=%u if=%u bandwidth=%u\n", |
---|
| 616 | + params->mode, system, std, params->frequency, |
---|
| 617 | + freq, if_frequency, bandwidth); |
---|
| 618 | + |
---|
| 619 | + /* set analog IF port */ |
---|
| 620 | + memcpy(cmd.args, "\x14\x00\x03\x06\x08\x02", 6); |
---|
| 621 | + /* in using dev->if_port, we assume analog and digital IF's */ |
---|
| 622 | + /* are always on different ports */ |
---|
| 623 | + /* assumes if_port definition is 0 or 1 for digital out */ |
---|
| 624 | + cmd.args[4] = (dev->if_port == 1) ? 8 : 10; |
---|
| 625 | + /* Analog AGC assumed external */ |
---|
| 626 | + cmd.args[5] = (dev->if_port == 1) ? 2 : 1; |
---|
| 627 | + cmd.wlen = 6; |
---|
| 628 | + cmd.rlen = 4; |
---|
| 629 | + ret = si2157_cmd_execute(client, &cmd); |
---|
| 630 | + if (ret) |
---|
| 631 | + goto err; |
---|
| 632 | + |
---|
| 633 | + /* set analog IF output config */ |
---|
| 634 | + memcpy(cmd.args, "\x14\x00\x0d\x06\x94\x64", 6); |
---|
| 635 | + cmd.wlen = 6; |
---|
| 636 | + cmd.rlen = 4; |
---|
| 637 | + ret = si2157_cmd_execute(client, &cmd); |
---|
| 638 | + if (ret) |
---|
| 639 | + goto err; |
---|
| 640 | + |
---|
| 641 | + /* make this distinct from a digital IF */ |
---|
| 642 | + dev->if_frequency = if_frequency | 1; |
---|
| 643 | + |
---|
| 644 | + /* calc and set tuner analog if center frequency */ |
---|
| 645 | + if_frequency = if_frequency + 1250000 - (bandwidth / 2); |
---|
| 646 | + dev_dbg(&client->dev, "IF Ctr freq=%d\n", if_frequency); |
---|
| 647 | + |
---|
| 648 | + memcpy(cmd.args, "\x14\x00\x0C\x06", 4); |
---|
| 649 | + cmd.args[4] = (if_frequency / 1000) & 0xff; |
---|
| 650 | + cmd.args[5] = ((if_frequency / 1000) >> 8) & 0xff; |
---|
| 651 | + cmd.wlen = 6; |
---|
| 652 | + cmd.rlen = 4; |
---|
| 653 | + ret = si2157_cmd_execute(client, &cmd); |
---|
| 654 | + if (ret) |
---|
| 655 | + goto err; |
---|
| 656 | + |
---|
| 657 | + /* set analog AGC config */ |
---|
| 658 | + memcpy(cmd.args, "\x14\x00\x07\x06\x32\xc8", 6); |
---|
| 659 | + cmd.wlen = 6; |
---|
| 660 | + cmd.rlen = 4; |
---|
| 661 | + ret = si2157_cmd_execute(client, &cmd); |
---|
| 662 | + if (ret) |
---|
| 663 | + goto err; |
---|
| 664 | + |
---|
| 665 | + /* set analog video mode */ |
---|
| 666 | + memcpy(cmd.args, "\x14\x00\x04\x06\x00\x00", 6); |
---|
| 667 | + cmd.args[4] = system | color; |
---|
| 668 | + /* can use dev->inversion if assumed applies to both digital/analog */ |
---|
| 669 | + if (invert_analog) |
---|
| 670 | + cmd.args[5] |= 0x02; |
---|
| 671 | + cmd.wlen = 6; |
---|
| 672 | + cmd.rlen = 1; |
---|
| 673 | + ret = si2157_cmd_execute(client, &cmd); |
---|
| 674 | + if (ret) |
---|
| 675 | + goto err; |
---|
| 676 | + |
---|
| 677 | + /* set analog frequency */ |
---|
| 678 | + memcpy(cmd.args, "\x41\x01\x00\x00\x00\x00\x00\x00", 8); |
---|
| 679 | + cmd.args[4] = (freq >> 0) & 0xff; |
---|
| 680 | + cmd.args[5] = (freq >> 8) & 0xff; |
---|
| 681 | + cmd.args[6] = (freq >> 16) & 0xff; |
---|
| 682 | + cmd.args[7] = (freq >> 24) & 0xff; |
---|
| 683 | + cmd.wlen = 8; |
---|
| 684 | + cmd.rlen = 1; |
---|
| 685 | + ret = si2157_cmd_execute(client, &cmd); |
---|
| 686 | + if (ret) |
---|
| 687 | + goto err; |
---|
| 688 | + |
---|
| 689 | + dev->bandwidth = bandwidth; |
---|
| 690 | + |
---|
| 691 | + si2157_tune_wait(client, 0); /* wait to complete, ignore any errors */ |
---|
| 692 | + |
---|
| 693 | + return 0; |
---|
| 694 | +err: |
---|
| 695 | + dev->bandwidth = 0; |
---|
| 696 | + dev->frequency = 0; |
---|
| 697 | + dev->if_frequency = 0; |
---|
| 698 | + dev_dbg(&client->dev, "failed=%d\n", ret); |
---|
| 699 | + return ret; |
---|
| 700 | +} |
---|
| 701 | + |
---|
| 702 | +static int si2157_get_frequency(struct dvb_frontend *fe, u32 *frequency) |
---|
| 703 | +{ |
---|
| 704 | + struct i2c_client *client = fe->tuner_priv; |
---|
| 705 | + struct si2157_dev *dev = i2c_get_clientdata(client); |
---|
| 706 | + |
---|
| 707 | + *frequency = dev->frequency; |
---|
| 708 | + dev_dbg(&client->dev, "freq=%u\n", dev->frequency); |
---|
| 709 | + return 0; |
---|
| 710 | +} |
---|
| 711 | + |
---|
| 712 | +static int si2157_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) |
---|
| 713 | +{ |
---|
| 714 | + struct i2c_client *client = fe->tuner_priv; |
---|
| 715 | + struct si2157_dev *dev = i2c_get_clientdata(client); |
---|
| 716 | + |
---|
| 717 | + *bandwidth = dev->bandwidth; |
---|
| 718 | + dev_dbg(&client->dev, "bandwidth=%u\n", dev->bandwidth); |
---|
| 719 | + return 0; |
---|
376 | 720 | } |
---|
377 | 721 | |
---|
378 | 722 | static int si2157_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) |
---|
.. | .. |
---|
380 | 724 | struct i2c_client *client = fe->tuner_priv; |
---|
381 | 725 | struct si2157_dev *dev = i2c_get_clientdata(client); |
---|
382 | 726 | |
---|
383 | | - *frequency = dev->if_frequency; |
---|
| 727 | + *frequency = dev->if_frequency & ~1; /* strip analog IF indicator bit */ |
---|
| 728 | + dev_dbg(&client->dev, "if_frequency=%u\n", *frequency); |
---|
384 | 729 | return 0; |
---|
| 730 | +} |
---|
| 731 | + |
---|
| 732 | +static int si2157_get_rf_strength(struct dvb_frontend *fe, u16 *rssi) |
---|
| 733 | +{ |
---|
| 734 | + struct i2c_client *client = fe->tuner_priv; |
---|
| 735 | + struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
---|
| 736 | + struct si2157_cmd cmd; |
---|
| 737 | + int ret; |
---|
| 738 | + int strength; |
---|
| 739 | + |
---|
| 740 | + dev_dbg(&client->dev, "\n"); |
---|
| 741 | + |
---|
| 742 | + memcpy(cmd.args, "\x42\x00", 2); |
---|
| 743 | + cmd.wlen = 2; |
---|
| 744 | + cmd.rlen = 12; |
---|
| 745 | + ret = si2157_cmd_execute(client, &cmd); |
---|
| 746 | + if (ret) |
---|
| 747 | + goto err; |
---|
| 748 | + |
---|
| 749 | + c->strength.stat[0].scale = FE_SCALE_DECIBEL; |
---|
| 750 | + c->strength.stat[0].svalue = (s8)cmd.args[3] * 1000; |
---|
| 751 | + |
---|
| 752 | + /* normalize values based on Silicon Labs reference |
---|
| 753 | + * add 100, then anything > 80 is 100% signal |
---|
| 754 | + */ |
---|
| 755 | + strength = (s8)cmd.args[3] + 100; |
---|
| 756 | + strength = clamp_val(strength, 0, 80); |
---|
| 757 | + *rssi = (u16)(strength * 0xffff / 80); |
---|
| 758 | + |
---|
| 759 | + dev_dbg(&client->dev, "strength=%d rssi=%u\n", |
---|
| 760 | + (s8)cmd.args[3], *rssi); |
---|
| 761 | + |
---|
| 762 | + return 0; |
---|
| 763 | +err: |
---|
| 764 | + dev_dbg(&client->dev, "failed=%d\n", ret); |
---|
| 765 | + return ret; |
---|
385 | 766 | } |
---|
386 | 767 | |
---|
387 | 768 | static const struct dvb_tuner_ops si2157_ops = { |
---|
.. | .. |
---|
394 | 775 | .init = si2157_init, |
---|
395 | 776 | .sleep = si2157_sleep, |
---|
396 | 777 | .set_params = si2157_set_params, |
---|
397 | | - .get_if_frequency = si2157_get_if_frequency, |
---|
| 778 | + .set_analog_params = si2157_set_analog_params, |
---|
| 779 | + .get_frequency = si2157_get_frequency, |
---|
| 780 | + .get_bandwidth = si2157_get_bandwidth, |
---|
| 781 | + .get_if_frequency = si2157_get_if_frequency, |
---|
| 782 | + |
---|
| 783 | + .get_rf_strength = si2157_get_rf_strength, |
---|
398 | 784 | }; |
---|
399 | 785 | |
---|
400 | 786 | static void si2157_stat_work(struct work_struct *work) |
---|
.. | .. |
---|
444 | 830 | i2c_set_clientdata(client, dev); |
---|
445 | 831 | dev->fe = cfg->fe; |
---|
446 | 832 | dev->inversion = cfg->inversion; |
---|
| 833 | + dev->dont_load_firmware = cfg->dont_load_firmware; |
---|
447 | 834 | dev->if_port = cfg->if_port; |
---|
448 | 835 | dev->chiptype = (u8)id->driver_data; |
---|
449 | 836 | dev->if_frequency = 5000000; /* default value of property 0x0706 */ |
---|
.. | .. |
---|
454 | 841 | cmd.wlen = 0; |
---|
455 | 842 | cmd.rlen = 1; |
---|
456 | 843 | ret = si2157_cmd_execute(client, &cmd); |
---|
457 | | - if (ret) |
---|
| 844 | + if (ret && ret != -EAGAIN) |
---|
458 | 845 | goto err_kfree; |
---|
459 | 846 | |
---|
460 | 847 | memcpy(&fe->ops.tuner_ops, &si2157_ops, sizeof(struct dvb_tuner_ops)); |
---|
.. | .. |
---|
467 | 854 | dev->ent.name = KBUILD_MODNAME; |
---|
468 | 855 | dev->ent.function = MEDIA_ENT_F_TUNER; |
---|
469 | 856 | |
---|
470 | | - dev->pad[TUNER_PAD_RF_INPUT].flags = MEDIA_PAD_FL_SINK; |
---|
471 | | - dev->pad[TUNER_PAD_OUTPUT].flags = MEDIA_PAD_FL_SOURCE; |
---|
472 | | - dev->pad[TUNER_PAD_AUD_OUT].flags = MEDIA_PAD_FL_SOURCE; |
---|
| 857 | + dev->pad[SI2157_PAD_RF_INPUT].flags = MEDIA_PAD_FL_SINK; |
---|
| 858 | + dev->pad[SI2157_PAD_RF_INPUT].sig_type = PAD_SIGNAL_ANALOG; |
---|
| 859 | + dev->pad[SI2157_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE; |
---|
| 860 | + dev->pad[SI2157_PAD_VID_OUT].sig_type = PAD_SIGNAL_ANALOG; |
---|
| 861 | + dev->pad[SI2157_PAD_AUD_OUT].flags = MEDIA_PAD_FL_SOURCE; |
---|
| 862 | + dev->pad[SI2157_PAD_AUD_OUT].sig_type = PAD_SIGNAL_AUDIO; |
---|
473 | 863 | |
---|
474 | | - ret = media_entity_pads_init(&dev->ent, TUNER_NUM_PADS, |
---|
| 864 | + ret = media_entity_pads_init(&dev->ent, SI2157_NUM_PADS, |
---|
475 | 865 | &dev->pad[0]); |
---|
476 | 866 | |
---|
477 | 867 | if (ret) |
---|
.. | .. |
---|
525 | 915 | {"si2157", SI2157_CHIPTYPE_SI2157}, |
---|
526 | 916 | {"si2146", SI2157_CHIPTYPE_SI2146}, |
---|
527 | 917 | {"si2141", SI2157_CHIPTYPE_SI2141}, |
---|
| 918 | + {"si2177", SI2157_CHIPTYPE_SI2177}, |
---|
528 | 919 | {} |
---|
529 | 920 | }; |
---|
530 | 921 | MODULE_DEVICE_TABLE(i2c, si2157_id_table); |
---|
.. | .. |
---|
546 | 937 | MODULE_LICENSE("GPL"); |
---|
547 | 938 | MODULE_FIRMWARE(SI2158_A20_FIRMWARE); |
---|
548 | 939 | MODULE_FIRMWARE(SI2141_A10_FIRMWARE); |
---|
| 940 | +MODULE_FIRMWARE(SI2157_A30_FIRMWARE); |
---|