blob: 55d2a802a6cb6a3a20962592af3a70f3cb76dbf0 [file] [log] [blame]
Sylwester Nawrocki4d19f2c2019-04-19 12:21:53 +02001// 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
Christian Pellegrin7ad933d2008-11-15 08:58:32 +01009
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010010#include <linux/clk.h>
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010011#include <linux/gpio.h>
Paul Gortmakerda155d52011-07-15 12:38:28 -040012#include <linux/module.h>
Seungwhan Youn0378b6a2011-01-11 07:26:06 +090013
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010014#include <sound/soc.h>
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010015#include <sound/s3c24xx_uda134x.h>
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010016
Arnd Bergmann5d229ce52013-04-11 19:08:42 +020017#include "regs-iis.h"
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010018#include "s3c24xx-i2s.h"
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010019
Sylwester Nawrocki892ccf02016-10-25 12:57:57 +020020struct s3c24xx_uda134x {
21 struct clk *xtal;
22 struct clk *pclk;
23 struct mutex clk_lock;
24 int clk_users;
25};
26
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010027/* #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
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010041static unsigned int rates[33 * 2];
42#ifdef ENFORCE_RATES
Takashi Iwai0994c032017-06-08 23:37:25 +020043static const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010044 .count = ARRAY_SIZE(rates),
45 .list = rates,
46 .mask = 0,
47};
48#endif
49
Mark Brownd0c36632008-11-18 21:57:17 +000050static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010051{
Sylwester Nawrocki1bc610e2016-08-04 11:51:25 +020052 struct snd_soc_pcm_runtime *rtd = substream->private_data;
Sylwester Nawrocki892ccf02016-10-25 12:57:57 +020053 struct s3c24xx_uda134x *priv = snd_soc_card_get_drvdata(rtd->card);
Sylwester Nawrocki1bc610e2016-08-04 11:51:25 +020054 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
Sylwester Nawrocki1bc610e2016-08-04 11:51:25 +020055 int ret = 0;
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010056
Sylwester Nawrocki892ccf02016-10-25 12:57:57 +020057 mutex_lock(&priv->clk_lock);
Sylwester Nawrocki45ef4962016-08-05 11:47:09 +020058
Sylwester Nawrocki892ccf02016-10-25 12:57:57 +020059 if (priv->clk_users == 0) {
60 priv->xtal = clk_get(rtd->dev, "xtal");
61 if (IS_ERR(priv->xtal)) {
Sylwester Nawrocki45ef4962016-08-05 11:47:09 +020062 dev_err(rtd->dev, "%s cannot get xtal\n", __func__);
Sylwester Nawrocki892ccf02016-10-25 12:57:57 +020063 ret = PTR_ERR(priv->xtal);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010064 } else {
Sylwester Nawrocki892ccf02016-10-25 12:57:57 +020065 priv->pclk = clk_get(cpu_dai->dev, "iis");
66 if (IS_ERR(priv->pclk)) {
Sylwester Nawrocki45ef4962016-08-05 11:47:09 +020067 dev_err(rtd->dev, "%s cannot get pclk\n",
68 __func__);
Sylwester Nawrocki892ccf02016-10-25 12:57:57 +020069 clk_put(priv->xtal);
70 ret = PTR_ERR(priv->pclk);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010071 }
72 }
73 if (!ret) {
74 int i, j;
75
76 for (i = 0; i < 2; i++) {
77 int fs = i ? 256 : 384;
78
Sylwester Nawrocki892ccf02016-10-25 12:57:57 +020079 rates[i*33] = clk_get_rate(priv->xtal) / fs;
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010080 for (j = 1; j < 33; j++)
Sylwester Nawrocki892ccf02016-10-25 12:57:57 +020081 rates[i*33 + j] = clk_get_rate(priv->pclk) /
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010082 (j * fs);
83 }
84 }
85 }
Sylwester Nawrocki892ccf02016-10-25 12:57:57 +020086 priv->clk_users += 1;
87 mutex_unlock(&priv->clk_lock);
88
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010089 if (!ret) {
90#ifdef ENFORCE_RATES
Sylwester Nawrocki892ccf02016-10-25 12:57:57 +020091 ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010092 SNDRV_PCM_HW_PARAM_RATE,
93 &hw_constraints_rates);
94 if (ret < 0)
Sylwester Nawrocki45ef4962016-08-05 11:47:09 +020095 dev_err(rtd->dev, "%s cannot set constraints\n",
96 __func__);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010097#endif
98 }
99 return ret;
100}
101
Mark Brownd0c36632008-11-18 21:57:17 +0000102static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100103{
Sylwester Nawrocki892ccf02016-10-25 12:57:57 +0200104 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;
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100114 }
Sylwester Nawrocki892ccf02016-10-25 12:57:57 +0200115 mutex_unlock(&priv->clk_lock);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100116}
117
118static 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;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000122 struct snd_soc_dai *codec_dai = rtd->codec_dai;
123 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100124 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 }
Sylwester Nawrocki45ef4962016-08-05 11:47:09 +0200154
155 dev_dbg(rtd->dev, "%s desired rate %lu, %d\n", __func__, rate, bi);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100156
157 clk = (fs_mode == S3C2410_IISMOD_384FS ? 384 : 256) * rate;
Sylwester Nawrocki45ef4962016-08-05 11:47:09 +0200158
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);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100163
164 if ((err * 100 / rate) > 5) {
Sylwester Nawrocki45ef4962016-08-05 11:47:09 +0200165 dev_err(rtd->dev, "effective frequency too different "
166 "from desired (%ld%%)\n", err * 100 / rate);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100167 return -EINVAL;
168 }
169
Mark Brownd0c36632008-11-18 21:57:17 +0000170 ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
171 SND_SOC_CLOCK_IN);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100172 if (ret < 0)
173 return ret;
174
Mark Brownd0c36632008-11-18 21:57:17 +0000175 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100176 if (ret < 0)
177 return ret;
178
Mark Brownd0c36632008-11-18 21:57:17 +0000179 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
180 S3C2410_IISMOD_32FS);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100181 if (ret < 0)
182 return ret;
183
Mark Brownd0c36632008-11-18 21:57:17 +0000184 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
185 S3C24XX_PRESCALE(div, div));
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100186 if (ret < 0)
187 return ret;
188
189 /* set the codec system clock for DAC and ADC */
Mark Brownd0c36632008-11-18 21:57:17 +0000190 ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
191 SND_SOC_CLOCK_OUT);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100192 if (ret < 0)
193 return ret;
194
195 return 0;
196}
197
Bhumika Goyal2af23632017-08-16 22:29:29 +0530198static const struct snd_soc_ops s3c24xx_uda134x_ops = {
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100199 .startup = s3c24xx_uda134x_startup,
200 .shutdown = s3c24xx_uda134x_shutdown,
201 .hw_params = s3c24xx_uda134x_hw_params,
202};
203
Kuninori Morimotobb5e4a02019-06-06 13:10:06 +0900204SND_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
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100209static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
210 .name = "UDA134X",
211 .stream_name = "UDA134X",
Lars-Peter Clausen517b9a22015-01-01 17:16:25 +0100212 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
213 SND_SOC_DAIFMT_CBS_CFS,
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100214 .ops = &s3c24xx_uda134x_ops,
Kuninori Morimotobb5e4a02019-06-06 13:10:06 +0900215 SND_SOC_DAILINK_REG(uda134x),
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100216};
217
Mark Brown87506542008-11-18 20:50:34 +0000218static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100219 .name = "S3C24XX_UDA134X",
Axel Lin095d79d2011-12-22 10:53:15 +0800220 .owner = THIS_MODULE,
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100221 .dai_link = &s3c24xx_uda134x_dai_link,
222 .num_links = 1,
223};
224
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100225static int s3c24xx_uda134x_probe(struct platform_device *pdev)
226{
Sylwester Nawrocki28405212016-08-04 15:38:46 +0200227 struct snd_soc_card *card = &snd_soc_s3c24xx_uda134x;
Sylwester Nawrocki892ccf02016-10-25 12:57:57 +0200228 struct s3c24xx_uda134x *priv;
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100229 int ret;
230
Sylwester Nawrocki892ccf02016-10-25 12:57:57 +0200231 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
232 if (!priv)
233 return -ENOMEM;
234
235 mutex_init(&priv->clk_lock);
236
Sylwester Nawrocki28405212016-08-04 15:38:46 +0200237 card->dev = &pdev->dev;
Sylwester Nawrocki892ccf02016-10-25 12:57:57 +0200238 snd_soc_card_set_drvdata(card, priv);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100239
Sylwester Nawrocki28405212016-08-04 15:38:46 +0200240 ret = devm_snd_soc_register_card(&pdev->dev, card);
241 if (ret)
242 dev_err(&pdev->dev, "failed to register card: %d\n", ret);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100243
244 return ret;
245}
246
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100247static struct platform_driver s3c24xx_uda134x_driver = {
248 .probe = s3c24xx_uda134x_probe,
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100249 .driver = {
250 .name = "s3c24xx_uda134x",
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100251 },
252};
Mark Browne00c3f52011-11-23 15:20:13 +0000253module_platform_driver(s3c24xx_uda134x_driver);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100254
255MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
256MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
257MODULE_LICENSE("GPL");