From: Zheng Yang Date: Mon, 23 Jul 2012 10:14:13 +0000 (+0800) Subject: rk2928: add audio codec. X-Git-Tag: firefly_0821_release~8912^2~72 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=046ee1ba24b1f38a4d4ae73a7f8a92dabd3534f8;p=firefly-linux-kernel-4.4.55.git rk2928: add audio codec. --- diff --git a/arch/arm/mach-rk2928/devices.c b/arch/arm/mach-rk2928/devices.c index 4d9f39826138..5c670f4330df 100755 --- a/arch/arm/mach-rk2928/devices.c +++ b/arch/arm/mach-rk2928/devices.c @@ -679,6 +679,24 @@ static void __init rk2928_init_sdmmc(void) platform_device_register(&device_sdmmc1); #endif } + +#ifdef CONFIG_SND_SOC_RK2928 +static struct resource resources_acodec[] = { + { + .start = RK2928_ACODEC_PHYS, + .end = RK2928_ACODEC_PHYS + RK2928_ACODEC_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device device_acodec = { + .name = "rk2928-codec", + .id = -1, + .num_resources = ARRAY_SIZE(resources_acodec), + .resource = resources_acodec, +}; +#endif + static int __init rk2928_init_devices(void) { rk2928_init_dma(); @@ -699,6 +717,9 @@ static int __init rk2928_init_devices(void) rk_serial_debug_init(DEBUG_UART_BASE, IRQ_DEBUG_UART, IRQ_UART_SIGNAL, -1); #endif rk2928_init_i2s(); +#ifdef CONFIG_SND_SOC_RK2928 + platform_device_register(&device_acodec); +#endif return 0; } arch_initcall(rk2928_init_devices); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig old mode 100644 new mode 100755 index ab6dbb51b7c2..7a3904d93387 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -418,3 +418,7 @@ config SND_SOC_WM2000 config SND_SOC_WM9090 tristate + +config SND_SOC_RK2928 + tristate + depends on ARCH_RK2928 \ No newline at end of file diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile old mode 100644 new mode 100755 index ea82f0d8e08e..2e6d428b45d0 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -89,7 +89,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o snd-soc-rk1000-objs := rk1000_codec.o snd-soc-jz4740-codec-objs := jz4740.o snd-soc-rk610-objs := rk610_codec.o - +snd-soc-rk2928-objs := rk2928_codec.o # Amp snd-soc-lm4857-objs := lm4857.o snd-soc-max9877-objs := max9877.o @@ -188,6 +188,7 @@ obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o obj-$(CONFIG_SND_SOC_RK1000) += snd-soc-rk1000.o obj-$(CONFIG_SND_SOC_RK610) += snd-soc-rk610.o +obj-$(CONFIG_SND_SOC_RK2928) += snd-soc-rk2928.o # Amp obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o diff --git a/sound/soc/codecs/rk2928_codec.c b/sound/soc/codecs/rk2928_codec.c new file mode 100755 index 000000000000..57086b8ec237 --- /dev/null +++ b/sound/soc/codecs/rk2928_codec.c @@ -0,0 +1,337 @@ +/* + * rk2928_codec.c ALSA SoC RK2928 codec driver + * + * Copyright 2012 Rockchip + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "rk2928_codec.h" + +static struct rk2928_codec_data { + struct device *dev; + struct clk *hclk; + int regbase; + int regbase_phy; + int regsize_phy; + int mute; +} rk2928_data; + +static const struct snd_soc_dapm_widget rk2928_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DACL", "HIFI Playback", CODEC_REG_POWER, 5, 1), + SND_SOC_DAPM_DAC("DACR", "HIFI Playback", CODEC_REG_POWER, 4, 1), + SND_SOC_DAPM_PGA("DACL Amp", CODEC_REG_DAC_GAIN, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA("DACR Amp", CODEC_REG_DAC_GAIN, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("DACL Drv", CODEC_REG_DAC_MUTE, 1, 1, NULL, 0), + SND_SOC_DAPM_OUT_DRV("DACR Drv", CODEC_REG_DAC_MUTE, 0, 1, NULL, 0), + SND_SOC_DAPM_OUTPUT("SPKL"), + SND_SOC_DAPM_OUTPUT("SPKR"), + SND_SOC_DAPM_ADC("ADCL", "HIFI Capture", CODEC_REG_POWER, 3, 1), + SND_SOC_DAPM_ADC("ADCR", "HIFI Capture", CODEC_REG_POWER, 2, 1), + SND_SOC_DAPM_INPUT("MICL"), + SND_SOC_DAPM_INPUT("MICR"), +}; + +static const struct snd_soc_dapm_route rk2928_audio_map[] = { + {"DACL Drv", "DACL Amp", "DACL"}, + {"DACR Drv", "DACR Amp", "DACR"}, + {"SPKL", NULL, "DACL Drv"}, + {"SPKR", NULL, "DACR Drv"}, + {"ADCL", NULL, "MICL"}, + {"ADCR", NULL, "MICR"}, +}; + +void codec_set_spk(bool on) +{ + +} + +static unsigned int rk2928_read(struct snd_soc_codec *codec, unsigned int reg) +{ + return readl(rk2928_data.regbase + reg); +} + +static int rk2928_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) +{ + DBG("%s reg 0x%02x value 0x%02x", __FUNCTION__, reg, value); + writel(value, rk2928_data.regbase + reg); + return 0; +} + +static int rk2928_write_mask(struct snd_soc_codec *codec, unsigned int reg, + unsigned int mask, unsigned int value) +{ + unsigned int regvalue = rk2928_read(codec, reg); + + DBG("%s reg 0x%02x mask 0x%02x value 0x%02x", __FUNCTION__, reg, mask, value); + + regvalue &= ~mask; + regvalue |= mask & value; + return rk2928_write(codec, reg, regvalue); +} + +static int rk2928_audio_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ +// struct snd_soc_pcm_runtime *rtd = substream->private_data; +// struct snd_soc_codec *codec = rtd->codec; +// struct rk2928_codec_data *priv = snd_soc_codec_get_drvdata(codec); + + DBG("%s", __FUNCTION__); + + return 0; +} + +static int rk2928_audio_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ +// struct snd_soc_pcm_runtime *rtd = substream->private_data; +// struct snd_soc_codec *codec = rtd->codec; +// struct rk2928_codec_data *priv = snd_soc_codec_get_drvdata(codec); + int err = 0; + + DBG("%s cmd 0x%x", __FUNCTION__, cmd); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + break; + default: + err = -EINVAL; + } + return err; +} + +static int rk2928_audio_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ +// struct snd_soc_pcm_runtime *rtd = substream->private_data; +// struct snd_soc_codec *codec = rtd->codec; + DBG("%s", __FUNCTION__); + return 0; +} + +static int rk2928_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + DBG("%s level %d", __FUNCTION__, level); + + if(codec == NULL) + return -1; + + switch(level) + { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + rk2928_write_mask(codec, CODEC_REG_POWER, m_PD_MIC_BIAS | m_PD_CODEC, v_PD_MIC_BIAS(0) | v_PD_CODEC(0)); + break; + case SND_SOC_BIAS_STANDBY: + case SND_SOC_BIAS_OFF: + rk2928_write(codec, CODEC_REG_POWER, v_PWR_OFF); + break; + default: + return -1; + } + codec->dapm.bias_level = level; + return 0; +} + +static int rk2928_probe(struct snd_soc_codec *codec) +{ + struct platform_device *pdev = to_platform_device(codec->dev); + struct snd_soc_dapm_context *dapm = &codec->dapm; + struct resource *res, *mem; + int ret; + + DBG("%s", __FUNCTION__); + + snd_soc_codec_set_drvdata(codec, &rk2928_data); + + rk2928_data.dev = &pdev->dev; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Unable to get register resource\n"); + ret = -ENXIO; + goto err0; + } + rk2928_data.regbase_phy = res->start; + rk2928_data.regsize_phy = (res->end - res->start) + 1; + mem = request_mem_region(res->start, (res->end - res->start) + 1, pdev->name); + if (!mem) + { + dev_err(&pdev->dev, "failed to request mem region for rk2928 codec\n"); + ret = -ENOENT; + goto err0; + } + + + rk2928_data.regbase = (int)ioremap(res->start, (res->end - res->start) + 1); + if (!rk2928_data.regbase) { + dev_err(&pdev->dev, "cannot ioremap acodec registers\n"); + ret = -ENXIO; + goto err1; + } + + rk2928_write(codec, CODEC_REG_DAC_MUTE, v_MUTE_DAC(1)); + rk2928_write(codec, CODEC_REG_DAC_GAIN, v_GAIN_DAC(DAC_GAIN_3DB_P)); + rk2928_set_bias_level(codec, SND_SOC_BIAS_OFF); + + snd_soc_dapm_new_controls(dapm, rk2928_dapm_widgets, + ARRAY_SIZE(rk2928_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, rk2928_audio_map, ARRAY_SIZE(rk2928_audio_map)); + + return 0; + +err1: + release_mem_region(res->start,(res->end - res->start) + 1); +// clk_disable(rk2928_data.hclk); +err0: + DBG("%s failed", __FUNCTION__); + return ret; +} + +static int rk2928_remove(struct snd_soc_codec *codec) +{ + return 0; +} + +static int rk2928_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + DBG("%s", __FUNCTION__); + rk2928_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int rk2928_resume(struct snd_soc_codec *codec) +{ + DBG("%s", __FUNCTION__); + rk2928_write(codec, CODEC_REG_POWER, v_PD_ADC(1) | v_PD_DAC(1) | v_PD_MIC_BIAS(1)); + return 0; +} + +static struct snd_soc_codec_driver rk2928_audio_codec_drv = { + .probe = rk2928_probe, + .remove = rk2928_remove, + .suspend = rk2928_suspend, + .resume = rk2928_resume, + .read = rk2928_read, + .write = rk2928_write, + .set_bias_level = rk2928_set_bias_level, +}; + +static struct snd_soc_dai_ops rk2928_audio_codec_ops = { + .hw_params = rk2928_audio_hw_params, + .trigger = rk2928_audio_trigger, + .startup = rk2928_audio_startup, +}; + +static struct snd_soc_dai_driver rk2928_codec_dai = { + .name = "rk2928-codec", + .playback = { + .stream_name = "HIFI Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S24_LE, + }, + .capture = { + .stream_name = "HIFI Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE, + }, + .ops = &rk2928_audio_codec_ops, +}; + +static __devinit int rk2928_codec_probe(struct platform_device *pdev) +{ + int r; + + DBG("%s", __FUNCTION__); + + /* Register ASoC codec DAI */ + r = snd_soc_register_codec(&pdev->dev, &rk2928_audio_codec_drv, + &rk2928_codec_dai, 1); + if (r) { + dev_err(&pdev->dev, "can't register ASoC rk2928 audio codec\n"); + return r; + } + + DBG("%s success", __FUNCTION__); + return 0; + +} + +static int __devexit rk2928_codec_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static struct platform_driver rk2928_codec_driver = { + .probe = rk2928_codec_probe, + .remove = __devexit_p(rk2928_codec_remove), + .driver = { + .name = "rk2928-codec", + .owner = THIS_MODULE, + }, +}; + +static int __init rk2928_codec_init(void) +{ + return platform_driver_register(&rk2928_codec_driver); +} +module_init(rk2928_codec_init); + +static void __exit rk2928_codec_exit(void) +{ + #ifdef CODEC_I2C_MODE + i2c_del_driver(&rk2928_codec_driver); + #else + platform_driver_unregister(&rk2928_codec_driver); + #endif +} +module_exit(rk2928_codec_exit); + +MODULE_DESCRIPTION("ASoC RK2928 codec driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rk2928_codec.h b/sound/soc/codecs/rk2928_codec.h new file mode 100755 index 000000000000..42401a94afde --- /dev/null +++ b/sound/soc/codecs/rk2928_codec.h @@ -0,0 +1,82 @@ +/* + * rk2928.h ALSA SoC RK2928 codec driver + * + * Copyright 2012 Rockchip + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __RK2928_CODEC_H__ +#define __RK2928_CODEC_H__ + + +#define CODEC_REG_ADC_PGA_GAIN 0x0b + #define m_MIC_GAIN_CHANNEL_L (0x0F << 4) + #define m_MIC_GAIN_CHANNEL_R (0x0F) + #define v_MIC_GAIN_CHANNEL_L(n) ((n) << 4) + #define v_MIC_GAIN_CHANNEL_R(n) (n) + +#define CODEC_REG_POWER 0x0c + #define m_PD_CODEC (0x01) + #define m_PD_MIC_BIAS (0x01 << 1) + #define m_PD_ADC (0x03 << 2) + #define m_PD_DAC (0x03 << 4) + #define v_PD_CODEC(n) (n) + #define v_PD_MIC_BIAS(n) (n << 1) + #define v_PD_ADC_R(n) (n << 2) + #define v_PD_ADC_L(n) (n << 3) + #define v_PD_DAC_R(n) (n << 4) + #define v_PD_DAC_L(n) (n << 5) + #define v_PD_ADC(n) (v_PD_ADC_L(n) | v_PD_ADC_R(n)) + #define v_PD_DAC(n) (v_PD_DAC_L(n) | v_PD_DAC_R(n)) + #define v_PWR_OFF v_PD_DAC_L(1) | v_PD_DAC_R(1) | v_PD_ADC_L(1) | v_PD_ADC_R(1) | v_PD_MIC_BIAS(1) | v_PD_CODEC(1) + +#define CODEC_REG_VCM_BIAS 0x0d + #define v_MIC_BIAS(n) (n) + enum { + VCM_RESISTOR_100K = 0, + VCM_RESISTOR_25K + }; + #define v_VCM_25K_100K(n) (n << 2) + +#define CODEC_REG_DAC_MUTE 0x0e + #define v_MUTE_DAC_L(n) (n << 1) + #define v_MUTE_DAC_R(n) (n) + #define v_MUTE_DAC(n) v_MUTE_DAC_L(n) | v_MUTE_DAC_R(n) + +#define CODEC_REG_ADC_SOURCE 0x0f + enum { + ADC_SRC_MIC = 0, + ADC_SRC_LINE_IN + }; + #define v_SRC_ADC_L(n) (n << 1) + #define v_SRC_ADC_R(n) (n) + +#define CODEC_REG_DAC_GAIN 0x10 + #define m_GAIN_DAC_L (0x03 << 2) + #define m_GAIN_DAC_R (0x03) + enum { + DAC_GAIN_0DB = 0, + DAC_GAIN_3DB_P = 0x2, //3db + DAC_GAIN_3DB_N //-3db + }; + #define v_GAIN_DAC_L(n) (n << 2) + #define v_GAIN_DAC_R(n) (n) + #define v_GAIN_DAC(n) (v_GAIN_DAC_L(n) | v_GAIN_DAC_R(n)) + +//#ifndef DEBUG +//#define DEBUG +//#endif + +#ifdef DEBUG +#define DBG(format, ...) \ + printk(KERN_INFO "RK2928 CODEC: " format "\n", ## __VA_ARGS__) +#else +#define DBG(format, ...) +#endif + + + +#endif /* __RK2928_CODEC_H__ */ diff --git a/sound/soc/rk29/Kconfig b/sound/soc/rk29/Kconfig index 63e28d3c21e1..c95b1d03768f 100755 --- a/sound/soc/rk29/Kconfig +++ b/sound/soc/rk29/Kconfig @@ -169,7 +169,17 @@ config SND_RK29_SOC_RK610 Say Y if you want to add support for SoC audio on rockchip with the RK610(JETTA). -if SND_RK29_SOC_WM8988 || SND_RK29_SOC_RK1000 || SND_RK29_SOC_WM8994 || SND_RK29_SOC_WM8900 || SND_RK29_SOC_RT5621 || SND_RK29_SOC_RT5631 || SND_RK29_SOC_RT5625 || SND_RK29_SOC_CS42L52 || SND_RK29_SOC_AIC3111 || SND_RK29_SOC_HDMI || SND_RK29_SOC_RK610 || SND_RK29_SOC_AIC3262 +config SND_RK_SOC_RK2928 + tristate "SoC I2S Audio support for rockchip - RK2928" + depends on SND_RK29_SOC && ARCH_RK2928 + select SND_RK29_SOC_I2S + select SND_SOC_RK2928 + select SND_RK29_CODEC_SOC_SLAVE + help + Say Y if you want to add support for SoC audio on rockchip + with the RK2928 internal codec. + +if SND_RK29_SOC_WM8988 || SND_RK29_SOC_RK1000 || SND_RK29_SOC_WM8994 || SND_RK29_SOC_WM8900 || SND_RK29_SOC_RT5621 || SND_RK29_SOC_RT5631 || SND_RK29_SOC_RT5625 || SND_RK29_SOC_CS42L52 || SND_RK29_SOC_AIC3111 || SND_RK29_SOC_HDMI || SND_RK29_SOC_RK610 || SND_RK29_SOC_AIC3262 || SND_RK_SOC_RK2928 choice bool "Set i2s type" default SND_RK29_CODEC_SOC_SLAVE diff --git a/sound/soc/rk29/Makefile b/sound/soc/rk29/Makefile old mode 100644 new mode 100755 index 0d06e37a1620..1f9622291282 --- a/sound/soc/rk29/Makefile +++ b/sound/soc/rk29/Makefile @@ -28,6 +28,7 @@ snd-soc-wm8994-objs := rk29_wm8994.o snd-soc-hdmi-objs := rk29_hdmi.o snd-soc-rk610-objs := rk29_jetta_codec.o snd-soc-aic3262-objs := rk29_aic3262.o +snd-soc-rk2928-objs := rk2928-card.o obj-$(CONFIG_SND_RK29_SOC_WM8994) += snd-soc-wm8994.o obj-$(CONFIG_SND_RK29_SOC_WM8988) += snd-soc-wm8988.o @@ -41,3 +42,4 @@ obj-$(CONFIG_SND_RK29_SOC_AIC3111) += snd-soc-aic3111.o obj-$(CONFIG_SND_RK29_SOC_AIC3262) += snd-soc-aic3262.o obj-$(CONFIG_SND_RK29_SOC_HDMI) += snd-soc-hdmi.o obj-$(CONFIG_SND_RK29_SOC_RK610) += snd-soc-rk610.o +obj-$(CONFIG_SND_RK_SOC_RK2928) += snd-soc-rk2928.o \ No newline at end of file diff --git a/sound/soc/rk29/rk2928-card.c b/sound/soc/rk29/rk2928-card.c new file mode 100755 index 000000000000..185d8ea1567d --- /dev/null +++ b/sound/soc/rk29/rk2928-card.c @@ -0,0 +1,163 @@ +/* + * rk2928-card.c -- SoC audio for RockChip RK2928 + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ +#include +#include +#include + +#include "rk29_pcm.h" +#include "rk29_i2s.h" + +#ifdef DEBUG +#define DBG(format, ...) \ + printk(KERN_INFO "RK2928 Card: " format "\n", ## __VA_ARGS__) +#else +#define DBG(format, ...) +#endif + +static int rk2928_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + unsigned int pll_out = 0; + int div_bclk,div_mclk; + int ret; + + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + + /* set cpu DAI configuration */ + #if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) + DBG("Set cpu_dai master\n"); + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); + #endif + #if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER) + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + DBG("Set cpu_dai slave\n"); + #endif + if (ret < 0) + return ret; + + switch(params_rate(params)) { + case 8000: + case 16000: + case 24000: + case 32000: + case 48000: + case 96000: + pll_out = 12288000; + break; + case 11025: + case 22050: + case 44100: + case 88200: + pll_out = 11289600; + break; + case 176400: + pll_out = 11289600*2; + break; + case 192000: + pll_out = 12288000*2; + break; + default: + DBG("Enter:%s, %d, Error rate=%d\n",__FUNCTION__,__LINE__,params_rate(params)); + return -EINVAL; + break; + } + DBG("Enter:%s, %d, rate=%d\n",__FUNCTION__,__LINE__,params_rate(params)); + + #if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER) + snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0); + #endif + + #if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) + div_bclk = 63; + div_mclk = pll_out/(params_rate(params)*64) - 1; + + snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0); + snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_BCLK,div_bclk); + snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_MCLK, div_mclk); + #endif + + return 0; +} + +static struct snd_soc_ops rk2928_dai_ops = { + .hw_params = rk2928_dai_hw_params, +}; + +static struct snd_soc_dai_link rk2928_dai[] = { + { + .name = "RK2928", + .stream_name = "RK2928", + .cpu_dai_name = "rk29_i2s.0", + .platform_name = "rockchip-audio", + .codec_name = "rk2928-codec.0-0001", + .codec_dai_name = "rk2928-codec", + .ops = &rk2928_dai_ops, + }, +}; + +/* Audio machine driver */ +static struct snd_soc_card snd_soc_rk2928 = { + .name = "RK2928", + .dai_link = rk2928_dai, + .num_links = ARRAY_SIZE(rk2928_dai), +}; + +static struct platform_device *rk2928_snd_device; + +static int __init rk2928_soc_init(void) +{ + int ret; + + printk(KERN_INFO "RK2928 SoC init\n"); + + rk2928_snd_device = platform_device_alloc("soc-audio", -1); + if (!rk2928_snd_device) { + printk(KERN_ERR "Platform device allocation failed\n"); + return -ENOMEM; + } + + platform_set_drvdata(rk2928_snd_device, &snd_soc_rk2928); + + ret = platform_device_add(rk2928_snd_device); + if (ret) + goto err1; + + return 0; + +err1: + printk(KERN_ERR "Unable to add platform device\n"); + platform_device_put(rk2928_snd_device); + + return ret; +} +module_init(rk2928_soc_init); + +static void __exit rk2928_soc_exit(void) +{ + platform_device_unregister(rk2928_snd_device); +} +module_exit(rk2928_soc_exit); + +MODULE_DESCRIPTION("ALSA SoC RK2928"); +MODULE_LICENSE("GPL"); \ No newline at end of file