android13/kernel-5.10/sound/soc/codecs/rk730.c

972 lines
29 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* rk730.c -- RK730 ALSA SoC Audio driver
*
* Copyright (C) 2022 Rockchip Electronics Co.,Ltd
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/clk.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include "rk730.h"
enum rk730_mix_mode {
RK730_MIX_MODE_1_PATH,
RK730_MIX_MODE_2_PATHS,
RK730_MIX_MODE_3_PATHS,
};
enum rk730_chop_freq {
RK730_CHOP_FREQ_NONE,
RK730_CHOP_FREQ_200KHZ,
RK730_CHOP_FREQ_400KHZ,
RK730_CHOP_FREQ_800KHZ,
};
struct rk730_priv {
struct regmap *regmap;
struct clk *mclk;
atomic_t mix_mode;
};
/* ADC Digital Volume */
static const DECLARE_TLV_DB_SCALE(adc_dig_tlv, -95625, 375, 0);
/* DAC Digital Volume */
static const DECLARE_TLV_DB_SCALE(dac_dig_tlv, -95625, 375, 0);
/* D2S Volume */
static const DECLARE_TLV_DB_SCALE(d2s_tlv, -1800, 300, 0);
/* ADC Volume */
static const DECLARE_TLV_DB_SCALE(adc_tlv, -1200, 300, 0);
/* MUX Volume */
static const DECLARE_TLV_DB_SCALE(mux_tlv, -600, 600, 0);
/* MIX Buf Volume */
static const DECLARE_TLV_DB_SCALE(mix_buf_tlv, -1800, 300, 0);
/* HP Volume */
static const DECLARE_TLV_DB_SCALE(hp_tlv, 0, 300, 0);
/* LINEOUT Volume */
static const DECLARE_TLV_DB_SCALE(lineout_tlv, 0, 300, 0);
/* MIC Boost Volume */
static const DECLARE_TLV_DB_RANGE(micboost_tlv,
0, 2, TLV_DB_SCALE_ITEM(0, 600, 0),
3, 4, TLV_DB_SCALE_ITEM(2400, 1200, 0),
5, 7, TLV_DB_SCALE_ITEM(4200, 600, 0),
8, 8, TLV_DB_SCALE_ITEM(-300, 0, 0),
16, 16, TLV_DB_SCALE_ITEM(-600, 0, 0),
24, 24, TLV_DB_SCALE_ITEM(-900, 0, 0)
);
static const char * const mux_out_l_text[] = { "DIFF", "MIC1N", "MIC2N" };
static const char * const mux_out_r_text[] = { "DIFF", "MIC2P", "MIC1P" };
static const char * const mux_input_l_text[] = { "DIFF", "VINP1", "VINN1" };
static const char * const mux_input_r_text[] = { "DIFF", "VINP2", "VINN2" };
static SOC_ENUM_SINGLE_DECL(mux_out_l_enum, RK730_MUXER_0, 2, mux_out_l_text);
static SOC_ENUM_SINGLE_DECL(mux_out_r_enum, RK730_MUXER_0, 6, mux_out_r_text);
static SOC_ENUM_SINGLE_DECL(mux_input_l_enum, RK730_ADC_PGA_BLOCK_0,
4, mux_input_l_text);
static SOC_ENUM_SINGLE_DECL(mux_input_r_enum, RK730_ADC_PGA_BLOCK_1,
4, mux_input_r_text);
static const struct snd_kcontrol_new mux_out_l =
SOC_DAPM_ENUM("Left Out Mux", mux_out_l_enum);
static const struct snd_kcontrol_new mux_out_r =
SOC_DAPM_ENUM("Right Out Mux", mux_out_r_enum);
static const struct snd_kcontrol_new mux_input_l =
SOC_DAPM_ENUM("Left Input Mux", mux_input_l_enum);
static const struct snd_kcontrol_new mux_input_r =
SOC_DAPM_ENUM("Right Input Mux", mux_input_r_enum);
static const struct snd_kcontrol_new mix_ctls[] = {
SOC_DAPM_SINGLE("Left Out Mux Switch", RK730_MUXER_0, 0, 1, 1),
SOC_DAPM_SINGLE("Right Out Mux Switch", RK730_MUXER_0, 4, 1, 1),
};
static const char * const adc_hpf_cutoff_text[] = {
"3.79Hz", "60Hz", "243Hz", "493Hz",
};
static const char * const dac_hpf_cutoff_text[] = {
"80Hz", "100Hz", "120Hz", "140Hz",
};
static SOC_ENUM_SINGLE_DECL(adc_hpf_cutoff_enum, RK730_DADC_HPF,
4, adc_hpf_cutoff_text);
static SOC_ENUM_SINGLE_DECL(dac_hpf_cutoff_enum, RK730_DDAC_MUTE_MIXCTL,
5, dac_hpf_cutoff_text);
static const char * const chop_freq_text[] = {
"Disabled", "200kHz", "400kHz", "800kHz",
};
static SOC_ENUM_SINGLE_DECL(dac_ref_buf_chop_freq_enum, RK730_HK_TOP_1,
6, chop_freq_text);
static SOC_ENUM_SINGLE_DECL(mic_chop_freq_enum, RK730_MIC_BOOST_3,
6, chop_freq_text);
static SOC_ENUM_SINGLE_DECL(adc_pga_chop_freq_enum, RK730_ADC_PGA_BLOCK_1,
6, chop_freq_text);
static SOC_ENUM_SINGLE_DECL(mux_out_chop_freq_enum, RK730_MUXER_1,
0, chop_freq_text);
static SOC_ENUM_SINGLE_DECL(mix_chop_freq_enum, RK730_MIXER_2,
6, chop_freq_text);
static SOC_ENUM_SINGLE_DECL(hp_lo_chop_freq_enum, RK730_HP_1,
5, chop_freq_text);
static const char * const micbias_volt_text[] = {
"2.0v", "2.2v", "2.5v", "2.8v",
};
static const char * const charge_pump_volt_text[] = {
"2.1v", "2.3v", "2.5v", "2.7v",
};
static SOC_ENUM_SINGLE_DECL(micbias_volt_enum, RK730_MIC_BIAS,
2, micbias_volt_text);
static SOC_ENUM_SINGLE_DECL(charge_pump_volt_enum, RK730_CHARGE_PUMP,
1, charge_pump_volt_text);
static int rk730_adc_vol_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int val = snd_soc_component_read(component, mc->reg);
unsigned int sign = snd_soc_component_read(component, RK730_DADC_SR_ACL);
unsigned int mask = (1 << fls(mc->max)) - 1;
unsigned int shift = mc->shift;
int mid = mc->max / 2;
int uv;
uv = (val >> shift) & mask;
sign &= RK730_DADC_SR_ACL_VOLL_POL_MASK;
if (sign)
uv = mid + uv;
else
uv = mid - uv;
ucontrol->value.integer.value[0] = uv;
ucontrol->value.integer.value[1] = uv;
return 0;
}
static int rk730_adc_vol_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int rreg = mc->rreg;
unsigned int shift = mc->shift;
unsigned int mask = (1 << fls(mc->max)) - 1;
unsigned int val, val_mask, sign, sign_mask;
int uv = ucontrol->value.integer.value[0];
int min = mc->min;
int mid = mc->max / 2;
sign_mask = RK730_DADC_SR_ACL_VOLL_POL_MASK | RK730_DADC_SR_ACL_VOLR_POL_MASK;
if (uv > mid) {
sign = RK730_DADC_SR_ACL_VOLL_POS | RK730_DADC_SR_ACL_VOLR_POS;
uv = uv - mid;
} else {
sign = RK730_DADC_SR_ACL_VOLL_NEG | RK730_DADC_SR_ACL_VOLR_NEG;
uv = mid - uv;
}
val = ((uv + min) & mask);
val_mask = mask << shift;
val = val << shift;
snd_soc_component_update_bits(component, reg, val_mask, val);
snd_soc_component_update_bits(component, rreg, val_mask, val);
snd_soc_component_update_bits(component, RK730_DADC_SR_ACL, sign_mask, sign);
return 1;
}
static int rk730_dac_vol_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int val = snd_soc_component_read(component, mc->reg);
unsigned int sign = snd_soc_component_read(component, RK730_DDAC_SR_LMT);
unsigned int mask = (1 << fls(mc->max)) - 1;
unsigned int shift = mc->shift;
int mid = mc->max / 2;
int uv;
uv = (val >> shift) & mask;
sign &= RK730_DDAC_SR_LMT_VOLL_POL_MASK;
if (sign)
uv = mid + uv;
else
uv = mid - uv;
ucontrol->value.integer.value[0] = uv;
ucontrol->value.integer.value[1] = uv;
return 0;
}
static int rk730_dac_vol_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int rreg = mc->rreg;
unsigned int shift = mc->shift;
unsigned int mask = (1 << fls(mc->max)) - 1;
unsigned int val, val_mask, sign, sign_mask;
int uv = ucontrol->value.integer.value[0];
int min = mc->min;
int mid = mc->max / 2;
sign_mask = RK730_DDAC_SR_LMT_VOLL_POL_MASK | RK730_DDAC_SR_LMT_VOLR_POL_MASK;
if (uv > mid) {
sign = RK730_DDAC_SR_LMT_VOLL_POS | RK730_DDAC_SR_LMT_VOLR_POS;
uv = uv - mid;
} else {
sign = RK730_DDAC_SR_LMT_VOLL_NEG | RK730_DDAC_SR_LMT_VOLR_NEG;
uv = mid - uv;
}
val = ((uv + min) & mask);
val_mask = mask << shift;
val = val << shift;
snd_soc_component_update_bits(component, reg, val_mask, val);
snd_soc_component_update_bits(component, rreg, val_mask, val);
snd_soc_component_update_bits(component, RK730_DDAC_SR_LMT, sign_mask, sign);
return 1;
}
static int rk730_cp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
if (SND_SOC_DAPM_EVENT_ON(event))
usleep_range(5000, 5100);
return 0;
}
static int rk730_pll_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
if (SND_SOC_DAPM_EVENT_ON(event))
snd_soc_component_write(component, RK730_SYSPLL_0, 0x00);
else
snd_soc_component_write(component, RK730_SYSPLL_0, 0xff);
return 0;
}
static int rk730_adc_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
if (SND_SOC_DAPM_EVENT_ON(event)) {
snd_soc_component_update_bits(component, RK730_ADC_0,
RK730_ADC_0_DEM_EN_MASK,
RK730_ADC_0_DEM_EN);
snd_soc_component_update_bits(component, RK730_DTOP_DIGEN_CLKE,
RK730_DTOP_DIGEN_CLKE_ADC_CKE_MASK |
RK730_DTOP_DIGEN_CLKE_I2STX_CKE_MASK |
RK730_DTOP_DIGEN_CLKE_ADC_EN_MASK |
RK730_DTOP_DIGEN_CLKE_I2STX_EN_MASK,
RK730_DTOP_DIGEN_CLKE_ADC_CKE_EN |
RK730_DTOP_DIGEN_CLKE_I2STX_CKE_EN |
RK730_DTOP_DIGEN_CLKE_ADC_EN |
RK730_DTOP_DIGEN_CLKE_I2STX_EN);
usleep_range(20000, 21000);
snd_soc_component_update_bits(component, RK730_DI2S_TXCR_3_TXCMD,
RK730_DI2S_TXCR_3_TXCMD_TXS_MASK,
RK730_DI2S_TXCR_3_TXCMD_TXS_EN);
} else {
snd_soc_component_update_bits(component, RK730_DI2S_TXCR_3_TXCMD,
RK730_DI2S_TXCR_3_TXCMD_TXS_MASK,
RK730_DI2S_TXCR_3_TXCMD_TXS_DIS);
snd_soc_component_update_bits(component, RK730_ADC_0,
RK730_ADC_0_DEM_EN_MASK,
RK730_ADC_0_DEM_DIS);
snd_soc_component_update_bits(component, RK730_DTOP_DIGEN_CLKE,
RK730_DTOP_DIGEN_CLKE_ADC_CKE_MASK |
RK730_DTOP_DIGEN_CLKE_I2STX_CKE_MASK |
RK730_DTOP_DIGEN_CLKE_ADC_EN_MASK |
RK730_DTOP_DIGEN_CLKE_I2STX_EN_MASK,
RK730_DTOP_DIGEN_CLKE_ADC_CKE_DIS |
RK730_DTOP_DIGEN_CLKE_I2STX_CKE_DIS |
RK730_DTOP_DIGEN_CLKE_ADC_DIS |
RK730_DTOP_DIGEN_CLKE_I2STX_DIS);
}
return 0;
}
static int rk730_dac_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
if (SND_SOC_DAPM_EVENT_ON(event)) {
snd_soc_component_update_bits(component, RK730_DTOP_DIGEN_CLKE,
RK730_DTOP_DIGEN_CLKE_DAC_CKE_MASK |
RK730_DTOP_DIGEN_CLKE_I2SRX_CKE_MASK |
RK730_DTOP_DIGEN_CLKE_DAC_EN_MASK |
RK730_DTOP_DIGEN_CLKE_I2SRX_EN_MASK,
RK730_DTOP_DIGEN_CLKE_DAC_CKE_EN |
RK730_DTOP_DIGEN_CLKE_I2SRX_CKE_EN |
RK730_DTOP_DIGEN_CLKE_DAC_EN |
RK730_DTOP_DIGEN_CLKE_I2SRX_EN);
snd_soc_component_update_bits(component, RK730_DI2S_RXCMD_TSD,
RK730_DI2S_RXCMD_TSD_RXS_MASK,
RK730_DI2S_RXCMD_TSD_RXS_EN);
} else {
snd_soc_component_update_bits(component, RK730_DI2S_RXCMD_TSD,
RK730_DI2S_RXCMD_TSD_RXS_MASK,
RK730_DI2S_RXCMD_TSD_RXS_DIS);
snd_soc_component_update_bits(component, RK730_DTOP_DIGEN_CLKE,
RK730_DTOP_DIGEN_CLKE_DAC_CKE_MASK |
RK730_DTOP_DIGEN_CLKE_I2SRX_CKE_MASK |
RK730_DTOP_DIGEN_CLKE_DAC_EN_MASK |
RK730_DTOP_DIGEN_CLKE_I2SRX_EN_MASK,
RK730_DTOP_DIGEN_CLKE_DAC_CKE_DIS |
RK730_DTOP_DIGEN_CLKE_I2SRX_CKE_DIS |
RK730_DTOP_DIGEN_CLKE_DAC_DIS |
RK730_DTOP_DIGEN_CLKE_I2SRX_DIS);
}
return 0;
}
static int rk730_mux_out_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct rk730_priv *rk730 = snd_soc_component_get_drvdata(component);
unsigned int val;
if (SND_SOC_DAPM_EVENT_ON(event))
val = atomic_inc_return(&rk730->mix_mode);
else
val = atomic_dec_return(&rk730->mix_mode);
snd_soc_component_update_bits(component, RK730_MIXER_2,
RK730_MIXER_2_MIX_R_MODE_MASK |
RK730_MIXER_2_MIX_L_MODE_MASK,
RK730_MIXER_2_MIX_R_MODE(val) |
RK730_MIXER_2_MIX_L_MODE(val));
return 0;
}
static const struct snd_kcontrol_new rk730_snd_controls[] = {
SOC_DOUBLE_R_TLV("ADC Volume", RK730_ADC_PGA_BLOCK_0, RK730_ADC_PGA_BLOCK_1,
1, 0x7, 1, adc_tlv),
SOC_DOUBLE_R_TLV("D2S Volume", RK730_DAC_1, RK730_DAC_2,
1, 0x6, 1, d2s_tlv),
SOC_DOUBLE_R_TLV("MIC1 Boost Volume", RK730_MIC_BOOST_0, RK730_MIC_BOOST_1,
1, 0x18, 0, micboost_tlv),
SOC_DOUBLE_R_TLV("MIC2 Boost Volume", RK730_MIC_BOOST_2, RK730_MIC_BOOST_3,
1, 0x18, 0, micboost_tlv),
SOC_DOUBLE_TLV("Out Mux Volume", RK730_MUXER_0, 1, 5, 0x1, 0, mux_tlv),
SOC_SINGLE_TLV("Left Out Mux -> Left Out Mixer Volume",
RK730_MIXER_0, 1, 0x6, 1, mix_buf_tlv),
SOC_SINGLE_TLV("Left Out Mux -> Right Out Mixer Volume",
RK730_MIXER_0, 5, 0x6, 1, mix_buf_tlv),
SOC_SINGLE_TLV("Right Out Mux -> Left Out Mixer Volume",
RK730_MIXER_1, 1, 0x6, 1, mix_buf_tlv),
SOC_SINGLE_TLV("Right Out Mux -> Right Out Mixer Volume",
RK730_MIXER_1, 5, 0x6, 1, mix_buf_tlv),
SOC_SINGLE_TLV("HP Volume", RK730_HP_0, 6, 0x3, 0, hp_tlv),
SOC_SINGLE_TLV("Line Out Volume", RK730_LINEOUT_1, 2, 0x3, 0, lineout_tlv),
SOC_DOUBLE_R_EXT_TLV("ADC Digital Volume",
RK730_DADC_VOLL, RK730_DADC_VOLR, 0, 0x1fe, 0,
rk730_adc_vol_get,
rk730_adc_vol_put,
adc_dig_tlv),
SOC_DOUBLE_R_EXT_TLV("DAC Digital Volume",
RK730_DDAC_VOLL, RK730_DDAC_VOLR, 0, 0x1fe, 0,
rk730_dac_vol_get,
rk730_dac_vol_put,
dac_dig_tlv),
SOC_ENUM("ADC HPF Cutoff", adc_hpf_cutoff_enum),
SOC_ENUM("DAC HPF Cutoff", dac_hpf_cutoff_enum),
SOC_ENUM("DAC Ref Buf Chop Freq", dac_ref_buf_chop_freq_enum),
SOC_ENUM("MIC Chop Freq", mic_chop_freq_enum),
SOC_ENUM("ADC PGA Chop Freq", adc_pga_chop_freq_enum),
SOC_ENUM("Out Mux Chop Freq", mux_out_chop_freq_enum),
SOC_ENUM("Mixer Chop Freq", mix_chop_freq_enum),
SOC_ENUM("HP / Lineout Chop Freq", hp_lo_chop_freq_enum),
SOC_ENUM("Mic Bias Volt", micbias_volt_enum),
SOC_ENUM("Charge Pump Volt", charge_pump_volt_enum),
SOC_SINGLE("ADCL HPF Switch", RK730_DADC_HPF, 7, 1, 0),
SOC_SINGLE("ADCR HPF Switch", RK730_DADC_HPF, 6, 1, 0),
SOC_SINGLE("DAC HPF Switch", RK730_DDAC_MUTE_MIXCTL, 7, 1, 0),
SOC_SINGLE("ADC Volume Bypass Switch", RK730_DTOP_VUCTL, 7, 1, 0),
SOC_SINGLE("DAC Volume Bypass Switch", RK730_DTOP_VUCTL, 6, 1, 0),
SOC_SINGLE("ADC Fade Switch", RK730_DTOP_VUCTL, 5, 1, 0),
SOC_SINGLE("DAC Fade Switch", RK730_DTOP_VUCTL, 4, 1, 0),
SOC_SINGLE("ADC Zero Crossing Switch", RK730_DTOP_VUCTL, 1, 1, 0),
SOC_SINGLE("DAC Zero Crossing Switch", RK730_DTOP_VUCTL, 0, 1, 0),
SOC_SINGLE("MIC1N / MIC2P Exchanged Switch", RK730_MIC_BOOST_2, 7, 1, 0),
};
static const struct snd_soc_dapm_widget rk730_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY_S("ANA LDO", 0, RK730_LDO, 7, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("OSC CLK", 1, RK730_HK_TOP_2, 4, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("VAG BUF", 1, RK730_HK_TOP_2, 2, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("ADC BUF", 1, RK730_HK_TOP_2, 1, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("DAC BUF", 1, RK730_HK_TOP_2, 0, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("MICBIAS", 1, RK730_MIC_BIAS, 0, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, RK730_CHARGE_PUMP, 0, 0,
rk730_cp_event, SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY_S("PLL", 2, SND_SOC_NOPM, 0, 0, rk730_pll_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY_S("DAC Bias", 2, RK730_DAC_0, 2, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("HP Bias", 2, RK730_HP_0, 5, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("Line Out Bias", 2, RK730_LINEOUT_1, 7, 1, NULL, 0),
SND_SOC_DAPM_ADC_E("ADCL", "HiFi Capture", RK730_ADC_0, 0, 1,
rk730_adc_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_ADC_E("ADCR", "HiFi Capture", RK730_ADC_0, 1, 1,
rk730_adc_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_DAC_E("DACL", "HiFi Playback", RK730_DAC_0, 0, 1,
rk730_dac_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_DAC_E("DACR", "HiFi Playback", RK730_DAC_0, 1, 1,
rk730_dac_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_PGA("ADCL PGA", RK730_ADC_PGA_BLOCK_0, 0, 1, NULL, 0),
SND_SOC_DAPM_PGA("ADCR PGA", RK730_ADC_PGA_BLOCK_1, 0, 1, NULL, 0),
SND_SOC_DAPM_PGA("D2SL", RK730_DAC_1, 0, 1, NULL, 0),
SND_SOC_DAPM_PGA("D2SR", RK730_DAC_2, 0, 1, NULL, 0),
SND_SOC_DAPM_PGA("MIC1P", RK730_MIC_BOOST_0, 0, 1, NULL, 0),
SND_SOC_DAPM_PGA("MIC1N", RK730_MIC_BOOST_1, 0, 1, NULL, 0),
SND_SOC_DAPM_PGA("MIC2P", RK730_MIC_BOOST_2, 0, 1, NULL, 0),
SND_SOC_DAPM_PGA("MIC2N", RK730_MIC_BOOST_3, 0, 1, NULL, 0),
SND_SOC_DAPM_PGA("DIFFL", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("DIFFR", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("HP Out", RK730_HP_0, 2, 1, NULL, 0),
SND_SOC_DAPM_PGA("HP Out Stage", RK730_HP_0, 3, 1, NULL, 0),
SND_SOC_DAPM_PGA("Line Out", RK730_LINEOUT_0, 2, 1, NULL, 0),
SND_SOC_DAPM_PGA("Line Out Stage", RK730_LINEOUT_0, 3, 1, NULL, 0),
SND_SOC_DAPM_MUX_E("Left Out Mux", SND_SOC_NOPM, 0, 0, &mux_out_l,
rk730_mux_out_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MUX_E("Right Out Mux", SND_SOC_NOPM, 0, 0, &mux_out_r,
rk730_mux_out_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MUX("Left Input Mux", SND_SOC_NOPM, 0, 0, &mux_input_l),
SND_SOC_DAPM_MUX("Right Input Mux", SND_SOC_NOPM, 0, 0, &mux_input_r),
SND_SOC_DAPM_MIXER("Left Out Mixer", RK730_MIXER_2, 0, 1,
mix_ctls, ARRAY_SIZE(mix_ctls)),
SND_SOC_DAPM_MIXER("Right Out Mixer", RK730_MIXER_2, 3, 1,
mix_ctls, ARRAY_SIZE(mix_ctls)),
SND_SOC_DAPM_OUTPUT("HPL"),
SND_SOC_DAPM_OUTPUT("HPR"),
SND_SOC_DAPM_OUTPUT("LINEOUTL"),
SND_SOC_DAPM_OUTPUT("LINEOUTR"),
SND_SOC_DAPM_INPUT("MIC1"),
SND_SOC_DAPM_INPUT("MIC2"),
};
static const struct snd_soc_dapm_route rk730_dapm_routes[] = {
{ "DACL", NULL, "ANA LDO" },
{ "DACR", NULL, "ANA LDO" },
{ "DACL", NULL, "OSC CLK" },
{ "DACR", NULL, "OSC CLK" },
{ "DACL", NULL, "VAG BUF" },
{ "DACR", NULL, "VAG BUF" },
{ "DACL", NULL, "DAC BUF" },
{ "DACR", NULL, "DAC BUF" },
{ "DACL", NULL, "PLL" },
{ "DACR", NULL, "PLL" },
{ "DACL", NULL, "DAC Bias" },
{ "DACR", NULL, "DAC Bias" },
{ "D2SL", NULL, "DACL" },
{ "D2SR", NULL, "DACR" },
{ "Left Out Mixer", NULL, "D2SL" },
{ "Left Out Mixer", "Left Out Mux Switch", "Left Out Mux" },
{ "Left Out Mixer", "Right Out Mux Switch", "Right Out Mux" },
{ "Right Out Mixer", NULL, "D2SR" },
{ "Right Out Mixer", "Left Out Mux Switch", "Left Out Mux" },
{ "Right Out Mixer", "Right Out Mux Switch", "Right Out Mux" },
{ "Left Out Mux", "DIFF", "DIFFL" },
{ "Left Out Mux", "MIC1N", "MIC1N" },
{ "Left Out Mux", "MIC2N", "MIC2N" },
{ "Right Out Mux", "DIFF", "DIFFR" },
{ "Right Out Mux", "MIC1P", "MIC1P" },
{ "Right Out Mux", "MIC2P", "MIC2P" },
{ "HP Out", NULL, "HP Bias" },
{ "HP Out", NULL, "HP Bias" },
{ "Line Out", NULL, "Line Out Bias" },
{ "HP Out", NULL, "Left Out Mixer" },
{ "HP Out", NULL, "Right Out Mixer" },
{ "Line Out", NULL, "Left Out Mixer" },
{ "Line Out", NULL, "Right Out Mixer" },
{ "HP Out Stage", NULL, "HP Out" },
{ "Line Out Stage", NULL, "Line Out" },
{ "HPL", NULL, "HP Out Stage" },
{ "HPR", NULL, "HP Out Stage" },
{ "HPL", NULL, "Charge Pump" },
{ "HPR", NULL, "Charge Pump" },
{ "LINEOUTL", NULL, "Line Out Stage" },
{ "LINEOUTR", NULL, "Line Out Stage" },
{ "LINEOUTL", NULL, "Charge Pump" },
{ "LINEOUTR", NULL, "Charge Pump" },
{ "ADCL", NULL, "ANA LDO" },
{ "ADCR", NULL, "ANA LDO" },
{ "ADCL", NULL, "OSC CLK" },
{ "ADCR", NULL, "OSC CLK" },
{ "ADCL", NULL, "ADC BUF" },
{ "ADCR", NULL, "ADC BUF" },
{ "ADCL", NULL, "VAG BUF" },
{ "ADCR", NULL, "VAG BUF" },
{ "ADCL", NULL, "PLL" },
{ "ADCR", NULL, "PLL" },
{ "ADCL", NULL, "ADCL PGA" },
{ "ADCR", NULL, "ADCR PGA" },
{ "ADCL PGA", NULL, "Left Input Mux" },
{ "ADCR PGA", NULL, "Right Input Mux" },
{ "Left Input Mux", "DIFF", "DIFFL" },
{ "Left Input Mux", "VINP1", "MIC1P" },
{ "Left Input Mux", "VINN1", "MIC1N" },
{ "Right Input Mux", "DIFF", "DIFFR" },
{ "Right Input Mux", "VINP2", "MIC2P" },
{ "Right Input Mux", "VINN2", "MIC2N" },
{ "DIFFL", NULL, "MIC1P" },
{ "DIFFL", NULL, "MIC1N" },
{ "DIFFR", NULL, "MIC2P" },
{ "DIFFR", NULL, "MIC2N" },
{ "MIC1P", NULL, "MIC1" },
{ "MIC1N", NULL, "MIC1" },
{ "MIC2P", NULL, "MIC2" },
{ "MIC2N", NULL, "MIC2" },
{ "MIC1", NULL, "MICBIAS" },
{ "MIC2", NULL, "MICBIAS" },
};
static unsigned int samplerate_to_bit(unsigned int samplerate)
{
switch (samplerate) {
case 8000:
case 11025:
case 12000:
return 0;
case 16000:
case 22050:
case 24000:
return 1;
case 32000:
case 44100:
case 48000:
return 2;
case 64000:
case 88200:
case 96000:
return 3;
case 128000:
case 176400:
case 192000:
return 4;
default:
return 2;
}
}
static int rk730_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
unsigned int width, rate;
width = min(params_width(params), 24);
rate = samplerate_to_bit(params_rate(params));
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
snd_soc_component_update_bits(component, RK730_DI2S_RXCR_2,
RK730_DI2S_XCR2_VDW_MASK,
RK730_DI2S_XCR2_VDW(width));
snd_soc_component_update_bits(component, RK730_DDAC_SR_LMT,
RK730_DDAC_SR_LMT_SRT_MASK,
RK730_DDAC_SR_LMT_SRT(rate));
} else {
snd_soc_component_update_bits(component, RK730_DI2S_TXCR_2,
RK730_DI2S_XCR2_VDW_MASK,
RK730_DI2S_XCR2_VDW(width));
snd_soc_component_update_bits(component, RK730_DADC_SR_ACL,
RK730_DADC_SR_ACL_SRT_MASK,
RK730_DADC_SR_ACL_SRT(rate));
}
return 0;
}
static int rk730_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
struct snd_soc_component *component = codec_dai->component;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
snd_soc_component_update_bits(component, RK730_DI2S_CKM,
RK730_DI2S_CKM_MST_MASK,
RK730_DI2S_CKM_MST_SLAVE);
break;
case SND_SOC_DAIFMT_CBM_CFM:
snd_soc_component_update_bits(component, RK730_DI2S_CKM,
RK730_DI2S_CKM_MST_MASK,
RK730_DI2S_CKM_MST_MASTER);
break;
default:
return -EINVAL;
}
return 0;
}
static int rk730_dai_mute(struct snd_soc_dai *codec_dai, int mute, int direction)
{
struct snd_soc_component *component = codec_dai->component;
if (mute)
snd_soc_component_update_bits(component, RK730_DDAC_MUTE_MIXCTL,
RK730_DDAC_MUTE_MIXCTL_MUTE_MASK,
RK730_DDAC_MUTE_MIXCTL_MUTE);
else
snd_soc_component_update_bits(component, RK730_DDAC_MUTE_MIXCTL,
RK730_DDAC_MUTE_MIXCTL_MUTE_MASK,
RK730_DDAC_MUTE_MIXCTL_UNMUTE);
return 0;
}
static int rk730_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
struct rk730_priv *rk730 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
/*
* SND_SOC_BIAS_PREPARE is called while preparing for a
* transition to ON or away from ON. If current bias_level
* is SND_SOC_BIAS_ON, then it is preparing for a transition
* away from ON. Disable the clock in that case, otherwise
* enable it.
*/
if (!IS_ERR(rk730->mclk)) {
if (snd_soc_component_get_bias_level(component) ==
SND_SOC_BIAS_ON)
clk_disable_unprepare(rk730->mclk);
else
clk_prepare_enable(rk730->mclk);
}
break;
case SND_SOC_BIAS_STANDBY:
if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
regcache_sync(rk730->regmap);
break;
case SND_SOC_BIAS_OFF:
regcache_mark_dirty(rk730->regmap);
break;
}
return 0;
}
#define RK730_RATES SNDRV_PCM_RATE_8000_192000
#define RK730_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
static const struct snd_soc_dai_ops rk730_dai_ops = {
.set_fmt = rk730_dai_set_fmt,
.hw_params = rk730_dai_hw_params,
.mute_stream = rk730_dai_mute,
.no_capture_mute = 1,
};
static struct snd_soc_dai_driver rk730_dai = {
.name = "HiFi",
.playback = {
.stream_name = "HiFi Playback",
.channels_min = 1,
.channels_max = 2,
.rates = RK730_RATES,
.formats = RK730_FORMATS,
},
.capture = {
.stream_name = "HiFi Capture",
.channels_min = 1,
.channels_max = 2,
.rates = RK730_RATES,
.formats = RK730_FORMATS,
},
.ops = &rk730_dai_ops,
};
static int rk730_reset(struct snd_soc_component *component)
{
struct rk730_priv *rk730 = snd_soc_component_get_drvdata(component);
clk_prepare_enable(rk730->mclk);
snd_soc_component_write(component, RK730_DTOP_LPT_SRST, 0x40);
udelay(10);
/* WA: Initial micbias default, ADC stopped with micbias(>2.5v) */
snd_soc_component_update_bits(component, RK730_MIC_BIAS,
RK730_MIC_BIAS_VOLT_MASK,
RK730_MIC_BIAS_VOLT_2_2V);
/* PF: Use the maximum bias current for better performance */
snd_soc_component_update_bits(component, RK730_HK_TOP_1,
RK730_HK_TOP_1_IBIAS_STD_SEL_MASK |
RK730_HK_TOP_1_IBIAS_GAIN_SEL_MASK,
RK730_HK_TOP_1_IBIAS_STD_SEL_27_5UA |
RK730_HK_TOP_1_IBIAS_GAIN_SEL_200);
/* PF: Use the chop 400kHz for better ADC noise performance */
snd_soc_component_update_bits(component, RK730_MIC_BOOST_3,
RK730_MIC_BOOST_3_MIC_CHOP_MASK,
RK730_MIC_BOOST_3_MIC_CHOP(RK730_CHOP_FREQ_400KHZ));
snd_soc_component_update_bits(component, RK730_ADC_PGA_BLOCK_1,
RK730_ADC_PGA_BLOCK_1_PGA_CHOP_MASK,
RK730_ADC_PGA_BLOCK_1_PGA_CHOP(RK730_CHOP_FREQ_400KHZ));
clk_disable_unprepare(rk730->mclk);
return 0;
}
static int rk730_probe(struct snd_soc_component *component)
{
struct rk730_priv *rk730 = snd_soc_component_get_drvdata(component);
int ret = 0;
regcache_mark_dirty(rk730->regmap);
/* initialize private data */
atomic_set(&rk730->mix_mode, RK730_MIX_MODE_1_PATH);
ret = snd_soc_component_read(component, RK730_HK_TOP_0);
if (ret < 0) {
dev_err(component->dev, "Failed to read register: %d\n", ret);
return ret;
}
rk730_reset(component);
return ret;
}
static const struct snd_soc_component_driver rk730_component_driver = {
.probe = rk730_probe,
.set_bias_level = rk730_set_bias_level,
.controls = rk730_snd_controls,
.num_controls = ARRAY_SIZE(rk730_snd_controls),
.dapm_widgets = rk730_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(rk730_dapm_widgets),
.dapm_routes = rk730_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(rk730_dapm_routes),
.suspend_bias_off = 1,
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
.non_legacy_dai_naming = 1,
};
static const struct reg_default rk730_reg_defaults[] = {
{ 0x00, 0x40 },
{ 0x02, 0x17 },
{ 0x05, 0x03 },
{ 0x06, 0x22 },
{ 0x07, 0x02 },
{ 0x08, 0x07 },
{ 0x09, 0x01 },
{ 0x0a, 0x01 },
{ 0x0b, 0x01 },
{ 0x0c, 0x01 },
{ 0x0d, 0x01 },
{ 0x0e, 0x01 },
{ 0x0f, 0x07 },
{ 0x10, 0x07 },
{ 0x11, 0xff },
{ 0x12, 0x07 },
{ 0x13, 0x54 },
{ 0x14, 0x04 },
{ 0x15, 0x23 },
{ 0x16, 0x35 },
{ 0x17, 0x67 },
{ 0x18, 0x1e },
{ 0x19, 0xc0 },
{ 0x1a, 0x13 },
{ 0x1b, 0x04 },
{ 0x1c, 0x20 },
{ 0x1f, 0x90 },
{ 0x20, 0x11 },
{ 0x21, 0x09 },
{ 0x22, 0x33 },
{ 0x24, 0x11 },
{ 0x25, 0x11 },
{ 0x26, 0x09 },
{ 0x27, 0x02 },
{ 0x28, 0x2c },
{ 0x2a, 0x0c },
{ 0x2b, 0x80 },
{ 0x40, 0x03 },
{ 0x42, 0x20 },
{ 0x47, 0xe6 },
{ 0x48, 0xd0 },
{ 0x49, 0x17 },
{ 0x4a, 0x26 },
{ 0x4b, 0x01 },
{ 0x4c, 0x05 },
{ 0x4d, 0x0e },
{ 0x4e, 0x09 },
{ 0x4f, 0x02 },
{ 0x5b, 0xe6 },
{ 0x5c, 0xd0 },
{ 0x5d, 0x17 },
{ 0x5e, 0x26 },
{ 0x5f, 0x01 },
{ 0x60, 0x05 },
{ 0x61, 0x0e },
{ 0x62, 0x09 },
{ 0x63, 0x20 },
{ 0x66, 0x01 },
{ 0x69, 0x17 },
{ 0x6c, 0x17 },
};
static bool rk730_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case RK730_DTOP_LPT_SRST:
return true;
default:
return false;
}
}
static const struct regmap_config rk730_regmap = {
.reg_bits = 8,
.val_bits = 8,
.volatile_reg = rk730_volatile_register,
.max_register = RK730_DAC_ATTN,
.reg_defaults = rk730_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(rk730_reg_defaults),
.cache_type = REGCACHE_RBTREE,
};
static int rk730_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct rk730_priv *rk730;
int ret;
rk730 = devm_kzalloc(&i2c->dev, sizeof(struct rk730_priv), GFP_KERNEL);
if (!rk730)
return -ENOMEM;
rk730->regmap = devm_regmap_init_i2c(i2c, &rk730_regmap);
if (IS_ERR(rk730->regmap))
return PTR_ERR(rk730->regmap);
rk730->mclk = devm_clk_get(&i2c->dev, "mclk");
if (IS_ERR(rk730->mclk))
return PTR_ERR(rk730->mclk);
i2c_set_clientdata(i2c, rk730);
ret = devm_snd_soc_register_component(&i2c->dev,
&rk730_component_driver, &rk730_dai, 1);
return ret;
}
static const struct i2c_device_id rk730_i2c_id[] = {
{ "rk730", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, rk730_i2c_id);
#if defined(CONFIG_OF)
static const struct of_device_id rk730_of_match[] = {
{ .compatible = "rockchip,rk730" },
{ }
};
MODULE_DEVICE_TABLE(of, rk730_of_match);
#endif
static struct i2c_driver rk730_i2c_driver = {
.driver = {
.name = "rk730",
.of_match_table = of_match_ptr(rk730_of_match),
},
.probe = rk730_i2c_probe,
.id_table = rk730_i2c_id,
};
module_i2c_driver(rk730_i2c_driver);
MODULE_DESCRIPTION("ASoC RK730 driver");
MODULE_AUTHOR("Sugar Zhang <sugar.zhang@rock-chips.com>");
MODULE_LICENSE("GPL");