1805d132dSanish kumar /* 2805d132dSanish kumar * max9867.c -- max9867 ALSA SoC Audio driver 3805d132dSanish kumar * 4805d132dSanish kumar * Copyright 2013-15 Maxim Integrated Products 5805d132dSanish kumar * 6805d132dSanish kumar * This program is free software; you can redistribute it and/or modify 7805d132dSanish kumar * it under the terms of the GNU General Public License version 2 as 8805d132dSanish kumar * published by the Free Software Foundation. 9805d132dSanish kumar */ 10805d132dSanish kumar 11805d132dSanish kumar #include <linux/delay.h> 12805d132dSanish kumar #include <linux/i2c.h> 13805d132dSanish kumar #include <linux/module.h> 14805d132dSanish kumar #include <linux/regmap.h> 15805d132dSanish kumar #include <sound/pcm_params.h> 16805d132dSanish kumar #include <sound/soc.h> 17805d132dSanish kumar #include <sound/tlv.h> 18805d132dSanish kumar #include "max9867.h" 19805d132dSanish kumar 20805d132dSanish kumar static const char *const max9867_spmode[] = { 21805d132dSanish kumar "Stereo Diff", "Mono Diff", 22805d132dSanish kumar "Stereo Cap", "Mono Cap", 23805d132dSanish kumar "Stereo Single", "Mono Single", 24805d132dSanish kumar "Stereo Single Fast", "Mono Single Fast" 25805d132dSanish kumar }; 26805d132dSanish kumar static const char *const max9867_sidetone_text[] = { 27805d132dSanish kumar "None", "Left", "Right", "LeftRight", "LeftRightDiv2", 28805d132dSanish kumar }; 29805d132dSanish kumar static const char *const max9867_filter_text[] = {"IIR", "FIR"}; 30805d132dSanish kumar 31805d132dSanish kumar static SOC_ENUM_SINGLE_DECL(max9867_filter, MAX9867_CODECFLTR, 7, 32805d132dSanish kumar max9867_filter_text); 33805d132dSanish kumar static SOC_ENUM_SINGLE_DECL(max9867_spkmode, MAX9867_MODECONFIG, 0, 34805d132dSanish kumar max9867_spmode); 35805d132dSanish kumar static SOC_ENUM_SINGLE_DECL(max9867_sidetone, MAX9867_DACGAIN, 6, 36805d132dSanish kumar max9867_sidetone_text); 37805d132dSanish kumar static DECLARE_TLV_DB_SCALE(max9860_capture_tlv, -600, 200, 0); 38805d132dSanish kumar static DECLARE_TLV_DB_SCALE(max9860_mic_tlv, 2000, 100, 1); 39805d132dSanish kumar static DECLARE_TLV_DB_SCALE(max9860_adc_left_tlv, -1200, 100, 1); 40805d132dSanish kumar static DECLARE_TLV_DB_SCALE(max9860_adc_right_tlv, -1200, 100, 1); 41349fa18cSTakashi Sakamoto static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(max98088_micboost_tlv, 42805d132dSanish kumar 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0), 43805d132dSanish kumar 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0), 44349fa18cSTakashi Sakamoto ); 45805d132dSanish kumar 46805d132dSanish kumar static const struct snd_kcontrol_new max9867_snd_controls[] = { 47805d132dSanish kumar SOC_DOUBLE_R("Master Playback Volume", MAX9867_LEFTVOL, 48805d132dSanish kumar MAX9867_RIGHTVOL, 0, 63, 1), 49805d132dSanish kumar SOC_DOUBLE_R_TLV("Capture Volume", MAX9867_LEFTMICGAIN, 50805d132dSanish kumar MAX9867_RIGHTMICGAIN, 51805d132dSanish kumar 0, 15, 1, max9860_capture_tlv), 52805d132dSanish kumar SOC_DOUBLE_R_TLV("Mic Volume", MAX9867_LEFTMICGAIN, 53805d132dSanish kumar MAX9867_RIGHTMICGAIN, 0, 31, 1, max9860_mic_tlv), 54805d132dSanish kumar SOC_DOUBLE_R_TLV("Mic Boost Volume", MAX9867_LEFTMICGAIN, 55805d132dSanish kumar MAX9867_RIGHTMICGAIN, 5, 3, 0, max98088_micboost_tlv), 56805d132dSanish kumar SOC_ENUM("Digital Sidetone Src", max9867_sidetone), 57805d132dSanish kumar SOC_SINGLE("Sidetone Volume", MAX9867_DACGAIN, 0, 31, 1), 58805d132dSanish kumar SOC_SINGLE("DAC Volume", MAX9867_DACLEVEL, 4, 3, 0), 59805d132dSanish kumar SOC_SINGLE("DAC Attenuation", MAX9867_DACLEVEL, 0, 15, 1), 60805d132dSanish kumar SOC_SINGLE_TLV("ADC Left Volume", MAX9867_ADCLEVEL, 61805d132dSanish kumar 4, 15, 1, max9860_adc_left_tlv), 62805d132dSanish kumar SOC_SINGLE_TLV("ADC Right Volume", MAX9867_ADCLEVEL, 63805d132dSanish kumar 0, 15, 1, max9860_adc_right_tlv), 64805d132dSanish kumar SOC_ENUM("Speaker Mode", max9867_spkmode), 65805d132dSanish kumar SOC_SINGLE("Volume Smoothing Switch", MAX9867_MODECONFIG, 6, 1, 0), 66805d132dSanish kumar SOC_SINGLE("ZCD Switch", MAX9867_MODECONFIG, 5, 1, 0), 67805d132dSanish kumar SOC_ENUM("DSP Filter", max9867_filter), 68805d132dSanish kumar }; 69805d132dSanish kumar 70805d132dSanish kumar static const char *const max9867_mux[] = {"None", "Mic", "Line", "Mic_Line"}; 71805d132dSanish kumar 72805d132dSanish kumar static SOC_ENUM_SINGLE_DECL(max9867_mux_enum, 73805d132dSanish kumar MAX9867_INPUTCONFIG, MAX9867_INPUT_SHIFT, 74805d132dSanish kumar max9867_mux); 75805d132dSanish kumar 76805d132dSanish kumar static const struct snd_kcontrol_new max9867_dapm_mux_controls = 77805d132dSanish kumar SOC_DAPM_ENUM("Route", max9867_mux_enum); 78805d132dSanish kumar 79805d132dSanish kumar static const struct snd_kcontrol_new max9867_left_dapm_control = 80805d132dSanish kumar SOC_DAPM_SINGLE("Switch", MAX9867_PWRMAN, 6, 1, 0); 81805d132dSanish kumar static const struct snd_kcontrol_new max9867_right_dapm_control = 82805d132dSanish kumar SOC_DAPM_SINGLE("Switch", MAX9867_PWRMAN, 5, 1, 0); 83805d132dSanish kumar static const struct snd_kcontrol_new max9867_line_dapm_control = 84805d132dSanish kumar SOC_DAPM_SINGLE("Switch", MAX9867_LEFTLINELVL, 6, 1, 1); 85805d132dSanish kumar 86805d132dSanish kumar static const struct snd_soc_dapm_widget max9867_dapm_widgets[] = { 87805d132dSanish kumar SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), 88805d132dSanish kumar SND_SOC_DAPM_DAC("Left DAC", NULL, MAX9867_PWRMAN, 3, 0), 89805d132dSanish kumar SND_SOC_DAPM_DAC("Right DAC", NULL, MAX9867_PWRMAN, 2, 0), 90805d132dSanish kumar SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), 91805d132dSanish kumar SND_SOC_DAPM_OUTPUT("HPOUT"), 92805d132dSanish kumar 93805d132dSanish kumar SND_SOC_DAPM_AIF_IN("DAI_IN", "HiFi Capture", 0, SND_SOC_NOPM, 0, 0), 94805d132dSanish kumar SND_SOC_DAPM_ADC("Left ADC", "HiFi Capture", MAX9867_PWRMAN, 1, 0), 95805d132dSanish kumar SND_SOC_DAPM_ADC("Right ADC", "HiFi Capture", MAX9867_PWRMAN, 0, 0), 96805d132dSanish kumar SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, 97805d132dSanish kumar &max9867_dapm_mux_controls), 98805d132dSanish kumar 99805d132dSanish kumar SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), 100805d132dSanish kumar SND_SOC_DAPM_SWITCH("Left Line", MAX9867_LEFTLINELVL, 6, 1, 101805d132dSanish kumar &max9867_left_dapm_control), 102805d132dSanish kumar SND_SOC_DAPM_SWITCH("Right Line", MAX9867_RIGTHLINELVL, 6, 1, 103805d132dSanish kumar &max9867_right_dapm_control), 104805d132dSanish kumar SND_SOC_DAPM_SWITCH("Line Mixer", SND_SOC_NOPM, 0, 0, 105805d132dSanish kumar &max9867_line_dapm_control), 106805d132dSanish kumar SND_SOC_DAPM_INPUT("LINE_IN"), 107805d132dSanish kumar }; 108805d132dSanish kumar 109805d132dSanish kumar static const struct snd_soc_dapm_route max9867_audio_map[] = { 110805d132dSanish kumar {"Left DAC", NULL, "DAI_OUT"}, 111805d132dSanish kumar {"Right DAC", NULL, "DAI_OUT"}, 112805d132dSanish kumar {"Output Mixer", NULL, "Left DAC"}, 113805d132dSanish kumar {"Output Mixer", NULL, "Right DAC"}, 114805d132dSanish kumar {"HPOUT", NULL, "Output Mixer"}, 115805d132dSanish kumar 116805d132dSanish kumar {"Left ADC", NULL, "DAI_IN"}, 117805d132dSanish kumar {"Right ADC", NULL, "DAI_IN"}, 118805d132dSanish kumar {"Input Mixer", NULL, "Left ADC"}, 119805d132dSanish kumar {"Input Mixer", NULL, "Right ADC"}, 120805d132dSanish kumar {"Input Mux", "Line", "Input Mixer"}, 121805d132dSanish kumar {"Input Mux", "Mic", "Input Mixer"}, 122805d132dSanish kumar {"Input Mux", "Mic_Line", "Input Mixer"}, 123805d132dSanish kumar {"Right Line", "Switch", "Input Mux"}, 124805d132dSanish kumar {"Left Line", "Switch", "Input Mux"}, 125805d132dSanish kumar {"LINE_IN", NULL, "Left Line"}, 126805d132dSanish kumar {"LINE_IN", NULL, "Right Line"}, 127805d132dSanish kumar }; 128805d132dSanish kumar 129*715ee191SLadislav Michl static const unsigned int max9867_rates_44k1[] = { 130*715ee191SLadislav Michl 11025, 22050, 44100, 131805d132dSanish kumar }; 132805d132dSanish kumar 133*715ee191SLadislav Michl static const struct snd_pcm_hw_constraint_list max9867_constraints_44k1 = { 134*715ee191SLadislav Michl .list = max9867_rates_44k1, 135*715ee191SLadislav Michl .count = ARRAY_SIZE(max9867_rates_44k1), 136805d132dSanish kumar }; 137805d132dSanish kumar 138*715ee191SLadislav Michl static const unsigned int max9867_rates_48k[] = { 139*715ee191SLadislav Michl 8000, 16000, 32000, 48000, 140*715ee191SLadislav Michl }; 141*715ee191SLadislav Michl 142*715ee191SLadislav Michl static const struct snd_pcm_hw_constraint_list max9867_constraints_48k = { 143*715ee191SLadislav Michl .list = max9867_rates_48k, 144*715ee191SLadislav Michl .count = ARRAY_SIZE(max9867_rates_48k), 145*715ee191SLadislav Michl }; 146*715ee191SLadislav Michl 147*715ee191SLadislav Michl struct max9867_priv { 148*715ee191SLadislav Michl struct regmap *regmap; 149*715ee191SLadislav Michl const struct snd_pcm_hw_constraint_list *constraints; 150*715ee191SLadislav Michl unsigned int sysclk, pclk; 151*715ee191SLadislav Michl bool master, dsp_a; 152*715ee191SLadislav Michl }; 153*715ee191SLadislav Michl 154*715ee191SLadislav Michl static int max9867_startup(struct snd_pcm_substream *substream, 155*715ee191SLadislav Michl struct snd_soc_dai *dai) 156805d132dSanish kumar { 157*715ee191SLadislav Michl struct max9867_priv *max9867 = 158*715ee191SLadislav Michl snd_soc_component_get_drvdata(dai->component); 159805d132dSanish kumar 160*715ee191SLadislav Michl if (max9867->constraints) 161*715ee191SLadislav Michl snd_pcm_hw_constraint_list(substream->runtime, 0, 162*715ee191SLadislav Michl SNDRV_PCM_HW_PARAM_RATE, max9867->constraints); 163805d132dSanish kumar 164*715ee191SLadislav Michl return 0; 165805d132dSanish kumar } 166805d132dSanish kumar 167805d132dSanish kumar static int max9867_dai_hw_params(struct snd_pcm_substream *substream, 168805d132dSanish kumar struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) 169805d132dSanish kumar { 170*715ee191SLadislav Michl int value; 171*715ee191SLadislav Michl unsigned long int rate, ratio; 17291880a6bSKuninori Morimoto struct snd_soc_component *component = dai->component; 17391880a6bSKuninori Morimoto struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); 174*715ee191SLadislav Michl unsigned int ni = DIV_ROUND_CLOSEST_ULL(96ULL * 0x10000 * params_rate(params), 175*715ee191SLadislav Michl max9867->pclk); 176805d132dSanish kumar 177805d132dSanish kumar /* set up the ni value */ 178805d132dSanish kumar regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH, 179*715ee191SLadislav Michl MAX9867_NI_HIGH_MASK, (0xFF00 & ni) >> 8); 180805d132dSanish kumar regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW, 181*715ee191SLadislav Michl MAX9867_NI_LOW_MASK, 0x00FF & ni); 182*715ee191SLadislav Michl if (max9867->master) { 183*715ee191SLadislav Michl if (max9867->dsp_a) { 184*715ee191SLadislav Michl value = MAX9867_IFC1B_48X; 185*715ee191SLadislav Michl } else { 186*715ee191SLadislav Michl rate = params_rate(params) * 2 * params_width(params); 187*715ee191SLadislav Michl ratio = max9867->pclk / rate; 188*715ee191SLadislav Michl switch (params_width(params)) { 189*715ee191SLadislav Michl case 8: 190*715ee191SLadislav Michl case 16: 191*715ee191SLadislav Michl switch (ratio) { 192*715ee191SLadislav Michl case 2: 193*715ee191SLadislav Michl value = MAX9867_IFC1B_PCLK_2; 194*715ee191SLadislav Michl break; 195*715ee191SLadislav Michl case 4: 196*715ee191SLadislav Michl value = MAX9867_IFC1B_PCLK_4; 197*715ee191SLadislav Michl break; 198*715ee191SLadislav Michl case 8: 199*715ee191SLadislav Michl value = MAX9867_IFC1B_PCLK_8; 200*715ee191SLadislav Michl break; 201*715ee191SLadislav Michl case 16: 202*715ee191SLadislav Michl value = MAX9867_IFC1B_PCLK_16; 203*715ee191SLadislav Michl break; 204*715ee191SLadislav Michl default: 205*715ee191SLadislav Michl return -EINVAL; 206*715ee191SLadislav Michl } 207*715ee191SLadislav Michl break; 208*715ee191SLadislav Michl case 24: 209*715ee191SLadislav Michl value = MAX9867_IFC1B_48X; 210*715ee191SLadislav Michl break; 211*715ee191SLadislav Michl case 32: 212*715ee191SLadislav Michl value = MAX9867_IFC1B_64X; 213*715ee191SLadislav Michl break; 214*715ee191SLadislav Michl default: 215*715ee191SLadislav Michl return -EINVAL; 216*715ee191SLadislav Michl } 217*715ee191SLadislav Michl } 218*715ee191SLadislav Michl regmap_update_bits(max9867->regmap, MAX9867_IFC1B, 219*715ee191SLadislav Michl MAX9867_IFC1B_BCLK_MASK, value); 220*715ee191SLadislav Michl } else { 221805d132dSanish kumar /* 222805d132dSanish kumar * digital pll locks on to any externally supplied LRCLK signal 223805d132dSanish kumar * and also enable rapid lock mode. 224805d132dSanish kumar */ 225805d132dSanish kumar regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW, 226805d132dSanish kumar MAX9867_RAPID_LOCK, MAX9867_RAPID_LOCK); 227805d132dSanish kumar regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH, 228805d132dSanish kumar MAX9867_PLL, MAX9867_PLL); 229805d132dSanish kumar } 230805d132dSanish kumar return 0; 231805d132dSanish kumar } 232805d132dSanish kumar 233805d132dSanish kumar static int max9867_mute(struct snd_soc_dai *dai, int mute) 234805d132dSanish kumar { 23591880a6bSKuninori Morimoto struct snd_soc_component *component = dai->component; 23691880a6bSKuninori Morimoto struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); 237805d132dSanish kumar 238805d132dSanish kumar if (mute) 239805d132dSanish kumar regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL, 240805d132dSanish kumar MAX9867_DAC_MUTE_MASK, MAX9867_DAC_MUTE_MASK); 241805d132dSanish kumar else 242805d132dSanish kumar regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL, 243805d132dSanish kumar MAX9867_DAC_MUTE_MASK, 0); 244805d132dSanish kumar return 0; 245805d132dSanish kumar } 246805d132dSanish kumar 247805d132dSanish kumar static int max9867_set_dai_sysclk(struct snd_soc_dai *codec_dai, 248805d132dSanish kumar int clk_id, unsigned int freq, int dir) 249805d132dSanish kumar { 25091880a6bSKuninori Morimoto struct snd_soc_component *component = codec_dai->component; 25191880a6bSKuninori Morimoto struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); 252805d132dSanish kumar int value = 0; 253805d132dSanish kumar 254805d132dSanish kumar /* Set the prescaler based on the master clock frequency*/ 255805d132dSanish kumar if (freq >= 10000000 && freq <= 20000000) { 256805d132dSanish kumar value |= MAX9867_PSCLK_10_20; 257805d132dSanish kumar max9867->pclk = freq; 258805d132dSanish kumar } else if (freq >= 20000000 && freq <= 40000000) { 259805d132dSanish kumar value |= MAX9867_PSCLK_20_40; 260805d132dSanish kumar max9867->pclk = freq / 2; 261805d132dSanish kumar } else if (freq >= 40000000 && freq <= 60000000) { 262805d132dSanish kumar value |= MAX9867_PSCLK_40_60; 263805d132dSanish kumar max9867->pclk = freq / 4; 264805d132dSanish kumar } else { 2658b9c716aSLadislav Michl dev_err(component->dev, 2668b9c716aSLadislav Michl "Invalid clock frequency %uHz (required 10-60MHz)\n", 2678b9c716aSLadislav Michl freq); 268805d132dSanish kumar return -EINVAL; 269805d132dSanish kumar } 270*715ee191SLadislav Michl if (freq % 48000 == 0) 271*715ee191SLadislav Michl max9867->constraints = &max9867_constraints_48k; 272*715ee191SLadislav Michl else if (freq % 44100 == 0) 273*715ee191SLadislav Michl max9867->constraints = &max9867_constraints_44k1; 274*715ee191SLadislav Michl else 275*715ee191SLadislav Michl dev_warn(component->dev, 276*715ee191SLadislav Michl "Unable to set exact rate with %uHz clock frequency\n", 277*715ee191SLadislav Michl freq); 278805d132dSanish kumar max9867->sysclk = freq; 279*715ee191SLadislav Michl value = value << MAX9867_PSCLK_SHIFT; 280805d132dSanish kumar /* exact integer mode is not supported */ 281805d132dSanish kumar value &= ~MAX9867_FREQ_MASK; 282805d132dSanish kumar regmap_update_bits(max9867->regmap, MAX9867_SYSCLK, 283805d132dSanish kumar MAX9867_PSCLK_MASK, value); 284805d132dSanish kumar return 0; 285805d132dSanish kumar } 286805d132dSanish kumar 287805d132dSanish kumar static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai, 288805d132dSanish kumar unsigned int fmt) 289805d132dSanish kumar { 29091880a6bSKuninori Morimoto struct snd_soc_component *component = codec_dai->component; 29191880a6bSKuninori Morimoto struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); 292*715ee191SLadislav Michl u8 iface1A, iface1B; 293805d132dSanish kumar 294805d132dSanish kumar switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 295805d132dSanish kumar case SND_SOC_DAIFMT_CBM_CFM: 296*715ee191SLadislav Michl max9867->master = true; 297*715ee191SLadislav Michl iface1A = MAX9867_MASTER; 298*715ee191SLadislav Michl iface1B = MAX9867_IFC1B_48X; 299805d132dSanish kumar break; 300805d132dSanish kumar case SND_SOC_DAIFMT_CBS_CFS: 301*715ee191SLadislav Michl max9867->master = false; 302*715ee191SLadislav Michl iface1A = iface1B = 0; 303805d132dSanish kumar break; 304805d132dSanish kumar default: 305805d132dSanish kumar return -EINVAL; 306805d132dSanish kumar } 307805d132dSanish kumar 30879e13974SLadislav Michl switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 30979e13974SLadislav Michl case SND_SOC_DAIFMT_I2S: 310*715ee191SLadislav Michl max9867->dsp_a = false; 311805d132dSanish kumar iface1A |= MAX9867_I2S_DLY; 31279e13974SLadislav Michl break; 31379e13974SLadislav Michl case SND_SOC_DAIFMT_DSP_A: 314*715ee191SLadislav Michl max9867->dsp_a = true; 31579e13974SLadislav Michl iface1A |= MAX9867_TDM_MODE | MAX9867_SDOUT_HIZ; 31679e13974SLadislav Michl break; 31779e13974SLadislav Michl default: 31879e13974SLadislav Michl return -EINVAL; 31979e13974SLadislav Michl } 320805d132dSanish kumar 321805d132dSanish kumar /* Clock inversion bits, BCI and WCI */ 322805d132dSanish kumar switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 323805d132dSanish kumar case SND_SOC_DAIFMT_NB_NF: 324805d132dSanish kumar break; 325805d132dSanish kumar case SND_SOC_DAIFMT_IB_IF: 326805d132dSanish kumar iface1A |= MAX9867_WCI_MODE | MAX9867_BCI_MODE; 327805d132dSanish kumar break; 328805d132dSanish kumar case SND_SOC_DAIFMT_IB_NF: 329805d132dSanish kumar iface1A |= MAX9867_BCI_MODE; 330805d132dSanish kumar break; 331805d132dSanish kumar case SND_SOC_DAIFMT_NB_IF: 332805d132dSanish kumar iface1A |= MAX9867_WCI_MODE; 333805d132dSanish kumar break; 334805d132dSanish kumar default: 335805d132dSanish kumar return -EINVAL; 336805d132dSanish kumar } 337805d132dSanish kumar 3389fe78b28SVinod Koul regmap_write(max9867->regmap, MAX9867_IFC1A, iface1A); 3399fe78b28SVinod Koul regmap_write(max9867->regmap, MAX9867_IFC1B, iface1B); 340*715ee191SLadislav Michl 341805d132dSanish kumar return 0; 342805d132dSanish kumar } 343805d132dSanish kumar 344eb59d73cSArvind Yadav static const struct snd_soc_dai_ops max9867_dai_ops = { 345805d132dSanish kumar .set_sysclk = max9867_set_dai_sysclk, 346*715ee191SLadislav Michl .set_fmt = max9867_dai_set_fmt, 347805d132dSanish kumar .digital_mute = max9867_mute, 348*715ee191SLadislav Michl .startup = max9867_startup, 349805d132dSanish kumar .hw_params = max9867_dai_hw_params, 350805d132dSanish kumar }; 351805d132dSanish kumar 352805d132dSanish kumar static struct snd_soc_dai_driver max9867_dai[] = { 353805d132dSanish kumar { 354805d132dSanish kumar .name = "max9867-aif1", 355805d132dSanish kumar .playback = { 356805d132dSanish kumar .stream_name = "HiFi Playback", 357e6ceb922SLadislav Michl .channels_min = 2, 358805d132dSanish kumar .channels_max = 2, 359*715ee191SLadislav Michl .rates = SNDRV_PCM_RATE_8000_48000, 360*715ee191SLadislav Michl .formats = SNDRV_PCM_FMTBIT_S16_LE, 361805d132dSanish kumar }, 362805d132dSanish kumar .capture = { 363805d132dSanish kumar .stream_name = "HiFi Capture", 364e6ceb922SLadislav Michl .channels_min = 2, 365805d132dSanish kumar .channels_max = 2, 366*715ee191SLadislav Michl .rates = SNDRV_PCM_RATE_8000_48000, 367*715ee191SLadislav Michl .formats = SNDRV_PCM_FMTBIT_S16_LE, 368805d132dSanish kumar }, 369805d132dSanish kumar .ops = &max9867_dai_ops, 370e6ceb922SLadislav Michl .symmetric_rates = 1, 371805d132dSanish kumar } 372805d132dSanish kumar }; 373805d132dSanish kumar 37429f58ff0SLadislav Michl #ifdef CONFIG_PM 37529f58ff0SLadislav Michl static int max9867_suspend(struct snd_soc_component *component) 376805d132dSanish kumar { 37729f58ff0SLadislav Michl snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); 378805d132dSanish kumar 379805d132dSanish kumar return 0; 380805d132dSanish kumar } 381805d132dSanish kumar 38229f58ff0SLadislav Michl static int max9867_resume(struct snd_soc_component *component) 383805d132dSanish kumar { 38429f58ff0SLadislav Michl snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); 385805d132dSanish kumar 386805d132dSanish kumar return 0; 387805d132dSanish kumar } 38829f58ff0SLadislav Michl #else 38929f58ff0SLadislav Michl #define max9867_suspend NULL 39029f58ff0SLadislav Michl #define max9867_resume NULL 391805d132dSanish kumar #endif 392805d132dSanish kumar 39329f58ff0SLadislav Michl static int max9867_set_bias_level(struct snd_soc_component *component, 39429f58ff0SLadislav Michl enum snd_soc_bias_level level) 39529f58ff0SLadislav Michl { 39629f58ff0SLadislav Michl int err; 39729f58ff0SLadislav Michl struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); 39829f58ff0SLadislav Michl 39929f58ff0SLadislav Michl switch (level) { 40029f58ff0SLadislav Michl case SND_SOC_BIAS_STANDBY: 40129f58ff0SLadislav Michl if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { 40229f58ff0SLadislav Michl err = regcache_sync(max9867->regmap); 40329f58ff0SLadislav Michl if (err) 40429f58ff0SLadislav Michl return err; 40529f58ff0SLadislav Michl 40629f58ff0SLadislav Michl err = regmap_update_bits(max9867->regmap, MAX9867_PWRMAN, 40729f58ff0SLadislav Michl MAX9867_SHTDOWN, MAX9867_SHTDOWN); 40829f58ff0SLadislav Michl if (err) 40929f58ff0SLadislav Michl return err; 41029f58ff0SLadislav Michl } 41129f58ff0SLadislav Michl break; 41229f58ff0SLadislav Michl case SND_SOC_BIAS_OFF: 41329f58ff0SLadislav Michl err = regmap_update_bits(max9867->regmap, MAX9867_PWRMAN, 41429f58ff0SLadislav Michl MAX9867_SHTDOWN, 0); 41529f58ff0SLadislav Michl if (err) 41629f58ff0SLadislav Michl return err; 41729f58ff0SLadislav Michl 41829f58ff0SLadislav Michl regcache_mark_dirty(max9867->regmap); 41929f58ff0SLadislav Michl break; 42029f58ff0SLadislav Michl default: 42129f58ff0SLadislav Michl break; 42229f58ff0SLadislav Michl } 42329f58ff0SLadislav Michl 42429f58ff0SLadislav Michl return 0; 42529f58ff0SLadislav Michl } 42629f58ff0SLadislav Michl 42791880a6bSKuninori Morimoto static const struct snd_soc_component_driver max9867_component = { 428805d132dSanish kumar .controls = max9867_snd_controls, 429805d132dSanish kumar .num_controls = ARRAY_SIZE(max9867_snd_controls), 430805d132dSanish kumar .dapm_routes = max9867_audio_map, 431805d132dSanish kumar .num_dapm_routes = ARRAY_SIZE(max9867_audio_map), 432805d132dSanish kumar .dapm_widgets = max9867_dapm_widgets, 433805d132dSanish kumar .num_dapm_widgets = ARRAY_SIZE(max9867_dapm_widgets), 43429f58ff0SLadislav Michl .suspend = max9867_suspend, 43529f58ff0SLadislav Michl .resume = max9867_resume, 43629f58ff0SLadislav Michl .set_bias_level = max9867_set_bias_level, 43791880a6bSKuninori Morimoto .idle_bias_on = 1, 43891880a6bSKuninori Morimoto .use_pmdown_time = 1, 43991880a6bSKuninori Morimoto .endianness = 1, 44091880a6bSKuninori Morimoto .non_legacy_dai_naming = 1, 441805d132dSanish kumar }; 442805d132dSanish kumar 443805d132dSanish kumar static bool max9867_volatile_register(struct device *dev, unsigned int reg) 444805d132dSanish kumar { 445805d132dSanish kumar switch (reg) { 446805d132dSanish kumar case MAX9867_STATUS: 447805d132dSanish kumar case MAX9867_JACKSTATUS: 448805d132dSanish kumar case MAX9867_AUXHIGH: 449805d132dSanish kumar case MAX9867_AUXLOW: 450805d132dSanish kumar return true; 451805d132dSanish kumar default: 452805d132dSanish kumar return false; 453805d132dSanish kumar } 454805d132dSanish kumar } 455805d132dSanish kumar 456250a99e7SAxel Lin static const struct reg_default max9867_reg[] = { 457805d132dSanish kumar { 0x04, 0x00 }, 458805d132dSanish kumar { 0x05, 0x00 }, 459805d132dSanish kumar { 0x06, 0x00 }, 460805d132dSanish kumar { 0x07, 0x00 }, 461805d132dSanish kumar { 0x08, 0x00 }, 462805d132dSanish kumar { 0x09, 0x00 }, 463805d132dSanish kumar { 0x0A, 0x00 }, 464805d132dSanish kumar { 0x0B, 0x00 }, 465805d132dSanish kumar { 0x0C, 0x00 }, 466805d132dSanish kumar { 0x0D, 0x00 }, 467805d132dSanish kumar { 0x0E, 0x00 }, 468805d132dSanish kumar { 0x0F, 0x00 }, 469805d132dSanish kumar { 0x10, 0x00 }, 470805d132dSanish kumar { 0x11, 0x00 }, 471805d132dSanish kumar { 0x12, 0x00 }, 472805d132dSanish kumar { 0x13, 0x00 }, 473805d132dSanish kumar { 0x14, 0x00 }, 474805d132dSanish kumar { 0x15, 0x00 }, 475805d132dSanish kumar { 0x16, 0x00 }, 476805d132dSanish kumar { 0x17, 0x00 }, 477805d132dSanish kumar }; 478805d132dSanish kumar 479250a99e7SAxel Lin static const struct regmap_config max9867_regmap = { 480805d132dSanish kumar .reg_bits = 8, 481805d132dSanish kumar .val_bits = 8, 482805d132dSanish kumar .max_register = MAX9867_REVISION, 483805d132dSanish kumar .reg_defaults = max9867_reg, 484805d132dSanish kumar .num_reg_defaults = ARRAY_SIZE(max9867_reg), 485805d132dSanish kumar .volatile_reg = max9867_volatile_register, 486805d132dSanish kumar .cache_type = REGCACHE_RBTREE, 487805d132dSanish kumar }; 488805d132dSanish kumar 489805d132dSanish kumar static int max9867_i2c_probe(struct i2c_client *i2c, 490805d132dSanish kumar const struct i2c_device_id *id) 491805d132dSanish kumar { 492805d132dSanish kumar struct max9867_priv *max9867; 4938efc1afdSLadislav Michl int ret, reg; 494805d132dSanish kumar 495933662f2SLadislav Michl max9867 = devm_kzalloc(&i2c->dev, sizeof(*max9867), GFP_KERNEL); 496805d132dSanish kumar if (!max9867) 497805d132dSanish kumar return -ENOMEM; 498805d132dSanish kumar 499805d132dSanish kumar i2c_set_clientdata(i2c, max9867); 500805d132dSanish kumar max9867->regmap = devm_regmap_init_i2c(i2c, &max9867_regmap); 501805d132dSanish kumar if (IS_ERR(max9867->regmap)) { 502805d132dSanish kumar ret = PTR_ERR(max9867->regmap); 5038b9c716aSLadislav Michl dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret); 504805d132dSanish kumar return ret; 505805d132dSanish kumar } 506933662f2SLadislav Michl ret = regmap_read(max9867->regmap, MAX9867_REVISION, ®); 507805d132dSanish kumar if (ret < 0) { 508805d132dSanish kumar dev_err(&i2c->dev, "Failed to read: %d\n", ret); 509805d132dSanish kumar return ret; 510805d132dSanish kumar } 511805d132dSanish kumar dev_info(&i2c->dev, "device revision: %x\n", reg); 51291880a6bSKuninori Morimoto ret = devm_snd_soc_register_component(&i2c->dev, &max9867_component, 513805d132dSanish kumar max9867_dai, ARRAY_SIZE(max9867_dai)); 5148efc1afdSLadislav Michl if (ret < 0) 51591880a6bSKuninori Morimoto dev_err(&i2c->dev, "Failed to register component: %d\n", ret); 516805d132dSanish kumar return ret; 517805d132dSanish kumar } 518805d132dSanish kumar 519805d132dSanish kumar static const struct i2c_device_id max9867_i2c_id[] = { 520805d132dSanish kumar { "max9867", 0 }, 5211b36e4a2SAxel Lin { } 522805d132dSanish kumar }; 52356af0e4cSJavier Martinez Canillas MODULE_DEVICE_TABLE(i2c, max9867_i2c_id); 524805d132dSanish kumar 525805d132dSanish kumar static const struct of_device_id max9867_of_match[] = { 526805d132dSanish kumar { .compatible = "maxim,max9867", }, 527805d132dSanish kumar { } 528805d132dSanish kumar }; 52956af0e4cSJavier Martinez Canillas MODULE_DEVICE_TABLE(of, max9867_of_match); 530805d132dSanish kumar 531805d132dSanish kumar static struct i2c_driver max9867_i2c_driver = { 532805d132dSanish kumar .driver = { 533805d132dSanish kumar .name = "max9867", 534805d132dSanish kumar .of_match_table = of_match_ptr(max9867_of_match), 535805d132dSanish kumar }, 536805d132dSanish kumar .probe = max9867_i2c_probe, 537805d132dSanish kumar .id_table = max9867_i2c_id, 538805d132dSanish kumar }; 539805d132dSanish kumar 540805d132dSanish kumar module_i2c_driver(max9867_i2c_driver); 541805d132dSanish kumar 542805d132dSanish kumar MODULE_AUTHOR("anish kumar <yesanishhere@gmail.com>"); 543805d132dSanish kumar MODULE_DESCRIPTION("ALSA SoC MAX9867 driver"); 544805d132dSanish kumar MODULE_LICENSE("GPL"); 545