| .. | .. |
|---|
| 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); |
|---|