Commit 547a4a6a authored by Takashi Iwai's avatar Takashi Iwai

Merge tag 'asoc-fix-v5.16-rc1' of...

Merge tag 'asoc-fix-v5.16-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus

ASoC: Fixes for v5.16

A relatively large collection of fixes that came in since the merge
window, though a lot of this is just a collection of new machine quirks
for x86 platforms.
parents 05ec7161 424fe7ed
...@@ -612,6 +612,12 @@ static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = { ...@@ -612,6 +612,12 @@ static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = {
SND_SOC_DAPM_AIF_OUT("ASPTX3", NULL, 0, CS35L41_SP_ENABLES, 2, 0), SND_SOC_DAPM_AIF_OUT("ASPTX3", NULL, 0, CS35L41_SP_ENABLES, 2, 0),
SND_SOC_DAPM_AIF_OUT("ASPTX4", NULL, 0, CS35L41_SP_ENABLES, 3, 0), SND_SOC_DAPM_AIF_OUT("ASPTX4", NULL, 0, CS35L41_SP_ENABLES, 3, 0),
SND_SOC_DAPM_SIGGEN("VSENSE"),
SND_SOC_DAPM_SIGGEN("ISENSE"),
SND_SOC_DAPM_SIGGEN("VP"),
SND_SOC_DAPM_SIGGEN("VBST"),
SND_SOC_DAPM_SIGGEN("TEMP"),
SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L41_PWR_CTRL2, 12, 0), SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L41_PWR_CTRL2, 12, 0),
SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L41_PWR_CTRL2, 13, 0), SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L41_PWR_CTRL2, 13, 0),
SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L41_PWR_CTRL2, 8, 0), SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L41_PWR_CTRL2, 8, 0),
...@@ -623,12 +629,6 @@ static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = { ...@@ -623,12 +629,6 @@ static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = {
cs35l41_main_amp_event, cs35l41_main_amp_event,
SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_INPUT("VP"),
SND_SOC_DAPM_INPUT("VBST"),
SND_SOC_DAPM_INPUT("ISENSE"),
SND_SOC_DAPM_INPUT("VSENSE"),
SND_SOC_DAPM_INPUT("TEMP"),
SND_SOC_DAPM_MUX("ASP TX1 Source", SND_SOC_NOPM, 0, 0, &asp_tx1_mux), SND_SOC_DAPM_MUX("ASP TX1 Source", SND_SOC_NOPM, 0, 0, &asp_tx1_mux),
SND_SOC_DAPM_MUX("ASP TX2 Source", SND_SOC_NOPM, 0, 0, &asp_tx2_mux), SND_SOC_DAPM_MUX("ASP TX2 Source", SND_SOC_NOPM, 0, 0, &asp_tx2_mux),
SND_SOC_DAPM_MUX("ASP TX3 Source", SND_SOC_NOPM, 0, 0, &asp_tx3_mux), SND_SOC_DAPM_MUX("ASP TX3 Source", SND_SOC_NOPM, 0, 0, &asp_tx3_mux),
...@@ -674,8 +674,8 @@ static const struct snd_soc_dapm_route cs35l41_audio_map[] = { ...@@ -674,8 +674,8 @@ static const struct snd_soc_dapm_route cs35l41_audio_map[] = {
{"VMON ADC", NULL, "VSENSE"}, {"VMON ADC", NULL, "VSENSE"},
{"IMON ADC", NULL, "ISENSE"}, {"IMON ADC", NULL, "ISENSE"},
{"VPMON ADC", NULL, "VP"}, {"VPMON ADC", NULL, "VP"},
{"TEMPMON ADC", NULL, "TEMP"},
{"VBSTMON ADC", NULL, "VBST"}, {"VBSTMON ADC", NULL, "VBST"},
{"TEMPMON ADC", NULL, "TEMP"},
{"ASPRX1", NULL, "AMP Playback"}, {"ASPRX1", NULL, "AMP Playback"},
{"ASPRX2", NULL, "AMP Playback"}, {"ASPRX2", NULL, "AMP Playback"},
......
...@@ -2188,7 +2188,7 @@ static int rx_macro_config_classh(struct snd_soc_component *component, ...@@ -2188,7 +2188,7 @@ static int rx_macro_config_classh(struct snd_soc_component *component,
snd_soc_component_update_bits(component, snd_soc_component_update_bits(component,
CDC_RX_CLSH_DECAY_CTRL, CDC_RX_CLSH_DECAY_CTRL,
CDC_RX_CLSH_DECAY_RATE_MASK, 0x0); CDC_RX_CLSH_DECAY_RATE_MASK, 0x0);
snd_soc_component_update_bits(component, snd_soc_component_write_field(component,
CDC_RX_RX1_RX_PATH_CFG0, CDC_RX_RX1_RX_PATH_CFG0,
CDC_RX_RXn_CLSH_EN_MASK, 0x1); CDC_RX_RXn_CLSH_EN_MASK, 0x1);
break; break;
......
...@@ -1311,13 +1311,54 @@ static int rt1011_r0_load_info(struct snd_kcontrol *kcontrol, ...@@ -1311,13 +1311,54 @@ static int rt1011_r0_load_info(struct snd_kcontrol *kcontrol,
.put = rt1011_r0_load_mode_put \ .put = rt1011_r0_load_mode_put \
} }
static const char * const rt1011_i2s_ref_texts[] = { static const char * const rt1011_i2s_ref[] = {
"Left Channel", "Right Channel" "None", "Left Channel", "Right Channel"
}; };
static SOC_ENUM_SINGLE_DECL(rt1011_i2s_ref_enum, static SOC_ENUM_SINGLE_DECL(rt1011_i2s_ref_enum, 0, 0,
RT1011_TDM1_SET_1, 7, rt1011_i2s_ref);
rt1011_i2s_ref_texts);
static int rt1011_i2s_ref_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component =
snd_soc_kcontrol_component(kcontrol);
struct rt1011_priv *rt1011 =
snd_soc_component_get_drvdata(component);
rt1011->i2s_ref = ucontrol->value.enumerated.item[0];
switch (rt1011->i2s_ref) {
case RT1011_I2S_REF_LEFT_CH:
regmap_write(rt1011->regmap, RT1011_TDM_TOTAL_SET, 0x0240);
regmap_write(rt1011->regmap, RT1011_TDM1_SET_2, 0x8);
regmap_write(rt1011->regmap, RT1011_TDM1_SET_1, 0x1022);
regmap_write(rt1011->regmap, RT1011_ADCDAT_OUT_SOURCE, 0x4);
break;
case RT1011_I2S_REF_RIGHT_CH:
regmap_write(rt1011->regmap, RT1011_TDM_TOTAL_SET, 0x0240);
regmap_write(rt1011->regmap, RT1011_TDM1_SET_2, 0x8);
regmap_write(rt1011->regmap, RT1011_TDM1_SET_1, 0x10a2);
regmap_write(rt1011->regmap, RT1011_ADCDAT_OUT_SOURCE, 0x4);
break;
default:
dev_info(component->dev, "I2S Reference: Do nothing\n");
}
return 0;
}
static int rt1011_i2s_ref_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component =
snd_soc_kcontrol_component(kcontrol);
struct rt1011_priv *rt1011 =
snd_soc_component_get_drvdata(component);
ucontrol->value.enumerated.item[0] = rt1011->i2s_ref;
return 0;
}
static const struct snd_kcontrol_new rt1011_snd_controls[] = { static const struct snd_kcontrol_new rt1011_snd_controls[] = {
/* I2S Data In Selection */ /* I2S Data In Selection */
...@@ -1358,7 +1399,8 @@ static const struct snd_kcontrol_new rt1011_snd_controls[] = { ...@@ -1358,7 +1399,8 @@ static const struct snd_kcontrol_new rt1011_snd_controls[] = {
SOC_SINGLE("R0 Temperature", RT1011_STP_INITIAL_RESISTANCE_TEMP, SOC_SINGLE("R0 Temperature", RT1011_STP_INITIAL_RESISTANCE_TEMP,
2, 255, 0), 2, 255, 0),
/* I2S Reference */ /* I2S Reference */
SOC_ENUM("I2S Reference", rt1011_i2s_ref_enum), SOC_ENUM_EXT("I2S Reference", rt1011_i2s_ref_enum,
rt1011_i2s_ref_get, rt1011_i2s_ref_put),
}; };
static int rt1011_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, static int rt1011_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
...@@ -2017,6 +2059,7 @@ static int rt1011_probe(struct snd_soc_component *component) ...@@ -2017,6 +2059,7 @@ static int rt1011_probe(struct snd_soc_component *component)
schedule_work(&rt1011->cali_work); schedule_work(&rt1011->cali_work);
rt1011->i2s_ref = 0;
rt1011->bq_drc_params = devm_kcalloc(component->dev, rt1011->bq_drc_params = devm_kcalloc(component->dev,
RT1011_ADVMODE_NUM, sizeof(struct rt1011_bq_drc_params *), RT1011_ADVMODE_NUM, sizeof(struct rt1011_bq_drc_params *),
GFP_KERNEL); GFP_KERNEL);
......
...@@ -654,6 +654,12 @@ enum { ...@@ -654,6 +654,12 @@ enum {
RT1011_AIFS RT1011_AIFS
}; };
enum {
RT1011_I2S_REF_NONE,
RT1011_I2S_REF_LEFT_CH,
RT1011_I2S_REF_RIGHT_CH,
};
/* BiQual & DRC related settings */ /* BiQual & DRC related settings */
#define RT1011_BQ_DRC_NUM 128 #define RT1011_BQ_DRC_NUM 128
struct rt1011_bq_drc_params { struct rt1011_bq_drc_params {
...@@ -692,6 +698,7 @@ struct rt1011_priv { ...@@ -692,6 +698,7 @@ struct rt1011_priv {
unsigned int r0_reg, cali_done; unsigned int r0_reg, cali_done;
unsigned int r0_calib, temperature_calib; unsigned int r0_calib, temperature_calib;
int recv_spk_mode; int recv_spk_mode;
int i2s_ref;
}; };
#endif /* end of _RT1011_H_ */ #endif /* end of _RT1011_H_ */
...@@ -198,6 +198,7 @@ static int rt5682_i2c_probe(struct i2c_client *i2c, ...@@ -198,6 +198,7 @@ static int rt5682_i2c_probe(struct i2c_client *i2c,
} }
mutex_init(&rt5682->calibrate_mutex); mutex_init(&rt5682->calibrate_mutex);
mutex_init(&rt5682->jdet_mutex);
rt5682_calibrate(rt5682); rt5682_calibrate(rt5682);
rt5682_apply_patch_list(rt5682, &i2c->dev); rt5682_apply_patch_list(rt5682, &i2c->dev);
......
...@@ -48,6 +48,8 @@ static const struct reg_sequence patch_list[] = { ...@@ -48,6 +48,8 @@ static const struct reg_sequence patch_list[] = {
{RT5682_SAR_IL_CMD_6, 0x0110}, {RT5682_SAR_IL_CMD_6, 0x0110},
{RT5682_CHARGE_PUMP_1, 0x0210}, {RT5682_CHARGE_PUMP_1, 0x0210},
{RT5682_HP_LOGIC_CTRL_2, 0x0007}, {RT5682_HP_LOGIC_CTRL_2, 0x0007},
{RT5682_SAR_IL_CMD_2, 0xac00},
{RT5682_CBJ_CTRL_7, 0x0104},
}; };
void rt5682_apply_patch_list(struct rt5682_priv *rt5682, struct device *dev) void rt5682_apply_patch_list(struct rt5682_priv *rt5682, struct device *dev)
...@@ -940,6 +942,10 @@ int rt5682_headset_detect(struct snd_soc_component *component, int jack_insert) ...@@ -940,6 +942,10 @@ int rt5682_headset_detect(struct snd_soc_component *component, int jack_insert)
snd_soc_component_update_bits(component, snd_soc_component_update_bits(component,
RT5682_HP_CHARGE_PUMP_1, RT5682_HP_CHARGE_PUMP_1,
RT5682_OSW_L_MASK | RT5682_OSW_R_MASK, 0); RT5682_OSW_L_MASK | RT5682_OSW_R_MASK, 0);
rt5682_enable_push_button_irq(component, false);
snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1,
RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_LOW);
usleep_range(55000, 60000);
snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1,
RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_HIGH); RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_HIGH);
...@@ -1092,6 +1098,7 @@ void rt5682_jack_detect_handler(struct work_struct *work) ...@@ -1092,6 +1098,7 @@ void rt5682_jack_detect_handler(struct work_struct *work)
while (!rt5682->component->card->instantiated) while (!rt5682->component->card->instantiated)
usleep_range(10000, 15000); usleep_range(10000, 15000);
mutex_lock(&rt5682->jdet_mutex);
mutex_lock(&rt5682->calibrate_mutex); mutex_lock(&rt5682->calibrate_mutex);
val = snd_soc_component_read(rt5682->component, RT5682_AJD1_CTRL) val = snd_soc_component_read(rt5682->component, RT5682_AJD1_CTRL)
...@@ -1165,6 +1172,7 @@ void rt5682_jack_detect_handler(struct work_struct *work) ...@@ -1165,6 +1172,7 @@ void rt5682_jack_detect_handler(struct work_struct *work)
} }
mutex_unlock(&rt5682->calibrate_mutex); mutex_unlock(&rt5682->calibrate_mutex);
mutex_unlock(&rt5682->jdet_mutex);
} }
EXPORT_SYMBOL_GPL(rt5682_jack_detect_handler); EXPORT_SYMBOL_GPL(rt5682_jack_detect_handler);
...@@ -1514,6 +1522,7 @@ static int rt5682_hp_event(struct snd_soc_dapm_widget *w, ...@@ -1514,6 +1522,7 @@ static int rt5682_hp_event(struct snd_soc_dapm_widget *w,
{ {
struct snd_soc_component *component = struct snd_soc_component *component =
snd_soc_dapm_to_component(w->dapm); snd_soc_dapm_to_component(w->dapm);
struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
switch (event) { switch (event) {
case SND_SOC_DAPM_PRE_PMU: case SND_SOC_DAPM_PRE_PMU:
...@@ -1525,12 +1534,17 @@ static int rt5682_hp_event(struct snd_soc_dapm_widget *w, ...@@ -1525,12 +1534,17 @@ static int rt5682_hp_event(struct snd_soc_dapm_widget *w,
RT5682_DEPOP_1, 0x60, 0x60); RT5682_DEPOP_1, 0x60, 0x60);
snd_soc_component_update_bits(component, snd_soc_component_update_bits(component,
RT5682_DAC_ADC_DIG_VOL1, 0x00c0, 0x0080); RT5682_DAC_ADC_DIG_VOL1, 0x00c0, 0x0080);
mutex_lock(&rt5682->jdet_mutex);
snd_soc_component_update_bits(component, RT5682_HP_CTRL_2, snd_soc_component_update_bits(component, RT5682_HP_CTRL_2,
RT5682_HP_C2_DAC_L_EN | RT5682_HP_C2_DAC_R_EN, RT5682_HP_C2_DAC_L_EN | RT5682_HP_C2_DAC_R_EN,
RT5682_HP_C2_DAC_L_EN | RT5682_HP_C2_DAC_R_EN); RT5682_HP_C2_DAC_L_EN | RT5682_HP_C2_DAC_R_EN);
usleep_range(5000, 10000); usleep_range(5000, 10000);
snd_soc_component_update_bits(component, RT5682_CHARGE_PUMP_1, snd_soc_component_update_bits(component, RT5682_CHARGE_PUMP_1,
RT5682_CP_SW_SIZE_MASK, RT5682_CP_SW_SIZE_L); RT5682_CP_SW_SIZE_MASK, RT5682_CP_SW_SIZE_L);
mutex_unlock(&rt5682->jdet_mutex);
break; break;
case SND_SOC_DAPM_POST_PMD: case SND_SOC_DAPM_POST_PMD:
...@@ -2942,10 +2956,7 @@ static int rt5682_suspend(struct snd_soc_component *component) ...@@ -2942,10 +2956,7 @@ static int rt5682_suspend(struct snd_soc_component *component)
cancel_delayed_work_sync(&rt5682->jack_detect_work); cancel_delayed_work_sync(&rt5682->jack_detect_work);
cancel_delayed_work_sync(&rt5682->jd_check_work); cancel_delayed_work_sync(&rt5682->jd_check_work);
if (rt5682->hs_jack && rt5682->jack_type == SND_JACK_HEADSET) { if (rt5682->hs_jack && (rt5682->jack_type & SND_JACK_HEADSET) == SND_JACK_HEADSET) {
snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1,
RT5682_MB1_PATH_MASK | RT5682_MB2_PATH_MASK,
RT5682_CTRL_MB1_REG | RT5682_CTRL_MB2_REG);
val = snd_soc_component_read(component, val = snd_soc_component_read(component,
RT5682_CBJ_CTRL_2) & RT5682_JACK_TYPE_MASK; RT5682_CBJ_CTRL_2) & RT5682_JACK_TYPE_MASK;
...@@ -2967,10 +2978,17 @@ static int rt5682_suspend(struct snd_soc_component *component) ...@@ -2967,10 +2978,17 @@ static int rt5682_suspend(struct snd_soc_component *component)
/* enter SAR ADC power saving mode */ /* enter SAR ADC power saving mode */
snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1,
RT5682_SAR_BUTT_DET_MASK | RT5682_SAR_BUTDET_MODE_MASK | RT5682_SAR_BUTT_DET_MASK | RT5682_SAR_BUTDET_MODE_MASK |
RT5682_SAR_BUTDET_RST_MASK | RT5682_SAR_SEL_MB1_MB2_MASK, 0); RT5682_SAR_SEL_MB1_MB2_MASK, 0);
usleep_range(5000, 6000);
snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1,
RT5682_MB1_PATH_MASK | RT5682_MB2_PATH_MASK,
RT5682_CTRL_MB1_REG | RT5682_CTRL_MB2_REG);
usleep_range(10000, 12000);
snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1,
RT5682_SAR_BUTT_DET_MASK | RT5682_SAR_BUTDET_MODE_MASK | RT5682_SAR_BUTDET_RST_MASK, RT5682_SAR_BUTT_DET_MASK | RT5682_SAR_BUTDET_MODE_MASK,
RT5682_SAR_BUTT_DET_EN | RT5682_SAR_BUTDET_POW_SAV | RT5682_SAR_BUTDET_RST_NORMAL); RT5682_SAR_BUTT_DET_EN | RT5682_SAR_BUTDET_POW_SAV);
snd_soc_component_update_bits(component, RT5682_HP_CHARGE_PUMP_1,
RT5682_OSW_L_MASK | RT5682_OSW_R_MASK, 0);
} }
regcache_cache_only(rt5682->regmap, true); regcache_cache_only(rt5682->regmap, true);
...@@ -2988,10 +3006,11 @@ static int rt5682_resume(struct snd_soc_component *component) ...@@ -2988,10 +3006,11 @@ static int rt5682_resume(struct snd_soc_component *component)
regcache_cache_only(rt5682->regmap, false); regcache_cache_only(rt5682->regmap, false);
regcache_sync(rt5682->regmap); regcache_sync(rt5682->regmap);
if (rt5682->hs_jack && rt5682->jack_type == SND_JACK_HEADSET) { if (rt5682->hs_jack && (rt5682->jack_type & SND_JACK_HEADSET) == SND_JACK_HEADSET) {
snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1,
RT5682_SAR_BUTDET_MODE_MASK | RT5682_SAR_SEL_MB1_MB2_MASK, RT5682_SAR_BUTDET_MODE_MASK | RT5682_SAR_SEL_MB1_MB2_MASK,
RT5682_SAR_BUTDET_POW_NORM | RT5682_SAR_SEL_MB1_MB2_AUTO); RT5682_SAR_BUTDET_POW_NORM | RT5682_SAR_SEL_MB1_MB2_AUTO);
usleep_range(5000, 6000);
snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1,
RT5682_MB1_PATH_MASK | RT5682_MB2_PATH_MASK, RT5682_MB1_PATH_MASK | RT5682_MB2_PATH_MASK,
RT5682_CTRL_MB1_FSM | RT5682_CTRL_MB2_FSM); RT5682_CTRL_MB1_FSM | RT5682_CTRL_MB2_FSM);
...@@ -2999,8 +3018,9 @@ static int rt5682_resume(struct snd_soc_component *component) ...@@ -2999,8 +3018,9 @@ static int rt5682_resume(struct snd_soc_component *component)
RT5682_PWR_CBJ, RT5682_PWR_CBJ); RT5682_PWR_CBJ, RT5682_PWR_CBJ);
} }
rt5682->jack_type = 0;
mod_delayed_work(system_power_efficient_wq, mod_delayed_work(system_power_efficient_wq,
&rt5682->jack_detect_work, msecs_to_jiffies(250)); &rt5682->jack_detect_work, msecs_to_jiffies(0));
return 0; return 0;
} }
......
...@@ -1463,6 +1463,7 @@ struct rt5682_priv { ...@@ -1463,6 +1463,7 @@ struct rt5682_priv {
int jack_type; int jack_type;
int irq_work_delay_time; int irq_work_delay_time;
struct mutex jdet_mutex;
}; };
extern const char *rt5682_supply_names[RT5682_NUM_SUPPLIES]; extern const char *rt5682_supply_names[RT5682_NUM_SUPPLIES];
......
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
#include <linux/bits.h> #include <linux/bits.h>
#include <linux/bitfield.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/gpio/consumer.h> #include <linux/gpio/consumer.h>
#include <linux/i2c.h> #include <linux/i2c.h>
...@@ -23,9 +24,11 @@ ...@@ -23,9 +24,11 @@
#define RT9120_REG_ERRRPT 0x10 #define RT9120_REG_ERRRPT 0x10
#define RT9120_REG_MSVOL 0x20 #define RT9120_REG_MSVOL 0x20
#define RT9120_REG_SWRESET 0x40 #define RT9120_REG_SWRESET 0x40
#define RT9120_REG_INTERCFG 0x63
#define RT9120_REG_INTERNAL0 0x65 #define RT9120_REG_INTERNAL0 0x65
#define RT9120_REG_INTERNAL1 0x69 #define RT9120_REG_INTERNAL1 0x69
#define RT9120_REG_UVPOPT 0x6C #define RT9120_REG_UVPOPT 0x6C
#define RT9120_REG_DIGCFG 0xF8
#define RT9120_VID_MASK GENMASK(15, 8) #define RT9120_VID_MASK GENMASK(15, 8)
#define RT9120_SWRST_MASK BIT(7) #define RT9120_SWRST_MASK BIT(7)
...@@ -46,8 +49,10 @@ ...@@ -46,8 +49,10 @@
#define RT9120_CFG_WORDLEN_24 24 #define RT9120_CFG_WORDLEN_24 24
#define RT9120_CFG_WORDLEN_32 32 #define RT9120_CFG_WORDLEN_32 32
#define RT9120_DVDD_UVSEL_MASK GENMASK(5, 4) #define RT9120_DVDD_UVSEL_MASK GENMASK(5, 4)
#define RT9120_AUTOSYNC_MASK BIT(6)
#define RT9120_VENDOR_ID 0x4200 #define RT9120_VENDOR_ID 0x42
#define RT9120S_VENDOR_ID 0x43
#define RT9120_RESET_WAITMS 20 #define RT9120_RESET_WAITMS 20
#define RT9120_CHIPON_WAITMS 20 #define RT9120_CHIPON_WAITMS 20
#define RT9120_AMPON_WAITMS 50 #define RT9120_AMPON_WAITMS 50
...@@ -61,9 +66,16 @@ ...@@ -61,9 +66,16 @@
SNDRV_PCM_FMTBIT_S24_LE |\ SNDRV_PCM_FMTBIT_S24_LE |\
SNDRV_PCM_FMTBIT_S32_LE) SNDRV_PCM_FMTBIT_S32_LE)
enum {
CHIP_IDX_RT9120 = 0,
CHIP_IDX_RT9120S,
CHIP_IDX_MAX
};
struct rt9120_data { struct rt9120_data {
struct device *dev; struct device *dev;
struct regmap *regmap; struct regmap *regmap;
int chip_idx;
}; };
/* 11bit [min,max,step] = [-103.9375dB, 24dB, 0.0625dB] */ /* 11bit [min,max,step] = [-103.9375dB, 24dB, 0.0625dB] */
...@@ -149,8 +161,12 @@ static int rt9120_codec_probe(struct snd_soc_component *comp) ...@@ -149,8 +161,12 @@ static int rt9120_codec_probe(struct snd_soc_component *comp)
snd_soc_component_init_regmap(comp, data->regmap); snd_soc_component_init_regmap(comp, data->regmap);
/* Internal setting */ /* Internal setting */
snd_soc_component_write(comp, RT9120_REG_INTERNAL1, 0x03); if (data->chip_idx == CHIP_IDX_RT9120S) {
snd_soc_component_write(comp, RT9120_REG_INTERNAL0, 0x69); snd_soc_component_write(comp, RT9120_REG_INTERCFG, 0xde);
snd_soc_component_write(comp, RT9120_REG_INTERNAL0, 0x66);
} else
snd_soc_component_write(comp, RT9120_REG_INTERNAL0, 0x04);
return 0; return 0;
} }
...@@ -201,8 +217,8 @@ static int rt9120_hw_params(struct snd_pcm_substream *substream, ...@@ -201,8 +217,8 @@ static int rt9120_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct snd_soc_component *comp = dai->component; struct snd_soc_component *comp = dai->component;
unsigned int param_width, param_slot_width; unsigned int param_width, param_slot_width, auto_sync;
int width; int width, fs;
switch (width = params_width(param)) { switch (width = params_width(param)) {
case 16: case 16:
...@@ -240,6 +256,16 @@ static int rt9120_hw_params(struct snd_pcm_substream *substream, ...@@ -240,6 +256,16 @@ static int rt9120_hw_params(struct snd_pcm_substream *substream,
snd_soc_component_update_bits(comp, RT9120_REG_I2SWL, snd_soc_component_update_bits(comp, RT9120_REG_I2SWL,
RT9120_AUDWL_MASK, param_slot_width); RT9120_AUDWL_MASK, param_slot_width);
fs = width * params_channels(param);
/* If fs is divided by 48, disable auto sync */
if (fs % 48 == 0)
auto_sync = 0;
else
auto_sync = RT9120_AUTOSYNC_MASK;
snd_soc_component_update_bits(comp, RT9120_REG_DIGCFG,
RT9120_AUTOSYNC_MASK, auto_sync);
return 0; return 0;
} }
...@@ -279,9 +305,11 @@ static const struct regmap_range rt9120_rd_yes_ranges[] = { ...@@ -279,9 +305,11 @@ static const struct regmap_range rt9120_rd_yes_ranges[] = {
regmap_reg_range(0x20, 0x27), regmap_reg_range(0x20, 0x27),
regmap_reg_range(0x30, 0x38), regmap_reg_range(0x30, 0x38),
regmap_reg_range(0x3A, 0x40), regmap_reg_range(0x3A, 0x40),
regmap_reg_range(0x63, 0x63),
regmap_reg_range(0x65, 0x65), regmap_reg_range(0x65, 0x65),
regmap_reg_range(0x69, 0x69), regmap_reg_range(0x69, 0x69),
regmap_reg_range(0x6C, 0x6C) regmap_reg_range(0x6C, 0x6C),
regmap_reg_range(0xF8, 0xF8)
}; };
static const struct regmap_access_table rt9120_rd_table = { static const struct regmap_access_table rt9120_rd_table = {
...@@ -297,9 +325,11 @@ static const struct regmap_range rt9120_wr_yes_ranges[] = { ...@@ -297,9 +325,11 @@ static const struct regmap_range rt9120_wr_yes_ranges[] = {
regmap_reg_range(0x30, 0x38), regmap_reg_range(0x30, 0x38),
regmap_reg_range(0x3A, 0x3D), regmap_reg_range(0x3A, 0x3D),
regmap_reg_range(0x40, 0x40), regmap_reg_range(0x40, 0x40),
regmap_reg_range(0x63, 0x63),
regmap_reg_range(0x65, 0x65), regmap_reg_range(0x65, 0x65),
regmap_reg_range(0x69, 0x69), regmap_reg_range(0x69, 0x69),
regmap_reg_range(0x6C, 0x6C) regmap_reg_range(0x6C, 0x6C),
regmap_reg_range(0xF8, 0xF8)
}; };
static const struct regmap_access_table rt9120_wr_table = { static const struct regmap_access_table rt9120_wr_table = {
...@@ -370,7 +400,7 @@ static int rt9120_reg_write(void *context, unsigned int reg, unsigned int val) ...@@ -370,7 +400,7 @@ static int rt9120_reg_write(void *context, unsigned int reg, unsigned int val)
static const struct regmap_config rt9120_regmap_config = { static const struct regmap_config rt9120_regmap_config = {
.reg_bits = 8, .reg_bits = 8,
.val_bits = 32, .val_bits = 32,
.max_register = RT9120_REG_UVPOPT, .max_register = RT9120_REG_DIGCFG,
.reg_read = rt9120_reg_read, .reg_read = rt9120_reg_read,
.reg_write = rt9120_reg_write, .reg_write = rt9120_reg_write,
...@@ -388,8 +418,16 @@ static int rt9120_check_vendor_info(struct rt9120_data *data) ...@@ -388,8 +418,16 @@ static int rt9120_check_vendor_info(struct rt9120_data *data)
if (ret) if (ret)
return ret; return ret;
if ((devid & RT9120_VID_MASK) != RT9120_VENDOR_ID) { devid = FIELD_GET(RT9120_VID_MASK, devid);
dev_err(data->dev, "DEVID not correct [0x%04x]\n", devid); switch (devid) {
case RT9120_VENDOR_ID:
data->chip_idx = CHIP_IDX_RT9120;
break;
case RT9120S_VENDOR_ID:
data->chip_idx = CHIP_IDX_RT9120S;
break;
default:
dev_err(data->dev, "DEVID not correct [0x%0x]\n", devid);
return -ENODEV; return -ENODEV;
} }
......
...@@ -1896,9 +1896,8 @@ static int wcd934x_hw_params(struct snd_pcm_substream *substream, ...@@ -1896,9 +1896,8 @@ static int wcd934x_hw_params(struct snd_pcm_substream *substream,
} }
wcd->dai[dai->id].sconfig.rate = params_rate(params); wcd->dai[dai->id].sconfig.rate = params_rate(params);
wcd934x_slim_set_hw_params(wcd, &wcd->dai[dai->id], substream->stream);
return 0; return wcd934x_slim_set_hw_params(wcd, &wcd->dai[dai->id], substream->stream);
} }
static int wcd934x_hw_free(struct snd_pcm_substream *substream, static int wcd934x_hw_free(struct snd_pcm_substream *substream,
......
...@@ -1174,6 +1174,9 @@ static bool wcd938x_readonly_register(struct device *dev, unsigned int reg) ...@@ -1174,6 +1174,9 @@ static bool wcd938x_readonly_register(struct device *dev, unsigned int reg)
case WCD938X_DIGITAL_INTR_STATUS_0: case WCD938X_DIGITAL_INTR_STATUS_0:
case WCD938X_DIGITAL_INTR_STATUS_1: case WCD938X_DIGITAL_INTR_STATUS_1:
case WCD938X_DIGITAL_INTR_STATUS_2: case WCD938X_DIGITAL_INTR_STATUS_2:
case WCD938X_DIGITAL_INTR_CLEAR_0:
case WCD938X_DIGITAL_INTR_CLEAR_1:
case WCD938X_DIGITAL_INTR_CLEAR_2:
case WCD938X_DIGITAL_SWR_HM_TEST_0: case WCD938X_DIGITAL_SWR_HM_TEST_0:
case WCD938X_DIGITAL_SWR_HM_TEST_1: case WCD938X_DIGITAL_SWR_HM_TEST_1:
case WCD938X_DIGITAL_EFUSE_T_DATA_0: case WCD938X_DIGITAL_EFUSE_T_DATA_0:
......
...@@ -617,8 +617,9 @@ static int wm_adsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl) ...@@ -617,8 +617,9 @@ static int wm_adsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl)
switch (cs_dsp->fw_ver) { switch (cs_dsp->fw_ver) {
case 0: case 0:
case 1: case 1:
snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s %x", ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
cs_dsp->name, region_name, cs_ctl->alg_region.alg); "%s %s %x", cs_dsp->name, region_name,
cs_ctl->alg_region.alg);
break; break;
case 2: case 2:
ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
......
...@@ -248,6 +248,75 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { ...@@ -248,6 +248,75 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = {
SOF_BT_OFFLOAD_SSP(2) | SOF_BT_OFFLOAD_SSP(2) |
SOF_SSP_BT_OFFLOAD_PRESENT), SOF_SSP_BT_OFFLOAD_PRESENT),
}, },
{
.callback = sof_sdw_quirk_cb,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AF3"),
},
/* No Jack */
.driver_data = (void *)(SOF_SDW_TGL_HDMI |
SOF_SDW_FOUR_SPK),
},
{
.callback = sof_sdw_quirk_cb,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B00")
},
.driver_data = (void *)(SOF_SDW_TGL_HDMI |
RT711_JD2 |
SOF_SDW_FOUR_SPK),
},
{
.callback = sof_sdw_quirk_cb,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B01")
},
.driver_data = (void *)(SOF_SDW_TGL_HDMI |
RT711_JD2 |
SOF_SDW_FOUR_SPK),
},
{
.callback = sof_sdw_quirk_cb,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B11")
},
.driver_data = (void *)(SOF_SDW_TGL_HDMI |
RT711_JD2 |
SOF_SDW_FOUR_SPK),
},
{
.callback = sof_sdw_quirk_cb,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B12")
},
.driver_data = (void *)(SOF_SDW_TGL_HDMI |
RT711_JD2 |
SOF_SDW_FOUR_SPK),
},
{
.callback = sof_sdw_quirk_cb,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B13"),
},
/* No Jack */
.driver_data = (void *)SOF_SDW_TGL_HDMI,
},
{
.callback = sof_sdw_quirk_cb,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B29"),
},
.driver_data = (void *)(SOF_SDW_TGL_HDMI |
RT711_JD2 |
SOF_SDW_FOUR_SPK),
},
{} {}
}; };
......
...@@ -74,6 +74,15 @@ static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = { ...@@ -74,6 +74,15 @@ static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = {
} }
}; };
static const struct snd_soc_acpi_adr_device rt711_sdca_2_adr[] = {
{
.adr = 0x000230025D071101ull,
.num_endpoints = 1,
.endpoints = &single_endpoint,
.name_prefix = "rt711"
}
};
static const struct snd_soc_acpi_adr_device rt1316_1_group1_adr[] = { static const struct snd_soc_acpi_adr_device rt1316_1_group1_adr[] = {
{ {
.adr = 0x000131025D131601ull, /* unique ID is set for some reason */ .adr = 0x000131025D131601ull, /* unique ID is set for some reason */
...@@ -101,6 +110,24 @@ static const struct snd_soc_acpi_adr_device rt1316_3_group1_adr[] = { ...@@ -101,6 +110,24 @@ static const struct snd_soc_acpi_adr_device rt1316_3_group1_adr[] = {
} }
}; };
static const struct snd_soc_acpi_adr_device rt1316_0_group2_adr[] = {
{
.adr = 0x000031025D131601ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "rt1316-1"
}
};
static const struct snd_soc_acpi_adr_device rt1316_1_group2_adr[] = {
{
.adr = 0x000130025D131601ull,
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "rt1316-2"
}
};
static const struct snd_soc_acpi_adr_device rt1316_2_single_adr[] = { static const struct snd_soc_acpi_adr_device rt1316_2_single_adr[] = {
{ {
.adr = 0x000230025D131601ull, .adr = 0x000230025D131601ull,
...@@ -209,6 +236,63 @@ static const struct snd_soc_acpi_link_adr adl_sdca_3_in_1[] = { ...@@ -209,6 +236,63 @@ static const struct snd_soc_acpi_link_adr adl_sdca_3_in_1[] = {
{} {}
}; };
static const struct snd_soc_acpi_link_adr adl_sdw_rt711_link2_rt1316_link01_rt714_link3[] = {
{
.mask = BIT(2),
.num_adr = ARRAY_SIZE(rt711_sdca_2_adr),
.adr_d = rt711_sdca_2_adr,
},
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(rt1316_0_group2_adr),
.adr_d = rt1316_0_group2_adr,
},
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(rt1316_1_group2_adr),
.adr_d = rt1316_1_group2_adr,
},
{
.mask = BIT(3),
.num_adr = ARRAY_SIZE(rt714_3_adr),
.adr_d = rt714_3_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr adl_sdw_rt1316_link12_rt714_link0[] = {
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(rt1316_1_group1_adr),
.adr_d = rt1316_1_group1_adr,
},
{
.mask = BIT(2),
.num_adr = ARRAY_SIZE(rt1316_2_group1_adr),
.adr_d = rt1316_2_group1_adr,
},
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(rt714_0_adr),
.adr_d = rt714_0_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr adl_sdw_rt1316_link2_rt714_link3[] = {
{
.mask = BIT(2),
.num_adr = ARRAY_SIZE(rt1316_2_single_adr),
.adr_d = rt1316_2_single_adr,
},
{
.mask = BIT(3),
.num_adr = ARRAY_SIZE(rt714_3_adr),
.adr_d = rt714_3_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr adl_sdw_rt1316_link2_rt714_link0[] = { static const struct snd_soc_acpi_link_adr adl_sdw_rt1316_link2_rt714_link0[] = {
{ {
.mask = BIT(2), .mask = BIT(2),
...@@ -339,6 +423,27 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_sdw_machines[] = { ...@@ -339,6 +423,27 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_sdw_machines[] = {
.drv_name = "sof_sdw", .drv_name = "sof_sdw",
.sof_tplg_filename = "sof-adl-rt711-l0-rt1316-l13-rt714-l2.tplg", .sof_tplg_filename = "sof-adl-rt711-l0-rt1316-l13-rt714-l2.tplg",
}, },
{
.link_mask = 0xF, /* 4 active links required */
.links = adl_sdw_rt711_link2_rt1316_link01_rt714_link3,
.drv_name = "sof_sdw",
.sof_fw_filename = "sof-adl.ri",
.sof_tplg_filename = "sof-adl-rt711-l2-rt1316-l01-rt714-l3.tplg",
},
{
.link_mask = 0xC, /* rt1316 on link2 & rt714 on link3 */
.links = adl_sdw_rt1316_link2_rt714_link3,
.drv_name = "sof_sdw",
.sof_fw_filename = "sof-adl.ri",
.sof_tplg_filename = "sof-adl-rt1316-l2-mono-rt714-l3.tplg",
},
{
.link_mask = 0x7, /* rt714 on link0 & two rt1316s on link1 and link2 */
.links = adl_sdw_rt1316_link12_rt714_link0,
.drv_name = "sof_sdw",
.sof_fw_filename = "sof-adl.ri",
.sof_tplg_filename = "sof-adl-rt1316-l12-rt714-l0.tplg",
},
{ {
.link_mask = 0x5, /* 2 active links required */ .link_mask = 0x5, /* 2 active links required */
.links = adl_sdw_rt1316_link2_rt714_link0, .links = adl_sdw_rt1316_link2_rt714_link0,
......
...@@ -1054,6 +1054,7 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) ...@@ -1054,6 +1054,7 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
int irq_id; int irq_id;
struct mtk_base_afe *afe; struct mtk_base_afe *afe;
struct mt8173_afe_private *afe_priv; struct mt8173_afe_private *afe_priv;
struct snd_soc_component *comp_pcm, *comp_hdmi;
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33)); ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33));
if (ret) if (ret)
...@@ -1142,23 +1143,55 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) ...@@ -1142,23 +1143,55 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
if (ret) if (ret)
goto err_pm_disable; goto err_pm_disable;
ret = devm_snd_soc_register_component(&pdev->dev, comp_pcm = devm_kzalloc(&pdev->dev, sizeof(*comp_pcm), GFP_KERNEL);
&mt8173_afe_pcm_dai_component, if (!comp_pcm) {
mt8173_afe_pcm_dais, ret = -ENOMEM;
ARRAY_SIZE(mt8173_afe_pcm_dais)); goto err_pm_disable;
}
ret = snd_soc_component_initialize(comp_pcm,
&mt8173_afe_pcm_dai_component,
&pdev->dev);
if (ret) if (ret)
goto err_pm_disable; goto err_pm_disable;
ret = devm_snd_soc_register_component(&pdev->dev, #ifdef CONFIG_DEBUG_FS
&mt8173_afe_hdmi_dai_component, comp_pcm->debugfs_prefix = "pcm";
mt8173_afe_hdmi_dais, #endif
ARRAY_SIZE(mt8173_afe_hdmi_dais));
ret = snd_soc_add_component(comp_pcm,
mt8173_afe_pcm_dais,
ARRAY_SIZE(mt8173_afe_pcm_dais));
if (ret)
goto err_pm_disable;
comp_hdmi = devm_kzalloc(&pdev->dev, sizeof(*comp_hdmi), GFP_KERNEL);
if (!comp_hdmi) {
ret = -ENOMEM;
goto err_pm_disable;
}
ret = snd_soc_component_initialize(comp_hdmi,
&mt8173_afe_hdmi_dai_component,
&pdev->dev);
if (ret) if (ret)
goto err_pm_disable; goto err_pm_disable;
#ifdef CONFIG_DEBUG_FS
comp_hdmi->debugfs_prefix = "hdmi";
#endif
ret = snd_soc_add_component(comp_hdmi,
mt8173_afe_hdmi_dais,
ARRAY_SIZE(mt8173_afe_hdmi_dais));
if (ret)
goto err_cleanup_components;
dev_info(&pdev->dev, "MT8173 AFE driver initialized.\n"); dev_info(&pdev->dev, "MT8173 AFE driver initialized.\n");
return 0; return 0;
err_cleanup_components:
snd_soc_unregister_component(&pdev->dev);
err_pm_disable: err_pm_disable:
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
return ret; return ret;
...@@ -1166,6 +1199,8 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) ...@@ -1166,6 +1199,8 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
static int mt8173_afe_pcm_dev_remove(struct platform_device *pdev) static int mt8173_afe_pcm_dev_remove(struct platform_device *pdev)
{ {
snd_soc_unregister_component(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev)) if (!pm_runtime_status_suspended(&pdev->dev))
mt8173_afe_runtime_suspend(&pdev->dev); mt8173_afe_runtime_suspend(&pdev->dev);
......
...@@ -30,15 +30,15 @@ static struct mt8173_rt5650_platform_data mt8173_rt5650_priv = { ...@@ -30,15 +30,15 @@ static struct mt8173_rt5650_platform_data mt8173_rt5650_priv = {
}; };
static const struct snd_soc_dapm_widget mt8173_rt5650_widgets[] = { static const struct snd_soc_dapm_widget mt8173_rt5650_widgets[] = {
SND_SOC_DAPM_SPK("Speaker", NULL), SND_SOC_DAPM_SPK("Ext Spk", NULL),
SND_SOC_DAPM_MIC("Int Mic", NULL), SND_SOC_DAPM_MIC("Int Mic", NULL),
SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL),
}; };
static const struct snd_soc_dapm_route mt8173_rt5650_routes[] = { static const struct snd_soc_dapm_route mt8173_rt5650_routes[] = {
{"Speaker", NULL, "SPOL"}, {"Ext Spk", NULL, "SPOL"},
{"Speaker", NULL, "SPOR"}, {"Ext Spk", NULL, "SPOR"},
{"DMIC L1", NULL, "Int Mic"}, {"DMIC L1", NULL, "Int Mic"},
{"DMIC R1", NULL, "Int Mic"}, {"DMIC R1", NULL, "Int Mic"},
{"Headphone", NULL, "HPOL"}, {"Headphone", NULL, "HPOL"},
...@@ -48,7 +48,7 @@ static const struct snd_soc_dapm_route mt8173_rt5650_routes[] = { ...@@ -48,7 +48,7 @@ static const struct snd_soc_dapm_route mt8173_rt5650_routes[] = {
}; };
static const struct snd_kcontrol_new mt8173_rt5650_controls[] = { static const struct snd_kcontrol_new mt8173_rt5650_controls[] = {
SOC_DAPM_PIN_SWITCH("Speaker"), SOC_DAPM_PIN_SWITCH("Ext Spk"),
SOC_DAPM_PIN_SWITCH("Int Mic"), SOC_DAPM_PIN_SWITCH("Int Mic"),
SOC_DAPM_PIN_SWITCH("Headphone"), SOC_DAPM_PIN_SWITCH("Headphone"),
SOC_DAPM_PIN_SWITCH("Headset Mic"), SOC_DAPM_PIN_SWITCH("Headset Mic"),
......
...@@ -550,6 +550,10 @@ struct audio_hw_clk_cfg { ...@@ -550,6 +550,10 @@ struct audio_hw_clk_cfg {
uint32_t clock_root; uint32_t clock_root;
} __packed; } __packed;
struct audio_hw_clk_rel_cfg {
uint32_t clock_id;
} __packed;
#define PARAM_ID_HW_EP_POWER_MODE_CFG 0x8001176 #define PARAM_ID_HW_EP_POWER_MODE_CFG 0x8001176
#define AR_HW_EP_POWER_MODE_0 0 /* default */ #define AR_HW_EP_POWER_MODE_0 0 /* default */
#define AR_HW_EP_POWER_MODE_1 1 /* XO Shutdown allowed */ #define AR_HW_EP_POWER_MODE_1 1 /* XO Shutdown allowed */
......
...@@ -390,7 +390,7 @@ struct q6copp *q6adm_open(struct device *dev, int port_id, int path, int rate, ...@@ -390,7 +390,7 @@ struct q6copp *q6adm_open(struct device *dev, int port_id, int path, int rate,
int ret = 0; int ret = 0;
if (port_id < 0) { if (port_id < 0) {
dev_err(dev, "Invalid port_id 0x%x\n", port_id); dev_err(dev, "Invalid port_id %d\n", port_id);
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
...@@ -508,7 +508,7 @@ int q6adm_matrix_map(struct device *dev, int path, ...@@ -508,7 +508,7 @@ int q6adm_matrix_map(struct device *dev, int path,
int port_idx = payload_map.port_id[i]; int port_idx = payload_map.port_id[i];
if (port_idx < 0) { if (port_idx < 0) {
dev_err(dev, "Invalid port_id 0x%x\n", dev_err(dev, "Invalid port_id %d\n",
payload_map.port_id[i]); payload_map.port_id[i]);
kfree(pkt); kfree(pkt);
return -EINVAL; return -EINVAL;
......
...@@ -269,9 +269,7 @@ static int q6asm_dai_prepare(struct snd_soc_component *component, ...@@ -269,9 +269,7 @@ static int q6asm_dai_prepare(struct snd_soc_component *component,
if (ret < 0) { if (ret < 0) {
dev_err(dev, "%s: q6asm_open_write failed\n", __func__); dev_err(dev, "%s: q6asm_open_write failed\n", __func__);
q6asm_audio_client_free(prtd->audio_client); goto open_err;
prtd->audio_client = NULL;
return -ENOMEM;
} }
prtd->session_id = q6asm_get_session_id(prtd->audio_client); prtd->session_id = q6asm_get_session_id(prtd->audio_client);
...@@ -279,7 +277,7 @@ static int q6asm_dai_prepare(struct snd_soc_component *component, ...@@ -279,7 +277,7 @@ static int q6asm_dai_prepare(struct snd_soc_component *component,
prtd->session_id, substream->stream); prtd->session_id, substream->stream);
if (ret) { if (ret) {
dev_err(dev, "%s: stream reg failed ret:%d\n", __func__, ret); dev_err(dev, "%s: stream reg failed ret:%d\n", __func__, ret);
return ret; goto routing_err;
} }
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
...@@ -301,10 +299,19 @@ static int q6asm_dai_prepare(struct snd_soc_component *component, ...@@ -301,10 +299,19 @@ static int q6asm_dai_prepare(struct snd_soc_component *component,
} }
if (ret < 0) if (ret < 0)
dev_info(dev, "%s: CMD Format block failed\n", __func__); dev_info(dev, "%s: CMD Format block failed\n", __func__);
else
prtd->state = Q6ASM_STREAM_RUNNING;
prtd->state = Q6ASM_STREAM_RUNNING; return ret;
return 0; routing_err:
q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE);
open_err:
q6asm_unmap_memory_regions(substream->stream, prtd->audio_client);
q6asm_audio_client_free(prtd->audio_client);
prtd->audio_client = NULL;
return ret;
} }
static int q6asm_dai_trigger(struct snd_soc_component *component, static int q6asm_dai_trigger(struct snd_soc_component *component,
......
...@@ -42,6 +42,12 @@ struct prm_cmd_request_rsc { ...@@ -42,6 +42,12 @@ struct prm_cmd_request_rsc {
struct audio_hw_clk_cfg clock_id; struct audio_hw_clk_cfg clock_id;
} __packed; } __packed;
struct prm_cmd_release_rsc {
struct apm_module_param_data param_data;
uint32_t num_clk_id;
struct audio_hw_clk_rel_cfg clock_id;
} __packed;
static int q6prm_send_cmd_sync(struct q6prm *prm, struct gpr_pkt *pkt, uint32_t rsp_opcode) static int q6prm_send_cmd_sync(struct q6prm *prm, struct gpr_pkt *pkt, uint32_t rsp_opcode)
{ {
return audioreach_send_cmd_sync(prm->dev, prm->gdev, &prm->result, &prm->lock, return audioreach_send_cmd_sync(prm->dev, prm->gdev, &prm->result, &prm->lock,
...@@ -102,8 +108,8 @@ int q6prm_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, uint32_ ...@@ -102,8 +108,8 @@ int q6prm_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, uint32_
} }
EXPORT_SYMBOL_GPL(q6prm_unvote_lpass_core_hw); EXPORT_SYMBOL_GPL(q6prm_unvote_lpass_core_hw);
int q6prm_set_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_root, static int q6prm_request_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_root,
unsigned int freq) unsigned int freq)
{ {
struct q6prm *prm = dev_get_drvdata(dev->parent); struct q6prm *prm = dev_get_drvdata(dev->parent);
struct apm_module_param_data *param_data; struct apm_module_param_data *param_data;
...@@ -138,6 +144,49 @@ int q6prm_set_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_ ...@@ -138,6 +144,49 @@ int q6prm_set_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_
return rc; return rc;
} }
static int q6prm_release_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_root,
unsigned int freq)
{
struct q6prm *prm = dev_get_drvdata(dev->parent);
struct apm_module_param_data *param_data;
struct prm_cmd_release_rsc *rel;
gpr_device_t *gdev = prm->gdev;
struct gpr_pkt *pkt;
int rc;
pkt = audioreach_alloc_cmd_pkt(sizeof(*rel), PRM_CMD_RELEASE_HW_RSC, 0, gdev->svc.id,
GPR_PRM_MODULE_IID);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
rel = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
param_data = &rel->param_data;
param_data->module_instance_id = GPR_PRM_MODULE_IID;
param_data->error_code = 0;
param_data->param_id = PARAM_ID_RSC_AUDIO_HW_CLK;
param_data->param_size = sizeof(*rel) - APM_MODULE_PARAM_DATA_SIZE;
rel->num_clk_id = 1;
rel->clock_id.clock_id = clk_id;
rc = q6prm_send_cmd_sync(prm, pkt, PRM_CMD_RSP_RELEASE_HW_RSC);
kfree(pkt);
return rc;
}
int q6prm_set_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_root,
unsigned int freq)
{
if (freq)
return q6prm_request_lpass_clock(dev, clk_id, clk_attr, clk_attr, freq);
return q6prm_release_lpass_clock(dev, clk_id, clk_attr, clk_attr, freq);
}
EXPORT_SYMBOL_GPL(q6prm_set_lpass_clock); EXPORT_SYMBOL_GPL(q6prm_set_lpass_clock);
static int prm_callback(struct gpr_resp_pkt *data, void *priv, int op) static int prm_callback(struct gpr_resp_pkt *data, void *priv, int op)
......
...@@ -372,6 +372,12 @@ int q6routing_stream_open(int fedai_id, int perf_mode, ...@@ -372,6 +372,12 @@ int q6routing_stream_open(int fedai_id, int perf_mode,
} }
session = &routing_data->sessions[stream_id - 1]; session = &routing_data->sessions[stream_id - 1];
if (session->port_id < 0) {
dev_err(routing_data->dev, "Routing not setup for MultiMedia%d Session\n",
session->fedai_id);
return -EINVAL;
}
pdata = &routing_data->port_data[session->port_id]; pdata = &routing_data->port_data[session->port_id];
mutex_lock(&routing_data->lock); mutex_lock(&routing_data->lock);
...@@ -495,7 +501,11 @@ static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol, ...@@ -495,7 +501,11 @@ static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol,
session->port_id = be_id; session->port_id = be_id;
snd_soc_dapm_mixer_update_power(dapm, kcontrol, 1, update); snd_soc_dapm_mixer_update_power(dapm, kcontrol, 1, update);
} else { } else {
session->port_id = -1; if (session->port_id == be_id) {
session->port_id = -1;
return 0;
}
snd_soc_dapm_mixer_update_power(dapm, kcontrol, 0, update); snd_soc_dapm_mixer_update_power(dapm, kcontrol, 0, update);
} }
......
...@@ -102,7 +102,7 @@ static int rsnd_dmaen_stop(struct rsnd_mod *mod, ...@@ -102,7 +102,7 @@ static int rsnd_dmaen_stop(struct rsnd_mod *mod,
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
if (dmaen->chan) if (dmaen->chan)
dmaengine_terminate_sync(dmaen->chan); dmaengine_terminate_async(dmaen->chan);
return 0; return 0;
} }
......
...@@ -2559,8 +2559,13 @@ static struct snd_soc_dapm_widget *dapm_find_widget( ...@@ -2559,8 +2559,13 @@ static struct snd_soc_dapm_widget *dapm_find_widget(
return NULL; return NULL;
} }
static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, /*
const char *pin, int status) * set the DAPM pin status:
* returns 1 when the value has been updated, 0 when unchanged, or a negative
* error code; called from kcontrol put callback
*/
static int __snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
const char *pin, int status)
{ {
struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true); struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true);
int ret = 0; int ret = 0;
...@@ -2586,6 +2591,18 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, ...@@ -2586,6 +2591,18 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
return ret; return ret;
} }
/*
* similar as __snd_soc_dapm_set_pin(), but returns 0 when successful;
* called from several API functions below
*/
static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
const char *pin, int status)
{
int ret = __snd_soc_dapm_set_pin(dapm, pin, status);
return ret < 0 ? ret : 0;
}
/** /**
* snd_soc_dapm_sync_unlocked - scan and power dapm paths * snd_soc_dapm_sync_unlocked - scan and power dapm paths
* @dapm: DAPM context * @dapm: DAPM context
...@@ -3589,10 +3606,10 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol, ...@@ -3589,10 +3606,10 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol,
const char *pin = (const char *)kcontrol->private_value; const char *pin = (const char *)kcontrol->private_value;
int ret; int ret;
if (ucontrol->value.integer.value[0]) mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
ret = snd_soc_dapm_enable_pin(&card->dapm, pin); ret = __snd_soc_dapm_set_pin(&card->dapm, pin,
else !!ucontrol->value.integer.value[0]);
ret = snd_soc_dapm_disable_pin(&card->dapm, pin); mutex_unlock(&card->dapm_mutex);
snd_soc_dapm_sync(&card->dapm); snd_soc_dapm_sync(&card->dapm);
return ret; return ret;
......
...@@ -2700,6 +2700,7 @@ EXPORT_SYMBOL_GPL(snd_soc_tplg_component_load); ...@@ -2700,6 +2700,7 @@ EXPORT_SYMBOL_GPL(snd_soc_tplg_component_load);
/* remove dynamic controls from the component driver */ /* remove dynamic controls from the component driver */
int snd_soc_tplg_component_remove(struct snd_soc_component *comp) int snd_soc_tplg_component_remove(struct snd_soc_component *comp)
{ {
struct snd_card *card = comp->card->snd_card;
struct snd_soc_dobj *dobj, *next_dobj; struct snd_soc_dobj *dobj, *next_dobj;
int pass = SOC_TPLG_PASS_END; int pass = SOC_TPLG_PASS_END;
...@@ -2707,6 +2708,7 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp) ...@@ -2707,6 +2708,7 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp)
while (pass >= SOC_TPLG_PASS_START) { while (pass >= SOC_TPLG_PASS_START) {
/* remove mixer controls */ /* remove mixer controls */
down_write(&card->controls_rwsem);
list_for_each_entry_safe(dobj, next_dobj, &comp->dobj_list, list_for_each_entry_safe(dobj, next_dobj, &comp->dobj_list,
list) { list) {
...@@ -2745,6 +2747,7 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp) ...@@ -2745,6 +2747,7 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp)
break; break;
} }
} }
up_write(&card->controls_rwsem);
pass--; pass--;
} }
......
...@@ -47,7 +47,7 @@ config SND_SOC_SOF_OF ...@@ -47,7 +47,7 @@ config SND_SOC_SOF_OF
Say Y if you need this option. If unsure select "N". Say Y if you need this option. If unsure select "N".
config SND_SOC_SOF_COMPRESS config SND_SOC_SOF_COMPRESS
tristate bool
select SND_SOC_COMPRESS select SND_SOC_COMPRESS
config SND_SOC_SOF_DEBUG_PROBES config SND_SOC_SOF_DEBUG_PROBES
......
...@@ -69,7 +69,7 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol) ...@@ -69,7 +69,7 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
{ {
struct sof_ipc_ctrl_data *cdata = scontrol->control_data; struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
struct snd_soc_component *scomp = scontrol->scomp; struct snd_soc_component *scomp = scontrol->scomp;
enum sof_ipc_ctrl_type ctrl_type; u32 ipc_cmd;
int ret; int ret;
if (!scontrol->comp_data_dirty) if (!scontrol->comp_data_dirty)
...@@ -79,9 +79,9 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol) ...@@ -79,9 +79,9 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
return; return;
if (scontrol->cmd == SOF_CTRL_CMD_BINARY) if (scontrol->cmd == SOF_CTRL_CMD_BINARY)
ctrl_type = SOF_IPC_COMP_GET_DATA; ipc_cmd = SOF_IPC_COMP_GET_DATA;
else else
ctrl_type = SOF_IPC_COMP_GET_VALUE; ipc_cmd = SOF_IPC_COMP_GET_VALUE;
/* set the ABI header values */ /* set the ABI header values */
cdata->data->magic = SOF_ABI_MAGIC; cdata->data->magic = SOF_ABI_MAGIC;
...@@ -89,7 +89,7 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol) ...@@ -89,7 +89,7 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
/* refresh the component data from DSP */ /* refresh the component data from DSP */
scontrol->comp_data_dirty = false; scontrol->comp_data_dirty = false;
ret = snd_sof_ipc_set_get_comp_data(scontrol, ctrl_type, ret = snd_sof_ipc_set_get_comp_data(scontrol, ipc_cmd,
SOF_CTRL_TYPE_VALUE_CHAN_GET, SOF_CTRL_TYPE_VALUE_CHAN_GET,
scontrol->cmd, false); scontrol->cmd, false);
if (ret < 0) { if (ret < 0) {
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include <linux/io.h> #include <linux/io.h>
#include <sound/hdaudio.h> #include <sound/hdaudio.h>
#include <sound/hda_i915.h> #include <sound/hda_i915.h>
#include <sound/hda_codec.h>
#include <sound/hda_register.h>
#include "../sof-priv.h" #include "../sof-priv.h"
#include "hda.h" #include "hda.h"
...@@ -21,6 +23,18 @@ ...@@ -21,6 +23,18 @@
#endif #endif
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
static void update_codec_wake_enable(struct hdac_bus *bus, unsigned int addr, bool link_power)
{
unsigned int mask = snd_hdac_chip_readw(bus, WAKEEN);
if (link_power)
mask &= ~BIT(addr);
else
mask |= BIT(addr);
snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, mask);
}
static void sof_hda_bus_link_power(struct hdac_device *codec, bool enable) static void sof_hda_bus_link_power(struct hdac_device *codec, bool enable)
{ {
struct hdac_bus *bus = codec->bus; struct hdac_bus *bus = codec->bus;
...@@ -41,6 +55,9 @@ static void sof_hda_bus_link_power(struct hdac_device *codec, bool enable) ...@@ -41,6 +55,9 @@ static void sof_hda_bus_link_power(struct hdac_device *codec, bool enable)
*/ */
if (codec->addr == HDA_IDISP_ADDR && !enable) if (codec->addr == HDA_IDISP_ADDR && !enable)
snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false); snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false);
/* WAKEEN needs to be set for disabled links */
update_codec_wake_enable(bus, codec->addr, enable);
} }
static const struct hdac_bus_ops bus_core_ops = { static const struct hdac_bus_ops bus_core_ops = {
......
...@@ -622,8 +622,7 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend) ...@@ -622,8 +622,7 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend)
hda_dsp_ipc_int_disable(sdev); hda_dsp_ipc_int_disable(sdev);
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
if (runtime_suspend) hda_codec_jack_wake_enable(sdev, runtime_suspend);
hda_codec_jack_wake_enable(sdev, true);
/* power down all hda link */ /* power down all hda link */
snd_hdac_ext_bus_link_power_down_all(bus); snd_hdac_ext_bus_link_power_down_all(bus);
......
...@@ -810,6 +810,20 @@ static int hda_init_caps(struct snd_sof_dev *sdev) ...@@ -810,6 +810,20 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
return 0; return 0;
} }
static void hda_check_for_state_change(struct snd_sof_dev *sdev)
{
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
struct hdac_bus *bus = sof_to_bus(sdev);
unsigned int codec_mask;
codec_mask = snd_hdac_chip_readw(bus, STATESTS);
if (codec_mask) {
hda_codec_jack_check(sdev);
snd_hdac_chip_writew(bus, STATESTS, codec_mask);
}
#endif
}
static irqreturn_t hda_dsp_interrupt_handler(int irq, void *context) static irqreturn_t hda_dsp_interrupt_handler(int irq, void *context)
{ {
struct snd_sof_dev *sdev = context; struct snd_sof_dev *sdev = context;
...@@ -851,6 +865,8 @@ static irqreturn_t hda_dsp_interrupt_thread(int irq, void *context) ...@@ -851,6 +865,8 @@ static irqreturn_t hda_dsp_interrupt_thread(int irq, void *context)
if (hda_sdw_check_wakeen_irq(sdev)) if (hda_sdw_check_wakeen_irq(sdev))
hda_sdw_process_wakeen(sdev); hda_sdw_process_wakeen(sdev);
hda_check_for_state_change(sdev);
/* enable GIE interrupt */ /* enable GIE interrupt */
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
SOF_HDA_INTCTL, SOF_HDA_INTCTL,
......
...@@ -700,7 +700,7 @@ static int stm32_i2s_configure_clock(struct snd_soc_dai *cpu_dai, ...@@ -700,7 +700,7 @@ static int stm32_i2s_configure_clock(struct snd_soc_dai *cpu_dai,
if (ret < 0) if (ret < 0)
return ret; return ret;
nb_bits = frame_len * ((cgfr & I2S_CGFR_CHLEN) + 1); nb_bits = frame_len * (FIELD_GET(I2S_CGFR_CHLEN, cgfr) + 1);
ret = stm32_i2s_calc_clk_div(i2s, i2s_clock_rate, ret = stm32_i2s_calc_clk_div(i2s, i2s_clock_rate,
(nb_bits * rate)); (nb_bits * rate));
if (ret) if (ret)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment