1
2
3
4
5
6
7
8
9
10
11
12
13
14#include <linux/clk.h>
15#include <linux/gpio.h>
16#include <linux/module.h>
17
18#include <sound/soc.h>
19#include <sound/s3c24xx_uda134x.h>
20
21#include "regs-iis.h"
22
23#include "s3c24xx-i2s.h"
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39static struct clk *xtal;
40static struct clk *pclk;
41
42
43
44
45static int clk_users;
46static DEFINE_MUTEX(clk_lock);
47
48static unsigned int rates[33 * 2];
49#ifdef ENFORCE_RATES
50static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
51 .count = ARRAY_SIZE(rates),
52 .list = rates,
53 .mask = 0,
54};
55#endif
56
57static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
58{
59 struct snd_soc_pcm_runtime *rtd = substream->private_data;
60 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
61#ifdef ENFORCE_RATES
62 struct snd_pcm_runtime *runtime = substream->runtime;
63#endif
64 int ret = 0;
65
66 mutex_lock(&clk_lock);
67
68 if (clk_users == 0) {
69 xtal = clk_get(rtd->dev, "xtal");
70 if (IS_ERR(xtal)) {
71 dev_err(rtd->dev, "%s cannot get xtal\n", __func__);
72 ret = PTR_ERR(xtal);
73 } else {
74 pclk = clk_get(cpu_dai->dev, "iis");
75 if (IS_ERR(pclk)) {
76 dev_err(rtd->dev, "%s cannot get pclk\n",
77 __func__);
78 clk_put(xtal);
79 ret = PTR_ERR(pclk);
80 }
81 }
82 if (!ret) {
83 int i, j;
84
85 for (i = 0; i < 2; i++) {
86 int fs = i ? 256 : 384;
87
88 rates[i*33] = clk_get_rate(xtal) / fs;
89 for (j = 1; j < 33; j++)
90 rates[i*33 + j] = clk_get_rate(pclk) /
91 (j * fs);
92 }
93 }
94 }
95 clk_users += 1;
96 mutex_unlock(&clk_lock);
97 if (!ret) {
98#ifdef ENFORCE_RATES
99 ret = snd_pcm_hw_constraint_list(runtime, 0,
100 SNDRV_PCM_HW_PARAM_RATE,
101 &hw_constraints_rates);
102 if (ret < 0)
103 dev_err(rtd->dev, "%s cannot set constraints\n",
104 __func__);
105#endif
106 }
107 return ret;
108}
109
110static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
111{
112 mutex_lock(&clk_lock);
113 clk_users -= 1;
114 if (clk_users == 0) {
115 clk_put(xtal);
116 xtal = NULL;
117 clk_put(pclk);
118 pclk = NULL;
119 }
120 mutex_unlock(&clk_lock);
121}
122
123static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream,
124 struct snd_pcm_hw_params *params)
125{
126 struct snd_soc_pcm_runtime *rtd = substream->private_data;
127 struct snd_soc_dai *codec_dai = rtd->codec_dai;
128 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
129 unsigned int clk = 0;
130 int ret = 0;
131 int clk_source, fs_mode;
132 unsigned long rate = params_rate(params);
133 long err, cerr;
134 unsigned int div;
135 int i, bi;
136
137 err = 999999;
138 bi = 0;
139 for (i = 0; i < 2*33; i++) {
140 cerr = rates[i] - rate;
141 if (cerr < 0)
142 cerr = -cerr;
143 if (cerr < err) {
144 err = cerr;
145 bi = i;
146 }
147 }
148 if (bi / 33 == 1)
149 fs_mode = S3C2410_IISMOD_256FS;
150 else
151 fs_mode = S3C2410_IISMOD_384FS;
152 if (bi % 33 == 0) {
153 clk_source = S3C24XX_CLKSRC_MPLL;
154 div = 1;
155 } else {
156 clk_source = S3C24XX_CLKSRC_PCLK;
157 div = bi % 33;
158 }
159
160 dev_dbg(rtd->dev, "%s desired rate %lu, %d\n", __func__, rate, bi);
161
162 clk = (fs_mode == S3C2410_IISMOD_384FS ? 384 : 256) * rate;
163
164 dev_dbg(rtd->dev, "%s will use: %s %s %d sysclk %d err %ld\n", __func__,
165 fs_mode == S3C2410_IISMOD_384FS ? "384FS" : "256FS",
166 clk_source == S3C24XX_CLKSRC_MPLL ? "MPLLin" : "PCLK",
167 div, clk, err);
168
169 if ((err * 100 / rate) > 5) {
170 dev_err(rtd->dev, "effective frequency too different "
171 "from desired (%ld%%)\n", err * 100 / rate);
172 return -EINVAL;
173 }
174
175 ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
176 SND_SOC_CLOCK_IN);
177 if (ret < 0)
178 return ret;
179
180 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
181 if (ret < 0)
182 return ret;
183
184 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
185 S3C2410_IISMOD_32FS);
186 if (ret < 0)
187 return ret;
188
189 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
190 S3C24XX_PRESCALE(div, div));
191 if (ret < 0)
192 return ret;
193
194
195 ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
196 SND_SOC_CLOCK_OUT);
197 if (ret < 0)
198 return ret;
199
200 return 0;
201}
202
203static struct snd_soc_ops s3c24xx_uda134x_ops = {
204 .startup = s3c24xx_uda134x_startup,
205 .shutdown = s3c24xx_uda134x_shutdown,
206 .hw_params = s3c24xx_uda134x_hw_params,
207};
208
209static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
210 .name = "UDA134X",
211 .stream_name = "UDA134X",
212 .codec_name = "uda134x-codec",
213 .codec_dai_name = "uda134x-hifi",
214 .cpu_dai_name = "s3c24xx-iis",
215 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
216 SND_SOC_DAIFMT_CBS_CFS,
217 .ops = &s3c24xx_uda134x_ops,
218 .platform_name = "s3c24xx-iis",
219};
220
221static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
222 .name = "S3C24XX_UDA134X",
223 .owner = THIS_MODULE,
224 .dai_link = &s3c24xx_uda134x_dai_link,
225 .num_links = 1,
226};
227
228static int s3c24xx_uda134x_probe(struct platform_device *pdev)
229{
230 struct snd_soc_card *card = &snd_soc_s3c24xx_uda134x;
231 int ret;
232
233 platform_set_drvdata(pdev, card);
234 card->dev = &pdev->dev;
235
236 ret = devm_snd_soc_register_card(&pdev->dev, card);
237 if (ret)
238 dev_err(&pdev->dev, "failed to register card: %d\n", ret);
239
240 return ret;
241}
242
243static struct platform_driver s3c24xx_uda134x_driver = {
244 .probe = s3c24xx_uda134x_probe,
245 .driver = {
246 .name = "s3c24xx_uda134x",
247 },
248};
249module_platform_driver(s3c24xx_uda134x_driver);
250
251MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
252MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
253MODULE_LICENSE("GPL");
254