| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * Copyright (c) 2021 Amlogic, Inc. All rights reserved. |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/moduleparam.h> |
| #include <linux/slab.h> |
| #include <linux/of.h> |
| #include <linux/init.h> |
| #include <linux/i2c.h> |
| #include <linux/regmap.h> |
| |
| #include <sound/initval.h> |
| #include <sound/core.h> |
| #include <sound/pcm.h> |
| #include <sound/pcm_params.h> |
| #include <sound/soc.h> |
| #include <sound/tlv.h> |
| |
| #include <linux/amlogic/aml_gpio_consumer.h> |
| #include "aml_codec_pa1.h" |
| |
| #define PA1_DRV_NAME "pa1_acodec" |
| #define PA1_RATES (SNDRV_PCM_RATE_8000 | \ |
| SNDRV_PCM_RATE_11025 | \ |
| SNDRV_PCM_RATE_16000 | \ |
| SNDRV_PCM_RATE_22050 | \ |
| SNDRV_PCM_RATE_32000 | \ |
| SNDRV_PCM_RATE_44100 | \ |
| SNDRV_PCM_RATE_48000 | \ |
| SNDRV_PCM_RATE_64000 | \ |
| SNDRV_PCM_RATE_88200 | \ |
| SNDRV_PCM_RATE_96000 | \ |
| SNDRV_PCM_RATE_176400 | \ |
| SNDRV_PCM_RATE_192000) |
| #define PA1_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ |
| SNDRV_PCM_FMTBIT_S20_3LE |\ |
| SNDRV_PCM_FMTBIT_S24_LE | \ |
| SNDRV_PCM_FMTBIT_S32_LE) |
| |
| /* When multiple Codecs share a PDN, this flag is used to record the PDN status */ |
| static bool reset_pin_setting; |
| |
| struct pa1_acodec_priv { |
| struct regmap *regmap; |
| struct pa1_acodec_platform_data *pdata; |
| int mute; |
| unsigned int vol; |
| unsigned int dsp_status; |
| struct snd_soc_component *component; |
| unsigned int pa1_page; |
| }; |
| |
| const struct regmap_config pa1_acodec_regmap = { |
| .reg_bits = 8, |
| .val_bits = 32, |
| .reg_stride = 1, |
| .cache_type = REGCACHE_NONE, |
| .reg_format_endian = REGMAP_ENDIAN_LITTLE, |
| .val_format_endian = REGMAP_ENDIAN_LITTLE, |
| }; |
| |
| static const DECLARE_TLV_DB_SCALE(pa1_vol_tlv, -12276, 12, 1); |
| |
| static int top_conversion_to_dsp(struct snd_soc_component *component) |
| { |
| struct pa1_acodec_priv *pa1_acodec = snd_soc_component_get_drvdata(component); |
| |
| if (pa1_acodec->pa1_page == 0) { |
| snd_soc_component_write(component, PA1_PAGE_NUM, 0x01); |
| pa1_acodec->pa1_page = 1; |
| } |
| |
| return 0; |
| } |
| |
| static int dsp_conversion_to_top(struct snd_soc_component *component) |
| { |
| struct pa1_acodec_priv *pa1_acodec = snd_soc_component_get_drvdata(component); |
| |
| if (pa1_acodec->pa1_page == 1) { |
| snd_soc_component_write(component, PA1_PAGE_NUM, 0x0); |
| pa1_acodec->pa1_page = 0; |
| } |
| |
| return 0; |
| } |
| |
| static int pa1_acodec_mute(struct snd_soc_component *component, int mute) |
| { |
| unsigned int pa1_mute_ctrl_value = 0; |
| |
| if (mute) { |
| /* mute master */ |
| pa1_mute_ctrl_value = 0x4; |
| } else { |
| /* unmute */ |
| pa1_mute_ctrl_value = 0x0; |
| } |
| |
| top_conversion_to_dsp(component); |
| snd_soc_component_write(component, PA1_AED_MUTE_CTRL, pa1_mute_ctrl_value); |
| |
| return 0; |
| } |
| |
| static int pa1_mixer_aed_read(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
| struct soc_mixer_control *mc = |
| (struct soc_mixer_control *)kcontrol->private_value; |
| unsigned int reg = mc->reg; |
| unsigned int shift = mc->shift; |
| unsigned int max = mc->max; |
| unsigned int invert = mc->invert; |
| unsigned int value = 0; |
| |
| top_conversion_to_dsp(component); |
| value = snd_soc_component_read(component, reg); |
| value = (value >> shift) & max; |
| if (invert) |
| value = (~value) & max; |
| ucontrol->value.integer.value[0] = value; |
| |
| return 0; |
| } |
| |
| static int pa1_mixer_aed_write(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
| struct soc_mixer_control *mc = |
| (struct soc_mixer_control *)kcontrol->private_value; |
| unsigned int reg = mc->reg; |
| unsigned int shift = mc->shift; |
| unsigned int max = mc->max; |
| unsigned int invert = mc->invert; |
| unsigned int value = ucontrol->value.integer.value[0]; |
| unsigned int new_val = 0; |
| |
| top_conversion_to_dsp(component); |
| new_val = snd_soc_component_read(component, reg); |
| if (invert) |
| value = (~value) & max; |
| max = ~(max << shift); |
| new_val &= max; |
| new_val |= (value << shift); |
| |
| snd_soc_component_write(component, reg, new_val); |
| |
| return 0; |
| } |
| |
| static int pa1_mixer_aed_get_mute(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
| struct pa1_acodec_priv *pa1_acodec = snd_soc_component_get_drvdata(component); |
| |
| ucontrol->value.integer.value[0] = pa1_acodec->mute; |
| |
| return 0; |
| } |
| |
| static int pa1_mixer_aed_set_mute(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
| struct pa1_acodec_priv *pa1_acodec = snd_soc_component_get_drvdata(component); |
| struct soc_mixer_control *mc = |
| (struct soc_mixer_control *)kcontrol->private_value; |
| unsigned int reg = mc->reg; |
| unsigned int shift = mc->shift; |
| unsigned int max = mc->max; |
| unsigned int new_val; |
| |
| top_conversion_to_dsp(component); |
| new_val = (unsigned int)snd_soc_component_read(component, reg); |
| |
| pa1_acodec->mute = ucontrol->value.integer.value[0]; |
| max = ~(max << shift); |
| new_val &= max; |
| new_val |= ((pa1_acodec->mute) << shift); |
| |
| snd_soc_component_write(component, reg, new_val); |
| |
| return 0; |
| } |
| |
| void pa1_get_mixer_gain_value(struct snd_soc_component *component, int addr, unsigned int *value) |
| { |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, (0x1 << 6) | (0x1 << 0), |
| (0x0 << 6) | (0x0 << 0)); |
| top_conversion_to_dsp(component); |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0xf); |
| |
| snd_soc_component_write(component, PA1_AEQ_COEF_ADDR, addr); |
| |
| *value = snd_soc_component_read(component, PA1_AEQ_COEF_DATA); |
| |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0x0); |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, 0x1 << 6, 0x1 << 6); |
| } |
| |
| void pa1_set_mixer_gain_value(struct snd_soc_component *component, int addr, |
| unsigned int value, bool ctrl_flag) |
| { |
| unsigned int another_val = 0; |
| |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, (0x1 << 6) | (0x1 << 0), |
| (0x0 << 6) | (0x0 << 0)); |
| top_conversion_to_dsp(component); |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0xf); |
| |
| if (ctrl_flag) |
| snd_soc_component_write(component, PA1_AEQ_COEF_ADDR, addr + 1); |
| else |
| snd_soc_component_write(component, PA1_AEQ_COEF_ADDR, addr - 1); |
| |
| another_val = snd_soc_component_read(component, PA1_AEQ_COEF_DATA); |
| |
| if (ctrl_flag) { |
| snd_soc_component_write(component, PA1_AEQ_COEF_ADDR, addr); |
| snd_soc_component_write(component, PA1_AEQ_COEF_DATA, value); |
| snd_soc_component_write(component, PA1_AEQ_COEF_DATA, another_val); |
| } else { |
| snd_soc_component_write(component, PA1_AEQ_COEF_ADDR, addr - 1); |
| snd_soc_component_write(component, PA1_AEQ_COEF_DATA, another_val); |
| snd_soc_component_write(component, PA1_AEQ_COEF_DATA, value); |
| } |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0x0); |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, 0x1 << 6, 0x1 << 6); |
| } |
| |
| static int pa1_mixer_get_gain(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
| struct soc_mixer_control *mc = |
| (struct soc_mixer_control *)kcontrol->private_value; |
| unsigned int reg = mc->reg; |
| unsigned int value = 0; |
| |
| pa1_get_mixer_gain_value(component, reg, &value); |
| |
| ucontrol->value.integer.value[0] = value; |
| |
| return 0; |
| } |
| |
| static int pa1_mixer_set_gain(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
| struct soc_mixer_control *mc = |
| (struct soc_mixer_control *)kcontrol->private_value; |
| unsigned int reg = mc->reg; |
| unsigned int invert = mc->invert; |
| unsigned int value = ucontrol->value.integer.value[0]; |
| |
| if (invert) |
| pa1_set_mixer_gain_value(component, reg, value, true); |
| else |
| pa1_set_mixer_gain_value(component, reg, value, false); |
| |
| return 0; |
| } |
| |
| static int pa1_mixer_set_level_meter_coeff(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
| unsigned int value = ucontrol->value.integer.value[0]; |
| |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, (0x1 << 6) | (0x1 << 0), |
| (0x0 << 6) | (0x0 << 0)); |
| top_conversion_to_dsp(component); |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0xf); |
| |
| snd_soc_component_write(component, PA1_AEQ_COEF_ADDR, PA1_LEVEL_METER_RAM_ADD); |
| snd_soc_component_write(component, PA1_AEQ_COEF_DATA, value); |
| snd_soc_component_write(component, PA1_AEQ_COEF_DATA, 0x0); |
| |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0x0); |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, 0x1 << 6, 0x1 << 6); |
| |
| return 0; |
| } |
| |
| static int pa1_str2int(char *str, unsigned int *data, int size) |
| { |
| int num = 0; |
| unsigned int temp = 0; |
| char *ptr = str; |
| unsigned int *val = data; |
| |
| while (size-- != 0) { |
| if ((*ptr >= '0') && (*ptr <= '9')) { |
| temp = temp * 16 + (*ptr - '0'); |
| } else if ((*ptr >= 'a') && (*ptr <= 'f')) { |
| temp = temp * 16 + (*ptr - 'a' + 10); |
| } else if (*ptr == ' ') { |
| *(val + num) = temp; |
| temp = 0; |
| num++; |
| } |
| ptr++; |
| } |
| |
| return num; |
| } |
| |
| void pa1_aed_set_volume(struct snd_soc_component *component, unsigned int master_vol, |
| unsigned int lch_vol, unsigned int rch_vol) |
| { |
| top_conversion_to_dsp(component); |
| snd_soc_component_write(component, PA1_AED_EQ_VOLUME_VAL, |
| (master_vol << 20) | /* master volume: 0dB */ |
| (lch_vol << 10) | /* channel 2 volume: 0dB */ |
| (rch_vol << 0) /* channel 1 volume: 0dB */ |
| ); |
| snd_soc_component_write(component, PA1_AED_EQ_VOLUME_STEP_CNT, |
| (0x2 << 30) | |
| /*volume step: 0x03:0.5dB, 0x02:0.5dB, 0x01:0.25dB, 0x00:0.125dB*/ |
| (0x2 << 28) | |
| /*volume master step: 0x03:0.5dB, 0x02:0.5dB, 0x01:0.25dB, 0x00:0.125dB*/ |
| (0x1 << 12) | |
| /*40ms from -120dB~0dB*/ |
| (0x1 << 0) |
| ); |
| pa1_acodec_mute(component, 0); |
| } |
| |
| void pa1_aed_eq_taps(struct snd_soc_component *component, unsigned int eq_taps) |
| { |
| if (eq_taps > 15) { |
| pr_err("Error EQ1_Tap = %d\n", eq_taps); |
| return; |
| } |
| |
| top_conversion_to_dsp(component); |
| snd_soc_component_update_bits(component, PA1_AED_STATUS_CTRL, 0x1f << 8, eq_taps << 8); |
| } |
| |
| void pa1_aed_get_ram_coeff(struct snd_soc_component *component, int add, |
| int len, unsigned int *params) |
| { |
| int i, ctrl_v; |
| unsigned int *p = params; |
| |
| top_conversion_to_dsp(component); |
| for (i = 0; i < len; i++, p++) { |
| ctrl_v = add + i; |
| snd_soc_component_write(component, PA1_AEQ_COEF_ADDR, ctrl_v); |
| *p = snd_soc_component_read(component, PA1_AEQ_COEF_DATA); |
| } |
| } |
| |
| void pa1_aed_set_ram_coeff(struct snd_soc_component *component, int add, |
| int len, unsigned int *params) |
| { |
| int i; |
| unsigned int *p = params; |
| |
| top_conversion_to_dsp(component); |
| snd_soc_component_write(component, PA1_AEQ_COEF_ADDR, add); |
| for (i = 0; i < len; i++, p++) |
| snd_soc_component_write(component, PA1_AEQ_COEF_DATA, *p); |
| } |
| |
| void pa1_aed_set_fullband_drc_coeff(struct snd_soc_component *component, unsigned int *params) |
| { |
| unsigned int *p = params; |
| int i; |
| |
| top_conversion_to_dsp(component); |
| snd_soc_component_write(component, PA1_AEQ_COEF_ADDR, PA1_FULLBAND_FILTER_RAM_ADD); |
| for (i = 0; i < PA1_AED_FULLBAND_DRC_SIZE; i++, p++) |
| snd_soc_component_write(component, PA1_AEQ_COEF_DATA, *p); |
| } |
| |
| void pa1_aed_set_mixer_params(struct snd_soc_component *component) |
| { |
| unsigned int *p = &PA1_MIXER_PARAM[0]; |
| |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, (0x1 << 6) | (0x1 << 0), |
| (0x0 << 6) | (0x0 << 0)); |
| top_conversion_to_dsp(component); |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0xf); |
| |
| /* Initialize parameters of input mixer */ |
| pa1_aed_set_ram_coeff(component, PA1_MIXER_GAIN_RAM_ADD, PA1_AED_MIXER_INPUT_SIZE, p); |
| |
| /* Initialize parameters of post mixer */ |
| p = &PA1_MIXER_PARAM[PA1_AED_MIXER_INPUT_SIZE]; |
| pa1_aed_set_ram_coeff(component, PA1_MIXER_GAIN_DAC_RAM_ADD, PA1_AED_MIXER_POST_SIZE, p); |
| |
| snd_soc_component_write(component, PA1_AED_CLIP_THD, 0x7ffffff); |
| } |
| |
| static void pa1_aed_set_filter_data(struct snd_soc_component *component) |
| { |
| unsigned int *p; |
| int i, addr; |
| |
| /* set default filter param*/ |
| p = &PA1_DC_CUT_COEFF[0]; |
| pa1_aed_set_ram_coeff(component, PA1_DC_CUT_RAM_ADD, PA1_DC_FILTER_PARAM_SIZE, p); |
| p = &PA1_EQ_COEFF[0]; |
| pa1_aed_set_ram_coeff(component, PA1_EQ_FILTER_RAM_ADD, PA1_EQ_FILTER_SIZE, p); |
| p = &PA1_3D_SURROUND_COEFF[0]; |
| pa1_aed_set_ram_coeff(component, PA1_3D_SURROUND_RAM_ADD, PA1_AED_3D_SURROUND_SIZE, p); |
| |
| for (i = 0; i < 4; i++) { |
| p = &PA1_CROSSOVER_COEFF[i * (PA1_FILTER_PARAM_SIZE + 1)]; |
| if (i == 0) |
| addr = PA1_CROSSOVER_FILTER_RAM_ADD + PA1_FILTER_PARAM_SIZE + 1; |
| else if (i == 1) |
| addr = PA1_CROSSOVER_FILTER_RAM_ADD + 2 * (PA1_FILTER_PARAM_SIZE + 1); |
| else if (i == 2) |
| addr = PA1_CROSSOVER_FILTER_RAM_ADD; |
| else if (i == 3) |
| addr = PA1_CROSSOVER_FILTER_RAM_ADD + 3 * (PA1_FILTER_PARAM_SIZE + 1); |
| |
| pa1_aed_set_ram_coeff(component, addr, (PA1_FILTER_PARAM_SIZE + 1), p); |
| } |
| } |
| |
| void pa1_aed_set_multiband_drc_coeff(struct snd_soc_component *component, int band) |
| { |
| int i, ctrl_v; |
| unsigned int *p = &PA1_MULTIBAND_DRC_COEFF[0]; |
| |
| ctrl_v = PA1_MULTIBAND_FILTER_RAM_ADD + band * 2; |
| p = &PA1_MULTIBAND_DRC_COEFF[band * PA1_AED_SINGLE_BAND_DRC_SIZE]; |
| top_conversion_to_dsp(component); |
| snd_soc_component_write(component, PA1_AEQ_COEF_ADDR, ctrl_v); |
| for (i = 0; i < 2; i++, p++) |
| snd_soc_component_write(component, PA1_AEQ_COEF_DATA, *p); |
| |
| ctrl_v = PA1_MULTIBAND_FILTER_RAM_ADD + band * 10 + 6; |
| p = &PA1_MULTIBAND_DRC_COEFF[band * PA1_AED_SINGLE_BAND_DRC_SIZE + 2]; |
| snd_soc_component_write(component, PA1_AEQ_COEF_ADDR, ctrl_v); |
| for (i = 0; i < 10; i++, p++) |
| snd_soc_component_write(component, PA1_AEQ_COEF_DATA, *p); |
| |
| ctrl_v = PA1_MULTIBAND_FILTER_RAM_ADD + band * 2 + 36; |
| p = &PA1_MULTIBAND_DRC_COEFF[band * PA1_AED_SINGLE_BAND_DRC_SIZE + 12]; |
| snd_soc_component_write(component, PA1_AEQ_COEF_ADDR, ctrl_v); |
| for (i = 0; i < 2; i++, p++) |
| snd_soc_component_write(component, PA1_AEQ_COEF_DATA, *p); |
| } |
| |
| void pa1_aed_get_multiband_drc_coeff(struct snd_soc_component *component, |
| int band, unsigned int *params) |
| { |
| int i, ctrl_v, add; |
| unsigned int value = 0; |
| unsigned int *p = params; |
| |
| ctrl_v = PA1_MULTIBAND_FILTER_RAM_ADD + band * 2; |
| top_conversion_to_dsp(component); |
| snd_soc_component_write(component, PA1_AEQ_COEF_ADDR, ctrl_v); |
| value = snd_soc_component_read(component, PA1_AEQ_COEF_DATA); |
| |
| *p++ = cpu_to_be32(value); |
| |
| ctrl_v = PA1_MULTIBAND_FILTER_RAM_ADD + band * 10 + 6; |
| snd_soc_component_write(component, PA1_AEQ_COEF_ADDR, ctrl_v); |
| value = snd_soc_component_read(component, PA1_AEQ_COEF_DATA); |
| |
| *p++ = cpu_to_be32(value); |
| |
| add = PA1_MULTIBAND_FILTER_RAM_ADD + band * 10 + 8; |
| for (i = 0; i < 8; i++, p++) { |
| ctrl_v = add + i; |
| snd_soc_component_write(component, PA1_AEQ_COEF_ADDR, ctrl_v); |
| value = snd_soc_component_read(component, PA1_AEQ_COEF_DATA); |
| *p++ = cpu_to_be32(value); |
| } |
| |
| ctrl_v = PA1_MULTIBAND_FILTER_RAM_ADD + band * 2 + 36; |
| snd_soc_component_write(component, PA1_AEQ_COEF_ADDR, ctrl_v); |
| value = snd_soc_component_read(component, PA1_AEQ_COEF_DATA); |
| *p++ = cpu_to_be32(value); |
| } |
| |
| void pa1_aed_set_multiband_drc_param(struct snd_soc_component *component) |
| { |
| int i; |
| |
| for (i = 0; i < 3; i++) |
| pa1_aed_set_multiband_drc_coeff(component, i); |
| } |
| |
| static void pa1_aed_set_fullband_drc_param(struct snd_soc_component *component) |
| { |
| unsigned int *p; |
| |
| p = &PA1_FULLBAND_DRC_COEFF[0]; |
| pa1_aed_set_fullband_drc_coeff(component, p); |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0x0); |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, 0x1 << 6, 0x1 << 6); |
| } |
| |
| static void pa1_aed_enable_dsp_process_module(struct snd_soc_component *component) |
| { |
| top_conversion_to_dsp(component); |
| snd_soc_component_update_bits(component, PA1_AED_STATUS_CTRL, |
| 0x7 << 20 | 0x3 << 24, |
| 0x7 << 20 | 0x3 << 24); |
| } |
| |
| static int pa1_mixer_get_3D_Surround_params(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
| unsigned int *value = (unsigned int *)ucontrol->value.bytes.data; |
| unsigned int *p = &PA1_3D_SURROUND_COEFF[0]; |
| int i; |
| |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, (0x1 << 6) | (0x1 << 0), |
| (0x0 << 6) | (0x0 << 0)); |
| top_conversion_to_dsp(component); |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0xf); |
| |
| pa1_aed_get_ram_coeff(component, PA1_3D_SURROUND_RAM_ADD, PA1_AED_FULLBAND_DRC_SIZE, p); |
| |
| for (i = 0; i < PA1_AED_FULLBAND_DRC_SIZE; i++) { |
| if (i == 5) |
| p++; |
| else |
| *value++ = cpu_to_be32(*p++); |
| } |
| |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0x0); |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, 0x1 << 6, 0x1 << 6); |
| |
| return 0; |
| } |
| |
| static int pa1_mixer_set_3D_Surround_params(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
| unsigned int tmp_data[PA1_FILTER_PARAM_SIZE + 2] = {0}; |
| unsigned int *p_data = &tmp_data[0]; |
| char tmp_string[PA1_FILTER_PARAM_BYTE]; |
| char *p_string = &tmp_string[0]; |
| unsigned int *p = &PA1_3D_SURROUND_COEFF[0]; |
| int num, i, band_id, addr; |
| char *val = (char *)ucontrol->value.bytes.data; |
| |
| if (!val) |
| return -ENOMEM; |
| memcpy(p_string, val, PA1_FILTER_PARAM_BYTE); |
| |
| num = pa1_str2int(p_string, p_data, PA1_FILTER_PARAM_BYTE); |
| band_id = tmp_data[0]; |
| if (num != (PA1_FILTER_PARAM_SIZE + 1) || band_id >= PA1_3D_SURROUND_BAND) { |
| pr_err("Error: parma_num = %d, band_id = %d\n", |
| num, tmp_data[0]); |
| return 0; |
| } |
| |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, (0x1 << 6) | (0x1 << 0), |
| (0x0 << 6) | (0x0 << 0)); |
| top_conversion_to_dsp(component); |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0xf); |
| |
| p_data = &tmp_data[1]; |
| p = &PA1_3D_SURROUND_COEFF[band_id * (PA1_FILTER_PARAM_SIZE + 1)]; |
| for (i = 0; i < (PA1_FILTER_PARAM_SIZE + 1); i++, p++, p_data++) |
| *p = *p_data; |
| |
| p = &PA1_3D_SURROUND_COEFF[band_id * (PA1_FILTER_PARAM_SIZE + 1)]; |
| if (band_id == 0) { |
| addr = PA1_3D_SURROUND_RAM_ADD; |
| pa1_aed_set_ram_coeff(component, addr, PA1_FILTER_PARAM_SIZE + 1, p); |
| } else if (band_id == 1) { |
| addr = PA1_3D_SURROUND_RAM_ADD + PA1_FILTER_PARAM_SIZE + 1; |
| pa1_aed_set_ram_coeff(component, addr, PA1_FILTER_PARAM_SIZE + 1, p); |
| } |
| |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0x0); |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, 0x1 << 6, 0x1 << 6); |
| |
| return 0; |
| } |
| |
| static int pa1_mixer_get_EQ_params(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
| unsigned int *value = (unsigned int *)ucontrol->value.bytes.data; |
| unsigned int *p = &PA1_EQ_COEFF[0]; |
| int i, j = 0; |
| |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, (0x1 << 6) | (0x1 << 0), |
| (0x0 << 6) | (0x0 << 0)); |
| top_conversion_to_dsp(component); |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0xf); |
| |
| pa1_aed_get_ram_coeff(component, PA1_EQ_FILTER_RAM_ADD, PA1_EQ_FILTER_SIZE_CH, p); |
| |
| for (i = 0; i < (PA1_EQ_FILTER_SIZE_CH - 1); i++) { |
| j = i + 1; |
| if (j % 6 == 0) |
| p++; |
| else |
| *value++ = cpu_to_be32(*p++); |
| } |
| |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0x0); |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, 0x1 << 6, 0x1 << 6); |
| |
| return 0; |
| } |
| |
| static int pa1_mixer_set_EQ_params(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
| unsigned int tmp_data[PA1_FILTER_PARAM_SIZE + 1] = {0}; |
| unsigned int *p_data = &tmp_data[0]; |
| char tmp_string[PA1_FILTER_PARAM_BYTE]; |
| char *p_string = &tmp_string[0]; |
| unsigned int *p = &PA1_EQ_COEFF[0]; |
| int num, i, band_id; |
| char *val = (char *)ucontrol->value.bytes.data; |
| |
| if (!val) |
| return -ENOMEM; |
| memcpy(p_string, val, PA1_FILTER_PARAM_BYTE); |
| |
| num = pa1_str2int(p_string, p_data, PA1_FILTER_PARAM_BYTE); |
| band_id = tmp_data[0]; |
| if (num != (PA1_FILTER_PARAM_SIZE + 1) || band_id >= PA1_EQ_BAND) { |
| pr_err("Error: parma_num = %d, band_id = %d\n", |
| num, tmp_data[0]); |
| return 0; |
| } |
| |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, (0x1 << 6) | (0x1 << 0), |
| (0x0 << 6) | (0x0 << 0)); |
| top_conversion_to_dsp(component); |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0xf); |
| |
| p_data = &tmp_data[1]; |
| p = &PA1_EQ_COEFF[band_id * (PA1_FILTER_PARAM_SIZE + 1)]; |
| for (i = 0; i < PA1_FILTER_PARAM_SIZE; i++, p++, p_data++) { |
| *p = *p_data; |
| *(p + PA1_EQ_FILTER_SIZE_CH) = *p_data; |
| } |
| |
| p = &PA1_EQ_COEFF[band_id * (PA1_FILTER_PARAM_SIZE + 1)]; |
| pa1_aed_set_ram_coeff(component, (PA1_EQ_FILTER_RAM_ADD + |
| band_id * (PA1_FILTER_PARAM_SIZE + 1)), |
| PA1_FILTER_PARAM_SIZE + 1, p); |
| |
| p = &PA1_EQ_COEFF[band_id * (PA1_FILTER_PARAM_SIZE + 1) + PA1_EQ_FILTER_SIZE_CH]; |
| pa1_aed_set_ram_coeff(component, (PA1_EQ_FILTER_RAM_ADD + |
| PA1_EQ_FILTER_SIZE_CH + band_id * (PA1_FILTER_PARAM_SIZE + 1)), |
| PA1_FILTER_PARAM_SIZE + 1, p); |
| |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0x0); |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, 0x1 << 6, 0x1 << 6); |
| |
| return 0; |
| } |
| |
| static int pa1_mixer_get_fullband_DRC_params(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
| unsigned int *value = (unsigned int *)ucontrol->value.bytes.data; |
| unsigned int *p = &PA1_FULLBAND_DRC_COEFF[0]; |
| int i; |
| int len = PA1_AED_FULLBAND_DRC_SIZE; |
| |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, (0x1 << 6) | (0x1 << 0), |
| (0x0 << 6) | (0x0 << 0)); |
| top_conversion_to_dsp(component); |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0xf); |
| |
| pa1_aed_get_ram_coeff(component, PA1_FULLBAND_FILTER_RAM_ADD, len, p); |
| |
| for (i = 0; i < len; i++) |
| if (i == 1 || i == 3) |
| p++; |
| else |
| *value++ = cpu_to_be32(*p++); |
| |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0x0); |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, 0x1 << 6, 0x1 << 6); |
| |
| return 0; |
| } |
| |
| static int pa1_mixer_set_fullband_DRC_params(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
| unsigned int tmp_data[PA1_AED_FULLBAND_DRC_SIZE + 1] = {0}; |
| unsigned int *p_data = &tmp_data[0]; |
| char tmp_string[PA1_AED_FULLBAND_DRC_BYTES]; |
| char *p_string = &tmp_string[0]; |
| unsigned int *p = &PA1_FULLBAND_DRC_COEFF[0]; |
| int num, i, group; |
| char *val = (char *)ucontrol->value.bytes.data; |
| |
| if (!val) |
| return -ENOMEM; |
| memcpy(p_string, val, PA1_AED_FULLBAND_DRC_BYTES); |
| |
| num = pa1_str2int(p_string, p_data, PA1_AED_FULLBAND_DRC_BYTES); |
| group = tmp_data[0]; |
| if (num != (PA1_AED_FULLBAND_DRC_SIZE + 1) || |
| group >= PA1_AED_FULLBAND_DRC_GROUP_SIZE) { |
| pr_err("Error: parma_num = %d, group = %d\n", |
| num, tmp_data[0]); |
| return 0; |
| } |
| |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, (0x1 << 6) | (0x1 << 0), |
| (0x0 << 6) | (0x0 << 0)); |
| top_conversion_to_dsp(component); |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0xf); |
| |
| p_data = &tmp_data[1]; |
| p = &PA1_FULLBAND_DRC_COEFF[0]; |
| for (i = 0; i < PA1_AED_FULLBAND_DRC_SIZE; i++) |
| *p++ = *p_data++; |
| |
| p = &PA1_FULLBAND_DRC_COEFF[0]; |
| pa1_aed_set_fullband_drc_coeff(component, p); |
| |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0x0); |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, 0x1 << 6, 0x1 << 6); |
| |
| return 0; |
| } |
| |
| static int pa1_mixer_get_multiband_DRC_params(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
| unsigned int *value = (unsigned int *)ucontrol->value.bytes.data; |
| int i; |
| |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, (0x1 << 6) | (0x1 << 0), |
| (0x0 << 6) | (0x0 << 0)); |
| top_conversion_to_dsp(component); |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0xf); |
| |
| for (i = 0; i < 3; i++) |
| pa1_aed_get_multiband_drc_coeff(component, i, value); |
| |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0x0); |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, 0x1 << 6, 0x1 << 6); |
| |
| return 0; |
| } |
| |
| static int pa1_mixer_set_multiband_DRC_params(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
| unsigned int tmp_data[PA1_AED_SINGLE_BAND_DRC_SIZE + 1] = {0}; |
| unsigned int *p_data = &tmp_data[0]; |
| char tmp_string[PA1_MULTIBAND_DRC_PARAM_BYTE]; |
| char *p_string = &tmp_string[0]; |
| unsigned int *p = &PA1_MULTIBAND_DRC_COEFF[0]; |
| int num, i, band_id; |
| char *val = (char *)ucontrol->value.bytes.data; |
| |
| if (!val) |
| return -ENOMEM; |
| memcpy(p_string, val, PA1_MULTIBAND_DRC_PARAM_BYTE); |
| |
| num = pa1_str2int(p_string, p_data, PA1_MULTIBAND_DRC_PARAM_BYTE); |
| band_id = tmp_data[0]; |
| if (num != (PA1_AED_SINGLE_BAND_DRC_SIZE + 1) || |
| band_id >= PA1_AED_MULTIBAND_DRC_BANDS) { |
| pr_err("Error: parma_num = %d, band_id = %d\n", |
| num, tmp_data[0]); |
| return 0; |
| } |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, (0x1 << 6) | (0x1 << 0), |
| (0x0 << 6) | (0x0 << 0)); |
| top_conversion_to_dsp(component); |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0xf); |
| |
| /*Don't update offset and gain*/ |
| p_data = &tmp_data[1]; |
| p = &PA1_MULTIBAND_DRC_COEFF[band_id * PA1_AED_SINGLE_BAND_DRC_SIZE]; |
| |
| for (i = 0; i < PA1_AED_SINGLE_BAND_DRC_SIZE; i++) |
| *p++ = *p_data++; |
| |
| pa1_aed_set_multiband_drc_coeff(component, band_id); |
| |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0x0); |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, 0x1 << 6, 0x1 << 6); |
| |
| return 0; |
| } |
| |
| static int pa1_mixer_get_crossover_params(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
| unsigned int *value = (unsigned int *)ucontrol->value.bytes.data; |
| unsigned int *p = &PA1_CROSSOVER_COEFF[0]; |
| int i, j = 0; |
| |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, (0x1 << 6) | (0x1 << 0), |
| (0x0 << 6) | (0x0 << 0)); |
| top_conversion_to_dsp(component); |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0xf); |
| |
| pa1_aed_get_ram_coeff(component, |
| (PA1_CROSSOVER_FILTER_RAM_ADD + PA1_FILTER_PARAM_SIZE + 1), |
| PA1_FILTER_PARAM_SIZE, p); |
| |
| p = &PA1_CROSSOVER_COEFF[PA1_FILTER_PARAM_SIZE + 1]; |
| pa1_aed_get_ram_coeff(component, |
| (PA1_CROSSOVER_FILTER_RAM_ADD + 2 * (PA1_FILTER_PARAM_SIZE + 1)), |
| PA1_FILTER_PARAM_SIZE, p); |
| |
| p = &PA1_CROSSOVER_COEFF[2 * (PA1_FILTER_PARAM_SIZE + 1)]; |
| pa1_aed_get_ram_coeff(component, PA1_CROSSOVER_FILTER_RAM_ADD, |
| PA1_FILTER_PARAM_SIZE, p); |
| |
| p = &PA1_CROSSOVER_COEFF[3 * (PA1_FILTER_PARAM_SIZE + 1)]; |
| pa1_aed_get_ram_coeff(component, |
| (PA1_CROSSOVER_FILTER_RAM_ADD + 3 * (PA1_FILTER_PARAM_SIZE + 1)), |
| PA1_FILTER_PARAM_SIZE, p); |
| |
| p = &PA1_CROSSOVER_COEFF[0]; |
| for (i = 0; i < (PA1_CROSSOVER_FILTER_SIZE - 1); i++) { |
| j = i + 1; |
| if (j % 6 == 0) |
| p++; |
| else |
| *value++ = cpu_to_be32(*p++); |
| } |
| |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0x0); |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, 0x1 << 6, 0x1 << 6); |
| |
| return 0; |
| } |
| |
| static int pa1_mixer_set_crossover_params(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
| unsigned int tmp_data[PA1_FILTER_PARAM_SIZE + 1] = {0}; |
| unsigned int *p_data = &tmp_data[0]; |
| char tmp_string[PA1_FILTER_PARAM_BYTE]; |
| char *p_string = &tmp_string[0]; |
| unsigned int *p = &PA1_CROSSOVER_COEFF[0]; |
| int num, i, band_id, addr; |
| char *val = (char *)ucontrol->value.bytes.data; |
| |
| if (!val) |
| return -ENOMEM; |
| memcpy(p_string, val, PA1_FILTER_PARAM_BYTE); |
| |
| num = pa1_str2int(p_string, p_data, PA1_FILTER_PARAM_BYTE); |
| band_id = tmp_data[0]; |
| if (num != (PA1_FILTER_PARAM_SIZE + 1) || |
| band_id >= PA1_CROSSOVER_FILTER_BAND) { |
| pr_err("Error: parma_num = %d, band_id = %d\n", |
| num, tmp_data[0]); |
| return 0; |
| } |
| |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, (0x1 << 6) | (0x1 << 0), |
| (0x0 << 6) | (0x0 << 0)); |
| top_conversion_to_dsp(component); |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0xf); |
| |
| p_data = &tmp_data[1]; |
| p = &PA1_CROSSOVER_COEFF[band_id * (PA1_FILTER_PARAM_SIZE + 1)]; |
| |
| for (i = 0; i < PA1_FILTER_PARAM_SIZE; i++) |
| *p++ = *p_data++; |
| |
| p = &PA1_CROSSOVER_COEFF[band_id * (PA1_FILTER_PARAM_SIZE + 1)]; |
| if (band_id == 0) { |
| addr = PA1_CROSSOVER_FILTER_RAM_ADD + PA1_FILTER_PARAM_SIZE + 1; |
| pa1_aed_set_ram_coeff(component, addr, PA1_FILTER_PARAM_SIZE + 1, p); |
| } else if (band_id == 1) { |
| addr = PA1_CROSSOVER_FILTER_RAM_ADD + 2 * (PA1_FILTER_PARAM_SIZE + 1); |
| pa1_aed_set_ram_coeff(component, addr, PA1_FILTER_PARAM_SIZE + 1, p); |
| } else if (band_id == 2) { |
| addr = PA1_CROSSOVER_FILTER_RAM_ADD; |
| pa1_aed_set_ram_coeff(component, addr, PA1_FILTER_PARAM_SIZE + 1, p); |
| } else if (band_id == 3) { |
| addr = PA1_CROSSOVER_FILTER_RAM_ADD + 3 * (PA1_FILTER_PARAM_SIZE + 1); |
| pa1_aed_set_ram_coeff(component, addr, PA1_FILTER_PARAM_SIZE + 1, p); |
| } |
| |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0x0); |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, 0x1 << 6, 0x1 << 6); |
| |
| return 0; |
| } |
| |
| static const struct snd_kcontrol_new pa1_acodec_vol_control[] = { |
| SOC_SINGLE_EXT_TLV("PA1 AED master volume", |
| PA1_AED_EQ_VOLUME_VAL, 20, 0x3FF, 1, |
| pa1_mixer_aed_read, pa1_mixer_aed_write, |
| pa1_vol_tlv), |
| |
| SOC_SINGLE_EXT_TLV("PA1 AED Lch volume", |
| PA1_AED_EQ_VOLUME_VAL, 0, 0x3FF, 1, |
| pa1_mixer_aed_read, pa1_mixer_aed_write, |
| pa1_vol_tlv), |
| |
| SOC_SINGLE_EXT_TLV("PA1 AED Rch volume", |
| PA1_AED_EQ_VOLUME_VAL, 10, 0x3FF, 1, |
| pa1_mixer_aed_read, pa1_mixer_aed_write, |
| pa1_vol_tlv), |
| |
| SOC_SINGLE_EXT("PA1 AED Mute", |
| PA1_AED_MUTE_CTRL, 2, 0x1, 0, |
| pa1_mixer_aed_get_mute, pa1_mixer_aed_set_mute), |
| |
| SOC_SINGLE_EXT("PA1 AED DC cut enable", |
| PA1_AED_STATUS_CTRL, 0, 0x1, 0, |
| pa1_mixer_aed_read, pa1_mixer_aed_write), |
| |
| SOC_SINGLE_EXT("PA1 AED 3D Surround enable", |
| PA1_AED_STATUS_CTRL, 3, 0x1, 0, |
| pa1_mixer_aed_read, pa1_mixer_aed_write), |
| |
| SND_SOC_BYTES_EXT("PA1 AED 3D Surround Parameters", |
| PA1_AED_FULLBAND_DRC_BYTES, |
| pa1_mixer_get_3D_Surround_params, |
| pa1_mixer_set_3D_Surround_params), |
| |
| SOC_SINGLE_EXT("PA1 AED Level Meter enable", |
| PA1_AED_STATUS_CTRL, 6, 0x1, 0, |
| pa1_mixer_aed_read, pa1_mixer_aed_write), |
| |
| SOC_SINGLE_EXT("PA1 AED EQ enable", |
| PA1_AED_STATUS_CTRL, 2, 0x1, 0, |
| pa1_mixer_aed_read, pa1_mixer_aed_write), |
| |
| SND_SOC_BYTES_EXT("PA1 AED EQ Parameters", |
| (PA1_EQ_FILTER_SIZE_CH * 4), |
| pa1_mixer_get_EQ_params, |
| pa1_mixer_set_EQ_params), |
| |
| SOC_SINGLE_EXT("PA1 AED Multi-band DRC enable", |
| PA1_AED_STATUS_CTRL, 7, 0x1, 0, |
| pa1_mixer_aed_read, pa1_mixer_aed_write), |
| |
| SND_SOC_BYTES_EXT("PA1 AED Crossover Filter Parameters", |
| (PA1_CROSSOVER_FILTER_SIZE * 4), |
| pa1_mixer_get_crossover_params, |
| pa1_mixer_set_crossover_params), |
| |
| SND_SOC_BYTES_EXT("PA1 AED Multi-band DRC Parameters", |
| (PA1_AED_MULTIBAND_DRC_SIZE * 4), |
| pa1_mixer_get_multiband_DRC_params, |
| pa1_mixer_set_multiband_DRC_params), |
| |
| SOC_SINGLE_EXT("PA1 AED Full-band DRC enable", |
| PA1_AED_STATUS_CTRL, 23, 0x1, 0, |
| pa1_mixer_aed_read, pa1_mixer_aed_write), |
| |
| SND_SOC_BYTES_EXT("PA1 AED Full-band DRC Parameters", |
| PA1_AED_FULLBAND_DRC_BYTES, |
| pa1_mixer_get_fullband_DRC_params, |
| pa1_mixer_set_fullband_DRC_params), |
| |
| SOC_SINGLE_EXT("PA1 AED Clip THD", |
| PA1_AED_CLIP_THD, 0, 0x7FFFFFF, 0, |
| pa1_mixer_aed_read, pa1_mixer_aed_write), |
| |
| SOC_SINGLE_EXT("PA1 AED Input Mixer Gain LL", |
| PA1_MIXER_GAIN_RAM_ADD, 0, 0xfffffff, 1, |
| pa1_mixer_get_gain, pa1_mixer_set_gain), |
| |
| SOC_SINGLE_EXT("PA1 AED Input Mixer Gain RL", |
| PA1_MIXER_GAIN_RAM_ADD + 1, 0, 0xfffffff, 0, |
| pa1_mixer_get_gain, pa1_mixer_set_gain), |
| |
| SOC_SINGLE_EXT("PA1 AED Input Mixer Gain LR", |
| PA1_MIXER_GAIN_RAM_ADD + 2, 0, 0xfffffff, 1, |
| pa1_mixer_get_gain, pa1_mixer_set_gain), |
| |
| SOC_SINGLE_EXT("PA1 AED Input Mixer Gain RR", |
| PA1_MIXER_GAIN_RAM_ADD + 3, 0, 0xfffffff, 0, |
| pa1_mixer_get_gain, pa1_mixer_set_gain), |
| |
| SOC_SINGLE_EXT("PA1 AED Mixer Gain LL DAC", |
| PA1_MIXER_GAIN_DAC_RAM_ADD, 0, 0xfffffff, 1, |
| pa1_mixer_get_gain, pa1_mixer_set_gain), |
| |
| SOC_SINGLE_EXT("PA1 AED Mixer Gain RL DAC", |
| PA1_MIXER_GAIN_DAC_RAM_ADD + 1, 0, 0xfffffff, 0, |
| pa1_mixer_get_gain, pa1_mixer_set_gain), |
| |
| SOC_SINGLE_EXT("PA1 AED Mixer Gain LR DAC", |
| PA1_MIXER_GAIN_DAC_RAM_ADD + 2, 0, 0xfffffff, 1, |
| pa1_mixer_get_gain, pa1_mixer_set_gain), |
| |
| SOC_SINGLE_EXT("PA1 AED Mixer Gain RR DAC", |
| PA1_MIXER_GAIN_DAC_RAM_ADD + 3, 0, 0xfffffff, 0, |
| pa1_mixer_get_gain, pa1_mixer_set_gain), |
| |
| SOC_SINGLE_EXT("PA1 AED Mixer Gain LL I2S", |
| PA1_MIXER_GAIN_I2S_RAM_ADD, 0, 0xfffffff, 1, |
| pa1_mixer_get_gain, pa1_mixer_set_gain), |
| |
| SOC_SINGLE_EXT("PA1 AED Mixer Gain RL I2S", |
| PA1_MIXER_GAIN_I2S_RAM_ADD + 1, 0, 0xfffffff, 0, |
| pa1_mixer_get_gain, pa1_mixer_set_gain), |
| |
| SOC_SINGLE_EXT("PA1 AED Mixer Gain LR I2S", |
| PA1_MIXER_GAIN_I2S_RAM_ADD + 2, 0, 0xfffffff, 1, |
| pa1_mixer_get_gain, pa1_mixer_set_gain), |
| |
| SOC_SINGLE_EXT("PA1 AED Mixer Gain RR I2S", |
| PA1_MIXER_GAIN_I2S_RAM_ADD + 3, 0, 0xfffffff, 0, |
| pa1_mixer_get_gain, pa1_mixer_set_gain), |
| |
| SOC_SINGLE_EXT("PA1 AED Level Meter RMS Parameters", |
| PA1_LEVEL_METER_RAM_ADD, 0, 0xfffffff, 0, |
| pa1_mixer_get_gain, pa1_mixer_set_level_meter_coeff), |
| }; |
| |
| static int pa1_acodec_set_bias_level(struct snd_soc_component *component, |
| enum snd_soc_bias_level level) |
| { |
| switch (level) { |
| case SND_SOC_BIAS_ON: |
| break; |
| |
| case SND_SOC_BIAS_PREPARE: |
| /* Full power on */ |
| break; |
| |
| case SND_SOC_BIAS_STANDBY: |
| break; |
| |
| case SND_SOC_BIAS_OFF: |
| /* The chip runs through the power down sequence for us. */ |
| break; |
| } |
| component->dapm.bias_level = level; |
| return 0; |
| } |
| |
| static int pa1_acodec_trigger(struct snd_pcm_substream *substream, int cmd, |
| struct snd_soc_dai *codec_dai) |
| { |
| struct pa1_acodec_priv *pa1_acodec = snd_soc_dai_get_drvdata(codec_dai); |
| struct snd_soc_component *component = pa1_acodec->component; |
| |
| if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
| switch (cmd) { |
| case SNDRV_PCM_TRIGGER_START: |
| case SNDRV_PCM_TRIGGER_RESUME: |
| case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
| pr_debug("%s(), start\n", __func__); |
| if (!pa1_acodec->mute) |
| pa1_acodec_mute(component, 0); |
| break; |
| case SNDRV_PCM_TRIGGER_STOP: |
| case SNDRV_PCM_TRIGGER_SUSPEND: |
| case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
| pr_debug("%s(), stop\n", __func__); |
| if (!pa1_acodec->mute) |
| pa1_acodec_mute(component, 1); |
| break; |
| } |
| } |
| return 0; |
| } |
| |
| static int reset_pa1_acodec_GPIO(struct device *dev) |
| { |
| struct pa1_acodec_priv *pa1_acodec = dev_get_drvdata(dev); |
| struct pa1_acodec_platform_data *pdata = pa1_acodec->pdata; |
| int ret = 0; |
| |
| if (!gpio_is_valid(pdata->reset_pin)) |
| return 0; |
| |
| if (!reset_pin_setting) { |
| ret = gpio_request(pdata->reset_pin, NULL); |
| if (ret < 0) |
| return -1; |
| |
| gpio_direction_output(pdata->reset_pin, GPIOF_OUT_INIT_LOW); |
| usleep_range(10 * 500, 10 * 1000); |
| |
| gpio_direction_output(pdata->reset_pin, GPIOF_OUT_INIT_HIGH); |
| usleep_range(10 * 500, 10 * 1000); |
| reset_pin_setting = true; |
| gpio_free(pdata->reset_pin); |
| } |
| |
| return 0; |
| } |
| |
| static int pa1_acodec_snd_suspend(struct snd_soc_component *component) |
| { |
| struct pa1_acodec_priv *pa1_acodec = snd_soc_component_get_drvdata(component); |
| struct pa1_acodec_platform_data *pdata = pa1_acodec->pdata; |
| unsigned int *p = &PA1_MIXER_PARAM[0]; |
| int len = PA1_AED_MIXER_INPUT_SIZE; |
| int ret; |
| |
| dev_info(component->dev, "pa1_acodec_suspend!\n"); |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, (0x1 << 6) | (0x1 << 0), |
| (0x0 << 6) | (0x0 << 0)); |
| top_conversion_to_dsp(component); |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0xf); |
| pa1_aed_get_ram_coeff(component, PA1_MIXER_GAIN_RAM_ADD, len, p); |
| p = &PA1_MIXER_PARAM[4]; |
| len = PA1_AED_MIXER_POST_SIZE; |
| pa1_aed_get_ram_coeff(component, PA1_MIXER_GAIN_DAC_RAM_ADD, len, p); |
| snd_soc_component_write(component, PA1_AEQ_SOFT_REST, 0x0); |
| dsp_conversion_to_top(component); |
| snd_soc_component_update_bits(component, PA1_DSP_MISC0, 0x1 << 6, 0x1 << 6); |
| |
| top_conversion_to_dsp(component); |
| pa1_acodec->vol = (unsigned int)snd_soc_component_read(component, PA1_AED_EQ_VOLUME_VAL); |
| pa1_acodec->dsp_status = |
| (unsigned int)snd_soc_component_read(component, PA1_AED_STATUS_CTRL); |
| pa1_acodec_set_bias_level(component, SND_SOC_BIAS_OFF); |
| |
| if (gpio_is_valid(pdata->reset_pin) && reset_pin_setting) { |
| // request amp PD pin control GPIO |
| ret = gpio_request(pdata->reset_pin, NULL); |
| if (ret < 0) |
| dev_err(component->dev, "failed to request gpio: %d\n", ret); |
| gpio_direction_output(pdata->reset_pin, GPIOF_OUT_INIT_LOW); |
| reset_pin_setting = false; |
| gpio_free(pdata->reset_pin); |
| } |
| usleep_range(9, 15); |
| |
| return 0; |
| } |
| |
| static void pa1_effect_init(struct snd_soc_component *component) |
| { |
| /* all 15 bands for EQ */ |
| pa1_aed_eq_taps(component, PA1_EQ_BAND); |
| /* set default mixer gain */ |
| pa1_aed_set_mixer_params(component); |
| /* set default filter param */ |
| pa1_aed_set_filter_data(component); |
| /* set multi-band drc param */ |
| pa1_aed_set_multiband_drc_param(component); |
| /* set full-band drc param */ |
| pa1_aed_set_fullband_drc_param(component); |
| /* enable some module which must be on when dsp is running */ |
| pa1_aed_enable_dsp_process_module(component); |
| /* set master & channel volume gain to 0dB */ |
| pa1_aed_set_volume(component, 0xc0, 0xc0, 0xc0); |
| } |
| |
| static void pa1_playback_mode(struct snd_soc_component *component) |
| { |
| int j; |
| |
| dsp_conversion_to_top(component); |
| snd_soc_component_write(component, PA1_RESET_CTRL, 0x10); |
| msleep(20); |
| for (j = 0; j < ARRAY_SIZE(pa1_reg_set); j++) |
| snd_soc_component_write(component, pa1_reg_set[j][0], |
| pa1_reg_set[j][1]); |
| } |
| |
| static int pa1_acodec_snd_resume(struct snd_soc_component *component) |
| { |
| struct pa1_acodec_priv *pa1_acodec = snd_soc_component_get_drvdata(component); |
| struct pa1_acodec_platform_data *pdata = pa1_acodec->pdata; |
| int ret; |
| |
| if (gpio_is_valid(pdata->reset_pin) && !reset_pin_setting) { |
| // request amp PD pin control GPIO |
| ret = gpio_request(pdata->reset_pin, NULL); |
| if (ret < 0) |
| dev_err(component->dev, "failed to request gpio: %d\n", ret); |
| reset_pin_setting = true; |
| gpio_direction_output(pdata->reset_pin, GPIOF_OUT_INIT_HIGH); |
| /* need delay before regcache for spec request */ |
| gpio_free(pdata->reset_pin); |
| } |
| |
| usleep_range(1 * 500, 1 * 1000); |
| |
| pa1_acodec_set_bias_level(component, SND_SOC_BIAS_STANDBY); |
| msleep(20); |
| pa1_playback_mode(component); |
| msleep(30); |
| pa1_effect_init(component); |
| top_conversion_to_dsp(component); |
| snd_soc_component_write(component, PA1_AED_EQ_VOLUME_VAL, pa1_acodec->vol); |
| snd_soc_component_write(component, PA1_AED_STATUS_CTRL, pa1_acodec->dsp_status); |
| pa1_acodec_mute(component, pa1_acodec->mute); |
| |
| return 0; |
| } |
| |
| static int pa1_acodec_probe(struct snd_soc_component *component) |
| { |
| struct pa1_acodec_priv *pa1_acodec = snd_soc_component_get_drvdata(component); |
| |
| pr_debug("%s: Init PA1!\n", __func__); |
| |
| snd_soc_add_component_controls(component, pa1_acodec_vol_control, |
| ARRAY_SIZE(pa1_acodec_vol_control)); |
| pa1_acodec->component = component; |
| |
| msleep(20); |
| pa1_acodec->pa1_page = 0; |
| pa1_playback_mode(component); |
| msleep(30); |
| pa1_effect_init(component); |
| |
| return 0; |
| } |
| |
| static void pa1_acodec_remove(struct snd_soc_component *component) |
| { |
| struct pa1_acodec_priv *pa1_acodec = snd_soc_component_get_drvdata(component); |
| struct pa1_acodec_platform_data *pdata = pa1_acodec->pdata; |
| |
| if (gpio_is_valid(pdata->reset_pin)) |
| gpio_direction_output(pdata->reset_pin, GPIOF_OUT_INIT_LOW); |
| |
| usleep_range(9, 15); |
| } |
| |
| static const struct snd_soc_component_driver soc_codec_pa1_acodec = { |
| .probe = pa1_acodec_probe, |
| .remove = pa1_acodec_remove, |
| .suspend = pa1_acodec_snd_suspend, |
| .resume = pa1_acodec_snd_resume, |
| .set_bias_level = pa1_acodec_set_bias_level, |
| }; |
| |
| static const struct snd_soc_dai_ops pa1_acodec_dai_ops = { |
| .trigger = pa1_acodec_trigger, |
| }; |
| |
| static struct snd_soc_dai_driver pa1_acodec_dai = { |
| .name = "pa1_acodec-amplifier", |
| .playback = { |
| .stream_name = "Playback", |
| .channels_min = 2, |
| .channels_max = 8, |
| .rates = PA1_RATES, |
| .formats = PA1_FORMATS, |
| }, |
| .ops = &pa1_acodec_dai_ops, |
| }; |
| |
| static int pa1_acodec_parse_dt(struct pa1_acodec_priv *pa1_acodec, |
| struct device_node *np) |
| { |
| int ret = 0; |
| int reset_pin = -1; |
| |
| reset_pin = of_get_named_gpio(np, "reset_pin", 0); |
| if (!gpio_is_valid(reset_pin)) { |
| pr_err("%s fail to get reset pin from dts!\n", __func__); |
| ret = -1; |
| } else { |
| pr_debug("%s pdata->reset_pin = %d!\n", __func__, reset_pin); |
| } |
| |
| pa1_acodec->pdata->reset_pin = reset_pin; |
| return ret; |
| } |
| |
| static int pa1_acodec_i2c_probe(struct i2c_client *i2c) |
| { |
| struct regmap *regmap; |
| struct regmap_config config = pa1_acodec_regmap; |
| struct pa1_acodec_priv *pa1_acodec; |
| struct pa1_acodec_platform_data *pdata; |
| int ret = 0; |
| |
| pa1_acodec = devm_kzalloc(&i2c->dev, sizeof(struct pa1_acodec_priv), GFP_KERNEL); |
| if (!pa1_acodec) |
| return -ENOMEM; |
| |
| regmap = devm_regmap_init_i2c(i2c, &config); |
| if (IS_ERR(regmap)) |
| return PTR_ERR(regmap); |
| |
| pr_debug("%s i2c addr:0x%x\n", __func__, i2c->addr); |
| |
| pdata = devm_kzalloc(&i2c->dev, |
| sizeof(struct pa1_acodec_platform_data), |
| GFP_KERNEL); |
| if (!pdata) { |
| pr_err("%s failed to kzalloc for pa1_acodec pdata\n", __func__); |
| devm_kfree(&i2c->dev, i2c_get_clientdata(i2c)); |
| return -ENOMEM; |
| } |
| pa1_acodec->pdata = pdata; |
| |
| pa1_acodec_parse_dt(pa1_acodec, i2c->dev.of_node); |
| pa1_acodec->regmap = regmap; |
| |
| dev_set_drvdata(&i2c->dev, pa1_acodec); |
| |
| ret = devm_snd_soc_register_component(&i2c->dev, &soc_codec_pa1_acodec, |
| &pa1_acodec_dai, 1); |
| if (ret != 0) { |
| devm_kfree(&i2c->dev, i2c_get_clientdata(i2c)); |
| return -ENOMEM; |
| } |
| reset_pa1_acodec_GPIO(&i2c->dev); |
| |
| return ret; |
| } |
| |
| static void pa1_acodec_i2c_remove(struct i2c_client *i2c) |
| { |
| devm_kfree(&i2c->dev, i2c_get_clientdata(i2c)); |
| } |
| |
| static void pa1_acodec_i2c_shutdown(struct i2c_client *i2c) |
| { |
| struct pa1_acodec_priv *pa1_acodec = i2c_get_clientdata(i2c); |
| struct pa1_acodec_platform_data *pdata = pa1_acodec->pdata; |
| int ret; |
| |
| if (gpio_is_valid(pdata->reset_pin) && reset_pin_setting) { |
| // request amp PD pin control GPIO |
| ret = gpio_request(pdata->reset_pin, NULL); |
| if (ret < 0) |
| pr_err("failed to request gpio: %d\n", ret); |
| gpio_direction_output(pdata->reset_pin, GPIOF_OUT_INIT_LOW); |
| reset_pin_setting = false; |
| gpio_free(pdata->reset_pin); |
| } |
| |
| } |
| |
| static const struct i2c_device_id pa1_acodec_i2c_id[] = { |
| {"pa1_acodec",}, |
| {} |
| }; |
| |
| MODULE_DEVICE_TABLE(i2c, pa1_acodec_i2c_id); |
| |
| #ifdef CONFIG_OF |
| static const struct of_device_id pa1_acodec_of_match[] = { |
| {.compatible = "amlogic, pa1_acodec",}, |
| {} |
| }; |
| |
| MODULE_DEVICE_TABLE(of, pa1_acodec_of_match); |
| #endif |
| |
| static struct i2c_driver pa1_acodec_i2c_driver = { |
| .probe = pa1_acodec_i2c_probe, |
| .remove = pa1_acodec_i2c_remove, |
| .shutdown = pa1_acodec_i2c_shutdown, |
| .id_table = pa1_acodec_i2c_id, |
| .driver = { |
| .name = PA1_DRV_NAME, |
| .of_match_table = pa1_acodec_of_match, |
| }, |
| }; |
| |
| module_i2c_driver(pa1_acodec_i2c_driver); |
| |
| MODULE_AUTHOR("AMLogic, Inc."); |
| MODULE_DESCRIPTION("PA1 Audio Amplifier Driver"); |
| MODULE_LICENSE("GPL v2"); |