audio: rk3036 codec driver

add rk3036 audio codec driver program, include DTS node.

Signed-off-by: dgl@rock-chips.com
This commit is contained in:
dgl
2014-07-28 10:07:40 +08:00
parent 466b5f40fc
commit f3fcb4ab6a
11 changed files with 1128 additions and 4 deletions

View File

@@ -0,0 +1,36 @@
* Rockchip Audio controller
Required SoC Specific Properties:
- compatible : "rk3036-audio"
- reg: physical base address of the controller and length of memory mapped
region.
- clocks: must include clock specifiers corresponding to entries in the
clock-names property.
- clocks-names: list of clock names sorted in the same order as the clocks
property. Must contain "rockchip-i2s" and "rk3036-codec".
- interrupts: interrupt number to the cpu.
- dmas: list of DMA controller phandle and DMA request line ordered pairs.
- dma-names: identifier string for each DMA request line in the dmas property.
These strings correspond 1:1 with the ordered pairs in dmas.
- pinctrl-names: must contain a "default" entry.
- pinctrl-0: pin control group to be used for this controller.
- pinctrl-1: pin control group to be used for gpio.
Example:
rockchip-audio {
compatible = "rk3036-audio";
dais {
dai0 {
audio-codec = <&codec>;
i2s-controller = <&i2s>;
format = "i2s";
//continuous-clock;
//bitclock-inversion;
//frame-inversion;
//bitclock-master;
//frame-master;
};
};
};

16
arch/arm/boot/dts/rk3036-sdk.dts Executable file → Normal file
View File

