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