2 * rk2928_codec.c ALSA SoC RK2928 codec driver
4 * Copyright 2012 Rockchip
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * version 2 as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22 #include <linux/module.h>
23 #include <linux/moduleparam.h>
24 #include <linux/init.h>
25 #include <linux/delay.h>
27 #include <linux/gpio.h>
28 #include <linux/platform_device.h>
29 #include <linux/slab.h>
33 #include <sound/core.h>
34 #include <sound/pcm.h>
35 #include <sound/pcm_params.h>
36 #include <sound/soc.h>
37 #include <sound/initval.h>
38 #include <sound/tlv.h>
40 #include "rk2928_codec.h"
42 static struct rk2928_codec_data {
51 static const struct snd_soc_dapm_widget rk2928_dapm_widgets[] = {
52 SND_SOC_DAPM_DAC("DACL", "HIFI Playback", CODEC_REG_POWER, 5, 1),
53 SND_SOC_DAPM_DAC("DACR", "HIFI Playback", CODEC_REG_POWER, 4, 1),
54 SND_SOC_DAPM_PGA("DACL Amp", CODEC_REG_DAC_GAIN, 2, 0, NULL, 0),
55 SND_SOC_DAPM_PGA("DACR Amp", CODEC_REG_DAC_GAIN, 0, 0, NULL, 0),
56 SND_SOC_DAPM_OUT_DRV("DACL Drv", CODEC_REG_DAC_MUTE, 1, 1, NULL, 0),
57 SND_SOC_DAPM_OUT_DRV("DACR Drv", CODEC_REG_DAC_MUTE, 0, 1, NULL, 0),
58 SND_SOC_DAPM_OUTPUT("SPKL"),
59 SND_SOC_DAPM_OUTPUT("SPKR"),
60 SND_SOC_DAPM_ADC("ADCL", "HIFI Capture", CODEC_REG_POWER, 3, 1),
61 SND_SOC_DAPM_ADC("ADCR", "HIFI Capture", CODEC_REG_POWER, 2, 1),
62 SND_SOC_DAPM_INPUT("MICL"),
63 SND_SOC_DAPM_INPUT("MICR"),
66 static const struct snd_soc_dapm_route rk2928_audio_map[] = {
67 {"DACL Drv", "DACL Amp", "DACL"},
68 {"DACR Drv", "DACR Amp", "DACR"},
69 {"SPKL", NULL, "DACL Drv"},
70 {"SPKR", NULL, "DACR Drv"},
71 {"ADCL", NULL, "MICL"},
72 {"ADCR", NULL, "MICR"},
75 void codec_set_spk(bool on)
80 static unsigned int rk2928_read(struct snd_soc_codec *codec, unsigned int reg)
82 return readl(rk2928_data.regbase + reg);
85 static int rk2928_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value)
87 DBG("%s reg 0x%02x value 0x%02x", __FUNCTION__, reg, value);
88 writel(value, rk2928_data.regbase + reg);
92 static int rk2928_write_mask(struct snd_soc_codec *codec, unsigned int reg,
93 unsigned int mask, unsigned int value)
95 unsigned int regvalue = rk2928_read(codec, reg);
97 DBG("%s reg 0x%02x mask 0x%02x value 0x%02x", __FUNCTION__, reg, mask, value);
100 regvalue |= mask & value;
101 return rk2928_write(codec, reg, regvalue);
104 static int rk2928_audio_hw_params(struct snd_pcm_substream *substream,
105 struct snd_pcm_hw_params *params,
106 struct snd_soc_dai *dai)
108 // struct snd_soc_pcm_runtime *rtd = substream->private_data;
109 // struct snd_soc_codec *codec = rtd->codec;
110 // struct rk2928_codec_data *priv = snd_soc_codec_get_drvdata(codec);
112 DBG("%s", __FUNCTION__);
117 static int rk2928_audio_trigger(struct snd_pcm_substream *substream, int cmd,
118 struct snd_soc_dai *dai)
120 // struct snd_soc_pcm_runtime *rtd = substream->private_data;
121 // struct snd_soc_codec *codec = rtd->codec;
122 // struct rk2928_codec_data *priv = snd_soc_codec_get_drvdata(codec);
125 DBG("%s cmd 0x%x", __FUNCTION__, cmd);
128 case SNDRV_PCM_TRIGGER_START:
129 case SNDRV_PCM_TRIGGER_RESUME:
130 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
132 case SNDRV_PCM_TRIGGER_STOP:
133 case SNDRV_PCM_TRIGGER_SUSPEND:
134 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
142 static int rk2928_audio_startup(struct snd_pcm_substream *substream,
143 struct snd_soc_dai *dai)
145 // struct snd_soc_pcm_runtime *rtd = substream->private_data;
146 // struct snd_soc_codec *codec = rtd->codec;
147 DBG("%s", __FUNCTION__);
151 static int rk2928_set_bias_level(struct snd_soc_codec *codec,
152 enum snd_soc_bias_level level)
154 DBG("%s level %d", __FUNCTION__, level);
161 case SND_SOC_BIAS_ON:
163 case SND_SOC_BIAS_PREPARE:
164 rk2928_write_mask(codec, CODEC_REG_POWER, m_PD_MIC_BIAS | m_PD_CODEC, v_PD_MIC_BIAS(0) | v_PD_CODEC(0));
166 case SND_SOC_BIAS_STANDBY:
167 case SND_SOC_BIAS_OFF:
168 rk2928_write(codec, CODEC_REG_POWER, v_PWR_OFF);
173 codec->dapm.bias_level = level;
177 static int rk2928_probe(struct snd_soc_codec *codec)
179 struct platform_device *pdev = to_platform_device(codec->dev);
180 struct snd_soc_dapm_context *dapm = &codec->dapm;
181 struct resource *res, *mem;
184 DBG("%s", __FUNCTION__);
186 snd_soc_codec_set_drvdata(codec, &rk2928_data);
188 rk2928_data.dev = &pdev->dev;
189 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
191 dev_err(&pdev->dev, "Unable to get register resource\n");
195 rk2928_data.regbase_phy = res->start;
196 rk2928_data.regsize_phy = (res->end - res->start) + 1;
197 mem = request_mem_region(res->start, (res->end - res->start) + 1, pdev->name);
200 dev_err(&pdev->dev, "failed to request mem region for rk2928 codec\n");
206 rk2928_data.regbase = (int)ioremap(res->start, (res->end - res->start) + 1);
207 if (!rk2928_data.regbase) {
208 dev_err(&pdev->dev, "cannot ioremap acodec registers\n");
213 rk2928_write(codec, CODEC_REG_DAC_MUTE, v_MUTE_DAC(1));
214 rk2928_write(codec, CODEC_REG_DAC_GAIN, v_GAIN_DAC(DAC_GAIN_3DB_P));
215 rk2928_set_bias_level(codec, SND_SOC_BIAS_OFF);
217 snd_soc_dapm_new_controls(dapm, rk2928_dapm_widgets,
218 ARRAY_SIZE(rk2928_dapm_widgets));
219 snd_soc_dapm_add_routes(dapm, rk2928_audio_map, ARRAY_SIZE(rk2928_audio_map));
224 release_mem_region(res->start,(res->end - res->start) + 1);
225 // clk_disable(rk2928_data.hclk);
227 DBG("%s failed", __FUNCTION__);
231 static int rk2928_remove(struct snd_soc_codec *codec)
236 static int rk2928_suspend(struct snd_soc_codec *codec, pm_message_t state)
238 DBG("%s", __FUNCTION__);
239 rk2928_set_bias_level(codec, SND_SOC_BIAS_OFF);
243 static int rk2928_resume(struct snd_soc_codec *codec)
245 DBG("%s", __FUNCTION__);
246 rk2928_write(codec, CODEC_REG_POWER, v_PD_ADC(1) | v_PD_DAC(1) | v_PD_MIC_BIAS(1));
250 static struct snd_soc_codec_driver rk2928_audio_codec_drv = {
251 .probe = rk2928_probe,
252 .remove = rk2928_remove,
253 .suspend = rk2928_suspend,
254 .resume = rk2928_resume,
256 .write = rk2928_write,
257 .set_bias_level = rk2928_set_bias_level,
260 static struct snd_soc_dai_ops rk2928_audio_codec_ops = {
261 .hw_params = rk2928_audio_hw_params,
262 .trigger = rk2928_audio_trigger,
263 .startup = rk2928_audio_startup,
266 static struct snd_soc_dai_driver rk2928_codec_dai = {
267 .name = "rk2928-codec",
269 .stream_name = "HIFI Playback",
272 .rates = SNDRV_PCM_RATE_8000_48000,
273 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |
274 SNDRV_PCM_FMTBIT_S24_LE,
277 .stream_name = "HIFI Capture",
280 .rates = SNDRV_PCM_RATE_8000_48000,
281 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE,
283 .ops = &rk2928_audio_codec_ops,
286 static __devinit int rk2928_codec_probe(struct platform_device *pdev)
290 DBG("%s", __FUNCTION__);
292 /* Register ASoC codec DAI */
293 r = snd_soc_register_codec(&pdev->dev, &rk2928_audio_codec_drv,
294 &rk2928_codec_dai, 1);
296 dev_err(&pdev->dev, "can't register ASoC rk2928 audio codec\n");
300 DBG("%s success", __FUNCTION__);
305 static int __devexit rk2928_codec_remove(struct platform_device *pdev)
307 snd_soc_unregister_codec(&pdev->dev);
311 static struct platform_driver rk2928_codec_driver = {
312 .probe = rk2928_codec_probe,
313 .remove = __devexit_p(rk2928_codec_remove),
315 .name = "rk2928-codec",
316 .owner = THIS_MODULE,
320 static int __init rk2928_codec_init(void)
322 return platform_driver_register(&rk2928_codec_driver);
324 module_init(rk2928_codec_init);
326 static void __exit rk2928_codec_exit(void)
328 #ifdef CODEC_I2C_MODE
329 i2c_del_driver(&rk2928_codec_driver);
331 platform_driver_unregister(&rk2928_codec_driver);
334 module_exit(rk2928_codec_exit);
336 MODULE_DESCRIPTION("ASoC RK2928 codec driver");
337 MODULE_LICENSE("GPL");