// SPDX-License-Identifier: GPL-2.0-or-later /* * rk730.c -- RK730 ALSA SoC Audio driver * * Copyright (C) 2022 Rockchip Electronics Co., Ltd. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 "); MODULE_LICENSE("GPL");