xref: /linux/sound/soc/codecs/max98926.c (revision e354d86e551656668e879c833de9890b5053cc88)
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, &reg);
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