mirror of
https://github.com/hardkernel/linux.git
synced 2026-03-25 03:50:24 +09:00
ASoC: codecs: add support for rk3328
Change-Id: I66ff61c18fe70135fd7ac0569954263743263a3a Signed-off-by: Sugar Zhang <sugar.zhang@rock-chips.com>
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
* Rockchip Rk3328 internal codec
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: "rockchip,rk3328-codec"
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- rockchip,grf: the phandle of the syscon node for GRF register.
|
||||
- clocks: a list of phandle + clock-specifer pairs, one for each entry in clock-names.
|
||||
- clock-names: should be "pclk".
|
||||
- spk-depop-time-ms: speak depop time msec.
|
||||
|
||||
Example for rk3328 internal codec:
|
||||
|
||||
codec: codec@ff410000 {
|
||||
compatible = "rockchip,rk3328-codec";
|
||||
reg = <0x0 0xff410000 0x0 0x1000>;
|
||||
rockchip,grf = <&grf>;
|
||||
clocks = <&cru PCLK_ACODEC>;
|
||||
clock-names = "pclk";
|
||||
spk-depop-time-ms = 100;
|
||||
status = "disabled";
|
||||
};
|
||||
@@ -91,6 +91,7 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_PCM3008
|
||||
select SND_SOC_PCM512x_I2C if I2C
|
||||
select SND_SOC_PCM512x_SPI if SPI_MASTER
|
||||
select SND_SOC_RK3328
|
||||
select SND_SOC_RT286 if I2C
|
||||
select SND_SOC_RT298 if I2C
|
||||
select SND_SOC_RT5616 if I2C
|
||||
@@ -548,6 +549,10 @@ config SND_SOC_PCM512x_SPI
|
||||
select SND_SOC_PCM512x
|
||||
select REGMAP_SPI
|
||||
|
||||
config SND_SOC_RK3328
|
||||
select REGMAP_MMIO
|
||||
tristate "Rockchip RK3328 CODEC"
|
||||
|
||||
config SND_SOC_RL6231
|
||||
tristate
|
||||
default y if SND_SOC_RT5616=y
|
||||
|
||||
@@ -86,6 +86,7 @@ snd-soc-pcm3008-objs := pcm3008.o
|
||||
snd-soc-pcm512x-objs := pcm512x.o
|
||||
snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
|
||||
snd-soc-pcm512x-spi-objs := pcm512x-spi.o
|
||||
snd-soc-rk3328-objs := rk3328_codec.o
|
||||
snd-soc-rl6231-objs := rl6231.o
|
||||
snd-soc-rl6347a-objs := rl6347a.o
|
||||
snd-soc-rt286-objs := rt286.o
|
||||
@@ -288,6 +289,7 @@ obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
|
||||
obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o
|
||||
obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o
|
||||
obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o
|
||||
obj-$(CONFIG_SND_SOC_RK3328) += snd-soc-rk3328.o
|
||||
obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o
|
||||
obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o
|
||||
obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o
|
||||
|
||||
519
sound/soc/codecs/rk3328_codec.c
Normal file
519
sound/soc/codecs/rk3328_codec.c
Normal file
@@ -0,0 +1,519 @@
|
||||
/*
|
||||
* rk3328_codec.c -- rk3328 ALSA Soc Audio driver
|
||||
*
|
||||
* Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include "rk3328_codec.h"
|
||||
|
||||
/*
|
||||
* volume setting
|
||||
* 0: -39dB
|
||||
* 26: 0dB
|
||||
* 31: 6dB
|
||||
* Step: 1.5dB
|
||||
*/
|
||||
#define OUT_VOLUME (0x18)
|
||||
#define RK3328_GRF_SOC_CON2 (0x0408)
|
||||
#define RK3328_GRF_SOC_CON10 (0x0428)
|
||||
|
||||
struct rk3328_codec_priv {
|
||||
struct regmap *regmap;
|
||||
struct regmap *grf;
|
||||
struct clk *pclk;
|
||||
unsigned int sclk;
|
||||
int spk_depop_time; /* msec */
|
||||
};
|
||||
|
||||
static const struct reg_default rk3328_codec_reg_defaults[] = {
|
||||
{ CODEC_RESET, 0x03 },
|
||||
{ DAC_INIT_CTRL1, 0x00 },
|
||||
{ DAC_INIT_CTRL2, 0x50 },
|
||||
{ DAC_INIT_CTRL3, 0x0e },
|
||||
{ DAC_PRECHARGE_CTRL, 0x01 },
|
||||
{ DAC_PWR_CTRL, 0x00 },
|
||||
{ DAC_CLK_CTRL, 0x00 },
|
||||
{ HPMIX_CTRL, 0x00 },
|
||||
{ HPOUT_CTRL, 0x00 },
|
||||
{ HPOUTL_GAIN_CTRL, 0x00 },
|
||||
{ HPOUTR_GAIN_CTRL, 0x00 },
|
||||
{ HPOUT_POP_CTRL, 0x11 },
|
||||
};
|
||||
|
||||
static int rk3328_codec_reset(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct rk3328_codec_priv *rk3328 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
regmap_write(rk3328->regmap, CODEC_RESET, 0);
|
||||
mdelay(10);
|
||||
regmap_write(rk3328->regmap, CODEC_RESET, 0x03);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rk3328_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
||||
unsigned int fmt)
|
||||
{
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct rk3328_codec_priv *rk3328 = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int val = 0;
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
val |= PIN_DIRECTION_IN | DAC_I2S_MODE_SLAVE;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
val |= PIN_DIRECTION_OUT | DAC_I2S_MODE_MASTER;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL1,
|
||||
PIN_DIRECTION_MASK | DAC_I2S_MODE_MASK, val);
|
||||
|
||||
val = 0;
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
val |= DAC_MODE_PCM;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
val |= DAC_MODE_I2S;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
val |= DAC_MODE_RJM;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
val |= DAC_MODE_LJM;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL2,
|
||||
DAC_MODE_MASK, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rk3328_analog_output(struct rk3328_codec_priv *rk3328, int mute)
|
||||
{
|
||||
regmap_write(rk3328->grf, RK3328_GRF_SOC_CON10,
|
||||
(BIT(1) << 16) | (mute << 1));
|
||||
}
|
||||
|
||||
static int rk3328_digital_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct rk3328_codec_priv *rk3328 = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int val = 0;
|
||||
|
||||
if (mute)
|
||||
val = HPOUTL_MUTE | HPOUTR_MUTE;
|
||||
else
|
||||
val = HPOUTL_UNMUTE | HPOUTR_UNMUTE;
|
||||
|
||||
regmap_update_bits(rk3328->regmap, HPOUT_CTRL,
|
||||
HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rk3328_codec_power_on(struct snd_soc_codec *codec, int wait_ms)
|
||||
{
|
||||
struct rk3328_codec_priv *rk3328 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
|
||||
DAC_CHARGE_XCHARGE_MASK, DAC_CHARGE_PRECHARGE);
|
||||
mdelay(10);
|
||||
regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
|
||||
DAC_CHARGE_CURRENT_ALL_MASK,
|
||||
DAC_CHARGE_CURRENT_ALL_ON);
|
||||
|
||||
mdelay(wait_ms);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rk3328_codec_power_off(struct snd_soc_codec *codec, int wait_ms)
|
||||
{
|
||||
struct rk3328_codec_priv *rk3328 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
|
||||
DAC_CHARGE_XCHARGE_MASK, DAC_CHARGE_DISCHARGE);
|
||||
mdelay(10);
|
||||
regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
|
||||
DAC_CHARGE_CURRENT_ALL_MASK,
|
||||
DAC_CHARGE_CURRENT_ALL_ON);
|
||||
|
||||
mdelay(wait_ms);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct rk3328_reg_msk_val playback_open_list[] = {
|
||||
{ DAC_PWR_CTRL, DAC_PWR_MASK, DAC_PWR_ON },
|
||||
{ DAC_PWR_CTRL, DACL_PATH_REFV_MASK | DACR_PATH_REFV_MASK,
|
||||
DACL_PATH_REFV_ON | DACR_PATH_REFV_ON },
|
||||
{ DAC_PWR_CTRL, HPOUTL_ZERO_CROSSING_ON | HPOUTR_ZERO_CROSSING_ON,
|
||||
HPOUTL_ZERO_CROSSING_ON | HPOUTR_ZERO_CROSSING_ON },
|
||||
{ HPOUT_POP_CTRL, HPOUTR_POP_MASK | HPOUTL_POP_MASK,
|
||||
HPOUTR_POP_WORK | HPOUTL_POP_WORK },
|
||||
{ HPMIX_CTRL, HPMIXL_MASK | HPMIXR_MASK, HPMIXL_EN | HPMIXR_EN },
|
||||
{ HPMIX_CTRL, HPMIXL_INIT_MASK | HPMIXR_INIT_MASK,
|
||||
HPMIXL_INIT_EN | HPMIXR_INIT_EN },
|
||||
{ HPOUT_CTRL, HPOUTL_MASK | HPOUTR_MASK, HPOUTL_EN | HPOUTR_EN },
|
||||
{ HPOUT_CTRL, HPOUTL_INIT_MASK | HPOUTR_INIT_MASK,
|
||||
HPOUTL_INIT_EN | HPOUTR_INIT_EN },
|
||||
{ DAC_CLK_CTRL, DACL_REFV_MASK | DACR_REFV_MASK,
|
||||
DACL_REFV_ON | DACR_REFV_ON },
|
||||
{ DAC_CLK_CTRL, DACL_CLK_MASK | DACR_CLK_MASK,
|
||||
DACL_CLK_ON | DACR_CLK_ON },
|
||||
{ DAC_CLK_CTRL, DACL_MASK | DACR_MASK, DACL_ON | DACR_ON },
|
||||
{ DAC_CLK_CTRL, DACL_INIT_MASK | DACR_INIT_MASK,
|
||||
DACL_INIT_ON | DACR_INIT_ON },
|
||||
{ DAC_SELECT, DACL_SELECT_MASK | DACR_SELECT_MASK,
|
||||
DACL_SELECT | DACR_SELECT },
|
||||
{ HPMIX_CTRL, HPMIXL_INIT2_MASK | HPMIXR_INIT2_MASK,
|
||||
HPMIXL_INIT2_EN | HPMIXR_INIT2_EN },
|
||||
{ HPOUT_CTRL, HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK,
|
||||
HPOUTL_UNMUTE | HPOUTR_UNMUTE },
|
||||
};
|
||||
|
||||
#define PLAYBACK_OPEN_LIST_LEN ARRAY_SIZE(playback_open_list)
|
||||
|
||||
static int rk3328_codec_open_playback(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct rk3328_codec_priv *rk3328 = snd_soc_codec_get_drvdata(codec);
|
||||
int i = 0;
|
||||
|
||||
regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
|
||||
DAC_CHARGE_CURRENT_ALL_MASK,
|
||||
DAC_CHARGE_CURRENT_I);
|
||||
|
||||
for (i = 0; i < PLAYBACK_OPEN_LIST_LEN; i++) {
|
||||
regmap_update_bits(rk3328->regmap,
|
||||
playback_open_list[i].reg,
|
||||
playback_open_list[i].msk,
|
||||
playback_open_list[i].val);
|
||||
mdelay(1);
|
||||
}
|
||||
|
||||
msleep(rk3328->spk_depop_time);
|
||||
rk3328_analog_output(rk3328, 1);
|
||||
|
||||
regmap_update_bits(rk3328->regmap, HPOUTL_GAIN_CTRL,
|
||||
HPOUTL_GAIN_MASK, OUT_VOLUME);
|
||||
regmap_update_bits(rk3328->regmap, HPOUTR_GAIN_CTRL,
|
||||
HPOUTR_GAIN_MASK, OUT_VOLUME);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct rk3328_reg_msk_val playback_close_list[] = {
|
||||
{ HPMIX_CTRL, HPMIXL_INIT2_MASK | HPMIXR_INIT2_MASK,
|
||||
HPMIXL_INIT2_DIS | HPMIXR_INIT2_DIS },
|
||||
{ DAC_SELECT, DACL_SELECT_MASK | DACR_SELECT_MASK,
|
||||
DACL_UNSELECT | DACR_UNSELECT },
|
||||
{ HPOUT_CTRL, HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK,
|
||||
HPOUTL_MUTE | HPOUTR_MUTE },
|
||||
{ HPOUT_CTRL, HPOUTL_INIT_MASK | HPOUTR_INIT_MASK,
|
||||
HPOUTL_INIT_DIS | HPOUTR_INIT_DIS },
|
||||
{ HPOUT_CTRL, HPOUTL_MASK | HPOUTR_MASK, HPOUTL_DIS | HPOUTR_DIS },
|
||||
{ HPMIX_CTRL, HPMIXL_MASK | HPMIXR_MASK, HPMIXL_DIS | HPMIXR_DIS },
|
||||
{ DAC_CLK_CTRL, DACL_MASK | DACR_MASK, DACL_OFF | DACR_OFF },
|
||||
{ DAC_CLK_CTRL, DACL_CLK_MASK | DACR_CLK_MASK,
|
||||
DACL_CLK_OFF | DACR_CLK_OFF },
|
||||
{ DAC_CLK_CTRL, DACL_REFV_MASK | DACR_REFV_MASK,
|
||||
DACL_REFV_OFF | DACR_REFV_OFF },
|
||||
{ HPOUT_POP_CTRL, HPOUTR_POP_MASK | HPOUTL_POP_MASK,
|
||||
HPOUTR_POP_XCHARGE | HPOUTL_POP_XCHARGE },
|
||||
{ DAC_PWR_CTRL, DACL_PATH_REFV_MASK | DACR_PATH_REFV_MASK,
|
||||
DACL_PATH_REFV_OFF | DACR_PATH_REFV_OFF },
|
||||
{ DAC_PWR_CTRL, DAC_PWR_MASK, DAC_PWR_OFF },
|
||||
{ HPMIX_CTRL, HPMIXL_INIT_MASK | HPMIXR_INIT_MASK,
|
||||
HPMIXL_INIT_DIS | HPMIXR_INIT_DIS },
|
||||
{ DAC_CLK_CTRL, DACL_INIT_MASK | DACR_INIT_MASK,
|
||||
DACL_INIT_OFF | DACR_INIT_OFF },
|
||||
};
|
||||
|
||||
#define PLAYBACK_CLOSE_LIST_LEN ARRAY_SIZE(playback_close_list)
|
||||
|
||||
static int rk3328_codec_close_playback(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct rk3328_codec_priv *rk3328 = snd_soc_codec_get_drvdata(codec);
|
||||
int i = 0;
|
||||
|
||||
rk3328_analog_output(rk3328, 0);
|
||||
|
||||
regmap_update_bits(rk3328->regmap, HPOUTL_GAIN_CTRL,
|
||||
HPOUTL_GAIN_MASK, 0);
|
||||
regmap_update_bits(rk3328->regmap, HPOUTR_GAIN_CTRL,
|
||||
HPOUTR_GAIN_MASK, 0);
|
||||
|
||||
for (i = 0; i < PLAYBACK_CLOSE_LIST_LEN; i++) {
|
||||
regmap_update_bits(rk3328->regmap,
|
||||
playback_close_list[i].reg,
|
||||
playback_close_list[i].msk,
|
||||
playback_close_list[i].val);
|
||||
mdelay(1);
|
||||
}
|
||||
|
||||
regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
|
||||
DAC_CHARGE_CURRENT_ALL_MASK,
|
||||
DAC_CHARGE_CURRENT_I);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rk3328_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct rk3328_codec_priv *rk3328 = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int val = 0;
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
val |= DAC_VDL_16BITS;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
val |= DAC_VDL_20BITS;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
val |= DAC_VDL_24BITS;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
val |= DAC_VDL_32BITS;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL2, DAC_VDL_MASK, val);
|
||||
val = DAC_WL_32BITS | DAC_RST_DIS;
|
||||
regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL3,
|
||||
DAC_WL_MASK | DAC_RST_MASK, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rk3328_pcm_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
|
||||
return rk3328_codec_open_playback(codec);
|
||||
}
|
||||
|
||||
static void rk3328_pcm_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
|
||||
rk3328_codec_close_playback(codec);
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_ops rk3328_dai_ops = {
|
||||
.hw_params = rk3328_hw_params,
|
||||
.set_fmt = rk3328_set_dai_fmt,
|
||||
.digital_mute = rk3328_digital_mute,
|
||||
.startup = rk3328_pcm_startup,
|
||||
.shutdown = rk3328_pcm_shutdown,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver rk3328_dai[] = {
|
||||
{
|
||||
.name = "rk3328-hifi",
|
||||
.id = RK3328_HIFI,
|
||||
.playback = {
|
||||
.stream_name = "HIFI Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_96000,
|
||||
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_S20_3LE |
|
||||
SNDRV_PCM_FMTBIT_S24_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE),
|
||||
},
|
||||
.ops = &rk3328_dai_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static int rk3328_codec_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
rk3328_codec_reset(codec);
|
||||
rk3328_codec_power_on(codec, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rk3328_codec_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
rk3328_codec_close_playback(codec);
|
||||
rk3328_codec_power_off(codec, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_codec_driver soc_codec_dev_rk3328 = {
|
||||
.probe = rk3328_codec_probe,
|
||||
.remove = rk3328_codec_remove,
|
||||
};
|
||||
|
||||
static bool rk3328_codec_write_read_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case CODEC_RESET:
|
||||
case DAC_INIT_CTRL1:
|
||||
case DAC_INIT_CTRL2:
|
||||
case DAC_INIT_CTRL3:
|
||||
case DAC_PRECHARGE_CTRL:
|
||||
case DAC_PWR_CTRL:
|
||||
case DAC_CLK_CTRL:
|
||||
case HPMIX_CTRL:
|
||||
case DAC_SELECT:
|
||||
case HPOUT_CTRL:
|
||||
case HPOUTL_GAIN_CTRL:
|
||||
case HPOUTR_GAIN_CTRL:
|
||||
case HPOUT_POP_CTRL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool rk3328_codec_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case CODEC_RESET:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct regmap_config rk3328_codec_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.max_register = HPOUT_POP_CTRL,
|
||||
.writeable_reg = rk3328_codec_write_read_reg,
|
||||
.readable_reg = rk3328_codec_write_read_reg,
|
||||
.volatile_reg = rk3328_codec_volatile_reg,
|
||||
.reg_defaults = rk3328_codec_reg_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(rk3328_codec_reg_defaults),
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id rk3328codec_of_match[] = {
|
||||
{ .compatible = "rockchip,rk3328-codec", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rk3328codec_of_match);
|
||||
#endif
|
||||
|
||||
static int rk3328_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *rk3328_np = pdev->dev.of_node;
|
||||
struct rk3328_codec_priv *rk3328;
|
||||
struct resource *res;
|
||||
struct regmap *grf;
|
||||
void __iomem *base;
|
||||
int ret = 0;
|
||||
|
||||
rk3328 = devm_kzalloc(&pdev->dev, sizeof(*rk3328), GFP_KERNEL);
|
||||
if (!rk3328)
|
||||
return -ENOMEM;
|
||||
|
||||
grf = syscon_regmap_lookup_by_phandle(rk3328_np,
|
||||
"rockchip,grf");
|
||||
if (IS_ERR(grf)) {
|
||||
dev_err(&pdev->dev, "missing 'rockchip,grf'\n");
|
||||
return PTR_ERR(grf);
|
||||
}
|
||||
rk3328->grf = grf;
|
||||
/* enable i2s_acodec_en */
|
||||
regmap_write(grf, RK3328_GRF_SOC_CON2,
|
||||
(BIT(14) << 16 | BIT(14)));
|
||||
|
||||
ret = of_property_read_u32(rk3328_np, "spk-depop-time-ms",
|
||||
&rk3328->spk_depop_time);
|
||||
if (ret < 0) {
|
||||
dev_info(&pdev->dev, "spk_depop_time use default value.\n");
|
||||
rk3328->spk_depop_time = 200;
|
||||
}
|
||||
|
||||
rk3328_analog_output(rk3328, 0);
|
||||
|
||||
rk3328->pclk = devm_clk_get(&pdev->dev, "pclk");
|
||||
if (IS_ERR(rk3328->pclk)) {
|
||||
dev_err(&pdev->dev, "can't get acodec pclk\n");
|
||||
return PTR_ERR(rk3328->pclk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(rk3328->pclk);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to enable acodec pclk\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
rk3328->regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||
&rk3328_codec_regmap_config);
|
||||
if (IS_ERR(rk3328->regmap))
|
||||
return PTR_ERR(rk3328->regmap);
|
||||
|
||||
platform_set_drvdata(pdev, rk3328);
|
||||
|
||||
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_rk3328,
|
||||
rk3328_dai, ARRAY_SIZE(rk3328_dai));
|
||||
}
|
||||
|
||||
static int rk3328_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_codec(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver rk3328_codec_driver = {
|
||||
.driver = {
|
||||
.name = "rk3328-codec",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(rk3328codec_of_match),
|
||||
},
|
||||
.probe = rk3328_platform_probe,
|
||||
.remove = rk3328_platform_remove,
|
||||
};
|
||||
module_platform_driver(rk3328_codec_driver);
|
||||
|
||||
MODULE_AUTHOR("Sugar Zhang <sugar.zhang@rock-chips.com>");
|
||||
MODULE_DESCRIPTION("ASoC rk3328 codec driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
218
sound/soc/codecs/rk3328_codec.h
Normal file
218
sound/soc/codecs/rk3328_codec.h
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* rk3328_codec.h -- rk3328 ALSA Soc Audio driver
|
||||
*
|
||||
* Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _RK3328_CODEC_H
|
||||
#define _RK3328_CODEC_H
|
||||
|
||||
/* codec register */
|
||||
#define CODEC_RESET (0x00 << 2)
|
||||
#define DAC_INIT_CTRL1 (0x03 << 2)
|
||||
#define DAC_INIT_CTRL2 (0x04 << 2)
|
||||
#define DAC_INIT_CTRL3 (0x05 << 2)
|
||||
#define DAC_PRECHARGE_CTRL (0x22 << 2)
|
||||
#define DAC_PWR_CTRL (0x23 << 2)
|
||||
#define DAC_CLK_CTRL (0x24 << 2)
|
||||
#define HPMIX_CTRL (0x25 << 2)
|
||||
#define DAC_SELECT (0x26 << 2)
|
||||
#define HPOUT_CTRL (0x27 << 2)
|
||||
#define HPOUTL_GAIN_CTRL (0x28 << 2)
|
||||
#define HPOUTR_GAIN_CTRL (0x29 << 2)
|
||||
#define HPOUT_POP_CTRL (0x2a << 2)
|
||||
|
||||
/* REG00: CODEC_RESET */
|
||||
#define PWR_RST_BYPASS_DIS BIT(6)
|
||||
#define PWR_RST_BYPASS_EN BIT(6)
|
||||
#define DIG_CORE_RST (0 << 1)
|
||||
#define DIG_CORE_WORK BIT(1)
|
||||
#define SYS_RST (0)
|
||||
#define SYS_WORK BIT(0)
|
||||
|
||||
/* REG03: DAC_INIT_CTRL1 */
|
||||
#define PIN_DIRECTION_MASK BIT(5)
|
||||
#define PIN_DIRECTION_IN (0 << 5)
|
||||
#define PIN_DIRECTION_OUT BIT(5)
|
||||
#define DAC_I2S_MODE_MASK BIT(4)
|
||||
#define DAC_I2S_MODE_SLAVE (0 << 4)
|
||||
#define DAC_I2S_MODE_MASTER BIT(4)
|
||||
|
||||
/* REG04: DAC_INIT_CTRL2 */
|
||||
#define DAC_I2S_LRP_MASK BIT(7)
|
||||
#define DAC_I2S_LRP_NORMAL (0 << 7)
|
||||
#define DAC_I2S_LRP_REVERSAL BIT(7)
|
||||
#define DAC_VDL_MASK (3 << 5)
|
||||
#define DAC_VDL_16BITS (0 << 5)
|
||||
#define DAC_VDL_20BITS BIT(5)
|
||||
#define DAC_VDL_24BITS (2 << 5)
|
||||
#define DAC_VDL_32BITS (3 << 5)
|
||||
#define DAC_MODE_MASK (3 << 3)
|
||||
#define DAC_MODE_RJM (0 << 3)
|
||||
#define DAC_MODE_LJM BIT(3)
|
||||
#define DAC_MODE_I2S (2 << 3)
|
||||
#define DAC_MODE_PCM (3 << 3)
|
||||
#define DAC_LR_SWAP_MASK BIT(2)
|
||||
#define DAC_LR_SWAP_DIS (0 << 2)
|
||||
#define DAC_LR_SWAP_EN BIT(2)
|
||||
|
||||
/* REG05: DAC_INIT_CTRL3 */
|
||||
#define DAC_WL_MASK (3 << 2)
|
||||
#define DAC_WL_16BITS (0 << 2)
|
||||
#define DAC_WL_20BITS BIT(2)
|
||||
#define DAC_WL_24BITS (2 << 2)
|
||||
#define DAC_WL_32BITS (3 << 2)
|
||||
#define DAC_RST_MASK BIT(1)
|
||||
#define DAC_RST_EN (0 << 1)
|
||||
#define DAC_RST_DIS BIT(1)
|
||||
#define DAC_BCP_MASK BIT(0)
|
||||
#define DAC_BCP_NORMAL (0 << 0)
|
||||
#define DAC_BCP_REVERSAL BIT(0)
|
||||
|
||||
/* REG22: DAC_PRECHARGE_CTRL */
|
||||
#define DAC_CHARGE_PRECHARGE BIT(7)
|
||||
#define DAC_CHARGE_DISCHARGE (0 << 7)
|
||||
#define DAC_CHARGE_XCHARGE_MASK BIT(7)
|
||||
#define DAC_CHARGE_CURRENT_64I BIT(6)
|
||||
#define DAC_CHARGE_CURRENT_64I_MASK BIT(6)
|
||||
#define DAC_CHARGE_CURRENT_32I BIT(5)
|
||||
#define DAC_CHARGE_CURRENT_32I_MASK BIT(5)
|
||||
#define DAC_CHARGE_CURRENT_16I BIT(4)
|
||||
#define DAC_CHARGE_CURRENT_16I_MASK BIT(4)
|
||||
#define DAC_CHARGE_CURRENT_08I BIT(3)
|
||||
#define DAC_CHARGE_CURRENT_08I_MASK BIT(3)
|
||||
#define DAC_CHARGE_CURRENT_04I BIT(2)
|
||||
#define DAC_CHARGE_CURRENT_04I_MASK BIT(2)
|
||||
#define DAC_CHARGE_CURRENT_02I BIT(1)
|
||||
#define DAC_CHARGE_CURRENT_02I_MASK BIT(1)
|
||||
#define DAC_CHARGE_CURRENT_I BIT(0)
|
||||
#define DAC_CHARGE_CURRENT_I_MASK BIT(0)
|
||||
#define DAC_CHARGE_CURRENT_ALL_MASK (0x7f)
|
||||
#define DAC_CHARGE_CURRENT_ALL_OFF (0x0)
|
||||
#define DAC_CHARGE_CURRENT_ALL_ON (0x7f)
|
||||
|
||||
/* REG23: DAC_PWR_CTRL */
|
||||
#define DAC_PWR_OFF (0 << 6)
|
||||
#define DAC_PWR_ON BIT(6)
|
||||
#define DAC_PWR_MASK BIT(6)
|
||||
#define DACL_PATH_REFV_OFF (0 << 5)
|
||||
#define DACL_PATH_REFV_ON BIT(5)
|
||||
#define DACL_PATH_REFV_MASK BIT(5)
|
||||
#define HPOUTL_ZERO_CROSSING_OFF (0 << 4)
|
||||
#define HPOUTL_ZERO_CROSSING_ON BIT(4)
|
||||
#define DACR_PATH_REFV_OFF (0 << 1)
|
||||
#define DACR_PATH_REFV_ON BIT(1)
|
||||
#define DACR_PATH_REFV_MASK BIT(1)
|
||||
#define HPOUTR_ZERO_CROSSING_OFF (0 << 0)
|
||||
#define HPOUTR_ZERO_CROSSING_ON BIT(0)
|
||||
|
||||
/* REG24: DAC_CLK_CTRL */
|
||||
#define DACL_REFV_OFF (0 << 7)
|
||||
#define DACL_REFV_ON BIT(7)
|
||||
#define DACL_REFV_MASK BIT(7)
|
||||
#define DACL_CLK_OFF (0 << 6)
|
||||
#define DACL_CLK_ON BIT(6)
|
||||
#define DACL_CLK_MASK BIT(6)
|
||||
#define DACL_OFF (0 << 5)
|
||||
#define DACL_ON BIT(5)
|
||||
#define DACL_MASK BIT(5)
|
||||
#define DACL_INIT_OFF (0 << 4)
|
||||
#define DACL_INIT_ON BIT(4)
|
||||
#define DACL_INIT_MASK BIT(4)
|
||||
#define DACR_REFV_OFF (0 << 3)
|
||||
#define DACR_REFV_ON BIT(3)
|
||||
#define DACR_REFV_MASK BIT(3)
|
||||
#define DACR_CLK_OFF (0 << 2)
|
||||
#define DACR_CLK_ON BIT(2)
|
||||
#define DACR_CLK_MASK BIT(2)
|
||||
#define DACR_OFF (0 << 1)
|
||||
#define DACR_ON BIT(1)
|
||||
#define DACR_MASK BIT(1)
|
||||
#define DACR_INIT_OFF (0 << 0)
|
||||
#define DACR_INIT_ON BIT(0)
|
||||
#define DACR_INIT_MASK BIT(0)
|
||||
|
||||
/* REG25: HPMIX_CTRL*/
|
||||
#define HPMIXL_DIS (0 << 6)
|
||||
#define HPMIXL_EN BIT(6)
|
||||
#define HPMIXL_MASK BIT(6)
|
||||
#define HPMIXL_INIT_DIS (0 << 5)
|
||||
#define HPMIXL_INIT_EN BIT(5)
|
||||
#define HPMIXL_INIT_MASK BIT(5)
|
||||
#define HPMIXL_INIT2_DIS (0 << 4)
|
||||
#define HPMIXL_INIT2_EN BIT(4)
|
||||
#define HPMIXL_INIT2_MASK BIT(4)
|
||||
#define HPMIXR_DIS (0 << 2)
|
||||
#define HPMIXR_EN BIT(2)
|
||||
#define HPMIXR_MASK BIT(2)
|
||||
#define HPMIXR_INIT_DIS (0 << 1)
|
||||
#define HPMIXR_INIT_EN BIT(1)
|
||||
#define HPMIXR_INIT_MASK BIT(1)
|
||||
#define HPMIXR_INIT2_DIS (0 << 0)
|
||||
#define HPMIXR_INIT2_EN BIT(0)
|
||||
#define HPMIXR_INIT2_MASK BIT(0)
|
||||
|
||||
/* REG26: DAC_SELECT */
|
||||
#define DACL_SELECT BIT(4)
|
||||
#define DACL_SELECT_MASK BIT(4)
|
||||
#define DACL_UNSELECT (0 << 4)
|
||||
#define DACR_SELECT BIT(0)
|
||||
#define DACR_SELECT_MASK BIT(0)
|
||||
#define DACR_UNSELECT (0 << 0)
|
||||
|
||||
/* REG27: HPOUT_CTRL */
|
||||
#define HPOUTL_DIS (0 << 7)
|
||||
#define HPOUTL_EN BIT(7)
|
||||
#define HPOUTL_MASK BIT(7)
|
||||
#define HPOUTL_INIT_DIS (0 << 6)
|
||||
#define HPOUTL_INIT_EN BIT(6)
|
||||
#define HPOUTL_INIT_MASK BIT(6)
|
||||
#define HPOUTL_MUTE (0 << 5)
|
||||
#define HPOUTL_UNMUTE BIT(5)
|
||||
#define HPOUTL_MUTE_MASK BIT(5)
|
||||
#define HPOUTR_DIS (0 << 4)
|
||||
#define HPOUTR_EN BIT(4)
|
||||
#define HPOUTR_MASK BIT(4)
|
||||
#define HPOUTR_INIT_DIS (0 << 3)
|
||||
#define HPOUTR_INIT_EN BIT(3)
|
||||
#define HPOUTR_INIT_MASK BIT(3)
|
||||
#define HPOUTR_MUTE (0 << 2)
|
||||
#define HPOUTR_UNMUTE BIT(2)
|
||||
#define HPOUTR_MUTE_MASK BIT(2)
|
||||
|
||||
/* REG28: HPOUTL_GAIN_CTRL */
|
||||
#define HPOUTL_GAIN_MASK (0X1f << 0)
|
||||
|
||||
/* REG29: HPOUTR_GAIN_CTRL */
|
||||
#define HPOUTR_GAIN_MASK (0X1f << 0)
|
||||
|
||||
/* REG2a: HPOUT_POP_CTRL */
|
||||
#define HPOUTR_POP_XCHARGE BIT(4)
|
||||
#define HPOUTR_POP_WORK (2 << 4)
|
||||
#define HPOUTR_POP_MASK (3 << 4)
|
||||
#define HPOUTL_POP_XCHARGE BIT(0)
|
||||
#define HPOUTL_POP_WORK (2 << 0)
|
||||
#define HPOUTL_POP_MASK (3 << 0)
|
||||
|
||||
#define RK3328_HIFI (0)
|
||||
|
||||
struct rk3328_reg_msk_val {
|
||||
unsigned int reg;
|
||||
unsigned int msk;
|
||||
unsigned int val;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user