Merge remote-tracking branch 'keys/keys-next'
[deliverable/linux.git] / sound / soc / samsung / s3c24xx_uda134x.c
CommitLineData
7ad933d7
CP
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
7ad933d7 14#include <linux/clk.h>
7ad933d7 15#include <linux/gpio.h>
da155d5b 16#include <linux/module.h>
0378b6ac 17
7ad933d7 18#include <sound/soc.h>
7ad933d7 19#include <sound/s3c24xx_uda134x.h>
7ad933d7 20
5d229ce5 21#include "regs-iis.h"
7ad933d7 22
7ad933d7 23#include "s3c24xx-i2s.h"
7ad933d7
CP
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
39static struct clk *xtal;
40static 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 */
45static int clk_users;
46static DEFINE_MUTEX(clk_lock);
47
48static unsigned int rates[33 * 2];
49#ifdef ENFORCE_RATES
50static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
51 .count = ARRAY_SIZE(rates),
52 .list = rates,
53 .mask = 0,
54};
55#endif
56
d0c36631 57static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
7ad933d7 58{
1bc610e7
SN
59 struct snd_soc_pcm_runtime *rtd = substream->private_data;
60 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
7ad933d7 61#ifdef ENFORCE_RATES
a419aef8 62 struct snd_pcm_runtime *runtime = substream->runtime;
7ad933d7 63#endif
1bc610e7 64 int ret = 0;
7ad933d7
CP
65
66 mutex_lock(&clk_lock);
45ef4969 67
7ad933d7 68 if (clk_users == 0) {
28405212 69 xtal = clk_get(rtd->dev, "xtal");
7803e329 70 if (IS_ERR(xtal)) {
45ef4969 71 dev_err(rtd->dev, "%s cannot get xtal\n", __func__);
7803e329 72 ret = PTR_ERR(xtal);
7ad933d7 73 } else {
1bc610e7 74 pclk = clk_get(cpu_dai->dev, "iis");
7803e329 75 if (IS_ERR(pclk)) {
45ef4969
SN
76 dev_err(rtd->dev, "%s cannot get pclk\n",
77 __func__);
7ad933d7 78 clk_put(xtal);
7803e329 79 ret = PTR_ERR(pclk);
7ad933d7
CP
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)
45ef4969
SN
103 dev_err(rtd->dev, "%s cannot set constraints\n",
104 __func__);
7ad933d7
CP
105#endif
106 }
107 return ret;
108}
109
d0c36631 110static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
7ad933d7
CP
111{
112 mutex_lock(&clk_lock);
7ad933d7
CP
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
123static 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;
f0fba2ad
LG
127 struct snd_soc_dai *codec_dai = rtd->codec_dai;
128 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
7ad933d7
CP
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 }
45ef4969
SN
159
160 dev_dbg(rtd->dev, "%s desired rate %lu, %d\n", __func__, rate, bi);
7ad933d7
CP
161
162 clk = (fs_mode == S3C2410_IISMOD_384FS ? 384 : 256) * rate;
45ef4969
SN
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);
7ad933d7
CP
168
169 if ((err * 100 / rate) > 5) {
45ef4969
SN
170 dev_err(rtd->dev, "effective frequency too different "
171 "from desired (%ld%%)\n", err * 100 / rate);
7ad933d7
CP
172 return -EINVAL;
173 }
174
d0c36631
MB
175 ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
176 SND_SOC_CLOCK_IN);
7ad933d7
CP
177 if (ret < 0)
178 return ret;
179
d0c36631 180 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
7ad933d7
CP
181 if (ret < 0)
182 return ret;
183
d0c36631
MB
184 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
185 S3C2410_IISMOD_32FS);
7ad933d7
CP
186 if (ret < 0)
187 return ret;
188
d0c36631
MB
189 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
190 S3C24XX_PRESCALE(div, div));
7ad933d7
CP
191 if (ret < 0)
192 return ret;
193
194 /* set the codec system clock for DAC and ADC */
d0c36631
MB
195 ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
196 SND_SOC_CLOCK_OUT);
7ad933d7
CP
197 if (ret < 0)
198 return ret;
199
200 return 0;
201}
202
203static 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
209static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
210 .name = "UDA134X",
211 .stream_name = "UDA134X",
a110f4ef 212 .codec_name = "uda134x-codec",
f0fba2ad 213 .codec_dai_name = "uda134x-hifi",
518aa59f 214 .cpu_dai_name = "s3c24xx-iis",
517b9a2a
LPC
215 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
216 SND_SOC_DAIFMT_CBS_CFS,
7ad933d7 217 .ops = &s3c24xx_uda134x_ops,
a08485d8 218 .platform_name = "s3c24xx-iis",
7ad933d7
CP
219};
220
87506549 221static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
7ad933d7 222 .name = "S3C24XX_UDA134X",
095d79dc 223 .owner = THIS_MODULE,
7ad933d7
CP
224 .dai_link = &s3c24xx_uda134x_dai_link,
225 .num_links = 1,
226};
227
7ad933d7
CP
228static int s3c24xx_uda134x_probe(struct platform_device *pdev)
229{
28405212 230 struct snd_soc_card *card = &snd_soc_s3c24xx_uda134x;
7ad933d7
CP
231 int ret;
232
28405212
SN
233 platform_set_drvdata(pdev, card);
234 card->dev = &pdev->dev;
7ad933d7 235
28405212
SN
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);
7ad933d7
CP
239
240 return ret;
241}
242
7ad933d7
CP
243static struct platform_driver s3c24xx_uda134x_driver = {
244 .probe = s3c24xx_uda134x_probe,
7ad933d7
CP
245 .driver = {
246 .name = "s3c24xx_uda134x",
7ad933d7
CP
247 },
248};
e00c3f55 249module_platform_driver(s3c24xx_uda134x_driver);
7ad933d7
CP
250
251MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
252MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
253MODULE_LICENSE("GPL");
This page took 0.347328 seconds and 5 git commands to generate.