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