~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/samsung/smdk_spdif.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0+
  2 //
  3 // smdk_spdif.c - S/PDIF audio for SMDK
  4 //
  5 // Copyright (C) 2010 Samsung Electronics Co., Ltd.
  6 
  7 #include <linux/clk.h>
  8 #include <linux/module.h>
  9 
 10 #include <sound/soc.h>
 11 
 12 #include "spdif.h"
 13 
 14 /* Audio clock settings are belonged to board specific part. Every
 15  * board can set audio source clock setting which is matched with H/W
 16  * like this function-'set_audio_clock_heirachy'.
 17  */
 18 static int set_audio_clock_heirachy(struct platform_device *pdev)
 19 {
 20         struct clk *fout_epll, *mout_epll, *sclk_audio0, *sclk_spdif;
 21         int ret = 0;
 22 
 23         fout_epll = clk_get(NULL, "fout_epll");
 24         if (IS_ERR(fout_epll)) {
 25                 printk(KERN_WARNING "%s: Cannot find fout_epll.\n",
 26                                 __func__);
 27                 return -EINVAL;
 28         }
 29 
 30         mout_epll = clk_get(NULL, "mout_epll");
 31         if (IS_ERR(mout_epll)) {
 32                 printk(KERN_WARNING "%s: Cannot find mout_epll.\n",
 33                                 __func__);
 34                 ret = -EINVAL;
 35                 goto out1;
 36         }
 37 
 38         sclk_audio0 = clk_get(&pdev->dev, "sclk_audio");
 39         if (IS_ERR(sclk_audio0)) {
 40                 printk(KERN_WARNING "%s: Cannot find sclk_audio.\n",
 41                                 __func__);
 42                 ret = -EINVAL;
 43                 goto out2;
 44         }
 45 
 46         sclk_spdif = clk_get(NULL, "sclk_spdif");
 47         if (IS_ERR(sclk_spdif)) {
 48                 printk(KERN_WARNING "%s: Cannot find sclk_spdif.\n",
 49                                 __func__);
 50                 ret = -EINVAL;
 51                 goto out3;
 52         }
 53 
 54         /* Set audio clock hierarchy for S/PDIF */
 55         clk_set_parent(mout_epll, fout_epll);
 56         clk_set_parent(sclk_audio0, mout_epll);
 57         clk_set_parent(sclk_spdif, sclk_audio0);
 58 
 59         clk_put(sclk_spdif);
 60 out3:
 61         clk_put(sclk_audio0);
 62 out2:
 63         clk_put(mout_epll);
 64 out1:
 65         clk_put(fout_epll);
 66 
 67         return ret;
 68 }
 69 
 70 /* We should haved to set clock directly on this part because of clock
 71  * scheme of Samsudng SoCs did not support to set rates from abstrct
 72  * clock of it's hierarchy.
 73  */
 74 static int set_audio_clock_rate(unsigned long epll_rate,
 75                                 unsigned long audio_rate)
 76 {
 77         struct clk *fout_epll, *sclk_spdif;
 78 
 79         fout_epll = clk_get(NULL, "fout_epll");
 80         if (IS_ERR(fout_epll)) {
 81                 printk(KERN_ERR "%s: failed to get fout_epll\n", __func__);
 82                 return -ENOENT;
 83         }
 84 
 85         clk_set_rate(fout_epll, epll_rate);
 86         clk_put(fout_epll);
 87 
 88         sclk_spdif = clk_get(NULL, "sclk_spdif");
 89         if (IS_ERR(sclk_spdif)) {
 90                 printk(KERN_ERR "%s: failed to get sclk_spdif\n", __func__);
 91                 return -ENOENT;
 92         }
 93 
 94         clk_set_rate(sclk_spdif, audio_rate);
 95         clk_put(sclk_spdif);
 96 
 97         return 0;
 98 }
 99 
