1169 lines
35 KiB
C
1169 lines
35 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"
|
|
|
|
/* Output diagram
|
|
* FOR current evb, LOUT2 to speaker and LOUT2 to headphone
|
|
* DAC0(DAC_L_N/DAC_R_N)->HP_0 -> LOUT2
|
|
* DAC1(DAC_L_P/DAC_R_P)->SP_0 -> LOUT1
|
|
*
|
|
* FOR HP differential mode
|
|
*- DAC_L_N(DAC0) -> HP Driver -> LOUT2 ----------
|
|
*- DAC_R_N(DAC0) -> HP Driver -> ROUT2 ---- |
|
|
* | |
|
|
* | R
|
|
* R |
|
|
* | |
|
|
*- DAC_L_P(DAC1) -> SPK Driver -> LOUT1 ---|-----
|
|
*- DAC_R_P(DAC1) -> SPK Driver -> ROUT1 ---
|
|
*/
|
|
|
|
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;
|
|
unsigned int sysclk;
|
|
atomic_t mix_mode;
|
|
};
|
|
|
|
/* ADC Digital Volume */
|
|
static const DECLARE_TLV_DB_MINMAX(adc_dig_tlv, -9600, 0);
|
|
/* DAC Digital Volume */
|
|
static const DECLARE_TLV_DB_MINMAX(dac_dig_tlv, -9600, 0);
|
|
/* ADC Volume */
|
|
static const DECLARE_TLV_DB_MINMAX(adc_tlv, -1050, 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),
|
|
);
|
|
|
|
static const DECLARE_TLV_DB_SCALE(micdecrease_tlv, 0, -300, 0);
|
|
|
|
static const char * const mux_input_l_text[] = { "DIFF", "VINR2", "VINL2" };
|
|
static const char * const mux_input_r_text[] = { "DIFF", "VINR1", "VINL1" };
|
|
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_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 char * const adc_data_sel_text[] = { "normal", "left", "right", "swap" };
|
|
|
|
static SOC_ENUM_SINGLE_DECL(adc_data_sel_enum, RK730_DADC_SEL,
|
|
0, adc_data_sel_text);
|
|
static const struct snd_kcontrol_new adc_data_sel =
|
|
SOC_DAPM_ENUM("ADCSel Mux", adc_data_sel_enum);
|
|
|
|
static const char * const chop_freq_text[] = {
|
|
"Disabled", "200kHz", "400kHz", "800kHz",
|
|
};
|
|
|
|
static const char * const hfp_center_freq_text[] = {
|
|
"80HZ", "100HZ", "120HZ", "140HZ",
|
|
};
|
|
|
|
static const char * const dac_hfp_center_freq_text[] = {
|
|
"C*0.8", "C*0.9", "C*1.0(fs=6.144M)", "C*1.1(fs=5.6448M)",
|
|
"C*1.2", "C*1.3", "C*1.4", "C*1.5(fs=4.096M)",
|
|
};
|
|
|
|
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(hp_lo_chop_freq_enum, RK730_HP_1,
|
|
5, chop_freq_text);
|
|
static SOC_ENUM_SINGLE_DECL(dac_hfp_center_freq_enum, RK730_DDAC_FILTER,
|
|
2, hfp_center_freq_text);
|
|
|
|
static SOC_ENUM_SINGLE_DECL(adc_capacity_trim_enum, RK730_ADC_2,
|
|
0, dac_hfp_center_freq_text);
|
|
|
|
static const char * const micbias_volt_text[] = {
|
|
"2.0v", "2.2v", "2.5v", "2.8v",
|
|
};
|
|
|
|
static SOC_ENUM_SINGLE_DECL(micbias_volt_enum, RK730_MIC_BIAS,
|
|
2, micbias_volt_text);
|
|
|
|
static const char * const dig_ldo_volt_text[] = {
|
|
"1.4v", "1.5v", "1.6v", "1.7v",
|
|
};
|
|
|
|
static SOC_ENUM_SINGLE_DECL(dig_ldo_volt_enum, RK730_LDO,
|
|
0, dig_ldo_volt_text);
|
|
|
|
static const char * const ana_ldo_volt_text[] = {
|
|
"1.4v", "1.5v", "1.6v", "1.7v",
|
|
};
|
|
|
|
static const struct snd_kcontrol_new rk730_out1_switch =
|
|
SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
|
|
|
|
static const struct snd_kcontrol_new rk730_out2_switch =
|
|
SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
|
|
|
|
static SOC_ENUM_SINGLE_DECL(ana_ldo_volt_enum, RK730_LDO,
|
|
4, ana_ldo_volt_text);
|
|
|
|
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)) {
|
|
dev_dbg(component->dev, "%s on\n", __func__);
|
|
snd_soc_component_write(component, RK730_SYSPLL_0, 0x00);
|
|
} else {
|
|
dev_dbg(component->dev, "%s off\n", __func__);
|
|
snd_soc_component_write(component, RK730_SYSPLL_0, 0xff);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int rk730_dacl_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))
|
|
dev_dbg(component->dev, "%s on\n", __func__);
|
|
else
|
|
dev_dbg(component->dev, "%s off\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int rk730_out1_drv_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))
|
|
dev_dbg(component->dev, "%s on\n", __func__);
|
|
else
|
|
dev_dbg(component->dev, "%s off\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int rk730_out2_drv_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))
|
|
dev_dbg(component->dev, "%s on\n", __func__);
|
|
else
|
|
dev_dbg(component->dev, "%s off\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int rk730_dacr_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))
|
|
dev_dbg(component->dev, "%s on\n", __func__);
|
|
else
|
|
dev_dbg(component->dev, "%s off\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int rk730_sdin_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);
|
|
|
|
switch (event) {
|
|
case SND_SOC_DAPM_PRE_PMU:
|
|
dev_dbg(component->dev, "%s on\n", __func__);
|
|
snd_soc_component_update_bits(component, RK730_DI2S_RXCMD_TSD,
|
|
RK730_DI2S_RXCMD_TSD_RXS_MASK,
|
|
RK730_DI2S_RXCMD_TSD_RXS_EN);
|
|
snd_soc_component_update_bits(component, RK730_DTOP_DIGEN_CLKE,
|
|
RK730_DTOP_DIGEN_CLKE_I2SRX_CKE_MASK |
|
|
RK730_DTOP_DIGEN_CLKE_I2SRX_EN_MASK,
|
|
RK730_DTOP_DIGEN_CLKE_I2SRX_CKE_EN |
|
|
RK730_DTOP_DIGEN_CLKE_I2SRX_EN);
|
|
snd_soc_component_update_bits(component, RK730_DTOP_DIGEN_CLKE,
|
|
RK730_DTOP_DIGEN_CLKE_DAC_CKE_MASK |
|
|
RK730_DTOP_DIGEN_CLKE_DAC_EN_MASK,
|
|
RK730_DTOP_DIGEN_CLKE_DAC_CKE_EN |
|
|
RK730_DTOP_DIGEN_CLKE_DAC_EN);
|
|
break;
|
|
case SND_SOC_DAPM_POST_PMD:
|
|
dev_dbg(component->dev, "%s off\n", __func__);
|
|
snd_soc_component_update_bits(component, RK730_DTOP_DIGEN_CLKE,
|
|
RK730_DTOP_DIGEN_CLKE_DAC_CKE_MASK |
|
|
RK730_DTOP_DIGEN_CLKE_DAC_EN_MASK,
|
|
RK730_DTOP_DIGEN_CLKE_DAC_CKE_DIS |
|
|
RK730_DTOP_DIGEN_CLKE_DAC_DIS);
|
|
snd_soc_component_update_bits(component, RK730_DTOP_DIGEN_CLKE,
|
|
RK730_DTOP_DIGEN_CLKE_I2SRX_CKE_MASK |
|
|
RK730_DTOP_DIGEN_CLKE_I2SRX_EN_MASK,
|
|
RK730_DTOP_DIGEN_CLKE_I2SRX_CKE_DIS |
|
|
RK730_DTOP_DIGEN_CLKE_I2SRX_DIS);
|
|
snd_soc_component_update_bits(component, RK730_DI2S_RXCMD_TSD,
|
|
RK730_DI2S_RXCMD_TSD_RXS_MASK,
|
|
RK730_DI2S_RXCMD_TSD_RXS_DIS);
|
|
break;
|
|
default:
|
|
dev_err(component->dev, "%s Invalid event = 0x%x\n", __func__, event);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int rk730_sdout_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)) {
|
|
dev_dbg(component->dev, "%s on\n", __func__);
|
|
snd_soc_component_update_bits(component, RK730_DI2S_TXCR3_TXCMD,
|
|
RK730_DI2S_TXCR_3_TXCMD_TXS_MASK,
|
|
RK730_DI2S_TXCR_3_TXCMD_TXS_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_CKE_EN |
|
|
RK730_DTOP_DIGEN_CLKE_I2STX_CKE_EN);
|
|
usleep_range(20000, 21000);
|
|
snd_soc_component_update_bits(component, RK730_DTOP_DIGEN_CLKE,
|
|
RK730_DTOP_DIGEN_CLKE_ADC_EN_MASK |
|
|
RK730_DTOP_DIGEN_CLKE_I2STX_EN_MASK,
|
|
RK730_DTOP_DIGEN_CLKE_ADC_EN |
|
|
RK730_DTOP_DIGEN_CLKE_I2STX_EN);
|
|
} else {
|
|
dev_dbg(component->dev, "%s off\n", __func__);
|
|
|
|
snd_soc_component_update_bits(component, RK730_DTOP_DIGEN_CLKE,
|
|
RK730_DTOP_DIGEN_CLKE_ADC_EN_MASK |
|
|
RK730_DTOP_DIGEN_CLKE_I2STX_EN_MASK,
|
|
RK730_DTOP_DIGEN_CLKE_ADC_DIS |
|
|
RK730_DTOP_DIGEN_CLKE_I2STX_DIS);
|
|
usleep_range(50, 60);
|
|
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_CKE_DIS |
|
|
RK730_DTOP_DIGEN_CLKE_I2STX_CKE_DIS);
|
|
snd_soc_component_update_bits(component, RK730_DI2S_TXCR3_TXCMD,
|
|
RK730_DI2S_TXCR_3_TXCMD_TXS_MASK,
|
|
RK730_DI2S_TXCR_3_TXCMD_TXS_DIS);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int rk730_adcr_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);
|
|
|
|
switch (event) {
|
|
case SND_SOC_DAPM_POST_PMU:
|
|
dev_dbg(component->dev, "%s on\n", __func__);
|
|
break;
|
|
case SND_SOC_DAPM_PRE_PMD:
|
|
dev_dbg(component->dev, "%s off\n", __func__);
|
|
break;
|
|
default:
|
|
dev_err(component->dev, "%s Invalid event = 0x%x\n", __func__, event);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int rk730_adcl_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);
|
|
|
|
switch (event) {
|
|
case SND_SOC_DAPM_POST_PMU:
|
|
dev_dbg(component->dev, "%s on\n", __func__);
|
|
break;
|
|
case SND_SOC_DAPM_PRE_PMD:
|
|
dev_dbg(component->dev, "%s off\n", __func__);
|
|
break;
|
|
default:
|
|
dev_err(component->dev, "%s Invalid event = 0x%x\n", __func__, event);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int rk730_dac0_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)) {
|
|
dev_dbg(component->dev, "%s on\n", __func__);
|
|
|
|
/* analog config */
|
|
snd_soc_component_update_bits(component, RK730_DAC_0,
|
|
RK730_DAC_0_DAC_R_HP_PWD_MASK |
|
|
RK730_DAC_0_DAC_L_HP_PWD_MASK |
|
|
RK730_DAC_0_DAC_HP_IBIAS_MASK,
|
|
RK730_DAC_0_DAC_L_HP_PWU |
|
|
RK730_DAC_0_DAC_R_HP_PWU |
|
|
RK730_DAC_0_DAC_HP_IBIAS_ON);
|
|
snd_soc_component_update_bits(component, RK730_HP_0,
|
|
RK730_HP_0_ANTIPOP_PWR_MASK |
|
|
RK730_HP_0_HP_IBIAS_MASK |
|
|
RK730_HP_0_HP_TWOTAGE_EN_MASK |
|
|
RK730_HP_0_HP_OSTG_PWR_MASK |
|
|
RK730_HP_0_HP_BLOCK_PWR_MASK,
|
|
RK730_HP_0_ANTIPOP_PWR_OFF |
|
|
RK730_HP_0_HP_IBIAS_ON |
|
|
RK730_HP_0_HP_TWOTAGE_EN |
|
|
RK730_HP_0_HP_OSTG_PWR_ON |
|
|
RK730_HP_0_HP_BLOCK_PWR_ON);
|
|
} else {
|
|
dev_dbg(component->dev, "%s off\n", __func__);
|
|
|
|
/* analog config */
|
|
snd_soc_component_update_bits(component, RK730_HP_0,
|
|
RK730_HP_0_ANTIPOP_PWR_MASK |
|
|
RK730_HP_0_HP_IBIAS_MASK |
|
|
RK730_HP_0_HP_TWOTAGE_EN_MASK |
|
|
RK730_HP_0_HP_OSTG_PWR_MASK |
|
|
RK730_HP_0_HP_BLOCK_PWR_MASK,
|
|
RK730_HP_0_ANTIPOP_PWR_ON |
|
|
RK730_HP_0_HP_IBIAS_OFF |
|
|
RK730_HP_0_HP_TWOTAGE_DIS |
|
|
RK730_HP_0_HP_OSTG_PWR_OFF |
|
|
RK730_HP_0_HP_BLOCK_PWR_OFF);
|
|
snd_soc_component_update_bits(component, RK730_DAC_0,
|
|
RK730_DAC_0_DAC_R_HP_PWD_MASK |
|
|
RK730_DAC_0_DAC_L_HP_PWD_MASK |
|
|
RK730_DAC_0_DAC_HP_IBIAS_MASK,
|
|
RK730_DAC_0_DAC_L_HP_PWD |
|
|
RK730_DAC_0_DAC_R_HP_PWD|
|
|
RK730_DAC_0_DAC_HP_IBIAS_OFF);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int rk730_dac1_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)) {
|
|
dev_dbg(component->dev, "%s on\n", __func__);
|
|
|
|
/* analog config */
|
|
snd_soc_component_update_bits(component, RK730_DAC_1,
|
|
RK730_DAC_1_DAC_SPK_IBIAS_MASK |
|
|
RK730_DAC_1_DAC_R_SPK_PWD_MASK |
|
|
RK730_DAC_1_DAC_L_SPK_PWD_MASK,
|
|
RK730_DAC_1_DAC_SPK_IBIAS_ON |
|
|
RK730_DAC_1_DAC_R_SPK_PWU |
|
|
RK730_DAC_1_DAC_L_SPK_PWU);
|
|
snd_soc_component_update_bits(component, RK730_SPK_0,
|
|
RK730_SPK_0_ANTIPOP_PWR_MASK |
|
|
RK730_SPK_0_SPK_IBIAS_MASK |
|
|
RK730_SPK_0_SPK_OSTAGE_PWR_MASK |
|
|
RK730_SPK_0_SPK_BLOCK_PWR_MASK,
|
|
RK730_SPK_0_ANTIPOP_PWR_OFF |
|
|
RK730_SPK_0_SPK_IBIAS_ON |
|
|
RK730_SPK_0_SPK_OSTAGE_PWR_ON |
|
|
RK730_SPK_0_SPK_BLOCK_PWR_ON);
|
|
} else {
|
|
dev_dbg(component->dev, "%s off\n", __func__);
|
|
|
|
/* analog config */
|
|
snd_soc_component_update_bits(component, RK730_SPK_0,
|
|
RK730_SPK_0_ANTIPOP_PWR_MASK |
|
|
RK730_SPK_0_SPK_IBIAS_MASK |
|
|
RK730_SPK_0_SPK_OSTAGE_PWR_MASK |
|
|
RK730_SPK_0_SPK_BLOCK_PWR_MASK,
|
|
RK730_SPK_0_ANTIPOP_PWR_ON |
|
|
RK730_SPK_0_SPK_IBIAS_OFF |
|
|
RK730_SPK_0_SPK_OSTAGE_PWR_OFF |
|
|
RK730_SPK_0_SPK_BLOCK_PWR_OFF);
|
|
snd_soc_component_update_bits(component, RK730_DAC_1,
|
|
RK730_DAC_1_DAC_SPK_IBIAS_MASK |
|
|
RK730_DAC_1_DAC_R_SPK_PWD_MASK |
|
|
RK730_DAC_1_DAC_L_SPK_PWD_MASK,
|
|
RK730_DAC_1_DAC_SPK_IBIAS_OFF |
|
|
RK730_DAC_1_DAC_R_SPK_PWD |
|
|
RK730_DAC_1_DAC_L_SPK_PWD);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int rk730_out1_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))
|
|
dev_dbg(component->dev, "%s on\n", __func__);
|
|
else
|
|
dev_dbg(component->dev, "%s off\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int rk730_out2_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))
|
|
dev_dbg(component->dev, "%s on\n", __func__);
|
|
else
|
|
dev_dbg(component->dev, "%s off\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static const struct snd_kcontrol_new rk730_snd_controls[] = {
|
|
SOC_DOUBLE_R_TLV("ADC PGA Volume", RK730_ADC_PGA_BLOCK_0, RK730_ADC_PGA_BLOCK_1,
|
|
1, 0x7, 1, adc_tlv),
|
|
SOC_DOUBLE_R_TLV("MIC2 Boost Volume", RK730_MIC_BOOST_0, RK730_MIC_BOOST_1,
|
|
1, 0x7, 0, micboost_tlv),
|
|
SOC_DOUBLE_R_TLV("MIC1 Boost Volume", RK730_MIC_BOOST_2, RK730_MIC_BOOST_3,
|
|
1, 0x7, 0, micboost_tlv),
|
|
SOC_DOUBLE_R_TLV("MIC2 decrease Volume", RK730_MIC_BOOST_0, RK730_MIC_BOOST_1,
|
|
4, 0x3, 0, micdecrease_tlv),
|
|
SOC_DOUBLE_R_TLV("MIC1 decrease Volume", RK730_MIC_BOOST_2, RK730_MIC_BOOST_3,
|
|
4, 0x3, 0, micdecrease_tlv),
|
|
SOC_DOUBLE_R_TLV("ADC Digital Volume", RK730_DADC_VOLL, RK730_DADC_VOLR,
|
|
0, 0xff, 1, adc_dig_tlv),
|
|
SOC_DOUBLE_R_TLV("DAC Digital Volume", RK730_DDAC_VOLL, RK730_DDAC_VOLR,
|
|
0, 0xff, 1, dac_dig_tlv),
|
|
SOC_ENUM("DIG DLO VOLTAGE", dig_ldo_volt_enum),
|
|
SOC_ENUM("ANA DLO VOLTAGE", ana_ldo_volt_enum),
|
|
SOC_ENUM("MIC Chop Freq", mic_chop_freq_enum),
|
|
SOC_ENUM("ADC PGA Chop Freq", adc_pga_chop_freq_enum),
|
|
SOC_ENUM("HP / Lineout Chop Freq", hp_lo_chop_freq_enum),
|
|
SOC_ENUM("Mic Bias Volt", micbias_volt_enum),
|
|
SOC_ENUM("DAC HPF Center Freq", dac_hfp_center_freq_enum),
|
|
SOC_ENUM("ADC CAPACITY TRIM", adc_capacity_trim_enum),
|
|
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("DAC Left gain polarity", RK730_DTOP_VUCTL, 3, 1, 0),
|
|
SOC_SINGLE("DAC Right gain polarity", RK730_DTOP_VUCTL, 2, 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),
|
|
SOC_SINGLE("ADC CHOP EN", RK730_ADC_2, 4, 1, 0),
|
|
};
|
|
|
|
static const struct snd_soc_dapm_widget rk730_dapm_widgets[] = {
|
|
SND_SOC_DAPM_SUPPLY_S("ANA LDO", 1, RK730_LDO, 7, 0, NULL, 0),
|
|
SND_SOC_DAPM_SUPPLY_S("PLL", 1, SND_SOC_NOPM, 0, 0,
|
|
rk730_pll_event,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
SND_SOC_DAPM_SUPPLY_S("DAC BUF", 2, RK730_HK_TOP_2, 0, 1, NULL, 0),
|
|
SND_SOC_DAPM_SUPPLY_S("HK VAG BUF", 0, RK730_HK_TOP_1, 7, 1, NULL, 0),
|
|
SND_SOC_DAPM_SUPPLY_S("HK DAC BUF", 0, RK730_HK_TOP_1, 6, 1, NULL, 0),
|
|
SND_SOC_DAPM_SUPPLY_S("MICBIAS", 1, RK730_MIC_BIAS, 0, 1, NULL, 0),
|
|
SND_SOC_DAPM_DAC_E("DAC0", NULL, SND_SOC_NOPM, 0, 0,
|
|
rk730_dac0_event,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
SND_SOC_DAPM_DAC_E("DAC1", NULL, SND_SOC_NOPM, 0, 0,
|
|
rk730_dac1_event,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
SND_SOC_DAPM_DAC_E("DACL", "Left Playback", SND_SOC_NOPM, 0, 0,
|
|
rk730_dacl_event,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
SND_SOC_DAPM_DAC_E("DACR", "Right Playback", SND_SOC_NOPM, 0, 0,
|
|
rk730_dacr_event,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
SND_SOC_DAPM_SUPPLY("OUT1 Power", SND_SOC_NOPM, 0, 0,
|
|
rk730_out1_event,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
SND_SOC_DAPM_SUPPLY("OUT2 Power", SND_SOC_NOPM, 0, 0,
|
|
rk730_out2_event,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
SND_SOC_DAPM_SWITCH("OUT1", SND_SOC_NOPM, 0, 0, &rk730_out1_switch),
|
|
SND_SOC_DAPM_SWITCH("OUT2", SND_SOC_NOPM, 0, 0, &rk730_out2_switch),
|
|
SND_SOC_DAPM_OUT_DRV_E("OUT1 DRV", SND_SOC_NOPM, 0, 0, NULL, 0, rk730_out1_drv_event,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
SND_SOC_DAPM_OUT_DRV_E("OUT2 DRV", SND_SOC_NOPM, 0, 0, NULL, 0, rk730_out2_drv_event,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
SND_SOC_DAPM_OUTPUT("LOUT1"),
|
|
SND_SOC_DAPM_OUTPUT("ROUT1"),
|
|
SND_SOC_DAPM_OUTPUT("LOUT2"),
|
|
SND_SOC_DAPM_OUTPUT("ROUT2"),
|
|
SND_SOC_DAPM_AIF_IN_E("I2S IN", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0,
|
|
rk730_sdin_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
SND_SOC_DAPM_AIF_OUT_E("I2S OUT", "HiFi Capture", 0, SND_SOC_NOPM, 0, 0,
|
|
rk730_sdout_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
SND_SOC_DAPM_ADC_E("ADCL", "Left Capture", RK730_ADC_0, 0, 1,
|
|
rk730_adcl_event,
|
|
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
|
SND_SOC_DAPM_ADC_E("ADCR", "Right Capture", RK730_ADC_0, 1, 1,
|
|
rk730_adcr_event,
|
|
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
|
SND_SOC_DAPM_MUX("ADCSel Mux", SND_SOC_NOPM, 0, 1, &adc_data_sel),
|
|
SND_SOC_DAPM_MUX("Left PGA Mux", RK730_ADC_PGA_BLOCK_0, 0, 1, &mux_input_l),
|
|
SND_SOC_DAPM_MUX("Right PGA Mux", RK730_ADC_PGA_BLOCK_1, 0, 1, &mux_input_r),
|
|
/* 0:mic_r2_pwd 3:1 mic_r2_boost */
|
|
SND_SOC_DAPM_PGA("MIC2R", RK730_MIC_BOOST_0, 0, 1, NULL, 0),
|
|
SND_SOC_DAPM_PGA("MIC2L", RK730_MIC_BOOST_1, 0, 1, NULL, 0),
|
|
SND_SOC_DAPM_PGA("MIC1R", RK730_MIC_BOOST_2, 0, 1, NULL, 0),
|
|
SND_SOC_DAPM_PGA("MIC1L", 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_INPUT("MIC1"),
|
|
SND_SOC_DAPM_INPUT("MIC2"),
|
|
};
|
|
|
|
static const struct snd_soc_dapm_route rk730_dapm_routes[] = {
|
|
{"OUT1", NULL, "OUT1 Power"},
|
|
{"OUT2", NULL, "OUT2 Power"},
|
|
|
|
{"LOUT1", NULL, "OUT1 DRV"},
|
|
{"ROUT1", NULL, "OUT1 DRV"},
|
|
{"LOUT2", NULL, "OUT2 DRV"},
|
|
{"ROUT2", NULL, "OUT2 DRV"},
|
|
|
|
{"OUT1 DRV", NULL, "OUT1"},
|
|
{"OUT2 DRV", NULL, "OUT2"},
|
|
|
|
{"OUT1", "Switch", "DAC1"},
|
|
{"OUT2", "Switch", "DAC0"},
|
|
|
|
{"DAC1", NULL, "I2S IN"},
|
|
{"DAC0", NULL, "I2S IN"},
|
|
|
|
{"HK DAC BUF", NULL, "HK VAG BUF"},
|
|
{"I2S IN", NULL, "HK DAC BUF"},
|
|
{"I2S OUT", NULL, "HK VAG BUF"},
|
|
{"I2S IN", NULL, "PLL"},
|
|
{"I2S OUT", NULL, "PLL"},
|
|
{"I2S IN", NULL, "ANA LDO"},
|
|
{"I2S OUT", NULL, "ANA LDO"},
|
|
|
|
{"I2S OUT", NULL, "ADCR"},
|
|
{"I2S OUT", NULL, "ADCL"},
|
|
{"ADCR", NULL, "ADCSel Mux"},
|
|
{"ADCL", NULL, "ADCSel Mux"},
|
|
|
|
{"ADCSel Mux", "normal", "Left PGA Mux"},
|
|
{"ADCSel Mux", "normal", "Right PGA Mux"},
|
|
{"ADCSel Mux", "swap", "Left PGA Mux"},
|
|
{"ADCSel Mux", "swap", "Right PGA Mux"},
|
|
{"ADCSel Mux", "left", "Left PGA Mux"},
|
|
{"ADCSel Mux", "right", "Right PGA Mux"},
|
|
|
|
{"Left PGA Mux", "DIFF", "DIFFL"},
|
|
{"Left PGA Mux", "VINR2", "MIC2R"},
|
|
{"Left PGA Mux", "VINL2", "MIC2L"},
|
|
|
|
{"Right PGA Mux", "DIFF", "DIFFR"},
|
|
{"Right PGA Mux", "VINR1", "MIC1R"},
|
|
{"Right PGA Mux", "VINL1", "MIC1L"},
|
|
|
|
{"DIFFL", NULL, "MIC2R"},
|
|
{"DIFFL", NULL, "MIC2L"},
|
|
{"DIFFR", NULL, "MIC1R"},
|
|
{"DIFFR", NULL, "MIC1L"},
|
|
|
|
{"MIC1R", NULL, "MIC1"},
|
|
{"MIC1L", NULL, "MIC1"},
|
|
{"MIC2R", NULL, "MIC2"},
|
|
{"MIC2L", NULL, "MIC2"},
|
|
{"MIC1", NULL, "MICBIAS"},
|
|
{"MIC2", NULL, "MICBIAS"},
|
|
};
|
|
|
|
struct _coeff_div {
|
|
int mclk;
|
|
int rate;
|
|
char syspll_channel;
|
|
char fsclk_channel;
|
|
char refclk_channel;
|
|
};
|
|
|
|
/* codec hifi mclk clock divider coefficients */
|
|
static const struct _coeff_div coeff_div[] = {
|
|
/* mclk */
|
|
{12288000, 48000, 0x0, 0x1, 0x0},
|
|
{12288000, 96000, 0x0, 0x1, 0x0},
|
|
{12288000, 192000, 0x0, 0x1, 0x0},
|
|
{12288000, 44100, 0x1, 0x1, 0x0},
|
|
{12288000, 88200, 0x1, 0x1, 0x0},
|
|
{12288000, 176000, 0x1, 0x1, 0x0},
|
|
{12288000, 8000, 0x2, 0x3, 0x0},
|
|
{12288000, 16000, 0x2, 0x3, 0x0},
|
|
{12288000, 32000, 0x2, 0x3, 0x0},
|
|
{12288000, 64000, 0x2, 0x3, 0x0},
|
|
{12288000, 128000, 0x2, 0x3, 0x0},
|
|
|
|
{12000000, 48000, 0x3, 0x1, 0x0},
|
|
{12000000, 96000, 0x3, 0x1, 0x0},
|
|
{12000000, 192000, 0x3, 0x1, 0x0},
|
|
{12000000, 44100, 0x4, 0x1, 0x0},
|
|
{12000000, 88200, 0x4, 0x1, 0x0},
|
|
{12000000, 176000, 0x4, 0x1, 0x0},
|
|
{12000000, 8000, 0x5, 0x3, 0x0},
|
|
{12000000, 16000, 0x5, 0x3, 0x0},
|
|
{12000000, 32000, 0x5, 0x3, 0x0},
|
|
{12000000, 64000, 0x5, 0x3, 0x0},
|
|
{12000000, 128000, 0x5, 0x3, 0x0},
|
|
|
|
{24000000, 48000, 0x9, 0x1, 0x1},
|
|
{24000000, 96000, 0x9, 0x1, 0x1},
|
|
{24000000, 192000, 0x9, 0x1, 0x1},
|
|
{24000000, 44100, 0xa, 0x1, 0x1},
|
|
{24000000, 88200, 0xa, 0x1, 0x1},
|
|
{24000000, 176000, 0xa, 0x1, 0x1},
|
|
{24000000, 8000, 0xb, 0x3, 0x1},
|
|
{24000000, 16000, 0xb, 0x3, 0x1},
|
|
{24000000, 32000, 0xb, 0x3, 0x1},
|
|
{24000000, 64000, 0xb, 0x3, 0x1},
|
|
{24000000, 128000, 0xb, 0x3, 0x1},
|
|
|
|
{6144000, 48000, 0xc, 0x1, 0x0},
|
|
{6144000, 96000, 0xc, 0x1, 0x0},
|
|
{6144000, 192000, 0xc, 0x1, 0x0},
|
|
{11289600, 44100, 0xd, 0x1, 0x0},
|
|
{11289600, 88200, 0xd, 0x1, 0x0},
|
|
{11289600, 176000, 0xd, 0x1, 0x0},
|
|
{8192000, 8000, 0xe, 0x3, 0x0},
|
|
{8192000, 16000, 0xe, 0x3, 0x0},
|
|
{8192000, 32000, 0xe, 0x3, 0x0},
|
|
{8192000, 64000, 0xe, 0x3, 0x0},
|
|
{8192000, 128000, 0xe, 0x3, 0x0},
|
|
};
|
|
|
|
static inline int get_coeff(int mclk, int rate)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
|
|
if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
|
|
return i;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
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;
|
|
struct rk730_priv *rk730 = snd_soc_component_get_drvdata(component);
|
|
unsigned int rate;
|
|
int coeff;
|
|
|
|
coeff = get_coeff(rk730->sysclk, params_rate(params));
|
|
if (coeff < 0)
|
|
coeff = get_coeff(rk730->sysclk / 2, params_rate(params));
|
|
if (coeff < 0)
|
|
coeff = get_coeff(rk730->sysclk * 2, params_rate(params));
|
|
if (coeff < 0) {
|
|
dev_err(component->dev,
|
|
"Unable to configure sample rate %dHz with %dHz MCLK\n",
|
|
params_rate(params), rk730->sysclk);
|
|
return coeff;
|
|
}
|
|
dev_info(component->dev, "%s:index %d mclk=%d rate=%d\n",
|
|
__func__, coeff, coeff_div[coeff].mclk, coeff_div[coeff].rate);
|
|
|
|
switch (params_format(params)) {
|
|
case SNDRV_PCM_FORMAT_S16_LE:
|
|
snd_soc_component_update_bits(component, RK730_DI2S_RXCR2,
|
|
RK730_DI2S_RXCR2_VDW_MASK,
|
|
RK730_DI2S_RXCR2_VDW(16));
|
|
snd_soc_component_update_bits(component, RK730_DI2S_TXCR2,
|
|
RK730_DI2S_TXCR2_VDW_MASK,
|
|
RK730_DI2S_TXCR2_VDW(16));
|
|
break;
|
|
case SNDRV_PCM_FORMAT_S24_LE:
|
|
case SNDRV_PCM_FORMAT_S32_LE:
|
|
snd_soc_component_update_bits(component, RK730_DI2S_RXCR2,
|
|
RK730_DI2S_RXCR2_VDW_MASK,
|
|
RK730_DI2S_RXCR2_VDW(24));
|
|
snd_soc_component_update_bits(component, RK730_DI2S_TXCR2,
|
|
RK730_DI2S_TXCR2_VDW_MASK,
|
|
RK730_DI2S_TXCR2_VDW(24));
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
rate = samplerate_to_bit(params_rate(params));
|
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
|
snd_soc_component_update_bits(component, RK730_DTOP_SRT,
|
|
RK730_DTOP_DACSRT_MASK,
|
|
RK730_DTOP_DACSRT(rate));
|
|
} else {
|
|
snd_soc_component_update_bits(component, RK730_DTOP_SRT,
|
|
RK730_DTOP_ADCSRT_MASK,
|
|
RK730_DTOP_ADCSRT(rate));
|
|
}
|
|
snd_soc_component_write(component, RK730_SYSPLL_3,
|
|
coeff_div[coeff].syspll_channel << 4 |
|
|
coeff_div[coeff].fsclk_channel << 2 |
|
|
coeff_div[coeff].refclk_channel << 0);
|
|
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;
|
|
u8 val = 0, format = 0;
|
|
u8 mask = RK730_DI2S_TXCR1_TFS_TX_MASK;
|
|
int ret = 0;
|
|
|
|
/* interface format */
|
|
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
|
case SND_SOC_DAIFMT_I2S:
|
|
mask |= RK730_DI2S_TXCR1_IBM_TX_MASK;
|
|
format = RK730_DI2S_TXCR1_TFS_TX_I2S | RK730_DI2S_TXCR1_IBM_TX_NORMAL;
|
|
break;
|
|
case SND_SOC_DAIFMT_RIGHT_J:
|
|
mask |= RK730_DI2S_TXCR1_IBM_TX_MASK;
|
|
format = RK730_DI2S_TXCR1_TFS_TX_I2S | RK730_DI2S_TXCR1_IBM_TX_RIGHT;
|
|
break;
|
|
case SND_SOC_DAIFMT_LEFT_J:
|
|
mask |= RK730_DI2S_TXCR1_PBM_TX_MASK;
|
|
format = RK730_DI2S_TXCR1_TFS_TX_I2S | RK730_DI2S_TXCR1_IBM_TX_LEFT;
|
|
break;
|
|
case SND_SOC_DAIFMT_DSP_A:
|
|
mask |= RK730_DI2S_TXCR1_PBM_TX_MASK;
|
|
format = RK730_DI2S_TXCR1_TFS_TX_PCM | RK730_DI2S_TXCR1_PBM_TX_NODELAY;
|
|
break;
|
|
case SND_SOC_DAIFMT_DSP_B:
|
|
mask |= RK730_DI2S_TXCR1_PBM_TX_MASK;
|
|
format = RK730_DI2S_TXCR1_TFS_TX_PCM | RK730_DI2S_TXCR1_PBM_TX_DELAY1;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
snd_soc_component_update_bits(component, RK730_DI2S_RXCR1, mask, format);
|
|
snd_soc_component_update_bits(component, RK730_DI2S_TXCR1, mask, format);
|
|
|
|
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
|
case SND_SOC_DAIFMT_IB_NF:
|
|
val |= RK730_DI2S_CKM_SCLK_INVERTED;
|
|
break;
|
|
case SND_SOC_DAIFMT_NB_NF:
|
|
val |= RK730_DI2S_CKM_SCLK_NORMAL;
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
}
|
|
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
|
case SND_SOC_DAIFMT_CBS_CFS:
|
|
val |= RK730_DI2S_CKM_MST_SLAVE | RK730_DI2S_CKM_SCLK_DIS;
|
|
break;
|
|
case SND_SOC_DAIFMT_CBM_CFM:
|
|
val |= RK730_DI2S_CKM_MST_MASTER | RK730_DI2S_CKM_SCLK_EN;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
snd_soc_component_update_bits(component, RK730_DI2S_CKM,
|
|
RK730_DI2S_CKM_MST_MASK |
|
|
RK730_DI2S_CKM_SCLK_POL_MASK |
|
|
RK730_DI2S_CKM_SCLK_EN_MASK,
|
|
val);
|
|
return ret;
|
|
}
|
|
|
|
static int rk730_dai_mute(struct snd_soc_dai *codec_dai, int mute, int stream)
|
|
{
|
|
struct snd_soc_component *component = codec_dai->component;
|
|
|
|
dev_dbg(component->dev, "%s %d stream %d\n", __func__, mute, stream);
|
|
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
|
if (mute) {
|
|
snd_soc_component_update_bits(component, RK730_DADC_SEL,
|
|
RK730_DADC_DAC_MUTE_MASK,
|
|
RK730_DADC_DAC_MUTE);
|
|
} else {
|
|
snd_soc_component_update_bits(component, RK730_DADC_SEL,
|
|
RK730_DADC_DAC_MUTE_MASK,
|
|
RK730_DADC_DAC_UNMUTE);
|
|
}
|
|
} else {
|
|
if (mute) {
|
|
snd_soc_component_update_bits(component, RK730_DADC_SEL,
|
|
RK730_DADC_ADC_MUTE_MASK,
|
|
RK730_DADC_ADC_L_MUTE |
|
|
RK730_DADC_ADC_R_MUTE);
|
|
} else {
|
|
snd_soc_component_update_bits(component, RK730_DADC_SEL,
|
|
RK730_DADC_ADC_MUTE_MASK,
|
|
RK730_DADC_ADC_L_UNMUTE |
|
|
RK730_DADC_ADC_R_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;
|
|
}
|
|
|
|
/*
|
|
* Note that this should be called from init rather than from hw_params.
|
|
*/
|
|
static int rk730_set_dai_sysclk(struct snd_soc_dai *codec_dai,
|
|
int clk_id, unsigned int freq, int dir)
|
|
{
|
|
struct snd_soc_component *component = codec_dai->component;
|
|
struct rk730_priv *rk730 = snd_soc_component_get_drvdata(component);
|
|
|
|
rk730->sysclk = freq;
|
|
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,
|
|
.set_sysclk = rk730_set_dai_sysclk,
|
|
};
|
|
|
|
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);
|
|
udelay(10);
|
|
snd_soc_component_update_bits(component, RK730_DTOP_SRT,
|
|
RK730_DTOP_SRST_MASK,
|
|
RK730_DTOP_SRST_EN);
|
|
snd_soc_component_update_bits(component, RK730_DTOP_SRT,
|
|
RK730_DTOP_SRST_MASK,
|
|
RK730_DTOP_SRST_DIS);
|
|
/* 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 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,
|
|
};
|
|
|
|
static const struct reg_default rk730_reg_defaults[] = {
|
|
{0x00, 0x08},
|
|
{0x01, 0xc0},
|
|
{0x02, 0x00},
|
|
{0x03, 0x00},
|
|
{0x04, 0x03},
|
|
{0x05, 0x42},
|
|
{0x06, 0x02},
|
|
{0x07, 0x05},
|
|
{0x08, 0x07},
|
|
{0x09, 0x70},
|
|
{0x0a, 0x01},
|
|
{0x0b, 0x01},
|
|
{0x0c, 0x01},
|
|
{0x0d, 0x01},
|
|
{0x0e, 0x01},
|
|
{0x0f, 0x01},
|
|
{0x10, 0xff},
|
|
{0x11, 0x07},
|
|
{0x12, 0x54},
|
|
{0x13, 0x04},
|
|
{0x14, 0x23},
|
|
{0x15, 0x35},
|
|
{0x16, 0x67},
|
|
{0x17, 0x1e},
|
|
{0x18, 0xc0},
|
|
{0x19, 0x13},
|
|
{0x1a, 0x04},
|
|
{0x1b, 0x20},
|
|
{0x1c, 0x00},
|
|
{0x1d, 0x00},
|
|
{0x1e, 0x90},
|
|
{0x1f, 0x11},
|
|
{0x20, 0x09},
|
|
{0x21, 0xac},
|
|
{0x22, 0x80},
|
|
{0x23, 0x00},
|
|
{0x24, 0xac},
|
|
{0x25, 0x80},
|
|
{0x26, 0x00},
|
|
{0x27, 0x00},
|
|
{0x28, 0x20},
|
|
{0x29, 0x00},
|
|
{0x40, 0x00},
|
|
{0x41, 0x00},
|
|
{0x42, 0x00},
|
|
{0x43, 0x88},
|
|
{0x44, 0x01},
|
|
{0x45, 0xff},
|
|
{0x46, 0x00},
|
|
{0x47, 0x00},
|
|
{0x48, 0x00},
|
|
{0x49, 0x00},
|
|
{0x4a, 0x00},
|
|
{0x4b, 0x00},
|
|
{0x4c, 0xff},
|
|
{0x4d, 0xff},
|
|
{0x4e, 0x1e},
|
|
{0x4f, 0xf4},
|
|
{0x50, 0x00},
|
|
{0x51, 0x00},
|
|
{0x52, 0x1f},
|
|
{0x53, 0x1f},
|
|
{0x54, 0x02},
|
|
{0x55, 0x00},
|
|
{0x56, 0xc0},
|
|
{0x57, 0x06},
|
|
{0x58, 0x60},
|
|
{0x59, 0x07},
|
|
{0x5a, 0x03},
|
|
{0x5b, 0x00},
|
|
{0x5c, 0x03},
|
|
{0x5d, 0x66},
|
|
{0x5e, 0x9a},
|
|
{0x5f, 0xcc},
|
|
{0x60, 0x1f},
|
|
{0x61, 0xcc},
|
|
{0x62, 0x0e},
|
|
{0x63, 0x08},
|
|
{0x64, 0x06},
|
|
{0x65, 0x02},
|
|
{0x66, 0x05},
|
|
{0x67, 0x01},
|
|
{0x68, 0x00},
|
|
{0x69, 0x01},
|
|
{0x6a, 0x00},
|
|
{0x6b, 0x00},
|
|
{0x6c, 0x00},
|
|
{0x6d, 0x17},
|
|
{0x6e, 0x00},
|
|
{0x6f, 0x00},
|
|
{0x70, 0x17},
|
|
{0x71, 0x00},
|
|
};
|
|
|
|
static const struct regmap_config rk730_regmap = {
|
|
.reg_bits = 8,
|
|
.val_bits = 8,
|
|
.max_register = RK730_DI2S_TXCR3_TXCMD,
|
|
.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");
|