2 * rk_rk616.c -- SoC audio for rockchip
\r
4 * Driver for rockchip rk616 audio
\r
6 * This program is free software; you can redistribute it and/or modify it
\r
7 * under the terms of the GNU General Public License as published by the
\r
8 * Free Software Foundation; either version 2 of the License, or (at your
\r
9 * option) any later version.
\r
14 #include <linux/module.h>
\r
15 #include <linux/device.h>
\r
16 #include <sound/core.h>
\r
17 #include <sound/pcm.h>
\r
18 #include <sound/soc.h>
\r
19 #include <sound/soc-dapm.h>
\r
21 #include <mach/hardware.h>
\r
22 #include "../codecs/rk616_codec.h"
\r
23 #include "rk29_pcm.h"
\r
24 #include "rk29_i2s.h"
\r
27 #define DBG(x...) printk(KERN_INFO x)
\r
32 static const struct snd_soc_dapm_widget rk_dapm_widgets[] = {
\r
33 SND_SOC_DAPM_MIC("Mic Jack", NULL),
\r
34 SND_SOC_DAPM_MIC("Headset Jack", NULL),
\r
35 SND_SOC_DAPM_SPK("Ext Spk", NULL),
\r
36 SND_SOC_DAPM_HP("Headphone Jack", NULL),
\r
39 static const struct snd_soc_dapm_route rk_audio_map[]={
\r
41 /* Mic Jack --> MIC_IN*/
\r
42 {"Mic1 Bias", NULL, "Mic Jack"},
\r
43 {"MIC1P", NULL, "Mic1 Bias"},
\r
44 {"MIC1N", NULL, "Mic1 Bias"},
\r
47 {"Mic2 Bias", NULL, "Headset Jack"},
\r
48 {"MIC2P", NULL, "Mic2 Bias"},
\r
49 {"MIC2N", NULL, "Mic2 Bias"},
\r
51 {"Ext Spk", NULL, "SPKOUTR"},
\r
52 {"Ext Spk", NULL, "SPKOUTL"},
\r
54 {"Headphone Jack", NULL, "HPOUTR"},
\r
55 {"Headphone Jack", NULL, "HPOUTL"},
\r
58 static const struct snd_kcontrol_new rk_controls[] = {
\r
59 SOC_DAPM_PIN_SWITCH("Mic Jack"),
\r
60 SOC_DAPM_PIN_SWITCH("Headset Jack"),
\r
61 SOC_DAPM_PIN_SWITCH("Ext Spk"),
\r
62 SOC_DAPM_PIN_SWITCH("Headphone Jack"),
\r
65 static int rk616_init(struct snd_soc_pcm_runtime *rtd)
\r
67 struct snd_soc_codec *codec = rtd->codec;
\r
68 struct snd_soc_dapm_context *dapm = &codec->dapm;
\r
70 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
\r
72 snd_soc_add_controls(codec, rk_controls,
\r
73 ARRAY_SIZE(rk_controls));
\r
75 /* Add specific widgets */
\r
76 snd_soc_dapm_new_controls(dapm, rk_dapm_widgets,
\r
77 ARRAY_SIZE(rk_dapm_widgets));
\r
78 /* Set up specific audio path audio_mapnects */
\r
79 snd_soc_dapm_add_routes(dapm, rk_audio_map, ARRAY_SIZE(rk_audio_map));
\r
81 snd_soc_dapm_enable_pin(dapm, "Mic Jack");
\r
82 snd_soc_dapm_enable_pin(dapm, "Headset Jack");
\r
83 snd_soc_dapm_enable_pin(dapm, "Ext Spk");
\r
84 snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
\r
86 snd_soc_dapm_sync(dapm);
\r
91 static int rk_hifi_hw_params(struct snd_pcm_substream *substream,
\r
92 struct snd_pcm_hw_params *params)
\r
94 struct snd_soc_pcm_runtime *rtd = substream->private_data;
\r
95 struct snd_soc_dai *codec_dai = rtd->codec_dai;
\r
96 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
\r
97 unsigned int pll_out = 0;
\r
100 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
\r
102 #if defined(CONFIG_SND_RK_SOC_SPDIF)
\r
103 /* MCLK must be 12M when HDMI is in */
\r
104 if (get_hdmi_state()) {
\r
105 DBG("%s : HDMI is in, do not set sys clk\n",__FUNCTION__);
\r
110 /* set codec DAI configuration */
\r
111 #if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE)
\r
113 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
\r
114 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
\r
116 #if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER)
\r
118 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
\r
119 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM );
\r
124 /* set cpu DAI configuration */
\r
125 #if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE)
\r
126 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
\r
127 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
\r
129 #if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER)
\r
130 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
\r
131 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
\r
136 switch(params_rate(params)) {
\r
142 pll_out = 12288000;
\r
147 pll_out = 11289600;
\r
150 DBG("Enter:%s, %d, Error rate=%d\n", __FUNCTION__, __LINE__, params_rate(params));
\r
155 DBG("Enter:%s, %d, rate=%d\n", __FUNCTION__, __LINE__, params_rate(params));
\r
157 /*Set the system clk for codec*/
\r
158 ret = snd_soc_dai_set_sysclk(codec_dai, 0, pll_out, SND_SOC_CLOCK_IN);
\r
160 DBG("rk_hifi_hw_params:failed to set the sysclk for codec side\n");
\r
164 snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0);
\r
165 snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_BCLK, (pll_out/4)/params_rate(params)-1);
\r
166 snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_MCLK, 3);
\r
168 DBG("Enter:%s, %d, pll_out/4/params_rate(params) = %d \n", __FUNCTION__, __LINE__, (pll_out/4)/params_rate(params));
\r
173 static int rk_voice_hw_params(struct snd_pcm_substream *substream,
\r
174 struct snd_pcm_hw_params *params)
\r
176 struct snd_soc_pcm_runtime *rtd = substream->private_data;
\r
177 struct snd_soc_dai *codec_dai = rtd->codec_dai;
\r
178 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
\r
179 unsigned int pll_out = 0;
\r
182 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
\r
184 /* MCLK must be 12M when HDMI is in */
\r
185 #if defined(CONFIG_SND_RK_SOC_SPDIF)
\r
186 if (get_hdmi_state()) {
\r
187 DBG("%s : HDMI is in, do not set sys clk\n",__FUNCTION__);
\r
192 /* set codec DAI configuration */
\r
193 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A |
\r
194 SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBS_CFS);
\r
196 switch(params_rate(params)) {
\r
202 pll_out = 12288000;
\r
207 pll_out = 11289600;
\r
210 DBG("Enter:%s, %d, Error rate=%d\n", __FUNCTION__, __LINE__, params_rate(params));
\r
215 //snd_soc_dai_set_pll(codec_dai, RT5625_PLL_MCLK_TO_VSYSCLK, 0, pll_out, 24576000);
\r
217 /*Set the system clk for codec*/
\r
218 ret = snd_soc_dai_set_sysclk(codec_dai, 0, pll_out, SND_SOC_CLOCK_IN);
\r
221 printk("rk_voice_hw_params:failed to set the sysclk for codec side\n");
\r
225 ret = snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0);
\r
230 static struct snd_soc_ops rk616_hifi_ops = {
\r
231 .hw_params = rk_hifi_hw_params,
\r
234 static struct snd_soc_ops rk616_voice_ops = {
\r
235 .hw_params = rk_voice_hw_params,
\r
238 static struct snd_soc_dai_link rk_dai[] = {
\r
240 .name = "RK616 I2S1",
\r
241 .stream_name = "RK616 PCM",
\r
242 .codec_name = "rk616-codec.4-0050",
\r
243 .platform_name = "rockchip-audio",
\r
244 .cpu_dai_name = "rk29_i2s.1",
\r
245 .codec_dai_name = "rk616-hifi",
\r
246 .init = rk616_init,
\r
247 .ops = &rk616_hifi_ops,
\r
250 .name = "RK616 I2S2",
\r
251 .stream_name = "RK616 PCM",
\r
252 .codec_name = "rk616-codec.4-0050",
\r
253 .platform_name = "rockchip-audio",
\r
254 .cpu_dai_name = "rk29_i2s.1",
\r
255 .codec_dai_name = "rk616-voice",
\r
256 .ops = &rk616_voice_ops,
\r
260 static struct snd_soc_card snd_soc_card_rk = {
\r
261 .name = "RK_RK616",
\r
262 .dai_link = rk_dai,
\r
266 static struct platform_device *rk_snd_device;
\r
268 static int __init audio_card_init(void)
\r
272 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
\r
274 rk_snd_device = platform_device_alloc("soc-audio", -1);
\r
275 if (!rk_snd_device) {
\r
276 printk("platform device allocation failed\n");
\r
280 platform_set_drvdata(rk_snd_device, &snd_soc_card_rk);
\r
281 ret = platform_device_add(rk_snd_device);
\r
283 printk("platform device add failed\n");
\r
285 platform_device_put(rk_snd_device);
\r
292 static void __exit audio_card_exit(void)
\r
294 platform_device_unregister(rk_snd_device);
\r
297 module_init(audio_card_init);
\r
298 module_exit(audio_card_exit);
\r
299 /* Module information */
\r
300 MODULE_AUTHOR("rockchip");
\r
301 MODULE_DESCRIPTION("ROCKCHIP i2s ASoC Interface");
\r
302 MODULE_LICENSE("GPL");
\r