diff options
Diffstat (limited to 'drivers/media/video/msp3400-driver.c')
-rw-r--r-- | drivers/media/video/msp3400-driver.c | 402 |
1 files changed, 223 insertions, 179 deletions
diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index 3da74dcee902..a622dbb72ed8 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c | |||
@@ -51,14 +51,14 @@ | |||
51 | #include <linux/module.h> | 51 | #include <linux/module.h> |
52 | #include <linux/slab.h> | 52 | #include <linux/slab.h> |
53 | #include <linux/i2c.h> | 53 | #include <linux/i2c.h> |
54 | #include <linux/kthread.h> | ||
55 | #include <linux/freezer.h> | ||
54 | #include <linux/videodev2.h> | 56 | #include <linux/videodev2.h> |
55 | #include <media/v4l2-common.h> | 57 | #include <media/v4l2-device.h> |
56 | #include <media/v4l2-ioctl.h> | 58 | #include <media/v4l2-ioctl.h> |
57 | #include <media/v4l2-i2c-drv-legacy.h> | 59 | #include <media/v4l2-i2c-drv-legacy.h> |
58 | #include <media/tvaudio.h> | ||
59 | #include <media/msp3400.h> | 60 | #include <media/msp3400.h> |
60 | #include <linux/kthread.h> | 61 | #include <media/tvaudio.h> |
61 | #include <linux/freezer.h> | ||
62 | #include "msp3400-driver.h" | 62 | #include "msp3400-driver.h" |
63 | 63 | ||
64 | /* ---------------------------------------------------------------------- */ | 64 | /* ---------------------------------------------------------------------- */ |
@@ -265,7 +265,7 @@ static char *scart_names[] = { | |||
265 | 265 | ||
266 | void msp_set_scart(struct i2c_client *client, int in, int out) | 266 | void msp_set_scart(struct i2c_client *client, int in, int out) |
267 | { | 267 | { |
268 | struct msp_state *state = i2c_get_clientdata(client); | 268 | struct msp_state *state = to_state(i2c_get_clientdata(client)); |
269 | 269 | ||
270 | state->in_scart = in; | 270 | state->in_scart = in; |
271 | 271 | ||
@@ -289,7 +289,7 @@ void msp_set_scart(struct i2c_client *client, int in, int out) | |||
289 | 289 | ||
290 | void msp_set_audio(struct i2c_client *client) | 290 | void msp_set_audio(struct i2c_client *client) |
291 | { | 291 | { |
292 | struct msp_state *state = i2c_get_clientdata(client); | 292 | struct msp_state *state = to_state(i2c_get_clientdata(client)); |
293 | int bal = 0, bass, treble, loudness; | 293 | int bal = 0, bass, treble, loudness; |
294 | int val = 0; | 294 | int val = 0; |
295 | int reallymuted = state->muted | state->scan_in_progress; | 295 | int reallymuted = state->muted | state->scan_in_progress; |
@@ -336,7 +336,7 @@ void msp_set_audio(struct i2c_client *client) | |||
336 | 336 | ||
337 | static void msp_wake_thread(struct i2c_client *client) | 337 | static void msp_wake_thread(struct i2c_client *client) |
338 | { | 338 | { |
339 | struct msp_state *state = i2c_get_clientdata(client); | 339 | struct msp_state *state = to_state(i2c_get_clientdata(client)); |
340 | 340 | ||
341 | if (NULL == state->kthread) | 341 | if (NULL == state->kthread) |
342 | return; | 342 | return; |
@@ -390,9 +390,9 @@ static int msp_mode_v4l1_to_v4l2(int mode) | |||
390 | } | 390 | } |
391 | #endif | 391 | #endif |
392 | 392 | ||
393 | static int msp_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) | 393 | static int msp_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) |
394 | { | 394 | { |
395 | struct msp_state *state = i2c_get_clientdata(client); | 395 | struct msp_state *state = to_state(sd); |
396 | 396 | ||
397 | switch (ctrl->id) { | 397 | switch (ctrl->id) { |
398 | case V4L2_CID_AUDIO_VOLUME: | 398 | case V4L2_CID_AUDIO_VOLUME: |
@@ -433,9 +433,10 @@ static int msp_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) | |||
433 | return 0; | 433 | return 0; |
434 | } | 434 | } |
435 | 435 | ||
436 | static int msp_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) | 436 | static int msp_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) |
437 | { | 437 | { |
438 | struct msp_state *state = i2c_get_clientdata(client); | 438 | struct msp_state *state = to_state(sd); |
439 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
439 | 440 | ||
440 | switch (ctrl->id) { | 441 | switch (ctrl->id) { |
441 | case V4L2_CID_AUDIO_VOLUME: | 442 | case V4L2_CID_AUDIO_VOLUME: |
@@ -481,40 +482,16 @@ static int msp_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) | |||
481 | return 0; | 482 | return 0; |
482 | } | 483 | } |
483 | 484 | ||
484 | static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) | 485 | #ifdef CONFIG_VIDEO_ALLOW_V4L1 |
486 | static int msp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) | ||
485 | { | 487 | { |
486 | struct msp_state *state = i2c_get_clientdata(client); | 488 | struct msp_state *state = to_state(sd); |
487 | 489 | struct i2c_client *client = v4l2_get_subdevdata(sd); | |
488 | if (msp_debug >= 2) | ||
489 | v4l_i2c_print_ioctl(client, cmd); | ||
490 | 490 | ||
491 | switch (cmd) { | 491 | switch (cmd) { |
492 | case AUDC_SET_RADIO: | ||
493 | if (state->radio) | ||
494 | return 0; | ||
495 | state->radio = 1; | ||
496 | v4l_dbg(1, msp_debug, client, "switching to radio mode\n"); | ||
497 | state->watch_stereo = 0; | ||
498 | switch (state->opmode) { | ||
499 | case OPMODE_MANUAL: | ||
500 | /* set msp3400 to FM radio mode */ | ||
501 | msp3400c_set_mode(client, MSP_MODE_FM_RADIO); | ||
502 | msp3400c_set_carrier(client, MSP_CARRIER(10.7), | ||
503 | MSP_CARRIER(10.7)); | ||
504 | msp_set_audio(client); | ||
505 | break; | ||
506 | case OPMODE_AUTODETECT: | ||
507 | case OPMODE_AUTOSELECT: | ||
508 | /* the thread will do for us */ | ||
509 | msp_wake_thread(client); | ||
510 | break; | ||
511 | } | ||
512 | break; | ||
513 | |||
514 | /* --- v4l ioctls --- */ | 492 | /* --- v4l ioctls --- */ |
515 | /* take care: bttv does userspace copying, we'll get a | 493 | /* take care: bttv does userspace copying, we'll get a |
516 | kernel pointer here... */ | 494 | kernel pointer here... */ |
517 | #ifdef CONFIG_VIDEO_ALLOW_V4L1 | ||
518 | case VIDIOCGAUDIO: | 495 | case VIDIOCGAUDIO: |
519 | { | 496 | { |
520 | struct video_audio *va = arg; | 497 | struct video_audio *va = arg; |
@@ -588,105 +565,137 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) | |||
588 | msp_wake_thread(client); | 565 | msp_wake_thread(client); |
589 | break; | 566 | break; |
590 | } | 567 | } |
591 | #endif | 568 | default: |
592 | case VIDIOC_S_FREQUENCY: | 569 | return -ENOIOCTLCMD; |
593 | { | ||
594 | /* new channel -- kick audio carrier scan */ | ||
595 | msp_wake_thread(client); | ||
596 | break; | ||
597 | } | 570 | } |
571 | return 0; | ||
572 | } | ||
573 | #endif | ||
598 | 574 | ||
599 | /* --- v4l2 ioctls --- */ | 575 | /* --- v4l2 ioctls --- */ |
600 | case VIDIOC_S_STD: | 576 | static int msp_s_radio(struct v4l2_subdev *sd) |
601 | { | 577 | { |
602 | v4l2_std_id *id = arg; | 578 | struct msp_state *state = to_state(sd); |
603 | int update = state->radio || state->v4l2_std != *id; | 579 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
604 | 580 | ||
605 | state->v4l2_std = *id; | 581 | if (state->radio) |
606 | state->radio = 0; | ||
607 | if (update) | ||
608 | msp_wake_thread(client); | ||
609 | return 0; | 582 | return 0; |
583 | state->radio = 1; | ||
584 | v4l_dbg(1, msp_debug, client, "switching to radio mode\n"); | ||
585 | state->watch_stereo = 0; | ||
586 | switch (state->opmode) { | ||
587 | case OPMODE_MANUAL: | ||
588 | /* set msp3400 to FM radio mode */ | ||
589 | msp3400c_set_mode(client, MSP_MODE_FM_RADIO); | ||
590 | msp3400c_set_carrier(client, MSP_CARRIER(10.7), | ||
591 | MSP_CARRIER(10.7)); | ||
592 | msp_set_audio(client); | ||
593 | break; | ||
594 | case OPMODE_AUTODETECT: | ||
595 | case OPMODE_AUTOSELECT: | ||
596 | /* the thread will do for us */ | ||
597 | msp_wake_thread(client); | ||
598 | break; | ||
610 | } | 599 | } |
600 | return 0; | ||
601 | } | ||
611 | 602 | ||
612 | case VIDIOC_INT_G_AUDIO_ROUTING: | 603 | static int msp_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) |
613 | { | 604 | { |
614 | struct v4l2_routing *rt = arg; | 605 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
615 | 606 | ||
616 | *rt = state->routing; | 607 | /* new channel -- kick audio carrier scan */ |
617 | break; | 608 | msp_wake_thread(client); |
618 | } | 609 | return 0; |
610 | } | ||
619 | 611 | ||
620 | case VIDIOC_INT_S_AUDIO_ROUTING: | 612 | static int msp_s_std(struct v4l2_subdev *sd, v4l2_std_id id) |
621 | { | 613 | { |
622 | struct v4l2_routing *rt = arg; | 614 | struct msp_state *state = to_state(sd); |
623 | int tuner = (rt->input >> 3) & 1; | 615 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
624 | int sc_in = rt->input & 0x7; | 616 | int update = state->radio || state->v4l2_std != id; |
625 | int sc1_out = rt->output & 0xf; | 617 | |
626 | int sc2_out = (rt->output >> 4) & 0xf; | 618 | state->v4l2_std = id; |
627 | u16 val, reg; | 619 | state->radio = 0; |
628 | int i; | 620 | if (update) |
629 | int extern_input = 1; | ||
630 | |||
631 | if (state->routing.input == rt->input && | ||
632 | state->routing.output == rt->output) | ||
633 | break; | ||
634 | state->routing = *rt; | ||
635 | /* check if the tuner input is used */ | ||
636 | for (i = 0; i < 5; i++) { | ||
637 | if (((rt->input >> (4 + i * 4)) & 0xf) == 0) | ||
638 | extern_input = 0; | ||
639 | } | ||
640 | state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT; | ||
641 | state->rxsubchans = V4L2_TUNER_SUB_STEREO; | ||
642 | msp_set_scart(client, sc_in, 0); | ||
643 | msp_set_scart(client, sc1_out, 1); | ||
644 | msp_set_scart(client, sc2_out, 2); | ||
645 | msp_set_audmode(client); | ||
646 | reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb; | ||
647 | val = msp_read_dem(client, reg); | ||
648 | msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8)); | ||
649 | /* wake thread when a new input is chosen */ | ||
650 | msp_wake_thread(client); | 621 | msp_wake_thread(client); |
651 | break; | 622 | return 0; |
623 | } | ||
624 | |||
625 | static int msp_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *rt) | ||
626 | { | ||
627 | struct msp_state *state = to_state(sd); | ||
628 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
629 | int tuner = (rt->input >> 3) & 1; | ||
630 | int sc_in = rt->input & 0x7; | ||
631 | int sc1_out = rt->output & 0xf; | ||
632 | int sc2_out = (rt->output >> 4) & 0xf; | ||
633 | u16 val, reg; | ||
634 | int i; | ||
635 | int extern_input = 1; | ||
636 | |||
637 | if (state->routing.input == rt->input && | ||
638 | state->routing.output == rt->output) | ||
639 | return 0; | ||
640 | state->routing = *rt; | ||
641 | /* check if the tuner input is used */ | ||
642 | for (i = 0; i < 5; i++) { | ||
643 | if (((rt->input >> (4 + i * 4)) & 0xf) == 0) | ||
644 | extern_input = 0; | ||
652 | } | 645 | } |
646 | state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT; | ||
647 | state->rxsubchans = V4L2_TUNER_SUB_STEREO; | ||
648 | msp_set_scart(client, sc_in, 0); | ||
649 | msp_set_scart(client, sc1_out, 1); | ||
650 | msp_set_scart(client, sc2_out, 2); | ||
651 | msp_set_audmode(client); | ||
652 | reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb; | ||
653 | val = msp_read_dem(client, reg); | ||
654 | msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8)); | ||
655 | /* wake thread when a new input is chosen */ | ||
656 | msp_wake_thread(client); | ||
657 | return 0; | ||
658 | } | ||
653 | 659 | ||
654 | case VIDIOC_G_TUNER: | 660 | static int msp_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) |
655 | { | 661 | { |
656 | struct v4l2_tuner *vt = arg; | 662 | struct msp_state *state = to_state(sd); |
663 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
657 | 664 | ||
658 | if (state->radio) | 665 | if (state->radio) |
659 | break; | 666 | return 0; |
660 | if (state->opmode == OPMODE_AUTOSELECT) | 667 | if (state->opmode == OPMODE_AUTOSELECT) |
661 | msp_detect_stereo(client); | 668 | msp_detect_stereo(client); |
662 | vt->audmode = state->audmode; | 669 | vt->audmode = state->audmode; |
663 | vt->rxsubchans = state->rxsubchans; | 670 | vt->rxsubchans = state->rxsubchans; |
664 | vt->capability |= V4L2_TUNER_CAP_STEREO | | 671 | vt->capability |= V4L2_TUNER_CAP_STEREO | |
665 | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; | 672 | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; |
666 | break; | 673 | return 0; |
667 | } | 674 | } |
668 | 675 | ||
669 | case VIDIOC_S_TUNER: | 676 | static int msp_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) |
670 | { | 677 | { |
671 | struct v4l2_tuner *vt = (struct v4l2_tuner *)arg; | 678 | struct msp_state *state = to_state(sd); |
679 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
672 | 680 | ||
673 | if (state->radio) /* TODO: add mono/stereo support for radio */ | 681 | if (state->radio) /* TODO: add mono/stereo support for radio */ |
674 | break; | 682 | return 0; |
675 | if (state->audmode == vt->audmode) | 683 | if (state->audmode == vt->audmode) |
676 | break; | 684 | return 0; |
677 | state->audmode = vt->audmode; | 685 | state->audmode = vt->audmode; |
678 | /* only set audmode */ | 686 | /* only set audmode */ |
679 | msp_set_audmode(client); | 687 | msp_set_audmode(client); |
680 | break; | 688 | return 0; |
681 | } | 689 | } |
682 | 690 | ||
683 | case VIDIOC_INT_I2S_CLOCK_FREQ: | 691 | static int msp_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq) |
684 | { | 692 | { |
685 | u32 *a = (u32 *)arg; | 693 | struct msp_state *state = to_state(sd); |
694 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
686 | 695 | ||
687 | v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", *a); | 696 | v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", freq); |
688 | 697 | ||
689 | switch (*a) { | 698 | switch (freq) { |
690 | case 1024000: | 699 | case 1024000: |
691 | state->i2s_mode = 0; | 700 | state->i2s_mode = 0; |
692 | break; | 701 | break; |
@@ -695,24 +704,24 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) | |||
695 | break; | 704 | break; |
696 | default: | 705 | default: |
697 | return -EINVAL; | 706 | return -EINVAL; |
698 | } | ||
699 | break; | ||
700 | } | 707 | } |
708 | return 0; | ||
709 | } | ||
701 | 710 | ||
702 | case VIDIOC_QUERYCTRL: | 711 | static int msp_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) |
703 | { | 712 | { |
704 | struct v4l2_queryctrl *qc = arg; | 713 | struct msp_state *state = to_state(sd); |
705 | 714 | ||
706 | switch (qc->id) { | 715 | switch (qc->id) { |
707 | case V4L2_CID_AUDIO_VOLUME: | 716 | case V4L2_CID_AUDIO_VOLUME: |
708 | case V4L2_CID_AUDIO_MUTE: | 717 | case V4L2_CID_AUDIO_MUTE: |
709 | return v4l2_ctrl_query_fill_std(qc); | 718 | return v4l2_ctrl_query_fill_std(qc); |
710 | default: | 719 | default: |
711 | break; | 720 | break; |
712 | } | 721 | } |
713 | if (!state->has_sound_processing) | 722 | if (!state->has_sound_processing) |
714 | return -EINVAL; | 723 | return -EINVAL; |
715 | switch (qc->id) { | 724 | switch (qc->id) { |
716 | case V4L2_CID_AUDIO_LOUDNESS: | 725 | case V4L2_CID_AUDIO_LOUDNESS: |
717 | case V4L2_CID_AUDIO_BALANCE: | 726 | case V4L2_CID_AUDIO_BALANCE: |
718 | case V4L2_CID_AUDIO_BASS: | 727 | case V4L2_CID_AUDIO_BASS: |
@@ -720,32 +729,38 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) | |||
720 | return v4l2_ctrl_query_fill_std(qc); | 729 | return v4l2_ctrl_query_fill_std(qc); |
721 | default: | 730 | default: |
722 | return -EINVAL; | 731 | return -EINVAL; |
723 | } | ||
724 | } | 732 | } |
733 | return 0; | ||
734 | } | ||
725 | 735 | ||
726 | case VIDIOC_G_CTRL: | 736 | static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) |
727 | return msp_get_ctrl(client, arg); | 737 | { |
728 | 738 | struct msp_state *state = to_state(sd); | |
729 | case VIDIOC_S_CTRL: | 739 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
730 | return msp_set_ctrl(client, arg); | ||
731 | 740 | ||
732 | case VIDIOC_LOG_STATUS: | 741 | return v4l2_chip_ident_i2c_client(client, chip, state->ident, |
733 | { | 742 | (state->rev1 << 16) | state->rev2); |
734 | const char *p; | 743 | } |
735 | 744 | ||
736 | if (state->opmode == OPMODE_AUTOSELECT) | 745 | static int msp_log_status(struct v4l2_subdev *sd) |
737 | msp_detect_stereo(client); | 746 | { |
738 | v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n", | 747 | struct msp_state *state = to_state(sd); |
739 | client->name, state->rev1, state->rev2); | 748 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
740 | v4l_info(client, "Audio: volume %d%s\n", | 749 | const char *p; |
741 | state->volume, state->muted ? " (muted)" : ""); | 750 | |
742 | if (state->has_sound_processing) { | 751 | if (state->opmode == OPMODE_AUTOSELECT) |
743 | v4l_info(client, "Audio: balance %d bass %d treble %d loudness %s\n", | 752 | msp_detect_stereo(client); |
744 | state->balance, state->bass, | 753 | v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n", |
745 | state->treble, | 754 | client->name, state->rev1, state->rev2); |
746 | state->loudness ? "on" : "off"); | 755 | v4l_info(client, "Audio: volume %d%s\n", |
747 | } | 756 | state->volume, state->muted ? " (muted)" : ""); |
748 | switch (state->mode) { | 757 | if (state->has_sound_processing) { |
758 | v4l_info(client, "Audio: balance %d bass %d treble %d loudness %s\n", | ||
759 | state->balance, state->bass, | ||
760 | state->treble, | ||
761 | state->loudness ? "on" : "off"); | ||
762 | } | ||
763 | switch (state->mode) { | ||
749 | case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break; | 764 | case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break; |
750 | case MSP_MODE_FM_RADIO: p = "FM Radio"; break; | 765 | case MSP_MODE_FM_RADIO: p = "FM Radio"; break; |
751 | case MSP_MODE_FM_TERRA: p = "Terrestial FM-mono/stereo"; break; | 766 | case MSP_MODE_FM_TERRA: p = "Terrestial FM-mono/stereo"; break; |
@@ -756,36 +771,25 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) | |||
756 | case MSP_MODE_BTSC: p = "BTSC"; break; | 771 | case MSP_MODE_BTSC: p = "BTSC"; break; |
757 | case MSP_MODE_EXTERN: p = "External input"; break; | 772 | case MSP_MODE_EXTERN: p = "External input"; break; |
758 | default: p = "unknown"; break; | 773 | default: p = "unknown"; break; |
759 | } | 774 | } |
760 | if (state->mode == MSP_MODE_EXTERN) { | 775 | if (state->mode == MSP_MODE_EXTERN) { |
761 | v4l_info(client, "Mode: %s\n", p); | 776 | v4l_info(client, "Mode: %s\n", p); |
762 | } else if (state->opmode == OPMODE_MANUAL) { | 777 | } else if (state->opmode == OPMODE_MANUAL) { |
763 | v4l_info(client, "Mode: %s (%s%s)\n", p, | 778 | v4l_info(client, "Mode: %s (%s%s)\n", p, |
764 | (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", | 779 | (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", |
765 | (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); | 780 | (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); |
766 | } else { | 781 | } else { |
767 | if (state->opmode == OPMODE_AUTODETECT) | 782 | if (state->opmode == OPMODE_AUTODETECT) |
768 | v4l_info(client, "Mode: %s\n", p); | 783 | v4l_info(client, "Mode: %s\n", p); |
769 | v4l_info(client, "Standard: %s (%s%s)\n", | 784 | v4l_info(client, "Standard: %s (%s%s)\n", |
770 | msp_standard_std_name(state->std), | 785 | msp_standard_std_name(state->std), |
771 | (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", | 786 | (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", |
772 | (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); | 787 | (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); |
773 | } | ||
774 | v4l_info(client, "Audmode: 0x%04x\n", state->audmode); | ||
775 | v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n", | ||
776 | state->routing.input, state->routing.output); | ||
777 | v4l_info(client, "ACB: 0x%04x\n", state->acb); | ||
778 | break; | ||
779 | } | ||
780 | |||
781 | case VIDIOC_G_CHIP_IDENT: | ||
782 | return v4l2_chip_ident_i2c_client(client, arg, state->ident, | ||
783 | (state->rev1 << 16) | state->rev2); | ||
784 | |||
785 | default: | ||
786 | /* unknown */ | ||
787 | return -EINVAL; | ||
788 | } | 788 | } |
789 | v4l_info(client, "Audmode: 0x%04x\n", state->audmode); | ||
790 | v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n", | ||
791 | state->routing.input, state->routing.output); | ||
792 | v4l_info(client, "ACB: 0x%04x\n", state->acb); | ||
789 | return 0; | 793 | return 0; |
790 | } | 794 | } |
791 | 795 | ||
@@ -803,11 +807,49 @@ static int msp_resume(struct i2c_client *client) | |||
803 | return 0; | 807 | return 0; |
804 | } | 808 | } |
805 | 809 | ||
810 | static int msp_command(struct i2c_client *client, unsigned cmd, void *arg) | ||
811 | { | ||
812 | return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); | ||
813 | } | ||
814 | |||
815 | /* ----------------------------------------------------------------------- */ | ||
816 | |||
817 | static const struct v4l2_subdev_core_ops msp_core_ops = { | ||
818 | .log_status = msp_log_status, | ||
819 | .g_chip_ident = msp_g_chip_ident, | ||
820 | .g_ctrl = msp_g_ctrl, | ||
821 | .s_ctrl = msp_s_ctrl, | ||
822 | .queryctrl = msp_queryctrl, | ||
823 | #ifdef CONFIG_VIDEO_ALLOW_V4L1 | ||
824 | .ioctl = msp_ioctl, | ||
825 | #endif | ||
826 | }; | ||
827 | |||
828 | static const struct v4l2_subdev_tuner_ops msp_tuner_ops = { | ||
829 | .s_frequency = msp_s_frequency, | ||
830 | .g_tuner = msp_g_tuner, | ||
831 | .s_tuner = msp_s_tuner, | ||
832 | .s_radio = msp_s_radio, | ||
833 | .s_std = msp_s_std, | ||
834 | }; | ||
835 | |||
836 | static const struct v4l2_subdev_audio_ops msp_audio_ops = { | ||
837 | .s_routing = msp_s_routing, | ||
838 | .s_i2s_clock_freq = msp_s_i2s_clock_freq, | ||
839 | }; | ||
840 | |||
841 | static const struct v4l2_subdev_ops msp_ops = { | ||
842 | .core = &msp_core_ops, | ||
843 | .tuner = &msp_tuner_ops, | ||
844 | .audio = &msp_audio_ops, | ||
845 | }; | ||
846 | |||
806 | /* ----------------------------------------------------------------------- */ | 847 | /* ----------------------------------------------------------------------- */ |
807 | 848 | ||
808 | static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) | 849 | static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) |
809 | { | 850 | { |
810 | struct msp_state *state; | 851 | struct msp_state *state; |
852 | struct v4l2_subdev *sd; | ||
811 | int (*thread_func)(void *data) = NULL; | 853 | int (*thread_func)(void *data) = NULL; |
812 | int msp_hard; | 854 | int msp_hard; |
813 | int msp_family; | 855 | int msp_family; |
@@ -827,7 +869,8 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) | |||
827 | if (!state) | 869 | if (!state) |
828 | return -ENOMEM; | 870 | return -ENOMEM; |
829 | 871 | ||
830 | i2c_set_clientdata(client, state); | 872 | sd = &state->sd; |
873 | v4l2_i2c_subdev_init(sd, client, &msp_ops); | ||
831 | 874 | ||
832 | state->v4l2_std = V4L2_STD_NTSC; | 875 | state->v4l2_std = V4L2_STD_NTSC; |
833 | state->audmode = V4L2_TUNER_MODE_STEREO; | 876 | state->audmode = V4L2_TUNER_MODE_STEREO; |
@@ -972,8 +1015,9 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) | |||
972 | 1015 | ||
973 | static int msp_remove(struct i2c_client *client) | 1016 | static int msp_remove(struct i2c_client *client) |
974 | { | 1017 | { |
975 | struct msp_state *state = i2c_get_clientdata(client); | 1018 | struct msp_state *state = to_state(i2c_get_clientdata(client)); |
976 | 1019 | ||
1020 | v4l2_device_unregister_subdev(&state->sd); | ||
977 | /* shutdown control thread */ | 1021 | /* shutdown control thread */ |
978 | if (state->kthread) { | 1022 | if (state->kthread) { |
979 | state->restart = 1; | 1023 | state->restart = 1; |