mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-10 21:07:02 +09:00
rk2928: add audio codec.
This commit is contained in:
@@ -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
4
sound/soc/codecs/Kconfig
Normal file → Executable 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
3
sound/soc/codecs/Makefile
Normal file → Executable 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
337
sound/soc/codecs/rk2928_codec.c
Executable 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
82
sound/soc/codecs/rk2928_codec.h
Executable 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__ */
|
||||
@@ -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
2
sound/soc/rk29/Makefile
Normal file → Executable 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
163
sound/soc/rk29/rk2928-card.c
Executable 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");
|
||||
Reference in New Issue
Block a user