1 // SPDX-License-Identifier: GPL-2.0
2 //
3 // Modifications by Christian Pellegrin <chripell@evolware.org>
4 //
5 // s3c24xx_uda134x.c - S3C24XX_UDA134X ALSA SoC Audio board driver
6 //
7 // Copyright 2007 Dension Audio Systems Ltd.
8 // Author: Zoltan Devai
9
10 #include <linux/clk.h>
11 #include <linux/gpio.h>
12 #include <linux/module.h>
13
14 #include <sound/soc.h>
15 #include <sound/s3c24xx_uda134x.h>
16
17 #include "regs-iis.h"
18 #include "s3c24xx-i2s.h"
19
20 struct s3c24xx_uda134x {
21 struct clk *xtal;
22 struct clk *pclk;
23 struct mutex clk_lock;
24 int clk_users;
25 };
26
27 /* #define ENFORCE_RATES 1 */
28 /*
29 Unfortunately the S3C24XX in master mode has a limited capacity of
30 generating the clock for the codec. If you define this only rates
31 that are really available will be enforced. But be careful, most
32 user level application just want the usual sampling frequencies (8,
33 11.025, 22.050, 44.1 kHz) and anyway resampling is a costly
34 operation for embedded systems. So if you aren't very lucky or your
35 hardware engineer wasn't very forward-looking it's better to leave
36 this undefined. If you do so an approximate value for the requested
37 sampling rate in the range -/+ 5% will be chosen. If this in not
38 possible an error will be returned.
39 */
40
41 static unsigned int rates[33 * 2];
42 #ifdef ENFORCE_RATES
43 static const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
44 .count = ARRAY_SIZE(rates),
45 .list = rates,
46 .mask = 0,
47 };
48 #endif
49
s3c24xx_uda134x_startup(struct snd_pcm_substream * substream)50 static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
51 {
52 struct snd_soc_pcm_runtime *rtd = substream->private_data;
53 struct s3c24xx_uda134x *priv = snd_soc_card_get_drvdata(rtd->card);
54 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
55 int ret = 0;
56
57 mutex_lock(&priv->clk_lock);
58
59 if (priv->clk_users == 0) {
60 priv->xtal = clk_get(rtd->dev, "xtal");
61 if (IS_ERR(priv->xtal)) {
62 dev_err(rtd->dev, "%s cannot get xtal\n", __func__);
63 ret = PTR_ERR(priv->xtal);
64 } else {
65 priv->pclk = clk_get(cpu_dai->dev, "iis");
66 if (IS_ERR(priv->pclk)) {
67 dev_err(rtd->dev, "%s cannot get pclk\n",
68 __func__);
69 clk_put(priv->xtal);
70 ret = PTR_ERR(priv->pclk);
71 }
72 }
73 if (!ret) {
74 int i, j;
75
76 for (i = 0; i < 2; i++) {
77 int fs = i ? 256 : 384;
78
79 rates[i*33] = clk_get_rate(priv->xtal) / fs;
80 for (j = 1; j < 33; j++)
81 rates[i*33 + j] = clk_get_rate(priv->pclk) /
82 (j * fs);
83 }
84 }
85 }
86 priv->clk_users += 1;
87 mutex_unlock(&priv->clk_lock);
88
89 if (!ret) {
90 #ifdef ENFORCE_RATES
91 ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
92 SNDRV_PCM_HW_PARAM_RATE,
93 &hw_constraints_rates);
94 if (ret < 0)
95 dev_err(rtd->dev, "%s cannot set constraints\n",
96 __func__);
97 #endif
98 }
99 return ret;
100 }
101
s3c24xx_uda134x_shutdown(struct snd_pcm_substream * substream)102 static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
103 {
104 struct snd_soc_pcm_runtime *rtd = substream->private_data;
105 struct s3c24xx_uda134x *priv = snd_soc_card_get_drvdata(rtd->card);
106
107 mutex_lock(&priv->clk_lock);
108 priv->clk_users -= 1;
109 if (priv->clk_users == 0) {
110 clk_put(priv->xtal);
111 priv->xtal = NULL;
112 clk_put(priv->pclk);
113 priv->pclk = NULL;
114 }
115 mutex_unlock(&priv->clk_lock);
116 }
117
s3c24xx_uda134x_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)118 static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream,
119 struct snd_pcm_hw_params *params)
120 {
121 struct snd_soc_pcm_runtime *rtd = substream->private_data;
122 struct snd_soc_dai *codec_dai = rtd->codec_dai;
123 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
124 unsigned int clk = 0;
125 int ret = 0;
126 int clk_source, fs_mode;
127 unsigned long rate = params_rate(params);
128 long err, cerr;
129 unsigned int div;
130 int i, bi;
131
132 err = 999999;
133 bi = 0;
134 for (i = 0; i < 2*33; i++) {
135 cerr = rates[i] - rate;
136 if (cerr < 0)
137 cerr = -cerr;
138 if (cerr < err) {
139 err = cerr;
140 bi = i;
141 }
142 }
143 if (bi / 33 == 1)
144 fs_mode = S3C2410_IISMOD_256FS;
145 else
146 fs_mode = S3C2410_IISMOD_384FS;
147 if (bi % 33 == 0) {
148 clk_source = S3C24XX_CLKSRC_MPLL;
149 div = 1;
150 } else {
151 clk_source = S3C24XX_CLKSRC_PCLK;
152 div = bi % 33;
153 }
154
155 dev_dbg(rtd->dev, "%s desired rate %lu, %d\n", __func__, rate, bi);
156
157 clk = (fs_mode == S3C2410_IISMOD_384FS ? 384 : 256) * rate;
158
159 dev_dbg(rtd->dev, "%s will use: %s %s %d sysclk %d err %ld\n", __func__,
160 fs_mode == S3C2410_IISMOD_384FS ? "384FS" : "256FS",
161 clk_source == S3C24XX_CLKSRC_MPLL ? "MPLLin" : "PCLK",
162 div, clk, err);
163
164 if ((err * 100 / rate) > 5) {
165 dev_err(rtd->dev, "effective frequency too different "
166 "from desired (%ld%%)\n", err * 100 / rate);
167 return -EINVAL;
168 }
169
170 ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
171 SND_SOC_CLOCK_IN);
172 if (ret < 0)
173 return ret;
174
175 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
176 if (ret < 0)
177 return ret;
178
179 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
180 S3C2410_IISMOD_32FS);
181 if (ret < 0)
182 return ret;
183
184 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
185 S3C24XX_PRESCALE(div, div));
186 if (ret < 0)
187 return ret;
188
189 /* set the codec system clock for DAC and ADC */
190 ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
191 SND_SOC_CLOCK_OUT);
192 if (ret < 0)
193 return ret;
194
195 return 0;
196 }
197
198 static const struct snd_soc_ops s3c24xx_uda134x_ops = {
199 .startup = s3c24xx_uda134x_startup,
200 .shutdown = s3c24xx_uda134x_shutdown,
201 .hw_params = s3c24xx_uda134x_hw_params,
202 };
203
204 SND_SOC_DAILINK_DEFS(uda134x,
205 DAILINK_COMP_ARRAY(COMP_CPU("s3c24xx-iis")),
206 DAILINK_COMP_ARRAY(COMP_CODEC("uda134x-codec", "uda134x-hifi")),
207 DAILINK_COMP_ARRAY(COMP_PLATFORM("s3c24xx-iis")));
208
209 static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
210 .name = "UDA134X",
211 .stream_name = "UDA134X",
212 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
213 SND_SOC_DAIFMT_CBS_CFS,
214 .ops = &s3c24xx_uda134x_ops,
215 SND_SOC_DAILINK_REG(uda134x),
216 };
217
218 static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
219 .name = "S3C24XX_UDA134X",
220 .owner = THIS_MODULE,
221 .dai_link = &s3c24xx_uda134x_dai_link,
222 .num_links = 1,
223 };
224
s3c24xx_uda134x_probe(struct platform_device * pdev)225 static int s3c24xx_uda134x_probe(struct platform_device *pdev)
226 {
227 struct snd_soc_card *card = &snd_soc_s3c24xx_uda134x;
228 struct s3c24xx_uda134x *priv;
229 int ret;
230
231 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
232 if (!priv)
233 return -ENOMEM;
234
235 mutex_init(&priv->clk_lock);
236
237 card->dev = &pdev->dev;
238 snd_soc_card_set_drvdata(card, priv);
239
240 ret = devm_snd_soc_register_card(&pdev->dev, card);
241 if (ret)
242 dev_err(&pdev->dev, "failed to register card: %d\n", ret);
243
244 return ret;
245 }
246
247 static struct platform_driver s3c24xx_uda134x_driver = {
248 .probe = s3c24xx_uda134x_probe,
249 .driver = {
250 .name = "s3c24xx_uda134x",
251 },
252 };
253 module_platform_driver(s3c24xx_uda134x_driver);
254
255 MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
256 MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
257 MODULE_LICENSE("GPL");
258