Commit 6951803c2402d1af0e76df051cc9b117f504550f
Committed by
Mauro Carvalho Chehab
1 parent
724dcbfa9c
Exists in
master
and in
39 other branches
[media] Add proper audio support for Nova-S Plus with wm8775 ADC
This patch adds audio DMA capture and ALSA mixer elements for the line input jack of the Hauppauge Nova-S-plus DVB-S PCI card. The Nova-S-plus has a WM8775 ADC that is currently not detected. This patch enables this chip and exports volume, balance mute and ALC elements for ALSA mixer controls. [mchehab@redhat.com: Changed the patch to only talk with wm8775 if board info says so. Also, added platform_data support, to avoid changing the behaviour for other boards, and fixed CodingStyle] [awalls@md.metrocast.net: Changed patch to make the WM8775_GID defintion bridge driver private and let the bridge driver set the value of v4l2_subdev.grp_id.] -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com> Signed-off-by: Andy Walls <awalls@md.metrocast.net>
Showing 7 changed files with 267 additions and 62 deletions Side-by-side Diff
drivers/media/video/cx88/cx88-alsa.c
... | ... | @@ -40,6 +40,7 @@ |
40 | 40 | #include <sound/control.h> |
41 | 41 | #include <sound/initval.h> |
42 | 42 | #include <sound/tlv.h> |
43 | +#include <media/wm8775.h> | |
43 | 44 | |
44 | 45 | #include "cx88.h" |
45 | 46 | #include "cx88-reg.h" |
... | ... | @@ -577,6 +578,35 @@ |
577 | 578 | return 0; |
578 | 579 | } |
579 | 580 | |
581 | +static void snd_cx88_wm8775_volume_put(struct snd_kcontrol *kcontrol, | |
582 | + struct snd_ctl_elem_value *value) | |
583 | +{ | |
584 | + snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); | |
585 | + struct cx88_core *core = chip->core; | |
586 | + struct v4l2_control client_ctl; | |
587 | + int left = value->value.integer.value[0]; | |
588 | + int right = value->value.integer.value[1]; | |
589 | + int v, b; | |
590 | + | |
591 | + memset(&client_ctl, 0, sizeof(client_ctl)); | |
592 | + | |
593 | + /* Pass volume & balance onto any WM8775 */ | |
594 | + if (left >= right) { | |
595 | + v = left << 10; | |
596 | + b = left ? (0x8000 * right) / left : 0x8000; | |
597 | + } else { | |
598 | + v = right << 10; | |
599 | + b = right ? 0xffff - (0x8000 * left) / right : 0x8000; | |
600 | + } | |
601 | + client_ctl.value = v; | |
602 | + client_ctl.id = V4L2_CID_AUDIO_VOLUME; | |
603 | + call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl); | |
604 | + | |
605 | + client_ctl.value = b; | |
606 | + client_ctl.id = V4L2_CID_AUDIO_BALANCE; | |
607 | + call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl); | |
608 | +} | |
609 | + | |
580 | 610 | /* OK - TODO: test it */ |
581 | 611 | static int snd_cx88_volume_put(struct snd_kcontrol *kcontrol, |
582 | 612 | struct snd_ctl_elem_value *value) |
583 | 613 | |
584 | 614 | |
585 | 615 | |
586 | 616 | |
... | ... | @@ -587,25 +617,28 @@ |
587 | 617 | int changed = 0; |
588 | 618 | u32 old; |
589 | 619 | |
620 | + if (core->board.audio_chip == V4L2_IDENT_WM8775) | |
621 | + snd_cx88_wm8775_volume_put(kcontrol, value); | |
622 | + | |
590 | 623 | left = value->value.integer.value[0] & 0x3f; |
591 | 624 | right = value->value.integer.value[1] & 0x3f; |
592 | 625 | b = right - left; |
593 | 626 | if (b < 0) { |
594 | - v = 0x3f - left; | |
595 | - b = (-b) | 0x40; | |
627 | + v = 0x3f - left; | |
628 | + b = (-b) | 0x40; | |
596 | 629 | } else { |
597 | - v = 0x3f - right; | |
630 | + v = 0x3f - right; | |
598 | 631 | } |
599 | 632 | /* Do we really know this will always be called with IRQs on? */ |
600 | 633 | spin_lock_irq(&chip->reg_lock); |
601 | 634 | old = cx_read(AUD_VOL_CTL); |
602 | 635 | if (v != (old & 0x3f)) { |
603 | - cx_write(AUD_VOL_CTL, (old & ~0x3f) | v); | |
604 | - changed = 1; | |
636 | + cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, (old & ~0x3f) | v); | |
637 | + changed = 1; | |
605 | 638 | } |
606 | - if (cx_read(AUD_BAL_CTL) != b) { | |
607 | - cx_write(AUD_BAL_CTL, b); | |
608 | - changed = 1; | |
639 | + if ((cx_read(AUD_BAL_CTL) & 0x7f) != b) { | |
640 | + cx_write(AUD_BAL_CTL, b); | |
641 | + changed = 1; | |
609 | 642 | } |
610 | 643 | spin_unlock_irq(&chip->reg_lock); |
611 | 644 | |
... | ... | @@ -618,7 +651,7 @@ |
618 | 651 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
619 | 652 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | |
620 | 653 | SNDRV_CTL_ELEM_ACCESS_TLV_READ, |
621 | - .name = "Playback Volume", | |
654 | + .name = "Analog-TV Volume", | |
622 | 655 | .info = snd_cx88_volume_info, |
623 | 656 | .get = snd_cx88_volume_get, |
624 | 657 | .put = snd_cx88_volume_put, |
... | ... | @@ -649,7 +682,17 @@ |
649 | 682 | vol = cx_read(AUD_VOL_CTL); |
650 | 683 | if (value->value.integer.value[0] != !(vol & bit)) { |
651 | 684 | vol ^= bit; |
652 | - cx_write(AUD_VOL_CTL, vol); | |
685 | + cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, vol); | |
686 | + /* Pass mute onto any WM8775 */ | |
687 | + if ((core->board.audio_chip == V4L2_IDENT_WM8775) && | |
688 | + ((1<<6) == bit)) { | |
689 | + struct v4l2_control client_ctl; | |
690 | + | |
691 | + memset(&client_ctl, 0, sizeof(client_ctl)); | |
692 | + client_ctl.value = 0 != (vol & bit); | |
693 | + client_ctl.id = V4L2_CID_AUDIO_MUTE; | |
694 | + call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl); | |
695 | + } | |
653 | 696 | ret = 1; |
654 | 697 | } |
655 | 698 | spin_unlock_irq(&chip->reg_lock); |
... | ... | @@ -658,7 +701,7 @@ |
658 | 701 | |
659 | 702 | static const struct snd_kcontrol_new snd_cx88_dac_switch = { |
660 | 703 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
661 | - .name = "Playback Switch", | |
704 | + .name = "Audio-Out Switch", | |
662 | 705 | .info = snd_ctl_boolean_mono_info, |
663 | 706 | .get = snd_cx88_switch_get, |
664 | 707 | .put = snd_cx88_switch_put, |
665 | 708 | |
... | ... | @@ -667,13 +710,51 @@ |
667 | 710 | |
668 | 711 | static const struct snd_kcontrol_new snd_cx88_source_switch = { |
669 | 712 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
670 | - .name = "Capture Switch", | |
713 | + .name = "Analog-TV Switch", | |
671 | 714 | .info = snd_ctl_boolean_mono_info, |
672 | 715 | .get = snd_cx88_switch_get, |
673 | 716 | .put = snd_cx88_switch_put, |
674 | 717 | .private_value = (1<<6), |
675 | 718 | }; |
676 | 719 | |
720 | +static int snd_cx88_alc_get(struct snd_kcontrol *kcontrol, | |
721 | + struct snd_ctl_elem_value *value) | |
722 | +{ | |
723 | + snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); | |
724 | + struct cx88_core *core = chip->core; | |
725 | + struct v4l2_control client_ctl; | |
726 | + | |
727 | + memset(&client_ctl, 0, sizeof(client_ctl)); | |
728 | + client_ctl.id = V4L2_CID_AUDIO_LOUDNESS; | |
729 | + call_hw(core, WM8775_GID, core, g_ctrl, &client_ctl); | |
730 | + value->value.integer.value[0] = client_ctl.value ? 1 : 0; | |
731 | + | |
732 | + return 0; | |
733 | +} | |
734 | + | |
735 | +static int snd_cx88_alc_put(struct snd_kcontrol *kcontrol, | |
736 | + struct snd_ctl_elem_value *value) | |
737 | +{ | |
738 | + snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); | |
739 | + struct cx88_core *core = chip->core; | |
740 | + struct v4l2_control client_ctl; | |
741 | + | |
742 | + memset(&client_ctl, 0, sizeof(client_ctl)); | |
743 | + client_ctl.value = 0 != value->value.integer.value[0]; | |
744 | + client_ctl.id = V4L2_CID_AUDIO_LOUDNESS; | |
745 | + call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl); | |
746 | + | |
747 | + return 0; | |
748 | +} | |
749 | + | |
750 | +static struct snd_kcontrol_new snd_cx88_alc_switch = { | |
751 | + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
752 | + .name = "Line-In ALC Switch", | |
753 | + .info = snd_ctl_boolean_mono_info, | |
754 | + .get = snd_cx88_alc_get, | |
755 | + .put = snd_cx88_alc_put, | |
756 | +}; | |
757 | + | |
677 | 758 | /**************************************************************************** |
678 | 759 | Basic Flow for Sound Devices |
679 | 760 | ****************************************************************************/ |
... | ... | @@ -724,7 +805,8 @@ |
724 | 805 | static int devno; |
725 | 806 | static int __devinit snd_cx88_create(struct snd_card *card, |
726 | 807 | struct pci_dev *pci, |
727 | - snd_cx88_card_t **rchip) | |
808 | + snd_cx88_card_t **rchip, | |
809 | + struct cx88_core **core_ptr) | |
728 | 810 | { |
729 | 811 | snd_cx88_card_t *chip; |
730 | 812 | struct cx88_core *core; |
... | ... | @@ -750,7 +832,7 @@ |
750 | 832 | if (!pci_dma_supported(pci,DMA_BIT_MASK(32))) { |
751 | 833 | dprintk(0, "%s/1: Oops: no 32bit PCI DMA ???\n",core->name); |
752 | 834 | err = -EIO; |
753 | - cx88_core_put(core,pci); | |
835 | + cx88_core_put(core, pci); | |
754 | 836 | return err; |
755 | 837 | } |
756 | 838 | |
... | ... | @@ -786,6 +868,7 @@ |
786 | 868 | snd_card_set_dev(card, &pci->dev); |
787 | 869 | |
788 | 870 | *rchip = chip; |
871 | + *core_ptr = core; | |
789 | 872 | |
790 | 873 | return 0; |
791 | 874 | } |
... | ... | @@ -795,6 +878,7 @@ |
795 | 878 | { |
796 | 879 | struct snd_card *card; |
797 | 880 | snd_cx88_card_t *chip; |
881 | + struct cx88_core *core; | |
798 | 882 | int err; |
799 | 883 | |
800 | 884 | if (devno >= SNDRV_CARDS) |
... | ... | @@ -812,7 +896,7 @@ |
812 | 896 | |
813 | 897 | card->private_free = snd_cx88_dev_free; |
814 | 898 | |
815 | - err = snd_cx88_create(card, pci, &chip); | |
899 | + err = snd_cx88_create(card, pci, &chip, &core); | |
816 | 900 | if (err < 0) |
817 | 901 | goto error; |
818 | 902 | |
... | ... | @@ -829,6 +913,10 @@ |
829 | 913 | err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_source_switch, chip)); |
830 | 914 | if (err < 0) |
831 | 915 | goto error; |
916 | + | |
917 | + /* If there's a wm8775 then add a Line-In ALC switch */ | |
918 | + if (core->board.audio_chip == V4L2_IDENT_WM8775) | |
919 | + snd_ctl_add(card, snd_ctl_new1(&snd_cx88_alc_switch, chip)); | |
832 | 920 | |
833 | 921 | strcpy (card->driver, "CX88x"); |
834 | 922 | sprintf(card->shortname, "Conexant CX%x", pci->device); |
drivers/media/video/cx88/cx88-cards.c
drivers/media/video/cx88/cx88-tvaudio.c
... | ... | @@ -786,8 +786,12 @@ |
786 | 786 | break; |
787 | 787 | case WW_I2SADC: |
788 | 788 | set_audio_start(core, 0x01); |
789 | - /* Slave/Philips/Autobaud */ | |
790 | - cx_write(AUD_I2SINPUTCNTL, 0); | |
789 | + /* | |
790 | + * Slave/Philips/Autobaud | |
791 | + * NB on Nova-S bit1 NPhilipsSony appears to be inverted: | |
792 | + * 0= Sony, 1=Philips | |
793 | + */ | |
794 | + cx_write(AUD_I2SINPUTCNTL, core->board.i2sinputcntl); | |
791 | 795 | /* Switch to "I2S ADC mode" */ |
792 | 796 | cx_write(AUD_I2SCNTL, 0x1); |
793 | 797 | set_audio_finish(core, EN_I2SIN_ENABLE); |
drivers/media/video/cx88/cx88-video.c
... | ... | @@ -40,6 +40,7 @@ |
40 | 40 | #include "cx88.h" |
41 | 41 | #include <media/v4l2-common.h> |
42 | 42 | #include <media/v4l2-ioctl.h> |
43 | +#include <media/wm8775.h> | |
43 | 44 | |
44 | 45 | MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards"); |
45 | 46 | MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); |
... | ... | @@ -989,6 +990,32 @@ |
989 | 990 | ctl->value = c->v.minimum; |
990 | 991 | if (ctl->value > c->v.maximum) |
991 | 992 | ctl->value = c->v.maximum; |
993 | + | |
994 | + /* Pass changes onto any WM8775 */ | |
995 | + if (core->board.audio_chip == V4L2_IDENT_WM8775) { | |
996 | + struct v4l2_control client_ctl; | |
997 | + memset(&client_ctl, 0, sizeof(client_ctl)); | |
998 | + client_ctl.id = ctl->id; | |
999 | + | |
1000 | + switch (ctl->id) { | |
1001 | + case V4L2_CID_AUDIO_MUTE: | |
1002 | + client_ctl.value = ctl->value; | |
1003 | + break; | |
1004 | + case V4L2_CID_AUDIO_VOLUME: | |
1005 | + client_ctl.value = (ctl->value) ? | |
1006 | + (0x90 + ctl->value) << 8 : 0; | |
1007 | + break; | |
1008 | + case V4L2_CID_AUDIO_BALANCE: | |
1009 | + client_ctl.value = ctl->value << 9; | |
1010 | + break; | |
1011 | + default: | |
1012 | + client_ctl.id = 0; | |
1013 | + break; | |
1014 | + } | |
1015 | + if (client_ctl.id) | |
1016 | + call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl); | |
1017 | + } | |
1018 | + | |
992 | 1019 | mask=c->mask; |
993 | 1020 | switch (ctl->id) { |
994 | 1021 | case V4L2_CID_AUDIO_BALANCE: |
... | ... | @@ -1526,7 +1553,9 @@ |
1526 | 1553 | if (c->id < V4L2_CID_BASE || |
1527 | 1554 | c->id >= V4L2_CID_LASTP1) |
1528 | 1555 | return -EINVAL; |
1529 | - if (c->id == V4L2_CID_AUDIO_MUTE) { | |
1556 | + if (c->id == V4L2_CID_AUDIO_MUTE || | |
1557 | + c->id == V4L2_CID_AUDIO_VOLUME || | |
1558 | + c->id == V4L2_CID_AUDIO_BALANCE) { | |
1530 | 1559 | for (i = 0; i < CX8800_CTLS; i++) { |
1531 | 1560 | if (cx8800_ctls[i].v.id == c->id) |
1532 | 1561 | break; |
... | ... | @@ -1856,9 +1885,24 @@ |
1856 | 1885 | |
1857 | 1886 | /* load and configure helper modules */ |
1858 | 1887 | |
1859 | - if (core->board.audio_chip == V4L2_IDENT_WM8775) | |
1860 | - v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap, | |
1861 | - "wm8775", 0x36 >> 1, NULL); | |
1888 | + if (core->board.audio_chip == V4L2_IDENT_WM8775) { | |
1889 | + struct i2c_board_info wm8775_info = { | |
1890 | + .type = "wm8775", | |
1891 | + .addr = 0x36 >> 1, | |
1892 | + .platform_data = &core->wm8775_data, | |
1893 | + }; | |
1894 | + struct v4l2_subdev *sd; | |
1895 | + | |
1896 | + if (core->boardnr == CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1) | |
1897 | + core->wm8775_data.is_nova_s = true; | |
1898 | + else | |
1899 | + core->wm8775_data.is_nova_s = false; | |
1900 | + | |
1901 | + sd = v4l2_i2c_new_subdev_board(&core->v4l2_dev, &core->i2c_adap, | |
1902 | + &wm8775_info, NULL); | |
1903 | + if (sd != NULL) | |
1904 | + sd->grp_id = WM8775_GID; | |
1905 | + } | |
1862 | 1906 | |
1863 | 1907 | if (core->board.audio_chip == V4L2_IDENT_TVAUDIO) { |
1864 | 1908 | /* This probes for a tda9874 as is used on some |
drivers/media/video/cx88/cx88.h
... | ... | @@ -33,6 +33,7 @@ |
33 | 33 | #include <media/cx2341x.h> |
34 | 34 | #include <media/videobuf-dvb.h> |
35 | 35 | #include <media/ir-kbd-i2c.h> |
36 | +#include <media/wm8775.h> | |
36 | 37 | |
37 | 38 | #include "btcx-risc.h" |
38 | 39 | #include "cx88-reg.h" |
... | ... | @@ -273,6 +274,9 @@ |
273 | 274 | enum cx88_board_type mpeg; |
274 | 275 | unsigned int audio_chip; |
275 | 276 | int num_frontends; |
277 | + | |
278 | + /* Used for I2S devices */ | |
279 | + int i2sinputcntl; | |
276 | 280 | }; |
277 | 281 | |
278 | 282 | struct cx88_subid { |
... | ... | @@ -379,6 +383,7 @@ |
379 | 383 | |
380 | 384 | /* I2C remote data */ |
381 | 385 | struct IR_i2c_init_data init_data; |
386 | + struct wm8775_platform_data wm8775_data; | |
382 | 387 | |
383 | 388 | struct mutex lock; |
384 | 389 | /* various v4l controls */ |
385 | 390 | |
386 | 391 | |
... | ... | @@ -398,16 +403,20 @@ |
398 | 403 | return container_of(v4l2_dev, struct cx88_core, v4l2_dev); |
399 | 404 | } |
400 | 405 | |
401 | -#define call_all(core, o, f, args...) \ | |
406 | +#define WM8775_GID (1 << 0) | |
407 | + | |
408 | +#define call_hw(core, grpid, o, f, args...) \ | |
402 | 409 | do { \ |
403 | 410 | if (!core->i2c_rc) { \ |
404 | 411 | if (core->gate_ctrl) \ |
405 | 412 | core->gate_ctrl(core, 1); \ |
406 | - v4l2_device_call_all(&core->v4l2_dev, 0, o, f, ##args); \ | |
413 | + v4l2_device_call_all(&core->v4l2_dev, grpid, o, f, ##args); \ | |
407 | 414 | if (core->gate_ctrl) \ |
408 | 415 | core->gate_ctrl(core, 0); \ |
409 | 416 | } \ |
410 | 417 | } while (0) |
418 | + | |
419 | +#define call_all(core, o, f, args...) call_hw(core, 0, o, f, ##args) | |
411 | 420 | |
412 | 421 | struct cx8800_dev; |
413 | 422 | struct cx8802_dev; |
drivers/media/video/wm8775.c
... | ... | @@ -35,6 +35,7 @@ |
35 | 35 | #include <media/v4l2-device.h> |
36 | 36 | #include <media/v4l2-chip-ident.h> |
37 | 37 | #include <media/v4l2-ctrls.h> |
38 | +#include <media/wm8775.h> | |
38 | 39 | |
39 | 40 | MODULE_DESCRIPTION("wm8775 driver"); |
40 | 41 | MODULE_AUTHOR("Ulf Eklund, Hans Verkuil"); |
41 | 42 | |
... | ... | @@ -50,10 +51,16 @@ |
50 | 51 | TOT_REGS |
51 | 52 | }; |
52 | 53 | |
54 | +#define ALC_HOLD 0x85 /* R17: use zero cross detection, ALC hold time 42.6 ms */ | |
55 | +#define ALC_EN 0x100 /* R17: ALC enable */ | |
56 | + | |
53 | 57 | struct wm8775_state { |
54 | 58 | struct v4l2_subdev sd; |
55 | 59 | struct v4l2_ctrl_handler hdl; |
56 | 60 | struct v4l2_ctrl *mute; |
61 | + struct v4l2_ctrl *vol; | |
62 | + struct v4l2_ctrl *bal; | |
63 | + struct v4l2_ctrl *loud; | |
57 | 64 | u8 input; /* Last selected input (0-0xf) */ |
58 | 65 | }; |
59 | 66 | |
... | ... | @@ -85,6 +92,30 @@ |
85 | 92 | return -1; |
86 | 93 | } |
87 | 94 | |
95 | +static void wm8775_set_audio(struct v4l2_subdev *sd, int quietly) | |
96 | +{ | |
97 | + struct wm8775_state *state = to_state(sd); | |
98 | + u8 vol_l, vol_r; | |
99 | + int muted = 0 != state->mute->val; | |
100 | + u16 volume = (u16)state->vol->val; | |
101 | + u16 balance = (u16)state->bal->val; | |
102 | + | |
103 | + /* normalize ( 65535 to 0 -> 255 to 0 (+24dB to -103dB) ) */ | |
104 | + vol_l = (min(65536 - balance, 32768) * volume) >> 23; | |
105 | + vol_r = (min(balance, (u16)32768) * volume) >> 23; | |
106 | + | |
107 | + /* Mute */ | |
108 | + if (muted || quietly) | |
109 | + wm8775_write(sd, R21, 0x0c0 | state->input); | |
110 | + | |
111 | + wm8775_write(sd, R14, vol_l | 0x100); /* 0x100= Left channel ADC zero cross enable */ | |
112 | + wm8775_write(sd, R15, vol_r | 0x100); /* 0x100= Right channel ADC zero cross enable */ | |
113 | + | |
114 | + /* Un-mute */ | |
115 | + if (!muted) | |
116 | + wm8775_write(sd, R21, state->input); | |
117 | +} | |
118 | + | |
88 | 119 | static int wm8775_s_routing(struct v4l2_subdev *sd, |
89 | 120 | u32 input, u32 output, u32 config) |
90 | 121 | { |
91 | 122 | |
92 | 123 | |
93 | 124 | |
... | ... | @@ -102,26 +133,27 @@ |
102 | 133 | state->input = input; |
103 | 134 | if (!v4l2_ctrl_g_ctrl(state->mute)) |
104 | 135 | return 0; |
105 | - wm8775_write(sd, R21, 0x0c0); | |
106 | - wm8775_write(sd, R14, 0x1d4); | |
107 | - wm8775_write(sd, R15, 0x1d4); | |
108 | - wm8775_write(sd, R21, 0x100 + state->input); | |
136 | + if (!v4l2_ctrl_g_ctrl(state->vol)) | |
137 | + return 0; | |
138 | + if (!v4l2_ctrl_g_ctrl(state->bal)) | |
139 | + return 0; | |
140 | + wm8775_set_audio(sd, 1); | |
109 | 141 | return 0; |
110 | 142 | } |
111 | 143 | |
112 | 144 | static int wm8775_s_ctrl(struct v4l2_ctrl *ctrl) |
113 | 145 | { |
114 | 146 | struct v4l2_subdev *sd = to_sd(ctrl); |
115 | - struct wm8775_state *state = to_state(sd); | |
116 | 147 | |
117 | 148 | switch (ctrl->id) { |
118 | 149 | case V4L2_CID_AUDIO_MUTE: |
119 | - wm8775_write(sd, R21, 0x0c0); | |
120 | - wm8775_write(sd, R14, 0x1d4); | |
121 | - wm8775_write(sd, R15, 0x1d4); | |
122 | - if (!ctrl->val) | |
123 | - wm8775_write(sd, R21, 0x100 + state->input); | |
150 | + case V4L2_CID_AUDIO_VOLUME: | |
151 | + case V4L2_CID_AUDIO_BALANCE: | |
152 | + wm8775_set_audio(sd, 0); | |
124 | 153 | return 0; |
154 | + case V4L2_CID_AUDIO_LOUDNESS: | |
155 | + wm8775_write(sd, R17, (ctrl->val ? ALC_EN : 0) | ALC_HOLD); | |
156 | + return 0; | |
125 | 157 | } |
126 | 158 | return -EINVAL; |
127 | 159 | } |
... | ... | @@ -144,16 +176,7 @@ |
144 | 176 | |
145 | 177 | static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) |
146 | 178 | { |
147 | - struct wm8775_state *state = to_state(sd); | |
148 | - | |
149 | - /* If I remove this, then it can happen that I have no | |
150 | - sound the first time I tune from static to a valid channel. | |
151 | - It's difficult to reproduce and is almost certainly related | |
152 | - to the zero cross detect circuit. */ | |
153 | - wm8775_write(sd, R21, 0x0c0); | |
154 | - wm8775_write(sd, R14, 0x1d4); | |
155 | - wm8775_write(sd, R15, 0x1d4); | |
156 | - wm8775_write(sd, R21, 0x100 + state->input); | |
179 | + wm8775_set_audio(sd, 0); | |
157 | 180 | return 0; |
158 | 181 | } |
159 | 182 | |
160 | 183 | |
... | ... | @@ -203,7 +226,14 @@ |
203 | 226 | { |
204 | 227 | struct wm8775_state *state; |
205 | 228 | struct v4l2_subdev *sd; |
229 | + int err; | |
230 | + bool is_nova_s = false; | |
206 | 231 | |
232 | + if (client->dev.platform_data) { | |
233 | + struct wm8775_platform_data *data = client->dev.platform_data; | |
234 | + is_nova_s = data->is_nova_s; | |
235 | + } | |
236 | + | |
207 | 237 | /* Check if the adapter supports the needed features */ |
208 | 238 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) |
209 | 239 | return -EIO; |
210 | 240 | |
211 | 241 | |
... | ... | @@ -218,13 +248,18 @@ |
218 | 248 | v4l2_i2c_subdev_init(sd, client, &wm8775_ops); |
219 | 249 | state->input = 2; |
220 | 250 | |
221 | - v4l2_ctrl_handler_init(&state->hdl, 1); | |
251 | + v4l2_ctrl_handler_init(&state->hdl, 4); | |
222 | 252 | state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, |
223 | 253 | V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); |
254 | + state->vol = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, | |
255 | + V4L2_CID_AUDIO_VOLUME, 0, 65535, (65535+99)/100, 0xCF00); /* 0dB*/ | |
256 | + state->bal = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, | |
257 | + V4L2_CID_AUDIO_BALANCE, 0, 65535, (65535+99)/100, 32768); | |
258 | + state->loud = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, | |
259 | + V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 1); | |
224 | 260 | sd->ctrl_handler = &state->hdl; |
225 | - if (state->hdl.error) { | |
226 | - int err = state->hdl.error; | |
227 | - | |
261 | + err = state->hdl.error; | |
262 | + if (err) { | |
228 | 263 | v4l2_ctrl_handler_free(&state->hdl); |
229 | 264 | kfree(state); |
230 | 265 | return err; |
231 | 266 | |
232 | 267 | |
... | ... | @@ -236,29 +271,44 @@ |
236 | 271 | wm8775_write(sd, R23, 0x000); |
237 | 272 | /* Disable zero cross detect timeout */ |
238 | 273 | wm8775_write(sd, R7, 0x000); |
239 | - /* Left justified, 24-bit mode */ | |
274 | + /* HPF enable, left justified, 24-bit (Philips) mode */ | |
240 | 275 | wm8775_write(sd, R11, 0x021); |
241 | 276 | /* Master mode, clock ratio 256fs */ |
242 | 277 | wm8775_write(sd, R12, 0x102); |
243 | 278 | /* Powered up */ |
244 | 279 | wm8775_write(sd, R13, 0x000); |
245 | - /* ADC gain +2.5dB, enable zero cross */ | |
246 | - wm8775_write(sd, R14, 0x1d4); | |
247 | - /* ADC gain +2.5dB, enable zero cross */ | |
248 | - wm8775_write(sd, R15, 0x1d4); | |
249 | - /* ALC Stereo, ALC target level -1dB FS max gain +8dB */ | |
250 | - wm8775_write(sd, R16, 0x1bf); | |
251 | - /* Enable gain control, use zero cross detection, | |
252 | - ALC hold time 42.6 ms */ | |
253 | - wm8775_write(sd, R17, 0x185); | |
280 | + | |
281 | + if (!is_nova_s) { | |
282 | + /* ADC gain +2.5dB, enable zero cross */ | |
283 | + wm8775_write(sd, R14, 0x1d4); | |
284 | + /* ADC gain +2.5dB, enable zero cross */ | |
285 | + wm8775_write(sd, R15, 0x1d4); | |
286 | + /* ALC Stereo, ALC target level -1dB FS max gain +8dB */ | |
287 | + wm8775_write(sd, R16, 0x1bf); | |
288 | + /* Enable gain control, use zero cross detection, | |
289 | + ALC hold time 42.6 ms */ | |
290 | + wm8775_write(sd, R17, 0x185); | |
291 | + } else { | |
292 | + /* ALC stereo, ALC target level -5dB FS, ALC max gain +8dB */ | |
293 | + wm8775_write(sd, R16, 0x1bb); | |
294 | + /* Set ALC mode and hold time */ | |
295 | + wm8775_write(sd, R17, (state->loud->val ? ALC_EN : 0) | ALC_HOLD); | |
296 | + } | |
254 | 297 | /* ALC gain ramp up delay 34 s, ALC gain ramp down delay 33 ms */ |
255 | 298 | wm8775_write(sd, R18, 0x0a2); |
256 | 299 | /* Enable noise gate, threshold -72dBfs */ |
257 | 300 | wm8775_write(sd, R19, 0x005); |
258 | - /* Transient window 4ms, lower PGA gain limit -1dB */ | |
259 | - wm8775_write(sd, R20, 0x07a); | |
260 | - /* LRBOTH = 1, use input 2. */ | |
261 | - wm8775_write(sd, R21, 0x102); | |
301 | + if (!is_nova_s) { | |
302 | + /* Transient window 4ms, lower PGA gain limit -1dB */ | |
303 | + wm8775_write(sd, R20, 0x07a); | |
304 | + /* LRBOTH = 1, use input 2. */ | |
305 | + wm8775_write(sd, R21, 0x102); | |
306 | + } else { | |
307 | + /* Transient window 4ms, ALC min gain -5dB */ | |
308 | + wm8775_write(sd, R20, 0x0fb); | |
309 | + | |
310 | + wm8775_set_audio(sd, 1); /* set volume/mute/mux */ | |
311 | + } | |
262 | 312 | return 0; |
263 | 313 | } |
264 | 314 |
include/media/wm8775.h
... | ... | @@ -32,5 +32,14 @@ |
32 | 32 | #define WM8775_AIN3 4 |
33 | 33 | #define WM8775_AIN4 8 |
34 | 34 | |
35 | + | |
36 | +struct wm8775_platform_data { | |
37 | + /* | |
38 | + * FIXME: Instead, we should parametrize the params | |
39 | + * that need different settings between ivtv, pvrusb2, and Nova-S | |
40 | + */ | |
41 | + bool is_nova_s; | |
42 | +}; | |
43 | + | |
35 | 44 | #endif |