Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 //
0003 // smdk_spdif.c - S/PDIF audio for SMDK
0004 //
0005 // Copyright (C) 2010 Samsung Electronics Co., Ltd.
0006 
0007 #include <linux/clk.h>
0008 #include <linux/module.h>
0009 
0010 #include <sound/soc.h>
0011 
0012 #include "spdif.h"
0013 
0014 /* Audio clock settings are belonged to board specific part. Every
0015  * board can set audio source clock setting which is matched with H/W
0016  * like this function-'set_audio_clock_heirachy'.
0017  */
0018 static int set_audio_clock_heirachy(struct platform_device *pdev)
0019 {
0020     struct clk *fout_epll, *mout_epll, *sclk_audio0, *sclk_spdif;
0021     int ret = 0;
0022 
0023     fout_epll = clk_get(NULL, "fout_epll");
0024     if (IS_ERR(fout_epll)) {
0025         printk(KERN_WARNING "%s: Cannot find fout_epll.\n",
0026                 __func__);
0027         return -EINVAL;
0028     }
0029 
0030     mout_epll = clk_get(NULL, "mout_epll");
0031     if (IS_ERR(mout_epll)) {
0032         printk(KERN_WARNING "%s: Cannot find mout_epll.\n",
0033                 __func__);
0034         ret = -EINVAL;
0035         goto out1;
0036     }
0037 
0038     sclk_audio0 = clk_get(&pdev->dev, "sclk_audio");
0039     if (IS_ERR(sclk_audio0)) {
0040         printk(KERN_WARNING "%s: Cannot find sclk_audio.\n",
0041                 __func__);
0042         ret = -EINVAL;
0043         goto out2;
0044     }
0045 
0046     sclk_spdif = clk_get(NULL, "sclk_spdif");
0047     if (IS_ERR(sclk_spdif)) {
0048         printk(KERN_WARNING "%s: Cannot find sclk_spdif.\n",
0049                 __func__);
0050         ret = -EINVAL;
0051         goto out3;
0052     }
0053 
0054     /* Set audio clock hierarchy for S/PDIF */
0055     clk_set_parent(mout_epll, fout_epll);
0056     clk_set_parent(sclk_audio0, mout_epll);
0057     clk_set_parent(sclk_spdif, sclk_audio0);
0058 
0059     clk_put(sclk_spdif);
0060 out3:
0061     clk_put(sclk_audio0);
0062 out2:
0063     clk_put(mout_epll);
0064 out1:
0065     clk_put(fout_epll);
0066 
0067     return ret;
0068 }
0069 
0070 /* We should haved to set clock directly on this part because of clock
0071  * scheme of Samsudng SoCs did not support to set rates from abstrct
0072  * clock of it's hierarchy.
0073  */
0074 static int set_audio_clock_rate(unsigned long epll_rate,
0075                 unsigned long audio_rate)
0076 {
0077     struct clk *fout_epll, *sclk_spdif;
0078 
0079     fout_epll = clk_get(NULL, "fout_epll");
0080     if (IS_ERR(fout_epll)) {
0081         printk(KERN_ERR "%s: failed to get fout_epll\n", __func__);
0082         return -ENOENT;
0083     }
0084 
0085     clk_set_rate(fout_epll, epll_rate);
0086     clk_put(fout_epll);
0087 
0088     sclk_spdif = clk_get(NULL, "sclk_spdif");
0089     if (IS_ERR(sclk_spdif)) {
0090         printk(KERN_ERR "%s: failed to get sclk_spdif\n", __func__);
0091         return -ENOENT;
0092     }
0093 
0094     clk_set_rate(sclk_spdif, audio_rate);
0095     clk_put(sclk_spdif);
0096 
0097     return 0;
0098 }
0099 
0100 static int smdk_hw_params(struct snd_pcm_substream *substream,
0101         struct snd_pcm_hw_params *params)
0102 {
0103     struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0104     struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
0105     unsigned long pll_out, rclk_rate;
0106     int ret, ratio;
0107 
0108     switch (params_rate(params)) {
0109     case 44100:
0110         pll_out = 45158400;
0111         break;
0112     case 32000:
0113     case 48000:
0114     case 96000:
0115         pll_out = 49152000;
0116         break;
0117     default:
0118         return -EINVAL;
0119     }
0120 
0121     /* Setting ratio to 512fs helps to use S/PDIF with HDMI without
0122      * modify S/PDIF ASoC machine driver.
0123      */
0124     ratio = 512;
0125     rclk_rate = params_rate(params) * ratio;
0126 
0127     /* Set audio source clock rates */
0128     ret = set_audio_clock_rate(pll_out, rclk_rate);
0129     if (ret < 0)
0130         return ret;
0131 
0132     /* Set S/PDIF uses internal source clock */
0133     ret = snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK,
0134                     rclk_rate, SND_SOC_CLOCK_IN);
0135     if (ret < 0)
0136         return ret;
0137 
0138     return ret;
0139 }
0140 
0141 static const struct snd_soc_ops smdk_spdif_ops = {
0142     .hw_params = smdk_hw_params,
0143 };
0144 
0145 SND_SOC_DAILINK_DEFS(spdif,
0146     DAILINK_COMP_ARRAY(COMP_CPU("samsung-spdif")),
0147     DAILINK_COMP_ARRAY(COMP_CODEC("spdif-dit", "dit-hifi")),
0148     DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-spdif")));
0149 
0150 static struct snd_soc_dai_link smdk_dai = {
0151     .name = "S/PDIF",
0152     .stream_name = "S/PDIF PCM Playback",
0153     .ops = &smdk_spdif_ops,
0154     SND_SOC_DAILINK_REG(spdif),
0155 };
0156 
0157 static struct snd_soc_card smdk = {
0158     .name = "SMDK-S/PDIF",
0159     .owner = THIS_MODULE,
0160     .dai_link = &smdk_dai,
0161     .num_links = 1,
0162 };
0163 
0164 static struct platform_device *smdk_snd_spdif_dit_device;
0165 static struct platform_device *smdk_snd_spdif_device;
0166 
0167 static int __init smdk_init(void)
0168 {
0169     int ret;
0170 
0171     smdk_snd_spdif_dit_device = platform_device_alloc("spdif-dit", -1);
0172     if (!smdk_snd_spdif_dit_device)
0173         return -ENOMEM;
0174 
0175     ret = platform_device_add(smdk_snd_spdif_dit_device);
0176     if (ret)
0177         goto err1;
0178 
0179     smdk_snd_spdif_device = platform_device_alloc("soc-audio", -1);
0180     if (!smdk_snd_spdif_device) {
0181         ret = -ENOMEM;
0182         goto err2;
0183     }
0184 
0185     platform_set_drvdata(smdk_snd_spdif_device, &smdk);
0186 
0187     ret = platform_device_add(smdk_snd_spdif_device);
0188     if (ret)
0189         goto err3;
0190 
0191     /* Set audio clock hierarchy manually */
0192     ret = set_audio_clock_heirachy(smdk_snd_spdif_device);
0193     if (ret)
0194         goto err4;
0195 
0196     return 0;
0197 err4:
0198     platform_device_del(smdk_snd_spdif_device);
0199 err3:
0200     platform_device_put(smdk_snd_spdif_device);
0201 err2:
0202     platform_device_del(smdk_snd_spdif_dit_device);
0203 err1:
0204     platform_device_put(smdk_snd_spdif_dit_device);
0205     return ret;
0206 }
0207 
0208 static void __exit smdk_exit(void)
0209 {
0210     platform_device_unregister(smdk_snd_spdif_device);
0211     platform_device_unregister(smdk_snd_spdif_dit_device);
0212 }
0213 
0214 module_init(smdk_init);
0215 module_exit(smdk_exit);
0216 
0217 MODULE_AUTHOR("Seungwhan Youn, <sw.youn@samsung.com>");
0218 MODULE_DESCRIPTION("ALSA SoC SMDK+S/PDIF");
0219 MODULE_LICENSE("GPL");