diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 7d5d6444a83737..6eb34fdea62581 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -55,6 +55,7 @@ source "sound/soc/spear/Kconfig" source "sound/soc/tegra/Kconfig" source "sound/soc/txx9/Kconfig" source "sound/soc/ux500/Kconfig" +source "sound/soc/xilinx/Kconfig" # Supported codecs source "sound/soc/codecs/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index d88edfced8c498..6cd66fe4b1609e 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -32,3 +32,4 @@ obj-$(CONFIG_SND_SOC) += spear/ obj-$(CONFIG_SND_SOC) += tegra/ obj-$(CONFIG_SND_SOC) += txx9/ obj-$(CONFIG_SND_SOC) += ux500/ +obj-$(CONFIG_SND_SOC) += xilinx/ diff --git a/sound/soc/xilinx/Kconfig b/sound/soc/xilinx/Kconfig new file mode 100644 index 00000000000000..94718542d74233 --- /dev/null +++ b/sound/soc/xilinx/Kconfig @@ -0,0 +1,5 @@ +config SND_SOC_XILINX_DP + tristate "Audio support for the the Xilinx DisplayPort" + select SND_DMAENGINE_PCM + help + Audio support the for Xilinx DisplayPort. diff --git a/sound/soc/xilinx/Makefile b/sound/soc/xilinx/Makefile new file mode 100644 index 00000000000000..f8d764f56e942e --- /dev/null +++ b/sound/soc/xilinx/Makefile @@ -0,0 +1,3 @@ +snd-soc-xilinx-dp-objs := xilinx-dp-pcm.o xilinx-dp-codec.o xilinx-dp-card.o + +obj-$(CONFIG_SND_SOC_XILINX_DP) += snd-soc-xilinx-dp.o diff --git a/sound/soc/xilinx/xilinx-dp-card.c b/sound/soc/xilinx/xilinx-dp-card.c new file mode 100644 index 00000000000000..e70c47860ac2ba --- /dev/null +++ b/sound/soc/xilinx/xilinx-dp-card.c @@ -0,0 +1,102 @@ +/* + * Xilinx DisplayPort SoC Sound Card support + * + * Copyright (C) 2015 Xilinx, Inc. + * + * Author: Hyun Woo Kwon + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include + +static struct snd_soc_dai_link xilinx_dp_dai_links[] = { + { + .name = "xilinx-dp0", + .codec_dai_name = "xilinx-dp-snd-codec-dai", + }, + { + .name = "xilinx-dp1", + .codec_dai_name = "xilinx-dp-snd-codec-dai", + }, + +}; + +static struct snd_soc_card xilinx_dp_card = { + .name = "DisplayPort monitor", + .owner = THIS_MODULE, + .dai_link = xilinx_dp_dai_links, + .num_links = 2, +}; + +static int xilinx_dp_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &xilinx_dp_card; + struct device_node *node = pdev->dev.of_node; + struct device_node *codec, *pcm; + int ret; + + card->dev = &pdev->dev; + + codec = of_parse_phandle(node, "xlnx,dp-snd-codec", 0); + if (!codec) + return -ENODEV; + + pcm = of_parse_phandle(node, "xlnx,dp-snd-pcm", 0); + if (!pcm) + return -ENODEV; + xilinx_dp_dai_links[0].platform_of_node = pcm; + xilinx_dp_dai_links[0].cpu_of_node = codec; + xilinx_dp_dai_links[0].codec_of_node = codec; + + pcm = of_parse_phandle(node, "xlnx,dp-snd-pcm", 1); + if (!pcm) + return -ENODEV; + xilinx_dp_dai_links[1].platform_of_node = pcm; + xilinx_dp_dai_links[1].cpu_of_node = codec; + xilinx_dp_dai_links[1].codec_of_node = codec; + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) + return ret; + + dev_info(&pdev->dev, "Xilinx DisplayPort Sound Card probed\n"); + + return 0; +} + +static int xilinx_dp_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id xilinx_dp_of_match[] = { + { .compatible = "xlnx,dp-snd-card", }, + {}, +}; +MODULE_DEVICE_TABLE(of, xilinx_dp_of_match); + +static struct platform_driver xilinx_dp_aud_driver = { + .driver = { + .name = "xilinx-dp-snd-card", + .of_match_table = xilinx_dp_of_match, + .pm = &snd_soc_pm_ops, + }, + .probe = xilinx_dp_probe, + .remove = xilinx_dp_remove, +}; +module_platform_driver(xilinx_dp_aud_driver); + +MODULE_DESCRIPTION("Xilinx DisplayPort Sound Card module"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/xilinx/xilinx-dp-codec.c b/sound/soc/xilinx/xilinx-dp-codec.c new file mode 100644 index 00000000000000..74efb9018048c4 --- /dev/null +++ b/sound/soc/xilinx/xilinx-dp-codec.c @@ -0,0 +1,118 @@ +/* + * Xilinx DisplayPort Sound Codec support + * + * Copyright (C) 2015 Xilinx, Inc. + * + * Author: Hyun Woo Kwon + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +#include + +/** + * struct xilinx_dp_codec - DisplayPort codec + * @aud_clk: audio clock + */ +struct xilinx_dp_codec { + struct clk *aud_clk; +}; + +static struct snd_soc_dai_driver xilinx_dp_codec_dai = { + .name = "xilinx-dp-snd-codec-dai", + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_44100, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}; + +static const struct snd_soc_codec_driver xilinx_dp_codec_codec_driver = { +}; + +static int xilinx_dp_codec_probe(struct platform_device *pdev) +{ + struct xilinx_dp_codec *codec; + int rate, ret; + + codec = devm_kzalloc(&pdev->dev, sizeof(*codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + + codec->aud_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(codec->aud_clk)) + return PTR_ERR(codec->aud_clk); + + ret = clk_prepare_enable(codec->aud_clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable the aud_clk\n"); + return ret; + } + + rate = clk_get_rate(codec->aud_clk) / 512; + if (rate == 44100) { + xilinx_dp_codec_dai.playback.rates = SNDRV_PCM_RATE_44100; + } else if (rate == 48000) { + xilinx_dp_codec_dai.playback.rates = SNDRV_PCM_RATE_48000; + } else { + ret = -EINVAL; + goto error_clk; + } + + ret = snd_soc_register_codec(&pdev->dev, &xilinx_dp_codec_codec_driver, + &xilinx_dp_codec_dai, 1); + if (ret) + goto error_clk; + + platform_set_drvdata(pdev, codec); + + dev_info(&pdev->dev, "Xilinx DisplayPort Sound Codec probed\n"); + + return 0; + +error_clk: + clk_disable_unprepare(codec->aud_clk); + return ret; +} + +static int xilinx_dp_codec_dev_remove(struct platform_device *pdev) +{ + struct xilinx_dp_codec *codec = platform_get_drvdata(pdev); + + snd_soc_unregister_codec(&pdev->dev); + clk_disable_unprepare(codec->aud_clk); + + return 0; +} + +static const struct of_device_id xilinx_dp_codec_of_match[] = { + { .compatible = "xlnx,dp-snd-codec", }, + { /* end of table */ }, +}; +MODULE_DEVICE_TABLE(of, xilinx_dp_codec_of_match); + +static struct platform_driver xilinx_dp_codec_driver = { + .driver = { + .name = "xilinx-dp-snd-codec", + .of_match_table = xilinx_dp_codec_of_match, + }, + .probe = xilinx_dp_codec_probe, + .remove = xilinx_dp_codec_dev_remove, +}; +module_platform_driver(xilinx_dp_codec_driver); + +MODULE_DESCRIPTION("Xilinx DisplayPort Sound Codec module"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/xilinx/xilinx-dp-pcm.c b/sound/soc/xilinx/xilinx-dp-pcm.c new file mode 100644 index 00000000000000..4c2cdfb9d6135f --- /dev/null +++ b/sound/soc/xilinx/xilinx-dp-pcm.c @@ -0,0 +1,81 @@ +/* + * Xilinx DisplayPort Sound PCM support + * + * Copyright (C) 2015 Xilinx, Inc. + * + * Author: Hyun Woo Kwon + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include +#include +#include + +static const struct snd_pcm_hardware xilinx_pcm_hw = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 256, + .period_bytes_max = 1024 * 1024, + .periods_min = 2, + .periods_max = 256, +}; + +static const struct snd_dmaengine_pcm_config xilinx_dmaengine_pcm_config = { + .pcm_hardware = &xilinx_pcm_hw, + .prealloc_buffer_size = 64 * 1024, +}; + +static int xilinx_dp_pcm_probe(struct platform_device *pdev) +{ + int ret; + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, + &xilinx_dmaengine_pcm_config, 0); + if (ret) + return ret; + + dev_info(&pdev->dev, "Xilinx DisplayPort Sound PCM probed\n"); + + return 0; +} + +static int xilinx_dp_pcm_dev_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id xilinx_dp_pcm_of_match[] = { + { .compatible = "xlnx,dp-snd-pcm", }, + { /* end of table */ }, +}; +MODULE_DEVICE_TABLE(of, xilinx_dp_pcm_of_match); + +static struct platform_driver xilinx_dp_pcm_driver = { + .driver = { + .name = "xilinx-dp-snd-pcm", + .of_match_table = xilinx_dp_pcm_of_match, + }, + .probe = xilinx_dp_pcm_probe, + .remove = xilinx_dp_pcm_dev_remove, +}; +module_platform_driver(xilinx_dp_pcm_driver); + +MODULE_DESCRIPTION("Xilinx DisplayPort Sound PCM module"); +MODULE_LICENSE("GPL v2");