@@ -106,6 +106,22 @@
};
};
};
rockchip-audio {
compatible = "rk3036-audio";
dais {
dai0 {
audio-codec = <&codec>;
i2s-controller = <&i2s>;
format = "i2s";
//continuous-clock;
//bitclock-inversion;
//frame-inversion;
//bitclock-master;
//frame-master;
};
};
};
};
&uart0{

12
arch/arm/boot/dts/rk3036.dtsi Executable file → Normal file
View File

@@ -386,6 +386,18 @@
//pinctrl-1 = <&i2s_gpio>;
};
codec: codec@20030000 {
compatible = "rk3036-codec";
reg = <0x20030000 0x1000>;
spk_ctl_io = <&gpio1 GPIO_A0 0>;
pinctrl-names = "default";
pinctrl-0 = <&i2s0_gpio>;
boot_depop = <1>;
pa_enable_time = <1000>;
clocks = <&clk_gates5 14>;
clock-names = "g_pclk_acodec";
};
spdif: spdif@10204000 {
compatible = "rockchip-spdif";
reg = <0x10204000 0x1000>;

View File

@@ -375,8 +375,7 @@ CONFIG_SND_USB_AUDIO=y
CONFIG_SND_SOC=y
CONFIG_SND_RK_SOC=y
CONFIG_SND_RK_SOC_HDMI_I2S=y
CONFIG_SND_RK_SOC_RT5631=y
CONFIG_SND_RK_SOC_RT3224=y
CONFIG_SND_RK_SOC_RK3036=y
CONFIG_HID_BATTERY_STRENGTH=y
CONFIG_HIDRAW=y
CONFIG_UHID=y

View File

@@ -314,6 +314,9 @@ config SND_SOC_OMAP_HDMI_CODEC
config SND_SOC_PCM3008
tristate
config SND_SOC_RK3036
tristate
#Freescale sgtl5000 codec
config SND_SOC_SGTL5000
tristate

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

@@ -129,12 +129,13 @@ snd-soc-rt5616-objs := rt5616.o
snd-soc-rt5631-phone-objs := rt5631_phone.o
snd-soc-rt5625-objs := rt5625.o
obj-y := rt56xx_ioctl.o
snd-soc-rt5639-objs := rt5639.o rt5639_ioctl.o
snd-soc-rt5639-objs := rt5639.o rt5639_ioctl.o
snd-soc-rt5640-objs := rt5640.o rt5640-dsp.o rt5640_ioctl.o
snd-soc-rt3224-objs := rt3261.o rt3261_ioctl.o rt_codec_ioctl.o
snd-soc-rt3261-objs := rt3261-dsp.o
snd-soc-cs42l52-objs := cs42l52.o
snd-soc-rk1000-objs := rk1000_codec.o
snd-soc-rk3036-objs := rk3036_codec.o
snd-soc-rk610-objs := rk610_codec.o
snd-soc-rk616-objs := rk616_codec.o
snd-soc-rk2928-objs := rk2928_codec.o
@@ -282,6 +283,7 @@ obj-$(CONFIG_SND_SOC_RT5639) += snd-soc-rt5639.o
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o
obj-$(CONFIG_SND_SOC_RK1000) += snd-soc-rk1000.o
obj-$(CONFIG_SND_SOC_RK3036) += snd-soc-rk3036.o
obj-$(CONFIG_SND_SOC_RK610) += snd-soc-rk610.o
obj-$(CONFIG_SND_SOC_RK616) += snd-soc-rk616.o
obj-$(CONFIG_SND_SOC_RK2928) += snd-soc-rk2928.o

View File

@@ -0,0 +1,762 @@
/*
* rk3036_codec.c
*
* Driver for rockchip rk3036 codec
* Copyright (C) 2014
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/version.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/rockchip/iomap.h>
#include <linux/rockchip/grf.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/dmaengine_pcm.h>
#include <linux/io.h>
#include <linux/spinlock.h>
#include "rk3036_codec.h"
#define DEBUGOPEN 0
#if DEBUGOPEN
#define dbg_codec(level, fmt, arg...) do { \
if (debug >= level) \
printk(fmt , ## arg); } \
while (0)
#else
#define dbg_codec(level, fmt, arg...)
#endif
#define INVALID_GPIO -1
#define SPK_CTRL_OPEN 1
#define SPK_CTRL_CLOSE 0
/* volume setting
* 0: -39dB
* 26: 0dB
* 31: 6dB
* Step: 1.5dB
*/
#define OUT_VOLUME 31
/*with capacity or not*/
#define WITH_CAP
struct rk3036_codec_priv {
void __iomem *regbase;
struct snd_soc_codec *codec;
unsigned int stereo_sysclk;
unsigned int rate;
struct delayed_work codec_delayed_work;
int spk_ctl_gpio;
int delay_time;
struct clk *pclk;
};
static struct rk3036_codec_priv *rk3036_priv;
static const unsigned int rk3036_reg_defaults[RK3036_CODEC_REG28+1] = {
[RK3036_CODEC_RESET] = 0x03,
[RK3036_CODEC_REG03] = 0x00,
[RK3036_CODEC_REG04] = 0x50,
[RK3036_CODEC_REG05] = 0x0E,
[RK3036_CODEC_REG22] = 0x00,
[RK3036_CODEC_REG23] = 0x00,
[RK3036_CODEC_REG24] = 0x00,
[RK3036_CODEC_REG25] = 0x00,
[RK3036_CODEC_REG26] = 0x00,
[RK3036_CODEC_REG27] = 0x05,
[RK3036_CODEC_REG28] = 0x00,
};
/* function declare: */
static int rk3036_codec_register(
struct snd_soc_codec *codec, unsigned int reg);
static int rk3036_volatile_register(
struct snd_soc_codec *codec, unsigned int reg);
static int rk3036_set_bias_level(
struct snd_soc_codec *codec, enum snd_soc_bias_level level);
static unsigned int rk3036_codec_read(
struct snd_soc_codec *codec, unsigned int reg);
static inline void rk3036_write_reg_cache(
struct snd_soc_codec *codec, unsigned int reg, unsigned int value);
static inline unsigned int rk3036_read_reg_cache(struct snd_soc_codec *
codec, unsigned int reg)
{
unsigned int *cache = codec->reg_cache;
if (rk3036_codec_register(codec, reg))
return cache[reg];
dbg_codec(2, "%s : reg error!\n", __func__);
return -EINVAL;
}
static inline void rk3036_write_reg_cache(struct snd_soc_codec *
codec, unsigned int reg, unsigned int value)
{
unsigned int *cache = codec->reg_cache;
if (rk3036_codec_register(codec, reg)) {
cache[reg] = value;
return;
}
dbg_codec(2, "%s : reg error!\n", __func__);
}
static int rk3036_reset(struct snd_soc_codec *codec)
{
writel(0x00, rk3036_priv->regbase+RK3036_CODEC_RESET);
mdelay(10);
writel(0x03, rk3036_priv->regbase+RK3036_CODEC_RESET);
mdelay(10);
memcpy(codec->
reg_cache, rk3036_reg_defaults,
sizeof(rk3036_reg_defaults));
return 0;
}
static int rk3036_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 rk3036_codec_priv *rk3036 = rk3036_priv;
unsigned int dac_aif1 = 0, dac_aif2 = 0;
if (!rk3036) {
dbg_codec(2, "%s : rk3036 is NULL\n", __func__);
return -EINVAL;
}
dac_aif2 |= RK3036_CR05_FRAMEH_32BITS;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
dac_aif1 |= RK3036_CR04_HFVALID_16BITS;
break;
case SNDRV_PCM_FORMAT_S20_3LE:
dac_aif1 |= RK3036_CR04_HFVALID_20BITS;
break;
case SNDRV_PCM_FORMAT_S24_LE:
dac_aif1 |= RK3036_CR04_HFVALID_24BITS;
break;
case SNDRV_PCM_FORMAT_S32_LE:
dac_aif1 |= RK3036_CR04_HFVALID_32BITS;
break;
default:
return -EINVAL;
}
dac_aif1 |= RK3036_CR04_LR_SWAP_DIS;
dac_aif2 |= RK3036_CR05_DAC_RESET_DIS;
snd_soc_update_bits(
codec, RK3036_CODEC_REG04,
RK3036_CR04_HFVALID_MASK
|RK3036_CR04_LR_SWAP_MASK, dac_aif1);
snd_soc_update_bits(
codec, RK3036_CODEC_REG05,
RK3036_CR05_FRAMEH_MASK
|RK3036_CR05_DAC_RESET_MASK, dac_aif2);
return 0;
}
static int rk3036_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
unsigned int fmt_ms = 0, dac_aif1 = 0, dac_aif2 = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
fmt_ms |= RK3036_CR03_DIRECTION_IN|RK3036_CR03_I2SMODE_SLAVE;
break;
case SND_SOC_DAIFMT_CBM_CFM:
fmt_ms |= RK3036_CR03_DIRECTION_IOUT|RK3036_CR03_I2SMODE_MASTER;
break;
default:
dbg_codec(2, "%s : set master mask failed!\n", __func__);
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_A:
dac_aif1 |= RK3036_CR04_MODE_PCM;
break;
case SND_SOC_DAIFMT_DSP_B:
break;
case SND_SOC_DAIFMT_I2S:
dac_aif1 |= RK3036_CR04_MODE_I2S;
break;
case SND_SOC_DAIFMT_RIGHT_J:
dac_aif1 |= RK3036_CR04_MODE_RIGHT;
break;
case SND_SOC_DAIFMT_LEFT_J:
dac_aif1 |= RK3036_CR04_MODE_LEFT;
break;
default:
dbg_codec(2, "%s : set format failed!\n", __func__);
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
dac_aif1 |= RK3036_CR04_I2SLRC_NORMAL;
dac_aif2 |= RK3036_CR05_BCLKPOL_NORMAL;
break;
case SND_SOC_DAIFMT_IB_IF:
dac_aif1 |= RK3036_CR04_I2SLRC_REVERSAL;
dac_aif2 |= RK3036_CR05_BCLKPOL_REVERSAL;
break;
case SND_SOC_DAIFMT_IB_NF:
dac_aif1 |= RK3036_CR04_I2SLRC_REVERSAL;
dac_aif2 |= RK3036_CR05_BCLKPOL_NORMAL;
break;
case SND_SOC_DAIFMT_NB_IF:
dac_aif1 |= RK3036_CR04_I2SLRC_NORMAL;
dac_aif2 |= RK3036_CR05_BCLKPOL_REVERSAL;
break;
default:
dbg_codec(2, "%s : set dai format failed!\n", __func__);
return -EINVAL;
}
snd_soc_update_bits(
codec, RK3036_CODEC_REG03,
RK3036_CR03_DIRECTION_MASK
|RK3036_CR03_I2SMODE_MASK, fmt_ms);
snd_soc_update_bits(
codec, RK3036_CODEC_REG04,
RK3036_CR04_I2SLRC_MASK
| RK3036_CR04_MODE_MASK, dac_aif1);
snd_soc_update_bits(
codec, RK3036_CODEC_REG05,
RK3036_CR05_BCLKPOL_MASK, dac_aif2);
return 0;
}
static int rk3036_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct rk3036_codec_priv *rk3036 = rk3036_priv;
if (!rk3036) {
dbg_codec(2, "%s : rk3036 is NULL\n", __func__);
return -EINVAL;
}
rk3036->stereo_sysclk = freq;
return 0;
}
static int rk3036_digital_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
unsigned int val;
if (mute) {
val = snd_soc_read(codec, RK3036_CODEC_REG27);
if (val & (RK3036_CR27_HPOUTL_G_WORK
|RK3036_CR27_HPOUTR_G_WORK)) {
val &= ~RK3036_CR27_HPOUTL_G_WORK;
val &= ~RK3036_CR27_HPOUTR_G_WORK;
snd_soc_write(codec, RK3036_CODEC_REG27, val);
}
} else {
val = snd_soc_read(codec, RK3036_CODEC_REG27);
if ((val | ~RK3036_CR27_HPOUTL_G_WORK) || (val
| ~RK3036_CR27_HPOUTR_G_WORK)) {
val |= RK3036_CR27_HPOUTL_G_WORK
|RK3036_CR27_HPOUTR_G_WORK;
snd_soc_write(codec, RK3036_CODEC_REG27, val);
}
}
return 0;
}
static struct rk3036_reg_val_typ playback_power_up_list[] = {
/*01*/{RK3036_CODEC_REG24,
RK3036_CR24_DAC_SOURCE_WORK},
/* open current source. */
/*02*/{RK3036_CODEC_REG22,
RK3036_CR22_DACL_PATH_REFV_WORK|RK3036_CR22_DACR_PATH_REFV_WORK},
/* power on dac path reference voltage. */
/*03*/{RK3036_CODEC_REG27,
RK3036_CR27_HPOUTL_POP_WORK|RK3036_CR27_HPOUTR_POP_WORK},
/* pop precharge work. */
/*04*/{RK3036_CODEC_REG23,
RK3036_CR23_HPOUTL_EN_WORK|RK3036_CR23_HPOUTR_EN_WORK},
/* start-up HPOUTL HPOUTR */
/*05*/{RK3036_CODEC_REG23,
RK3036_CR23_HPOUTL_EN_WORK|RK3036_CR23_HPOUTR_EN_WORK
|RK3036_CR23_HPOUTL_WORK|RK3036_CR23_HPOUTR_WORK},
/* end the init state of HPOUTL HPOUTR */
/*06*/{RK3036_CODEC_REG24,
RK3036_CR24_DAC_SOURCE_WORK
|RK3036_CR24_DAC_PRECHARGE
|RK3036_CR24_DACL_REFV_WORK|RK3036_CR24_DACR_REFV_WORK},
/* start-up special ref_v of DACL DACR */
/*07*/{RK3036_CODEC_REG22,
RK3036_CR22_DACL_PATH_REFV_WORK|RK3036_CR22_DACR_PATH_REFV_WORK
|RK3036_CR22_DACL_CLK_WORK|RK3036_CR22_DACR_CLK_WORK},
/* start-up clock modul of LR channel */
/*08*/{RK3036_CODEC_REG22,
RK3036_CR22_DACL_PATH_REFV_WORK|RK3036_CR22_DACR_PATH_REFV_WORK
|RK3036_CR22_DACL_CLK_WORK|RK3036_CR22_DACR_CLK_WORK
|RK3036_CR22_DACL_WORK|RK3036_CR22_DACR_WORK},
/* start-up DACL DACR module */
/*09*/{RK3036_CODEC_REG27,
RK3036_CR27_HPOUTL_POP_WORK|RK3036_CR27_HPOUTR_POP_WORK
|RK3036_CR27_DACL_WORK|RK3036_CR27_DACR_WORK},
/* end the init state of DACL DACR */
/*10*/{RK3036_CODEC_REG25, OUT_VOLUME},
/*11*/{RK3036_CODEC_REG26, OUT_VOLUME},
/*12*/{RK3036_CODEC_REG24,
RK3036_CR24_DAC_SOURCE_WORK
|RK3036_CR24_DAC_PRECHARGE
|RK3036_CR24_DACL_REFV_WORK|RK3036_CR24_DACR_REFV_WORK
|RK3036_CR24_VOUTL_ZEROD_WORK|RK3036_CR24_VOUTR_ZEROD_WORK},
/* according to the need, open the zero-crossing detection function. */
/*13*/{RK3036_CODEC_REG27,
RK3036_CR27_HPOUTL_POP_WORK|RK3036_CR27_HPOUTR_POP_WORK
|RK3036_CR27_DACL_WORK|RK3036_CR27_DACR_WORK
|RK3036_CR27_HPOUTL_G_WORK|RK3036_CR27_HPOUTR_G_WORK},
/* end initial status of HPOUTL HPOUTR module,start normal output. */
};
#define PLAYBACK_POWER_UP_LIST_LEN ARRAY_SIZE(playback_power_up_list)
static int rk3036_codec_power_up(void)
{
struct snd_soc_codec *codec = rk3036_priv->codec;
int i, volume = 0;
if (!rk3036_priv || !rk3036_priv->codec)
return -EINVAL;
for (i = 0; i < PLAYBACK_POWER_UP_LIST_LEN; i++) {
if ((playback_power_up_list[i].reg ==
RK3036_CODEC_REG25) && (volume < OUT_VOLUME)) {
snd_soc_write(codec, RK3036_CODEC_REG25, volume);
snd_soc_write(codec, RK3036_CODEC_REG26, volume);
volume++;
mdelay(10);
i--;
} else {
snd_soc_write(codec, playback_power_up_list[i].
reg, playback_power_up_list[i].value);
mdelay(1);
}
}
return 0;
}
#define RK3036_PLAYBACK_RATES (SNDRV_PCM_RATE_8000 |\
SNDRV_PCM_RATE_16000 | \
SNDRV_PCM_RATE_32000 | \
SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | \
SNDRV_PCM_RATE_96000)
#define RK3036_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE |\
SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_ops rk3036_dai_ops = {
.hw_params = rk3036_hw_params,
.set_fmt = rk3036_set_dai_fmt,
.set_sysclk = rk3036_set_dai_sysclk,
.digital_mute = rk3036_digital_mute,
};
static struct snd_soc_dai_driver rk3036_dai[] = {
{
.name = "rk3036-voice",
.id = RK3036_VOICE,
.playback = {
.stream_name = "RK3036 CODEC PCM",
.channels_min = 1,
.channels_max = 2,
.rates = RK3036_PLAYBACK_RATES,
.formats = RK3036_FORMATS,
},
.ops = &rk3036_dai_ops,
},
};
static unsigned int rk3036_codec_read(struct snd_soc_codec *
codec, unsigned int reg)
{
unsigned int value;
if (!rk3036_priv) {
dbg_codec(2, "%s : rk3036 is NULL\n", __func__);
return -EINVAL;
}
if (!rk3036_codec_register(codec, reg)) {
dbg_codec(2, "%s : reg error!\n", __func__);
return -EINVAL;
}
value = readl_relaxed(rk3036_priv->regbase+reg);
dbg_codec(2, "%s : reg = 0x%x, val= 0x%x\n", __func__, reg, value);
return value;
}
static int rk3036_hw_write(const struct i2c_client *
client, const char *buf, int count)
{
unsigned int reg, value;
if (!rk3036_priv || !rk3036_priv->codec)
return -EINVAL;
if (count == 2) {
reg = (unsigned int)buf[0];
value = (unsigned int)buf[1];
writel(value, rk3036_priv->regbase+reg);
} else {
dbg_codec(2, "%s : i2c len error\n", __func__);
}
return count;
}
static int rk3036_codec_write(struct snd_soc_codec *
codec, unsigned int reg, unsigned int value)
{
int new_value = -1;
if (!rk3036_priv) {
dbg_codec(2, "%s : rk3036 is NULL\n", __func__);
return -EINVAL;
} else if (!rk3036_codec_register(codec, reg)) {
dbg_codec(2, "%s : reg error!\n", __func__);
return -EINVAL;
}
/*new_value = rk3036_set_init_value(codec, reg, value);*/
if (new_value == -1) {
writel(value, rk3036_priv->regbase+reg);
rk3036_write_reg_cache(codec, reg, value);
}
return 0;
}
static void spk_ctrl_fun(int status)
{
if (rk3036_priv == NULL)
return;
if (rk3036_priv->spk_ctl_gpio != INVALID_GPIO)
gpio_set_value(rk3036_priv->spk_ctl_gpio, status);
}
static void rk3036_delayedwork_fun(struct work_struct *work)
{
if (rk3036_priv == NULL)
return;
dbg_codec(2, "rk3036_delayedwork_fun\n");
spk_ctrl_fun(SPK_CTRL_OPEN);
}
static int rk3036_probe(struct snd_soc_codec *codec)
{
struct rk3036_codec_priv *rk3036_codec
= snd_soc_codec_get_drvdata(codec);
unsigned int val;
int ret;
rk3036_codec->codec = codec;
clk_prepare_enable(rk3036_codec->pclk);
ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
if (ret != 0)
goto err__;
codec->hw_read = rk3036_codec_read;
codec->hw_write = (hw_write_t)rk3036_hw_write;
codec->read = rk3036_codec_read;
codec->write = rk3036_codec_write;
INIT_DELAYED_WORK(&rk3036_codec->
codec_delayed_work, rk3036_delayedwork_fun);
val = snd_soc_read(codec, RK3036_CODEC_RESET);
if (val != rk3036_reg_defaults[RK3036_CODEC_RESET]) {
ret = -ENODEV;
goto err__;
}
/* config i2s output to acodec module. */
val = readl_relaxed(RK_GRF_VIRT + RK3036_GRF_SOC_CON0);
writel_relaxed(val | 0x04000400, RK_GRF_VIRT + RK3036_GRF_SOC_CON0);
rk3036_reset(codec);
snd_soc_write(codec, RK3036_CODEC_REG24, RK3036_CR24_DAC_SOURCE_STOP
| RK3036_CR24_DAC_PRECHARGE
| RK3036_CR24_DACL_REFV_STOP
| RK3036_CR24_DACR_REFV_STOP
| RK3036_CR24_VOUTL_ZEROD_STOP
| RK3036_CR24_VOUTR_ZEROD_STOP);
#ifdef WITH_CAP
/*set for capacity output,clear up noise*/
snd_soc_write(codec, RK3036_CODEC_REG28, 0x37);
mdelay(50);
/*snd_soc_write(codec, 0xbc,0x28);*/
#endif
rk3036_codec_power_up();
schedule_delayed_work(&rk3036_codec->
codec_delayed_work, msecs_to_jiffies(5000));
codec->dapm.bias_level = SND_SOC_BIAS_PREPARE;
return 0;
err__:
dbg_codec(2, "%s err ret=%d\n", __func__, ret);
return ret;
}
static int rk3036_remove(struct snd_soc_codec *codec)
{
if (!rk3036_priv) {
dbg_codec(2, "%s : rk3036_priv is NULL\n", __func__);
return 0;
}
spk_ctrl_fun(SPK_CTRL_CLOSE);
mdelay(10);
snd_soc_write(codec, RK3036_CODEC_RESET, 0xfc);
mdelay(10);
snd_soc_write(codec, RK3036_CODEC_RESET, 0x3);
mdelay(10);
return 0;
}
static int rk3036_suspend(struct snd_soc_codec *codec)
{
rk3036_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
static int rk3036_resume(struct snd_soc_codec *codec)
{
rk3036_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
}
static int rk3036_set_bias_level(struct snd_soc_codec *codec, enum
snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
break;
case SND_SOC_BIAS_OFF:
break;
}
codec->dapm.bias_level = level;
return 0;
}
static int rk3036_volatile_register(struct snd_soc_codec *
codec, unsigned int reg)
{
switch (reg) {
case RK3036_CODEC_RESET:
return 1;
default:
return 0;
}
}
static int rk3036_codec_register(struct snd_soc_codec *codec, unsigned int reg)
{
switch (reg) {
case RK3036_CODEC_RESET:
case RK3036_CODEC_REG03:
case RK3036_CODEC_REG04:
case RK3036_CODEC_REG05:
case RK3036_CODEC_REG22:
case RK3036_CODEC_REG23:
case RK3036_CODEC_REG24:
case RK3036_CODEC_REG25:
case RK3036_CODEC_REG26:
case RK3036_CODEC_REG27:
case RK3036_CODEC_REG28:
return 1;
default:
return 0;
}
}
static struct snd_soc_codec_driver soc_codec_dev_rk3036 = {
.probe = rk3036_probe,
.remove = rk3036_remove,
.suspend = rk3036_suspend,
.resume = rk3036_resume,
.set_bias_level = rk3036_set_bias_level,
.reg_cache_size = ARRAY_SIZE(rk3036_reg_defaults),
.reg_word_size = sizeof(unsigned int),
.reg_cache_default = rk3036_reg_defaults,
.volatile_register = rk3036_volatile_register,
.readable_register = rk3036_codec_register,
.reg_cache_step = sizeof(unsigned int),
};
static int rk3036_platform_probe(struct platform_device *pdev)
{
struct device_node *rk3036_np = pdev->dev.of_node;
struct rk3036_codec_priv *rk3036;
struct resource *res;
int ret;
rk3036 = devm_kzalloc(&pdev->dev, sizeof(*rk3036), GFP_KERNEL);
if (!rk3036) {
dbg_codec(2, "%s : rk3036 priv kzalloc failed!\n", __func__);
return -ENOMEM;
}
rk3036_priv = rk3036;
platform_set_drvdata(pdev, rk3036);
rk3036->spk_ctl_gpio = of_get_named_gpio(rk3036_np, "spk_ctl_io", 0);
if (!gpio_is_valid(rk3036->spk_ctl_gpio)) {
dbg_codec(2, "invalid reset_gpio: %d\n", rk3036->spk_ctl_gpio);
ret = -ENOENT;
goto err__;
}
ret = devm_gpio_request(&pdev->dev, rk3036->spk_ctl_gpio, "spk_ctl");
if (ret < 0) {
dbg_codec(2, "rk3036_platform_probe spk_ctl_gpio fail\n");
goto err__;
}
gpio_direction_output(rk3036->spk_ctl_gpio, SPK_CTRL_CLOSE);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
rk3036->regbase = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(rk3036->regbase))
return PTR_ERR(rk3036->regbase);
rk3036->pclk = devm_clk_get(&pdev->dev, "g_pclk_acodec");
if (IS_ERR(rk3036->pclk)) {
dev_err(&pdev->dev, "Unable to get acodec hclk\n");
ret = -ENXIO;
goto err__;
}
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_rk3036,
rk3036_dai, ARRAY_SIZE(rk3036_dai));
err__:
platform_set_drvdata(pdev, NULL);
rk3036_priv = NULL;
return ret;
}
static int rk3036_platform_remove(struct platform_device *pdev)
{
rk3036_priv = NULL;
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
void rk3036_platform_shutdown(struct platform_device *pdev)
{
if (!rk3036_priv || !rk3036_priv->codec)
return;
spk_ctrl_fun(SPK_CTRL_CLOSE);
mdelay(10);
writel(0xfc, rk3036_priv->regbase+RK3036_CODEC_RESET);
mdelay(10);
writel(0x03, rk3036_priv->regbase+RK3036_CODEC_RESET);
}
#ifdef CONFIG_OF
static const struct of_device_id rk3036codec_of_match[] = {
{ .compatible = "rk3036-codec"},
{},
};
MODULE_DEVICE_TABLE(of, rk3036codec_of_match);
#endif
static struct platform_driver rk3036_codec_driver = {
.driver = {
.name = "rk3036-codec",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(rk3036codec_of_match),
},
.probe = rk3036_platform_probe,
.remove = rk3036_platform_remove,
.shutdown = rk3036_platform_shutdown,
};
module_platform_driver(rk3036_codec_driver);
/* Module information */
MODULE_AUTHOR("rockchip");
MODULE_DESCRIPTION("ROCKCHIP i2s ASoC Interface");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,160 @@
/*
*
* Copyright (C) 2014 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 _RK3036_CODEC_H
#define _RK3036_CODEC_H
/* codec register */
#define RK3036_CODEC_RESET (0x00 << 2)
#define RK3036_CODEC_REG03 (0x03 << 2)
#define RK3036_CODEC_REG04 (0x04 << 2)
#define RK3036_CODEC_REG05 (0x05 << 2)
#define RK3036_CODEC_REG22 (0x22 << 2)
#define RK3036_CODEC_REG23 (0x23 << 2)
#define RK3036_CODEC_REG24 (0x24 << 2)
#define RK3036_CODEC_REG25 (0x25 << 2)
#define RK3036_CODEC_REG26 (0x26 << 2)
#define RK3036_CODEC_REG27 (0x27 << 2)
#define RK3036_CODEC_REG28 (0x28 << 2)
/* RK3036_CODEC_RESET */
#define RK3036_CR00_DIGITAL_RESET (0 << 1)
#define RK3036_CR00_DIGITAL_WORK (1 << 1)
#define RK3036_CR00_SYSTEM_RESET (0 << 0)
#define RK3036_CR00_SYSTEM_WORK (1 << 0)
/*RK3036_CODEC_REG03*/
#define RK3036_CR03_DIRECTION_MASK (1 << 5)
#define RK3036_CR03_DIRECTION_IN (0 << 5)
#define RK3036_CR03_DIRECTION_IOUT (1 << 5)
#define RK3036_CR03_I2SMODE_MASK (1 << 4)
#define RK3036_CR03_I2SMODE_SLAVE (0 << 4)
#define RK3036_CR03_I2SMODE_MASTER (1 << 4)
/*RK3036_CODEC_REG04*/
#define RK3036_CR04_I2SLRC_MASK (1 << 7)
#define RK3036_CR04_I2SLRC_NORMAL (0 << 7)
#define RK3036_CR04_I2SLRC_REVERSAL (1 << 7)
#define RK3036_CR04_HFVALID_MASK (3 << 5)
#define RK3036_CR04_HFVALID_16BITS (0 << 5)
#define RK3036_CR04_HFVALID_20BITS (1 << 5)
#define RK3036_CR04_HFVALID_24BITS (2 << 5)
#define RK3036_CR04_HFVALID_32BITS (3 << 5)
#define RK3036_CR04_MODE_MASK (3 << 3)
#define RK3036_CR04_MODE_RIGHT (0 << 3)
#define RK3036_CR04_MODE_LEFT (1 << 3)
#define RK3036_CR04_MODE_I2S (2 << 3)
#define RK3036_CR04_MODE_PCM (3 << 3)
#define RK3036_CR04_LR_SWAP_MASK (1 << 2)
#define RK3036_CR04_LR_SWAP_DIS (0 << 2)
#define RK3036_CR04_LR_SWAP_EN (1 << 2)
/*RK3036_CODEC_REG05*/
#define RK3036_CR05_FRAMEH_MASK (3 << 2)
#define RK3036_CR05_FRAMEH_16BITS (0 << 2)
#define RK3036_CR05_FRAMEH_20BITS (1 << 2)
#define RK3036_CR05_FRAMEH_24BITS (2 << 2)
#define RK3036_CR05_FRAMEH_32BITS (3 << 2)
#define RK3036_CR05_DAC_RESET_MASK (1 << 1)
#define RK3036_CR05_DAC_RESET_EN (0 << 1)
#define RK3036_CR05_DAC_RESET_DIS (1 << 1)
#define RK3036_CR05_BCLKPOL_MASK (1 << 0)
#define RK3036_CR05_BCLKPOL_NORMAL (0 << 0)
#define RK3036_CR05_BCLKPOL_REVERSAL (1 << 0)
/*RK3036_CODEC_REG22*/
#define RK3036_CR22_DACL_PATH_REFV_MASK (1 << 5)
#define RK3036_CR22_DACL_PATH_REFV_STOP (0 << 5)
#define RK3036_CR22_DACL_PATH_REFV_WORK (1 << 5)
#define RK3036_CR22_DACR_PATH_REFV_MASK (1 << 4)
#define RK3036_CR22_DACR_PATH_REFV_STOP (0 << 4)
#define RK3036_CR22_DACR_PATH_REFV_WORK (1 << 4)
#define RK3036_CR22_DACL_CLK_STOP (0 << 3)
#define RK3036_CR22_DACL_CLK_WORK (1 << 3)
#define RK3036_CR22_DACR_CLK_STOP (0 << 2)
#define RK3036_CR22_DACR_CLK_WORK (1 << 2)
#define RK3036_CR22_DACL_STOP (0 << 1)
#define RK3036_CR22_DACL_WORK (1 << 1)
#define RK3036_CR22_DACR_STOP (0 << 0)
#define RK3036_CR22_DACR_WORK (1 << 0)
/*RK3036_CODEC_REG23*/
#define RK3036_CR23_HPOUTL_INIT (0 << 3)
#define RK3036_CR23_HPOUTL_WORK (1 << 3)
#define RK3036_CR23_HPOUTR_INIT (0 << 2)
#define RK3036_CR23_HPOUTR_WORK (1 << 2)
#define RK3036_CR23_HPOUTL_EN_STOP (0 << 1)
#define RK3036_CR23_HPOUTL_EN_WORK (1 << 1)
#define RK3036_CR23_HPOUTR_EN_STOP (0 << 0)
#define RK3036_CR23_HPOUTR_EN_WORK (1 << 0)
/*RK3036_CODEC_REG24*/
#define RK3036_CR24_DAC_SOURCE_STOP (0 << 5)
#define RK3036_CR24_DAC_SOURCE_WORK (1 << 5)
#define RK3036_CR24_DAC_PRECHARGE (0 << 4)
#define RK3036_CR24_DAC_DISCHARGE (1 << 4)
#define RK3036_CR24_DACL_REFV_STOP (0 << 3)
#define RK3036_CR24_DACL_REFV_WORK (1 << 3)
#define RK3036_CR24_DACR_REFV_STOP (0 << 2)
#define RK3036_CR24_DACR_REFV_WORK (1 << 2)
#define RK3036_CR24_VOUTL_ZEROD_STOP (0 << 1)
#define RK3036_CR24_VOUTL_ZEROD_WORK (1 << 1)
#define RK3036_CR24_VOUTR_ZEROD_STOP (0 << 0)
#define RK3036_CR24_VOUTR_ZEROD_WORK (1 << 0)
/*RK3036_CODEC_REG27*/
#define RK3036_CR27_DACL_INIT (0 << 7)
#define RK3036_CR27_DACL_WORK (1 << 7)
#define RK3036_CR27_DACR_INIT (0 << 6)
#define RK3036_CR27_DACR_WORK (1 << 6)
#define RK3036_CR27_HPOUTL_G_MUTE (0 << 5)
#define RK3036_CR27_HPOUTL_G_WORK (1 << 5)
#define RK3036_CR27_HPOUTR_G_MUTE (0 << 4)
#define RK3036_CR27_HPOUTR_G_WORK (1 << 4)
#define RK3036_CR27_HPOUTL_POP_PRECHARGE (1 << 2)
#define RK3036_CR27_HPOUTL_POP_WORK (2 << 2)
#define RK3036_CR27_HPOUTR_POP_PRECHARGE (1 << 0)
#define RK3036_CR27_HPOUTR_POP_WORK (2 << 0)
/*RK3036_CODEC_REG28*/
#define RK3036_CR28_YES_027I (0 << 5)
#define RK3036_CR28_NON_027I (1 << 5)
#define RK3036_CR28_YES_050I (0 << 4)
#define RK3036_CR28_NON_050I (1 << 4)
#define RK3036_CR28_YES_100I (0 << 3)
#define RK3036_CR28_NON_100I (1 << 3)
#define RK3036_CR28_YES_130I (0 << 2)
#define RK3036_CR28_NON_130I (1 << 2)
#define RK3036_CR28_YES_260I (0 << 1)
#define RK3036_CR28_NON_260I (1 << 1)
#define RK3036_CR28_YES_400I (0 << 0)
#define RK3036_CR28_NON_400I (1 << 0)
enum {
RK3036_HIFI,
RK3036_VOICE,
};
struct rk3036_reg_val_typ {
unsigned int reg;
unsigned int value;
};
struct rk3036_init_bit_typ {
unsigned int reg;
unsigned int power_bit;
unsigned int init2_bit;
unsigned int init1_bit;
unsigned int init0_bit;
};
#endif

11
sound/soc/rockchip/Kconfig Executable file → Normal file
View File

@@ -20,7 +20,7 @@ choice
bool "Set audio support for HDMI"
default SND_RK_SOC_HDMI_I2S
config SND_RK_SOC_HDMI_I2S
depends on !RK616_USE_MCLK_12M || !SND_RK_SOC_RK616
depends on (!RK616_USE_MCLK_12M || !SND_RK_SOC_RK616) && !SND_RK_SOC_RK3036
select SND_RK_SOC_I2S
select SND_SOC_HDMI_I2S
tristate "HDMI use I2S"
@@ -240,6 +240,15 @@ config SND_RK_SOC_RK1000
Say Y if you want to add support for SoC audio on rockchip
with the RK1000.
config SND_RK_SOC_RK3036
tristate "SoC I2S Audio support for rockchip - RK3036"
depends on SND_RK_SOC
select SND_RK_SOC_I2S
select SND_SOC_RK3036
help
Say Y if you want to add support for SoC audio on rockchip
with the RK3036 s40.
config SND_RK_SOC_RK610
tristate "SoC I2S Audio support for rockchip - RK610"
depends on SND_RK_SOC && MFD_RK610

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

@@ -27,6 +27,7 @@ snd-soc-cs42l52-objs := rk_cs42l52.o
snd-soc-aic3111-objs := rk_aic3111.o
snd-soc-wm8988-objs := rk_wm8988.o
snd-soc-rk1000-objs := rk_rk1000codec.o
snd-soc-rk3036-objs := rk_rk3036.o
snd-soc-wm8994-objs := rk_wm8994.o
snd-soc-rk610-objs := rk_jetta_codec.o
snd-soc-rk616-objs := rk_rk616.o
@@ -54,6 +55,7 @@ obj-$(CONFIG_SND_RK_SOC_RT3224) += snd-soc-rt3224.o
obj-$(CONFIG_SND_RK_SOC_RT5639) += snd-soc-rt5639.o
obj-$(CONFIG_SND_RK_SOC_RT5616) += snd-soc-rt5616.o
obj-$(CONFIG_SND_RK_SOC_RK1000) += snd-soc-rk1000.o
obj-$(CONFIG_SND_RK_SOC_RK3036) += snd-soc-rk3036.o
obj-$(CONFIG_SND_RK_SOC_CS42L52) += snd-soc-cs42l52.o
obj-$(CONFIG_SND_RK_SOC_AIC3111) += snd-soc-aic3111.o
obj-$(CONFIG_SND_RK_SOC_AIC3262) += snd-soc-aic3262.o

View File

@@ -0,0 +1,123 @@
/*
* rk_rk3036codec.c-- SoC audio for rockchip
*
* Driver for rockchip internal rk_rk3036codec audio
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include "../codecs/rk3036_codec.h"
#include "card_info.h"
#include "rk_pcm.h"
#include "rk_i2s.h"
static int rk30_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 *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
unsigned int dai_fmt = rtd->dai_link->dai_fmt;
int ret;
/* set codec DAI configuration */
ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, dai_fmt);
if (ret < 0)
return ret;
return 0;
}
/*
* Logic for a rk3036 codec as connected on a rockchip board.
*/
static int rk30_rk3036_codec_init(struct snd_soc_pcm_runtime *rtd)
{
return 0;
}
static struct snd_soc_ops rk30_ops = {
.hw_params = rk30_hw_params,
};
static struct snd_soc_dai_link rk30_dai[] = {
{
.name = "RK3036",
.stream_name = "RK3036 CODEC PCM",
.codec_dai_name = "rk3036-voice",
.init = rk30_rk3036_codec_init,
.ops = &rk30_ops,
}
};
static struct snd_soc_card rockchip_rk3036_snd_card = {
.name = "RK_RK3036",
.dai_link = rk30_dai,
.num_links = 1,
};
static int rockchip_rk3036_audio_probe(struct platform_device *pdev)
{
int ret;
struct snd_soc_card *card = &rockchip_rk3036_snd_card;
card->dev = &pdev->dev;
ret = rockchip_of_get_sound_card_info(card);
if (ret < 0)
return ret;
ret = snd_soc_register_card(card);
return ret;
}
static int rockchip_rk3036_audio_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
snd_soc_unregister_card(card);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id rockchip_rk3036_of_match[] = {
{ .compatible = "rk3036-audio", },
{},
};
MODULE_DEVICE_TABLE(of, rockchip_rk3036_of_match);
#endif /* CONFIG_OF */
static struct platform_driver rockchip_rk3036_audio_driver = {
.driver = {
.name = "rk3036-audio",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(rockchip_rk3036_of_match),
},
.probe = rockchip_rk3036_audio_probe,
.remove = rockchip_rk3036_audio_remove,
};
module_platform_driver(rockchip_rk3036_audio_driver);
/* Module information */
MODULE_AUTHOR("rockchip");
MODULE_DESCRIPTION("ROCKCHIP i2s ASoC Interface");
MODULE_LICENSE("GPL");