diff options
author | spujar <spujar@nvidia.com> | 2018-05-15 08:25:44 -0400 |
---|---|---|
committer | mobile promotions <svcmobile_promotions@nvidia.com> | 2018-06-30 12:41:12 -0400 |
commit | 9415e9f69137a9bb5b16c54a7dda5be97098039e (patch) | |
tree | 80b02458edff49fe6233f4433001bf38acc698b5 /sound/pci/hda | |
parent | d9bd47b344cac6b8637e3b07c11bc75aa0ff3371 (diff) |
tegra: hda: expose mapping info for hda pcm devs
sysfs nodes are introduced for exposing info related to mapping between
hda pcm device ID and corresponding switch names. Currently display
driver registers the switch names and the same is queried here to create
mapping. Android userspace can query these nodes to know the mapping and
accordingly audio is routed to the corresponding hda codec.
Bug 200412336
Change-Id: I720980896311f6c30e0c7de9c7e85a9915031305
Signed-off-by: spujar <spujar@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/1719541
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
Diffstat (limited to 'sound/pci/hda')
-rw-r--r-- | sound/pci/hda/hda_tegra.c | 145 |
1 files changed, 141 insertions, 4 deletions
diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index 0d773d465..f3507d5ea 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c | |||
@@ -87,6 +87,17 @@ static const struct of_device_id tegra_disb_pd[] = { | |||
87 | /* GSC_ID register */ | 87 | /* GSC_ID register */ |
88 | #define HDA_GSC_REG 0x1e0 | 88 | #define HDA_GSC_REG 0x1e0 |
89 | 89 | ||
90 | #define CHAR_BUF_SIZE_MAX 50 | ||
91 | |||
92 | struct hda_pcm_devices { | ||
93 | struct azx_pcm *apcm; | ||
94 | struct kobject *kobj; | ||
95 | struct kobj_attribute pcm_attr; | ||
96 | struct kobj_attribute name_attr; | ||
97 | char switch_name[CHAR_BUF_SIZE_MAX]; | ||
98 | int dev_id; | ||
99 | }; | ||
100 | |||
90 | struct hda_tegra { | 101 | struct hda_tegra { |
91 | struct azx chip; | 102 | struct azx chip; |
92 | struct device *dev; | 103 | struct device *dev; |
@@ -100,6 +111,9 @@ struct hda_tegra { | |||
100 | struct work_struct probe_work; | 111 | struct work_struct probe_work; |
101 | bool init_done; | 112 | bool init_done; |
102 | bool do_reset; | 113 | bool do_reset; |
114 | int num_codecs; | ||
115 | struct kobject *kobj; | ||
116 | struct hda_pcm_devices *hda_pcm_dev; | ||
103 | }; | 117 | }; |
104 | 118 | ||
105 | #ifdef CONFIG_PM | 119 | #ifdef CONFIG_PM |
@@ -671,13 +685,125 @@ out_free: | |||
671 | return err; | 685 | return err; |
672 | } | 686 | } |
673 | 687 | ||
688 | static ssize_t hda_get_pcm_device_id(struct kobject *kobj, | ||
689 | struct kobj_attribute *attr, | ||
690 | char *buf) | ||
691 | { | ||
692 | struct hda_pcm_devices *pcm_dev = container_of(attr, | ||
693 | struct hda_pcm_devices, pcm_attr); | ||
694 | return snprintf(buf, PAGE_SIZE, "%d\n", pcm_dev->apcm->info->device); | ||
695 | } | ||
696 | |||
697 | static ssize_t hda_get_pcm_switch_name(struct kobject *kobj, | ||
698 | struct kobj_attribute *attr, char *buf) { | ||
699 | struct hda_pcm_devices *pcm_dev = container_of(attr, | ||
700 | struct hda_pcm_devices, name_attr); | ||
701 | return snprintf(buf, PAGE_SIZE, "%s\n", pcm_dev->switch_name); | ||
702 | } | ||
703 | |||
704 | /* switches are registered from display driver, get the names w.r.t dev ID */ | ||
705 | int tegra_hda_get_switch_name(int dev_id, char *name); | ||
706 | |||
707 | static int hda_tegra_create_sysfs(struct hda_tegra *hda) | ||
708 | { | ||
709 | struct azx *chip = &hda->chip; | ||
710 | char dirname[CHAR_BUF_SIZE_MAX] = "hda_pcm_map"; | ||
711 | int dev_count = 0, ret = 0; | ||
712 | struct azx_pcm *apcm; | ||
713 | struct kobject *parent = hda->dev->kobj.parent; | ||
714 | |||
715 | /* maintains list of all hda codecs */ | ||
716 | hda->hda_pcm_dev = | ||
717 | (struct hda_pcm_devices *) devm_kzalloc(hda->dev, | ||
718 | sizeof(struct hda_pcm_devices) * hda->num_codecs, | ||
719 | GFP_KERNEL); | ||
720 | if (!hda->hda_pcm_dev) | ||
721 | return -ENOMEM; | ||
722 | |||
723 | hda->kobj = kobject_create_and_add(dirname, parent); | ||
724 | if (!hda->kobj) | ||
725 | return -ENOMEM; | ||
726 | |||
727 | list_for_each_entry(apcm, &chip->pcm_list, list) { | ||
728 | char subdirname[CHAR_BUF_SIZE_MAX]; | ||
729 | struct hda_pcm_devices *pcm_dev = &hda->hda_pcm_dev[dev_count]; | ||
730 | |||
731 | pcm_dev->apcm = apcm; | ||
732 | snprintf(subdirname, sizeof(subdirname), "hda%d", dev_count); | ||
733 | pcm_dev->kobj = kobject_create_and_add(subdirname, hda->kobj); | ||
734 | if (!pcm_dev->kobj) | ||
735 | return -ENOMEM; | ||
736 | |||
737 | /* attributes for pcm device ID */ | ||
738 | pcm_dev->pcm_attr.attr.name = "pcm_dev_id"; | ||
739 | pcm_dev->pcm_attr.attr.mode = 0644; | ||
740 | pcm_dev->pcm_attr.show = hda_get_pcm_device_id; | ||
741 | |||
742 | /* attributes for switch name */ | ||
743 | pcm_dev->name_attr.attr.name = "switch_name"; | ||
744 | pcm_dev->name_attr.attr.mode = 0644; | ||
745 | pcm_dev->name_attr.show = hda_get_pcm_switch_name; | ||
746 | |||
747 | /* gets registered switch name for give dev ID | ||
748 | * TODO: may be we can create extcon node here itself and | ||
749 | * not rely on display driver | ||
750 | */ | ||
751 | pcm_dev->dev_id = (apcm->codec->core.vendor_id) & 0xffff; | ||
752 | if (tegra_hda_get_switch_name(pcm_dev->dev_id, | ||
753 | pcm_dev->switch_name) < 0) { | ||
754 | dev_info(hda->dev, "error in getting switch name" | ||
755 | " for hda_pcm_id(%d)\n", apcm->info->device); | ||
756 | kobject_put(pcm_dev->kobj); | ||
757 | continue; | ||
758 | } | ||
759 | |||
760 | /* create files for read from userspace */ | ||
761 | ret = sysfs_create_file(pcm_dev->kobj, | ||
762 | &(pcm_dev->pcm_attr.attr)); | ||
763 | if (ret < 0) | ||
764 | break; | ||
765 | |||
766 | ret = sysfs_create_file(pcm_dev->kobj, | ||
767 | &(pcm_dev->name_attr.attr)); | ||
768 | if (ret < 0) | ||
769 | break; | ||
770 | |||
771 | dev_count++; | ||
772 | } | ||
773 | return ret; | ||
774 | } | ||
775 | |||
776 | static void hda_tegra_remove_sysfs(struct device *dev) | ||
777 | { | ||
778 | struct snd_card *card = dev_get_drvdata(dev); | ||
779 | struct azx *chip = card->private_data; | ||
780 | struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); | ||
781 | int i; | ||
782 | |||
783 | if (!hda || !hda->hda_pcm_dev || !hda->kobj) | ||
784 | return; | ||
785 | |||
786 | for (i = 0; i < hda->num_codecs; i++) { | ||
787 | struct hda_pcm_devices *pcm_dev = &hda->hda_pcm_dev[i]; | ||
788 | |||
789 | if (pcm_dev->kobj) { | ||
790 | sysfs_remove_file(pcm_dev->kobj, | ||
791 | &pcm_dev->pcm_attr.attr); | ||
792 | sysfs_remove_file(pcm_dev->kobj, | ||
793 | &pcm_dev->name_attr.attr); | ||
794 | kobject_put(pcm_dev->kobj); | ||
795 | } | ||
796 | } | ||
797 | kobject_put(hda->kobj); | ||
798 | } | ||
799 | |||
674 | static void hda_tegra_probe_work(struct work_struct *work) | 800 | static void hda_tegra_probe_work(struct work_struct *work) |
675 | { | 801 | { |
676 | struct hda_tegra *hda = container_of(work, struct hda_tegra, probe_work); | 802 | struct hda_tegra *hda = container_of(work, struct hda_tegra, probe_work); |
677 | struct azx *chip = &hda->chip; | 803 | struct azx *chip = &hda->chip; |
678 | struct platform_device *pdev = to_platform_device(hda->dev); | 804 | struct platform_device *pdev = to_platform_device(hda->dev); |
679 | struct device_node *np = pdev->dev.of_node; | 805 | struct device_node *np = pdev->dev.of_node; |
680 | int num_codec_slots = 0, gsc_id; | 806 | int gsc_id; |
681 | struct hdac_bus *bus = azx_bus(chip); | 807 | struct hdac_bus *bus = azx_bus(chip); |
682 | int err; | 808 | int err; |
683 | 809 | ||
@@ -686,8 +812,8 @@ static void hda_tegra_probe_work(struct work_struct *work) | |||
686 | goto out_free; | 812 | goto out_free; |
687 | 813 | ||
688 | if (of_property_read_u32(np, "nvidia,max-codec-slot", | 814 | if (of_property_read_u32(np, "nvidia,max-codec-slot", |
689 | &num_codec_slots) < 0) | 815 | &hda->num_codecs) < 0) |
690 | num_codec_slots = 0; | 816 | hda->num_codecs = 0; |
691 | 817 | ||
692 | bus->avoid_compact_sdo_bw = of_property_read_bool(np, | 818 | bus->avoid_compact_sdo_bw = of_property_read_bool(np, |
693 | "nvidia,avoid-compact-sdo-bw"); | 819 | "nvidia,avoid-compact-sdo-bw"); |
@@ -704,7 +830,7 @@ static void hda_tegra_probe_work(struct work_struct *work) | |||
704 | hda_tegra_writel(gsc_id, hda->regs + HDA_GSC_REG); | 830 | hda_tegra_writel(gsc_id, hda->regs + HDA_GSC_REG); |
705 | 831 | ||
706 | /* create codec instances */ | 832 | /* create codec instances */ |
707 | err = azx_probe_codecs(chip, num_codec_slots); | 833 | err = azx_probe_codecs(chip, hda->num_codecs); |
708 | if (err < 0) | 834 | if (err < 0) |
709 | goto out_free; | 835 | goto out_free; |
710 | 836 | ||
@@ -723,6 +849,14 @@ static void hda_tegra_probe_work(struct work_struct *work) | |||
723 | 849 | ||
724 | pm_runtime_put(hda->dev); | 850 | pm_runtime_put(hda->dev); |
725 | 851 | ||
852 | /* export pcm device mapping to userspace - needed for android */ | ||
853 | err = hda_tegra_create_sysfs(hda); | ||
854 | if (err < 0) { | ||
855 | dev_err(&pdev->dev, | ||
856 | "error:%d in creating sysfs nodes for hda\n", err); | ||
857 | /* free allocated resources */ | ||
858 | hda_tegra_remove_sysfs(&pdev->dev); | ||
859 | } | ||
726 | out_free: | 860 | out_free: |
727 | return; /* no error return from async probe */ | 861 | return; /* no error return from async probe */ |
728 | } | 862 | } |
@@ -733,6 +867,9 @@ static int hda_tegra_remove(struct platform_device *pdev) | |||
733 | if (!pm_runtime_status_suspended(&pdev->dev)) | 867 | if (!pm_runtime_status_suspended(&pdev->dev)) |
734 | hda_tegra_runtime_suspend(&pdev->dev); | 868 | hda_tegra_runtime_suspend(&pdev->dev); |
735 | 869 | ||
870 | /* remove sysfs files */ | ||
871 | hda_tegra_remove_sysfs(&pdev->dev); | ||
872 | |||
736 | return snd_card_free(dev_get_drvdata(&pdev->dev)); | 873 | return snd_card_free(dev_get_drvdata(&pdev->dev)); |
737 | } | 874 | } |
738 | 875 | ||