diff options
author | Andy Walls <awalls@radix.net> | 2009-02-14 00:32:39 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-03-30 11:43:01 -0400 |
commit | 1a2670465ec94029e5df62e3decca9e2f7aea075 (patch) | |
tree | 23b2984c9d92089a726da305a13f53388d7c348c | |
parent | a0beec8f6fd37fc1418970ab6a574fabedd9e324 (diff) |
V4L/DVB (10755): cx18: Convert the integrated A/V decoder core interface to a v4l2_subdev
This is the next step in converting the cx18 driver to use the v4l2_device/
v4l2_subdevice framework. This is a straightforward conversion of the
cx18_av_*[ch] files. It compiles, but leaves the driver in an unlinkable
state at the moment.
Note, the cx18 integrated A/V digitizer will now make a host match at address 1,
as far as v4l2-dbg is concerned. That means it identifies itself as a separate
"chip", and acts as an alias to the integrated A/V decoder registers that are
also available with the host match at address 0.
Signed-off-by: Andy Walls <awalls@radix.net>
[mchehab@redhat.com: fix merge conflicts due to the removal of v4l2_ctrl_query_fill_std()]
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | drivers/media/video/cx18/cx18-av-core.c | 583 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-av-core.h | 13 |
2 files changed, 336 insertions, 260 deletions
diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c index fc576cf1a8b5..9b30f77b5205 100644 --- a/drivers/media/video/cx18/cx18-av-core.c +++ b/drivers/media/video/cx18/cx18-av-core.c | |||
@@ -22,8 +22,10 @@ | |||
22 | * 02110-1301, USA. | 22 | * 02110-1301, USA. |
23 | */ | 23 | */ |
24 | 24 | ||
25 | #include <media/v4l2-chip-ident.h> | ||
25 | #include "cx18-driver.h" | 26 | #include "cx18-driver.h" |
26 | #include "cx18-io.h" | 27 | #include "cx18-io.h" |
28 | #include "cx18-cards.h" | ||
27 | 29 | ||
28 | int cx18_av_write(struct cx18 *cx, u16 addr, u8 value) | 30 | int cx18_av_write(struct cx18 *cx, u16 addr, u8 value) |
29 | { | 31 | { |
@@ -97,15 +99,6 @@ int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask, | |||
97 | or_value); | 99 | or_value); |
98 | } | 100 | } |
99 | 101 | ||
100 | /* ----------------------------------------------------------------------- */ | ||
101 | |||
102 | static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, | ||
103 | enum cx18_av_audio_input aud_input); | ||
104 | static void log_audio_status(struct cx18 *cx); | ||
105 | static void log_video_status(struct cx18 *cx); | ||
106 | |||
107 | /* ----------------------------------------------------------------------- */ | ||
108 | |||
109 | static void cx18_av_initialize(struct cx18 *cx) | 102 | static void cx18_av_initialize(struct cx18 *cx) |
110 | { | 103 | { |
111 | struct cx18_av_state *state = &cx->av_state; | 104 | struct cx18_av_state *state = &cx->av_state; |
@@ -200,7 +193,26 @@ static void cx18_av_initialize(struct cx18 *cx) | |||
200 | state->default_volume = ((state->default_volume / 2) + 23) << 9; | 193 | state->default_volume = ((state->default_volume / 2) + 23) << 9; |
201 | } | 194 | } |
202 | 195 | ||
203 | /* ----------------------------------------------------------------------- */ | 196 | static int cx18_av_reset(struct v4l2_subdev *sd, u32 val) |
197 | { | ||
198 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
199 | |||
200 | cx18_av_initialize(cx); | ||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | static int cx18_av_init_hardware(struct v4l2_subdev *sd, u32 val) | ||
205 | { | ||
206 | struct cx18_av_state *state = to_cx18_av_state(sd); | ||
207 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
208 | |||
209 | if (!state->is_initialized) { | ||
210 | /* initialize on first use */ | ||
211 | state->is_initialized = 1; | ||
212 | cx18_av_initialize(cx); | ||
213 | } | ||
214 | return 0; | ||
215 | } | ||
204 | 216 | ||
205 | void cx18_av_std_setup(struct cx18 *cx) | 217 | void cx18_av_std_setup(struct cx18 *cx) |
206 | { | 218 | { |
@@ -362,7 +374,18 @@ void cx18_av_std_setup(struct cx18 *cx) | |||
362 | cx18_av_write(cx, 0x47f, state->slicer_line_delay); | 374 | cx18_av_write(cx, 0x47f, state->slicer_line_delay); |
363 | } | 375 | } |
364 | 376 | ||
365 | /* ----------------------------------------------------------------------- */ | 377 | static int cx18_av_decode_vbi_line(struct v4l2_subdev *sd, |
378 | struct v4l2_decode_vbi_line *vbi_line) | ||
379 | { | ||
380 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
381 | return cx18_av_vbi(cx, VIDIOC_INT_DECODE_VBI_LINE, vbi_line); | ||
382 | } | ||
383 | |||
384 | static int cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq) | ||
385 | { | ||
386 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
387 | return cx18_av_audio(cx, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freq); | ||
388 | } | ||
366 | 389 | ||
367 | static void input_change(struct cx18 *cx) | 390 | static void input_change(struct cx18 *cx) |
368 | { | 391 | { |
@@ -409,6 +432,14 @@ static void input_change(struct cx18 *cx) | |||
409 | } | 432 | } |
410 | } | 433 | } |
411 | 434 | ||
435 | static int cx18_av_s_frequency(struct v4l2_subdev *sd, | ||
436 | struct v4l2_frequency *freq) | ||
437 | { | ||
438 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
439 | input_change(cx); | ||
440 | return 0; | ||
441 | } | ||
442 | |||
412 | static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, | 443 | static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, |
413 | enum cx18_av_audio_input aud_input) | 444 | enum cx18_av_audio_input aud_input) |
414 | { | 445 | { |
@@ -488,14 +519,118 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, | |||
488 | return 0; | 519 | return 0; |
489 | } | 520 | } |
490 | 521 | ||
491 | /* ----------------------------------------------------------------------- */ | 522 | static int cx18_av_s_video_routing(struct v4l2_subdev *sd, |
523 | const struct v4l2_routing *route) | ||
524 | { | ||
525 | struct cx18_av_state *state = to_cx18_av_state(sd); | ||
526 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
527 | return set_input(cx, route->input, state->aud_input); | ||
528 | } | ||
492 | 529 | ||
493 | static int set_v4lstd(struct cx18 *cx) | 530 | static int cx18_av_s_audio_routing(struct v4l2_subdev *sd, |
531 | const struct v4l2_routing *route) | ||
494 | { | 532 | { |
495 | struct cx18_av_state *state = &cx->av_state; | 533 | struct cx18_av_state *state = to_cx18_av_state(sd); |
534 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
535 | return set_input(cx, state->vid_input, route->input); | ||
536 | } | ||
537 | |||
538 | static int cx18_av_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) | ||
539 | { | ||
540 | struct cx18_av_state *state = to_cx18_av_state(sd); | ||
541 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
542 | u8 vpres; | ||
543 | u8 mode; | ||
544 | int val = 0; | ||
545 | |||
546 | if (state->radio) | ||
547 | return 0; | ||
548 | |||
549 | vpres = cx18_av_read(cx, 0x40e) & 0x20; | ||
550 | vt->signal = vpres ? 0xffff : 0x0; | ||
551 | |||
552 | vt->capability |= | ||
553 | V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | | ||
554 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; | ||
555 | |||
556 | mode = cx18_av_read(cx, 0x804); | ||
557 | |||
558 | /* get rxsubchans and audmode */ | ||
559 | if ((mode & 0xf) == 1) | ||
560 | val |= V4L2_TUNER_SUB_STEREO; | ||
561 | else | ||
562 | val |= V4L2_TUNER_SUB_MONO; | ||
563 | |||
564 | if (mode == 2 || mode == 4) | ||
565 | val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; | ||
566 | |||
567 | if (mode & 0x10) | ||
568 | val |= V4L2_TUNER_SUB_SAP; | ||
569 | |||
570 | vt->rxsubchans = val; | ||
571 | vt->audmode = state->audmode; | ||
572 | return 0; | ||
573 | } | ||
574 | |||
575 | static int cx18_av_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) | ||
576 | { | ||
577 | struct cx18_av_state *state = to_cx18_av_state(sd); | ||
578 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
579 | u8 v; | ||
580 | |||
581 | if (state->radio) | ||
582 | return 0; | ||
583 | |||
584 | v = cx18_av_read(cx, 0x809); | ||
585 | v &= ~0xf; | ||
586 | |||
587 | switch (vt->audmode) { | ||
588 | case V4L2_TUNER_MODE_MONO: | ||
589 | /* mono -> mono | ||
590 | stereo -> mono | ||
591 | bilingual -> lang1 */ | ||
592 | break; | ||
593 | case V4L2_TUNER_MODE_STEREO: | ||
594 | case V4L2_TUNER_MODE_LANG1: | ||
595 | /* mono -> mono | ||
596 | stereo -> stereo | ||
597 | bilingual -> lang1 */ | ||
598 | v |= 0x4; | ||
599 | break; | ||
600 | case V4L2_TUNER_MODE_LANG1_LANG2: | ||
601 | /* mono -> mono | ||
602 | stereo -> stereo | ||
603 | bilingual -> lang1/lang2 */ | ||
604 | v |= 0x7; | ||
605 | break; | ||
606 | case V4L2_TUNER_MODE_LANG2: | ||
607 | /* mono -> mono | ||
608 | stereo -> stereo | ||
609 | bilingual -> lang2 */ | ||
610 | v |= 0x1; | ||
611 | break; | ||
612 | default: | ||
613 | return -EINVAL; | ||
614 | } | ||
615 | cx18_av_write_expect(cx, 0x809, v, v, 0xff); | ||
616 | state->audmode = vt->audmode; | ||
617 | return 0; | ||
618 | } | ||
619 | |||
620 | static int cx18_av_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) | ||
621 | { | ||
622 | struct cx18_av_state *state = to_cx18_av_state(sd); | ||
623 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
624 | |||
496 | u8 fmt = 0; /* zero is autodetect */ | 625 | u8 fmt = 0; /* zero is autodetect */ |
497 | u8 pal_m = 0; | 626 | u8 pal_m = 0; |
498 | 627 | ||
628 | if (state->radio == 0 && state->std == norm) | ||
629 | return 0; | ||
630 | |||
631 | state->radio = 0; | ||
632 | state->std = norm; | ||
633 | |||
499 | /* First tests should be against specific std */ | 634 | /* First tests should be against specific std */ |
500 | if (state->std == V4L2_STD_NTSC_M_JP) { | 635 | if (state->std == V4L2_STD_NTSC_M_JP) { |
501 | fmt = 0x2; | 636 | fmt = 0x2; |
@@ -538,10 +673,17 @@ static int set_v4lstd(struct cx18 *cx) | |||
538 | return 0; | 673 | return 0; |
539 | } | 674 | } |
540 | 675 | ||
541 | /* ----------------------------------------------------------------------- */ | 676 | static int cx18_av_s_radio(struct v4l2_subdev *sd) |
677 | { | ||
678 | struct cx18_av_state *state = to_cx18_av_state(sd); | ||
679 | state->radio = 1; | ||
680 | return 0; | ||
681 | } | ||
542 | 682 | ||
543 | static int set_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl) | 683 | static int cx18_av_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) |
544 | { | 684 | { |
685 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
686 | |||
545 | switch (ctrl->id) { | 687 | switch (ctrl->id) { |
546 | case V4L2_CID_BRIGHTNESS: | 688 | case V4L2_CID_BRIGHTNESS: |
547 | if (ctrl->value < 0 || ctrl->value > 255) { | 689 | if (ctrl->value < 0 || ctrl->value > 255) { |
@@ -593,12 +735,13 @@ static int set_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl) | |||
593 | default: | 735 | default: |
594 | return -EINVAL; | 736 | return -EINVAL; |
595 | } | 737 | } |
596 | |||
597 | return 0; | 738 | return 0; |
598 | } | 739 | } |
599 | 740 | ||
600 | static int get_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl) | 741 | static int cx18_av_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) |
601 | { | 742 | { |
743 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
744 | |||
602 | switch (ctrl->id) { | 745 | switch (ctrl->id) { |
603 | case V4L2_CID_BRIGHTNESS: | 746 | case V4L2_CID_BRIGHTNESS: |
604 | ctrl->value = (s8)cx18_av_read(cx, 0x414) + 128; | 747 | ctrl->value = (s8)cx18_av_read(cx, 0x414) + 128; |
@@ -621,27 +764,59 @@ static int get_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl) | |||
621 | default: | 764 | default: |
622 | return -EINVAL; | 765 | return -EINVAL; |
623 | } | 766 | } |
624 | |||
625 | return 0; | 767 | return 0; |
626 | } | 768 | } |
627 | 769 | ||
628 | /* ----------------------------------------------------------------------- */ | 770 | static int cx18_av_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) |
771 | { | ||
772 | struct cx18_av_state *state = to_cx18_av_state(sd); | ||
773 | |||
774 | switch (qc->id) { | ||
775 | case V4L2_CID_BRIGHTNESS: | ||
776 | return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); | ||
777 | case V4L2_CID_CONTRAST: | ||
778 | case V4L2_CID_SATURATION: | ||
779 | return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); | ||
780 | case V4L2_CID_HUE: | ||
781 | return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); | ||
782 | default: | ||
783 | break; | ||
784 | } | ||
785 | |||
786 | switch (qc->id) { | ||
787 | case V4L2_CID_AUDIO_VOLUME: | ||
788 | return v4l2_ctrl_query_fill(qc, 0, 65535, | ||
789 | 65535 / 100, state->default_volume); | ||
790 | case V4L2_CID_AUDIO_MUTE: | ||
791 | return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); | ||
792 | case V4L2_CID_AUDIO_BALANCE: | ||
793 | case V4L2_CID_AUDIO_BASS: | ||
794 | case V4L2_CID_AUDIO_TREBLE: | ||
795 | return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); | ||
796 | default: | ||
797 | return -EINVAL; | ||
798 | } | ||
799 | return -EINVAL; | ||
800 | } | ||
629 | 801 | ||
630 | static int get_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt) | 802 | static int cx18_av_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) |
631 | { | 803 | { |
804 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
805 | |||
632 | switch (fmt->type) { | 806 | switch (fmt->type) { |
633 | case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: | 807 | case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: |
634 | return cx18_av_vbi(cx, VIDIOC_G_FMT, fmt); | 808 | return cx18_av_vbi(cx, VIDIOC_G_FMT, fmt); |
635 | default: | 809 | default: |
636 | return -EINVAL; | 810 | return -EINVAL; |
637 | } | 811 | } |
638 | |||
639 | return 0; | 812 | return 0; |
640 | } | 813 | } |
641 | 814 | ||
642 | static int set_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt) | 815 | static int cx18_av_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) |
643 | { | 816 | { |
644 | struct cx18_av_state *state = &cx->av_state; | 817 | struct cx18_av_state *state = to_cx18_av_state(sd); |
818 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
819 | |||
645 | struct v4l2_pix_format *pix; | 820 | struct v4l2_pix_format *pix; |
646 | int HSC, VSC, Vsrc, Hsrc, filter, Vlines; | 821 | int HSC, VSC, Vsrc, Hsrc, filter, Vlines; |
647 | int is_50Hz = !(state->std & V4L2_STD_525_60); | 822 | int is_50Hz = !(state->std & V4L2_STD_525_60); |
@@ -701,252 +876,24 @@ static int set_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt) | |||
701 | default: | 876 | default: |
702 | return -EINVAL; | 877 | return -EINVAL; |
703 | } | 878 | } |
704 | |||
705 | return 0; | 879 | return 0; |
706 | } | 880 | } |
707 | 881 | ||
708 | /* ----------------------------------------------------------------------- */ | 882 | static int cx18_av_s_stream(struct v4l2_subdev *sd, int enable) |
709 | |||
710 | static int valid_av_cmd(unsigned int cmd) | ||
711 | { | 883 | { |
712 | switch (cmd) { | 884 | struct cx18 *cx = v4l2_get_subdevdata(sd); |
713 | /* All commands supported by cx18_av_cmd() */ | ||
714 | case VIDIOC_INT_DECODE_VBI_LINE: | ||
715 | case VIDIOC_INT_AUDIO_CLOCK_FREQ: | ||
716 | case VIDIOC_STREAMON: | ||
717 | case VIDIOC_STREAMOFF: | ||
718 | case VIDIOC_LOG_STATUS: | ||
719 | case VIDIOC_G_CTRL: | ||
720 | case VIDIOC_S_CTRL: | ||
721 | case VIDIOC_QUERYCTRL: | ||
722 | case VIDIOC_G_STD: | ||
723 | case VIDIOC_S_STD: | ||
724 | case AUDC_SET_RADIO: | ||
725 | case VIDIOC_INT_G_VIDEO_ROUTING: | ||
726 | case VIDIOC_INT_S_VIDEO_ROUTING: | ||
727 | case VIDIOC_INT_G_AUDIO_ROUTING: | ||
728 | case VIDIOC_INT_S_AUDIO_ROUTING: | ||
729 | case VIDIOC_S_FREQUENCY: | ||
730 | case VIDIOC_G_TUNER: | ||
731 | case VIDIOC_S_TUNER: | ||
732 | case VIDIOC_G_FMT: | ||
733 | case VIDIOC_S_FMT: | ||
734 | case VIDIOC_INT_RESET: | ||
735 | return 1; | ||
736 | default: | ||
737 | return 0; | ||
738 | } | ||
739 | return 0; | ||
740 | } | ||
741 | |||
742 | int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg) | ||
743 | { | ||
744 | struct cx18_av_state *state = &cx->av_state; | ||
745 | struct v4l2_tuner *vt = arg; | ||
746 | struct v4l2_routing *route = arg; | ||
747 | |||
748 | if (!state->is_initialized && valid_av_cmd(cmd)) { | ||
749 | CX18_DEBUG_INFO("cmd %08x triggered fw load\n", cmd); | ||
750 | /* initialize on first use */ | ||
751 | state->is_initialized = 1; | ||
752 | cx18_av_initialize(cx); | ||
753 | } | ||
754 | |||
755 | switch (cmd) { | ||
756 | case VIDIOC_INT_DECODE_VBI_LINE: | ||
757 | return cx18_av_vbi(cx, cmd, arg); | ||
758 | |||
759 | case VIDIOC_INT_AUDIO_CLOCK_FREQ: | ||
760 | return cx18_av_audio(cx, cmd, arg); | ||
761 | 885 | ||
762 | case VIDIOC_STREAMON: | 886 | CX18_DEBUG_INFO("%s output\n", enable ? "enable" : "disable"); |
763 | CX18_DEBUG_INFO("enable output\n"); | 887 | if (enable) { |
764 | cx18_av_write(cx, 0x115, 0x8c); | 888 | cx18_av_write(cx, 0x115, 0x8c); |
765 | cx18_av_write(cx, 0x116, 0x07); | 889 | cx18_av_write(cx, 0x116, 0x07); |
766 | break; | 890 | } else { |
767 | |||
768 | case VIDIOC_STREAMOFF: | ||
769 | CX18_DEBUG_INFO("disable output\n"); | ||
770 | cx18_av_write(cx, 0x115, 0x00); | 891 | cx18_av_write(cx, 0x115, 0x00); |
771 | cx18_av_write(cx, 0x116, 0x00); | 892 | cx18_av_write(cx, 0x116, 0x00); |
772 | break; | ||
773 | |||
774 | case VIDIOC_LOG_STATUS: | ||
775 | log_video_status(cx); | ||
776 | log_audio_status(cx); | ||
777 | break; | ||
778 | |||
779 | case VIDIOC_G_CTRL: | ||
780 | return get_v4lctrl(cx, (struct v4l2_control *)arg); | ||
781 | |||
782 | case VIDIOC_S_CTRL: | ||
783 | return set_v4lctrl(cx, (struct v4l2_control *)arg); | ||
784 | |||
785 | case VIDIOC_QUERYCTRL: | ||
786 | { | ||
787 | struct v4l2_queryctrl *qc = arg; | ||
788 | |||
789 | switch (qc->id) { | ||
790 | case V4L2_CID_BRIGHTNESS: | ||
791 | return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); | ||
792 | case V4L2_CID_CONTRAST: | ||
793 | case V4L2_CID_SATURATION: | ||
794 | return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); | ||
795 | case V4L2_CID_HUE: | ||
796 | return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); | ||
797 | default: | ||
798 | break; | ||
799 | } | ||
800 | |||
801 | switch (qc->id) { | ||
802 | case V4L2_CID_AUDIO_VOLUME: | ||
803 | return v4l2_ctrl_query_fill(qc, 0, 65535, | ||
804 | 65535 / 100, state->default_volume); | ||
805 | case V4L2_CID_AUDIO_MUTE: | ||
806 | return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); | ||
807 | case V4L2_CID_AUDIO_BALANCE: | ||
808 | case V4L2_CID_AUDIO_BASS: | ||
809 | case V4L2_CID_AUDIO_TREBLE: | ||
810 | return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); | ||
811 | default: | ||
812 | return -EINVAL; | ||
813 | } | ||
814 | return -EINVAL; | ||
815 | } | ||
816 | |||
817 | case VIDIOC_G_STD: | ||
818 | *(v4l2_std_id *)arg = state->std; | ||
819 | break; | ||
820 | |||
821 | case VIDIOC_S_STD: | ||
822 | if (state->radio == 0 && state->std == *(v4l2_std_id *)arg) | ||
823 | return 0; | ||
824 | state->radio = 0; | ||
825 | state->std = *(v4l2_std_id *)arg; | ||
826 | return set_v4lstd(cx); | ||
827 | |||
828 | case AUDC_SET_RADIO: | ||
829 | state->radio = 1; | ||
830 | break; | ||
831 | |||
832 | case VIDIOC_INT_G_VIDEO_ROUTING: | ||
833 | route->input = state->vid_input; | ||
834 | route->output = 0; | ||
835 | break; | ||
836 | |||
837 | case VIDIOC_INT_S_VIDEO_ROUTING: | ||
838 | return set_input(cx, route->input, state->aud_input); | ||
839 | |||
840 | case VIDIOC_INT_G_AUDIO_ROUTING: | ||
841 | route->input = state->aud_input; | ||
842 | route->output = 0; | ||
843 | break; | ||
844 | |||
845 | case VIDIOC_INT_S_AUDIO_ROUTING: | ||
846 | return set_input(cx, state->vid_input, route->input); | ||
847 | |||
848 | case VIDIOC_S_FREQUENCY: | ||
849 | input_change(cx); | ||
850 | break; | ||
851 | |||
852 | case VIDIOC_G_TUNER: | ||
853 | { | ||
854 | u8 vpres = cx18_av_read(cx, 0x40e) & 0x20; | ||
855 | u8 mode; | ||
856 | int val = 0; | ||
857 | |||
858 | if (state->radio) | ||
859 | break; | ||
860 | |||
861 | vt->signal = vpres ? 0xffff : 0x0; | ||
862 | |||
863 | vt->capability |= | ||
864 | V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | | ||
865 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; | ||
866 | |||
867 | mode = cx18_av_read(cx, 0x804); | ||
868 | |||
869 | /* get rxsubchans and audmode */ | ||
870 | if ((mode & 0xf) == 1) | ||
871 | val |= V4L2_TUNER_SUB_STEREO; | ||
872 | else | ||
873 | val |= V4L2_TUNER_SUB_MONO; | ||
874 | |||
875 | if (mode == 2 || mode == 4) | ||
876 | val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; | ||
877 | |||
878 | if (mode & 0x10) | ||
879 | val |= V4L2_TUNER_SUB_SAP; | ||
880 | |||
881 | vt->rxsubchans = val; | ||
882 | vt->audmode = state->audmode; | ||
883 | break; | ||
884 | } | 893 | } |
885 | |||
886 | case VIDIOC_S_TUNER: | ||
887 | { | ||
888 | u8 v; | ||
889 | |||
890 | if (state->radio) | ||
891 | break; | ||
892 | |||
893 | v = cx18_av_read(cx, 0x809); | ||
894 | v &= ~0xf; | ||
895 | |||
896 | switch (vt->audmode) { | ||
897 | case V4L2_TUNER_MODE_MONO: | ||
898 | /* mono -> mono | ||
899 | stereo -> mono | ||
900 | bilingual -> lang1 */ | ||
901 | break; | ||
902 | case V4L2_TUNER_MODE_STEREO: | ||
903 | case V4L2_TUNER_MODE_LANG1: | ||
904 | /* mono -> mono | ||
905 | stereo -> stereo | ||
906 | bilingual -> lang1 */ | ||
907 | v |= 0x4; | ||
908 | break; | ||
909 | case V4L2_TUNER_MODE_LANG1_LANG2: | ||
910 | /* mono -> mono | ||
911 | stereo -> stereo | ||
912 | bilingual -> lang1/lang2 */ | ||
913 | v |= 0x7; | ||
914 | break; | ||
915 | case V4L2_TUNER_MODE_LANG2: | ||
916 | /* mono -> mono | ||
917 | stereo -> stereo | ||
918 | bilingual -> lang2 */ | ||
919 | v |= 0x1; | ||
920 | break; | ||
921 | default: | ||
922 | return -EINVAL; | ||
923 | } | ||
924 | cx18_av_write_expect(cx, 0x809, v, v, 0xff); | ||
925 | state->audmode = vt->audmode; | ||
926 | break; | ||
927 | } | ||
928 | |||
929 | case VIDIOC_G_FMT: | ||
930 | return get_v4lfmt(cx, (struct v4l2_format *)arg); | ||
931 | |||
932 | case VIDIOC_S_FMT: | ||
933 | return set_v4lfmt(cx, (struct v4l2_format *)arg); | ||
934 | |||
935 | case VIDIOC_INT_RESET: | ||
936 | cx18_av_initialize(cx); | ||
937 | break; | ||
938 | |||
939 | default: | ||
940 | return -EINVAL; | ||
941 | } | ||
942 | |||
943 | return 0; | 894 | return 0; |
944 | } | 895 | } |
945 | 896 | ||
946 | /* ----------------------------------------------------------------------- */ | ||
947 | |||
948 | /* ----------------------------------------------------------------------- */ | ||
949 | |||
950 | static void log_video_status(struct cx18 *cx) | 897 | static void log_video_status(struct cx18 *cx) |
951 | { | 898 | { |
952 | static const char *const fmt_strs[] = { | 899 | static const char *const fmt_strs[] = { |
@@ -984,8 +931,6 @@ static void log_video_status(struct cx18 *cx) | |||
984 | CX18_INFO("Specified audioclock freq: %d Hz\n", state->audclk_freq); | 931 | CX18_INFO("Specified audioclock freq: %d Hz\n", state->audclk_freq); |
985 | } | 932 | } |
986 | 933 | ||
987 | /* ----------------------------------------------------------------------- */ | ||
988 | |||
989 | static void log_audio_status(struct cx18 *cx) | 934 | static void log_audio_status(struct cx18 *cx) |
990 | { | 935 | { |
991 | struct cx18_av_state *state = &cx->av_state; | 936 | struct cx18_av_state *state = &cx->av_state; |
@@ -1133,3 +1078,123 @@ static void log_audio_status(struct cx18 *cx) | |||
1133 | CX18_INFO("Selected 45 MHz format: %s\n", p); | 1078 | CX18_INFO("Selected 45 MHz format: %s\n", p); |
1134 | } | 1079 | } |
1135 | } | 1080 | } |
1081 | |||
1082 | static int cx18_av_log_status(struct v4l2_subdev *sd) | ||
1083 | { | ||
1084 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
1085 | log_video_status(cx); | ||
1086 | log_audio_status(cx); | ||
1087 | return 0; | ||
1088 | } | ||
1089 | |||
1090 | static inline int cx18_av_dbg_match(const struct v4l2_dbg_match *match) | ||
1091 | { | ||
1092 | return match->type == V4L2_CHIP_MATCH_HOST && match->addr == 1; | ||
1093 | } | ||
1094 | |||
1095 | static int cx18_av_g_chip_ident(struct v4l2_subdev *sd, | ||
1096 | struct v4l2_dbg_chip_ident *chip) | ||
1097 | { | ||
1098 | if (cx18_av_dbg_match(&chip->match)) { | ||
1099 | /* | ||
1100 | * Nothing else is going to claim to be this combination, | ||
1101 | * and the real host chip revision will be returned by a host | ||
1102 | * match on address 0. | ||
1103 | */ | ||
1104 | chip->ident = V4L2_IDENT_CX25843; | ||
1105 | chip->revision = V4L2_IDENT_CX23418; /* Why not */ | ||
1106 | } | ||
1107 | return 0; | ||
1108 | } | ||
1109 | |||
1110 | #ifdef CONFIG_VIDEO_ADV_DEBUG | ||
1111 | static int cx18_av_g_register(struct v4l2_subdev *sd, | ||
1112 | struct v4l2_dbg_register *reg) | ||
1113 | { | ||
1114 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
1115 | |||
1116 | if (!cx18_av_dbg_match(®->match)) | ||
1117 | return -EINVAL; | ||
1118 | if ((reg->reg & 0x3) != 0) | ||
1119 | return -EINVAL; | ||
1120 | if (!capable(CAP_SYS_ADMIN)) | ||
1121 | return -EPERM; | ||
1122 | reg->size = 4; | ||
1123 | reg->val = cx18_av_read4(cx, reg->reg & 0x00000ffc); | ||
1124 | return 0; | ||
1125 | } | ||
1126 | |||
1127 | static int cx18_av_s_register(struct v4l2_subdev *sd, | ||
1128 | struct v4l2_dbg_register *reg) | ||
1129 | { | ||
1130 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
1131 | |||
1132 | if (!cx18_av_dbg_match(®->match)) | ||
1133 | return -EINVAL; | ||
1134 | if ((reg->reg & 0x3) != 0) | ||
1135 | return -EINVAL; | ||
1136 | if (!capable(CAP_SYS_ADMIN)) | ||
1137 | return -EPERM; | ||
1138 | cx18_av_write4(cx, reg->reg & 0x00000ffc, reg->val); | ||
1139 | return 0; | ||
1140 | } | ||
1141 | #endif | ||
1142 | |||
1143 | static const struct v4l2_subdev_core_ops cx18_av_general_ops = { | ||
1144 | .g_chip_ident = cx18_av_g_chip_ident, | ||
1145 | .log_status = cx18_av_log_status, | ||
1146 | .init = cx18_av_init_hardware, | ||
1147 | .reset = cx18_av_reset, | ||
1148 | .queryctrl = cx18_av_queryctrl, | ||
1149 | .g_ctrl = cx18_av_g_ctrl, | ||
1150 | .s_ctrl = cx18_av_s_ctrl, | ||
1151 | #ifdef CONFIG_VIDEO_ADV_DEBUG | ||
1152 | .g_register = cx18_av_g_register, | ||
1153 | .s_register = cx18_av_s_register, | ||
1154 | #endif | ||
1155 | }; | ||
1156 | |||
1157 | static const struct v4l2_subdev_tuner_ops cx18_av_tuner_ops = { | ||
1158 | .s_radio = cx18_av_s_radio, | ||
1159 | .s_frequency = cx18_av_s_frequency, | ||
1160 | .g_tuner = cx18_av_g_tuner, | ||
1161 | .s_tuner = cx18_av_s_tuner, | ||
1162 | .s_std = cx18_av_s_std, | ||
1163 | }; | ||
1164 | |||
1165 | static const struct v4l2_subdev_audio_ops cx18_av_audio_ops = { | ||
1166 | .s_clock_freq = cx18_av_s_clock_freq, | ||
1167 | .s_routing = cx18_av_s_audio_routing, | ||
1168 | }; | ||
1169 | |||
1170 | static const struct v4l2_subdev_video_ops cx18_av_video_ops = { | ||
1171 | .s_routing = cx18_av_s_video_routing, | ||
1172 | .decode_vbi_line = cx18_av_decode_vbi_line, | ||
1173 | .s_stream = cx18_av_s_stream, | ||
1174 | .g_fmt = cx18_av_g_fmt, | ||
1175 | .s_fmt = cx18_av_s_fmt, | ||
1176 | }; | ||
1177 | |||
1178 | static const struct v4l2_subdev_ops cx18_av_ops = { | ||
1179 | .core = &cx18_av_general_ops, | ||
1180 | .tuner = &cx18_av_tuner_ops, | ||
1181 | .audio = &cx18_av_audio_ops, | ||
1182 | .video = &cx18_av_video_ops, | ||
1183 | }; | ||
1184 | |||
1185 | int cx18_av_init(struct cx18 *cx) | ||
1186 | { | ||
1187 | struct v4l2_subdev *sd = &cx->av_state.sd; | ||
1188 | |||
1189 | v4l2_subdev_init(sd, &cx18_av_ops); | ||
1190 | v4l2_set_subdevdata(sd, cx); | ||
1191 | snprintf(sd->name, sizeof(sd->name), | ||
1192 | "%s-internal A/V decoder", cx->v4l2_dev.name); | ||
1193 | sd->grp_id = CX18_HW_CX23418; | ||
1194 | return v4l2_device_register_subdev(&cx->v4l2_dev, sd); | ||
1195 | } | ||
1196 | |||
1197 | void cx18_av_fini(struct cx18 *cx) | ||
1198 | { | ||
1199 | v4l2_device_unregister_subdev(&cx->av_state.sd); | ||
1200 | } | ||
diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h index d83760cae540..025100aee4b8 100644 --- a/drivers/media/video/cx18/cx18-av-core.h +++ b/drivers/media/video/cx18/cx18-av-core.h | |||
@@ -25,6 +25,8 @@ | |||
25 | #ifndef _CX18_AV_CORE_H_ | 25 | #ifndef _CX18_AV_CORE_H_ |
26 | #define _CX18_AV_CORE_H_ | 26 | #define _CX18_AV_CORE_H_ |
27 | 27 | ||
28 | #include <media/v4l2-device.h> | ||
29 | |||
28 | struct cx18; | 30 | struct cx18; |
29 | 31 | ||
30 | enum cx18_av_video_input { | 32 | enum cx18_av_video_input { |
@@ -73,6 +75,7 @@ enum cx18_av_audio_input { | |||
73 | }; | 75 | }; |
74 | 76 | ||
75 | struct cx18_av_state { | 77 | struct cx18_av_state { |
78 | struct v4l2_subdev sd; | ||
76 | int radio; | 79 | int radio; |
77 | v4l2_std_id std; | 80 | v4l2_std_id std; |
78 | enum cx18_av_video_input vid_input; | 81 | enum cx18_av_video_input vid_input; |
@@ -315,6 +318,11 @@ struct cx18_av_state { | |||
315 | #define CXADEC_SELECT_AUDIO_STANDARD_FM 0xF9 /* FM radio */ | 318 | #define CXADEC_SELECT_AUDIO_STANDARD_FM 0xF9 /* FM radio */ |
316 | #define CXADEC_SELECT_AUDIO_STANDARD_AUTO 0xFF /* Auto detect */ | 319 | #define CXADEC_SELECT_AUDIO_STANDARD_AUTO 0xFF /* Auto detect */ |
317 | 320 | ||
321 | static inline struct cx18_av_state *to_cx18_av_state(struct v4l2_subdev *sd) | ||
322 | { | ||
323 | return container_of(sd, struct cx18_av_state, sd); | ||
324 | } | ||
325 | |||
318 | /* ----------------------------------------------------------------------- */ | 326 | /* ----------------------------------------------------------------------- */ |
319 | /* cx18_av-core.c */ | 327 | /* cx18_av-core.c */ |
320 | int cx18_av_write(struct cx18 *cx, u16 addr, u8 value); | 328 | int cx18_av_write(struct cx18 *cx, u16 addr, u8 value); |
@@ -327,9 +335,12 @@ u8 cx18_av_read(struct cx18 *cx, u16 addr); | |||
327 | u32 cx18_av_read4(struct cx18 *cx, u16 addr); | 335 | u32 cx18_av_read4(struct cx18 *cx, u16 addr); |
328 | int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned mask, u8 value); | 336 | int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned mask, u8 value); |
329 | int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value); | 337 | int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value); |
330 | int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg); | ||
331 | void cx18_av_std_setup(struct cx18 *cx); | 338 | void cx18_av_std_setup(struct cx18 *cx); |
332 | 339 | ||
340 | int cx18_av_cmd(struct cx18 *cx, int cmd, void *arg); /* FIXME - Remove */ | ||
341 | int cx18_av_init(struct cx18 *cx); | ||
342 | void cx18_av_fini(struct cx18 *cx); | ||
343 | |||
333 | /* ----------------------------------------------------------------------- */ | 344 | /* ----------------------------------------------------------------------- */ |
334 | /* cx18_av-firmware.c */ | 345 | /* cx18_av-firmware.c */ |
335 | int cx18_av_loadfw(struct cx18 *cx); | 346 | int cx18_av_loadfw(struct cx18 *cx); |