diff options
-rw-r--r-- | Documentation/devicetree/bindings/sound/atmel-classd.txt | 6 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/sound/da7218.txt | 104 | ||||
-rw-r--r-- | include/sound/da7218.h | 109 | ||||
-rw-r--r-- | sound/soc/atmel/atmel-classd.c | 12 | ||||
-rw-r--r-- | sound/soc/codecs/Kconfig | 4 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/codecs/da7218.c | 3314 | ||||
-rw-r--r-- | sound/soc/codecs/da7218.h | 1414 | ||||
-rw-r--r-- | sound/soc/fsl/imx-pcm-dma.c | 2 | ||||
-rw-r--r-- | sound/soc/intel/baytrail/sst-baytrail-pcm.c | 2 | ||||
-rw-r--r-- | sound/soc/omap/omap-hdmi-audio.c | 2 |
11 files changed, 4967 insertions, 4 deletions
diff --git a/Documentation/devicetree/bindings/sound/atmel-classd.txt b/Documentation/devicetree/bindings/sound/atmel-classd.txt index 0018451c4351..549e701cb7a1 100644 --- a/Documentation/devicetree/bindings/sound/atmel-classd.txt +++ b/Documentation/devicetree/bindings/sound/atmel-classd.txt @@ -16,6 +16,10 @@ Required properties: Required elements: "pclk", "gclk" and "aclk". - clocks Please refer to clock-bindings.txt. +- assigned-clocks + Should be <&classd_gclk>. +- assigned-clock-parents + Should be <&audio_pll_pmc>. Optional properties: - pinctrl-names, pinctrl-0 @@ -43,6 +47,8 @@ classd: classd@fc048000 { dma-names = "tx"; clocks = <&classd_clk>, <&classd_gclk>, <&audio_pll_pmc>; clock-names = "pclk", "gclk", "aclk"; + assigned-clocks = <&classd_gclk>; + assigned-clock-parents = <&audio_pll_pmc>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_classd_default>; diff --git a/Documentation/devicetree/bindings/sound/da7218.txt b/Documentation/devicetree/bindings/sound/da7218.txt new file mode 100644 index 000000000000..5ca5a709b6aa --- /dev/null +++ b/Documentation/devicetree/bindings/sound/da7218.txt @@ -0,0 +1,104 @@ +Dialog Semiconductor DA7218 Audio Codec bindings + +DA7218 is an audio codec with HP detect feature. + +====== + +Required properties: +- compatible : Should be "dlg,da7217" or "dlg,da7218" +- reg: Specifies the I2C slave address + +- VDD-supply: VDD power supply for the device +- VDDMIC-supply: VDDMIC power supply for the device +- VDDIO-supply: VDDIO power supply for the device + (See Documentation/devicetree/bindings/regulator/regulator.txt for further + information relating to regulators) + +Optional properties: +- interrupt-parent: Specifies the phandle of the interrupt controller to which + the IRQs from DA7218 are delivered to. +- interrupts: IRQ line info for DA7218 chip. + (See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt for + further information relating to interrupt properties) +- interrupt-names : Name associated with interrupt line. Should be "wakeup" if + interrupt is to be used to wake system, otherwise "irq" should be used. +- wakeup-source: Flag to indicate this device can wake system (suspend/resume). + +- clocks : phandle and clock specifier for codec MCLK. +- clock-names : Clock name string for 'clocks' attribute, should be "mclk". + +- dlg,micbias1-lvl-millivolt : Voltage (mV) for Mic Bias 1 + [<1200>, <1600>, <1800>, <2000>, <2200>, <2400>, <2600>, <2800>, <3000>] +- dlg,micbias2-lvl-millivolt : Voltage (mV) for Mic Bias 2 + [<1200>, <1600>, <1800>, <2000>, <2200>, <2400>, <2600>, <2800>, <3000>] +- dlg,mic1-amp-in-sel : Mic1 input source type + ["diff", "se_p", "se_n"] +- dlg,mic2-amp-in-sel : Mic2 input source type + ["diff", "se_p", "se_n"] +- dlg,dmic1-data-sel : DMIC1 channel select based on clock edge. + ["lrise_rfall", "lfall_rrise"] +- dlg,dmic1-samplephase : When to sample audio from DMIC1. + ["on_clkedge", "between_clkedge"] +- dlg,dmic1-clkrate-hz : DMic1 clock frequency (Hz). + [<1500000>, <3000000>] +- dlg,dmic2-data-sel : DMic2 channel select based on clock edge. + ["lrise_rfall", "lfall_rrise"] +- dlg,dmic2-samplephase : When to sample audio from DMic2. + ["on_clkedge", "between_clkedge"] +- dlg,dmic2-clkrate-hz : DMic2 clock frequency (Hz). + [<1500000>, <3000000>] +- dlg,hp-diff-single-supply : Boolean flag, use single supply for HP + (DA7217 only) + +====== + +Optional Child node - 'da7218_hpldet' (DA7218 only): + +Optional properties: +- dlg,jack-rate-us : Time between jack detect measurements (us) + [<5>, <10>, <20>, <40>, <80>, <160>, <320>, <640>] +- dlg,jack-debounce : Number of debounce measurements taken for jack detect + [<0>, <2>, <3>, <4>] +- dlg,jack-threshold-pct : Threshold level for jack detection (% of VDD) + [<84>, <88>, <92>, <96>] +- dlg,comp-inv : Boolean flag, invert comparator output +- dlg,hyst : Boolean flag, enable hysteresis +- dlg,discharge : Boolean flag, auto discharge of Mic Bias on jack removal + +====== + +Example: + + codec: da7218@1a { + compatible = "dlg,da7218"; + reg = <0x1a>; + interrupt-parent = <&gpio6>; + interrupts = <11 IRQ_TYPE_LEVEL_HIGH>; + wakeup-source; + + VDD-supply = <®_audio>; + VDDMIC-supply = <®_audio>; + VDDIO-supply = <®_audio>; + + clocks = <&clks 201>; + clock-names = "mclk"; + + dlg,micbias1-lvl-millivolt = <2600>; + dlg,micbias2-lvl-millivolt = <2600>; + dlg,mic1-amp-in-sel = "diff"; + dlg,mic2-amp-in-sel = "diff"; + + dlg,dmic1-data-sel = "lrise_rfall"; + dlg,dmic1-samplephase = "on_clkedge"; + dlg,dmic1-clkrate-hz = <3000000>; + dlg,dmic2-data-sel = "lrise_rfall"; + dlg,dmic2-samplephase = "on_clkedge"; + dlg,dmic2-clkrate-hz = <3000000>; + + da7218_hpldet { + dlg,jack-rate-us = <40>; + dlg,jack-debounce = <2>; + dlg,jack-threshold-pct = <84>; + dlg,hyst; + }; + }; diff --git a/include/sound/da7218.h b/include/sound/da7218.h new file mode 100644 index 000000000000..0dbb818ac116 --- /dev/null +++ b/include/sound/da7218.h @@ -0,0 +1,109 @@ +/* + * da7218.h - DA7218 ASoC Codec Driver Platform Data + * + * Copyright (c) 2015 Dialog Semiconductor + * + * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef _DA7218_PDATA_H +#define _DA7218_PDATA_H + +/* Mic Bias */ +enum da7218_micbias_voltage { + DA7218_MICBIAS_1_2V = -1, + DA7218_MICBIAS_1_6V, + DA7218_MICBIAS_1_8V, + DA7218_MICBIAS_2_0V, + DA7218_MICBIAS_2_2V, + DA7218_MICBIAS_2_4V, + DA7218_MICBIAS_2_6V, + DA7218_MICBIAS_2_8V, + DA7218_MICBIAS_3_0V, +}; + +enum da7218_mic_amp_in_sel { + DA7218_MIC_AMP_IN_SEL_DIFF = 0, + DA7218_MIC_AMP_IN_SEL_SE_P, + DA7218_MIC_AMP_IN_SEL_SE_N, +}; + +/* DMIC */ +enum da7218_dmic_data_sel { + DA7218_DMIC_DATA_LRISE_RFALL = 0, + DA7218_DMIC_DATA_LFALL_RRISE, +}; + +enum da7218_dmic_samplephase { + DA7218_DMIC_SAMPLE_ON_CLKEDGE = 0, + DA7218_DMIC_SAMPLE_BETWEEN_CLKEDGE, +}; + +enum da7218_dmic_clk_rate { + DA7218_DMIC_CLK_3_0MHZ = 0, + DA7218_DMIC_CLK_1_5MHZ, +}; + +/* Headphone Detect */ +enum da7218_hpldet_jack_rate { + DA7218_HPLDET_JACK_RATE_5US = 0, + DA7218_HPLDET_JACK_RATE_10US, + DA7218_HPLDET_JACK_RATE_20US, + DA7218_HPLDET_JACK_RATE_40US, + DA7218_HPLDET_JACK_RATE_80US, + DA7218_HPLDET_JACK_RATE_160US, + DA7218_HPLDET_JACK_RATE_320US, + DA7218_HPLDET_JACK_RATE_640US, +}; + +enum da7218_hpldet_jack_debounce { + DA7218_HPLDET_JACK_DEBOUNCE_OFF = 0, + DA7218_HPLDET_JACK_DEBOUNCE_2, + DA7218_HPLDET_JACK_DEBOUNCE_3, + DA7218_HPLDET_JACK_DEBOUNCE_4, +}; + +enum da7218_hpldet_jack_thr { + DA7218_HPLDET_JACK_THR_84PCT = 0, + DA7218_HPLDET_JACK_THR_88PCT, + DA7218_HPLDET_JACK_THR_92PCT, + DA7218_HPLDET_JACK_THR_96PCT, +}; + +struct da7218_hpldet_pdata { + enum da7218_hpldet_jack_rate jack_rate; + enum da7218_hpldet_jack_debounce jack_debounce; + enum da7218_hpldet_jack_thr jack_thr; + bool comp_inv; + bool hyst; + bool discharge; +}; + +struct da7218_pdata { + /* Mic */ + enum da7218_micbias_voltage micbias1_lvl; + enum da7218_micbias_voltage micbias2_lvl; + enum da7218_mic_amp_in_sel mic1_amp_in_sel; + enum da7218_mic_amp_in_sel mic2_amp_in_sel; + + /* DMIC */ + enum da7218_dmic_data_sel dmic1_data_sel; + enum da7218_dmic_data_sel dmic2_data_sel; + enum da7218_dmic_samplephase dmic1_samplephase; + enum da7218_dmic_samplephase dmic2_samplephase; + enum da7218_dmic_clk_rate dmic1_clk_rate; + enum da7218_dmic_clk_rate dmic2_clk_rate; + + /* HP Diff Supply - DA7217 only */ + bool hp_diff_single_supply; + + /* HP Detect - DA7218 only */ + struct da7218_hpldet_pdata *hpldet_pdata; +}; + +#endif /* _DA7218_PDATA_H */ diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c index f3ffb39bfe27..6107de9c538b 100644 --- a/sound/soc/atmel/atmel-classd.c +++ b/sound/soc/atmel/atmel-classd.c @@ -106,7 +106,7 @@ static const struct snd_pcm_hardware atmel_classd_hw = { .rates = ATMEL_CLASSD_RATES, .rate_min = 8000, .rate_max = 96000, - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .buffer_bytes_max = 64 * 1024, .period_bytes_min = 256, @@ -145,7 +145,7 @@ static const struct snd_soc_dai_ops atmel_classd_cpu_dai_ops = { static struct snd_soc_dai_driver atmel_classd_cpu_dai = { .playback = { - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = ATMEL_CLASSD_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE,}, @@ -171,9 +171,13 @@ atmel_classd_platform_configure_dma(struct snd_pcm_substream *substream, return -EINVAL; } + if (params_channels(params) == 1) + slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + else + slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + slave_config->direction = DMA_MEM_TO_DEV; slave_config->dst_addr = dd->phy_base + CLASSD_THR; - slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; slave_config->dst_maxburst = 1; slave_config->src_maxburst = 1; slave_config->device_fc = false; @@ -486,7 +490,7 @@ static struct snd_soc_dai_driver atmel_classd_codec_dai = { .name = ATMEL_CLASSD_CODEC_DAI_NAME, .playback = { .stream_name = "Playback", - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = ATMEL_CLASSD_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE, diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index efdd0f8e2e9c..05fb938c7704 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -59,6 +59,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_CX20442 if TTY select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI select SND_SOC_DA7213 if I2C + select SND_SOC_DA7218 if I2C select SND_SOC_DA7219 if I2C select SND_SOC_DA732X if I2C select SND_SOC_DA9055 if I2C @@ -450,6 +451,9 @@ config SND_SOC_DA7210 config SND_SOC_DA7213 tristate +config SND_SOC_DA7218 + tristate + config SND_SOC_DA7219 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 463d7d0880b7..266c6be0c7f5 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -51,6 +51,7 @@ snd-soc-cs47l24-objs := cs47l24.o snd-soc-cx20442-objs := cx20442.o snd-soc-da7210-objs := da7210.o snd-soc-da7213-objs := da7213.o +snd-soc-da7218-objs := da7218.o snd-soc-da7219-objs := da7219.o da7219-aad.o snd-soc-da732x-objs := da732x.o snd-soc-da9055-objs := da9055.o @@ -250,6 +251,7 @@ obj-$(CONFIG_SND_SOC_CS47L24) += snd-soc-cs47l24.o obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o +obj-$(CONFIG_SND_SOC_DA7218) += snd-soc-da7218.o obj-$(CONFIG_SND_SOC_DA7219) += snd-soc-da7219.o obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c new file mode 100644 index 000000000000..72686517ff54 --- /dev/null +++ b/sound/soc/codecs/da7218.c @@ -0,0 +1,3314 @@ +/* + * da7218.c - DA7218 ALSA SoC Codec Driver + * + * Copyright (c) 2015 Dialog Semiconductor + * + * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/clk.h> +#include <linux/i2c.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/pm.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/regulator/consumer.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/jack.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <asm/div64.h> + +#include <sound/da7218.h> +#include "da7218.h" + + +/* + * TLVs and Enums + */ + +/* Input TLVs */ +static const DECLARE_TLV_DB_SCALE(da7218_mic_gain_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7218_mixin_gain_tlv, -450, 150, 0); +static const DECLARE_TLV_DB_SCALE(da7218_in_dig_gain_tlv, -8325, 75, 0); +static const DECLARE_TLV_DB_SCALE(da7218_ags_trigger_tlv, -9000, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7218_ags_att_max_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7218_alc_threshold_tlv, -9450, 150, 0); +static const DECLARE_TLV_DB_SCALE(da7218_alc_gain_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7218_alc_ana_gain_tlv, 0, 600, 0); + +/* Input/Output TLVs */ +static const DECLARE_TLV_DB_SCALE(da7218_dmix_gain_tlv, -4200, 150, 0); + +/* Output TLVs */ +static const DECLARE_TLV_DB_SCALE(da7218_dgs_trigger_tlv, -9450, 150, 0); +static const DECLARE_TLV_DB_SCALE(da7218_dgs_anticlip_tlv, -4200, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7218_dgs_signal_tlv, -9000, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7218_out_eq_band_tlv, -1050, 150, 0); +static const DECLARE_TLV_DB_SCALE(da7218_out_dig_gain_tlv, -8325, 75, 0); +static const DECLARE_TLV_DB_SCALE(da7218_dac_ng_threshold_tlv, -10200, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7218_mixout_gain_tlv, -100, 50, 0); +static const DECLARE_TLV_DB_SCALE(da7218_hp_gain_tlv, -5700, 150, 0); + +/* Input Enums */ +static const char * const da7218_alc_attack_rate_txt[] = { + "7.33/fs", "14.66/fs", "29.32/fs", "58.64/fs", "117.3/fs", "234.6/fs", + "469.1/fs", "938.2/fs", "1876/fs", "3753/fs", "7506/fs", "15012/fs", + "30024/fs", +}; + +static const struct soc_enum da7218_alc_attack_rate = + SOC_ENUM_SINGLE(DA7218_ALC_CTRL2, DA7218_ALC_ATTACK_SHIFT, + DA7218_ALC_ATTACK_MAX, da7218_alc_attack_rate_txt); + +static const char * const da7218_alc_release_rate_txt[] = { + "28.66/fs", "57.33/fs", "114.6/fs", "229.3/fs", "458.6/fs", "917.1/fs", + "1834/fs", "3668/fs", "7337/fs", "14674/fs", "29348/fs", +}; + +static const struct soc_enum da7218_alc_release_rate = + SOC_ENUM_SINGLE(DA7218_ALC_CTRL2, DA7218_ALC_RELEASE_SHIFT, + DA7218_ALC_RELEASE_MAX, da7218_alc_release_rate_txt); + +static const char * const da7218_alc_hold_time_txt[] = { + "62/fs", "124/fs", "248/fs", "496/fs", "992/fs", "1984/fs", "3968/fs", + "7936/fs", "15872/fs", "31744/fs", "63488/fs", "126976/fs", + "253952/fs", "507904/fs", "1015808/fs", "2031616/fs" +}; + +static const struct soc_enum da7218_alc_hold_time = + SOC_ENUM_SINGLE(DA7218_ALC_CTRL3, DA7218_ALC_HOLD_SHIFT, + DA7218_ALC_HOLD_MAX, da7218_alc_hold_time_txt); + +static const char * const da7218_alc_anticlip_step_txt[] = { + "0.034dB/fs", "0.068dB/fs", "0.136dB/fs", "0.272dB/fs", +}; + +static const struct soc_enum da7218_alc_anticlip_step = + SOC_ENUM_SINGLE(DA7218_ALC_ANTICLIP_CTRL, + DA7218_ALC_ANTICLIP_STEP_SHIFT, + DA7218_ALC_ANTICLIP_STEP_MAX, + da7218_alc_anticlip_step_txt); + +static const char * const da7218_integ_rate_txt[] = { + "1/4", "1/16", "1/256", "1/65536" +}; + +static const struct soc_enum da7218_integ_attack_rate = + SOC_ENUM_SINGLE(DA7218_ENV_TRACK_CTRL, DA7218_INTEG_ATTACK_SHIFT, + DA7218_INTEG_MAX, da7218_integ_rate_txt); + +static const struct soc_enum da7218_integ_release_rate = + SOC_ENUM_SINGLE(DA7218_ENV_TRACK_CTRL, DA7218_INTEG_RELEASE_SHIFT, + DA7218_INTEG_MAX, da7218_integ_rate_txt); + +/* Input/Output Enums */ +static const char * const da7218_gain_ramp_rate_txt[] = { + "Nominal Rate * 8", "Nominal Rate", "Nominal Rate / 8", + "Nominal Rate / 16", +}; + +static const struct soc_enum da7218_gain_ramp_rate = + SOC_ENUM_SINGLE(DA7218_GAIN_RAMP_CTRL, DA7218_GAIN_RAMP_RATE_SHIFT, + DA7218_GAIN_RAMP_RATE_MAX, da7218_gain_ramp_rate_txt); + +static const char * const da7218_hpf_mode_txt[] = { + "Disabled", "Audio", "Voice", +}; + +static const unsigned int da7218_hpf_mode_val[] = { + DA7218_HPF_DISABLED, DA7218_HPF_AUDIO_EN, DA7218_HPF_VOICE_EN, +}; + +static const struct soc_enum da7218_in1_hpf_mode = + SOC_VALUE_ENUM_SINGLE(DA7218_IN_1_HPF_FILTER_CTRL, + DA7218_HPF_MODE_SHIFT, DA7218_HPF_MODE_MASK, + DA7218_HPF_MODE_MAX, da7218_hpf_mode_txt, + da7218_hpf_mode_val); + +static const struct soc_enum da7218_in2_hpf_mode = + SOC_VALUE_ENUM_SINGLE(DA7218_IN_2_HPF_FILTER_CTRL, + DA7218_HPF_MODE_SHIFT, DA7218_HPF_MODE_MASK, + DA7218_HPF_MODE_MAX, da7218_hpf_mode_txt, + da7218_hpf_mode_val); + +static const struct soc_enum da7218_out1_hpf_mode = + SOC_VALUE_ENUM_SINGLE(DA7218_OUT_1_HPF_FILTER_CTRL, + DA7218_HPF_MODE_SHIFT, DA7218_HPF_MODE_MASK, + DA7218_HPF_MODE_MAX, da7218_hpf_mode_txt, + da7218_hpf_mode_val); + +static const char * const da7218_audio_hpf_corner_txt[] = { + "2Hz", "4Hz", "8Hz", "16Hz", +}; + +static const struct soc_enum da7218_in1_audio_hpf_corner = + SOC_ENUM_SINGLE(DA7218_IN_1_HPF_FILTER_CTRL, + DA7218_IN_1_AUDIO_HPF_CORNER_SHIFT, + DA7218_AUDIO_HPF_CORNER_MAX, + da7218_audio_hpf_corner_txt); + +static const struct soc_enum da7218_in2_audio_hpf_corner = + SOC_ENUM_SINGLE(DA7218_IN_2_HPF_FILTER_CTRL, + DA7218_IN_2_AUDIO_HPF_CORNER_SHIFT, + DA7218_AUDIO_HPF_CORNER_MAX, + da7218_audio_hpf_corner_txt); + +static const struct soc_enum da7218_out1_audio_hpf_corner = + SOC_ENUM_SINGLE(DA7218_OUT_1_HPF_FILTER_CTRL, + DA7218_OUT_1_AUDIO_HPF_CORNER_SHIFT, + DA7218_AUDIO_HPF_CORNER_MAX, + da7218_audio_hpf_corner_txt); + +static const char * const da7218_voice_hpf_corner_txt[] = { + "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz", +}; + +static const struct soc_enum da7218_in1_voice_hpf_corner = + SOC_ENUM_SINGLE(DA7218_IN_1_HPF_FILTER_CTRL, + DA7218_IN_1_VOICE_HPF_CORNER_SHIFT, + DA7218_VOICE_HPF_CORNER_MAX, + da7218_voice_hpf_corner_txt); + +static const struct soc_enum da7218_in2_voice_hpf_corner = + SOC_ENUM_SINGLE(DA7218_IN_2_HPF_FILTER_CTRL, + DA7218_IN_2_VOICE_HPF_CORNER_SHIFT, + DA7218_VOICE_HPF_CORNER_MAX, + da7218_voice_hpf_corner_txt); + +static const struct soc_enum da7218_out1_voice_hpf_corner = + SOC_ENUM_SINGLE(DA7218_OUT_1_HPF_FILTER_CTRL, + DA7218_OUT_1_VOICE_HPF_CORNER_SHIFT, + DA7218_VOICE_HPF_CORNER_MAX, + da7218_voice_hpf_corner_txt); + +static const char * const da7218_tonegen_dtmf_key_txt[] = { + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", + "*", "#" +}; + +static const struct soc_enum da7218_tonegen_dtmf_key = + SOC_ENUM_SINGLE(DA7218_TONE_GEN_CFG1, DA7218_DTMF_REG_SHIFT, + DA7218_DTMF_REG_MAX, da7218_tonegen_dtmf_key_txt); + +static const char * const da7218_tonegen_swg_sel_txt[] = { + "Sum", "SWG1", "SWG2", "SWG1_1-Cos" +}; + +static const struct soc_enum da7218_tonegen_swg_sel = + SOC_ENUM_SINGLE(DA7218_TONE_GEN_CFG2, DA7218_SWG_SEL_SHIFT, + DA7218_SWG_SEL_MAX, da7218_tonegen_swg_sel_txt); + +/* Output Enums */ +static const char * const da7218_dgs_rise_coeff_txt[] = { + "1/1", "1/16", "1/64", "1/256", "1/1024", "1/4096", "1/16384", +}; + +static const struct soc_enum da7218_dgs_rise_coeff = + SOC_ENUM_SINGLE(DA7218_DGS_RISE_FALL, DA7218_DGS_RISE_COEFF_SHIFT, + DA7218_DGS_RISE_COEFF_MAX, da7218_dgs_rise_coeff_txt); + +static const char * const da7218_dgs_fall_coeff_txt[] = { + "1/4", "1/16", "1/64", "1/256", "1/1024", "1/4096", "1/16384", "1/65536", +}; + +static const struct soc_enum da7218_dgs_fall_coeff = + SOC_ENUM_SINGLE(DA7218_DGS_RISE_FALL, DA7218_DGS_FALL_COEFF_SHIFT, + DA7218_DGS_FALL_COEFF_MAX, da7218_dgs_fall_coeff_txt); + +static const char * const da7218_dac_ng_setup_time_txt[] = { + "256 Samples", "512 Samples", "1024 Samples", "2048 Samples" +}; + +static const struct soc_enum da7218_dac_ng_setup_time = + SOC_ENUM_SINGLE(DA7218_DAC_NG_SETUP_TIME, + DA7218_DAC_NG_SETUP_TIME_SHIFT, + DA7218_DAC_NG_SETUP_TIME_MAX, + da7218_dac_ng_setup_time_txt); + +static const char * const da7218_dac_ng_rampup_txt[] = { + "0.22ms/dB", "0.0138ms/dB" +}; + +static const struct soc_enum da7218_dac_ng_rampup_rate = + SOC_ENUM_SINGLE(DA7218_DAC_NG_SETUP_TIME, + DA7218_DAC_NG_RAMPUP_RATE_SHIFT, + DA7218_DAC_NG_RAMPUP_RATE_MAX, + da7218_dac_ng_rampup_txt); + +static const char * const da7218_dac_ng_rampdown_txt[] = { + "0.88ms/dB", "14.08ms/dB" +}; + +static const struct soc_enum da7218_dac_ng_rampdown_rate = + SOC_ENUM_SINGLE(DA7218_DAC_NG_SETUP_TIME, + DA7218_DAC_NG_RAMPDN_RATE_SHIFT, + DA7218_DAC_NG_RAMPDN_RATE_MAX, + da7218_dac_ng_rampdown_txt); + +static const char * const da7218_cp_mchange_txt[] = { + "Largest Volume", "DAC Volume", "Signal Magnitude" +}; + +static const unsigned int da7218_cp_mchange_val[] = { + DA7218_CP_MCHANGE_LARGEST_VOL, DA7218_CP_MCHANGE_DAC_VOL, + DA7218_CP_MCHANGE_SIG_MAG +}; + +static const struct soc_enum da7218_cp_mchange = + SOC_VALUE_ENUM_SINGLE(DA7218_CP_CTRL, DA7218_CP_MCHANGE_SHIFT, + DA7218_CP_MCHANGE_REL_MASK, DA7218_CP_MCHANGE_MAX, + da7218_cp_mchange_txt, da7218_cp_mchange_val); + +static const char * const da7218_cp_fcontrol_txt[] = { + "1MHz", "500KHz", "250KHz", "125KHz", "63KHz", "0KHz" +}; + +static const struct soc_enum da7218_cp_fcontrol = + SOC_ENUM_SINGLE(DA7218_CP_DELAY, DA7218_CP_FCONTROL_SHIFT, + DA7218_CP_FCONTROL_MAX, da7218_cp_fcontrol_txt); + +static const char * const da7218_cp_tau_delay_txt[] = { + "0ms", "2ms", "4ms", "16ms", "64ms", "128ms", "256ms", "512ms" +}; + +static const struct soc_enum da7218_cp_tau_delay = + SOC_ENUM_SINGLE(DA7218_CP_DELAY, DA7218_CP_TAU_DELAY_SHIFT, + DA7218_CP_TAU_DELAY_MAX, da7218_cp_tau_delay_txt); + +/* + * Control Functions + */ + +/* ALC */ +static void da7218_alc_calib(struct snd_soc_codec *codec) +{ + u8 mic_1_ctrl, mic_2_ctrl; + u8 mixin_1_ctrl, mixin_2_ctrl; + u8 in_1l_filt_ctrl, in_1r_filt_ctrl, in_2l_filt_ctrl, in_2r_filt_ctrl; + u8 in_1_hpf_ctrl, in_2_hpf_ctrl; + u8 calib_ctrl; + int i = 0; + bool calibrated = false; + + /* Save current state of MIC control registers */ + mic_1_ctrl = snd_soc_read(codec, DA7218_MIC_1_CTRL); + mic_2_ctrl = snd_soc_read(codec, DA7218_MIC_2_CTRL); + + /* Save current state of input mixer control registers */ + mixin_1_ctrl = snd_soc_read(codec, DA7218_MIXIN_1_CTRL); + mixin_2_ctrl = snd_soc_read(codec, DA7218_MIXIN_2_CTRL); + + /* Save current state of input filter control registers */ + in_1l_filt_ctrl = snd_soc_read(codec, DA7218_IN_1L_FILTER_CTRL); + in_1r_filt_ctrl = snd_soc_read(codec, DA7218_IN_1R_FILTER_CTRL); + in_2l_filt_ctrl = snd_soc_read(codec, DA7218_IN_2L_FILTER_CTRL); + in_2r_filt_ctrl = snd_soc_read(codec, DA7218_IN_2R_FILTER_CTRL); + + /* Save current state of input HPF control registers */ + in_1_hpf_ctrl = snd_soc_read(codec, DA7218_IN_1_HPF_FILTER_CTRL); + in_2_hpf_ctrl = snd_soc_read(codec, DA7218_IN_2_HPF_FILTER_CTRL); + + /* Enable then Mute MIC PGAs */ + snd_soc_update_bits(codec, DA7218_MIC_1_CTRL, DA7218_MIC_1_AMP_EN_MASK, + DA7218_MIC_1_AMP_EN_MASK); + snd_soc_update_bits(codec, DA7218_MIC_2_CTRL, DA7218_MIC_2_AMP_EN_MASK, + DA7218_MIC_2_AMP_EN_MASK); + snd_soc_update_bits(codec, DA7218_MIC_1_CTRL, + DA7218_MIC_1_AMP_MUTE_EN_MASK, + DA7218_MIC_1_AMP_MUTE_EN_MASK); + snd_soc_update_bits(codec, DA7218_MIC_2_CTRL, + DA7218_MIC_2_AMP_MUTE_EN_MASK, + DA7218_MIC_2_AMP_MUTE_EN_MASK); + + /* Enable input mixers unmuted */ + snd_soc_update_bits(codec, DA7218_MIXIN_1_CTRL, + DA7218_MIXIN_1_AMP_EN_MASK | + DA7218_MIXIN_1_AMP_MUTE_EN_MASK, + DA7218_MIXIN_1_AMP_EN_MASK); + snd_soc_update_bits(codec, DA7218_MIXIN_2_CTRL, + DA7218_MIXIN_2_AMP_EN_MASK | + DA7218_MIXIN_2_AMP_MUTE_EN_MASK, + DA7218_MIXIN_2_AMP_EN_MASK); + + /* Enable input filters unmuted */ + snd_soc_update_bits(codec, DA7218_IN_1L_FILTER_CTRL, + DA7218_IN_1L_FILTER_EN_MASK | + DA7218_IN_1L_MUTE_EN_MASK, + DA7218_IN_1L_FILTER_EN_MASK); + snd_soc_update_bits(codec, DA7218_IN_1R_FILTER_CTRL, + DA7218_IN_1R_FILTER_EN_MASK | + DA7218_IN_1R_MUTE_EN_MASK, + DA7218_IN_1R_FILTER_EN_MASK); + snd_soc_update_bits(codec, DA7218_IN_2L_FILTER_CTRL, + DA7218_IN_2L_FILTER_EN_MASK | + DA7218_IN_2L_MUTE_EN_MASK, + DA7218_IN_2L_FILTER_EN_MASK); + snd_soc_update_bits(codec, DA7218_IN_2R_FILTER_CTRL, + DA7218_IN_2R_FILTER_EN_MASK | + DA7218_IN_2R_MUTE_EN_MASK, + DA7218_IN_2R_FILTER_EN_MASK); + + /* + * Make sure input HPFs voice mode is disabled, otherwise for sampling + * rates above 32KHz the ADC signals will be stopped and will cause + * calibration to lock up. + */ + snd_soc_update_bits(codec, DA7218_IN_1_HPF_FILTER_CTRL, + DA7218_IN_1_VOICE_EN_MASK, 0); + snd_soc_update_bits(codec, DA7218_IN_2_HPF_FILTER_CTRL, + DA7218_IN_2_VOICE_EN_MASK, 0); + + /* Perform auto calibration */ + snd_soc_update_bits(codec, DA7218_CALIB_CTRL, DA7218_CALIB_AUTO_EN_MASK, + DA7218_CALIB_AUTO_EN_MASK); + do { + calib_ctrl = snd_soc_read(codec, DA7218_CALIB_CTRL); + if (calib_ctrl & DA7218_CALIB_AUTO_EN_MASK) { + ++i; + usleep_range(DA7218_ALC_CALIB_DELAY_MIN, + DA7218_ALC_CALIB_DELAY_MAX); + } else { + calibrated = true; + } + + } while ((i < DA7218_ALC_CALIB_MAX_TRIES) && (!calibrated)); + + /* If auto calibration fails, disable DC offset, hybrid ALC */ + if ((!calibrated) || (calib_ctrl & DA7218_CALIB_OVERFLOW_MASK)) { + dev_warn(codec->dev, + "ALC auto calibration failed - %s\n", + (calibrated) ? "overflow" : "timeout"); + snd_soc_update_bits(codec, DA7218_CALIB_CTRL, + DA7218_CALIB_OFFSET_EN_MASK, 0); + snd_soc_update_bits(codec, DA7218_ALC_CTRL1, + DA7218_ALC_SYNC_MODE_MASK, 0); + + } else { + /* Enable DC offset cancellation */ + snd_soc_update_bits(codec, DA7218_CALIB_CTRL, + DA7218_CALIB_OFFSET_EN_MASK, + DA7218_CALIB_OFFSET_EN_MASK); + + /* Enable ALC hybrid mode */ + snd_soc_update_bits(codec, DA7218_ALC_CTRL1, + DA7218_ALC_SYNC_MODE_MASK, + DA7218_ALC_SYNC_MODE_CH1 | + DA7218_ALC_SYNC_MODE_CH2); + } + + /* Restore input HPF control registers to original states */ + snd_soc_write(codec, DA7218_IN_1_HPF_FILTER_CTRL, in_1_hpf_ctrl); + snd_soc_write(codec, DA7218_IN_2_HPF_FILTER_CTRL, in_2_hpf_ctrl); + + /* Restore input filter control registers to original states */ + snd_soc_write(codec, DA7218_IN_1L_FILTER_CTRL, in_1l_filt_ctrl); + snd_soc_write(codec, DA7218_IN_1R_FILTER_CTRL, in_1r_filt_ctrl); + snd_soc_write(codec, DA7218_IN_2L_FILTER_CTRL, in_2l_filt_ctrl); + snd_soc_write(codec, DA7218_IN_2R_FILTER_CTRL, in_2r_filt_ctrl); + + /* Restore input mixer control registers to original state */ + snd_soc_write(codec, DA7218_MIXIN_1_CTRL, mixin_1_ctrl); + snd_soc_write(codec, DA7218_MIXIN_2_CTRL, mixin_2_ctrl); + + /* Restore MIC control registers to original states */ + snd_soc_write(codec, DA7218_MIC_1_CTRL, mic_1_ctrl); + snd_soc_write(codec, DA7218_MIC_2_CTRL, mic_2_ctrl); +} + +static int da7218_mixin_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + + /* + * If ALC in operation and value of control has been updated, + * make sure calibrated offsets are updated. + */ + if ((ret == 1) && (da7218->alc_en)) + da7218_alc_calib(codec); + + return ret; +} + +static int da7218_alc_sw_put(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_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + unsigned int lvalue = ucontrol->value.integer.value[0]; + unsigned int rvalue = ucontrol->value.integer.value[1]; + unsigned int lshift = mc->shift; + unsigned int rshift = mc->rshift; + unsigned int mask = (mc->max << lshift) | (mc->max << rshift); + + /* Force ALC offset calibration if enabling ALC */ + if ((lvalue || rvalue) && (!da7218->alc_en)) + da7218_alc_calib(codec); + + /* Update bits to detail which channels are enabled/disabled */ + da7218->alc_en &= ~mask; + da7218->alc_en |= (lvalue << lshift) | (rvalue << rshift); + + return snd_soc_put_volsw(kcontrol, ucontrol); +} + +/* ToneGen */ +static int da7218_tonegen_freq_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mixer_ctrl = + (struct soc_mixer_control *) kcontrol->private_value; + unsigned int reg = mixer_ctrl->reg; + u16 val; + int ret; + + /* + * Frequency value spans two 8-bit registers, lower then upper byte. + * Therefore we need to convert to host endianness here. + */ + ret = regmap_raw_read(da7218->regmap, reg, &val, 2); + if (ret) + return ret; + + ucontrol->value.integer.value[0] = le16_to_cpu(val); + + return 0; +} + +static int da7218_tonegen_freq_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mixer_ctrl = + (struct soc_mixer_control *) kcontrol->private_value; + unsigned int reg = mixer_ctrl->reg; + u16 val; + + /* + * Frequency value spans two 8-bit registers, lower then upper byte. + * Therefore we need to convert to little endian here to align with + * HW registers. + */ + val = cpu_to_le16(ucontrol->value.integer.value[0]); + + return regmap_raw_write(da7218->regmap, reg, &val, 2); +} + +static int da7218_mic_lvl_det_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mixer_ctrl = + (struct soc_mixer_control *) kcontrol->private_value; + unsigned int lvalue = ucontrol->value.integer.value[0]; + unsigned int rvalue = ucontrol->value.integer.value[1]; + unsigned int lshift = mixer_ctrl->shift; + unsigned int rshift = mixer_ctrl->rshift; + unsigned int mask = (mixer_ctrl->max << lshift) | + (mixer_ctrl->max << rshift); + da7218->mic_lvl_det_en &= ~mask; + da7218->mic_lvl_det_en |= (lvalue << lshift) | (rvalue << rshift); + + /* + * Here we only enable the feature on paths which are already + * powered. If a channel is enabled here for level detect, but that path + * isn't powered, then the channel will actually be enabled when we do + * power the path (IN_FILTER widget events). This handling avoids + * unwanted level detect events. + */ + return snd_soc_write(codec, mixer_ctrl->reg, + (da7218->in_filt_en & da7218->mic_lvl_det_en)); +} + +static int da7218_mic_lvl_det_sw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mixer_ctrl = + (struct soc_mixer_control *) kcontrol->private_value; + unsigned int lshift = mixer_ctrl->shift; + unsigned int rshift = mixer_ctrl->rshift; + unsigned int lmask = (mixer_ctrl->max << lshift); + unsigned int rmask = (mixer_ctrl->max << rshift); + + ucontrol->value.integer.value[0] = + (da7218->mic_lvl_det_en & lmask) >> lshift; + ucontrol->value.integer.value[1] = + (da7218->mic_lvl_det_en & rmask) >> rshift; + + return 0; +} + +static int da7218_biquad_coeff_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *) kcontrol->private_value; + + /* Determine which BiQuads we're setting based on size of config data */ + switch (bytes_ext->max) { + case DA7218_OUT_1_BIQ_5STAGE_CFG_SIZE: + memcpy(ucontrol->value.bytes.data, da7218->biq_5stage_coeff, + bytes_ext->max); + break; + case DA7218_SIDETONE_BIQ_3STAGE_CFG_SIZE: + memcpy(ucontrol->value.bytes.data, da7218->stbiq_3stage_coeff, + bytes_ext->max); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int da7218_biquad_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) |