Merge remote-tracking branch 'selinux/next'
[deliverable/linux.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
23 #include "s3c24xx-i2s.h"
24
25 /* #define ENFORCE_RATES 1 */
26 /*
27 Unfortunately the S3C24XX in master mode has a limited capacity of
28 generating the clock for the codec. If you define this only rates
29 that are really available will be enforced. But be careful, most
30 user level application just want the usual sampling frequencies (8,
31 11.025, 22.050, 44.1 kHz) and anyway resampling is a costly
32 operation for embedded systems. So if you aren't very lucky or your
33 hardware engineer wasn't very forward-looking it's better to leave
34 this undefined. If you do so an approximate value for the requested
35 sampling rate in the range -/+ 5% will be chosen. If this in not
36 possible an error will be returned.
37 */
38
39 static struct clk *xtal;
40 static struct clk *pclk;
41 /* this is need because we don't have a place where to keep the
42 * pointers to the clocks in each substream. We get the clocks only
43 * when we are actually using them so we don't block stuff like
44 * frequency change or oscillator power-off */
45 static int clk_users;
46 static DEFINE_MUTEX(clk_lock);
47
48 static unsigned int rates[33 * 2];
49 #ifdef ENFORCE_RATES
50 static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
51 .count = ARRAY_SIZE(rates),
52 .list = rates,
53 .mask = 0,
54 };
55 #endif
56
57 static 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
110 static 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
123 static 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 /* set the codec system clock for DAC and ADC */
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
203 static 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
209 static 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
221 static 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
228 static 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
243 static struct platform_driver s3c24xx_uda134x_driver = {
244 .probe = s3c24xx_uda134x_probe,
245 .driver = {
246 .name = "s3c24xx_uda134x",
247 },
248 };
249 module_platform_driver(s3c24xx_uda134x_driver);
250
251 MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
252 MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
253 MODULE_LICENSE("GPL");
This page took 0.036909 seconds and 5 git commands to generate.