Linux-libre 5.4.49-gnu
[librecmc/linux-libre.git] / sound / soc / samsung / s3c24xx_uda134x.c
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
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
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
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
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");