rk2928: add audio codec.

This commit is contained in:
Zheng Yang
2012-07-23 18:14:13 +08:00
parent 42c4038c46
commit 046ee1ba24
8 changed files with 622 additions and 2 deletions

View File

@@ -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);

4
sound/soc/codecs/Kconfig Normal file → Executable file
View File

@@ -418,3 +418,7 @@ config SND_SOC_WM2000
config SND_SOC_WM9090
tristate
config SND_SOC_RK2928
tristate
depends on ARCH_RK2928

3
sound/soc/codecs/Makefile Normal file → Executable file
View File

@@ -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

337
sound/soc/codecs/rk2928_codec.c Executable file
View File

@@ -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 <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#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");

82
sound/soc/codecs/rk2928_codec.h Executable file
View File

@@ -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__ */

View File

@@ -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

2
sound/soc/rk29/Makefile Normal file → Executable file
View File

@@ -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

163
sound/soc/rk29/rk2928-card.c Executable file
View File

@@ -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 <linux/delay.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#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");