| /* |
| * exynos8895_sound.c |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the |
| * Free Software Foundation; either version 2 of the License, or (at your |
| * option) any later version. |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/of.h> |
| #include <linux/clk.h> |
| #include <linux/io.h> |
| #include <linux/module.h> |
| |
| #include <sound/soc.h> |
| #include <sound/pcm.h> |
| #include <sound/pcm_params.h> |
| #include <sound/initval.h> |
| |
| #include <sound/samsung/abox.h> |
| |
| static const struct snd_soc_ops rdma_ops = { |
| }; |
| |
| static const struct snd_soc_ops wdma_ops = { |
| }; |
| |
| static const struct snd_soc_ops uaif_ops = { |
| }; |
| |
| static int dsif_hw_params(struct snd_pcm_substream *substream, |
| struct snd_pcm_hw_params *hw_params) |
| { |
| struct snd_soc_pcm_runtime *rtd = substream->private_data; |
| struct snd_soc_dai *cpu_dai = rtd->cpu_dai; |
| int tx_slot[] = {0, 1}; |
| |
| /* bclk ratio 64 for DSD64, 128 for DSD128 */ |
| snd_soc_dai_set_bclk_ratio(cpu_dai, 64); |
| |
| /* channel map 0 1 if left is first, 1 0 if right is first */ |
| snd_soc_dai_set_channel_map(cpu_dai, 2, tx_slot, 0, NULL); |
| return 0; |
| } |
| |
| static const struct snd_soc_ops dsif_ops = { |
| .hw_params = dsif_hw_params, |
| }; |
| |
| static struct snd_soc_dai_link exynos8895_dai_links[] = { |
| { |
| .name = "RDMA0", |
| .stream_name = "RDMA0", |
| .cpu_dai_name = "RDMA0", |
| .platform_name = "13e51000.abox_rdma", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .dynamic = 1, |
| .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST}, |
| .ops = &rdma_ops, |
| .dpcm_playback = 1, |
| }, |
| { |
| .name = "RDMA1", |
| .stream_name = "RDMA1", |
| .cpu_dai_name = "RDMA1", |
| .platform_name = "13e51100.abox_rdma", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .dynamic = 1, |
| .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST}, |
| .ops = &rdma_ops, |
| .dpcm_playback = 1, |
| }, |
| { |
| .name = "RDMA2", |
| .stream_name = "RDMA2", |
| .cpu_dai_name = "RDMA2", |
| .platform_name = "13e51200.abox_rdma", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .dynamic = 1, |
| .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST}, |
| .ops = &rdma_ops, |
| .dpcm_playback = 1, |
| }, |
| { |
| .name = "RDMA3", |
| .stream_name = "RDMA3", |
| .cpu_dai_name = "RDMA3", |
| .platform_name = "13e51300.abox_rdma", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .dynamic = 1, |
| .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST}, |
| .ops = &rdma_ops, |
| .dpcm_playback = 1, |
| }, |
| { |
| .name = "RDMA4", |
| .stream_name = "RDMA4", |
| .cpu_dai_name = "RDMA4", |
| .platform_name = "13e51400.abox_rdma", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .dynamic = 1, |
| .ignore_suspend = 1, |
| .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST}, |
| .ops = &rdma_ops, |
| .dpcm_playback = 1, |
| }, |
| { |
| .name = "RDMA5", |
| .stream_name = "RDMA5", |
| .cpu_dai_name = "RDMA5", |
| .platform_name = "13e51500.abox_rdma", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .dynamic = 1, |
| .ignore_suspend = 1, |
| .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST}, |
| .ops = &rdma_ops, |
| .dpcm_playback = 1, |
| }, |
| { |
| .name = "RDMA6", |
| .stream_name = "RDMA6", |
| .cpu_dai_name = "RDMA6", |
| .platform_name = "13e51600.abox_rdma", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .dynamic = 1, |
| .ignore_suspend = 1, |
| .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST}, |
| .ops = &rdma_ops, |
| .dpcm_playback = 1, |
| }, |
| { |
| .name = "RDMA7", |
| .stream_name = "RDMA7", |
| .cpu_dai_name = "RDMA7", |
| .platform_name = "13e51700.abox_rdma", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .dynamic = 1, |
| .ignore_suspend = 1, |
| .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST}, |
| .ops = &rdma_ops, |
| .dpcm_playback = 1, |
| }, |
| { |
| .name = "WDMA0", |
| .stream_name = "WDMA0", |
| .cpu_dai_name = "WDMA0", |
| .platform_name = "13e52000.abox_wdma", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .dynamic = 1, |
| .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST}, |
| .ops = &wdma_ops, |
| .dpcm_capture = 1, |
| }, |
| { |
| .name = "WDMA1", |
| .stream_name = "WDMA1", |
| .cpu_dai_name = "WDMA1", |
| .platform_name = "13e52100.abox_wdma", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .dynamic = 1, |
| .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST}, |
| .ops = &wdma_ops, |
| .dpcm_capture = 1, |
| }, |
| { |
| .name = "WDMA2", |
| .stream_name = "WDMA2", |
| .cpu_dai_name = "WDMA2", |
| .platform_name = "13e52200.abox_wdma", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .dynamic = 1, |
| .ignore_suspend = 1, |
| .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST}, |
| .ops = &wdma_ops, |
| .dpcm_capture = 1, |
| }, |
| { |
| .name = "WDMA3", |
| .stream_name = "WDMA3", |
| .cpu_dai_name = "WDMA3", |
| .platform_name = "13e52300.abox_wdma", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .dynamic = 1, |
| .ignore_suspend = 1, |
| .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST}, |
| .ops = &wdma_ops, |
| .dpcm_capture = 1, |
| }, |
| { |
| .name = "WDMA4", |
| .stream_name = "WDMA4", |
| .cpu_dai_name = "WDMA4", |
| .platform_name = "13e52400.abox_wdma", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .dynamic = 1, |
| .ignore_suspend = 1, |
| .trigger = {SND_SOC_DPCM_TRIGGER_POST_PRE, SND_SOC_DPCM_TRIGGER_PRE_POST}, |
| .ops = &wdma_ops, |
| .dpcm_capture = 1, |
| }, |
| { |
| .name = "UAIF0", |
| .stream_name = "UAIF0", |
| .cpu_dai_name = "UAIF0", |
| .platform_name = "snd-soc-dummy", |
| .codec_dai_name = "cod3033x-aif", |
| .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, |
| .no_pcm = 1, |
| .ignore_suspend = 1, |
| .ignore_pmdown_time = 1, |
| .be_hw_params_fixup = abox_hw_params_fixup_helper, |
| .ops = &uaif_ops, |
| .dpcm_playback = 1, |
| .dpcm_capture = 1, |
| }, |
| { |
| .name = "UAIF1", |
| .stream_name = "UAIF1", |
| .cpu_dai_name = "UAIF1", |
| .platform_name = "snd-soc-dummy", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM, |
| .no_pcm = 1, |
| .ignore_suspend = 1, |
| .ignore_pmdown_time = 1, |
| .be_hw_params_fixup = abox_hw_params_fixup_helper, |
| .ops = &uaif_ops, |
| .dpcm_playback = 1, |
| .dpcm_capture = 1, |
| }, |
| { |
| .name = "UAIF2", |
| .stream_name = "UAIF2", |
| .cpu_dai_name = "UAIF2", |
| .platform_name = "snd-soc-dummy", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM, |
| .no_pcm = 1, |
| .ignore_suspend = 1, |
| .ignore_pmdown_time = 1, |
| .be_hw_params_fixup = abox_hw_params_fixup_helper, |
| .ops = &uaif_ops, |
| .dpcm_playback = 1, |
| .dpcm_capture = 1, |
| }, |
| { |
| .name = "UAIF3", |
| .stream_name = "UAIF3", |
| .cpu_dai_name = "UAIF3", |
| .platform_name = "snd-soc-dummy", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM, |
| .no_pcm = 1, |
| .ignore_suspend = 1, |
| .ignore_pmdown_time = 1, |
| .be_hw_params_fixup = abox_hw_params_fixup_helper, |
| .ops = &uaif_ops, |
| .dpcm_playback = 1, |
| .dpcm_capture = 1, |
| }, |
| { |
| .name = "UAIF4", |
| .stream_name = "UAIF4", |
| .cpu_dai_name = "UAIF4", |
| .platform_name = "snd-soc-dummy", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM, |
| .no_pcm = 1, |
| .ignore_suspend = 1, |
| .ignore_pmdown_time = 1, |
| .be_hw_params_fixup = abox_hw_params_fixup_helper, |
| .ops = &uaif_ops, |
| .dpcm_playback = 1, |
| .dpcm_capture = 1, |
| }, |
| { |
| .name = "DSIF", |
| .stream_name = "DSIF", |
| .cpu_dai_name = "DSIF", |
| .platform_name = "snd-soc-dummy", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .dai_fmt = SND_SOC_DAIFMT_PDM | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, |
| .no_pcm = 1, |
| .ignore_suspend = 1, |
| .ignore_pmdown_time = 1, |
| .be_hw_params_fixup = abox_hw_params_fixup_helper, |
| .ops = &dsif_ops, |
| .dpcm_playback = 1, |
| }, |
| { |
| .name = "ABOX Internal", |
| .stream_name = "ABOX Internal", |
| .cpu_dai_name = "Internal", |
| .platform_name = "snd-soc-dummy", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .dai_fmt = SND_SOC_DAIFMT_PDM | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, |
| .no_pcm = 1, |
| .ignore_suspend = 1, |
| .ignore_pmdown_time = 1, |
| .dpcm_playback = 1, |
| .dpcm_capture = 1, |
| }, |
| { |
| .name = "VTS-Trigger", |
| .stream_name = "VTS-Trigger", |
| .cpu_dai_name = "vts-tri", |
| .platform_name = "vts_dma0", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .ignore_suspend = 1, |
| .ignore_pmdown_time = 1, |
| .capture_only = true, |
| }, |
| { |
| .name = "VTS-Record", |
| .stream_name = "VTS-Record", |
| .cpu_dai_name = "vts-rec", |
| .platform_name = "vts_dma1", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .ignore_suspend = 1, |
| .ignore_pmdown_time = 1, |
| .capture_only = true, |
| }, |
| { |
| .name = "DP Audio", |
| .stream_name = "DP Audio", |
| .cpu_dai_name = "snd-soc-dummy-dai", |
| .platform_name = "15a40000.displayport_adma", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| }, |
| }; |
| |
| static struct snd_soc_dapm_widget exynos8895_widgets[] = { |
| SND_SOC_DAPM_MIC("DMIC1", NULL), |
| SND_SOC_DAPM_MIC("DMIC2", NULL), |
| SND_SOC_DAPM_MIC("DMIC3", NULL), |
| SND_SOC_DAPM_MIC("EARMIC", NULL), |
| }; |
| |
| static struct snd_soc_dapm_route exynos8895_routes[] = { |
| {"VTS PAD DPDM", NULL, "DMIC3"}, |
| }; |
| |
| static struct snd_soc_codec_conf codec_conf[] = { |
| { |
| .name_prefix = "ABOX", |
| }, |
| { |
| .name_prefix = "VTS", |
| }, |
| }; |
| |
| static int exynos8895_sound_late_probe(struct snd_soc_card *card) |
| { |
| /* force enable mic bias for VTS */ |
| // snd_soc_dapm_force_enable_pin(&card->dapm, "DMIC3"); |
| // snd_soc_dapm_sync(&card->dapm); |
| return 0; |
| } |
| |
| static struct snd_soc_card exynos8895_snd_card = { |
| .name = "Exynos8895-snd-card", |
| .owner = THIS_MODULE, |
| .dai_link = exynos8895_dai_links, |
| .num_links = ARRAY_SIZE(exynos8895_dai_links), |
| .dapm_widgets = exynos8895_widgets, |
| .num_dapm_widgets = ARRAY_SIZE(exynos8895_widgets), |
| .dapm_routes = exynos8895_routes, |
| .num_dapm_routes = ARRAY_SIZE(exynos8895_routes), |
| .codec_conf = codec_conf, |
| .num_configs = ARRAY_SIZE(codec_conf), |
| .late_probe = exynos8895_sound_late_probe, |
| }; |
| |
| static int exynos8895_sound_probe(struct platform_device *pdev) |
| { |
| struct device *dev = &pdev->dev; |
| struct device_node *np = pdev->dev.of_node; |
| struct snd_soc_card *card = &exynos8895_snd_card; |
| int n, ret; |
| |
| dev_info(dev, "%s\n", __func__); |
| |
| card->dev = &pdev->dev; |
| |
| for (n = 0; np && n < ARRAY_SIZE(exynos8895_dai_links); n++) { |
| if (!exynos8895_dai_links[n].cpu_dai_name) { |
| exynos8895_dai_links[n].cpu_of_node = of_parse_phandle(np, |
| "samsung,audio-cpu", n); |
| |
| if (!exynos8895_dai_links[n].cpu_of_node) { |
| dev_err(&pdev->dev, "Property 'samsung,audio-cpu' missing or invalid\n"); |
| ret = -EINVAL; |
| } |
| } |
| |
| if (!exynos8895_dai_links[n].platform_name) { |
| exynos8895_dai_links[n].platform_of_node = exynos8895_dai_links[n].cpu_of_node; |
| } |
| |
| if (!exynos8895_dai_links[n].codec_name) { |
| exynos8895_dai_links[n].codec_of_node = of_parse_phandle(np, |
| "samsung,audio-codec", n); |
| if (!exynos8895_dai_links[n].codec_of_node) { |
| dev_err(&pdev->dev, "Property 'samsung,audio-codec' missing or invalid\n"); |
| ret = -EINVAL; |
| } |
| } |
| } |
| |
| for (n = 0; n < ARRAY_SIZE(codec_conf); n++) { |
| codec_conf[n].of_node = of_parse_phandle(np, "samsung,codec", n); |
| |
| if (!codec_conf[n].of_node) { |
| dev_err(&pdev->dev, |
| "Property 'samsung,codec' missing\n"); |
| return -EINVAL; |
| } |
| } |
| |
| ret = devm_snd_soc_register_card(dev, card); |
| |
| if (ret) |
| dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret); |
| |
| return ret; |
| } |
| |
| static int exynos8895_sound_remove(struct platform_device *pdev) |
| { |
| struct snd_soc_card *card = platform_get_drvdata(pdev); |
| |
| snd_soc_unregister_card(card); |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_OF |
| static const struct of_device_id exynos8895_sound_of_match[] = { |
| { .compatible = "samsung,exynos8895-sound", }, |
| {}, |
| }; |
| MODULE_DEVICE_TABLE(of, exynos8895_sound_of_match); |
| #endif /* CONFIG_OF */ |
| |
| static struct platform_driver exynos8895_sound_driver = { |
| .driver = { |
| .name = "exynos8895-sound", |
| .owner = THIS_MODULE, |
| .pm = &snd_soc_pm_ops, |
| .of_match_table = of_match_ptr(exynos8895_sound_of_match), |
| }, |
| .probe = exynos8895_sound_probe, |
| .remove = exynos8895_sound_remove, |
| }; |
| |
| module_platform_driver(exynos8895_sound_driver); |
| |
| MODULE_DESCRIPTION("ALSA SoC EXYNOS8895 sound driver"); |
| MODULE_LICENSE("GPL"); |
| MODULE_ALIAS("platform:exynos8895-sound"); |
| |