mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 11:26:02 +09:00
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:
36
Documentation/devicetree/bindings/sound/rockchip-audio.txt
Normal file
36
Documentation/devicetree/bindings/sound/rockchip-audio.txt
Normal 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
16
arch/arm/boot/dts/rk3036-sdk.dts
Executable file → Normal 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
12
arch/arm/boot/dts/rk3036.dtsi
Executable file → Normal 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>;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
4
sound/soc/codecs/Makefile
Executable file → Normal 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
|
||||
|
||||
762
sound/soc/codecs/rk3036_codec.c
Normal file
762
sound/soc/codecs/rk3036_codec.c
Normal 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");
|
||||
160
sound/soc/codecs/rk3036_codec.h
Normal file
160
sound/soc/codecs/rk3036_codec.h
Normal 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
11
sound/soc/rockchip/Kconfig
Executable file → Normal 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
2
sound/soc/rockchip/Makefile
Executable file → Normal 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
|
||||
|
||||
123
sound/soc/rockchip/rk_rk3036.c
Normal file
123
sound/soc/rockchip/rk_rk3036.c
Normal 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");
|
||||
Reference in New Issue
Block a user