18084945dSLadislav Michl // SPDX-License-Identifier: GPL-2.0 28084945dSLadislav Michl // 38084945dSLadislav Michl // MAX9867 ALSA SoC codec driver 48084945dSLadislav Michl // 58084945dSLadislav Michl // Copyright 2013-2015 Maxim Integrated Products 68084945dSLadislav Michl // Copyright 2018 Ladislav Michl <ladis@linux-mips.org> 78084945dSLadislav Michl // 8805d132dSanish kumar 9805d132dSanish kumar #include <linux/delay.h> 10805d132dSanish kumar #include <linux/i2c.h> 11805d132dSanish kumar #include <linux/module.h> 12805d132dSanish kumar #include <linux/regmap.h> 13805d132dSanish kumar #include <sound/pcm_params.h> 14805d132dSanish kumar #include <sound/soc.h> 15805d132dSanish kumar #include <sound/tlv.h> 16805d132dSanish kumar #include "max9867.h" 17805d132dSanish kumar 18a11ffbbaSPavel Dobias struct max9867_priv { 19a11ffbbaSPavel Dobias struct regmap *regmap; 20a11ffbbaSPavel Dobias const struct snd_pcm_hw_constraint_list *constraints; 21a11ffbbaSPavel Dobias unsigned int sysclk, pclk; 22a11ffbbaSPavel Dobias bool master, dsp_a; 23a11ffbbaSPavel Dobias unsigned int adc_dac_active; 24a11ffbbaSPavel Dobias }; 25a11ffbbaSPavel Dobias 26805d132dSanish kumar static const char *const max9867_spmode[] = { 27805d132dSanish kumar "Stereo Diff", "Mono Diff", 28805d132dSanish kumar "Stereo Cap", "Mono Cap", 29805d132dSanish kumar "Stereo Single", "Mono Single", 30805d132dSanish kumar "Stereo Single Fast", "Mono Single Fast" 31805d132dSanish kumar }; 32805d132dSanish kumar static const char *const max9867_filter_text[] = {"IIR", "FIR"}; 33805d132dSanish kumar 34af53d573SPavel Dobias static const char *const max9867_adc_dac_filter_text[] = { 35af53d573SPavel Dobias "Disabled", 36af53d573SPavel Dobias "Elliptical/16/256", 37af53d573SPavel Dobias "Butterworth/16/500", 38af53d573SPavel Dobias "Elliptical/8/256", 39af53d573SPavel Dobias "Butterworth/8/500", 40af53d573SPavel Dobias "Butterworth/8-24" 41af53d573SPavel Dobias }; 42af53d573SPavel Dobias 43a11ffbbaSPavel Dobias enum max9867_adc_dac { 44a11ffbbaSPavel Dobias MAX9867_ADC_LEFT, 45a11ffbbaSPavel Dobias MAX9867_ADC_RIGHT, 46a11ffbbaSPavel Dobias MAX9867_DAC_LEFT, 47a11ffbbaSPavel Dobias MAX9867_DAC_RIGHT, 48a11ffbbaSPavel Dobias }; 49a11ffbbaSPavel Dobias 50a11ffbbaSPavel Dobias static int max9867_adc_dac_event(struct snd_soc_dapm_widget *w, 51a11ffbbaSPavel Dobias struct snd_kcontrol *kcontrol, int event) 52a11ffbbaSPavel Dobias { 53a11ffbbaSPavel Dobias struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 54a11ffbbaSPavel Dobias struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); 55a11ffbbaSPavel Dobias enum max9867_adc_dac adc_dac; 56a11ffbbaSPavel Dobias 57a11ffbbaSPavel Dobias if (!strcmp(w->name, "ADCL")) 58a11ffbbaSPavel Dobias adc_dac = MAX9867_ADC_LEFT; 59a11ffbbaSPavel Dobias else if (!strcmp(w->name, "ADCR")) 60a11ffbbaSPavel Dobias adc_dac = MAX9867_ADC_RIGHT; 61a11ffbbaSPavel Dobias else if (!strcmp(w->name, "DACL")) 62a11ffbbaSPavel Dobias adc_dac = MAX9867_DAC_LEFT; 63a11ffbbaSPavel Dobias else if (!strcmp(w->name, "DACR")) 64a11ffbbaSPavel Dobias adc_dac = MAX9867_DAC_RIGHT; 65a11ffbbaSPavel Dobias else 66a11ffbbaSPavel Dobias return 0; 67a11ffbbaSPavel Dobias 68a11ffbbaSPavel Dobias if (SND_SOC_DAPM_EVENT_ON(event)) 69a11ffbbaSPavel Dobias max9867->adc_dac_active |= BIT(adc_dac); 70a11ffbbaSPavel Dobias else if (SND_SOC_DAPM_EVENT_OFF(event)) 71a11ffbbaSPavel Dobias max9867->adc_dac_active &= ~BIT(adc_dac); 72a11ffbbaSPavel Dobias 73a11ffbbaSPavel Dobias return 0; 74a11ffbbaSPavel Dobias } 75a11ffbbaSPavel Dobias 76a11ffbbaSPavel Dobias static int max9867_filter_get(struct snd_kcontrol *kcontrol, 77a11ffbbaSPavel Dobias struct snd_ctl_elem_value *ucontrol) 78a11ffbbaSPavel Dobias { 79a11ffbbaSPavel Dobias struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 80a11ffbbaSPavel Dobias struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); 81a11ffbbaSPavel Dobias unsigned int reg; 82a11ffbbaSPavel Dobias int ret; 83a11ffbbaSPavel Dobias 84a11ffbbaSPavel Dobias ret = regmap_read(max9867->regmap, MAX9867_CODECFLTR, ®); 85a11ffbbaSPavel Dobias if (ret) 86a11ffbbaSPavel Dobias return -EINVAL; 87a11ffbbaSPavel Dobias 88a11ffbbaSPavel Dobias if (reg & MAX9867_CODECFLTR_MODE) 89a11ffbbaSPavel Dobias ucontrol->value.enumerated.item[0] = 1; 90a11ffbbaSPavel Dobias else 91a11ffbbaSPavel Dobias ucontrol->value.enumerated.item[0] = 0; 92a11ffbbaSPavel Dobias 93a11ffbbaSPavel Dobias return 0; 94a11ffbbaSPavel Dobias } 95a11ffbbaSPavel Dobias 96a11ffbbaSPavel Dobias static int max9867_filter_set(struct snd_kcontrol *kcontrol, 97a11ffbbaSPavel Dobias struct snd_ctl_elem_value *ucontrol) 98a11ffbbaSPavel Dobias { 99a11ffbbaSPavel Dobias struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 100a11ffbbaSPavel Dobias struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); 101a11ffbbaSPavel Dobias unsigned int reg, mode = ucontrol->value.enumerated.item[0]; 102a11ffbbaSPavel Dobias int ret; 103a11ffbbaSPavel Dobias 104a11ffbbaSPavel Dobias if (mode > 1) 105a11ffbbaSPavel Dobias return -EINVAL; 106a11ffbbaSPavel Dobias 107a11ffbbaSPavel Dobias /* don't allow change if ADC/DAC active */ 108a11ffbbaSPavel Dobias if (max9867->adc_dac_active) 109a11ffbbaSPavel Dobias return -EBUSY; 110a11ffbbaSPavel Dobias 111a11ffbbaSPavel Dobias /* read current filter mode */ 112a11ffbbaSPavel Dobias ret = regmap_read(max9867->regmap, MAX9867_CODECFLTR, ®); 113a11ffbbaSPavel Dobias if (ret) 114a11ffbbaSPavel Dobias return -EINVAL; 115a11ffbbaSPavel Dobias 116a11ffbbaSPavel Dobias if (mode) 117a11ffbbaSPavel Dobias mode = MAX9867_CODECFLTR_MODE; 118a11ffbbaSPavel Dobias 119a11ffbbaSPavel Dobias /* check if change is needed */ 120a11ffbbaSPavel Dobias if ((reg & MAX9867_CODECFLTR_MODE) == mode) 121a11ffbbaSPavel Dobias return 0; 122a11ffbbaSPavel Dobias 123a11ffbbaSPavel Dobias /* shutdown codec before switching filter mode */ 124a11ffbbaSPavel Dobias regmap_update_bits(max9867->regmap, MAX9867_PWRMAN, 125a11ffbbaSPavel Dobias MAX9867_PWRMAN_SHDN, 0); 126a11ffbbaSPavel Dobias 127a11ffbbaSPavel Dobias /* switch filter mode */ 128a11ffbbaSPavel Dobias regmap_update_bits(max9867->regmap, MAX9867_CODECFLTR, 129a11ffbbaSPavel Dobias MAX9867_CODECFLTR_MODE, mode); 130a11ffbbaSPavel Dobias 131a11ffbbaSPavel Dobias /* out of shutdown now */ 132a11ffbbaSPavel Dobias regmap_update_bits(max9867->regmap, MAX9867_PWRMAN, 133a11ffbbaSPavel Dobias MAX9867_PWRMAN_SHDN, MAX9867_PWRMAN_SHDN); 134a11ffbbaSPavel Dobias 135a11ffbbaSPavel Dobias return 0; 136a11ffbbaSPavel Dobias } 137a11ffbbaSPavel Dobias 138a11ffbbaSPavel Dobias static SOC_ENUM_SINGLE_EXT_DECL(max9867_filter, max9867_filter_text); 139af53d573SPavel Dobias static SOC_ENUM_SINGLE_DECL(max9867_dac_filter, MAX9867_CODECFLTR, 0, 140af53d573SPavel Dobias max9867_adc_dac_filter_text); 141af53d573SPavel Dobias static SOC_ENUM_SINGLE_DECL(max9867_adc_filter, MAX9867_CODECFLTR, 4, 142af53d573SPavel Dobias max9867_adc_dac_filter_text); 143805d132dSanish kumar static SOC_ENUM_SINGLE_DECL(max9867_spkmode, MAX9867_MODECONFIG, 0, 144805d132dSanish kumar max9867_spmode); 145bc2610a6SLadislav Michl static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(max9867_master_tlv, 146bc2610a6SLadislav Michl 0, 2, TLV_DB_SCALE_ITEM(-8600, 200, 1), 147bc2610a6SLadislav Michl 3, 17, TLV_DB_SCALE_ITEM(-7800, 400, 0), 148bc2610a6SLadislav Michl 18, 25, TLV_DB_SCALE_ITEM(-2000, 200, 0), 149bc2610a6SLadislav Michl 26, 34, TLV_DB_SCALE_ITEM( -500, 100, 0), 150bc2610a6SLadislav Michl 35, 40, TLV_DB_SCALE_ITEM( 350, 50, 0), 151bc2610a6SLadislav Michl ); 152bc2610a6SLadislav Michl static DECLARE_TLV_DB_SCALE(max9867_mic_tlv, 0, 100, 0); 153bc2610a6SLadislav Michl static DECLARE_TLV_DB_SCALE(max9867_line_tlv, -600, 200, 0); 154bc2610a6SLadislav Michl static DECLARE_TLV_DB_SCALE(max9867_adc_tlv, -1200, 100, 0); 155bc2610a6SLadislav Michl static DECLARE_TLV_DB_SCALE(max9867_dac_tlv, -1500, 100, 0); 156bc2610a6SLadislav Michl static DECLARE_TLV_DB_SCALE(max9867_dacboost_tlv, 0, 600, 0); 157bc2610a6SLadislav Michl static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(max9867_micboost_tlv, 158bc2610a6SLadislav Michl 0, 2, TLV_DB_SCALE_ITEM(-2000, 2000, 1), 159bc2610a6SLadislav Michl 3, 3, TLV_DB_SCALE_ITEM(3000, 0, 0), 160349fa18cSTakashi Sakamoto ); 161805d132dSanish kumar 162805d132dSanish kumar static const struct snd_kcontrol_new max9867_snd_controls[] = { 163bc2610a6SLadislav Michl SOC_DOUBLE_R_TLV("Master Playback Volume", MAX9867_LEFTVOL, 1648ba4dc3cSPavel Dobias MAX9867_RIGHTVOL, 0, 40, 1, max9867_master_tlv), 165bc2610a6SLadislav Michl SOC_DOUBLE_R_TLV("Line Capture Volume", MAX9867_LEFTLINELVL, 166bc2610a6SLadislav Michl MAX9867_RIGHTLINELVL, 0, 15, 1, max9867_line_tlv), 167bc2610a6SLadislav Michl SOC_DOUBLE_R_TLV("Mic Capture Volume", MAX9867_LEFTMICGAIN, 168bc2610a6SLadislav Michl MAX9867_RIGHTMICGAIN, 0, 20, 1, max9867_mic_tlv), 169bc2610a6SLadislav Michl SOC_DOUBLE_R_TLV("Mic Boost Capture Volume", MAX9867_LEFTMICGAIN, 1708ba4dc3cSPavel Dobias MAX9867_RIGHTMICGAIN, 5, 3, 0, max9867_micboost_tlv), 171bc2610a6SLadislav Michl SOC_SINGLE("Digital Sidetone Volume", MAX9867_SIDETONE, 0, 31, 1), 172bc2610a6SLadislav Michl SOC_SINGLE_TLV("Digital Playback Volume", MAX9867_DACLEVEL, 0, 15, 1, 173bc2610a6SLadislav Michl max9867_dac_tlv), 174bc2610a6SLadislav Michl SOC_SINGLE_TLV("Digital Boost Playback Volume", MAX9867_DACLEVEL, 4, 3, 0, 175bc2610a6SLadislav Michl max9867_dacboost_tlv), 17653a58bf9SPavel Dobias SOC_DOUBLE_TLV("Digital Capture Volume", MAX9867_ADCLEVEL, 4, 0, 15, 1, 177bc2610a6SLadislav Michl max9867_adc_tlv), 178805d132dSanish kumar SOC_ENUM("Speaker Mode", max9867_spkmode), 179805d132dSanish kumar SOC_SINGLE("Volume Smoothing Switch", MAX9867_MODECONFIG, 6, 1, 0), 180bc2610a6SLadislav Michl SOC_SINGLE("Line ZC Switch", MAX9867_MODECONFIG, 5, 1, 0), 181a11ffbbaSPavel Dobias SOC_ENUM_EXT("DSP Filter", max9867_filter, max9867_filter_get, max9867_filter_set), 182af53d573SPavel Dobias SOC_ENUM("ADC Filter", max9867_adc_filter), 183af53d573SPavel Dobias SOC_ENUM("DAC Filter", max9867_dac_filter), 18480b9fa4dSPavel Dobias SOC_SINGLE("Mono Playback Switch", MAX9867_IFC1B, 3, 1, 0), 185805d132dSanish kumar }; 186805d132dSanish kumar 187bc2610a6SLadislav Michl /* Input mixer */ 188bc2610a6SLadislav Michl static const struct snd_kcontrol_new max9867_input_mixer_controls[] = { 189bc2610a6SLadislav Michl SOC_DAPM_DOUBLE("Line Capture Switch", MAX9867_INPUTCONFIG, 7, 5, 1, 0), 190bc2610a6SLadislav Michl SOC_DAPM_DOUBLE("Mic Capture Switch", MAX9867_INPUTCONFIG, 6, 4, 1, 0), 191bc2610a6SLadislav Michl }; 192805d132dSanish kumar 193bc2610a6SLadislav Michl /* Output mixer */ 194bc2610a6SLadislav Michl static const struct snd_kcontrol_new max9867_output_mixer_controls[] = { 195bc2610a6SLadislav Michl SOC_DAPM_DOUBLE_R("Line Bypass Switch", 196bc2610a6SLadislav Michl MAX9867_LEFTLINELVL, MAX9867_RIGHTLINELVL, 6, 1, 1), 197bc2610a6SLadislav Michl }; 198805d132dSanish kumar 199bc2610a6SLadislav Michl /* Sidetone mixer */ 200bc2610a6SLadislav Michl static const struct snd_kcontrol_new max9867_sidetone_mixer_controls[] = { 201bc2610a6SLadislav Michl SOC_DAPM_DOUBLE("Sidetone Switch", MAX9867_SIDETONE, 6, 7, 1, 0), 202bc2610a6SLadislav Michl }; 203805d132dSanish kumar 204bc2610a6SLadislav Michl /* Line out switch */ 205bc2610a6SLadislav Michl static const struct snd_kcontrol_new max9867_line_out_control = 206bc2610a6SLadislav Michl SOC_DAPM_DOUBLE_R("Switch", 207bc2610a6SLadislav Michl MAX9867_LEFTVOL, MAX9867_RIGHTVOL, 6, 1, 1); 208bc2610a6SLadislav Michl 209980b63f8SPavel Dobias /* DMIC mux */ 210980b63f8SPavel Dobias static const char *const dmic_mux_text[] = { 211980b63f8SPavel Dobias "ADC", "DMIC" 212980b63f8SPavel Dobias }; 213980b63f8SPavel Dobias static SOC_ENUM_SINGLE_DECL(left_dmic_mux_enum, 214980b63f8SPavel Dobias MAX9867_MICCONFIG, 5, dmic_mux_text); 215980b63f8SPavel Dobias static SOC_ENUM_SINGLE_DECL(right_dmic_mux_enum, 216980b63f8SPavel Dobias MAX9867_MICCONFIG, 4, dmic_mux_text); 217980b63f8SPavel Dobias static const struct snd_kcontrol_new max9867_left_dmic_mux = 218980b63f8SPavel Dobias SOC_DAPM_ENUM("DMICL Mux", left_dmic_mux_enum); 219980b63f8SPavel Dobias static const struct snd_kcontrol_new max9867_right_dmic_mux = 220980b63f8SPavel Dobias SOC_DAPM_ENUM("DMICR Mux", right_dmic_mux_enum); 221805d132dSanish kumar 222805d132dSanish kumar static const struct snd_soc_dapm_widget max9867_dapm_widgets[] = { 223bc2610a6SLadislav Michl SND_SOC_DAPM_INPUT("MICL"), 224bc2610a6SLadislav Michl SND_SOC_DAPM_INPUT("MICR"), 225980b63f8SPavel Dobias SND_SOC_DAPM_INPUT("DMICL"), 226980b63f8SPavel Dobias SND_SOC_DAPM_INPUT("DMICR"), 227bc2610a6SLadislav Michl SND_SOC_DAPM_INPUT("LINL"), 228bc2610a6SLadislav Michl SND_SOC_DAPM_INPUT("LINR"), 229805d132dSanish kumar 23029c859dfSPavel Dobias SND_SOC_DAPM_PGA("Left Line Input", SND_SOC_NOPM, 0, 0, NULL, 0), 23129c859dfSPavel Dobias SND_SOC_DAPM_PGA("Right Line Input", SND_SOC_NOPM, 0, 0, NULL, 0), 232bc2610a6SLadislav Michl SND_SOC_DAPM_MIXER_NAMED_CTL("Input Mixer", SND_SOC_NOPM, 0, 0, 233bc2610a6SLadislav Michl max9867_input_mixer_controls, 234bc2610a6SLadislav Michl ARRAY_SIZE(max9867_input_mixer_controls)), 235980b63f8SPavel Dobias SND_SOC_DAPM_MUX("DMICL Mux", SND_SOC_NOPM, 0, 0, 236980b63f8SPavel Dobias &max9867_left_dmic_mux), 237980b63f8SPavel Dobias SND_SOC_DAPM_MUX("DMICR Mux", SND_SOC_NOPM, 0, 0, 238980b63f8SPavel Dobias &max9867_right_dmic_mux), 239a11ffbbaSPavel Dobias SND_SOC_DAPM_ADC_E("ADCL", "HiFi Capture", SND_SOC_NOPM, 0, 0, 240a11ffbbaSPavel Dobias max9867_adc_dac_event, 241a11ffbbaSPavel Dobias SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), 242a11ffbbaSPavel Dobias SND_SOC_DAPM_ADC_E("ADCR", "HiFi Capture", SND_SOC_NOPM, 0, 0, 243a11ffbbaSPavel Dobias max9867_adc_dac_event, 244a11ffbbaSPavel Dobias SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), 245805d132dSanish kumar 246bc2610a6SLadislav Michl SND_SOC_DAPM_MIXER("Digital", SND_SOC_NOPM, 0, 0, 247bc2610a6SLadislav Michl max9867_sidetone_mixer_controls, 248bc2610a6SLadislav Michl ARRAY_SIZE(max9867_sidetone_mixer_controls)), 249bc2610a6SLadislav Michl SND_SOC_DAPM_MIXER_NAMED_CTL("Output Mixer", SND_SOC_NOPM, 0, 0, 250bc2610a6SLadislav Michl max9867_output_mixer_controls, 251bc2610a6SLadislav Michl ARRAY_SIZE(max9867_output_mixer_controls)), 252a11ffbbaSPavel Dobias SND_SOC_DAPM_DAC_E("DACL", "HiFi Playback", SND_SOC_NOPM, 0, 0, 253a11ffbbaSPavel Dobias max9867_adc_dac_event, 254a11ffbbaSPavel Dobias SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), 255a11ffbbaSPavel Dobias SND_SOC_DAPM_DAC_E("DACR", "HiFi Playback", SND_SOC_NOPM, 0, 0, 256a11ffbbaSPavel Dobias max9867_adc_dac_event, 257a11ffbbaSPavel Dobias SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), 258bc2610a6SLadislav Michl SND_SOC_DAPM_SWITCH("Master Playback", SND_SOC_NOPM, 0, 0, 259bc2610a6SLadislav Michl &max9867_line_out_control), 260bc2610a6SLadislav Michl SND_SOC_DAPM_OUTPUT("LOUT"), 261bc2610a6SLadislav Michl SND_SOC_DAPM_OUTPUT("ROUT"), 262805d132dSanish kumar }; 263805d132dSanish kumar 264805d132dSanish kumar static const struct snd_soc_dapm_route max9867_audio_map[] = { 265bc2610a6SLadislav Michl {"Left Line Input", NULL, "LINL"}, 266bc2610a6SLadislav Michl {"Right Line Input", NULL, "LINR"}, 267bc2610a6SLadislav Michl {"Input Mixer", "Mic Capture Switch", "MICL"}, 268bc2610a6SLadislav Michl {"Input Mixer", "Mic Capture Switch", "MICR"}, 269bc2610a6SLadislav Michl {"Input Mixer", "Line Capture Switch", "Left Line Input"}, 270bc2610a6SLadislav Michl {"Input Mixer", "Line Capture Switch", "Right Line Input"}, 271980b63f8SPavel Dobias {"DMICL Mux", "DMIC", "DMICL"}, 272980b63f8SPavel Dobias {"DMICR Mux", "DMIC", "DMICR"}, 273980b63f8SPavel Dobias {"DMICL Mux", "ADC", "Input Mixer"}, 274980b63f8SPavel Dobias {"DMICR Mux", "ADC", "Input Mixer"}, 275980b63f8SPavel Dobias {"ADCL", NULL, "DMICL Mux"}, 276980b63f8SPavel Dobias {"ADCR", NULL, "DMICR Mux"}, 277805d132dSanish kumar 278bc2610a6SLadislav Michl {"Digital", "Sidetone Switch", "ADCL"}, 279bc2610a6SLadislav Michl {"Digital", "Sidetone Switch", "ADCR"}, 280bc2610a6SLadislav Michl {"DACL", NULL, "Digital"}, 281bc2610a6SLadislav Michl {"DACR", NULL, "Digital"}, 282bc2610a6SLadislav Michl 283bc2610a6SLadislav Michl {"Output Mixer", "Line Bypass Switch", "Left Line Input"}, 284bc2610a6SLadislav Michl {"Output Mixer", "Line Bypass Switch", "Right Line Input"}, 285bc2610a6SLadislav Michl {"Output Mixer", NULL, "DACL"}, 286bc2610a6SLadislav Michl {"Output Mixer", NULL, "DACR"}, 287bc2610a6SLadislav Michl {"Master Playback", "Switch", "Output Mixer"}, 288bc2610a6SLadislav Michl {"LOUT", NULL, "Master Playback"}, 289bc2610a6SLadislav Michl {"ROUT", NULL, "Master Playback"}, 290805d132dSanish kumar }; 291805d132dSanish kumar 292715ee191SLadislav Michl static const unsigned int max9867_rates_44k1[] = { 293715ee191SLadislav Michl 11025, 22050, 44100, 294805d132dSanish kumar }; 295805d132dSanish kumar 296715ee191SLadislav Michl static const struct snd_pcm_hw_constraint_list max9867_constraints_44k1 = { 297715ee191SLadislav Michl .list = max9867_rates_44k1, 298715ee191SLadislav Michl .count = ARRAY_SIZE(max9867_rates_44k1), 299805d132dSanish kumar }; 300805d132dSanish kumar 301715ee191SLadislav Michl static const unsigned int max9867_rates_48k[] = { 302715ee191SLadislav Michl 8000, 16000, 32000, 48000, 303715ee191SLadislav Michl }; 304715ee191SLadislav Michl 305715ee191SLadislav Michl static const struct snd_pcm_hw_constraint_list max9867_constraints_48k = { 306715ee191SLadislav Michl .list = max9867_rates_48k, 307715ee191SLadislav Michl .count = ARRAY_SIZE(max9867_rates_48k), 308715ee191SLadislav Michl }; 309715ee191SLadislav Michl 310715ee191SLadislav Michl static int max9867_startup(struct snd_pcm_substream *substream, 311715ee191SLadislav Michl struct snd_soc_dai *dai) 312805d132dSanish kumar { 313715ee191SLadislav Michl struct max9867_priv *max9867 = 314715ee191SLadislav Michl snd_soc_component_get_drvdata(dai->component); 315805d132dSanish kumar 316715ee191SLadislav Michl if (max9867->constraints) 317715ee191SLadislav Michl snd_pcm_hw_constraint_list(substream->runtime, 0, 318715ee191SLadislav Michl SNDRV_PCM_HW_PARAM_RATE, max9867->constraints); 319805d132dSanish kumar 320715ee191SLadislav Michl return 0; 321805d132dSanish kumar } 322805d132dSanish kumar 323805d132dSanish kumar static int max9867_dai_hw_params(struct snd_pcm_substream *substream, 324805d132dSanish kumar struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) 325805d132dSanish kumar { 326715ee191SLadislav Michl int value; 327715ee191SLadislav Michl unsigned long int rate, ratio; 32891880a6bSKuninori Morimoto struct snd_soc_component *component = dai->component; 32991880a6bSKuninori Morimoto struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); 330715ee191SLadislav Michl unsigned int ni = DIV_ROUND_CLOSEST_ULL(96ULL * 0x10000 * params_rate(params), 331715ee191SLadislav Michl max9867->pclk); 332805d132dSanish kumar 333805d132dSanish kumar /* set up the ni value */ 334805d132dSanish kumar regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH, 335715ee191SLadislav Michl MAX9867_NI_HIGH_MASK, (0xFF00 & ni) >> 8); 336805d132dSanish kumar regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW, 337715ee191SLadislav Michl MAX9867_NI_LOW_MASK, 0x00FF & ni); 338715ee191SLadislav Michl if (max9867->master) { 339715ee191SLadislav Michl if (max9867->dsp_a) { 340715ee191SLadislav Michl value = MAX9867_IFC1B_48X; 341715ee191SLadislav Michl } else { 342715ee191SLadislav Michl rate = params_rate(params) * 2 * params_width(params); 343715ee191SLadislav Michl ratio = max9867->pclk / rate; 344715ee191SLadislav Michl switch (params_width(params)) { 345715ee191SLadislav Michl case 8: 346715ee191SLadislav Michl case 16: 347715ee191SLadislav Michl switch (ratio) { 348715ee191SLadislav Michl case 2: 349715ee191SLadislav Michl value = MAX9867_IFC1B_PCLK_2; 350715ee191SLadislav Michl break; 351715ee191SLadislav Michl case 4: 352715ee191SLadislav Michl value = MAX9867_IFC1B_PCLK_4; 353715ee191SLadislav Michl break; 354715ee191SLadislav Michl case 8: 355715ee191SLadislav Michl value = MAX9867_IFC1B_PCLK_8; 356715ee191SLadislav Michl break; 357715ee191SLadislav Michl case 16: 358715ee191SLadislav Michl value = MAX9867_IFC1B_PCLK_16; 359715ee191SLadislav Michl break; 360715ee191SLadislav Michl default: 361715ee191SLadislav Michl return -EINVAL; 362715ee191SLadislav Michl } 363715ee191SLadislav Michl break; 364715ee191SLadislav Michl case 24: 365715ee191SLadislav Michl value = MAX9867_IFC1B_48X; 366715ee191SLadislav Michl break; 367715ee191SLadislav Michl case 32: 368715ee191SLadislav Michl value = MAX9867_IFC1B_64X; 369715ee191SLadislav Michl break; 370715ee191SLadislav Michl default: 371715ee191SLadislav Michl return -EINVAL; 372715ee191SLadislav Michl } 373715ee191SLadislav Michl } 374715ee191SLadislav Michl regmap_update_bits(max9867->regmap, MAX9867_IFC1B, 375715ee191SLadislav Michl MAX9867_IFC1B_BCLK_MASK, value); 376715ee191SLadislav Michl } else { 377805d132dSanish kumar /* 378805d132dSanish kumar * digital pll locks on to any externally supplied LRCLK signal 379805d132dSanish kumar * and also enable rapid lock mode. 380805d132dSanish kumar */ 381805d132dSanish kumar regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW, 382805d132dSanish kumar MAX9867_RAPID_LOCK, MAX9867_RAPID_LOCK); 383805d132dSanish kumar regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH, 384805d132dSanish kumar MAX9867_PLL, MAX9867_PLL); 385805d132dSanish kumar } 386805d132dSanish kumar return 0; 387805d132dSanish kumar } 388805d132dSanish kumar 38918e028e2SKuninori Morimoto static int max9867_mute(struct snd_soc_dai *dai, int mute, int direction) 390805d132dSanish kumar { 39191880a6bSKuninori Morimoto struct snd_soc_component *component = dai->component; 39291880a6bSKuninori Morimoto struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); 393805d132dSanish kumar 394bc2610a6SLadislav Michl return regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL, 395bc2610a6SLadislav Michl 1 << 6, !!mute << 6); 396805d132dSanish kumar } 397805d132dSanish kumar 398805d132dSanish kumar static int max9867_set_dai_sysclk(struct snd_soc_dai *codec_dai, 399805d132dSanish kumar int clk_id, unsigned int freq, int dir) 400805d132dSanish kumar { 40191880a6bSKuninori Morimoto struct snd_soc_component *component = codec_dai->component; 40291880a6bSKuninori Morimoto struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); 403805d132dSanish kumar int value = 0; 404805d132dSanish kumar 405805d132dSanish kumar /* Set the prescaler based on the master clock frequency*/ 406805d132dSanish kumar if (freq >= 10000000 && freq <= 20000000) { 407805d132dSanish kumar value |= MAX9867_PSCLK_10_20; 408805d132dSanish kumar max9867->pclk = freq; 409805d132dSanish kumar } else if (freq >= 20000000 && freq <= 40000000) { 410805d132dSanish kumar value |= MAX9867_PSCLK_20_40; 411805d132dSanish kumar max9867->pclk = freq / 2; 412805d132dSanish kumar } else if (freq >= 40000000 && freq <= 60000000) { 413805d132dSanish kumar value |= MAX9867_PSCLK_40_60; 414805d132dSanish kumar max9867->pclk = freq / 4; 415805d132dSanish kumar } else { 4168b9c716aSLadislav Michl dev_err(component->dev, 4178b9c716aSLadislav Michl "Invalid clock frequency %uHz (required 10-60MHz)\n", 4188b9c716aSLadislav Michl freq); 419805d132dSanish kumar return -EINVAL; 420805d132dSanish kumar } 421715ee191SLadislav Michl if (freq % 48000 == 0) 422715ee191SLadislav Michl max9867->constraints = &max9867_constraints_48k; 423715ee191SLadislav Michl else if (freq % 44100 == 0) 424715ee191SLadislav Michl max9867->constraints = &max9867_constraints_44k1; 425715ee191SLadislav Michl else 426715ee191SLadislav Michl dev_warn(component->dev, 427715ee191SLadislav Michl "Unable to set exact rate with %uHz clock frequency\n", 428715ee191SLadislav Michl freq); 429805d132dSanish kumar max9867->sysclk = freq; 430715ee191SLadislav Michl value = value << MAX9867_PSCLK_SHIFT; 431805d132dSanish kumar /* exact integer mode is not supported */ 432805d132dSanish kumar value &= ~MAX9867_FREQ_MASK; 433805d132dSanish kumar regmap_update_bits(max9867->regmap, MAX9867_SYSCLK, 434805d132dSanish kumar MAX9867_PSCLK_MASK, value); 435805d132dSanish kumar return 0; 436805d132dSanish kumar } 437805d132dSanish kumar 438805d132dSanish kumar static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai, 439805d132dSanish kumar unsigned int fmt) 440805d132dSanish kumar { 44191880a6bSKuninori Morimoto struct snd_soc_component *component = codec_dai->component; 44291880a6bSKuninori Morimoto struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); 443715ee191SLadislav Michl u8 iface1A, iface1B; 444805d132dSanish kumar 445805d132dSanish kumar switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 446805d132dSanish kumar case SND_SOC_DAIFMT_CBM_CFM: 447715ee191SLadislav Michl max9867->master = true; 448715ee191SLadislav Michl iface1A = MAX9867_MASTER; 449715ee191SLadislav Michl iface1B = MAX9867_IFC1B_48X; 450805d132dSanish kumar break; 451805d132dSanish kumar case SND_SOC_DAIFMT_CBS_CFS: 452715ee191SLadislav Michl max9867->master = false; 453715ee191SLadislav Michl iface1A = iface1B = 0; 454805d132dSanish kumar break; 455805d132dSanish kumar default: 456805d132dSanish kumar return -EINVAL; 457805d132dSanish kumar } 458805d132dSanish kumar 45979e13974SLadislav Michl switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 46079e13974SLadislav Michl case SND_SOC_DAIFMT_I2S: 461715ee191SLadislav Michl max9867->dsp_a = false; 462805d132dSanish kumar iface1A |= MAX9867_I2S_DLY; 46379e13974SLadislav Michl break; 46479e13974SLadislav Michl case SND_SOC_DAIFMT_DSP_A: 465715ee191SLadislav Michl max9867->dsp_a = true; 46679e13974SLadislav Michl iface1A |= MAX9867_TDM_MODE | MAX9867_SDOUT_HIZ; 46779e13974SLadislav Michl break; 46879e13974SLadislav Michl default: 46979e13974SLadislav Michl return -EINVAL; 47079e13974SLadislav Michl } 471805d132dSanish kumar 472805d132dSanish kumar /* Clock inversion bits, BCI and WCI */ 473805d132dSanish kumar switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 474805d132dSanish kumar case SND_SOC_DAIFMT_NB_NF: 475805d132dSanish kumar break; 476805d132dSanish kumar case SND_SOC_DAIFMT_IB_IF: 477805d132dSanish kumar iface1A |= MAX9867_WCI_MODE | MAX9867_BCI_MODE; 478805d132dSanish kumar break; 479805d132dSanish kumar case SND_SOC_DAIFMT_IB_NF: 480805d132dSanish kumar iface1A |= MAX9867_BCI_MODE; 481805d132dSanish kumar break; 482805d132dSanish kumar case SND_SOC_DAIFMT_NB_IF: 483805d132dSanish kumar iface1A |= MAX9867_WCI_MODE; 484805d132dSanish kumar break; 485805d132dSanish kumar default: 486805d132dSanish kumar return -EINVAL; 487805d132dSanish kumar } 488805d132dSanish kumar 4899fe78b28SVinod Koul regmap_write(max9867->regmap, MAX9867_IFC1A, iface1A); 49080b9fa4dSPavel Dobias regmap_update_bits(max9867->regmap, MAX9867_IFC1B, 49180b9fa4dSPavel Dobias MAX9867_IFC1B_BCLK_MASK, iface1B); 492715ee191SLadislav Michl 493805d132dSanish kumar return 0; 494805d132dSanish kumar } 495805d132dSanish kumar 496eb59d73cSArvind Yadav static const struct snd_soc_dai_ops max9867_dai_ops = { 497805d132dSanish kumar .set_sysclk = max9867_set_dai_sysclk, 498715ee191SLadislav Michl .set_fmt = max9867_dai_set_fmt, 49918e028e2SKuninori Morimoto .mute_stream = max9867_mute, 500715ee191SLadislav Michl .startup = max9867_startup, 501805d132dSanish kumar .hw_params = max9867_dai_hw_params, 50218e028e2SKuninori Morimoto .no_capture_mute = 1, 503805d132dSanish kumar }; 504805d132dSanish kumar 505805d132dSanish kumar static struct snd_soc_dai_driver max9867_dai[] = { 506805d132dSanish kumar { 507805d132dSanish kumar .name = "max9867-aif1", 508805d132dSanish kumar .playback = { 509805d132dSanish kumar .stream_name = "HiFi Playback", 510e6ceb922SLadislav Michl .channels_min = 2, 511805d132dSanish kumar .channels_max = 2, 512715ee191SLadislav Michl .rates = SNDRV_PCM_RATE_8000_48000, 513715ee191SLadislav Michl .formats = SNDRV_PCM_FMTBIT_S16_LE, 514805d132dSanish kumar }, 515805d132dSanish kumar .capture = { 516805d132dSanish kumar .stream_name = "HiFi Capture", 517e6ceb922SLadislav Michl .channels_min = 2, 518805d132dSanish kumar .channels_max = 2, 519715ee191SLadislav Michl .rates = SNDRV_PCM_RATE_8000_48000, 520715ee191SLadislav Michl .formats = SNDRV_PCM_FMTBIT_S16_LE, 521805d132dSanish kumar }, 522805d132dSanish kumar .ops = &max9867_dai_ops, 523*cb40d1b4SKuninori Morimoto .symmetric_rate = 1, 524805d132dSanish kumar } 525805d132dSanish kumar }; 526805d132dSanish kumar 52729f58ff0SLadislav Michl #ifdef CONFIG_PM 52829f58ff0SLadislav Michl static int max9867_suspend(struct snd_soc_component *component) 529805d132dSanish kumar { 53029f58ff0SLadislav Michl snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); 531805d132dSanish kumar 532805d132dSanish kumar return 0; 533805d132dSanish kumar } 534805d132dSanish kumar 53529f58ff0SLadislav Michl static int max9867_resume(struct snd_soc_component *component) 536805d132dSanish kumar { 53729f58ff0SLadislav Michl snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); 538805d132dSanish kumar 539805d132dSanish kumar return 0; 540805d132dSanish kumar } 54129f58ff0SLadislav Michl #else 54229f58ff0SLadislav Michl #define max9867_suspend NULL 54329f58ff0SLadislav Michl #define max9867_resume NULL 544805d132dSanish kumar #endif 545805d132dSanish kumar 54629f58ff0SLadislav Michl static int max9867_set_bias_level(struct snd_soc_component *component, 54729f58ff0SLadislav Michl enum snd_soc_bias_level level) 54829f58ff0SLadislav Michl { 54929f58ff0SLadislav Michl int err; 55029f58ff0SLadislav Michl struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); 55129f58ff0SLadislav Michl 55229f58ff0SLadislav Michl switch (level) { 55329f58ff0SLadislav Michl case SND_SOC_BIAS_STANDBY: 55429f58ff0SLadislav Michl if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { 55529f58ff0SLadislav Michl err = regcache_sync(max9867->regmap); 55629f58ff0SLadislav Michl if (err) 55729f58ff0SLadislav Michl return err; 55829f58ff0SLadislav Michl 55929c859dfSPavel Dobias err = regmap_write(max9867->regmap, 56029c859dfSPavel Dobias MAX9867_PWRMAN, 0xff); 56129f58ff0SLadislav Michl if (err) 56229f58ff0SLadislav Michl return err; 56329f58ff0SLadislav Michl } 56429f58ff0SLadislav Michl break; 56529f58ff0SLadislav Michl case SND_SOC_BIAS_OFF: 56629c859dfSPavel Dobias err = regmap_write(max9867->regmap, MAX9867_PWRMAN, 0); 56729f58ff0SLadislav Michl if (err) 56829f58ff0SLadislav Michl return err; 56929f58ff0SLadislav Michl 57029f58ff0SLadislav Michl regcache_mark_dirty(max9867->regmap); 57129f58ff0SLadislav Michl break; 57229f58ff0SLadislav Michl default: 57329f58ff0SLadislav Michl break; 57429f58ff0SLadislav Michl } 57529f58ff0SLadislav Michl 57629f58ff0SLadislav Michl return 0; 57729f58ff0SLadislav Michl } 57829f58ff0SLadislav Michl 57991880a6bSKuninori Morimoto static const struct snd_soc_component_driver max9867_component = { 580805d132dSanish kumar .controls = max9867_snd_controls, 581805d132dSanish kumar .num_controls = ARRAY_SIZE(max9867_snd_controls), 582805d132dSanish kumar .dapm_routes = max9867_audio_map, 583805d132dSanish kumar .num_dapm_routes = ARRAY_SIZE(max9867_audio_map), 584805d132dSanish kumar .dapm_widgets = max9867_dapm_widgets, 585805d132dSanish kumar .num_dapm_widgets = ARRAY_SIZE(max9867_dapm_widgets), 58629f58ff0SLadislav Michl .suspend = max9867_suspend, 58729f58ff0SLadislav Michl .resume = max9867_resume, 58829f58ff0SLadislav Michl .set_bias_level = max9867_set_bias_level, 58991880a6bSKuninori Morimoto .idle_bias_on = 1, 59091880a6bSKuninori Morimoto .use_pmdown_time = 1, 59191880a6bSKuninori Morimoto .endianness = 1, 59291880a6bSKuninori Morimoto .non_legacy_dai_naming = 1, 593805d132dSanish kumar }; 594805d132dSanish kumar 595805d132dSanish kumar static bool max9867_volatile_register(struct device *dev, unsigned int reg) 596805d132dSanish kumar { 597805d132dSanish kumar switch (reg) { 598805d132dSanish kumar case MAX9867_STATUS: 599805d132dSanish kumar case MAX9867_JACKSTATUS: 600805d132dSanish kumar case MAX9867_AUXHIGH: 601805d132dSanish kumar case MAX9867_AUXLOW: 602805d132dSanish kumar return true; 603805d132dSanish kumar default: 604805d132dSanish kumar return false; 605805d132dSanish kumar } 606805d132dSanish kumar } 607805d132dSanish kumar 608250a99e7SAxel Lin static const struct regmap_config max9867_regmap = { 609805d132dSanish kumar .reg_bits = 8, 610805d132dSanish kumar .val_bits = 8, 611805d132dSanish kumar .max_register = MAX9867_REVISION, 612805d132dSanish kumar .volatile_reg = max9867_volatile_register, 613805d132dSanish kumar .cache_type = REGCACHE_RBTREE, 614805d132dSanish kumar }; 615805d132dSanish kumar 616805d132dSanish kumar static int max9867_i2c_probe(struct i2c_client *i2c, 617805d132dSanish kumar const struct i2c_device_id *id) 618805d132dSanish kumar { 619805d132dSanish kumar struct max9867_priv *max9867; 6208efc1afdSLadislav Michl int ret, reg; 621805d132dSanish kumar 622933662f2SLadislav Michl max9867 = devm_kzalloc(&i2c->dev, sizeof(*max9867), GFP_KERNEL); 623805d132dSanish kumar if (!max9867) 624805d132dSanish kumar return -ENOMEM; 625805d132dSanish kumar 626805d132dSanish kumar i2c_set_clientdata(i2c, max9867); 627805d132dSanish kumar max9867->regmap = devm_regmap_init_i2c(i2c, &max9867_regmap); 628805d132dSanish kumar if (IS_ERR(max9867->regmap)) { 629805d132dSanish kumar ret = PTR_ERR(max9867->regmap); 6308b9c716aSLadislav Michl dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret); 631805d132dSanish kumar return ret; 632805d132dSanish kumar } 633933662f2SLadislav Michl ret = regmap_read(max9867->regmap, MAX9867_REVISION, ®); 634805d132dSanish kumar if (ret < 0) { 635805d132dSanish kumar dev_err(&i2c->dev, "Failed to read: %d\n", ret); 636805d132dSanish kumar return ret; 637805d132dSanish kumar } 638805d132dSanish kumar dev_info(&i2c->dev, "device revision: %x\n", reg); 63991880a6bSKuninori Morimoto ret = devm_snd_soc_register_component(&i2c->dev, &max9867_component, 640805d132dSanish kumar max9867_dai, ARRAY_SIZE(max9867_dai)); 6418efc1afdSLadislav Michl if (ret < 0) 64291880a6bSKuninori Morimoto dev_err(&i2c->dev, "Failed to register component: %d\n", ret); 643805d132dSanish kumar return ret; 644805d132dSanish kumar } 645805d132dSanish kumar 646805d132dSanish kumar static const struct i2c_device_id max9867_i2c_id[] = { 647805d132dSanish kumar { "max9867", 0 }, 6481b36e4a2SAxel Lin { } 649805d132dSanish kumar }; 65056af0e4cSJavier Martinez Canillas MODULE_DEVICE_TABLE(i2c, max9867_i2c_id); 651805d132dSanish kumar 652682e2219SKrzysztof Kozlowski #ifdef CONFIG_OF 653805d132dSanish kumar static const struct of_device_id max9867_of_match[] = { 654805d132dSanish kumar { .compatible = "maxim,max9867", }, 655805d132dSanish kumar { } 656805d132dSanish kumar }; 65756af0e4cSJavier Martinez Canillas MODULE_DEVICE_TABLE(of, max9867_of_match); 658682e2219SKrzysztof Kozlowski #endif 659805d132dSanish kumar 660805d132dSanish kumar static struct i2c_driver max9867_i2c_driver = { 661805d132dSanish kumar .driver = { 662805d132dSanish kumar .name = "max9867", 663805d132dSanish kumar .of_match_table = of_match_ptr(max9867_of_match), 664805d132dSanish kumar }, 665805d132dSanish kumar .probe = max9867_i2c_probe, 666805d132dSanish kumar .id_table = max9867_i2c_id, 667805d132dSanish kumar }; 668805d132dSanish kumar 669805d132dSanish kumar module_i2c_driver(max9867_i2c_driver); 670805d132dSanish kumar 6718084945dSLadislav Michl MODULE_AUTHOR("Ladislav Michl <ladis@linux-mips.org>"); 6728084945dSLadislav Michl MODULE_DESCRIPTION("ASoC MAX9867 driver"); 673805d132dSanish kumar MODULE_LICENSE("GPL"); 674