// SPDX-License-Identifier: GPL-2.0 // // rk817 ALSA SoC Audio driver // // Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd All rights reserved. #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rk817_codec.h" #ifdef CONFIG_SND_DEBUG #define DBG(args...) pr_info(args) #else #define DBG(args...) #endif /* For route */ #define RK817_CODEC_PLAYBACK 1 #define RK817_CODEC_CAPTURE 2 #define RK817_CODEC_INCALL 4 #define RK817_CODEC_ALL (RK817_CODEC_PLAYBACK |\ RK817_CODEC_CAPTURE | RK817_CODEC_INCALL) /* * DDAC L/R volume setting * -1.125db~-95db,0.375db/step * 0~2 are not allowed to use * 0x03: -1.125dB * 0x0a: -3.75dB * 0x7d: -46dB * 0xff: -95dB */ #define OUT_VOLUME (0x03) /* * DADC L/R volume setting * 0db~-95db,0.375db/step,for example: * 0: 0dB * 0x0a: -3.75dB * 0x7d: -46dB * 0xff: -95dB */ #define CAPTURE_VOLUME (0x0) #define CODEC_SET_SPK 1 #define CODEC_SET_HP 2 #define RK817_DAC_VOL_MIN 3 #define RK817_DAC_VOL_MAX 255 struct rk817_codec_priv { struct snd_soc_component *component; struct regmap *regmap; struct rk808 *rk817; struct clk *mclk; struct mutex clk_lock; unsigned int clk_capture; unsigned int clk_playback; unsigned int stereo_sysclk; unsigned int rate; unsigned int spk_volume; unsigned int hp_volume; unsigned int capture_volume; bool mic_in_differential; bool pdmdata_out_enable; bool use_ext_amplifier; bool adc_for_loopback; bool resume_path; bool out_l2spk_r2hp; long int playback_path; long int capture_path; struct gpio_desc *spk_ctl_gpio; struct gpio_desc *hp_ctl_gpio; int spk_mute_delay; int hp_mute_delay; int chip_ver; }; /* * DADC L/R volume setting * 0db~-95db, 0.375db/step, for example: * 0x00: 0dB * 0xff: -95dB */ static const DECLARE_TLV_DB_MINMAX(adc_vol_tlv, -9500, 0); /* * DAC L/R Volume setting * -1.125db~-95db,0.375db/step * 0~2 are not allowed to use */ static const DECLARE_TLV_DB_MINMAX(dac_vol_tlv, -9500, -112); /* MIC_BOOST {0, +10, +20, +30} dB */ static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1000, 0); /* ADC PGA_GAIN -18dB to 27dB*/ static const DECLARE_TLV_DB_SCALE(adc_pga_tlv, -1800, 300, 0); /* HP Output Gain {0, +3, +6, +9} dB */ static const DECLARE_TLV_DB_SCALE(hp_out_tlv, 0, 300, 0); static const struct reg_default rk817_reg_defaults[] = { { RK817_CODEC_DTOP_VUCTL, 0x003 }, { RK817_CODEC_DTOP_VUCTIME, 0x00 }, { RK817_CODEC_DTOP_LPT_SRST, 0x00 }, { RK817_CODEC_DTOP_DIGEN_CLKE, 0x00 }, { RK817_CODEC_AREF_RTCFG0, 0x00 }, { RK817_CODEC_AREF_RTCFG1, 0x06 }, { RK817_CODEC_AADC_CFG0, 0xc8 }, { RK817_CODEC_AADC_CFG1, 0x00 }, { RK817_CODEC_DADC_VOLL, 0x00 }, { RK817_CODEC_DADC_VOLR, 0x00 }, { RK817_CODEC_DADC_SR_ACL0, 0x00 }, { RK817_CODEC_DADC_ALC1, 0x00 }, { RK817_CODEC_DADC_ALC2, 0x00 }, { RK817_CODEC_DADC_NG, 0x00 }, { RK817_CODEC_DADC_HPF, 0x00 }, { RK817_CODEC_DADC_RVOLL, 0xff }, { RK817_CODEC_DADC_RVOLR, 0xff }, { RK817_CODEC_AMIC_CFG0, 0x70 }, { RK817_CODEC_AMIC_CFG1, 0x00 }, { RK817_CODEC_DMIC_PGA_GAIN, 0x66 }, { RK817_CODEC_DMIC_LMT1, 0x00 }, { RK817_CODEC_DMIC_LMT2, 0x00 }, { RK817_CODEC_DMIC_NG1, 0x00 }, { RK817_CODEC_DMIC_NG2, 0x00 }, { RK817_CODEC_ADAC_CFG0, 0x00 }, { RK817_CODEC_ADAC_CFG1, 0x07 }, { RK817_CODEC_DDAC_POPD_DACST, 0x82 }, { RK817_CODEC_DDAC_VOLL, 0x00 }, { RK817_CODEC_DDAC_VOLR, 0x00 }, { RK817_CODEC_DDAC_SR_LMT0, 0x00 }, { RK817_CODEC_DDAC_LMT1, 0x00 }, { RK817_CODEC_DDAC_LMT2, 0x00 }, { RK817_CODEC_DDAC_MUTE_MIXCTL, 0xa0 }, { RK817_CODEC_DDAC_RVOLL, 0xff }, { RK817_CODEC_DDAC_RVOLR, 0xff }, { RK817_CODEC_AHP_ANTI0, 0x00 }, { RK817_CODEC_AHP_ANTI1, 0x00 }, { RK817_CODEC_AHP_CFG0, 0xe0 }, { RK817_CODEC_AHP_CFG1, 0x1f }, { RK817_CODEC_AHP_CP, 0x09 }, { RK817_CODEC_ACLASSD_CFG1, 0x69 }, { RK817_CODEC_ACLASSD_CFG2, 0x44 }, { RK817_CODEC_APLL_CFG0, 0x04 }, { RK817_CODEC_APLL_CFG1, 0x00 }, { RK817_CODEC_APLL_CFG2, 0x30 }, { RK817_CODEC_APLL_CFG3, 0x19 }, { RK817_CODEC_APLL_CFG4, 0x65 }, { RK817_CODEC_APLL_CFG5, 0x01 }, { RK817_CODEC_DI2S_CKM, 0x01 }, { RK817_CODEC_DI2S_RSD, 0x00 }, { RK817_CODEC_DI2S_RXCR1, 0x00 }, { RK817_CODEC_DI2S_RXCR2, 0x17 }, { RK817_CODEC_DI2S_RXCMD_TSD, 0x00 }, { RK817_CODEC_DI2S_TXCR1, 0x00 }, { RK817_CODEC_DI2S_TXCR2, 0x17 }, { RK817_CODEC_DI2S_TXCR3_TXCMD, 0x00 }, }; static bool rk817_volatile_register(struct device *dev, unsigned int reg) { switch (reg) { case RK817_CODEC_DTOP_LPT_SRST: case RK817_PMIC_CHIP_NAME: case RK817_PMIC_CHIP_VER: return true; default: return false; } } static bool rk817_codec_register(struct device *dev, unsigned int reg) { switch (reg) { case RK817_CODEC_DTOP_VUCTL: case RK817_CODEC_DTOP_VUCTIME: case RK817_CODEC_DTOP_LPT_SRST: case RK817_CODEC_DTOP_DIGEN_CLKE: case RK817_CODEC_AREF_RTCFG0: case RK817_CODEC_AREF_RTCFG1: case RK817_CODEC_AADC_CFG0: case RK817_CODEC_AADC_CFG1: case RK817_CODEC_DADC_VOLL: case RK817_CODEC_DADC_VOLR: case RK817_CODEC_DADC_SR_ACL0: case RK817_CODEC_DADC_ALC1: case RK817_CODEC_DADC_ALC2: case RK817_CODEC_DADC_NG: case RK817_CODEC_DADC_HPF: case RK817_CODEC_DADC_RVOLL: case RK817_CODEC_DADC_RVOLR: case RK817_CODEC_AMIC_CFG0: case RK817_CODEC_AMIC_CFG1: case RK817_CODEC_DMIC_PGA_GAIN: case RK817_CODEC_DMIC_LMT1: case RK817_CODEC_DMIC_LMT2: case RK817_CODEC_DMIC_NG1: case RK817_CODEC_DMIC_NG2: case RK817_CODEC_ADAC_CFG0: case RK817_CODEC_ADAC_CFG1: case RK817_CODEC_DDAC_POPD_DACST: case RK817_CODEC_DDAC_VOLL: case RK817_CODEC_DDAC_VOLR: case RK817_CODEC_DDAC_SR_LMT0: case RK817_CODEC_DDAC_LMT1: case RK817_CODEC_DDAC_LMT2: case RK817_CODEC_DDAC_MUTE_MIXCTL: case RK817_CODEC_DDAC_RVOLL: case RK817_CODEC_DDAC_RVOLR: case RK817_CODEC_AHP_ANTI0: case RK817_CODEC_AHP_ANTI1: case RK817_CODEC_AHP_CFG0: case RK817_CODEC_AHP_CFG1: case RK817_CODEC_AHP_CP: case RK817_CODEC_ACLASSD_CFG1: case RK817_CODEC_ACLASSD_CFG2: case RK817_CODEC_APLL_CFG0: case RK817_CODEC_APLL_CFG1: case RK817_CODEC_APLL_CFG2: case RK817_CODEC_APLL_CFG3: case RK817_CODEC_APLL_CFG4: case RK817_CODEC_APLL_CFG5: case RK817_CODEC_DI2S_CKM: case RK817_CODEC_DI2S_RSD: case RK817_CODEC_DI2S_RXCR1: case RK817_CODEC_DI2S_RXCR2: case RK817_CODEC_DI2S_RXCMD_TSD: case RK817_CODEC_DI2S_TXCR1: case RK817_CODEC_DI2S_TXCR2: case RK817_CODEC_DI2S_TXCR3_TXCMD: case RK817_PMIC_CHIP_NAME: case RK817_PMIC_CHIP_VER: return true; default: return false; } } static int rk817_codec_ctl_gpio(struct rk817_codec_priv *rk817, int gpio, int level) { if ((gpio & CODEC_SET_SPK) && rk817->spk_ctl_gpio) { if (level && rk817->spk_mute_delay) msleep(rk817->spk_mute_delay); gpiod_set_value(rk817->spk_ctl_gpio, level); DBG("%s set spk clt %d\n", __func__, level); if (!level && rk817->spk_mute_delay) msleep(rk817->spk_mute_delay); } if ((gpio & CODEC_SET_HP) && rk817->hp_ctl_gpio) { if (level && rk817->hp_mute_delay) msleep(rk817->hp_mute_delay); gpiod_set_value(rk817->hp_ctl_gpio, level); DBG("%s set hp clt %d\n", __func__, level); if (!level && rk817->hp_mute_delay) msleep(rk817->hp_mute_delay); } return 0; } static int rk817_reset(struct snd_soc_component *component) { struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component); snd_soc_component_write(component, RK817_CODEC_DTOP_LPT_SRST, 0x40); snd_soc_component_write(component, RK817_CODEC_DDAC_POPD_DACST, 0x02); snd_soc_component_write(component, RK817_CODEC_DI2S_CKM, 0x00); snd_soc_component_write(component, RK817_CODEC_DTOP_DIGEN_CLKE, 0xff); snd_soc_component_write(component, RK817_CODEC_APLL_CFG1, 0x58); snd_soc_component_write(component, RK817_CODEC_APLL_CFG2, 0x2d); snd_soc_component_write(component, RK817_CODEC_APLL_CFG3, 0x0c); snd_soc_component_write(component, RK817_CODEC_APLL_CFG5, 0x00); snd_soc_component_write(component, RK817_CODEC_DTOP_DIGEN_CLKE, 0x00); if (rk817->chip_ver <= 0x4) { DBG("%s (%d): 0x4 and previous versions\n", __func__, __LINE__); snd_soc_component_write(component, RK817_CODEC_APLL_CFG0, 0x0c); snd_soc_component_write(component, RK817_CODEC_APLL_CFG4, 0x95); } else { DBG("%s (%d): 0x4 version later\n", __func__, __LINE__); snd_soc_component_write(component, RK817_CODEC_APLL_CFG0, 0x04); snd_soc_component_write(component, RK817_CODEC_APLL_CFG4, 0xa5); } snd_soc_component_write(component, RK817_CODEC_DTOP_DIGEN_CLKE, 0x00); return 0; } static int rk817_restart_dac_digital_clk(struct snd_soc_component *component) { snd_soc_component_update_bits(component, RK817_CODEC_ADAC_CFG1, PWD_DACBIAS_MASK, PWD_DACBIAS_DOWN); usleep_range(500, 600); snd_soc_component_update_bits(component, RK817_CODEC_DTOP_DIGEN_CLKE, DAC_DIG_CLK_MASK, DAC_DIG_CLK_DIS); usleep_range(500, 600); snd_soc_component_update_bits(component, RK817_CODEC_DTOP_DIGEN_CLKE, DAC_DIG_CLK_MASK, DAC_DIG_CLK_EN); DBG("%s: %d - Playback DIG CLK OPS\n", __func__, __LINE__); usleep_range(500, 600); snd_soc_component_update_bits(component, RK817_CODEC_ADAC_CFG1, PWD_DACBIAS_MASK, PWD_DACBIAS_ON); return 0; } static int rk817_restart_dac_digital_clk_and_apll(struct snd_soc_component *component) { snd_soc_component_update_bits(component, RK817_CODEC_ADAC_CFG1, PWD_DACBIAS_MASK, PWD_DACBIAS_DOWN); usleep_range(500, 600); snd_soc_component_update_bits(component, RK817_CODEC_DTOP_DIGEN_CLKE, DAC_DIG_CLK_MASK, DAC_DIG_CLK_DIS); usleep_range(500, 600); snd_soc_component_update_bits(component, RK817_CODEC_DTOP_DIGEN_CLKE, DAC_DIG_CLK_MASK, DAC_DIG_CLK_EN); DBG("%s: %d - Playback DIG CLK OPS\n", __func__, __LINE__); snd_soc_component_update_bits(component, RK817_CODEC_APLL_CFG5, PLL_PW_DOWN, PLL_PW_DOWN); usleep_range(50, 60); snd_soc_component_update_bits(component, RK817_CODEC_APLL_CFG5, PLL_PW_DOWN, PLL_PW_UP); usleep_range(500, 600); snd_soc_component_update_bits(component, RK817_CODEC_ADAC_CFG1, PWD_DACBIAS_MASK, PWD_DACBIAS_ON); return 0; } static int rk817_restart_adc_digital_clk(struct snd_soc_component *component) { snd_soc_component_update_bits(component, RK817_CODEC_DTOP_DIGEN_CLKE, ADC_DIG_CLK_MASK, ADC_DIG_CLK_DIS); usleep_range(500, 600); snd_soc_component_update_bits(component, RK817_CODEC_DTOP_DIGEN_CLKE, ADC_DIG_CLK_MASK, ADC_DIG_CLK_EN); DBG("%s: %d - Capture DIG CLK OPS\n", __func__, __LINE__); return 0; } static int rk817_restart_adc_digital_clk_and_apll(struct snd_soc_component *component) { snd_soc_component_update_bits(component, RK817_CODEC_DTOP_DIGEN_CLKE, ADC_DIG_CLK_MASK, ADC_DIG_CLK_DIS); usleep_range(500, 600); snd_soc_component_update_bits(component, RK817_CODEC_DTOP_DIGEN_CLKE, ADC_DIG_CLK_MASK, ADC_DIG_CLK_EN); DBG("%s: %d - Capture DIG CLK OPS\n", __func__, __LINE__); snd_soc_component_update_bits(component, RK817_CODEC_APLL_CFG5, PLL_PW_DOWN, PLL_PW_DOWN); usleep_range(50, 60); snd_soc_component_update_bits(component, RK817_CODEC_APLL_CFG5, PLL_PW_DOWN, PLL_PW_UP); usleep_range(500, 600); return 0; } static struct rk817_reg_val_typ playback_power_up_list[] = { {RK817_CODEC_AREF_RTCFG1, 0x40}, {RK817_CODEC_DDAC_POPD_DACST, 0x02}, /* APLL */ /* {RK817_CODEC_APLL_CFG0, 0x04}, */ {RK817_CODEC_APLL_CFG1, 0x58}, {RK817_CODEC_APLL_CFG2, 0x2d}, /* {RK817_CODEC_APLL_CFG4, 0xa5}, */ {RK817_CODEC_APLL_CFG5, 0x00}, {RK817_CODEC_DI2S_RXCMD_TSD, 0x00}, {RK817_CODEC_DI2S_RSD, 0x00}, /* {RK817_CODEC_DI2S_CKM, 0x00}, */ {RK817_CODEC_DI2S_RXCR1, 0x00}, {RK817_CODEC_DI2S_RXCMD_TSD, 0x20}, {RK817_CODEC_DTOP_VUCTIME, 0xf4}, {RK817_CODEC_DDAC_MUTE_MIXCTL, 0x00}, {RK817_CODEC_DDAC_VOLL, 0x0a}, {RK817_CODEC_DDAC_VOLR, 0x0a}, }; #define RK817_CODEC_PLAYBACK_POWER_UP_LIST_LEN \ ARRAY_SIZE(playback_power_up_list) static struct rk817_reg_val_typ playback_power_down_list[] = { {RK817_CODEC_DDAC_MUTE_MIXCTL, 0x01}, {RK817_CODEC_ADAC_CFG1, 0x0f}, /* HP */ {RK817_CODEC_AHP_CFG0, 0xe0}, {RK817_CODEC_AHP_CP, 0x09}, /* SPK */ {RK817_CODEC_ACLASSD_CFG1, 0x69}, }; #define RK817_CODEC_PLAYBACK_POWER_DOWN_LIST_LEN \ ARRAY_SIZE(playback_power_down_list) static struct rk817_reg_val_typ capture_power_up_list[] = { {RK817_CODEC_AREF_RTCFG1, 0x40}, {RK817_CODEC_DADC_SR_ACL0, 0x02}, /* {RK817_CODEC_DTOP_DIGEN_CLKE, 0xff}, */ /* {RK817_CODEC_APLL_CFG0, 0x04}, */ {RK817_CODEC_APLL_CFG1, 0x58}, {RK817_CODEC_APLL_CFG2, 0x2d}, /* {RK817_CODEC_APLL_CFG4, 0xa5}, */ {RK817_CODEC_APLL_CFG5, 0x00}, /*{RK817_CODEC_DI2S_RXCMD_TSD, 0x00},*/ {RK817_CODEC_DI2S_RSD, 0x00}, /* {RK817_CODEC_DI2S_CKM, 0x00}, */ {RK817_CODEC_DI2S_RXCR1, 0x00}, {RK817_CODEC_DI2S_RXCMD_TSD, 0x20}, {RK817_CODEC_DTOP_VUCTIME, 0xf4}, {RK817_CODEC_DDAC_MUTE_MIXCTL, 0x00}, {RK817_CODEC_AADC_CFG0, 0x00}, {RK817_CODEC_AMIC_CFG0, 0x0a}, {RK817_CODEC_AMIC_CFG1, 0x30}, {RK817_CODEC_DI2S_TXCR3_TXCMD, 0x88}, {RK817_CODEC_DDAC_POPD_DACST, 0x02}, /* 0x29: -18db to 27db */ {RK817_CODEC_DMIC_PGA_GAIN, 0xaa}, }; #define RK817_CODEC_CAPTURE_POWER_UP_LIST_LEN \ ARRAY_SIZE(capture_power_up_list) static struct rk817_reg_val_typ capture_power_down_list[] = { {RK817_CODEC_AADC_CFG0, 0xc8}, {RK817_CODEC_AMIC_CFG0, 0x70}, }; #define RK817_CODEC_CAPTURE_POWER_DOWN_LIST_LEN \ ARRAY_SIZE(capture_power_down_list) static int rk817_codec_power_up(struct snd_soc_component *component, int type) { struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component); int i; DBG("%s : power up %s %s %s\n", __func__, type & RK817_CODEC_PLAYBACK ? "playback" : "", type & RK817_CODEC_CAPTURE ? "capture" : "", type & RK817_CODEC_INCALL ? "incall" : ""); if (type & RK817_CODEC_PLAYBACK) { snd_soc_component_update_bits(component, RK817_CODEC_DTOP_DIGEN_CLKE, DAC_DIG_CLK_MASK, DAC_DIG_CLK_EN); for (i = 0; i < RK817_CODEC_PLAYBACK_POWER_UP_LIST_LEN; i++) { snd_soc_component_write(component, playback_power_up_list[i].reg, playback_power_up_list[i].value); } /* configure APLL CFG0/4 */ if (rk817->chip_ver <= 0x4) { DBG("%s (%d): 0x4 and previous versions\n", __func__, __LINE__); snd_soc_component_write(component, RK817_CODEC_APLL_CFG0, 0x0c); snd_soc_component_write(component, RK817_CODEC_APLL_CFG4, 0x95); } else { DBG("%s: 0x4 version later\n", __func__); snd_soc_component_write(component, RK817_CODEC_APLL_CFG0, 0x04); snd_soc_component_write(component, RK817_CODEC_APLL_CFG4, 0xa5); } rk817_restart_dac_digital_clk(component); } if (type & RK817_CODEC_CAPTURE) { snd_soc_component_update_bits(component, RK817_CODEC_DTOP_DIGEN_CLKE, ADC_DIG_CLK_MASK, ADC_DIG_CLK_EN); for (i = 0; i < RK817_CODEC_CAPTURE_POWER_UP_LIST_LEN; i++) { snd_soc_component_write(component, capture_power_up_list[i].reg, capture_power_up_list[i].value); } /* configure APLL CFG0/4 */ if (rk817->chip_ver <= 0x4) { DBG("%s (%d): 0x4 and previous versions\n", __func__, __LINE__); snd_soc_component_write(component, RK817_CODEC_APLL_CFG0, 0x0c); snd_soc_component_write(component, RK817_CODEC_APLL_CFG4, 0x95); } else { DBG("%s: 0x4 version later\n", __func__); snd_soc_component_write(component, RK817_CODEC_APLL_CFG0, 0x04); snd_soc_component_write(component, RK817_CODEC_APLL_CFG4, 0xa5); } rk817_restart_adc_digital_clk(component); if (rk817->mic_in_differential) snd_soc_component_update_bits(component, RK817_CODEC_AMIC_CFG0, MIC_DIFF_MASK, MIC_DIFF_EN); else snd_soc_component_update_bits(component, RK817_CODEC_AMIC_CFG0, MIC_DIFF_MASK, MIC_DIFF_DIS); if (rk817->pdmdata_out_enable) snd_soc_component_update_bits(component, RK817_CODEC_DI2S_CKM, PDM_EN_MASK, PDM_EN_ENABLE); snd_soc_component_write(component, RK817_CODEC_DADC_VOLL, rk817->capture_volume); snd_soc_component_write(component, RK817_CODEC_DADC_VOLR, rk817->capture_volume); } return 0; } static int rk817_codec_power_down(struct snd_soc_component *component, int type) { struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component); int i; DBG("%s : power down %s %s %s\n", __func__, type & RK817_CODEC_PLAYBACK ? "playback" : "", type & RK817_CODEC_CAPTURE ? "capture" : "", type & RK817_CODEC_INCALL ? "incall" : ""); /* mute output for pop noise */ if ((type & RK817_CODEC_PLAYBACK) || (type & RK817_CODEC_INCALL)) { snd_soc_component_update_bits(component, RK817_CODEC_DDAC_MUTE_MIXCTL, DACMT_ENABLE, DACMT_ENABLE); } if (type & RK817_CODEC_CAPTURE) { for (i = 0; i < RK817_CODEC_CAPTURE_POWER_DOWN_LIST_LEN; i++) { snd_soc_component_write(component, capture_power_down_list[i].reg, capture_power_down_list[i].value); } snd_soc_component_update_bits(component, RK817_CODEC_DTOP_DIGEN_CLKE, ADC_DIG_CLK_MASK, ADC_DIG_CLK_DIS); } if (type & RK817_CODEC_PLAYBACK) { for (i = 0; i < RK817_CODEC_PLAYBACK_POWER_DOWN_LIST_LEN; i++) { snd_soc_component_write(component, playback_power_down_list[i].reg, playback_power_down_list[i].value); } snd_soc_component_update_bits(component, RK817_CODEC_DTOP_DIGEN_CLKE, DAC_DIG_CLK_MASK, DAC_DIG_CLK_DIS); } if (type == RK817_CODEC_ALL) { for (i = 0; i < RK817_CODEC_PLAYBACK_POWER_DOWN_LIST_LEN; i++) { snd_soc_component_write(component, playback_power_down_list[i].reg, playback_power_down_list[i].value); } for (i = 0; i < RK817_CODEC_CAPTURE_POWER_DOWN_LIST_LEN; i++) { snd_soc_component_write(component, capture_power_down_list[i].reg, capture_power_down_list[i].value); } snd_soc_component_write(component, RK817_CODEC_DTOP_DIGEN_CLKE, 0x00); snd_soc_component_write(component, RK817_CODEC_APLL_CFG5, 0x01); snd_soc_component_write(component, RK817_CODEC_AREF_RTCFG1, 0x06); rk817->rate = 0; } return 0; } /* For tiny alsa playback/capture/voice call path */ static const char * const rk817_playback_path_mode[] = { "OFF", "RCV", "SPK", "HP", "HP_NO_MIC", "BT", "SPK_HP", /* 0-6 */ "RING_SPK", "RING_HP", "RING_HP_NO_MIC", "RING_SPK_HP"}; /* 7-10 */ static const char * const rk817_capture_path_mode[] = { "MIC OFF", "Main Mic", "Hands Free Mic", "BT Sco Mic"}; static const char * const rk817_binary_mode[] = {"OFF", "ON"}; static SOC_ENUM_SINGLE_DECL(rk817_playback_path_type, 0, 0, rk817_playback_path_mode); static SOC_ENUM_SINGLE_DECL(rk817_capture_path_type, 0, 0, rk817_capture_path_mode); static SOC_ENUM_SINGLE_DECL(rk817_resume_path_type, 0, 0, rk817_binary_mode); static int rk817_playback_path_config(struct snd_soc_component *component, long pre_path, long target_path) { struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component); rk817->playback_path = target_path; DBG("%s : set playback_path %ld, pre_path %ld\n", __func__, rk817->playback_path, pre_path); mutex_lock(&rk817->clk_lock); if (rk817->playback_path != OFF) { if (rk817->clk_playback == 0) { clk_prepare_enable(rk817->mclk); rk817->clk_playback++; } } else { if (rk817->clk_playback > 0) { clk_disable_unprepare(rk817->mclk); rk817->clk_playback--; } } mutex_unlock(&rk817->clk_lock); switch (rk817->playback_path) { case OFF: if (pre_path != OFF && (pre_path != HP_PATH && pre_path != HP_NO_MIC && pre_path != RING_HP && pre_path != RING_HP_NO_MIC)) { rk817_codec_power_down(component, RK817_CODEC_PLAYBACK); if (rk817->capture_path == 0) rk817_codec_power_down(component, RK817_CODEC_ALL); } break; case RCV: case SPK_PATH: case RING_SPK: if (pre_path == OFF) rk817_codec_power_up(component, RK817_CODEC_PLAYBACK); if (rk817->out_l2spk_r2hp) { /* for costdown: ldac -> ClassD rdac -> Hp */ /* HP_CP_EN , CP 2.3V */ snd_soc_component_write(component, RK817_CODEC_AHP_CP, 0x11); /* power on HP two stage opamp ,HP amplitude 0db */ snd_soc_component_write(component, RK817_CODEC_AHP_CFG0, 0x80); /* power on dac ibias/l/r */ snd_soc_component_write(component, RK817_CODEC_ADAC_CFG1, PWD_DACBIAS_ON | PWD_DACD_ON | PWD_DACL_ON | PWD_DACR_ON); /* CLASS D mode */ snd_soc_component_write(component, RK817_CODEC_DDAC_MUTE_MIXCTL, 0x18); /* CLASS D enable */ snd_soc_component_write(component, RK817_CODEC_ACLASSD_CFG1, 0xa5); /* restart CLASS D, OCPP/N */ snd_soc_component_write(component, RK817_CODEC_ACLASSD_CFG2, 0xf7); } else if (!rk817->use_ext_amplifier) { /* power on dac ibias/l/r */ snd_soc_component_write(component, RK817_CODEC_ADAC_CFG1, PWD_DACBIAS_ON | PWD_DACD_ON | PWD_DACL_DOWN | PWD_DACR_DOWN); /* CLASS D mode */ snd_soc_component_write(component, RK817_CODEC_DDAC_MUTE_MIXCTL, 0x10); /* CLASS D enable */ snd_soc_component_write(component, RK817_CODEC_ACLASSD_CFG1, 0xa5); /* restart CLASS D, OCPP/N */ snd_soc_component_write(component, RK817_CODEC_ACLASSD_CFG2, 0xf7); } else { /* HP_CP_EN , CP 2.3V */ snd_soc_component_write(component, RK817_CODEC_AHP_CP, 0x11); /* power on HP two stage opamp ,HP amplitude 0db */ snd_soc_component_write(component, RK817_CODEC_AHP_CFG0, 0x80); /* power on dac ibias/l/r */ snd_soc_component_write(component, RK817_CODEC_ADAC_CFG1, PWD_DACBIAS_ON | PWD_DACD_DOWN | PWD_DACL_ON | PWD_DACR_ON); snd_soc_component_update_bits(component, RK817_CODEC_DDAC_MUTE_MIXCTL, DACMT_ENABLE, DACMT_DISABLE); } snd_soc_component_write(component, RK817_CODEC_DDAC_VOLL, rk817->spk_volume); snd_soc_component_write(component, RK817_CODEC_DDAC_VOLR, rk817->spk_volume); break; case HP_PATH: case HP_NO_MIC: case RING_HP: case RING_HP_NO_MIC: if (pre_path == OFF) rk817_codec_power_up(component, RK817_CODEC_PLAYBACK); /* HP_CP_EN , CP 2.3V */ snd_soc_component_write(component, RK817_CODEC_AHP_CP, 0x11); /* power on HP two stage opamp ,HP amplitude 0db */ snd_soc_component_write(component, RK817_CODEC_AHP_CFG0, 0x80); /* power on dac ibias/l/r */ snd_soc_component_write(component, RK817_CODEC_ADAC_CFG1, PWD_DACBIAS_ON | PWD_DACD_DOWN | PWD_DACL_ON | PWD_DACR_ON); snd_soc_component_update_bits(component, RK817_CODEC_DDAC_MUTE_MIXCTL, DACMT_ENABLE, DACMT_DISABLE); snd_soc_component_write(component, RK817_CODEC_DDAC_VOLL, rk817->hp_volume); snd_soc_component_write(component, RK817_CODEC_DDAC_VOLR, rk817->hp_volume); break; case BT: break; case SPK_HP: case RING_SPK_HP: if (pre_path == OFF) rk817_codec_power_up(component, RK817_CODEC_PLAYBACK); /* HP_CP_EN , CP 2.3V */ snd_soc_component_write(component, RK817_CODEC_AHP_CP, 0x11); /* power on HP two stage opamp ,HP amplitude 0db */ snd_soc_component_write(component, RK817_CODEC_AHP_CFG0, 0x80); /* power on dac ibias/l/r */ snd_soc_component_write(component, RK817_CODEC_ADAC_CFG1, PWD_DACBIAS_ON | PWD_DACD_ON | PWD_DACL_ON | PWD_DACR_ON); if (!rk817->use_ext_amplifier) { /* CLASS D mode */ snd_soc_component_write(component, RK817_CODEC_DDAC_MUTE_MIXCTL, 0x10); /* CLASS D enable */ snd_soc_component_write(component, RK817_CODEC_ACLASSD_CFG1, 0xa5); /* restart CLASS D, OCPP/N */ snd_soc_component_write(component, RK817_CODEC_ACLASSD_CFG2, 0xf7); } snd_soc_component_write(component, RK817_CODEC_DDAC_VOLL, rk817->hp_volume); snd_soc_component_write(component, RK817_CODEC_DDAC_VOLR, rk817->hp_volume); break; default: return -EINVAL; } return 0; } static int rk817_playback_path_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component); DBG("%s : playback_path %ld\n", __func__, rk817->playback_path); ucontrol->value.integer.value[0] = rk817->playback_path; return 0; } static int rk817_playback_path_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component); if (rk817->playback_path == ucontrol->value.integer.value[0]) { DBG("%s : playback_path is not changed!\n", __func__); return 0; } return rk817_playback_path_config(component, rk817->playback_path, ucontrol->value.integer.value[0]); } static int rk817_capture_path_config(struct snd_soc_component *component, long pre_path, long target_path) { struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component); rk817->capture_path = target_path; DBG("%s : set capture_path %ld, pre_path %ld\n", __func__, rk817->capture_path, pre_path); mutex_lock(&rk817->clk_lock); if (rk817->capture_path != MIC_OFF) { if (rk817->clk_capture == 0) { clk_prepare_enable(rk817->mclk); rk817->clk_capture++; } } else { if (rk817->clk_capture > 0) { clk_disable_unprepare(rk817->mclk); rk817->clk_capture--; } } mutex_unlock(&rk817->clk_lock); switch (rk817->capture_path) { case MIC_OFF: if (pre_path != MIC_OFF) { rk817_codec_power_down(component, RK817_CODEC_CAPTURE); if (rk817->playback_path == OFF) rk817_codec_power_down(component, RK817_CODEC_ALL); } break; case MAIN_MIC: if (pre_path == MIC_OFF) rk817_codec_power_up(component, RK817_CODEC_CAPTURE); if (rk817->adc_for_loopback) { /* don't need to gain when adc use for loopback */ snd_soc_component_update_bits(component, RK817_CODEC_AMIC_CFG0, 0xf, 0x0); snd_soc_component_write(component, RK817_CODEC_DMIC_PGA_GAIN, 0x66); snd_soc_component_write(component, RK817_CODEC_DADC_VOLL, 0x00); snd_soc_component_write(component, RK817_CODEC_DADC_VOLR, 0x00); break; } if (!rk817->mic_in_differential) { snd_soc_component_write(component, RK817_CODEC_DADC_VOLR, 0xff); snd_soc_component_update_bits(component, RK817_CODEC_AADC_CFG0, ADC_R_PWD_MASK, ADC_R_PWD_EN); snd_soc_component_update_bits(component, RK817_CODEC_AMIC_CFG0, PWD_PGA_R_MASK, PWD_PGA_R_EN); } break; case HANDS_FREE_MIC: if (pre_path == MIC_OFF) rk817_codec_power_up(component, RK817_CODEC_CAPTURE); if (rk817->adc_for_loopback) { /* don't need to gain when adc use for loopback */ snd_soc_component_update_bits(component, RK817_CODEC_AMIC_CFG0, 0xf, 0x0); snd_soc_component_write(component, RK817_CODEC_DMIC_PGA_GAIN, 0x66); snd_soc_component_write(component, RK817_CODEC_DADC_VOLL, 0x00); snd_soc_component_write(component, RK817_CODEC_DADC_VOLR, 0x00); break; } if (!rk817->mic_in_differential) { snd_soc_component_write(component, RK817_CODEC_DADC_VOLL, 0xff); snd_soc_component_update_bits(component, RK817_CODEC_AADC_CFG0, ADC_L_PWD_MASK, ADC_L_PWD_EN); snd_soc_component_update_bits(component, RK817_CODEC_AMIC_CFG0, PWD_PGA_L_MASK, PWD_PGA_L_EN); } break; case BT_SCO_MIC: break; default: return -EINVAL; } return 0; } static int rk817_capture_path_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component); DBG("%s : capture_path %ld\n", __func__, rk817->capture_path); ucontrol->value.integer.value[0] = rk817->capture_path; return 0; } static int rk817_capture_path_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component); if (rk817->capture_path == ucontrol->value.integer.value[0]) { DBG("%s : capture_path is not changed!\n", __func__); return 0; } return rk817_capture_path_config(component, rk817->capture_path, ucontrol->value.integer.value[0]); } static int rk817_resume_path_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component); DBG("%s : resume_path %d\n", __func__, rk817->resume_path); ucontrol->value.integer.value[0] = rk817->resume_path; return 0; } static int rk817_resume_path_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component); rk817->resume_path = ucontrol->value.integer.value[0]; return 0; } static int rk817_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); unsigned int left_val, right_val; unsigned int max_val = RK817_DAC_VOL_MAX; left_val = max_val - ucontrol->value.integer.value[0]; right_val = max_val - ucontrol->value.integer.value[1]; if (left_val < RK817_DAC_VOL_MIN || left_val > RK817_DAC_VOL_MAX || right_val < RK817_DAC_VOL_MIN || right_val > RK817_DAC_VOL_MAX) { dev_warn(component->dev, "%s: Volume out of range [%d, %d], left=%ld, right=%ld\n", __func__, max_val - RK817_DAC_VOL_MAX, max_val - RK817_DAC_VOL_MIN, ucontrol->value.integer.value[0], ucontrol->value.integer.value[1]); return -EINVAL; } /* Re-invert the values before setting them */ ucontrol->value.integer.value[0] = max_val - left_val; ucontrol->value.integer.value[1] = max_val - right_val; return snd_soc_put_volsw(kcontrol, ucontrol); } static struct snd_kcontrol_new rk817_snd_controls[] = { SOC_ENUM_EXT("Playback Path", rk817_playback_path_type, rk817_playback_path_get, rk817_playback_path_put), SOC_ENUM_EXT("Capture MIC Path", rk817_capture_path_type, rk817_capture_path_get, rk817_capture_path_put), SOC_ENUM_EXT("Resume Path", rk817_resume_path_type, rk817_resume_path_get, rk817_resume_path_put), SOC_DOUBLE_R_EXT_TLV("DAC Playback Volume", RK817_CODEC_DDAC_VOLL, RK817_CODEC_DDAC_VOLR, 0, 0xff, 1, snd_soc_get_volsw, rk817_dac_vol_put, dac_vol_tlv), SOC_DOUBLE_R_TLV("ADC Capture Volume", RK817_CODEC_DADC_VOLL, RK817_CODEC_DADC_VOLR, 0, 0xff, 1, adc_vol_tlv), SOC_DOUBLE_TLV("MIC Boost Gain", RK817_CODEC_AMIC_CFG0, 0, 2, 3, 0, adc_bst_tlv), SOC_DOUBLE_TLV("ADC PGA Gain", RK817_CODEC_DMIC_PGA_GAIN, 4, 0, 15, 0, adc_pga_tlv), SOC_SINGLE_TLV("HP Output Gain", RK817_CODEC_AHP_CFG0, 3, 3, 0, hp_out_tlv), }; static int rk817_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 rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component); rk817->stereo_sysclk = freq; DBG("%s : MCLK = %dHz\n", __func__, rk817->stereo_sysclk); return 0; } static int rk817_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_component *component = codec_dai->component; unsigned int i2s_mst = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: i2s_mst |= RK817_I2S_MODE_SLV; break; case SND_SOC_DAIFMT_CBM_CFM: i2s_mst |= RK817_I2S_MODE_MST; break; default: dev_err(component->dev, "%s : set master mask failed!\n", __func__); return -EINVAL; } DBG("%s : i2s %s mode\n", __func__, i2s_mst ? "master" : "slave"); snd_soc_component_update_bits(component, RK817_CODEC_DI2S_CKM, RK817_I2S_MODE_MASK, i2s_mst); return 0; } static int rk817_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 rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component); unsigned int rate = params_rate(params); unsigned char apll_cfg3_val; unsigned char dtop_digen_sr_lmt0; unsigned int ret = 0; DBG("%s : pre sample rate = %d, cur sample rate = %dHz\n", __func__, rk817->rate, rate); if (rk817->chip_ver <= 0x4) { DBG("%s: 0x4 and previous versions\n", __func__); snd_soc_component_write(component, RK817_CODEC_APLL_CFG0, 0x0c); snd_soc_component_write(component, RK817_CODEC_APLL_CFG4, 0x95); } else { DBG("%s: 0x4 version later\n", __func__); snd_soc_component_write(component, RK817_CODEC_APLL_CFG0, 0x04); snd_soc_component_write(component, RK817_CODEC_APLL_CFG4, 0xa5); } switch (rate) { case 8000: apll_cfg3_val = 0x03; dtop_digen_sr_lmt0 = 0x00; break; case 16000: apll_cfg3_val = 0x06; dtop_digen_sr_lmt0 = 0x01; break; case 96000: apll_cfg3_val = 0x18; dtop_digen_sr_lmt0 = 0x03; break; case 32000: case 44100: case 48000: apll_cfg3_val = 0x0c; dtop_digen_sr_lmt0 = 0x02; break; default: pr_err("Unsupported rate: %d\n", rate); return -EINVAL; } /** * Note that: If you use the ALSA hooks plugin, entering hw_params() * is before playback/capture_path_put, therefore, we need to configure * APLL_CFG3/DTOP_DIGEN_CLKE/DDAC_SR_LMT0 for different sample rates. */ if ((rk817->rate != rate) && !((substream->stream == SNDRV_PCM_STREAM_CAPTURE) && rk817->pdmdata_out_enable)) { ret = clk_set_rate(rk817->mclk, rk817->stereo_sysclk); if (ret) dev_warn(component->dev, "%s %d clk_set_rate %d failed\n", __func__, __LINE__, rk817->stereo_sysclk); snd_soc_component_write(component, RK817_CODEC_APLL_CFG3, apll_cfg3_val); snd_soc_component_update_bits(component, RK817_CODEC_DDAC_SR_LMT0, DACSRT_MASK, dtop_digen_sr_lmt0); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) rk817_restart_dac_digital_clk_and_apll(component); else rk817_restart_adc_digital_clk_and_apll(component); } rk817->rate = rate; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: snd_soc_component_write(component, RK817_CODEC_DI2S_RXCR2, VDW_RX_16BITS); snd_soc_component_write(component, RK817_CODEC_DI2S_TXCR2, VDW_TX_16BITS); break; case SNDRV_PCM_FORMAT_S24_LE: case SNDRV_PCM_FORMAT_S32_LE: snd_soc_component_write(component, RK817_CODEC_DI2S_RXCR2, VDW_RX_24BITS); snd_soc_component_write(component, RK817_CODEC_DI2S_TXCR2, VDW_TX_24BITS); break; default: return -EINVAL; } return 0; } static int rk817_digital_mute_dac(struct snd_soc_dai *dai, int mute, int stream) { struct snd_soc_component *component = dai->component; struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component); DBG("%s %d\n", __func__, mute); if (mute) { rk817_codec_ctl_gpio(rk817, CODEC_SET_SPK, 0); rk817_codec_ctl_gpio(rk817, CODEC_SET_HP, 0); snd_soc_component_update_bits(component, RK817_CODEC_DDAC_MUTE_MIXCTL, DACMT_ENABLE, DACMT_ENABLE); rk817_restart_dac_digital_clk(component); snd_soc_component_update_bits(component, RK817_CODEC_DTOP_DIGEN_CLKE, I2SRX_EN_MASK, I2SRX_DIS); } else { snd_soc_component_update_bits(component, RK817_CODEC_DTOP_DIGEN_CLKE, I2SRX_EN_MASK, I2SRX_EN); snd_soc_component_update_bits(component, RK817_CODEC_DDAC_MUTE_MIXCTL, DACMT_ENABLE, DACMT_DISABLE); switch (rk817->playback_path) { case SPK_PATH: case RING_SPK: if (rk817->out_l2spk_r2hp) { snd_soc_component_write(component, RK817_CODEC_ADAC_CFG1, PWD_DACBIAS_ON | PWD_DACD_ON | PWD_DACL_ON | PWD_DACR_ON); } else if (!rk817->use_ext_amplifier) { snd_soc_component_write(component, RK817_CODEC_ADAC_CFG1, PWD_DACBIAS_ON | PWD_DACD_ON | PWD_DACL_DOWN | PWD_DACR_DOWN); } else { snd_soc_component_write(component, RK817_CODEC_ADAC_CFG1, PWD_DACBIAS_ON | PWD_DACD_DOWN | PWD_DACL_ON | PWD_DACR_ON); } rk817_codec_ctl_gpio(rk817, CODEC_SET_SPK, 1); rk817_codec_ctl_gpio(rk817, CODEC_SET_HP, 0); break; case HP_PATH: case HP_NO_MIC: case RING_HP: case RING_HP_NO_MIC: snd_soc_component_write(component, RK817_CODEC_ADAC_CFG1, PWD_DACBIAS_ON | PWD_DACD_DOWN | PWD_DACL_ON | PWD_DACR_ON); rk817_codec_ctl_gpio(rk817, CODEC_SET_SPK, 0); rk817_codec_ctl_gpio(rk817, CODEC_SET_HP, 1); break; case SPK_HP: case RING_SPK_HP: snd_soc_component_write(component, RK817_CODEC_ADAC_CFG1, PWD_DACBIAS_ON | PWD_DACD_ON | PWD_DACL_ON | PWD_DACR_ON); rk817_codec_ctl_gpio(rk817, CODEC_SET_SPK, 1); rk817_codec_ctl_gpio(rk817, CODEC_SET_HP, 1); break; default: break; } } return 0; } static int rk817_digital_mute_adc(struct snd_soc_dai *dai, int mute, int stream) { struct snd_soc_component *component = dai->component; if (mute) { snd_soc_component_update_bits(component, RK817_CODEC_DTOP_DIGEN_CLKE, I2STX_EN_MASK, I2STX_DIS); } else { snd_soc_component_update_bits(component, RK817_CODEC_DTOP_DIGEN_CLKE, I2STX_EN_MASK, I2STX_EN); snd_soc_component_update_bits(component, RK817_CODEC_DTOP_DIGEN_CLKE, I2STX_CKE_EN, I2STX_CKE_EN); } return 0; } static int rk817_digital_mute(struct snd_soc_dai *dai, int mute, int stream) { if (stream == SNDRV_PCM_STREAM_PLAYBACK) return rk817_digital_mute_dac(dai, mute, stream); else return rk817_digital_mute_adc(dai, mute, stream); } #define RK817_PLAYBACK_RATES (SNDRV_PCM_RATE_8000 |\ SNDRV_PCM_RATE_16000 | \ SNDRV_PCM_RATE_32000 | \ SNDRV_PCM_RATE_44100 | \ SNDRV_PCM_RATE_48000 | \ SNDRV_PCM_RATE_96000) #define RK817_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\ SNDRV_PCM_RATE_16000 | \ SNDRV_PCM_RATE_32000 | \ SNDRV_PCM_RATE_44100 | \ SNDRV_PCM_RATE_48000 | \ SNDRV_PCM_RATE_96000) #define RK817_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE |\ SNDRV_PCM_FMTBIT_S32_LE) static void rk817_codec_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; /** * Note: The following configurations will take effect when i2s bclk * is working, and we just need to handle the part of ADC that is * output to SoC. */ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { snd_soc_component_update_bits(component, RK817_CODEC_DTOP_DIGEN_CLKE, I2STX_CKE_EN, I2STX_CKE_EN); usleep_range(1000, 1100); snd_soc_component_update_bits(component, RK817_CODEC_DTOP_DIGEN_CLKE, I2STX_CKE_EN, I2STX_CKE_DIS); } } static const struct snd_soc_dai_ops rk817_dai_ops = { .hw_params = rk817_hw_params, .set_fmt = rk817_set_dai_fmt, .set_sysclk = rk817_set_dai_sysclk, .mute_stream = rk817_digital_mute, .shutdown = rk817_codec_shutdown, }; static struct snd_soc_dai_driver rk817_dai[] = { { .name = "rk817-hifi", .id = RK817_HIFI, .playback = { .stream_name = "HiFi Playback", .channels_min = 2, .channels_max = 8, .rates = RK817_PLAYBACK_RATES, .formats = RK817_FORMATS, }, .capture = { .stream_name = "HiFi Capture", .channels_min = 2, .channels_max = 8, .rates = RK817_CAPTURE_RATES, .formats = RK817_FORMATS, }, .ops = &rk817_dai_ops, }, { .name = "rk817-voice", .id = RK817_VOICE, .playback = { .stream_name = "Voice Playback", .channels_min = 1, .channels_max = 2, .rates = RK817_PLAYBACK_RATES, .formats = RK817_FORMATS, }, .capture = { .stream_name = "Voice Capture", .channels_min = 2, .channels_max = 8, .rates = RK817_CAPTURE_RATES, .formats = RK817_FORMATS, }, .ops = &rk817_dai_ops, }, }; static int rk817_suspend(struct snd_soc_component *component) { rk817_codec_power_down(component, RK817_CODEC_ALL); return 0; } static int rk817_resume(struct snd_soc_component *component) { struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component); if (rk817->resume_path) { if (rk817->capture_path != MIC_OFF) rk817_capture_path_config(component, OFF, rk817->capture_path); if (rk817->playback_path != OFF) rk817_playback_path_config(component, OFF, rk817->playback_path); } return 0; } static int rk817_probe(struct snd_soc_component *component) { struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component); int chip_name = 0; int chip_ver = 0; DBG("%s\n", __func__); if (!rk817) { dev_err(component->dev, "%s : rk817 priv is NULL!\n", __func__); return -EINVAL; } snd_soc_component_init_regmap(component, rk817->regmap); rk817->component = component; rk817->playback_path = OFF; rk817->capture_path = MIC_OFF; rk817->rate = 0; chip_name = snd_soc_component_read(component, RK817_PMIC_CHIP_NAME); chip_ver = snd_soc_component_read(component, RK817_PMIC_CHIP_VER); rk817->chip_ver = (chip_ver & 0x0f); dev_info(component->dev, "%s: chip_name:0x%x, chip_ver:0x%x\n", __func__, chip_name, chip_ver); /* always enable mclk, and will disable mclk in rk817_remove */ clk_prepare_enable(rk817->mclk); rk817_reset(component); mutex_init(&rk817->clk_lock); rk817->clk_capture = 0; rk817->clk_playback = 0; snd_soc_add_component_controls(component, rk817_snd_controls, ARRAY_SIZE(rk817_snd_controls)); return 0; } /* power down chip */ static void rk817_remove(struct snd_soc_component *component) { struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component); DBG("%s\n", __func__); if (!rk817) { dev_err(component->dev, "%s : rk817 is NULL\n", __func__); return; } rk817_codec_power_down(component, RK817_CODEC_ALL); snd_soc_component_exit_regmap(component); mutex_destroy(&rk817->clk_lock); clk_disable_unprepare(rk817->mclk); mdelay(10); } static const struct snd_soc_component_driver soc_codec_dev_rk817 = { .probe = rk817_probe, .remove = rk817_remove, .suspend = rk817_suspend, .resume = rk817_resume, .idle_bias_on = 1, .use_pmdown_time = 1, .endianness = 1, }; static int rk817_codec_parse_dt_property(struct device *dev, struct rk817_codec_priv *rk817) { struct device_node *node = dev->parent->of_node; int ret; DBG("%s()\n", __func__); if (!node) { dev_err(dev, "%s() dev->parent->of_node is NULL\n", __func__); return -ENODEV; } node = of_get_child_by_name(dev->parent->of_node, "codec"); if (!node) { dev_err(dev, "%s() Can not get child: codec\n", __func__); return -ENODEV; } rk817->hp_ctl_gpio = devm_gpiod_get_optional(dev, "hp-ctl", GPIOD_OUT_LOW); if (!IS_ERR_OR_NULL(rk817->hp_ctl_gpio)) { DBG("%s : hp-ctl-gpio %d\n", __func__, desc_to_gpio(rk817->hp_ctl_gpio)); } rk817->spk_ctl_gpio = devm_gpiod_get_optional(dev, "spk-ctl", GPIOD_OUT_LOW); if (!IS_ERR_OR_NULL(rk817->spk_ctl_gpio)) { DBG("%s : spk-ctl-gpio %d\n", __func__, desc_to_gpio(rk817->spk_ctl_gpio)); } ret = of_property_read_u32(node, "spk-mute-delay-ms", &rk817->spk_mute_delay); if (ret < 0) { DBG("%s() Can not read property spk-mute-delay-ms\n", __func__); rk817->spk_mute_delay = 0; } ret = of_property_read_u32(node, "hp-mute-delay-ms", &rk817->hp_mute_delay); if (ret < 0) { DBG("%s() Can not read property hp-mute-delay-ms\n", __func__); rk817->hp_mute_delay = 0; } DBG("spk mute delay %dms --- hp mute delay %dms\n", rk817->spk_mute_delay, rk817->hp_mute_delay); ret = of_property_read_u32(node, "spk-volume", &rk817->spk_volume); if (ret < 0) { DBG("%s() Can not read property spk-volume\n", __func__); rk817->spk_volume = OUT_VOLUME; } if (rk817->spk_volume < 3) rk817->spk_volume = 3; ret = of_property_read_u32(node, "hp-volume", &rk817->hp_volume); if (ret < 0) { DBG("%s() Can not read property hp-volume\n", __func__); rk817->hp_volume = OUT_VOLUME; } if (rk817->hp_volume < 3) rk817->hp_volume = 3; ret = of_property_read_u32(node, "capture-volume", &rk817->capture_volume); if (ret < 0) { DBG("%s() Can not read property capture-volume\n", __func__); rk817->capture_volume = CAPTURE_VOLUME; } rk817->mic_in_differential = of_property_read_bool(node, "mic-in-differential"); rk817->pdmdata_out_enable = of_property_read_bool(node, "pdmdata-out-enable"); rk817->use_ext_amplifier = of_property_read_bool(node, "use-ext-amplifier"); rk817->out_l2spk_r2hp = of_property_read_bool(node, "out-l2spk-r2hp"); rk817->adc_for_loopback = of_property_read_bool(node, "adc-for-loopback"); of_node_put(node); return 0; } static const struct regmap_config rk817_codec_regmap_config = { .name = "rk817-codec", .reg_bits = 8, .val_bits = 8, .reg_stride = 1, .max_register = 0xfe, .cache_type = REGCACHE_FLAT, .volatile_reg = rk817_volatile_register, .writeable_reg = rk817_codec_register, .readable_reg = rk817_codec_register, .reg_defaults = rk817_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rk817_reg_defaults), }; static int rk817_platform_probe(struct platform_device *pdev) { struct rk808 *rk817 = dev_get_drvdata(pdev->dev.parent); struct rk817_codec_priv *rk817_codec_data; int ret; DBG("%s\n", __func__); if (!rk817) { dev_err(&pdev->dev, "%s : rk817 is NULL\n", __func__); return -EINVAL; } rk817_codec_data = devm_kzalloc(&pdev->dev, sizeof(struct rk817_codec_priv), GFP_KERNEL); if (!rk817_codec_data) return -ENOMEM; platform_set_drvdata(pdev, rk817_codec_data); ret = rk817_codec_parse_dt_property(&pdev->dev, rk817_codec_data); if (ret < 0) { dev_err(&pdev->dev, "%s() parse device tree property error %d\n", __func__, ret); goto err_; } rk817_codec_data->regmap = devm_regmap_init_i2c(rk817->i2c, &rk817_codec_regmap_config); if (IS_ERR(rk817_codec_data->regmap)) { ret = PTR_ERR(rk817_codec_data->regmap); dev_err(&pdev->dev, "failed to allocate register map: %d\n", ret); goto err_; } rk817_codec_data->mclk = devm_clk_get(&pdev->dev, "mclk"); if (IS_ERR(rk817_codec_data->mclk)) { dev_err(&pdev->dev, "Unable to get mclk\n"); ret = -ENXIO; goto err_; } ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_rk817, rk817_dai, ARRAY_SIZE(rk817_dai)); if (ret < 0) { dev_err(&pdev->dev, "%s() register codec error %d\n", __func__, ret); goto err_; } return 0; err_: return ret; } static int rk817_platform_remove(struct platform_device *pdev) { return 0; } static void rk817_platform_shutdown(struct platform_device *pdev) { struct rk817_codec_priv *rk817 = dev_get_drvdata(&pdev->dev); DBG("%s\n", __func__); if (rk817 && rk817->component) rk817_codec_power_down(rk817->component, RK817_CODEC_ALL); } static const struct of_device_id rk817_codec_dt_ids[] = { { .compatible = "rockchip,rk817-codec" }, {}, }; MODULE_DEVICE_TABLE(of, rk817_codec_dt_ids); static struct platform_driver rk817_codec_driver = { .driver = { .name = "rk817-codec", .of_match_table = rk817_codec_dt_ids, }, .probe = rk817_platform_probe, .remove = rk817_platform_remove, .shutdown = rk817_platform_shutdown, }; module_platform_driver(rk817_codec_driver); MODULE_DESCRIPTION("ASoC RK817 codec driver"); MODULE_AUTHOR("binyuan "); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:rk817-codec");