Linux 4.16.11
[linux/fpc-iii.git] / sound / soc / samsung / s3c24xx_uda134x.c
blob5fb3bab6bbfefea6860a1fe19c4cff55d31a2f7b
1 /*
2 * Modifications by Christian Pellegrin <chripell@evolware.org>
4 * s3c24xx_uda134x.c -- S3C24XX_UDA134X ALSA SoC Audio board driver
6 * Copyright 2007 Dension Audio Systems Ltd.
7 * Author: Zoltan Devai
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.
14 #include <linux/clk.h>
15 #include <linux/gpio.h>
16 #include <linux/module.h>
18 #include <sound/soc.h>
19 #include <sound/s3c24xx_uda134x.h>
21 #include "regs-iis.h"
22 #include "s3c24xx-i2s.h"
24 struct s3c24xx_uda134x {
25 struct clk *xtal;
26 struct clk *pclk;
27 struct mutex clk_lock;
28 int clk_users;
31 /* #define ENFORCE_RATES 1 */
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.
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,
52 #endif
54 static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
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;
61 mutex_lock(&priv->clk_lock);
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);
77 if (!ret) {
78 int i, j;
80 for (i = 0; i < 2; i++) {
81 int fs = i ? 256 : 384;
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);
90 priv->clk_users += 1;
91 mutex_unlock(&priv->clk_lock);
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
103 return ret;
106 static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
108 struct snd_soc_pcm_runtime *rtd = substream->private_data;
109 struct s3c24xx_uda134x *priv = snd_soc_card_get_drvdata(rtd->card);
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;
119 mutex_unlock(&priv->clk_lock);
122 static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream,
123 struct snd_pcm_hw_params *params)
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;
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;
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;
159 dev_dbg(rtd->dev, "%s desired rate %lu, %d\n", __func__, rate, bi);
161 clk = (fs_mode == S3C2410_IISMOD_384FS ? 384 : 256) * rate;
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);
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;
174 ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
175 SND_SOC_CLOCK_IN);
176 if (ret < 0)
177 return ret;
179 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
180 if (ret < 0)
181 return ret;
183 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
184 S3C2410_IISMOD_32FS);
185 if (ret < 0)
186 return ret;
188 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
189 S3C24XX_PRESCALE(div, div));
190 if (ret < 0)
191 return ret;
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;
199 return 0;
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,
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",
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,
227 static int s3c24xx_uda134x_probe(struct platform_device *pdev)
229 struct snd_soc_card *card = &snd_soc_s3c24xx_uda134x;
230 struct s3c24xx_uda134x *priv;
231 int ret;
233 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
234 if (!priv)
235 return -ENOMEM;
237 mutex_init(&priv->clk_lock);
239 card->dev = &pdev->dev;
240 snd_soc_card_set_drvdata(card, priv);
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);
246 return ret;
249 static struct platform_driver s3c24xx_uda134x_driver = {
250 .probe = s3c24xx_uda134x_probe,
251 .driver = {
252 .name = "s3c24xx_uda134x",
255 module_platform_driver(s3c24xx_uda134x_driver);
257 MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
258 MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
259 MODULE_LICENSE("GPL");