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.
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>
23 #include "s3c24xx-i2s.h"
25 /* #define ENFORCE_RATES 1 */
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.
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 */
46 static DEFINE_MUTEX(clk_lock);
48 static unsigned int rates[33 * 2];
50 static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
51 .count = ARRAY_SIZE(rates),
57 static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
61 struct snd_pcm_runtime *runtime = substream->runtime;
64 mutex_lock(&clk_lock);
67 xtal = clk_get(rtd->dev, "xtal");
69 dev_err(rtd->dev, "%s cannot get xtal\n", __func__);
72 pclk = clk_get(&s3c24xx_uda134x_snd_device->dev,
75 dev_err(rtd->dev, "%s cannot get pclk\n",
84 for (i = 0; i < 2; i++) {
85 int fs = i ? 256 : 384;
87 rates[i*33] = clk_get_rate(xtal) / fs;
88 for (j = 1; j < 33; j++)
89 rates[i*33 + j] = clk_get_rate(pclk) /
95 mutex_unlock(&clk_lock);
98 ret = snd_pcm_hw_constraint_list(runtime, 0,
99 SNDRV_PCM_HW_PARAM_RATE,
100 &hw_constraints_rates);
102 dev_err(rtd->dev, "%s cannot set constraints\n",
109 static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
111 mutex_lock(&clk_lock);
113 if (clk_users == 0) {
119 mutex_unlock(&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;
130 int clk_source, fs_mode;
131 unsigned long rate = params_rate(params);
138 for (i = 0; i < 2*33; i++) {
139 cerr = rates[i] - rate;
148 fs_mode = S3C2410_IISMOD_256FS;
150 fs_mode = S3C2410_IISMOD_384FS;
152 clk_source = S3C24XX_CLKSRC_MPLL;
155 clk_source = S3C24XX_CLKSRC_PCLK;
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",
168 if ((err * 100 / rate) > 5) {
169 dev_err(rtd->dev, "effective frequency too different "
170 "from desired (%ld%%)\n", err * 100 / rate);
174 ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
179 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
183 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
184 S3C2410_IISMOD_32FS);
188 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
189 S3C24XX_PRESCALE(div, div));
193 /* set the codec system clock for DAC and ADC */
194 ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
202 static 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 = {
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,
227 static int s3c24xx_uda134x_probe(struct platform_device *pdev)
229 struct snd_soc_card *card = &snd_soc_s3c24xx_uda134x;
232 platform_set_drvdata(pdev, card);
233 card->dev = &pdev->dev;
235 ret = devm_snd_soc_register_card(&pdev->dev, card);
237 dev_err(&pdev->dev, "failed to register card: %d\n", ret);
242 static struct platform_driver s3c24xx_uda134x_driver = {
243 .probe = s3c24xx_uda134x_probe,
245 .name = "s3c24xx_uda134x",
248 module_platform_driver(s3c24xx_uda134x_driver);
250 MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
251 MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
252 MODULE_LICENSE("GPL");