1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2a2e2876aSanish kumar /* 3a2e2876aSanish kumar * max98926.c -- ALSA SoC MAX98926 driver 4a2e2876aSanish kumar * Copyright 2013-15 Maxim Integrated Products 5a2e2876aSanish kumar */ 6a2e2876aSanish kumar #include <linux/delay.h> 7a2e2876aSanish kumar #include <linux/i2c.h> 8a2e2876aSanish kumar #include <linux/module.h> 9a2e2876aSanish kumar #include <linux/regmap.h> 10a2e2876aSanish kumar #include <linux/slab.h> 11a2e2876aSanish kumar #include <linux/cdev.h> 12a2e2876aSanish kumar #include <sound/pcm.h> 13a2e2876aSanish kumar #include <sound/pcm_params.h> 14a2e2876aSanish kumar #include <sound/soc.h> 15a2e2876aSanish kumar #include <sound/tlv.h> 16a2e2876aSanish kumar #include "max98926.h" 17a2e2876aSanish kumar 18a2e2876aSanish kumar static const char * const max98926_boost_voltage_txt[] = { 19a2e2876aSanish kumar "8.5V", "8.25V", "8.0V", "7.75V", "7.5V", "7.25V", "7.0V", "6.75V", 20a2e2876aSanish kumar "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V" 21a2e2876aSanish kumar }; 22a2e2876aSanish kumar 23a2e2876aSanish kumar static const char *const max98926_pdm_ch_text[] = { 24a2e2876aSanish kumar "Current", "Voltage", 25a2e2876aSanish kumar }; 26a2e2876aSanish kumar 27a2e2876aSanish kumar static const char *const max98926_hpf_cutoff_txt[] = { 28a2e2876aSanish kumar "Disable", "DC Block", "100Hz", 29a2e2876aSanish kumar "200Hz", "400Hz", "800Hz", 30a2e2876aSanish kumar }; 31a2e2876aSanish kumar 32e354d86eSAxel Lin static const struct reg_default max98926_reg[] = { 33a2e2876aSanish kumar { 0x0B, 0x00 }, /* IRQ Enable0 */ 34a2e2876aSanish kumar { 0x0C, 0x00 }, /* IRQ Enable1 */ 35a2e2876aSanish kumar { 0x0D, 0x00 }, /* IRQ Enable2 */ 36a2e2876aSanish kumar { 0x0E, 0x00 }, /* IRQ Clear0 */ 37a2e2876aSanish kumar { 0x0F, 0x00 }, /* IRQ Clear1 */ 38a2e2876aSanish kumar { 0x10, 0x00 }, /* IRQ Clear2 */ 39a2e2876aSanish kumar { 0x11, 0xC0 }, /* Map0 */ 40a2e2876aSanish kumar { 0x12, 0x00 }, /* Map1 */ 41a2e2876aSanish kumar { 0x13, 0x00 }, /* Map2 */ 42a2e2876aSanish kumar { 0x14, 0xF0 }, /* Map3 */ 43a2e2876aSanish kumar { 0x15, 0x00 }, /* Map4 */ 44a2e2876aSanish kumar { 0x16, 0xAB }, /* Map5 */ 45a2e2876aSanish kumar { 0x17, 0x89 }, /* Map6 */ 46a2e2876aSanish kumar { 0x18, 0x00 }, /* Map7 */ 47a2e2876aSanish kumar { 0x19, 0x00 }, /* Map8 */ 48a2e2876aSanish kumar { 0x1A, 0x04 }, /* DAI Clock Mode 1 */ 49a2e2876aSanish kumar { 0x1B, 0x00 }, /* DAI Clock Mode 2 */ 50a2e2876aSanish kumar { 0x1C, 0x00 }, /* DAI Clock Divider Denominator MSBs */ 51a2e2876aSanish kumar { 0x1D, 0x00 }, /* DAI Clock Divider Denominator LSBs */ 52a2e2876aSanish kumar { 0x1E, 0xF0 }, /* DAI Clock Divider Numerator MSBs */ 53a2e2876aSanish kumar { 0x1F, 0x00 }, /* DAI Clock Divider Numerator LSBs */ 54a2e2876aSanish kumar { 0x20, 0x50 }, /* Format */ 55a2e2876aSanish kumar { 0x21, 0x00 }, /* TDM Slot Select */ 56a2e2876aSanish kumar { 0x22, 0x00 }, /* DOUT Configuration VMON */ 57a2e2876aSanish kumar { 0x23, 0x00 }, /* DOUT Configuration IMON */ 58a2e2876aSanish kumar { 0x24, 0x00 }, /* DOUT Configuration VBAT */ 59a2e2876aSanish kumar { 0x25, 0x00 }, /* DOUT Configuration VBST */ 60a2e2876aSanish kumar { 0x26, 0x00 }, /* DOUT Configuration FLAG */ 61a2e2876aSanish kumar { 0x27, 0xFF }, /* DOUT HiZ Configuration 1 */ 62a2e2876aSanish kumar { 0x28, 0xFF }, /* DOUT HiZ Configuration 2 */ 63a2e2876aSanish kumar { 0x29, 0xFF }, /* DOUT HiZ Configuration 3 */ 64a2e2876aSanish kumar { 0x2A, 0xFF }, /* DOUT HiZ Configuration 4 */ 65a2e2876aSanish kumar { 0x2B, 0x02 }, /* DOUT Drive Strength */ 66a2e2876aSanish kumar { 0x2C, 0x90 }, /* Filters */ 67a2e2876aSanish kumar { 0x2D, 0x00 }, /* Gain */ 68a2e2876aSanish kumar { 0x2E, 0x02 }, /* Gain Ramping */ 69a2e2876aSanish kumar { 0x2F, 0x00 }, /* Speaker Amplifier */ 70a2e2876aSanish kumar { 0x30, 0x0A }, /* Threshold */ 71a2e2876aSanish kumar { 0x31, 0x00 }, /* ALC Attack */ 72a2e2876aSanish kumar { 0x32, 0x80 }, /* ALC Atten and Release */ 73a2e2876aSanish kumar { 0x33, 0x00 }, /* ALC Infinite Hold Release */ 74a2e2876aSanish kumar { 0x34, 0x92 }, /* ALC Configuration */ 75a2e2876aSanish kumar { 0x35, 0x01 }, /* Boost Converter */ 76a2e2876aSanish kumar { 0x36, 0x00 }, /* Block Enable */ 77a2e2876aSanish kumar { 0x37, 0x00 }, /* Configuration */ 78a2e2876aSanish kumar { 0x38, 0x00 }, /* Global Enable */ 79a2e2876aSanish kumar { 0x3A, 0x00 }, /* Boost Limiter */ 80a2e2876aSanish kumar }; 81a2e2876aSanish kumar 82a2e2876aSanish kumar static const struct soc_enum max98926_voltage_enum[] = { 83a2e2876aSanish kumar SOC_ENUM_SINGLE(MAX98926_DAI_CLK_DIV_N_LSBS, 0, 84a2e2876aSanish kumar ARRAY_SIZE(max98926_pdm_ch_text), 85a2e2876aSanish kumar max98926_pdm_ch_text), 86a2e2876aSanish kumar }; 87a2e2876aSanish kumar 88a2e2876aSanish kumar static const struct snd_kcontrol_new max98926_voltage_control = 89a2e2876aSanish kumar SOC_DAPM_ENUM("Route", max98926_voltage_enum); 90a2e2876aSanish kumar 91a2e2876aSanish kumar static const struct soc_enum max98926_current_enum[] = { 92a2e2876aSanish kumar SOC_ENUM_SINGLE(MAX98926_DAI_CLK_DIV_N_LSBS, 93a2e2876aSanish kumar MAX98926_PDM_SOURCE_1_SHIFT, 94a2e2876aSanish kumar ARRAY_SIZE(max98926_pdm_ch_text), 95a2e2876aSanish kumar max98926_pdm_ch_text), 96a2e2876aSanish kumar }; 97a2e2876aSanish kumar 98a2e2876aSanish kumar static const struct snd_kcontrol_new max98926_current_control = 99a2e2876aSanish kumar SOC_DAPM_ENUM("Route", max98926_current_enum); 100a2e2876aSanish kumar 101a2e2876aSanish kumar static const struct snd_kcontrol_new max98926_mixer_controls[] = { 102a2e2876aSanish kumar SOC_DAPM_SINGLE("PCM Single Switch", MAX98926_SPK_AMP, 103a2e2876aSanish kumar MAX98926_INSELECT_MODE_SHIFT, 0, 0), 104a2e2876aSanish kumar SOC_DAPM_SINGLE("PDM Single Switch", MAX98926_SPK_AMP, 105a2e2876aSanish kumar MAX98926_INSELECT_MODE_SHIFT, 1, 0), 106a2e2876aSanish kumar }; 107a2e2876aSanish kumar 108a2e2876aSanish kumar static const struct snd_kcontrol_new max98926_dai_controls[] = { 109a2e2876aSanish kumar SOC_DAPM_SINGLE("Left", MAX98926_GAIN, 110a2e2876aSanish kumar MAX98926_DAC_IN_SEL_SHIFT, 0, 0), 111a2e2876aSanish kumar SOC_DAPM_SINGLE("Right", MAX98926_GAIN, 112a2e2876aSanish kumar MAX98926_DAC_IN_SEL_SHIFT, 1, 0), 113a2e2876aSanish kumar SOC_DAPM_SINGLE("LeftRight", MAX98926_GAIN, 114a2e2876aSanish kumar MAX98926_DAC_IN_SEL_SHIFT, 2, 0), 115a2e2876aSanish kumar SOC_DAPM_SINGLE("(Left+Right)/2 Switch", MAX98926_GAIN, 116a2e2876aSanish kumar MAX98926_DAC_IN_SEL_SHIFT, 3, 0), 117a2e2876aSanish kumar }; 118a2e2876aSanish kumar 119a2e2876aSanish kumar static const struct snd_soc_dapm_widget max98926_dapm_widgets[] = { 120a2e2876aSanish kumar SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0, 121a2e2876aSanish kumar SND_SOC_NOPM, 0, 0), 122a2e2876aSanish kumar SND_SOC_DAPM_DAC("Amp Enable", NULL, MAX98926_BLOCK_ENABLE, 123a2e2876aSanish kumar MAX98926_SPK_EN_SHIFT, 0), 124a2e2876aSanish kumar SND_SOC_DAPM_SUPPLY("Global Enable", MAX98926_GLOBAL_ENABLE, 125a2e2876aSanish kumar MAX98926_EN_SHIFT, 0, NULL, 0), 126a2e2876aSanish kumar SND_SOC_DAPM_SUPPLY("VI Enable", MAX98926_BLOCK_ENABLE, 127a2e2876aSanish kumar MAX98926_ADC_IMON_EN_WIDTH | 128a2e2876aSanish kumar MAX98926_ADC_VMON_EN_SHIFT, 129a2e2876aSanish kumar 0, NULL, 0), 130a2e2876aSanish kumar SND_SOC_DAPM_PGA("BST Enable", MAX98926_BLOCK_ENABLE, 131a2e2876aSanish kumar MAX98926_BST_EN_SHIFT, 0, NULL, 0), 132a2e2876aSanish kumar SND_SOC_DAPM_OUTPUT("BE_OUT"), 133a2e2876aSanish kumar SND_SOC_DAPM_MIXER("PCM Sel", MAX98926_SPK_AMP, 134a2e2876aSanish kumar MAX98926_INSELECT_MODE_SHIFT, 0, 135a2e2876aSanish kumar &max98926_mixer_controls[0], 136a2e2876aSanish kumar ARRAY_SIZE(max98926_mixer_controls)), 137a2e2876aSanish kumar SND_SOC_DAPM_MIXER("DAI Sel", 138a2e2876aSanish kumar MAX98926_GAIN, MAX98926_DAC_IN_SEL_SHIFT, 0, 139a2e2876aSanish kumar &max98926_dai_controls[0], 140a2e2876aSanish kumar ARRAY_SIZE(max98926_dai_controls)), 141a2e2876aSanish kumar SND_SOC_DAPM_MUX("PDM CH1 Source", 142a2e2876aSanish kumar MAX98926_DAI_CLK_DIV_N_LSBS, 143a2e2876aSanish kumar MAX98926_PDM_CURRENT_SHIFT, 144a2e2876aSanish kumar 0, &max98926_current_control), 145a2e2876aSanish kumar SND_SOC_DAPM_MUX("PDM CH0 Source", 146a2e2876aSanish kumar MAX98926_DAI_CLK_DIV_N_LSBS, 147a2e2876aSanish kumar MAX98926_PDM_VOLTAGE_SHIFT, 148a2e2876aSanish kumar 0, &max98926_voltage_control), 149a2e2876aSanish kumar }; 150a2e2876aSanish kumar 151a2e2876aSanish kumar static const struct snd_soc_dapm_route max98926_audio_map[] = { 152a2e2876aSanish kumar {"VI Enable", NULL, "DAI_OUT"}, 153a2e2876aSanish kumar {"DAI Sel", "Left", "VI Enable"}, 154a2e2876aSanish kumar {"DAI Sel", "Right", "VI Enable"}, 155a2e2876aSanish kumar {"DAI Sel", "LeftRight", "VI Enable"}, 156a2e2876aSanish kumar {"DAI Sel", "LeftRightDiv2", "VI Enable"}, 157a2e2876aSanish kumar {"PCM Sel", "PCM", "DAI Sel"}, 158a2e2876aSanish kumar 159a2e2876aSanish kumar {"PDM CH1 Source", "Current", "DAI_OUT"}, 160a2e2876aSanish kumar {"PDM CH1 Source", "Voltage", "DAI_OUT"}, 161a2e2876aSanish kumar {"PDM CH0 Source", "Current", "DAI_OUT"}, 162a2e2876aSanish kumar {"PDM CH0 Source", "Voltage", "DAI_OUT"}, 163a2e2876aSanish kumar {"PCM Sel", "Analog", "PDM CH1 Source"}, 164a2e2876aSanish kumar {"PCM Sel", "Analog", "PDM CH0 Source"}, 165a2e2876aSanish kumar {"Amp Enable", NULL, "PCM Sel"}, 166a2e2876aSanish kumar 167a2e2876aSanish kumar {"BST Enable", NULL, "Amp Enable"}, 168a2e2876aSanish kumar {"BE_OUT", NULL, "BST Enable"}, 169a2e2876aSanish kumar }; 170a2e2876aSanish kumar 171a2e2876aSanish kumar static bool max98926_volatile_register(struct device *dev, unsigned int reg) 172a2e2876aSanish kumar { 173a2e2876aSanish kumar switch (reg) { 174a2e2876aSanish kumar case MAX98926_VBAT_DATA: 175a2e2876aSanish kumar case MAX98926_VBST_DATA: 176a2e2876aSanish kumar case MAX98926_LIVE_STATUS0: 177a2e2876aSanish kumar case MAX98926_LIVE_STATUS1: 178a2e2876aSanish kumar case MAX98926_LIVE_STATUS2: 179a2e2876aSanish kumar case MAX98926_STATE0: 180a2e2876aSanish kumar case MAX98926_STATE1: 181a2e2876aSanish kumar case MAX98926_STATE2: 182a2e2876aSanish kumar case MAX98926_FLAG0: 183a2e2876aSanish kumar case MAX98926_FLAG1: 184a2e2876aSanish kumar case MAX98926_FLAG2: 185a2e2876aSanish kumar case MAX98926_VERSION: 186a2e2876aSanish kumar return true; 187a2e2876aSanish kumar default: 188a2e2876aSanish kumar return false; 189a2e2876aSanish kumar } 190a2e2876aSanish kumar } 191a2e2876aSanish kumar 192a2e2876aSanish kumar static bool max98926_readable_register(struct device *dev, unsigned int reg) 193a2e2876aSanish kumar { 194a2e2876aSanish kumar switch (reg) { 195a2e2876aSanish kumar case MAX98926_IRQ_CLEAR0: 196a2e2876aSanish kumar case MAX98926_IRQ_CLEAR1: 197a2e2876aSanish kumar case MAX98926_IRQ_CLEAR2: 198a2e2876aSanish kumar case MAX98926_ALC_HOLD_RLS: 199a2e2876aSanish kumar return false; 200a2e2876aSanish kumar default: 201a2e2876aSanish kumar return true; 202a2e2876aSanish kumar } 203a2e2876aSanish kumar }; 204a2e2876aSanish kumar 2059ab51b94SColin Ian King static DECLARE_TLV_DB_SCALE(max98926_spk_tlv, -600, 100, 0); 2069ab51b94SColin Ian King static DECLARE_TLV_DB_RANGE(max98926_current_tlv, 207a2e2876aSanish kumar 0, 11, TLV_DB_SCALE_ITEM(20, 20, 0), 208a2e2876aSanish kumar 12, 15, TLV_DB_SCALE_ITEM(320, 40, 0), 209a2e2876aSanish kumar ); 210a2e2876aSanish kumar 211a2e2876aSanish kumar static SOC_ENUM_SINGLE_DECL(max98926_dac_hpf_cutoff, 212a2e2876aSanish kumar MAX98926_FILTERS, MAX98926_DAC_HPF_SHIFT, 213a2e2876aSanish kumar max98926_hpf_cutoff_txt); 214a2e2876aSanish kumar 215a2e2876aSanish kumar static SOC_ENUM_SINGLE_DECL(max98926_boost_voltage, 216a2e2876aSanish kumar MAX98926_CONFIGURATION, MAX98926_BST_VOUT_SHIFT, 217a2e2876aSanish kumar max98926_boost_voltage_txt); 218a2e2876aSanish kumar 219a2e2876aSanish kumar static const struct snd_kcontrol_new max98926_snd_controls[] = { 220a2e2876aSanish kumar SOC_SINGLE_TLV("Speaker Volume", MAX98926_GAIN, 221a2e2876aSanish kumar MAX98926_SPK_GAIN_SHIFT, 222a2e2876aSanish kumar (1<<MAX98926_SPK_GAIN_WIDTH)-1, 0, 223a2e2876aSanish kumar max98926_spk_tlv), 224a2e2876aSanish kumar SOC_SINGLE("Ramp Switch", MAX98926_GAIN_RAMPING, 225a2e2876aSanish kumar MAX98926_SPK_RMP_EN_SHIFT, 1, 0), 226a2e2876aSanish kumar SOC_SINGLE("ZCD Switch", MAX98926_GAIN_RAMPING, 227a2e2876aSanish kumar MAX98926_SPK_ZCD_EN_SHIFT, 1, 0), 228a2e2876aSanish kumar SOC_SINGLE("ALC Switch", MAX98926_THRESHOLD, 229a2e2876aSanish kumar MAX98926_ALC_EN_SHIFT, 1, 0), 230a2e2876aSanish kumar SOC_SINGLE("ALC Threshold", MAX98926_THRESHOLD, 231a2e2876aSanish kumar MAX98926_ALC_TH_SHIFT, 232a2e2876aSanish kumar (1<<MAX98926_ALC_TH_WIDTH)-1, 0), 233a2e2876aSanish kumar SOC_ENUM("Boost Output Voltage", max98926_boost_voltage), 234a2e2876aSanish kumar SOC_SINGLE_TLV("Boost Current Limit", MAX98926_BOOST_LIMITER, 235a2e2876aSanish kumar MAX98926_BST_ILIM_SHIFT, 236a2e2876aSanish kumar (1<<MAX98926_BST_ILIM_SHIFT)-1, 0, 237a2e2876aSanish kumar max98926_current_tlv), 238a2e2876aSanish kumar SOC_ENUM("DAC HPF Cutoff", max98926_dac_hpf_cutoff), 239a2e2876aSanish kumar SOC_DOUBLE("PDM Channel One", MAX98926_DAI_CLK_DIV_N_LSBS, 240a2e2876aSanish kumar MAX98926_PDM_CHANNEL_1_SHIFT, 241a2e2876aSanish kumar MAX98926_PDM_CHANNEL_1_HIZ, 1, 0), 242a2e2876aSanish kumar SOC_DOUBLE("PDM Channel Zero", MAX98926_DAI_CLK_DIV_N_LSBS, 243a2e2876aSanish kumar MAX98926_PDM_CHANNEL_0_SHIFT, 244a2e2876aSanish kumar MAX98926_PDM_CHANNEL_0_HIZ, 1, 0), 245a2e2876aSanish kumar }; 246a2e2876aSanish kumar 247a2e2876aSanish kumar static const struct { 248a2e2876aSanish kumar int rate; 249a2e2876aSanish kumar int sr; 250a2e2876aSanish kumar } rate_table[] = { 251a2e2876aSanish kumar { 252a2e2876aSanish kumar .rate = 8000, 253a2e2876aSanish kumar .sr = 0, 254a2e2876aSanish kumar }, 255a2e2876aSanish kumar { 256a2e2876aSanish kumar .rate = 11025, 257a2e2876aSanish kumar .sr = 1, 258a2e2876aSanish kumar }, 259a2e2876aSanish kumar { 260a2e2876aSanish kumar .rate = 12000, 261a2e2876aSanish kumar .sr = 2, 262a2e2876aSanish kumar }, 263a2e2876aSanish kumar { 264a2e2876aSanish kumar .rate = 16000, 265a2e2876aSanish kumar .sr = 3, 266a2e2876aSanish kumar }, 267a2e2876aSanish kumar { 268a2e2876aSanish kumar .rate = 22050, 269a2e2876aSanish kumar .sr = 4, 270a2e2876aSanish kumar }, 271a2e2876aSanish kumar { 272a2e2876aSanish kumar .rate = 24000, 273a2e2876aSanish kumar .sr = 5, 274a2e2876aSanish kumar }, 275a2e2876aSanish kumar { 276a2e2876aSanish kumar .rate = 32000, 277a2e2876aSanish kumar .sr = 6, 278a2e2876aSanish kumar }, 279a2e2876aSanish kumar { 280a2e2876aSanish kumar .rate = 44100, 281a2e2876aSanish kumar .sr = 7, 282a2e2876aSanish kumar }, 283a2e2876aSanish kumar { 284a2e2876aSanish kumar .rate = 48000, 285a2e2876aSanish kumar .sr = 8, 286a2e2876aSanish kumar }, 287a2e2876aSanish kumar }; 288a2e2876aSanish kumar 289a2e2876aSanish kumar static void max98926_set_sense_data(struct max98926_priv *max98926) 290a2e2876aSanish kumar { 291a2e2876aSanish kumar regmap_update_bits(max98926->regmap, 292a2e2876aSanish kumar MAX98926_DOUT_CFG_VMON, 293a2e2876aSanish kumar MAX98926_DAI_VMON_EN_MASK, 294a2e2876aSanish kumar MAX98926_DAI_VMON_EN_MASK); 295a2e2876aSanish kumar regmap_update_bits(max98926->regmap, 296a2e2876aSanish kumar MAX98926_DOUT_CFG_IMON, 297a2e2876aSanish kumar MAX98926_DAI_IMON_EN_MASK, 298a2e2876aSanish kumar MAX98926_DAI_IMON_EN_MASK); 299a2e2876aSanish kumar 300a2e2876aSanish kumar if (!max98926->interleave_mode) { 301a2e2876aSanish kumar /* set VMON slots */ 302a2e2876aSanish kumar regmap_update_bits(max98926->regmap, 303a2e2876aSanish kumar MAX98926_DOUT_CFG_VMON, 304a2e2876aSanish kumar MAX98926_DAI_VMON_SLOT_MASK, 305a2e2876aSanish kumar max98926->v_slot); 306a2e2876aSanish kumar /* set IMON slots */ 307a2e2876aSanish kumar regmap_update_bits(max98926->regmap, 308a2e2876aSanish kumar MAX98926_DOUT_CFG_IMON, 309a2e2876aSanish kumar MAX98926_DAI_IMON_SLOT_MASK, 310a2e2876aSanish kumar max98926->i_slot); 311a2e2876aSanish kumar } else { 312a2e2876aSanish kumar /* enable interleave mode */ 313a2e2876aSanish kumar regmap_update_bits(max98926->regmap, 314a2e2876aSanish kumar MAX98926_FORMAT, 315a2e2876aSanish kumar MAX98926_DAI_INTERLEAVE_MASK, 316a2e2876aSanish kumar MAX98926_DAI_INTERLEAVE_MASK); 317a2e2876aSanish kumar /* set interleave slots */ 318a2e2876aSanish kumar regmap_update_bits(max98926->regmap, 319a2e2876aSanish kumar MAX98926_DOUT_CFG_VBAT, 320a2e2876aSanish kumar MAX98926_DAI_INTERLEAVE_SLOT_MASK, 321a2e2876aSanish kumar max98926->v_slot); 322a2e2876aSanish kumar } 323a2e2876aSanish kumar } 324a2e2876aSanish kumar 325a2e2876aSanish kumar static int max98926_dai_set_fmt(struct snd_soc_dai *codec_dai, 326a2e2876aSanish kumar unsigned int fmt) 327a2e2876aSanish kumar { 3286f2b5d0dSKuninori Morimoto struct snd_soc_component *component = codec_dai->component; 3296f2b5d0dSKuninori Morimoto struct max98926_priv *max98926 = snd_soc_component_get_drvdata(component); 330a2e2876aSanish kumar unsigned int invert = 0; 331a2e2876aSanish kumar 3326f2b5d0dSKuninori Morimoto dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt); 333a2e2876aSanish kumar 334*502e1c8dSMark Brown switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 335*502e1c8dSMark Brown case SND_SOC_DAIFMT_CBC_CFC: 336a2e2876aSanish kumar max98926_set_sense_data(max98926); 337a2e2876aSanish kumar break; 338a2e2876aSanish kumar default: 3396f2b5d0dSKuninori Morimoto dev_err(component->dev, "DAI clock mode unsupported\n"); 340a2e2876aSanish kumar return -EINVAL; 341a2e2876aSanish kumar } 342a2e2876aSanish kumar 343a2e2876aSanish kumar switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 344a2e2876aSanish kumar case SND_SOC_DAIFMT_NB_NF: 345a2e2876aSanish kumar break; 346a2e2876aSanish kumar case SND_SOC_DAIFMT_NB_IF: 347a2e2876aSanish kumar invert = MAX98926_DAI_WCI_MASK; 348a2e2876aSanish kumar break; 349a2e2876aSanish kumar case SND_SOC_DAIFMT_IB_NF: 350a2e2876aSanish kumar invert = MAX98926_DAI_BCI_MASK; 351a2e2876aSanish kumar break; 352a2e2876aSanish kumar case SND_SOC_DAIFMT_IB_IF: 353a2e2876aSanish kumar invert = MAX98926_DAI_BCI_MASK | MAX98926_DAI_WCI_MASK; 354a2e2876aSanish kumar break; 355a2e2876aSanish kumar default: 3566f2b5d0dSKuninori Morimoto dev_err(component->dev, "DAI invert mode unsupported\n"); 357a2e2876aSanish kumar return -EINVAL; 358a2e2876aSanish kumar } 359a2e2876aSanish kumar 360a2e2876aSanish kumar regmap_write(max98926->regmap, 361a2e2876aSanish kumar MAX98926_FORMAT, MAX98926_DAI_DLY_MASK); 362a2e2876aSanish kumar regmap_update_bits(max98926->regmap, MAX98926_FORMAT, 363a2e2876aSanish kumar MAX98926_DAI_BCI_MASK, invert); 364a2e2876aSanish kumar return 0; 365a2e2876aSanish kumar } 366a2e2876aSanish kumar 367a2e2876aSanish kumar static int max98926_dai_hw_params(struct snd_pcm_substream *substream, 368a2e2876aSanish kumar struct snd_pcm_hw_params *params, 369a2e2876aSanish kumar struct snd_soc_dai *dai) 370a2e2876aSanish kumar { 371a2e2876aSanish kumar int dai_sr = -EINVAL; 372a2e2876aSanish kumar int rate = params_rate(params), i; 3736f2b5d0dSKuninori Morimoto struct snd_soc_component *component = dai->component; 3746f2b5d0dSKuninori Morimoto struct max98926_priv *max98926 = snd_soc_component_get_drvdata(component); 375cffee535SAxel Lin int blr_clk_ratio; 376a2e2876aSanish kumar 377a2e2876aSanish kumar switch (params_format(params)) { 378a2e2876aSanish kumar case SNDRV_PCM_FORMAT_S16_LE: 379a2e2876aSanish kumar regmap_update_bits(max98926->regmap, 380a2e2876aSanish kumar MAX98926_FORMAT, 381a2e2876aSanish kumar MAX98926_DAI_CHANSZ_MASK, 382a2e2876aSanish kumar MAX98926_DAI_CHANSZ_16); 383a2e2876aSanish kumar max98926->ch_size = 16; 384a2e2876aSanish kumar break; 385a2e2876aSanish kumar case SNDRV_PCM_FORMAT_S24_LE: 386a2e2876aSanish kumar regmap_update_bits(max98926->regmap, 387a2e2876aSanish kumar MAX98926_FORMAT, 388a2e2876aSanish kumar MAX98926_DAI_CHANSZ_MASK, 389a2e2876aSanish kumar MAX98926_DAI_CHANSZ_24); 390a2e2876aSanish kumar max98926->ch_size = 24; 391a2e2876aSanish kumar break; 392a2e2876aSanish kumar case SNDRV_PCM_FORMAT_S32_LE: 393a2e2876aSanish kumar regmap_update_bits(max98926->regmap, 394a2e2876aSanish kumar MAX98926_FORMAT, 395a2e2876aSanish kumar MAX98926_DAI_CHANSZ_MASK, 396a2e2876aSanish kumar MAX98926_DAI_CHANSZ_32); 397a2e2876aSanish kumar max98926->ch_size = 32; 398a2e2876aSanish kumar break; 399a2e2876aSanish kumar default: 4006f2b5d0dSKuninori Morimoto dev_dbg(component->dev, "format unsupported %d\n", 401a2e2876aSanish kumar params_format(params)); 402a2e2876aSanish kumar return -EINVAL; 403a2e2876aSanish kumar } 404a2e2876aSanish kumar 405cffee535SAxel Lin /* BCLK/LRCLK ratio calculation */ 406cffee535SAxel Lin blr_clk_ratio = params_channels(params) * max98926->ch_size; 407cffee535SAxel Lin 408a2e2876aSanish kumar switch (blr_clk_ratio) { 409a2e2876aSanish kumar case 32: 410a2e2876aSanish kumar regmap_update_bits(max98926->regmap, 411a2e2876aSanish kumar MAX98926_DAI_CLK_MODE2, 412a2e2876aSanish kumar MAX98926_DAI_BSEL_MASK, 413a2e2876aSanish kumar MAX98926_DAI_BSEL_32); 414a2e2876aSanish kumar break; 415a2e2876aSanish kumar case 48: 416a2e2876aSanish kumar regmap_update_bits(max98926->regmap, 417a2e2876aSanish kumar MAX98926_DAI_CLK_MODE2, 418a2e2876aSanish kumar MAX98926_DAI_BSEL_MASK, 419a2e2876aSanish kumar MAX98926_DAI_BSEL_48); 420a2e2876aSanish kumar break; 421a2e2876aSanish kumar case 64: 422a2e2876aSanish kumar regmap_update_bits(max98926->regmap, 423a2e2876aSanish kumar MAX98926_DAI_CLK_MODE2, 424a2e2876aSanish kumar MAX98926_DAI_BSEL_MASK, 425a2e2876aSanish kumar MAX98926_DAI_BSEL_64); 426a2e2876aSanish kumar break; 427a2e2876aSanish kumar default: 428a2e2876aSanish kumar return -EINVAL; 429a2e2876aSanish kumar } 430a2e2876aSanish kumar 431a2e2876aSanish kumar /* find the closest rate */ 432a2e2876aSanish kumar for (i = 0; i < ARRAY_SIZE(rate_table); i++) { 433a2e2876aSanish kumar if (rate_table[i].rate >= rate) { 434a2e2876aSanish kumar dai_sr = rate_table[i].sr; 435a2e2876aSanish kumar break; 436a2e2876aSanish kumar } 437a2e2876aSanish kumar } 438a2e2876aSanish kumar if (dai_sr < 0) 439a2e2876aSanish kumar return -EINVAL; 440a2e2876aSanish kumar 441a2e2876aSanish kumar /* set DAI_SR to correct LRCLK frequency */ 442a2e2876aSanish kumar regmap_update_bits(max98926->regmap, 443a2e2876aSanish kumar MAX98926_DAI_CLK_MODE2, 444a2e2876aSanish kumar MAX98926_DAI_SR_MASK, dai_sr << MAX98926_DAI_SR_SHIFT); 445a2e2876aSanish kumar return 0; 446a2e2876aSanish kumar } 447a2e2876aSanish kumar 448a2e2876aSanish kumar #define MAX98926_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ 449a2e2876aSanish kumar SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) 450a2e2876aSanish kumar 451eb59d73cSArvind Yadav static const struct snd_soc_dai_ops max98926_dai_ops = { 452a2e2876aSanish kumar .set_fmt = max98926_dai_set_fmt, 453a2e2876aSanish kumar .hw_params = max98926_dai_hw_params, 454a2e2876aSanish kumar }; 455a2e2876aSanish kumar 456a2e2876aSanish kumar static struct snd_soc_dai_driver max98926_dai[] = { 457a2e2876aSanish kumar { 458a2e2876aSanish kumar .name = "max98926-aif1", 459a2e2876aSanish kumar .playback = { 460a2e2876aSanish kumar .stream_name = "HiFi Playback", 461a2e2876aSanish kumar .channels_min = 1, 462a2e2876aSanish kumar .channels_max = 2, 463a2e2876aSanish kumar .rates = SNDRV_PCM_RATE_8000_48000, 464a2e2876aSanish kumar .formats = MAX98926_FORMATS, 465a2e2876aSanish kumar }, 466a2e2876aSanish kumar .capture = { 467a2e2876aSanish kumar .stream_name = "HiFi Capture", 468a2e2876aSanish kumar .channels_min = 1, 469a2e2876aSanish kumar .channels_max = 2, 470a2e2876aSanish kumar .rates = SNDRV_PCM_RATE_8000_48000, 471a2e2876aSanish kumar .formats = MAX98926_FORMATS, 472a2e2876aSanish kumar }, 473a2e2876aSanish kumar .ops = &max98926_dai_ops, 474a2e2876aSanish kumar } 475a2e2876aSanish kumar }; 476a2e2876aSanish kumar 4776f2b5d0dSKuninori Morimoto static int max98926_probe(struct snd_soc_component *component) 478a2e2876aSanish kumar { 4796f2b5d0dSKuninori Morimoto struct max98926_priv *max98926 = snd_soc_component_get_drvdata(component); 480a2e2876aSanish kumar 4816f2b5d0dSKuninori Morimoto max98926->component = component; 482866b9c81SKuninori Morimoto 483a2e2876aSanish kumar /* Hi-Z all the slots */ 484a2e2876aSanish kumar regmap_write(max98926->regmap, MAX98926_DOUT_HIZ_CFG4, 0xF0); 485a2e2876aSanish kumar return 0; 486a2e2876aSanish kumar } 487a2e2876aSanish kumar 4886f2b5d0dSKuninori Morimoto static const struct snd_soc_component_driver soc_component_dev_max98926 = { 489a2e2876aSanish kumar .probe = max98926_probe, 490a2e2876aSanish kumar .controls = max98926_snd_controls, 491a2e2876aSanish kumar .num_controls = ARRAY_SIZE(max98926_snd_controls), 492a2e2876aSanish kumar .dapm_routes = max98926_audio_map, 493a2e2876aSanish kumar .num_dapm_routes = ARRAY_SIZE(max98926_audio_map), 494a2e2876aSanish kumar .dapm_widgets = max98926_dapm_widgets, 495a2e2876aSanish kumar .num_dapm_widgets = ARRAY_SIZE(max98926_dapm_widgets), 4966f2b5d0dSKuninori Morimoto .idle_bias_on = 1, 4976f2b5d0dSKuninori Morimoto .use_pmdown_time = 1, 4986f2b5d0dSKuninori Morimoto .endianness = 1, 4996f2b5d0dSKuninori Morimoto .non_legacy_dai_naming = 1, 500a2e2876aSanish kumar }; 501a2e2876aSanish kumar 502e354d86eSAxel Lin static const struct regmap_config max98926_regmap = { 503a2e2876aSanish kumar .reg_bits = 8, 504a2e2876aSanish kumar .val_bits = 8, 505a2e2876aSanish kumar .max_register = MAX98926_VERSION, 506a2e2876aSanish kumar .reg_defaults = max98926_reg, 507a2e2876aSanish kumar .num_reg_defaults = ARRAY_SIZE(max98926_reg), 508a2e2876aSanish kumar .volatile_reg = max98926_volatile_register, 509a2e2876aSanish kumar .readable_reg = max98926_readable_register, 510a2e2876aSanish kumar .cache_type = REGCACHE_RBTREE, 511a2e2876aSanish kumar }; 512a2e2876aSanish kumar 513a2e2876aSanish kumar static int max98926_i2c_probe(struct i2c_client *i2c, 514a2e2876aSanish kumar const struct i2c_device_id *id) 515a2e2876aSanish kumar { 516a2e2876aSanish kumar int ret, reg; 517a2e2876aSanish kumar u32 value; 518a2e2876aSanish kumar struct max98926_priv *max98926; 519a2e2876aSanish kumar 520a2e2876aSanish kumar max98926 = devm_kzalloc(&i2c->dev, 521a2e2876aSanish kumar sizeof(*max98926), GFP_KERNEL); 522a2e2876aSanish kumar if (!max98926) 523a2e2876aSanish kumar return -ENOMEM; 524a2e2876aSanish kumar 525a2e2876aSanish kumar i2c_set_clientdata(i2c, max98926); 526a2e2876aSanish kumar max98926->regmap = devm_regmap_init_i2c(i2c, &max98926_regmap); 527a2e2876aSanish kumar if (IS_ERR(max98926->regmap)) { 528a2e2876aSanish kumar ret = PTR_ERR(max98926->regmap); 529a2e2876aSanish kumar dev_err(&i2c->dev, 530a2e2876aSanish kumar "Failed to allocate regmap: %d\n", ret); 531a2e2876aSanish kumar goto err_out; 532a2e2876aSanish kumar } 533a2e2876aSanish kumar if (of_property_read_bool(i2c->dev.of_node, "interleave-mode")) 534a2e2876aSanish kumar max98926->interleave_mode = true; 535a2e2876aSanish kumar 536a2e2876aSanish kumar if (!of_property_read_u32(i2c->dev.of_node, "vmon-slot-no", &value)) { 537a2e2876aSanish kumar if (value > MAX98926_DAI_VMON_SLOT_1E_1F) { 538a2e2876aSanish kumar dev_err(&i2c->dev, "vmon slot number is wrong:\n"); 539a2e2876aSanish kumar return -EINVAL; 540a2e2876aSanish kumar } 541a2e2876aSanish kumar max98926->v_slot = value; 542a2e2876aSanish kumar } 543a2e2876aSanish kumar if (!of_property_read_u32(i2c->dev.of_node, "imon-slot-no", &value)) { 544a2e2876aSanish kumar if (value > MAX98926_DAI_IMON_SLOT_1E_1F) { 545a2e2876aSanish kumar dev_err(&i2c->dev, "imon slot number is wrong:\n"); 546a2e2876aSanish kumar return -EINVAL; 547a2e2876aSanish kumar } 548a2e2876aSanish kumar max98926->i_slot = value; 549a2e2876aSanish kumar } 550a2e2876aSanish kumar ret = regmap_read(max98926->regmap, 551a2e2876aSanish kumar MAX98926_VERSION, ®); 552a2e2876aSanish kumar if (ret < 0) { 553a2e2876aSanish kumar dev_err(&i2c->dev, "Failed to read: %x\n", reg); 554a2e2876aSanish kumar return ret; 555a2e2876aSanish kumar } 556a2e2876aSanish kumar 5576f2b5d0dSKuninori Morimoto ret = devm_snd_soc_register_component(&i2c->dev, 5586f2b5d0dSKuninori Morimoto &soc_component_dev_max98926, 559a2e2876aSanish kumar max98926_dai, ARRAY_SIZE(max98926_dai)); 560a2e2876aSanish kumar if (ret < 0) 561a2e2876aSanish kumar dev_err(&i2c->dev, 5626f2b5d0dSKuninori Morimoto "Failed to register component: %d\n", ret); 563a2e2876aSanish kumar dev_info(&i2c->dev, "device version: %x\n", reg); 564a2e2876aSanish kumar err_out: 565a2e2876aSanish kumar return ret; 566a2e2876aSanish kumar } 567a2e2876aSanish kumar 568a2e2876aSanish kumar static const struct i2c_device_id max98926_i2c_id[] = { 569a2e2876aSanish kumar { "max98926", 0 }, 570a2e2876aSanish kumar { } 571a2e2876aSanish kumar }; 572a2e2876aSanish kumar MODULE_DEVICE_TABLE(i2c, max98926_i2c_id); 573a2e2876aSanish kumar 574fff68ff6SKrzysztof Kozlowski #ifdef CONFIG_OF 575a2e2876aSanish kumar static const struct of_device_id max98926_of_match[] = { 576a2e2876aSanish kumar { .compatible = "maxim,max98926", }, 577a2e2876aSanish kumar { } 578a2e2876aSanish kumar }; 579a2e2876aSanish kumar MODULE_DEVICE_TABLE(of, max98926_of_match); 580fff68ff6SKrzysztof Kozlowski #endif 581a2e2876aSanish kumar 582a2e2876aSanish kumar static struct i2c_driver max98926_i2c_driver = { 583a2e2876aSanish kumar .driver = { 584a2e2876aSanish kumar .name = "max98926", 585a2e2876aSanish kumar .of_match_table = of_match_ptr(max98926_of_match), 586a2e2876aSanish kumar }, 587a2e2876aSanish kumar .probe = max98926_i2c_probe, 588a2e2876aSanish kumar .id_table = max98926_i2c_id, 589a2e2876aSanish kumar }; 590a2e2876aSanish kumar 591a2e2876aSanish kumar module_i2c_driver(max98926_i2c_driver) 592a2e2876aSanish kumar MODULE_DESCRIPTION("ALSA SoC MAX98926 driver"); 593a2e2876aSanish kumar MODULE_AUTHOR("Anish kumar <anish.kumar@maximintegrated.com>"); 594a2e2876aSanish kumar MODULE_LICENSE("GPL"); 595