100 static int smdk_hw_params(struct snd_pcm_substream *substream,
101                 struct snd_pcm_hw_params *params)
102 {
103         struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
104         struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
105         unsigned long pll_out, rclk_rate;
106         int ret, ratio;
107 
108         switch (params_rate(params)) {
109         case 44100:
110                 pll_out = 45158400;
111                 break;
112         case 32000:
113         case 48000:
114         case 96000:
115                 pll_out = 49152000;
116                 break;
117         default:
118                 return -EINVAL;
119         }
120 
121         /* Setting ratio to 512fs helps to use S/PDIF with HDMI without
122          * modify S/PDIF ASoC machine driver.
123          */
124         ratio = 512;
125         rclk_rate = params_rate(params) * ratio;
126 
127         /* Set audio source clock rates */
128         ret = set_audio_clock_rate(pll_out, rclk_rate);
129         if (ret < 0)
130                 return ret;
131 
132         /* Set S/PDIF uses internal source clock */
133         ret = snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK,
134                                         rclk_rate, SND_SOC_CLOCK_IN);
135         if (ret < 0)
136                 return ret;
137 
138         return ret;
139 }
140 
141 static const struct snd_soc_ops smdk_spdif_ops = {
142         .hw_params = smdk_hw_params,
143 };
144 
145 SND_SOC_DAILINK_DEFS(spdif,
146         DAILINK_COMP_ARRAY(COMP_CPU("samsung-spdif")),
147         DAILINK_COMP_ARRAY(COMP_CODEC("spdif-dit", "dit-hifi")),
148         DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-spdif")));
149 
150 static struct snd_soc_dai_link smdk_dai = {
151         .name = "S/PDIF",
152         .stream_name = "S/PDIF PCM Playback",
153         .ops = &smdk_spdif_ops,
154         SND_SOC_DAILINK_REG(spdif),
155 };
156 
157 static struct snd_soc_card smdk = {
158         .name = "SMDK-S/PDIF",
159         .owner = THIS_MODULE,
160         .dai_link = &smdk_dai,
161         .num_links = 1,
162 };
163 
164 static struct platform_device *smdk_snd_spdif_dit_device;
165 static struct platform_device *smdk_snd_spdif_device;
166 
167 static int __init smdk_init(void)
168 {
169         int ret;
170 
171         smdk_snd_spdif_dit_device = platform_device_alloc("spdif-dit", -1);
172         if (!smdk_snd_spdif_dit_device)
173                 return -ENOMEM;
174 
175         ret = platform_device_add(smdk_snd_spdif_dit_device);
176         if (ret)
177                 goto err1;
178 
179         smdk_snd_spdif_device = platform_device_alloc("soc-audio", -1);
180         if (!smdk_snd_spdif_device) {
181                 ret = -ENOMEM;
182                 goto err2;
183         }
184 
185         platform_set_drvdata(smdk_snd_spdif_device, &smdk);
186 
187         ret = platform_device_add(smdk_snd_spdif_device);
188         if (ret)
189                 goto err3;
190 
191         /* Set audio clock hierarchy manually */
192         ret = set_audio_clock_heirachy(smdk_snd_spdif_device);
193         if (ret)
194                 goto err4;
195 
196         return 0;
197 err4:
198         platform_device_del(smdk_snd_spdif_device);
199 err3:
200         platform_device_put(smdk_snd_spdif_device);
201 err2:
202         platform_device_del(smdk_snd_spdif_dit_device);
203 err1:
204         platform_device_put(smdk_snd_spdif_dit_device);
205         return ret;
206 }
207 
208 static void __exit smdk_exit(void)
209 {
210         platform_device_unregister(smdk_snd_spdif_device);
211         platform_device_unregister(smdk_snd_spdif_dit_device);
212 }
213 
214 module_init(smdk_init);
215 module_exit(smdk_exit);
216 
217 MODULE_AUTHOR("Seungwhan Youn, <sw.youn@samsung.com>");
218 MODULE_DESCRIPTION("ALSA SoC SMDK+S/PDIF");
219 MODULE_LICENSE("GPL");
220 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php