diff options
| author | Antti Palosaari <crope@iki.fi> | 2014-01-26 19:02:53 -0500 |
|---|---|---|
| committer | Mauro Carvalho Chehab <m.chehab@samsung.com> | 2014-03-14 04:24:37 -0400 |
| commit | adaa616ffb697f00db9b4ccb638c5e9e719dbb7f (patch) | |
| tree | d3ef26a4b9abb751942dd6bb7e8ec6adfb853a29 | |
| parent | 28fd31f82dccfcfcb4c80fd916d4caf875c04d90 (diff) | |
[media] e4000: implement controls via v4l2 control framework
Implement gain and bandwidth controls using v4l2 control framework.
Cc: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Antti Palosaari <crope@iki.fi>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
| -rw-r--r-- | drivers/media/tuners/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/media/tuners/e4000.c | 217 | ||||
| -rw-r--r-- | drivers/media/tuners/e4000_priv.h | 77 |
3 files changed, 291 insertions, 5 deletions
diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig index ba2e365296cf..3b95392c75e6 100644 --- a/drivers/media/tuners/Kconfig +++ b/drivers/media/tuners/Kconfig | |||
| @@ -203,7 +203,7 @@ config MEDIA_TUNER_TDA18212 | |||
| 203 | 203 | ||
| 204 | config MEDIA_TUNER_E4000 | 204 | config MEDIA_TUNER_E4000 |
| 205 | tristate "Elonics E4000 silicon tuner" | 205 | tristate "Elonics E4000 silicon tuner" |
| 206 | depends on MEDIA_SUPPORT && I2C | 206 | depends on MEDIA_SUPPORT && I2C && VIDEO_V4L2 |
| 207 | default m if !MEDIA_SUBDRV_AUTOSELECT | 207 | default m if !MEDIA_SUBDRV_AUTOSELECT |
| 208 | help | 208 | help |
| 209 | Elonics E4000 silicon tuner driver. | 209 | Elonics E4000 silicon tuner driver. |
diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c index 015316985245..3a03b026eb0c 100644 --- a/drivers/media/tuners/e4000.c +++ b/drivers/media/tuners/e4000.c | |||
| @@ -385,6 +385,178 @@ static int e4000_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) | |||
| 385 | return 0; | 385 | return 0; |
| 386 | } | 386 | } |
| 387 | 387 | ||
| 388 | static int e4000_set_lna_gain(struct dvb_frontend *fe) | ||
| 389 | { | ||
| 390 | struct e4000_priv *priv = fe->tuner_priv; | ||
| 391 | int ret; | ||
| 392 | u8 u8tmp; | ||
| 393 | dev_dbg(&priv->client->dev, "%s: lna auto=%d->%d val=%d->%d\n", | ||
| 394 | __func__, priv->lna_gain_auto->cur.val, | ||
| 395 | priv->lna_gain_auto->val, priv->lna_gain->cur.val, | ||
| 396 | priv->lna_gain->val); | ||
| 397 | |||
| 398 | if (fe->ops.i2c_gate_ctrl) | ||
| 399 | fe->ops.i2c_gate_ctrl(fe, 1); | ||
| 400 | |||
| 401 | if (priv->lna_gain_auto->val && priv->if_gain_auto->cur.val) | ||
| 402 | u8tmp = 0x17; | ||
| 403 | else if (priv->lna_gain_auto->val) | ||
| 404 | u8tmp = 0x19; | ||
| 405 | else if (priv->if_gain_auto->cur.val) | ||
| 406 | u8tmp = 0x16; | ||
| 407 | else | ||
| 408 | u8tmp = 0x10; | ||
| 409 | |||
| 410 | ret = e4000_wr_reg(priv, 0x1a, u8tmp); | ||
| 411 | if (ret) | ||
| 412 | goto err; | ||
| 413 | |||
| 414 | if (priv->lna_gain_auto->val == false) { | ||
| 415 | ret = e4000_wr_reg(priv, 0x14, priv->lna_gain->val); | ||
| 416 | if (ret) | ||
| 417 | goto err; | ||
| 418 | } | ||
| 419 | |||
| 420 | if (fe->ops.i2c_gate_ctrl) | ||
| 421 | fe->ops.i2c_gate_ctrl(fe, 0); | ||
| 422 | |||
| 423 | return 0; | ||
| 424 | err: | ||
| 425 | if (fe->ops.i2c_gate_ctrl) | ||
| 426 | fe->ops.i2c_gate_ctrl(fe, 0); | ||
| 427 | |||
| 428 | dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret); | ||
| 429 | return ret; | ||
| 430 | } | ||
| 431 | |||
| 432 | static int e4000_set_mixer_gain(struct dvb_frontend *fe) | ||
| 433 | { | ||
| 434 | struct e4000_priv *priv = fe->tuner_priv; | ||
| 435 | int ret; | ||
| 436 | u8 u8tmp; | ||
| 437 | dev_dbg(&priv->client->dev, "%s: mixer auto=%d->%d val=%d->%d\n", | ||
| 438 | __func__, priv->mixer_gain_auto->cur.val, | ||
| 439 | priv->mixer_gain_auto->val, priv->mixer_gain->cur.val, | ||
| 440 | priv->mixer_gain->val); | ||
| 441 | |||
| 442 | if (fe->ops.i2c_gate_ctrl) | ||
| 443 | fe->ops.i2c_gate_ctrl(fe, 1); | ||
| 444 | |||
| 445 | if (priv->mixer_gain_auto->val) | ||
| 446 | u8tmp = 0x15; | ||
| 447 | else | ||
| 448 | u8tmp = 0x14; | ||
| 449 | |||
| 450 | ret = e4000_wr_reg(priv, 0x20, u8tmp); | ||
| 451 | if (ret) | ||
| 452 | goto err; | ||
| 453 | |||
| 454 | if (priv->mixer_gain_auto->val == false) { | ||
| 455 | ret = e4000_wr_reg(priv, 0x15, priv->mixer_gain->val); | ||
| 456 | if (ret) | ||
| 457 | goto err; | ||
| 458 | } | ||
| 459 | |||
| 460 | if (fe->ops.i2c_gate_ctrl) | ||
| 461 | fe->ops.i2c_gate_ctrl(fe, 0); | ||
| 462 | |||
| 463 | return 0; | ||
| 464 | err: | ||
| 465 | if (fe->ops.i2c_gate_ctrl) | ||
| 466 | fe->ops.i2c_gate_ctrl(fe, 0); | ||
| 467 | |||
| 468 | dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret); | ||
| 469 | return ret; | ||
| 470 | } | ||
| 471 | |||
| 472 | static int e4000_set_if_gain(struct dvb_frontend *fe) | ||
| 473 | { | ||
| 474 | struct e4000_priv *priv = fe->tuner_priv; | ||
| 475 | int ret; | ||
| 476 | u8 buf[2]; | ||
| 477 | u8 u8tmp; | ||
| 478 | dev_dbg(&priv->client->dev, "%s: if auto=%d->%d val=%d->%d\n", | ||
| 479 | __func__, priv->if_gain_auto->cur.val, | ||
| 480 | priv->if_gain_auto->val, priv->if_gain->cur.val, | ||
| 481 | priv->if_gain->val); | ||
| 482 | |||
| 483 | if (fe->ops.i2c_gate_ctrl) | ||
| 484 | fe->ops.i2c_gate_ctrl(fe, 1); | ||
| 485 | |||
| 486 | if (priv->if_gain_auto->val && priv->lna_gain_auto->cur.val) | ||
| 487 | u8tmp = 0x17; | ||
| 488 | else if (priv->lna_gain_auto->cur.val) | ||
| 489 | u8tmp = 0x19; | ||
| 490 | else if (priv->if_gain_auto->val) | ||
| 491 | u8tmp = 0x16; | ||
| 492 | else | ||
| 493 | u8tmp = 0x10; | ||
| 494 | |||
| 495 | ret = e4000_wr_reg(priv, 0x1a, u8tmp); | ||
| 496 | if (ret) | ||
| 497 | goto err; | ||
| 498 | |||
| 499 | if (priv->if_gain_auto->val == false) { | ||
| 500 | buf[0] = e4000_if_gain_lut[priv->if_gain->val].reg16_val; | ||
| 501 | buf[1] = e4000_if_gain_lut[priv->if_gain->val].reg17_val; | ||
| 502 | ret = e4000_wr_regs(priv, 0x16, buf, 2); | ||
| 503 | if (ret) | ||
| 504 | goto err; | ||
| 505 | } | ||
| 506 | |||
| 507 | if (fe->ops.i2c_gate_ctrl) | ||
| 508 | fe->ops.i2c_gate_ctrl(fe, 0); | ||
| 509 | |||
| 510 | return 0; | ||
| 511 | err: | ||
| 512 | if (fe->ops.i2c_gate_ctrl) | ||
| 513 | fe->ops.i2c_gate_ctrl(fe, 0); | ||
| 514 | |||
| 515 | dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret); | ||
| 516 | return ret; | ||
| 517 | } | ||
| 518 | |||
| 519 | static int e4000_s_ctrl(struct v4l2_ctrl *ctrl) | ||
| 520 | { | ||
| 521 | struct e4000_priv *priv = | ||
| 522 | container_of(ctrl->handler, struct e4000_priv, hdl); | ||
| 523 | struct dvb_frontend *fe = priv->fe; | ||
| 524 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; | ||
| 525 | int ret; | ||
| 526 | dev_dbg(&priv->client->dev, | ||
| 527 | "%s: id=%d name=%s val=%d min=%d max=%d step=%d\n", | ||
| 528 | __func__, ctrl->id, ctrl->name, ctrl->val, | ||
| 529 | ctrl->minimum, ctrl->maximum, ctrl->step); | ||
| 530 | |||
| 531 | switch (ctrl->id) { | ||
| 532 | case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: | ||
| 533 | case V4L2_CID_RF_TUNER_BANDWIDTH: | ||
| 534 | c->bandwidth_hz = priv->bandwidth->val; | ||
| 535 | ret = e4000_set_params(priv->fe); | ||
| 536 | break; | ||
| 537 | case V4L2_CID_RF_TUNER_LNA_GAIN_AUTO: | ||
| 538 | case V4L2_CID_RF_TUNER_LNA_GAIN: | ||
| 539 | ret = e4000_set_lna_gain(priv->fe); | ||
| 540 | break; | ||
| 541 | case V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO: | ||
| 542 | case V4L2_CID_RF_TUNER_MIXER_GAIN: | ||
| 543 | ret = e4000_set_mixer_gain(priv->fe); | ||
| 544 | break; | ||
| 545 | case V4L2_CID_RF_TUNER_IF_GAIN_AUTO: | ||
| 546 | case V4L2_CID_RF_TUNER_IF_GAIN: | ||
| 547 | ret = e4000_set_if_gain(priv->fe); | ||
| 548 | break; | ||
| 549 | default: | ||
| 550 | ret = -EINVAL; | ||
| 551 | } | ||
| 552 | |||
| 553 | return ret; | ||
| 554 | } | ||
| 555 | |||
| 556 | static const struct v4l2_ctrl_ops e4000_ctrl_ops = { | ||
| 557 | .s_ctrl = e4000_s_ctrl, | ||
| 558 | }; | ||
| 559 | |||
| 388 | static const struct dvb_tuner_ops e4000_tuner_ops = { | 560 | static const struct dvb_tuner_ops e4000_tuner_ops = { |
| 389 | .info = { | 561 | .info = { |
| 390 | .name = "Elonics E4000", | 562 | .name = "Elonics E4000", |
| @@ -399,6 +571,10 @@ static const struct dvb_tuner_ops e4000_tuner_ops = { | |||
| 399 | .get_if_frequency = e4000_get_if_frequency, | 571 | .get_if_frequency = e4000_get_if_frequency, |
| 400 | }; | 572 | }; |
| 401 | 573 | ||
| 574 | /* | ||
| 575 | * Use V4L2 subdev to carry V4L2 control handler, even we don't implement | ||
| 576 | * subdev itself, just to avoid reinventing the wheel. | ||
| 577 | */ | ||
| 402 | static int e4000_probe(struct i2c_client *client, | 578 | static int e4000_probe(struct i2c_client *client, |
| 403 | const struct i2c_device_id *id) | 579 | const struct i2c_device_id *id) |
| 404 | { | 580 | { |
| @@ -440,6 +616,37 @@ static int e4000_probe(struct i2c_client *client, | |||
| 440 | if (ret < 0) | 616 | if (ret < 0) |
| 441 | goto err; | 617 | goto err; |
| 442 | 618 | ||
| 619 | /* Register controls */ | ||
| 620 | v4l2_ctrl_handler_init(&priv->hdl, 8); | ||
| 621 | priv->bandwidth_auto = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops, | ||
| 622 | V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1); | ||
| 623 | priv->bandwidth = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops, | ||
| 624 | V4L2_CID_RF_TUNER_BANDWIDTH, 4300000, 11000000, 100000, 4300000); | ||
| 625 | v4l2_ctrl_auto_cluster(2, &priv->bandwidth_auto, 0, false); | ||
| 626 | priv->lna_gain_auto = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops, | ||
| 627 | V4L2_CID_RF_TUNER_LNA_GAIN_AUTO, 0, 1, 1, 1); | ||
| 628 | priv->lna_gain = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops, | ||
| 629 | V4L2_CID_RF_TUNER_LNA_GAIN, 0, 15, 1, 10); | ||
| 630 | v4l2_ctrl_auto_cluster(2, &priv->lna_gain_auto, 0, false); | ||
| 631 | priv->mixer_gain_auto = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops, | ||
| 632 | V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO, 0, 1, 1, 1); | ||
| 633 | priv->mixer_gain = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops, | ||
| 634 | V4L2_CID_RF_TUNER_MIXER_GAIN, 0, 1, 1, 1); | ||
| 635 | v4l2_ctrl_auto_cluster(2, &priv->mixer_gain_auto, 0, false); | ||
| 636 | priv->if_gain_auto = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops, | ||
| 637 | V4L2_CID_RF_TUNER_IF_GAIN_AUTO, 0, 1, 1, 1); | ||
| 638 | priv->if_gain = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops, | ||
| 639 | V4L2_CID_RF_TUNER_IF_GAIN, 0, 54, 1, 0); | ||
| 640 | v4l2_ctrl_auto_cluster(2, &priv->if_gain_auto, 0, false); | ||
| 641 | if (priv->hdl.error) { | ||
| 642 | ret = priv->hdl.error; | ||
| 643 | dev_err(&priv->client->dev, "Could not initialize controls\n"); | ||
| 644 | v4l2_ctrl_handler_free(&priv->hdl); | ||
| 645 | goto err; | ||
| 646 | } | ||
| 647 | |||
| 648 | priv->sd.ctrl_handler = &priv->hdl; | ||
| 649 | |||
| 443 | dev_info(&priv->client->dev, | 650 | dev_info(&priv->client->dev, |
| 444 | "%s: Elonics E4000 successfully identified\n", | 651 | "%s: Elonics E4000 successfully identified\n", |
| 445 | KBUILD_MODNAME); | 652 | KBUILD_MODNAME); |
| @@ -448,11 +655,12 @@ static int e4000_probe(struct i2c_client *client, | |||
| 448 | memcpy(&fe->ops.tuner_ops, &e4000_tuner_ops, | 655 | memcpy(&fe->ops.tuner_ops, &e4000_tuner_ops, |
| 449 | sizeof(struct dvb_tuner_ops)); | 656 | sizeof(struct dvb_tuner_ops)); |
| 450 | 657 | ||
| 658 | v4l2_set_subdevdata(&priv->sd, client); | ||
| 659 | i2c_set_clientdata(client, &priv->sd); | ||
| 660 | |||
| 451 | if (fe->ops.i2c_gate_ctrl) | 661 | if (fe->ops.i2c_gate_ctrl) |
| 452 | fe->ops.i2c_gate_ctrl(fe, 0); | 662 | fe->ops.i2c_gate_ctrl(fe, 0); |
| 453 | 663 | ||
| 454 | i2c_set_clientdata(client, priv); | ||
| 455 | |||
| 456 | return 0; | 664 | return 0; |
| 457 | err: | 665 | err: |
| 458 | if (fe->ops.i2c_gate_ctrl) | 666 | if (fe->ops.i2c_gate_ctrl) |
| @@ -465,11 +673,12 @@ err: | |||
| 465 | 673 | ||
| 466 | static int e4000_remove(struct i2c_client *client) | 674 | static int e4000_remove(struct i2c_client *client) |
| 467 | { | 675 | { |
| 468 | struct e4000_priv *priv = i2c_get_clientdata(client); | 676 | struct v4l2_subdev *sd = i2c_get_clientdata(client); |
| 677 | struct e4000_priv *priv = container_of(sd, struct e4000_priv, sd); | ||
| 469 | struct dvb_frontend *fe = priv->fe; | 678 | struct dvb_frontend *fe = priv->fe; |
| 470 | 679 | ||
| 471 | dev_dbg(&client->dev, "%s:\n", __func__); | 680 | dev_dbg(&client->dev, "%s:\n", __func__); |
| 472 | 681 | v4l2_ctrl_handler_free(&priv->hdl); | |
| 473 | memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops)); | 682 | memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops)); |
| 474 | fe->tuner_priv = NULL; | 683 | fe->tuner_priv = NULL; |
| 475 | kfree(priv); | 684 | kfree(priv); |
diff --git a/drivers/media/tuners/e4000_priv.h b/drivers/media/tuners/e4000_priv.h index 8f45a300f688..e2ad54f52280 100644 --- a/drivers/media/tuners/e4000_priv.h +++ b/drivers/media/tuners/e4000_priv.h | |||
| @@ -22,11 +22,25 @@ | |||
| 22 | #define E4000_PRIV_H | 22 | #define E4000_PRIV_H |
| 23 | 23 | ||
| 24 | #include "e4000.h" | 24 | #include "e4000.h" |
| 25 | #include <media/v4l2-ctrls.h> | ||
| 26 | #include <media/v4l2-subdev.h> | ||
| 25 | 27 | ||
| 26 | struct e4000_priv { | 28 | struct e4000_priv { |
| 27 | struct i2c_client *client; | 29 | struct i2c_client *client; |
| 28 | u32 clock; | 30 | u32 clock; |
| 29 | struct dvb_frontend *fe; | 31 | struct dvb_frontend *fe; |
| 32 | struct v4l2_subdev sd; | ||
| 33 | |||
| 34 | /* Controls */ | ||
| 35 | struct v4l2_ctrl_handler hdl; | ||
| 36 | struct v4l2_ctrl *bandwidth_auto; | ||
| 37 | struct v4l2_ctrl *bandwidth; | ||
| 38 | struct v4l2_ctrl *lna_gain_auto; | ||
| 39 | struct v4l2_ctrl *lna_gain; | ||
| 40 | struct v4l2_ctrl *mixer_gain_auto; | ||
| 41 | struct v4l2_ctrl *mixer_gain; | ||
| 42 | struct v4l2_ctrl *if_gain_auto; | ||
| 43 | struct v4l2_ctrl *if_gain; | ||
| 30 | }; | 44 | }; |
| 31 | 45 | ||
| 32 | struct e4000_pll { | 46 | struct e4000_pll { |
| @@ -145,4 +159,67 @@ static const struct e4000_if_filter e4000_if_filter_lut[] = { | |||
| 145 | { 0xffffffff, 0x00, 0x20 }, | 159 | { 0xffffffff, 0x00, 0x20 }, |
| 146 | }; | 160 | }; |
| 147 | 161 | ||
| 162 | struct e4000_if_gain { | ||
| 163 | u8 reg16_val; | ||
| 164 | u8 reg17_val; | ||
| 165 | }; | ||
| 166 | |||
| 167 | static const struct e4000_if_gain e4000_if_gain_lut[] = { | ||
| 168 | {0x00, 0x00}, | ||
| 169 | {0x20, 0x00}, | ||
| 170 | {0x40, 0x00}, | ||
| 171 | {0x02, 0x00}, | ||
| 172 | {0x22, 0x00}, | ||
| 173 | {0x42, 0x00}, | ||
| 174 | {0x04, 0x00}, | ||
| 175 | {0x24, 0x00}, | ||
| 176 | {0x44, 0x00}, | ||
| 177 | {0x01, 0x00}, | ||
| 178 | {0x21, 0x00}, | ||
| 179 | {0x41, 0x00}, | ||
| 180 | {0x03, 0x00}, | ||
| 181 | {0x23, 0x00}, | ||
| 182 | {0x43, 0x00}, | ||
| 183 | {0x05, 0x00}, | ||
| 184 | {0x25, 0x00}, | ||
| 185 | {0x45, 0x00}, | ||
| 186 | {0x07, 0x00}, | ||
| 187 | {0x27, 0x00}, | ||
| 188 | {0x47, 0x00}, | ||
| 189 | {0x0f, 0x00}, | ||
| 190 | {0x2f, 0x00}, | ||
| 191 | {0x4f, 0x00}, | ||
| 192 | {0x17, 0x00}, | ||
| 193 | {0x37, 0x00}, | ||
| 194 | {0x57, 0x00}, | ||
| 195 | {0x1f, 0x00}, | ||
| 196 | {0x3f, 0x00}, | ||
| 197 | {0x5f, 0x00}, | ||
| 198 | {0x1f, 0x01}, | ||
| 199 | {0x3f, 0x01}, | ||
| 200 | {0x5f, 0x01}, | ||
| 201 | {0x1f, 0x02}, | ||
| 202 | {0x3f, 0x02}, | ||
| 203 | {0x5f, 0x02}, | ||
| 204 | {0x1f, 0x03}, | ||
| 205 | {0x3f, 0x03}, | ||
| 206 | {0x5f, 0x03}, | ||
| 207 | {0x1f, 0x04}, | ||
| 208 | {0x3f, 0x04}, | ||
| 209 | {0x5f, 0x04}, | ||
| 210 | {0x1f, 0x0c}, | ||
| 211 | {0x3f, 0x0c}, | ||
| 212 | {0x5f, 0x0c}, | ||
| 213 | {0x1f, 0x14}, | ||
| 214 | {0x3f, 0x14}, | ||
| 215 | {0x5f, 0x14}, | ||
| 216 | {0x1f, 0x1c}, | ||
| 217 | {0x3f, 0x1c}, | ||
| 218 | {0x5f, 0x1c}, | ||
| 219 | {0x1f, 0x24}, | ||
| 220 | {0x3f, 0x24}, | ||
| 221 | {0x5f, 0x24}, | ||
| 222 | {0x7f, 0x24}, | ||
| 223 | }; | ||
| 224 | |||
| 148 | #endif | 225 | #endif |
