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