|
31 | 31 | #include <sound/sof/xtensa.h> |
32 | 32 | #include <sound/hda-mlink.h> |
33 | 33 | #include "../sof-audio.h" |
| 34 | +#include "../sof-client.h" |
| 35 | +#include "../sof-client-audio.h" |
34 | 36 | #include "../sof-pci-dev.h" |
35 | 37 | #include "../ops.h" |
36 | 38 | #include "../ipc4-topology.h" |
@@ -1767,6 +1769,241 @@ int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) |
1767 | 1769 | } |
1768 | 1770 | EXPORT_SYMBOL_NS(hda_pci_intel_probe, "SND_SOC_SOF_INTEL_HDA_GENERIC"); |
1769 | 1771 |
|
| 1772 | +static bool multi_card; |
| 1773 | +module_param(multi_card, bool, 0444); |
| 1774 | +MODULE_PARM_DESC(multi_card, "Split audio into per-function cards. Experimental."); |
| 1775 | + |
| 1776 | +struct hda_sdw_func_card { |
| 1777 | + u32 dai_type_bit; |
| 1778 | + const char *card_name; |
| 1779 | +}; |
| 1780 | + |
| 1781 | +static const struct hda_sdw_func_card sdw_func_cards[] = { |
| 1782 | + { BIT(SOC_SDW_DAI_TYPE_JACK), "jack" }, |
| 1783 | + { BIT(SOC_SDW_DAI_TYPE_AMP), "speaker" }, |
| 1784 | + { BIT(SOC_SDW_DAI_TYPE_MIC), "mic" }, |
| 1785 | +}; |
| 1786 | + |
| 1787 | +/* |
| 1788 | + * Group SDW DAI types that share physical codec devices. Types on the |
| 1789 | + * same device must be on the same card because ASoC components can only |
| 1790 | + * be bound to one card. |
| 1791 | + * |
| 1792 | + * Returns the number of groups. Each group's merged DAI type bitmask |
| 1793 | + * is stored in groups[] and the representative codec name in names[]. |
| 1794 | + */ |
| 1795 | +static int hda_sdw_group_dai_types(const struct snd_soc_acpi_mach *mach, |
| 1796 | + u32 *groups, const char **names, |
| 1797 | + int max_groups) |
| 1798 | +{ |
| 1799 | + const struct snd_soc_acpi_link_adr *adr_link; |
| 1800 | + int num_groups = 0; |
| 1801 | + int i, j, k; |
| 1802 | + |
| 1803 | + for (adr_link = mach->mach_params.links; |
| 1804 | + adr_link && adr_link->num_adr; adr_link++) { |
| 1805 | + for (i = 0; i < adr_link->num_adr; i++) { |
| 1806 | + const struct snd_soc_acpi_adr_device *adr_dev = |
| 1807 | + &adr_link->adr_d[i]; |
| 1808 | + struct asoc_sdw_codec_info *codec_info; |
| 1809 | + const char *dev_name = NULL; |
| 1810 | + u32 dev_types = 0; |
| 1811 | + int merged = -1; |
| 1812 | + |
| 1813 | + codec_info = asoc_sdw_find_codec_info_part(adr_dev->adr); |
| 1814 | + if (!codec_info) |
| 1815 | + continue; |
| 1816 | + |
| 1817 | + for (j = 0; j < adr_dev->num_endpoints; j++) { |
| 1818 | + int ep_num = adr_dev->endpoints[j].num; |
| 1819 | + |
| 1820 | + if (ep_num < codec_info->dai_num) |
| 1821 | + dev_types |= |
| 1822 | + BIT(codec_info->dais[ep_num].dai_type); |
| 1823 | + } |
| 1824 | + |
| 1825 | + if (!dev_types) |
| 1826 | + continue; |
| 1827 | + |
| 1828 | + /* |
| 1829 | + * Pick a representative codec name: prefer |
| 1830 | + * name_prefix for non-amp codecs, fall back to |
| 1831 | + * the first DAI's component_name for amps. |
| 1832 | + */ |
| 1833 | + if (!codec_info->is_amp) |
| 1834 | + dev_name = codec_info->name_prefix; |
| 1835 | + else if (codec_info->dai_num && |
| 1836 | + codec_info->dais[0].component_name) |
| 1837 | + dev_name = codec_info->dais[0].component_name; |
| 1838 | + |
| 1839 | + /* Merge with any existing group that overlaps */ |
| 1840 | + for (k = 0; k < num_groups; k++) { |
| 1841 | + if (!(groups[k] & dev_types)) |
| 1842 | + continue; |
| 1843 | + |
| 1844 | + if (merged < 0) { |
| 1845 | + groups[k] |= dev_types; |
| 1846 | + if (dev_name) |
| 1847 | + names[k] = dev_name; |
| 1848 | + merged = k; |
| 1849 | + } else { |
| 1850 | + groups[merged] |= groups[k]; |
| 1851 | + groups[k] = groups[--num_groups]; |
| 1852 | + names[k] = names[num_groups]; |
| 1853 | + k--; |
| 1854 | + } |
| 1855 | + } |
| 1856 | + |
| 1857 | + if (merged < 0 && num_groups < max_groups) { |
| 1858 | + groups[num_groups] = dev_types; |
| 1859 | + names[num_groups] = dev_name; |
| 1860 | + num_groups++; |
| 1861 | + } |
| 1862 | + } |
| 1863 | + } |
| 1864 | + |
| 1865 | + return num_groups; |
| 1866 | +} |
| 1867 | + |
| 1868 | +static int hda_register_audio_client_multi(struct snd_sof_dev *sdev) |
| 1869 | +{ |
| 1870 | + static const struct snd_soc_acpi_link_adr empty_links[] = { {} }; |
| 1871 | + const struct snd_soc_acpi_mach *mach = sdev->pdata->machine; |
| 1872 | + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; |
| 1873 | + struct sof_audio_client_pdata pdata; |
| 1874 | + u32 dai_groups[ARRAY_SIZE(sdw_func_cards)]; |
| 1875 | + const char *group_names[ARRAY_SIZE(sdw_func_cards)]; |
| 1876 | + int num_groups; |
| 1877 | + int card_idx = 0; |
| 1878 | + int ret, i, j; |
| 1879 | + |
| 1880 | + if (!mach) |
| 1881 | + return -ENODEV; |
| 1882 | + |
| 1883 | + num_groups = hda_sdw_group_dai_types(mach, dai_groups, group_names, |
| 1884 | + ARRAY_SIZE(dai_groups)); |
| 1885 | + |
| 1886 | + dev_dbg(sdev->dev, "multi_card: %d SDW groups, dmic: %d, idisp: %s\n", |
| 1887 | + num_groups, mach->mach_params.dmic_num, |
| 1888 | + HDA_IDISP_CODEC(mach->mach_params.codec_mask) ? "yes" : "no"); |
| 1889 | + |
| 1890 | + sof_audio_client_init_pdata(sdev, &pdata); |
| 1891 | + |
| 1892 | + /* Create one card per SDW DAI type group */ |
| 1893 | + for (i = 0; i < num_groups; i++) { |
| 1894 | + const char *card_name; |
| 1895 | + |
| 1896 | + /* |
| 1897 | + * Prefer codec device name (e.g. "cs42l43") over the |
| 1898 | + * generic function type name (e.g. "jack"). |
| 1899 | + */ |
| 1900 | + card_name = group_names[i]; |
| 1901 | + if (!card_name) { |
| 1902 | + for (j = 0; j < ARRAY_SIZE(sdw_func_cards); j++) { |
| 1903 | + if (dai_groups[i] & sdw_func_cards[j].dai_type_bit) { |
| 1904 | + card_name = sdw_func_cards[j].card_name; |
| 1905 | + break; |
| 1906 | + } |
| 1907 | + } |
| 1908 | + } |
| 1909 | + if (!card_name) |
| 1910 | + continue; |
| 1911 | + |
| 1912 | + dev_dbg(sdev->dev, "multi_card: group %d mask %#x card %s\n", |
| 1913 | + i, dai_groups[i], card_name); |
| 1914 | + |
| 1915 | + memcpy(&pdata.machine, mach, sizeof(pdata.machine)); |
| 1916 | + pdata.machine.mach_params.dai_type_mask = dai_groups[i]; |
| 1917 | + pdata.machine.mach_params.codec_mask &= ~BIT(HDA_IDISP_ADDR); |
| 1918 | + pdata.machine.mach_params.dmic_num = 0; |
| 1919 | + pdata.machine.mach_params.card_name = card_name; |
| 1920 | + ret = sof_client_dev_register(sdev, "audio", card_idx, |
| 1921 | + &pdata, sizeof(pdata)); |
| 1922 | + if (ret) { |
| 1923 | + dev_warn(sdev->dev, |
| 1924 | + "multi_card: failed to register card %s: %d\n", |
| 1925 | + card_name, ret); |
| 1926 | + continue; |
| 1927 | + } |
| 1928 | + card_idx++; |
| 1929 | + } |
| 1930 | + |
| 1931 | + /* DMIC card */ |
| 1932 | + if (mach->mach_params.dmic_num) { |
| 1933 | + memcpy(&pdata.machine, mach, sizeof(pdata.machine)); |
| 1934 | + pdata.machine.mach_params.links = empty_links; |
| 1935 | + pdata.machine.mach_params.link_mask = 0; |
| 1936 | + pdata.machine.mach_params.codec_mask &= ~BIT(HDA_IDISP_ADDR); |
| 1937 | + pdata.machine.mach_params.card_name = "dmic"; |
| 1938 | + ret = sof_client_dev_register(sdev, "audio", card_idx, |
| 1939 | + &pdata, sizeof(pdata)); |
| 1940 | + if (ret) |
| 1941 | + dev_warn(sdev->dev, |
| 1942 | + "multi_card: failed to register card dmic: %d\n", |
| 1943 | + ret); |
| 1944 | + else |
| 1945 | + card_idx++; |
| 1946 | + } |
| 1947 | + |
| 1948 | + /* HDMI card */ |
| 1949 | + if (HDA_IDISP_CODEC(mach->mach_params.codec_mask)) { |
| 1950 | + memcpy(&pdata.machine, mach, sizeof(pdata.machine)); |
| 1951 | + pdata.machine.mach_params.links = empty_links; |
| 1952 | + pdata.machine.mach_params.link_mask = 0; |
| 1953 | + pdata.machine.mach_params.codec_mask = BIT(HDA_IDISP_ADDR); |
| 1954 | + pdata.machine.mach_params.dmic_num = 0; |
| 1955 | + pdata.machine.mach_params.card_name = "hdmi"; |
| 1956 | + ret = sof_client_dev_register(sdev, "audio", card_idx, |
| 1957 | + &pdata, sizeof(pdata)); |
| 1958 | + if (ret) |
| 1959 | + dev_warn(sdev->dev, |
| 1960 | + "multi_card: failed to register card hdmi: %d\n", |
| 1961 | + ret); |
| 1962 | + else |
| 1963 | + card_idx++; |
| 1964 | + } |
| 1965 | + |
| 1966 | + hdev->num_audio_clients = card_idx; |
| 1967 | + |
| 1968 | + return 0; |
| 1969 | +} |
| 1970 | + |
| 1971 | +int hda_register_audio_client(struct snd_sof_dev *sdev) |
| 1972 | +{ |
| 1973 | + if (multi_card) { |
| 1974 | + const struct snd_soc_acpi_mach *mach = sdev->pdata->machine; |
| 1975 | + |
| 1976 | + if (mach && !sdev->pdata->disable_function_topology && |
| 1977 | + mach->get_function_tplg_files) |
| 1978 | + return hda_register_audio_client_multi(sdev); |
| 1979 | + |
| 1980 | + dev_warn(sdev->dev, |
| 1981 | + "multi_card is only supported with function topologies, using single card\n"); |
| 1982 | + } |
| 1983 | + |
| 1984 | + return sof_register_audio_client(sdev); |
| 1985 | +} |
| 1986 | + |
| 1987 | +void hda_unregister_audio_client(struct snd_sof_dev *sdev) |
| 1988 | +{ |
| 1989 | + if (multi_card) { |
| 1990 | + const struct snd_soc_acpi_mach *mach = sdev->pdata->machine; |
| 1991 | + |
| 1992 | + if (mach && !sdev->pdata->disable_function_topology && |
| 1993 | + mach->get_function_tplg_files) { |
| 1994 | + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; |
| 1995 | + int i; |
| 1996 | + |
| 1997 | + for (i = hdev->num_audio_clients - 1; i >= 0; i--) |
| 1998 | + sof_client_dev_unregister(sdev, "audio", i); |
| 1999 | + hdev->num_audio_clients = 0; |
| 2000 | + return; |
| 2001 | + } |
| 2002 | + } |
| 2003 | + |
| 2004 | + sof_unregister_audio_client(sdev); |
| 2005 | +} |
| 2006 | + |
1770 | 2007 | int hda_register_clients(struct snd_sof_dev *sdev) |
1771 | 2008 | { |
1772 | 2009 | return hda_probes_register(sdev); |
|
0 commit comments