579 lines
16 KiB
C
579 lines
16 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
//
|
|
// rxk1000_codec.c -- rk1000 ALSA Soc Audio driver
|
|
//
|
|
// Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
|
|
|
|
#include <linux/delay.h>
|
|
#include <linux/gpio/consumer.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/module.h>
|
|
#include <sound/core.h>
|
|
#include <sound/pcm.h>
|
|
#include <sound/pcm_params.h>
|
|
#include <sound/soc.h>
|
|
#include <sound/soc-dapm.h>
|
|
#include "rk1000_codec.h"
|
|
|
|
#define FREQ441KHZ (0x11 << 1)
|
|
/* rk1000 output volume, DAC Digital Gain */
|
|
/* 0x0000 ~ 0xF42 */
|
|
#define VOLUME_OUTPUT 0xF42
|
|
/* 0x0 ~ 0x3f(bit0-bit5) max=0x0(+6DB) min=0x3f(-60DB) Analog Gain */
|
|
#define VOLUME_CODEC_PA 0x0
|
|
|
|
/* rk1000 input volume, rk610 can not adjust the recording volume */
|
|
#define VOLUME_INPUT 0x07
|
|
|
|
#define OUT_CAPLESS (1)
|
|
|
|
static const struct reg_default rk1000_codec_reg[] = {
|
|
{ 0x00, 0x05 },
|
|
{ 0x01, 0x04 },
|
|
{ 0x02, 0xfd },
|
|
{ 0x03, 0xf3 },
|
|
{ 0x04, 0x03 },
|
|
{ 0x05, 0x00 },
|
|
{ 0x06, 0x00 },
|
|
{ 0x07, 0x00 },
|
|
{ 0x08, 0x00 },
|
|
{ 0x09, 0x05 },
|
|
{ 0x0a, 0x00 },
|
|
{ 0x0b, 0x00 },
|
|
{ 0x0c, 0x97 },
|
|
{ 0x0d, 0x97 },
|
|
{ 0x0e, 0x97 },
|
|
{ 0x0f, 0x97 },
|
|
{ 0x10, 0x97 },
|
|
{ 0x11, 0x97 },
|
|
{ 0x12, 0xcc },
|
|
{ 0x13, 0x00 },
|
|
{ 0x14, 0x00 },
|
|
{ 0x15, 0xf1 },
|
|
{ 0x16, 0x90 },
|
|
{ 0x17, 0xff },
|
|
{ 0x18, 0xff },
|
|
{ 0x19, 0xff },
|
|
{ 0x1a, 0x9c },
|
|
{ 0x1b, 0x00 },
|
|
{ 0x1c, 0x00 },
|
|
{ 0x1d, 0xff },
|
|
{ 0x1e, 0xff },
|
|
{ 0x1f, 0xff },
|
|
};
|
|
|
|
struct rk1000_codec_priv {
|
|
struct regmap *regmap;
|
|
struct regmap *ctlmap;
|
|
struct snd_soc_component *component;
|
|
struct delayed_work pa_delayed_work;
|
|
struct gpio_desc *spk_en_gpio;
|
|
/*
|
|
* Some amplifiers enable a longer time.
|
|
* config after pa_enable_io delay pa_enable_time(ms)
|
|
* so value range is 0 - 8000.
|
|
*/
|
|
unsigned int pa_enable_time;
|
|
};
|
|
|
|
static void spk_ctrl_fun(struct snd_soc_component *component, int status)
|
|
{
|
|
struct rk1000_codec_priv *rk1000_codec;
|
|
|
|
rk1000_codec = snd_soc_component_get_drvdata(component);
|
|
|
|
if (rk1000_codec->spk_en_gpio)
|
|
gpiod_set_value(rk1000_codec->spk_en_gpio, status);
|
|
}
|
|
|
|
static int rk1000_codec_set_bias_level(struct snd_soc_component *component,
|
|
enum snd_soc_bias_level level)
|
|
{
|
|
switch (level) {
|
|
case SND_SOC_BIAS_ON:
|
|
break;
|
|
|
|
case SND_SOC_BIAS_PREPARE:
|
|
snd_soc_component_write(component, ACCELCODEC_R1D, 0x2a);
|
|
snd_soc_component_write(component, ACCELCODEC_R1E, 0x40);
|
|
snd_soc_component_write(component, ACCELCODEC_R1F, 0x49);
|
|
break;
|
|
|
|
case SND_SOC_BIAS_STANDBY:
|
|
snd_soc_component_write(component, ACCELCODEC_R1D, 0x2a);
|
|
snd_soc_component_write(component, ACCELCODEC_R1E, 0x40);
|
|
snd_soc_component_write(component, ACCELCODEC_R1F, 0x49);
|
|
break;
|
|
|
|
case SND_SOC_BIAS_OFF:
|
|
spk_ctrl_fun(component, GPIO_LOW);
|
|
snd_soc_component_write(component, ACCELCODEC_R1D, 0xFF);
|
|
snd_soc_component_write(component, ACCELCODEC_R1E, 0xFF);
|
|
snd_soc_component_write(component, ACCELCODEC_R1F, 0xFF);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int rk1000_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
|
unsigned int fmt)
|
|
{
|
|
struct snd_soc_component *component = codec_dai->component;
|
|
struct rk1000_codec_priv *rk1000_codec;
|
|
u16 iface = 0;
|
|
|
|
rk1000_codec = snd_soc_component_get_drvdata(component);
|
|
/* setup Vmid and Vref, other module power down */
|
|
snd_soc_component_write(component, ACCELCODEC_R1D, 0x2a);
|
|
snd_soc_component_write(component, ACCELCODEC_R1E, 0x40);
|
|
/* set master/slave audio interface */
|
|
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
|
case SND_SOC_DAIFMT_CBM_CFM:
|
|
iface = 0x0040;
|
|
break;
|
|
|
|
case SND_SOC_DAIFMT_CBS_CFS:
|
|
iface = 0x0000;
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
/* interface format */
|
|
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
|
case SND_SOC_DAIFMT_I2S:
|
|
iface |= 0x0002;
|
|
break;
|
|
|
|
case SND_SOC_DAIFMT_RIGHT_J:
|
|
break;
|
|
|
|
case SND_SOC_DAIFMT_LEFT_J:
|
|
iface |= 0x0001;
|
|
break;
|
|
|
|
case SND_SOC_DAIFMT_DSP_A:
|
|
iface |= 0x0003;
|
|
break;
|
|
|
|
case SND_SOC_DAIFMT_DSP_B:
|
|
iface |= 0x0013;
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* clock inversion */
|
|
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
|
case SND_SOC_DAIFMT_NB_NF:
|
|
break;
|
|
|
|
case SND_SOC_DAIFMT_IB_IF:
|
|
iface |= 0x0090;
|
|
break;
|
|
|
|
case SND_SOC_DAIFMT_IB_NF:
|
|
iface |= 0x0080;
|
|
break;
|
|
|
|
case SND_SOC_DAIFMT_NB_IF:
|
|
iface |= 0x0010;
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
snd_soc_component_write(component, ACCELCODEC_R09, iface);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rk1000_codec_pcm_hw_params(struct snd_pcm_substream *substream,
|
|
struct snd_pcm_hw_params *params,
|
|
struct snd_soc_dai *dai)
|
|
{
|
|
u32 iface;
|
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
struct snd_soc_component *component = dai->component;
|
|
unsigned int dai_fmt;
|
|
|
|
dai_fmt = rtd->card->dai_link[0].dai_fmt;
|
|
iface = snd_soc_component_read(component, ACCELCODEC_R09) & 0x1f3;
|
|
snd_soc_component_write(component, ACCELCODEC_R0C, 0x17);
|
|
snd_soc_component_write(component, ACCELCODEC_R04,
|
|
ASC_INT_MUTE_L | ASC_INT_MUTE_R |
|
|
ASC_SIDETONE_L_OFF | ASC_SIDETONE_R_OFF);
|
|
snd_soc_component_write(component, ACCELCODEC_R0B,
|
|
ASC_DEC_DISABLE | ASC_INT_DISABLE);
|
|
|
|
if ((dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFM)
|
|
iface |= ASC_INVERT_BCLK;
|
|
snd_soc_component_write(component, ACCELCODEC_R09, iface);
|
|
snd_soc_component_write(component, ACCELCODEC_R0A, 0xa0);
|
|
snd_soc_component_write(component, ACCELCODEC_R0B, ASC_DEC_ENABLE | ASC_INT_ENABLE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rk1000_codec_mute(struct snd_soc_dai *dai, int mute, int stream)
|
|
{
|
|
struct snd_soc_component *component = dai->component;
|
|
struct rk1000_codec_priv *rk1000_codec;
|
|
|
|
rk1000_codec = snd_soc_component_get_drvdata(component);
|
|
if (mute) {
|
|
/* AOL */
|
|
snd_soc_component_write(component, ACCELCODEC_R17, 0xFF);
|
|
/* AOR */
|
|
snd_soc_component_write(component, ACCELCODEC_R18, 0xFF);
|
|
/* AOM */
|
|
snd_soc_component_write(component, ACCELCODEC_R19, 0xFF);
|
|
/* soft mute */
|
|
snd_soc_component_write(component, ACCELCODEC_R04,
|
|
ASC_INT_MUTE_L | ASC_INT_MUTE_R |
|
|
ASC_SIDETONE_L_OFF | ASC_SIDETONE_R_OFF);
|
|
} else {
|
|
/* setup Vmid and Vref, other module power down */
|
|
snd_soc_component_write(component, ACCELCODEC_R1D, 0x2a);
|
|
snd_soc_component_write(component, ACCELCODEC_R1E, 0x40);
|
|
/* AOL */
|
|
snd_soc_component_write(component, ACCELCODEC_R17,
|
|
VOLUME_CODEC_PA | ASC_OUTPUT_ACTIVE |
|
|
ASC_CROSSZERO_EN);
|
|
/* AOR */
|
|
snd_soc_component_write(component, ACCELCODEC_R18,
|
|
VOLUME_CODEC_PA | ASC_OUTPUT_ACTIVE |
|
|
ASC_CROSSZERO_EN);
|
|
snd_soc_component_write(component, ACCELCODEC_R04,
|
|
ASC_INT_ACTIVE_L | ASC_INT_ACTIVE_R |
|
|
ASC_SIDETONE_L_OFF | ASC_SIDETONE_R_OFF);
|
|
/* AOM */
|
|
snd_soc_component_write(component, ACCELCODEC_R19, 0x7F);
|
|
#if OUT_CAPLESS
|
|
snd_soc_component_write(component, ACCELCODEC_R1F,
|
|
0x09 | ASC_PDMIXM_ENABLE);
|
|
#else
|
|
snd_soc_component_write(component, ACCELCODEC_R1F,
|
|
0x09 | ASC_PDMIXM_ENABLE | ASC_PDPAM_ENABLE);
|
|
#endif
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void pa_delayedwork(struct work_struct *work)
|
|
{
|
|
struct rk1000_codec_priv *priv = container_of(work,
|
|
struct rk1000_codec_priv,
|
|
pa_delayed_work.work);
|
|
struct snd_soc_component *component = priv->component;
|
|
|
|
spk_ctrl_fun(component, GPIO_HIGH);
|
|
}
|
|
|
|
static struct snd_soc_dai_ops rk1000_codec_ops = {
|
|
.hw_params = rk1000_codec_pcm_hw_params,
|
|
.set_fmt = rk1000_codec_set_dai_fmt,
|
|
.mute_stream = rk1000_codec_mute,
|
|
.no_capture_mute = 1,
|
|
};
|
|
|
|
#define RK1000_CODEC_RATES SNDRV_PCM_RATE_8000_192000
|
|
#define RK1000_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
|
|
SNDRV_PCM_FMTBIT_S20_3LE | \
|
|
SNDRV_PCM_FMTBIT_S24_LE)
|
|
|
|
static struct snd_soc_dai_driver rk1000_codec_dai[] = {
|
|
{
|
|
.name = "rk1000_codec",
|
|
.playback = {
|
|
.stream_name = "Playback",
|
|
.channels_min = 2,
|
|
.channels_max = 8,
|
|
.rates = RK1000_CODEC_RATES,
|
|
.formats = RK1000_CODEC_FORMATS,
|
|
},
|
|
.capture = {
|
|
.stream_name = "Capture",
|
|
.channels_min = 2,
|
|
.channels_max = 2,
|
|
.rates = RK1000_CODEC_RATES,
|
|
.formats = RK1000_CODEC_FORMATS,
|
|
},
|
|
.ops = &rk1000_codec_ops,
|
|
.symmetric_rate = 1,
|
|
}
|
|
};
|
|
|
|
static void rk1000_codec_reg_init(struct snd_soc_component *component)
|
|
{
|
|
struct rk1000_codec_priv *rk1000_codec;
|
|
unsigned int digital_gain;
|
|
unsigned int mic_vol;
|
|
int ret;
|
|
|
|
mic_vol = VOLUME_INPUT;
|
|
rk1000_codec = snd_soc_component_get_drvdata(component);
|
|
ret = snd_soc_component_write(component, ACCELCODEC_R1D, 0x30);
|
|
snd_soc_component_write(component, ACCELCODEC_R1E, 0x40);
|
|
/*Route R-LPF->R-Mixer, L-LPF->L-Mixer*/
|
|
snd_soc_component_write(component, ACCELCODEC_R15, 0xC1);
|
|
/*With Cap Output, VMID ramp up slow*/
|
|
snd_soc_component_write(component, ACCELCODEC_R1A, 0x14);
|
|
mdelay(10);
|
|
snd_soc_component_write(component, ACCELCODEC_R0C, 0x10 | ASC_INPUT_VOL_0DB);
|
|
snd_soc_component_write(component, ACCELCODEC_R0D, 0x10 | ASC_INPUT_VOL_0DB);
|
|
if (mic_vol > 0x07) {
|
|
/*Select MIC input*/
|
|
snd_soc_component_write(component, ACCELCODEC_R12,
|
|
0x4c | ASC_MIC_INPUT | ASC_MIC_BOOST_20DB);
|
|
mic_vol -= 0x07;
|
|
} else {
|
|
/*Select MIC input*/
|
|
snd_soc_component_write(component, ACCELCODEC_R12, 0x4c | ASC_MIC_INPUT);
|
|
}
|
|
/*use default value*/
|
|
snd_soc_component_write(component, ACCELCODEC_R1C, ASC_DEM_ENABLE);
|
|
snd_soc_component_write(component, ACCELCODEC_R0E, 0x10 | mic_vol);
|
|
/* disable route PGA->R/L Mixer, PGA gain 0db. */
|
|
snd_soc_component_write(component, ACCELCODEC_R13, 0x05 | 0 << 3);
|
|
snd_soc_component_write(component, ACCELCODEC_R14, 0x05 | 0 << 3);
|
|
/*2soft mute*/
|
|
snd_soc_component_write(component, ACCELCODEC_R04,
|
|
ASC_INT_MUTE_L | ASC_INT_MUTE_R |
|
|
ASC_SIDETONE_L_OFF | ASC_SIDETONE_R_OFF);
|
|
/*2set default SR and clk*/
|
|
snd_soc_component_write(component, ACCELCODEC_R0A, FREQ441KHZ | ASC_NORMAL_MODE |
|
|
(0x10 << 1) | ASC_CLKNODIV | ASC_CLK_ENABLE);
|
|
/*2Config audio interface*/
|
|
snd_soc_component_write(component, ACCELCODEC_R09, ASC_I2S_MODE |
|
|
ASC_16BIT_MODE | ASC_NORMAL_LRCLK |
|
|
ASC_LRSWAP_DISABLE | ASC_NORMAL_BCLK);
|
|
snd_soc_component_write(component, ACCELCODEC_R00, ASC_HPF_ENABLE |
|
|
ASC_DSM_MODE_ENABLE | ASC_SCRAMBLE_ENABLE |
|
|
ASC_DITHER_ENABLE | ASC_BCLKDIV_4);
|
|
/*2volume,input,output*/
|
|
digital_gain = VOLUME_OUTPUT;
|
|
if (snd_soc_component_read(component, ACCELCODEC_R05) != 0x0f) {
|
|
snd_soc_component_write(component, ACCELCODEC_R05,
|
|
(digital_gain >> 8) & 0xFF);
|
|
snd_soc_component_write(component, ACCELCODEC_R06, digital_gain & 0xFF);
|
|
}
|
|
|
|
if (snd_soc_component_read(component, ACCELCODEC_R07) != 0x0f) {
|
|
snd_soc_component_write(component, ACCELCODEC_R07,
|
|
(digital_gain >> 8) & 0xFF);
|
|
snd_soc_component_write(component, ACCELCODEC_R08, digital_gain & 0xFF);
|
|
}
|
|
|
|
snd_soc_component_write(component, ACCELCODEC_R0B,
|
|
ASC_DEC_ENABLE | ASC_INT_ENABLE);
|
|
|
|
#if OUT_CAPLESS
|
|
snd_soc_component_write(component, ACCELCODEC_R1F,
|
|
0x09 | ASC_PDMIXM_ENABLE);
|
|
#else
|
|
snd_soc_component_write(component, ACCELCODEC_R1F, 0x09 |
|
|
ASC_PDMIXM_ENABLE | ASC_PDPAM_ENABLE);
|
|
#endif
|
|
|
|
snd_soc_component_write(component, ACCELCODEC_R17, VOLUME_CODEC_PA |
|
|
ASC_OUTPUT_ACTIVE | ASC_CROSSZERO_EN);
|
|
snd_soc_component_write(component, ACCELCODEC_R18, VOLUME_CODEC_PA |
|
|
ASC_OUTPUT_ACTIVE | ASC_CROSSZERO_EN);
|
|
snd_soc_component_write(component, ACCELCODEC_R04, ASC_INT_ACTIVE_L |
|
|
ASC_INT_ACTIVE_R | ASC_SIDETONE_L_OFF |
|
|
ASC_SIDETONE_R_OFF);
|
|
snd_soc_component_write(component, ACCELCODEC_R19, 0x7F);
|
|
}
|
|
|
|
static int rk1000_codec_suspend(struct snd_soc_component *component)
|
|
{
|
|
spk_ctrl_fun(component, GPIO_LOW);
|
|
rk1000_codec_set_bias_level(component, SND_SOC_BIAS_OFF);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rk1000_codec_resume(struct snd_soc_component *component)
|
|
{
|
|
rk1000_codec_set_bias_level(component, SND_SOC_BIAS_PREPARE);
|
|
spk_ctrl_fun(component, GPIO_HIGH);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rk1000_codec_probe(struct snd_soc_component *component)
|
|
{
|
|
struct rk1000_codec_priv *rk1000_codec;
|
|
|
|
rk1000_codec = snd_soc_component_get_drvdata(component);
|
|
rk1000_codec->component = component;
|
|
|
|
INIT_DELAYED_WORK(&rk1000_codec->pa_delayed_work,
|
|
pa_delayedwork);
|
|
|
|
rk1000_codec_set_bias_level(component, SND_SOC_BIAS_PREPARE);
|
|
schedule_delayed_work(&rk1000_codec->pa_delayed_work,
|
|
msecs_to_jiffies(rk1000_codec->pa_enable_time));
|
|
rk1000_codec_reg_init(component);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rk1000_codec_remove(struct snd_soc_component *component)
|
|
{
|
|
rk1000_codec_set_bias_level(component, SND_SOC_BIAS_OFF);
|
|
}
|
|
|
|
static const struct snd_soc_component_driver soc_codec_dev_rk1000_codec = {
|
|
.probe = rk1000_codec_probe,
|
|
.remove = rk1000_codec_remove,
|
|
.suspend = rk1000_codec_suspend,
|
|
.resume = rk1000_codec_resume,
|
|
.set_bias_level = rk1000_codec_set_bias_level,
|
|
};
|
|
|
|
static int rk1000_reg_write(void *context, unsigned int reg,
|
|
unsigned int value)
|
|
{
|
|
struct i2c_client *i2c = context;
|
|
struct i2c_msg msg;
|
|
u8 buf;
|
|
int ret;
|
|
|
|
buf = value;
|
|
msg.addr = i2c->addr | reg;
|
|
msg.flags = i2c->flags & I2C_M_TEN;
|
|
msg.len = 1;
|
|
msg.buf = &buf;
|
|
|
|
ret = i2c_transfer(i2c->adapter, &msg, 1);
|
|
|
|
return (ret == 1) ? 0 : ret;
|
|
}
|
|
|
|
static int rk1000_reg_read(void *context, unsigned int reg,
|
|
unsigned int *value)
|
|
{
|
|
struct i2c_client *i2c = context;
|
|
struct i2c_msg msg;
|
|
u8 buf;
|
|
int ret;
|
|
|
|
msg.addr = i2c->addr | reg;
|
|
msg.flags = I2C_M_RD;
|
|
msg.len = 1;
|
|
msg.buf = &buf;
|
|
|
|
ret = i2c_transfer(i2c->adapter, &msg, 1);
|
|
if (ret != 1)
|
|
return ret;
|
|
|
|
*value = buf;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct regmap_config rk1000_codec_regmap = {
|
|
.reg_bits = 8,
|
|
.val_bits = 8,
|
|
.reg_write = rk1000_reg_write,
|
|
.reg_read = rk1000_reg_read,
|
|
.max_register = ACCELCODEC_R1F,
|
|
.cache_type = REGCACHE_FLAT,
|
|
.reg_defaults = rk1000_codec_reg,
|
|
.num_reg_defaults = ARRAY_SIZE(rk1000_codec_reg),
|
|
};
|
|
|
|
static const struct regmap_config rk1000_ctl_regmap = {
|
|
.reg_bits = 8,
|
|
.val_bits = 8,
|
|
.max_register = CODEC_CON,
|
|
.cache_type = REGCACHE_FLAT,
|
|
};
|
|
|
|
static int rk1000_codec_i2c_probe(struct i2c_client *i2c,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
struct rk1000_codec_priv *rk1000;
|
|
struct device_node *np = i2c->dev.of_node;
|
|
struct device_node *ctl;
|
|
struct i2c_client *ctl_client;
|
|
|
|
rk1000 = devm_kzalloc(&i2c->dev, sizeof(*rk1000), GFP_KERNEL);
|
|
if (!rk1000)
|
|
return -ENOMEM;
|
|
|
|
i2c_set_clientdata(i2c, rk1000);
|
|
|
|
of_property_read_u32(np, "rockchip,pa-en-time-ms",
|
|
&rk1000->pa_enable_time);
|
|
|
|
rk1000->spk_en_gpio = devm_gpiod_get_optional(&i2c->dev, "rockchip,spk-en",
|
|
GPIOD_OUT_LOW);
|
|
if (IS_ERR(rk1000->spk_en_gpio))
|
|
return PTR_ERR(rk1000->spk_en_gpio);
|
|
|
|
ctl = of_parse_phandle(np, "rockchip,ctl", 0);
|
|
if (!ctl)
|
|
return -EINVAL;
|
|
|
|
ctl_client = of_find_i2c_device_by_node(ctl);
|
|
if (!ctl_client) {
|
|
dev_err(&i2c->dev, "can't find control client\n");
|
|
return -EPROBE_DEFER;
|
|
}
|
|
|
|
rk1000->regmap = devm_regmap_init(&i2c->dev, NULL,
|
|
i2c, &rk1000_codec_regmap);
|
|
if (IS_ERR(rk1000->regmap))
|
|
return PTR_ERR(rk1000->regmap);
|
|
|
|
rk1000->ctlmap = devm_regmap_init_i2c(ctl_client,
|
|
&rk1000_ctl_regmap);
|
|
if (IS_ERR(rk1000->ctlmap))
|
|
return PTR_ERR(rk1000->ctlmap);
|
|
|
|
regmap_write(rk1000->ctlmap, CODEC_CON, CODEC_ON);
|
|
|
|
return devm_snd_soc_register_component(&i2c->dev, &soc_codec_dev_rk1000_codec,
|
|
rk1000_codec_dai,
|
|
ARRAY_SIZE(rk1000_codec_dai));
|
|
}
|
|
|
|
static void rk1000_codec_i2c_remove(struct i2c_client *i2c)
|
|
{
|
|
struct rk1000_codec_priv *rk1000 = i2c_get_clientdata(i2c);
|
|
|
|
regmap_write(rk1000->ctlmap, CODEC_CON, CODEC_OFF);
|
|
}
|
|
|
|
static const struct i2c_device_id rk1000_codec_i2c_id[] = {
|
|
{ "rk1000_codec", 0 },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(i2c, rk1000_codec_i2c_id);
|
|
|
|
static const struct of_device_id rk1000_codec_of_match[] = {
|
|
{ .compatible = "rockchip,rk1000-codec", },
|
|
{},
|
|
};
|
|
|
|
static struct i2c_driver rk1000_codec_i2c_driver = {
|
|
.driver = {
|
|
.name = "rk1000_codec",
|
|
.of_match_table = of_match_ptr(rk1000_codec_of_match),
|
|
},
|
|
.probe = rk1000_codec_i2c_probe,
|
|
.remove = rk1000_codec_i2c_remove,
|
|
.id_table = rk1000_codec_i2c_id,
|
|
};
|
|
module_i2c_driver(rk1000_codec_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("Rockchip RK1000 CODEC driver");
|
|
MODULE_AUTHOR("Sugar Zhang <sugar.zhang@rock-chips.com>");
|
|
MODULE_LICENSE("GPL v2");
|