// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2018, The Linux Foundation. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "csra66x0.h" #define DRV_NAME "csra66x0_codec" /* CSRA66X0 register default values */ static struct reg_default csra66x0_reg_defaults[] = { {CSRA66X0_AUDIO_IF_RX_CONFIG1, 0x00}, {CSRA66X0_AUDIO_IF_RX_CONFIG2, 0x0B}, {CSRA66X0_AUDIO_IF_RX_CONFIG3, 0x0F}, {CSRA66X0_AUDIO_IF_TX_EN, 0x00}, {CSRA66X0_AUDIO_IF_TX_CONFIG1, 0x6B}, {CSRA66X0_AUDIO_IF_TX_CONFIG2, 0x02}, {CSRA66X0_I2C_DEVICE_ADDRESS, 0x0D}, {CSRA66X0_CHIP_ID_FA, 0x39}, {CSRA66X0_ROM_VER_FA, 0x08}, {CSRA66X0_CHIP_REV_0_FA, 0x05}, {CSRA66X0_CHIP_REV_1_FA, 0x03}, {CSRA66X0_CH1_MIX_SEL, 0x01}, {CSRA66X0_CH2_MIX_SEL, 0x10}, {CSRA66X0_CH1_SAMPLE1_SCALE_0, 0x00}, {CSRA66X0_CH1_SAMPLE1_SCALE_1, 0x20}, {CSRA66X0_CH1_SAMPLE3_SCALE_0, 0x00}, {CSRA66X0_CH1_SAMPLE3_SCALE_1, 0x20}, {CSRA66X0_CH1_SAMPLE5_SCALE_0, 0x00}, {CSRA66X0_CH1_SAMPLE5_SCALE_1, 0x20}, {CSRA66X0_CH1_SAMPLE7_SCALE_0, 0x00}, {CSRA66X0_CH1_SAMPLE7_SCALE_1, 0x20}, {CSRA66X0_CH1_SAMPLE2_SCALE_0, 0x00}, {CSRA66X0_CH1_SAMPLE2_SCALE_1, 0x20}, {CSRA66X0_CH1_SAMPLE4_SCALE_0, 0x00}, {CSRA66X0_CH1_SAMPLE4_SCALE_1, 0x20}, {CSRA66X0_CH1_SAMPLE6_SCALE_0, 0x00}, {CSRA66X0_CH1_SAMPLE6_SCALE_1, 0x20}, {CSRA66X0_CH1_SAMPLE8_SCALE_0, 0x00}, {CSRA66X0_CH1_SAMPLE8_SCALE_1, 0x20}, {CSRA66X0_CH2_SAMPLE1_SCALE_0, 0x00}, {CSRA66X0_CH2_SAMPLE1_SCALE_1, 0x20}, {CSRA66X0_CH2_SAMPLE3_SCALE_0, 0x00}, {CSRA66X0_CH2_SAMPLE3_SCALE_1, 0x20}, {CSRA66X0_CH2_SAMPLE5_SCALE_0, 0x00}, {CSRA66X0_CH2_SAMPLE5_SCALE_1, 0x20}, {CSRA66X0_CH2_SAMPLE7_SCALE_0, 0x00}, {CSRA66X0_CH2_SAMPLE7_SCALE_1, 0x20}, {CSRA66X0_CH2_SAMPLE2_SCALE_0, 0x00}, {CSRA66X0_CH2_SAMPLE2_SCALE_1, 0x20}, {CSRA66X0_CH2_SAMPLE4_SCALE_0, 0x00}, {CSRA66X0_CH2_SAMPLE4_SCALE_1, 0x20}, {CSRA66X0_CH2_SAMPLE6_SCALE_0, 0x00}, {CSRA66X0_CH2_SAMPLE6_SCALE_1, 0x20}, {CSRA66X0_CH2_SAMPLE8_SCALE_0, 0x00}, {CSRA66X0_CH2_SAMPLE8_SCALE_1, 0x20}, {CSRA66X0_VOLUME_CONFIG_FA, 0x26}, {CSRA66X0_STARTUP_DELAY_FA, 0x00}, {CSRA66X0_CH1_VOLUME_0_FA, 0x19}, {CSRA66X0_CH1_VOLUME_1_FA, 0x01}, {CSRA66X0_CH2_VOLUME_0_FA, 0x19}, {CSRA66X0_CH2_VOLUME_1_FA, 0x01}, {CSRA66X0_QUAD_ENC_COUNT_0_FA, 0x00}, {CSRA66X0_QUAD_ENC_COUNT_1_FA, 0x00}, {CSRA66X0_SOFT_CLIP_CONFIG, 0x00}, {CSRA66X0_CH1_HARD_CLIP_THRESH, 0x00}, {CSRA66X0_CH2_HARD_CLIP_THRESH, 0x00}, {CSRA66X0_SOFT_CLIP_THRESH, 0x00}, {CSRA66X0_DS_ENABLE_THRESH_0, 0x05}, {CSRA66X0_DS_ENABLE_THRESH_1, 0x00}, {CSRA66X0_DS_TARGET_COUNT_0, 0x00}, {CSRA66X0_DS_TARGET_COUNT_1, 0xFF}, {CSRA66X0_DS_TARGET_COUNT_2, 0xFF}, {CSRA66X0_DS_DISABLE_THRESH_0, 0x0F}, {CSRA66X0_DS_DISABLE_THRESH_1, 0x00}, {CSRA66X0_DCA_CTRL, 0x07}, {CSRA66X0_CH1_DCA_THRESH, 0x40}, {CSRA66X0_CH2_DCA_THRESH, 0x40}, {CSRA66X0_DCA_ATTACK_RATE, 0x00}, {CSRA66X0_DCA_RELEASE_RATE, 0x00}, {CSRA66X0_CH1_OUTPUT_INVERT_EN, 0x00}, {CSRA66X0_CH2_OUTPUT_INVERT_EN, 0x00}, {CSRA66X0_CH1_176P4K_DELAY, 0x00}, {CSRA66X0_CH2_176P4K_DELAY, 0x00}, {CSRA66X0_CH1_192K_DELAY, 0x00}, {CSRA66X0_CH2_192K_DELAY, 0x00}, {CSRA66X0_DEEMP_CONFIG_FA, 0x00}, {CSRA66X0_CH1_TREBLE_GAIN_CTRL_FA, 0x00}, {CSRA66X0_CH2_TREBLE_GAIN_CTRL_FA, 0x00}, {CSRA66X0_CH1_TREBLE_FC_CTRL_FA, 0x00}, {CSRA66X0_CH2_TREBLE_FC_CTRL_FA, 0x00}, {CSRA66X0_CH1_BASS_GAIN_CTRL_FA, 0x00}, {CSRA66X0_CH2_BASS_GAIN_CTRL_FA, 0x00}, {CSRA66X0_CH1_BASS_FC_CTRL_FA, 0x00}, {CSRA66X0_CH2_BASS_FC_CTRL_FA, 0x00}, {CSRA66X0_FILTER_SEL_8K, 0x00}, {CSRA66X0_FILTER_SEL_11P025K, 0x00}, {CSRA66X0_FILTER_SEL_16K, 0x00}, {CSRA66X0_FILTER_SEL_22P05K, 0x00}, {CSRA66X0_FILTER_SEL_32K, 0x00}, {CSRA66X0_FILTER_SEL_44P1K_48K, 0x00}, {CSRA66X0_FILTER_SEL_88P2K_96K, 0x00}, {CSRA66X0_FILTER_SEL_176P4K_192K, 0x00}, /* RESERVED */ {CSRA66X0_USER_DSP_CTRL, 0x00}, {CSRA66X0_TEST_TONE_CTRL, 0x00}, {CSRA66X0_TEST_TONE_FREQ_0, 0x00}, {CSRA66X0_TEST_TONE_FREQ_1, 0x00}, {CSRA66X0_TEST_TONE_FREQ_2, 0x00}, {CSRA66X0_AUDIO_RATE_CTRL_FA, 0x08}, {CSRA66X0_MODULATION_INDEX_CTRL, 0x3F}, {CSRA66X0_MODULATION_INDEX_COUNT, 0x10}, {CSRA66X0_MIN_MODULATION_PULSE_WIDTH, 0x7A}, {CSRA66X0_DEAD_TIME_CTRL, 0x00}, {CSRA66X0_DEAD_TIME_THRESHOLD_0, 0xE7}, {CSRA66X0_DEAD_TIME_THRESHOLD_1, 0x26}, {CSRA66X0_DEAD_TIME_THRESHOLD_2, 0x40}, {CSRA66X0_CH1_LOW_SIDE_DLY, 0x00}, {CSRA66X0_CH2_LOW_SIDE_DLY, 0x00}, {CSRA66X0_SPECTRUM_CTRL, 0x00}, /* RESERVED */ {CSRA66X0_SPECTRUM_SPREAD_CTRL, 0x0C}, /* RESERVED */ {CSRA66X0_EXT_PA_PROTECT_POLARITY, 0x03}, {CSRA66X0_TEMP0_BACKOFF_COMP_VALUE, 0x98}, {CSRA66X0_TEMP0_SHUTDOWN_COMP_VALUE, 0xA3}, {CSRA66X0_TEMP1_BACKOFF_COMP_VALUE, 0x98}, {CSRA66X0_TEMP1_SHUTDOWN_COMP_VALUE, 0xA3}, {CSRA66X0_TEMP_PROT_BACKOFF, 0x00}, {CSRA66X0_TEMP_READ0_FA, 0x00}, {CSRA66X0_TEMP_READ1_FA, 0x00}, {CSRA66X0_CHIP_STATE_CTRL_FA, 0x02}, /* RESERVED */ {CSRA66X0_PWM_OUTPUT_CONFIG, 0x00}, {CSRA66X0_MISC_CONTROL_STATUS_0, 0x08}, {CSRA66X0_MISC_CONTROL_STATUS_1_FA, 0x40}, {CSRA66X0_PIO0_SELECT, 0x00}, {CSRA66X0_PIO1_SELECT, 0x00}, {CSRA66X0_PIO2_SELECT, 0x00}, {CSRA66X0_PIO3_SELECT, 0x00}, {CSRA66X0_PIO4_SELECT, 0x00}, {CSRA66X0_PIO5_SELECT, 0x00}, {CSRA66X0_PIO6_SELECT, 0x00}, {CSRA66X0_PIO7_SELECT, 0x00}, {CSRA66X0_PIO8_SELECT, 0x00}, {CSRA66X0_PIO_DIRN0, 0xFF}, {CSRA66X0_PIO_DIRN1, 0x01}, {CSRA66X0_PIO_PULL_EN0, 0xFF}, {CSRA66X0_PIO_PULL_EN1, 0x01}, {CSRA66X0_PIO_PULL_DIR0, 0x00}, {CSRA66X0_PIO_PULL_DIR1, 0x00}, {CSRA66X0_PIO_DRIVE_OUT0_FA, 0x00}, {CSRA66X0_PIO_DRIVE_OUT1_FA, 0x00}, {CSRA66X0_PIO_STATUS_IN0_FA, 0x00}, {CSRA66X0_PIO_STATUS_IN1_FA, 0x00}, /* RESERVED */ {CSRA66X0_IRQ_OUTPUT_ENABLE, 0x00}, {CSRA66X0_IRQ_OUTPUT_POLARITY, 0x01}, {CSRA66X0_IRQ_OUTPUT_STATUS_FA, 0x00}, {CSRA66X0_CLIP_DCA_STATUS_FA, 0x00}, {CSRA66X0_CHIP_STATE_STATUS_FA, 0x02}, {CSRA66X0_FAULT_STATUS_FA, 0x00}, {CSRA66X0_OTP_STATUS_FA, 0x00}, {CSRA66X0_AUDIO_IF_STATUS_FA, 0x00}, /* RESERVED */ {CSRA66X0_DSP_SATURATION_STATUS_FA, 0x00}, {CSRA66X0_AUDIO_RATE_STATUS_FA, 0x00}, /* RESERVED */ {CSRA66X0_DISABLE_PWM_OUTPUT, 0x00}, /* RESERVED */ {CSRA66X0_OTP_VER_FA, 0x03}, {CSRA66X0_RAM_VER_FA, 0x02}, /* RESERVED */ {CSRA66X0_AUDIO_SATURATION_FLAGS_FA, 0x00}, {CSRA66X0_DCOFFSET_CHAN_1_01_FA, 0x00}, {CSRA66X0_DCOFFSET_CHAN_1_02_FA, 0x00}, {CSRA66X0_DCOFFSET_CHAN_1_03_FA, 0x00}, {CSRA66X0_DCOFFSET_CHAN_2_01_FA, 0x00}, {CSRA66X0_DCOFFSET_CHAN_2_02_FA, 0x00}, {CSRA66X0_DCOFFSET_CHAN_2_03_FA, 0x00}, {CSRA66X0_FORCED_PA_SWITCHING_CTRL, 0x90}, {CSRA66X0_PA_FORCE_PULSE_WIDTH, 0x07}, {CSRA66X0_PA_HIGH_MODULATION_CTRL_CH1, 0x00}, /* RESERVED */ {CSRA66X0_HIGH_MODULATION_THRESHOLD_LOW, 0xD4}, {CSRA66X0_HIGH_MODULATION_THRESHOLD_HIGH, 0x78}, /* RESERVED */ {CSRA66X0_PA_FREEZE_CTRL, 0x00}, {CSRA66X0_DCA_FREEZE_CTRL, 0x3C}, /* RESERVED */ }; static bool csra66x0_volatile_register(struct device *dev, unsigned int reg) { switch (reg) { case CSRA66X0_CHIP_ID_FA: case CSRA66X0_ROM_VER_FA: case CSRA66X0_CHIP_REV_0_FA: case CSRA66X0_CHIP_REV_1_FA: case CSRA66X0_TEMP_READ0_FA: case CSRA66X0_TEMP_READ1_FA: case CSRA66X0_MISC_CONTROL_STATUS_1_FA: case CSRA66X0_IRQ_OUTPUT_STATUS_FA: case CSRA66X0_CLIP_DCA_STATUS_FA: case CSRA66X0_CHIP_STATE_STATUS_FA: case CSRA66X0_FAULT_STATUS_FA: case CSRA66X0_OTP_STATUS_FA: case CSRA66X0_AUDIO_IF_STATUS_FA: case CSRA66X0_DSP_SATURATION_STATUS_FA: case CSRA66X0_AUDIO_RATE_STATUS_FA: return true; default: return false; } } static bool csra66x0_writeable_registers(struct device *dev, unsigned int reg) { if ((reg >= CSRA66X0_AUDIO_IF_RX_CONFIG1) && (reg <= CSRA66X0_MAX_REGISTER_ADDR)) return true; return false; } static bool csra66x0_readable_registers(struct device *dev, unsigned int reg) { if ((reg >= CSRA66X0_AUDIO_IF_RX_CONFIG1) && (reg <= CSRA66X0_MAX_REGISTER_ADDR)) return true; return false; } /* codec private data */ struct csra66x0_priv { struct regmap *regmap; struct snd_soc_component *component; int spk_volume_ch1; int spk_volume_ch2; int irq; int vreg_gpio; u32 irq_active_low; u32 in_cluster; u32 is_master; #if IS_ENABLED(CONFIG_DEBUG_FS) struct dentry *debugfs_dir; struct dentry *debugfs_file_wo; struct dentry *debugfs_file_ro; #endif /* CONFIG_DEBUG_FS */ }; struct csra66x0_cluster_device { struct csra66x0_priv *csra66x0_ptr; const char *csra66x0_prefix; }; struct csra66x0_cluster_device csra_clust_dev_tbl[] = { {NULL, "CSRA_12"}, {NULL, "CSRA_34"}, {NULL, "CSRA_56"}, {NULL, "CSRA_78"}, {NULL, "CSRA_9A"}, {NULL, "CSRA_BC"} }; #if IS_ENABLED(CONFIG_DEBUG_FS) static int debugfs_codec_open_op(struct inode *inode, struct file *file) { file->private_data = inode->i_private; return 0; } static int debugfs_get_parameters(char *buf, u32 *param1, int num_of_par) { char *token; int base, cnt; token = strsep(&buf, " "); for (cnt = 0; cnt < num_of_par; cnt++) { if (token) { if ((token[1] == 'x') || (token[1] == 'X')) base = 16; else base = 10; if (kstrtou32(token, base, ¶m1[cnt]) != 0) return -EINVAL; token = strsep(&buf, " "); } else { return -EINVAL; } } return 0; } static ssize_t debugfs_codec_write_op(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { struct csra66x0_priv *csra66x0 = (struct csra66x0_priv *) filp->private_data; struct snd_soc_component *component = csra66x0->component; char lbuf[32]; int rc; u32 param[2]; if (!filp || !ppos || !ubuf) return -EINVAL; if (cnt > sizeof(lbuf) - 1) return -EINVAL; rc = copy_from_user(lbuf, ubuf, cnt); if (rc) return -EFAULT; lbuf[cnt] = '\0'; rc = debugfs_get_parameters(lbuf, param, 2); if ((param[0] < CSRA66X0_AUDIO_IF_RX_CONFIG1) || (param[0] > CSRA66X0_MAX_REGISTER_ADDR)) { dev_err(component->dev, "%s: register address 0x%04X out of range\n", __func__, param[0]); return -EINVAL; } if ((param[1] < 0) || (param[1] > 255)) { dev_err(component->dev, "%s: register data 0x%02X out of range\n", __func__, param[1]); return -EINVAL; } if (rc == 0) { rc = cnt; dev_info(component->dev, "%s: reg[0x%04X]=0x%02X\n", __func__, param[0], param[1]); snd_soc_component_write(component, param[0], param[1]); } else { dev_err(component->dev, "%s: write to register addr=0x%04X failed\n", __func__, param[0]); } return rc; } static ssize_t debugfs_csra66x0_reg_show(struct snd_soc_component *component, char __user *ubuf, size_t count, loff_t *ppos) { int i, reg_val, len; ssize_t total = 0; char tmp_buf[20]; if (!ubuf || !ppos || !component || *ppos < 0) return -EINVAL; for (i = ((int) *ppos + CSRA66X0_BASE); i <= CSRA66X0_MAX_REGISTER_ADDR; i++) { reg_val = snd_soc_component_read32(component, i); len = snprintf(tmp_buf, 20, "0x%04X: 0x%02X\n", i, (reg_val & 0xFF)); if ((total + len) >= count - 1) break; if (copy_to_user((ubuf + total), tmp_buf, len)) { dev_err(component->dev, "%s: fail to copy reg dump\n", __func__); total = -EFAULT; goto copy_err; } *ppos += len; total += len; } copy_err: return total; } static ssize_t debugfs_codec_read_op(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) { struct csra66x0_priv *csra66x0 = (struct csra66x0_priv *) filp->private_data; struct snd_soc_component *component = csra66x0->component; ssize_t ret_cnt; if (!filp || !ppos || !ubuf || *ppos < 0) return -EINVAL; ret_cnt = debugfs_csra66x0_reg_show(component, ubuf, cnt, ppos); return ret_cnt; } static const struct file_operations debugfs_codec_ops = { .open = debugfs_codec_open_op, .write = debugfs_codec_write_op, .read = debugfs_codec_read_op, }; #endif /* CONFIG_DEBUG_FS */ /* * CSRA66X0 Controls */ static const DECLARE_TLV_DB_SCALE(csra66x0_volume_tlv, -9000, 25, 0); static const DECLARE_TLV_DB_RANGE(csra66x0_bass_treble_tlv, 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), 1, 15, TLV_DB_SCALE_ITEM(-1500, 100, 0), 16, 30, TLV_DB_SCALE_ITEM(100, 100, 0) ); static int csra66x0_get_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); unsigned int reg_l = mc->reg; unsigned int reg_r = mc->rreg; unsigned int val_l, val_r; val_l = (snd_soc_component_read32(component, reg_l) & 0xff) | ((snd_soc_component_read32(component, CSRA66X0_CH1_VOLUME_1_FA) & (0x01)) << 8); val_r = (snd_soc_component_read32(component, reg_r) & 0xff) | ((snd_soc_component_read32(component, CSRA66X0_CH2_VOLUME_1_FA) & (0x01)) << 8); ucontrol->value.integer.value[0] = val_l; ucontrol->value.integer.value[1] = val_r; return 0; } static int csra66x0_set_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct csra66x0_priv *csra66x0 = snd_soc_component_get_drvdata(component); unsigned int reg_l = mc->reg; unsigned int reg_r = mc->rreg; unsigned int val_l[2]; unsigned int val_r[2]; csra66x0->spk_volume_ch1 = (ucontrol->value.integer.value[0]); csra66x0->spk_volume_ch2 = (ucontrol->value.integer.value[1]); val_l[0] = csra66x0->spk_volume_ch1 & SPK_VOLUME_LSB_MSK; val_l[1] = (csra66x0->spk_volume_ch1 & SPK_VOLUME_MSB_MSK) ? 1 : 0; val_r[0] = csra66x0->spk_volume_ch2 & SPK_VOLUME_LSB_MSK; val_r[1] = (csra66x0->spk_volume_ch2 & SPK_VOLUME_MSB_MSK) ? 1 : 0; snd_soc_component_write(component, reg_l, val_l[0]); snd_soc_component_write(component, reg_r, val_r[0]); snd_soc_component_write(component, CSRA66X0_CH1_VOLUME_1_FA, val_l[1]); snd_soc_component_write(component, CSRA66X0_CH2_VOLUME_1_FA, val_r[1]); return 0; } /* enumerated controls */ static const char * const csra66x0_mute_output_text[] = {"PLAY", "MUTE"}; static const char * const csra66x0_output_invert_text[] = { "UNCHANGED", "INVERTED"}; static const char * const csra66x0_deemp_config_text[] = { "DISABLED", "ENABLED"}; SOC_ENUM_SINGLE_DECL(csra66x0_mute_output_enum, CSRA66X0_MISC_CONTROL_STATUS_1_FA, 2, csra66x0_mute_output_text); SOC_ENUM_SINGLE_DECL(csra66x0_ch1_output_invert_enum, CSRA66X0_CH1_OUTPUT_INVERT_EN, 0, csra66x0_output_invert_text); SOC_ENUM_SINGLE_DECL(csra66x0_ch2_output_invert_enum, CSRA66X0_CH2_OUTPUT_INVERT_EN, 0, csra66x0_output_invert_text); SOC_ENUM_DOUBLE_DECL(csra66x0_deemp_config_enum, CSRA66X0_DEEMP_CONFIG_FA, 0, 1, csra66x0_deemp_config_text); static const struct snd_kcontrol_new csra66x0_snd_controls[] = { /* volume */ SOC_DOUBLE_R_EXT_TLV("PA VOLUME", CSRA66X0_CH1_VOLUME_0_FA, CSRA66X0_CH2_VOLUME_0_FA, 0, 0x1C9, 0, csra66x0_get_volume, csra66x0_set_volume, csra66x0_volume_tlv), /* bass treble */ SOC_DOUBLE_R_TLV("PA BASS GAIN", CSRA66X0_CH1_BASS_GAIN_CTRL_FA, CSRA66X0_CH2_BASS_GAIN_CTRL_FA, 0, 0x1E, 0, csra66x0_bass_treble_tlv), SOC_DOUBLE_R_TLV("PA TREBLE GAIN", CSRA66X0_CH1_TREBLE_GAIN_CTRL_FA, CSRA66X0_CH2_TREBLE_GAIN_CTRL_FA, 0, 0x1E, 0, csra66x0_bass_treble_tlv), SOC_DOUBLE_R("PA BASS_XOVER FREQ", CSRA66X0_CH1_BASS_FC_CTRL_FA, CSRA66X0_CH2_BASS_FC_CTRL_FA, 0, 2, 0), SOC_DOUBLE_R("PA TREBLE_XOVER FREQ", CSRA66X0_CH1_TREBLE_FC_CTRL_FA, CSRA66X0_CH2_TREBLE_FC_CTRL_FA, 0, 2, 0), /* switch */ SOC_ENUM("PA MUTE_OUTPUT SWITCH", csra66x0_mute_output_enum), SOC_ENUM("PA DE-EMPHASIS SWITCH", csra66x0_deemp_config_enum), }; static const struct snd_kcontrol_new csra_mix_switch[] = { SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) }; static const struct snd_soc_dapm_widget csra66x0_dapm_widgets[] = { SND_SOC_DAPM_INPUT("IN"), SND_SOC_DAPM_MIXER("MIXER", SND_SOC_NOPM, 0, 0, csra_mix_switch, ARRAY_SIZE(csra_mix_switch)), SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_PGA("PGA", CSRA66X0_CHIP_STATE_CTRL_FA, 0, 0, NULL, 0), SND_SOC_DAPM_OUTPUT("SPKR"), SND_SOC_DAPM_SUPPLY("POWER", CSRA66X0_CHIP_STATE_CTRL_FA, 1, 1, NULL, 0), }; static const struct snd_soc_dapm_route csra66x0_dapm_routes[] = { {"MIXER", "Switch", "IN"}, {"DAC", NULL, "MIXER"}, {"PGA", NULL, "DAC"}, {"SPKR", NULL, "PGA"}, {"SPKR", NULL, "POWER"}, }; static int csra66x0_init(struct csra66x0_priv *csra66x0) { struct snd_soc_component *component = csra66x0->component; dev_dbg(component->dev, "%s: initialize %s\n", __func__, component->name); /* config */ snd_soc_component_write(component, CSRA66X0_CHIP_STATE_CTRL_FA, CONFIG_STATE); /* settle time in HW is min. 500ms before proceeding */ msleep(500); /* setup */ snd_soc_component_write(component, CSRA66X0_MISC_CONTROL_STATUS_0, 0x09); snd_soc_component_write(component, CSRA66X0_TEMP_PROT_BACKOFF, 0x0C); snd_soc_component_write(component, CSRA66X0_EXT_PA_PROTECT_POLARITY, 0x03); snd_soc_component_write(component, CSRA66X0_PWM_OUTPUT_CONFIG, 0xC8); csra66x0->spk_volume_ch1 = SPK_VOLUME_M20DB; csra66x0->spk_volume_ch2 = SPK_VOLUME_M20DB; snd_soc_component_write(component, CSRA66X0_CH1_VOLUME_0_FA, SPK_VOLUME_M20DB_LSB); snd_soc_component_write(component, CSRA66X0_CH2_VOLUME_0_FA, SPK_VOLUME_M20DB_LSB); snd_soc_component_write(component, CSRA66X0_CH1_VOLUME_1_FA, SPK_VOLUME_M20DB_MSB); snd_soc_component_write(component, CSRA66X0_CH2_VOLUME_1_FA, SPK_VOLUME_M20DB_MSB); snd_soc_component_write(component, CSRA66X0_DEAD_TIME_CTRL, 0x0); snd_soc_component_write(component, CSRA66X0_DEAD_TIME_THRESHOLD_0, 0xE7); snd_soc_component_write(component, CSRA66X0_DEAD_TIME_THRESHOLD_1, 0x26); snd_soc_component_write(component, CSRA66X0_DEAD_TIME_THRESHOLD_2, 0x40); snd_soc_component_write(component, CSRA66X0_MIN_MODULATION_PULSE_WIDTH, 0x7A); snd_soc_component_write(component, CSRA66X0_CH1_HARD_CLIP_THRESH, 0x00); snd_soc_component_write(component, CSRA66X0_CH2_HARD_CLIP_THRESH, 0x00); snd_soc_component_write(component, CSRA66X0_CH1_DCA_THRESH, 0x40); snd_soc_component_write(component, CSRA66X0_CH2_DCA_THRESH, 0x40); snd_soc_component_write(component, CSRA66X0_DCA_ATTACK_RATE, 0x00); snd_soc_component_write(component, CSRA66X0_DCA_RELEASE_RATE, 0x00); if (csra66x0->irq) { snd_soc_component_write(component, CSRA66X0_PIO0_SELECT, 0x1); if (csra66x0->irq_active_low) snd_soc_component_write(component, CSRA66X0_IRQ_OUTPUT_POLARITY, 0x0); else snd_soc_component_write(component, CSRA66X0_IRQ_OUTPUT_POLARITY, 0x1); snd_soc_component_write(component, CSRA66X0_IRQ_OUTPUT_ENABLE, 0x01); } else { snd_soc_component_write(component, CSRA66X0_IRQ_OUTPUT_ENABLE, 0x00); } /* settle time in HW is min. 500ms before slave initializing */ msleep(500); return 0; } static int csra66x0_reset(struct csra66x0_priv *csra66x0) { struct snd_soc_component *component = csra66x0->component; u16 val; val = snd_soc_component_read32(component, CSRA66X0_FAULT_STATUS_FA); if (val & FAULT_STATUS_INTERNAL) dev_dbg(component->dev, "%s: FAULT_STATUS_INTERNAL 0x%X\n", __func__, val); if (val & FAULT_STATUS_OTP_INTEGRITY) dev_dbg(component->dev, "%s: FAULT_STATUS_OTP_INTEGRITY 0x%X\n", __func__, val); if (val & FAULT_STATUS_PADS2) dev_dbg(component->dev, "%s: FAULT_STATUS_PADS2 0x%X\n", __func__, val); if (val & FAULT_STATUS_SMPS) dev_dbg(component->dev, "%s: FAULT_STATUS_SMPS 0x%X\n", __func__, val); if (val & FAULT_STATUS_TEMP) dev_dbg(component->dev, "%s: FAULT_STATUS_TEMP 0x%X\n", __func__, val); if (val & FAULT_STATUS_PROTECT) dev_dbg(component->dev, "%s: FAULT_STATUS_PROTECT 0x%X\n", __func__, val); dev_dbg(component->dev, "%s: reset %s\n", __func__, component->name); /* clear fault state and re-init */ snd_soc_component_write(component, CSRA66X0_FAULT_STATUS_FA, 0x00); snd_soc_component_write(component, CSRA66X0_IRQ_OUTPUT_STATUS_FA, 0x00); /* apply reset to CSRA66X0 */ val = snd_soc_component_read32(component, CSRA66X0_MISC_CONTROL_STATUS_1_FA); snd_soc_component_write(component, CSRA66X0_MISC_CONTROL_STATUS_1_FA, val | 0x08); /* wait 500ms after reset to recover CSRA66X0 */ msleep(500); return 0; } static int csra66x0_msconfig(struct csra66x0_priv *csra66x0) { struct snd_soc_component *component = csra66x0->component; dev_dbg(component->dev, "%s: configure %s\n", __func__, component->name); /* config */ snd_soc_component_write(component, CSRA66X0_CHIP_STATE_CTRL_FA, CONFIG_STATE); /* settle time in HW is min. 500ms before proceeding */ msleep(500); snd_soc_component_write(component, CSRA66X0_PIO7_SELECT, 0x04); snd_soc_component_write(component, CSRA66X0_PIO8_SELECT, 0x04); if (csra66x0->is_master) { /* Master specific config */ snd_soc_component_write(component, CSRA66X0_PIO_PULL_EN0, 0xFF); snd_soc_component_write(component, CSRA66X0_PIO_PULL_DIR0, 0x80); snd_soc_component_write(component, CSRA66X0_PIO_PULL_EN1, 0x01); snd_soc_component_write(component, CSRA66X0_PIO_PULL_DIR1, 0x01); } else { /* Slave specific config */ snd_soc_component_write(component, CSRA66X0_PIO_PULL_EN0, 0x7F); snd_soc_component_write(component, CSRA66X0_PIO_PULL_EN1, 0x00); } snd_soc_component_write(component, CSRA66X0_DCA_CTRL, 0x05); return 0; } static int csra66x0_soc_probe(struct snd_soc_component *component) { struct csra66x0_priv *csra66x0 = snd_soc_component_get_drvdata(component); struct snd_soc_dapm_context *dapm; char name[50]; unsigned int i, max_num_cluster_devices; csra66x0->component = component; if (csra66x0->in_cluster) { dapm = snd_soc_component_get_dapm(component); dev_dbg(component->dev, "%s: assign prefix %s to codec device %s\n", __func__, component->name_prefix, component->name); /* add device to cluster table */ max_num_cluster_devices = sizeof(csra_clust_dev_tbl)/ sizeof(csra_clust_dev_tbl[0]); for (i = 0; i < max_num_cluster_devices; i++) { if (!strncmp(component->name_prefix, csra_clust_dev_tbl[i].csra66x0_prefix, strlen( csra_clust_dev_tbl[i].csra66x0_prefix))) { csra_clust_dev_tbl[i].csra66x0_ptr = csra66x0; break; } if (i == max_num_cluster_devices-1) dev_warn(component->dev, "%s: Unknown prefix %s of cluster device %s\n", __func__, component->name_prefix, component->name); } /* master slave config */ csra66x0_msconfig(csra66x0); if (dapm->component) { strlcpy(name, dapm->component->name_prefix, sizeof(name)); strlcat(name, " IN", sizeof(name)); snd_soc_dapm_ignore_suspend(dapm, name); strlcpy(name, dapm->component->name_prefix, sizeof(name)); strlcat(name, " SPKR", sizeof(name)); snd_soc_dapm_ignore_suspend(dapm, name); } } /* common initialization */ csra66x0_init(csra66x0); return 0; } static void csra66x0_soc_remove(struct snd_soc_component *component) { snd_soc_component_write(component, CSRA66X0_CHIP_STATE_CTRL_FA, STDBY_STATE); return; } static int csra66x0_soc_suspend(struct snd_soc_component *component) { u16 state_reg = snd_soc_component_read32(component, CSRA66X0_CHIP_STATE_CTRL_FA) & 0xFC; snd_soc_component_write(component, CSRA66X0_CHIP_STATE_CTRL_FA, state_reg | STDBY_STATE); return 0; } static int csra66x0_soc_resume(struct snd_soc_component *component) { u16 state_reg = snd_soc_component_read32( component, CSRA66X0_CHIP_STATE_CTRL_FA) & 0xFC; snd_soc_component_write(component, CSRA66X0_CHIP_STATE_CTRL_FA, state_reg | RUN_STATE); return 0; } static const struct snd_soc_component_driver soc_codec_drv_csra66x0 = { .name = DRV_NAME, .probe = csra66x0_soc_probe, .remove = csra66x0_soc_remove, .suspend = csra66x0_soc_suspend, .resume = csra66x0_soc_resume, .controls = csra66x0_snd_controls, .num_controls = ARRAY_SIZE(csra66x0_snd_controls), .dapm_widgets = csra66x0_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(csra66x0_dapm_widgets), .dapm_routes = csra66x0_dapm_routes, .num_dapm_routes = ARRAY_SIZE(csra66x0_dapm_routes), }; static struct regmap_config csra66x0_regmap_config = { .reg_bits = 16, .val_bits = 8, .cache_type = REGCACHE_RBTREE, .reg_defaults = csra66x0_reg_defaults, .num_reg_defaults = ARRAY_SIZE(csra66x0_reg_defaults), .max_register = CSRA66X0_MAX_REGISTER_ADDR, .volatile_reg = csra66x0_volatile_register, .writeable_reg = csra66x0_writeable_registers, .readable_reg = csra66x0_readable_registers, }; static irqreturn_t csra66x0_irq(int irq, void *data) { struct csra66x0_priv *csra66x0 = (struct csra66x0_priv *) data; struct snd_soc_component *component = csra66x0->component; u16 val; unsigned int i, max_num_cluster_devices; /* Treat interrupt before component is initialized as spurious */ if (component == NULL) return IRQ_NONE; dev_dbg(component->dev, "%s: csra66x0_interrupt triggered by %s\n", __func__, component->name); /* fault indication */ val = snd_soc_component_read32(component, CSRA66X0_IRQ_OUTPUT_STATUS_FA) & 0x1; if (!val) return IRQ_HANDLED; if (csra66x0->in_cluster) { /* reset all slave codecs */ max_num_cluster_devices = sizeof(csra_clust_dev_tbl) / sizeof(csra_clust_dev_tbl[0]); for (i = 0; i < max_num_cluster_devices; i++) { if (i >= component->card->num_aux_devs) break; if (csra_clust_dev_tbl[i].csra66x0_ptr == NULL) continue; if (csra_clust_dev_tbl[i].csra66x0_ptr->is_master) continue; csra66x0_reset(csra_clust_dev_tbl[i].csra66x0_ptr); } /* reset all master codecs */ for (i = 0; i < max_num_cluster_devices; i++) { if (i >= component->card->num_aux_devs) break; if (csra_clust_dev_tbl[i].csra66x0_ptr == NULL) continue; if (csra_clust_dev_tbl[i].csra66x0_ptr->is_master) csra66x0_reset( csra_clust_dev_tbl[i].csra66x0_ptr); } /* recover all codecs */ for (i = 0; i < max_num_cluster_devices; i++) { if (i >= component->card->num_aux_devs) break; if (csra_clust_dev_tbl[i].csra66x0_ptr == NULL) continue; csra66x0_msconfig(csra_clust_dev_tbl[i].csra66x0_ptr); csra66x0_init(csra_clust_dev_tbl[i].csra66x0_ptr); } } else { csra66x0_reset(csra66x0); csra66x0_init(csra66x0); } return IRQ_HANDLED; }; static const struct of_device_id csra66x0_of_match[] = { { .compatible = "qcom,csra66x0", }, { } }; MODULE_DEVICE_TABLE(of, csra66x0_of_match); #if IS_ENABLED(CONFIG_I2C) static int csra66x0_i2c_probe(struct i2c_client *client_i2c, const struct i2c_device_id *id) { struct csra66x0_priv *csra66x0; int ret, irq_trigger; char debugfs_dir_name[32]; csra66x0 = devm_kzalloc(&client_i2c->dev, sizeof(struct csra66x0_priv), GFP_KERNEL); if (csra66x0 == NULL) return -ENOMEM; csra66x0->regmap = devm_regmap_init_i2c(client_i2c, &csra66x0_regmap_config); if (IS_ERR(csra66x0->regmap)) { ret = PTR_ERR(csra66x0->regmap); dev_err(&client_i2c->dev, "%s %d: Failed to allocate register map for I2C device: %d\n", __func__, __LINE__, ret); return ret; } i2c_set_clientdata(client_i2c, csra66x0); /* get data from device tree */ if (client_i2c->dev.of_node) { /* cluster of multiple devices */ ret = of_property_read_u32( client_i2c->dev.of_node, "qcom,csra-cluster", &csra66x0->in_cluster); if (ret) { dev_info(&client_i2c->dev, "%s: qcom,csra-cluster property not defined in DT\n", __func__); csra66x0->in_cluster = 0; } /* master or slave device */ ret = of_property_read_u32( client_i2c->dev.of_node, "qcom,csra-cluster-master", &csra66x0->is_master); if (ret) { dev_info(&client_i2c->dev, "%s: qcom,csra-cluster-master property not defined in DT, slave assumed\n", __func__); csra66x0->is_master = 0; } /* gpio setup for vreg */ csra66x0->vreg_gpio = of_get_named_gpio(client_i2c->dev.of_node, "qcom,csra-vreg-en-gpio", 0); if (!gpio_is_valid(csra66x0->vreg_gpio)) { dev_err(&client_i2c->dev, "%s: %s property is not found %d\n", __func__, "qcom,csra-vreg-en-gpio", csra66x0->vreg_gpio); return -ENODEV; } dev_dbg(&client_i2c->dev, "%s: vreg_en gpio %d\n", __func__, csra66x0->vreg_gpio); ret = gpio_request(csra66x0->vreg_gpio, dev_name(&client_i2c->dev)); if (ret) { if (ret == -EBUSY) { /* GPIO was already requested */ dev_dbg(&client_i2c->dev, "%s: gpio %d is already set\n", __func__, csra66x0->vreg_gpio); } else { dev_err(&client_i2c->dev, "%s: Failed to request gpio %d, err: %d\n", __func__, csra66x0->vreg_gpio, ret); } } else { gpio_direction_output(csra66x0->vreg_gpio, 1); gpio_set_value(csra66x0->vreg_gpio, 0); } /* register interrupt handle */ if (client_i2c->irq) { csra66x0->irq = client_i2c->irq; /* interrupt polarity */ ret = of_property_read_u32( client_i2c->dev.of_node, "irq-active-low", &csra66x0->irq_active_low); if (ret) { dev_info(&client_i2c->dev, "%s: irq-active-low property not defined in DT\n", __func__); csra66x0->irq_active_low = 0; } if (csra66x0->irq_active_low) irq_trigger = IRQF_TRIGGER_LOW; else irq_trigger = IRQF_TRIGGER_HIGH; ret = devm_request_threaded_irq(&client_i2c->dev, csra66x0->irq, NULL, csra66x0_irq, irq_trigger | IRQF_ONESHOT, "csra66x0_irq", csra66x0); if (ret) { dev_err(&client_i2c->dev, "%s: Failed to request IRQ %d: %d\n", __func__, csra66x0->irq, ret); csra66x0->irq = 0; } } } #if IS_ENABLED(CONFIG_DEBUG_FS) /* debugfs interface */ snprintf(debugfs_dir_name, sizeof(debugfs_dir_name), "%s-%s", client_i2c->name, dev_name(&client_i2c->dev)); csra66x0->debugfs_dir = debugfs_create_dir(debugfs_dir_name, NULL); if (!csra66x0->debugfs_dir) { dev_dbg(&client_i2c->dev, "%s: Failed to create /sys/kernel/debug/%s for debugfs\n", __func__, debugfs_dir_name); return -ENOMEM; } csra66x0->debugfs_file_wo = debugfs_create_file( "write_reg_val", S_IFREG | S_IRUGO, csra66x0->debugfs_dir, (void *) csra66x0, &debugfs_codec_ops); if (!csra66x0->debugfs_file_wo) { dev_dbg(&client_i2c->dev, "%s: Failed to create /sys/kernel/debug/%s/write_reg_val\n", __func__, debugfs_dir_name); return -ENOMEM; } csra66x0->debugfs_file_ro = debugfs_create_file( "show_reg_dump", S_IFREG | S_IRUGO, csra66x0->debugfs_dir, (void *) csra66x0, &debugfs_codec_ops); if (!csra66x0->debugfs_file_ro) { dev_dbg(&client_i2c->dev, "%s: Failed to create /sys/kernel/debug/%s/show_reg_dump\n", __func__, debugfs_dir_name); return -ENOMEM; } #endif /* CONFIG_DEBUG_FS */ /* register codec */ ret = snd_soc_register_component(&client_i2c->dev, &soc_codec_drv_csra66x0, NULL, 0); if (ret != 0) { dev_err(&client_i2c->dev, "%s %d: Failed to register codec: %d\n", __func__, __LINE__, ret); if (gpio_is_valid(csra66x0->vreg_gpio)) { gpio_set_value(csra66x0->vreg_gpio, 0); gpio_free(csra66x0->vreg_gpio); } return ret; } return 0; } static int csra66x0_i2c_remove(struct i2c_client *i2c_client) { struct csra66x0_priv *csra66x0 = i2c_get_clientdata(i2c_client); if (csra66x0) { if (gpio_is_valid(csra66x0->vreg_gpio)) { gpio_set_value(csra66x0->vreg_gpio, 0); gpio_free(csra66x0->vreg_gpio); } #if IS_ENABLED(CONFIG_DEBUG_FS) debugfs_remove_recursive(csra66x0->debugfs_dir); #endif } snd_soc_unregister_component(&i2c_client->dev); return 0; } static const struct i2c_device_id csra66x0_i2c_id[] = { { "csra66x0", 0}, { } }; MODULE_DEVICE_TABLE(i2c, csra66x0_i2c_id); static struct i2c_driver csra66x0_i2c_driver = { .probe = csra66x0_i2c_probe, .remove = csra66x0_i2c_remove, .id_table = csra66x0_i2c_id, .driver = { .name = "csra66x0", .owner = THIS_MODULE, .of_match_table = csra66x0_of_match }, }; #endif static int __init csra66x0_codec_init(void) { int ret = 0; #if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&csra66x0_i2c_driver); if (ret != 0) pr_err("%s: Failed to register CSRA66X0 I2C driver, ret = %d\n", __func__, ret); #endif return ret; } module_init(csra66x0_codec_init); static void __exit csra66x0_codec_exit(void) { #if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&csra66x0_i2c_driver); #endif } module_exit(csra66x0_codec_exit); MODULE_DESCRIPTION("CSRA66X0 Codec driver"); MODULE_LICENSE("GPL v2");