xref: /linux/sound/soc/codecs/tas2764.c (revision ab93e0dd72c37d378dd936f031ffb83ff2bd87ce)
1827ed8a0SDan Murphy // SPDX-License-Identifier: GPL-2.0
2827ed8a0SDan Murphy //
3827ed8a0SDan Murphy // Driver for the Texas Instruments TAS2764 CODEC
4827ed8a0SDan Murphy // Copyright (C) 2020 Texas Instruments Inc.
5827ed8a0SDan Murphy 
6827ed8a0SDan Murphy #include <linux/module.h>
7827ed8a0SDan Murphy #include <linux/moduleparam.h>
8827ed8a0SDan Murphy #include <linux/err.h>
9827ed8a0SDan Murphy #include <linux/init.h>
10827ed8a0SDan Murphy #include <linux/delay.h>
11186dfc85SJames Calligeros #include <linux/hwmon.h>
12827ed8a0SDan Murphy #include <linux/pm.h>
13827ed8a0SDan Murphy #include <linux/i2c.h>
14827ed8a0SDan Murphy #include <linux/gpio/consumer.h>
15827ed8a0SDan Murphy #include <linux/regulator/consumer.h>
16827ed8a0SDan Murphy #include <linux/regmap.h>
17827ed8a0SDan Murphy #include <linux/of.h>
18ad183929SMartin Povišer #include <linux/of_device.h>
19827ed8a0SDan Murphy #include <linux/slab.h>
20827ed8a0SDan Murphy #include <sound/soc.h>
21827ed8a0SDan Murphy #include <sound/pcm.h>
22827ed8a0SDan Murphy #include <sound/pcm_params.h>
23827ed8a0SDan Murphy #include <sound/initval.h>
24827ed8a0SDan Murphy #include <sound/tlv.h>
25827ed8a0SDan Murphy 
26827ed8a0SDan Murphy #include "tas2764.h"
27827ed8a0SDan Murphy 
28ad183929SMartin Povišer enum tas2764_devid {
29ad183929SMartin Povišer 	DEVID_TAS2764  = 0,
30ad183929SMartin Povišer 	DEVID_SN012776 = 1
31ad183929SMartin Povišer };
32ad183929SMartin Povišer 
33827ed8a0SDan Murphy struct tas2764_priv {
34827ed8a0SDan Murphy 	struct snd_soc_component *component;
35827ed8a0SDan Murphy 	struct gpio_desc *reset_gpio;
36827ed8a0SDan Murphy 	struct gpio_desc *sdz_gpio;
37827ed8a0SDan Murphy 	struct regmap *regmap;
38827ed8a0SDan Murphy 	struct device *dev;
39dae191fbSMartin Povišer 	int irq;
40ad183929SMartin Povišer 	enum tas2764_devid devid;
41827ed8a0SDan Murphy 
42827ed8a0SDan Murphy 	int v_sense_slot;
43827ed8a0SDan Murphy 	int i_sense_slot;
44f5ad67f1SMartin Povišer 
45f5ad67f1SMartin Povišer 	bool dac_powered;
46f5ad67f1SMartin Povišer 	bool unmuted;
47827ed8a0SDan Murphy };
48827ed8a0SDan Murphy 
49f33b01e0SMartin Povišer #include "tas2764-quirks.h"
50f33b01e0SMartin Povišer 
51dae191fbSMartin Povišer static const char *tas2764_int_ltch0_msgs[8] = {
52dae191fbSMartin Povišer 	"fault: over temperature", /* INT_LTCH0 & BIT(0) */
53dae191fbSMartin Povišer 	"fault: over current",
54dae191fbSMartin Povišer 	"fault: bad TDM clock",
55dae191fbSMartin Povišer 	"limiter active",
56dae191fbSMartin Povišer 	"fault: PVDD below limiter inflection point",
57dae191fbSMartin Povišer 	"fault: limiter max attenuation",
58dae191fbSMartin Povišer 	"fault: BOP infinite hold",
59dae191fbSMartin Povišer 	"fault: BOP mute", /* INT_LTCH0 & BIT(7) */
60dae191fbSMartin Povišer };
61dae191fbSMartin Povišer 
62dae191fbSMartin Povišer static const unsigned int tas2764_int_readout_regs[6] = {
63dae191fbSMartin Povišer 	TAS2764_INT_LTCH0,
64dae191fbSMartin Povišer 	TAS2764_INT_LTCH1,
65dae191fbSMartin Povišer 	TAS2764_INT_LTCH1_0,
66dae191fbSMartin Povišer 	TAS2764_INT_LTCH2,
67dae191fbSMartin Povišer 	TAS2764_INT_LTCH3,
68dae191fbSMartin Povišer 	TAS2764_INT_LTCH4,
69dae191fbSMartin Povišer };
70dae191fbSMartin Povišer 
tas2764_irq(int irq,void * data)71dae191fbSMartin Povišer static irqreturn_t tas2764_irq(int irq, void *data)
72dae191fbSMartin Povišer {
73dae191fbSMartin Povišer 	struct tas2764_priv *tas2764 = data;
74dae191fbSMartin Povišer 	u8 latched[6] = {0, 0, 0, 0, 0, 0};
75dae191fbSMartin Povišer 	int ret = IRQ_NONE;
76dae191fbSMartin Povišer 	int i;
77dae191fbSMartin Povišer 
78dae191fbSMartin Povišer 	for (i = 0; i < ARRAY_SIZE(latched); i++)
79dae191fbSMartin Povišer 		latched[i] = snd_soc_component_read(tas2764->component,
80dae191fbSMartin Povišer 						    tas2764_int_readout_regs[i]);
81dae191fbSMartin Povišer 
82dae191fbSMartin Povišer 	for (i = 0; i < 8; i++) {
83dae191fbSMartin Povišer 		if (latched[0] & BIT(i)) {
84dae191fbSMartin Povišer 			dev_crit_ratelimited(tas2764->dev, "%s\n",
85dae191fbSMartin Povišer 					     tas2764_int_ltch0_msgs[i]);
86dae191fbSMartin Povišer 			ret = IRQ_HANDLED;
87dae191fbSMartin Povišer 		}
88dae191fbSMartin Povišer 	}
89dae191fbSMartin Povišer 
90dae191fbSMartin Povišer 	if (latched[0]) {
91dae191fbSMartin Povišer 		dev_err_ratelimited(tas2764->dev, "other context to the fault: %02x,%02x,%02x,%02x,%02x",
92dae191fbSMartin Povišer 				    latched[1], latched[2], latched[3], latched[4], latched[5]);
93dae191fbSMartin Povišer 		snd_soc_component_update_bits(tas2764->component,
94dae191fbSMartin Povišer 					      TAS2764_INT_CLK_CFG,
95dae191fbSMartin Povišer 					      TAS2764_INT_CLK_CFG_IRQZ_CLR,
96dae191fbSMartin Povišer 					      TAS2764_INT_CLK_CFG_IRQZ_CLR);
97dae191fbSMartin Povišer 	}
98dae191fbSMartin Povišer 
99dae191fbSMartin Povišer 	return ret;
100dae191fbSMartin Povišer }
101dae191fbSMartin Povišer 
tas2764_reset(struct tas2764_priv * tas2764)102827ed8a0SDan Murphy static void tas2764_reset(struct tas2764_priv *tas2764)
103827ed8a0SDan Murphy {
104827ed8a0SDan Murphy 	if (tas2764->reset_gpio) {
105827ed8a0SDan Murphy 		gpiod_set_value_cansleep(tas2764->reset_gpio, 0);
106827ed8a0SDan Murphy 		msleep(20);
107827ed8a0SDan Murphy 		gpiod_set_value_cansleep(tas2764->reset_gpio, 1);
108cd10bb89SMartin Povišer 		usleep_range(1000, 2000);
109827ed8a0SDan Murphy 	}
110827ed8a0SDan Murphy 
111827ed8a0SDan Murphy 	snd_soc_component_write(tas2764->component, TAS2764_SW_RST,
112827ed8a0SDan Murphy 				TAS2764_RST);
113cd10bb89SMartin Povišer 	usleep_range(1000, 2000);
114827ed8a0SDan Murphy }
115827ed8a0SDan Murphy 
tas2764_update_pwr_ctrl(struct tas2764_priv * tas2764)116f5ad67f1SMartin Povišer static int tas2764_update_pwr_ctrl(struct tas2764_priv *tas2764)
117f5ad67f1SMartin Povišer {
118f5ad67f1SMartin Povišer 	struct snd_soc_component *component = tas2764->component;
119f5ad67f1SMartin Povišer 	unsigned int val;
120f5ad67f1SMartin Povišer 	int ret;
121f5ad67f1SMartin Povišer 
122f5ad67f1SMartin Povišer 	if (tas2764->dac_powered)
123f5ad67f1SMartin Povišer 		val = tas2764->unmuted ?
124f5ad67f1SMartin Povišer 			TAS2764_PWR_CTRL_ACTIVE : TAS2764_PWR_CTRL_MUTE;
125f5ad67f1SMartin Povišer 	else
126f5ad67f1SMartin Povišer 		val = TAS2764_PWR_CTRL_SHUTDOWN;
127f5ad67f1SMartin Povišer 
128f33b01e0SMartin Povišer 	if (ENABLED_APPLE_QUIRKS & TAS2764_SHUTDOWN_DANCE)
129f33b01e0SMartin Povišer 		return tas2764_do_quirky_pwr_ctrl_change(tas2764, val);
130f33b01e0SMartin Povišer 
131f5ad67f1SMartin Povišer 	ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
132f5ad67f1SMartin Povišer 					    TAS2764_PWR_CTRL_MASK, val);
133f5ad67f1SMartin Povišer 	if (ret < 0)
134f5ad67f1SMartin Povišer 		return ret;
135f5ad67f1SMartin Povišer 
136f5ad67f1SMartin Povišer 	return 0;
137f5ad67f1SMartin Povišer }
138f5ad67f1SMartin Povišer 
139827ed8a0SDan Murphy #ifdef CONFIG_PM
tas2764_codec_suspend(struct snd_soc_component * component)140827ed8a0SDan Murphy static int tas2764_codec_suspend(struct snd_soc_component *component)
141827ed8a0SDan Murphy {
142827ed8a0SDan Murphy 	struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
143827ed8a0SDan Murphy 	int ret;
144827ed8a0SDan Murphy 
145827ed8a0SDan Murphy 	ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
146827ed8a0SDan Murphy 					    TAS2764_PWR_CTRL_MASK,
147827ed8a0SDan Murphy 					    TAS2764_PWR_CTRL_SHUTDOWN);
148827ed8a0SDan Murphy 
149827ed8a0SDan Murphy 	if (ret < 0)
150827ed8a0SDan Murphy 		return ret;
151827ed8a0SDan Murphy 
152827ed8a0SDan Murphy 	if (tas2764->sdz_gpio)
153827ed8a0SDan Murphy 		gpiod_set_value_cansleep(tas2764->sdz_gpio, 0);
154827ed8a0SDan Murphy 
155827ed8a0SDan Murphy 	regcache_cache_only(tas2764->regmap, true);
156827ed8a0SDan Murphy 	regcache_mark_dirty(tas2764->regmap);
157827ed8a0SDan Murphy 
15808a66f55SHector Martin 	usleep_range(6000, 7000);
15908a66f55SHector Martin 
160827ed8a0SDan Murphy 	return 0;
161827ed8a0SDan Murphy }
162827ed8a0SDan Murphy 
tas2764_codec_resume(struct snd_soc_component * component)163827ed8a0SDan Murphy static int tas2764_codec_resume(struct snd_soc_component *component)
164827ed8a0SDan Murphy {
165827ed8a0SDan Murphy 	struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
166827ed8a0SDan Murphy 	int ret;
167827ed8a0SDan Murphy 
168cd10bb89SMartin Povišer 	if (tas2764->sdz_gpio) {
169827ed8a0SDan Murphy 		gpiod_set_value_cansleep(tas2764->sdz_gpio, 1);
170cd10bb89SMartin Povišer 		usleep_range(1000, 2000);
171cd10bb89SMartin Povišer 	}
172827ed8a0SDan Murphy 
173f5ad67f1SMartin Povišer 	ret = tas2764_update_pwr_ctrl(tas2764);
174827ed8a0SDan Murphy 
175827ed8a0SDan Murphy 	if (ret < 0)
176827ed8a0SDan Murphy 		return ret;
177827ed8a0SDan Murphy 
178827ed8a0SDan Murphy 	regcache_cache_only(tas2764->regmap, false);
179827ed8a0SDan Murphy 
180827ed8a0SDan Murphy 	return regcache_sync(tas2764->regmap);
181827ed8a0SDan Murphy }
182827ed8a0SDan Murphy #else
183827ed8a0SDan Murphy #define tas2764_codec_suspend NULL
184827ed8a0SDan Murphy #define tas2764_codec_resume NULL
185827ed8a0SDan Murphy #endif
186827ed8a0SDan Murphy 
187827ed8a0SDan Murphy static const char * const tas2764_ASI1_src[] = {
188827ed8a0SDan Murphy 	"I2C offset", "Left", "Right", "LeftRightDiv2",
189827ed8a0SDan Murphy };
190827ed8a0SDan Murphy 
191827ed8a0SDan Murphy static SOC_ENUM_SINGLE_DECL(
192d1a10f1bSMartin Povišer 	tas2764_ASI1_src_enum, TAS2764_TDM_CFG2, TAS2764_TDM_CFG2_SCFG_SHIFT,
193d1a10f1bSMartin Povišer 	tas2764_ASI1_src);
194827ed8a0SDan Murphy 
195827ed8a0SDan Murphy static const struct snd_kcontrol_new tas2764_asi1_mux =
196827ed8a0SDan Murphy 	SOC_DAPM_ENUM("ASI1 Source", tas2764_ASI1_src_enum);
197827ed8a0SDan Murphy 
198827ed8a0SDan Murphy static const struct snd_kcontrol_new isense_switch =
199827ed8a0SDan Murphy 	SOC_DAPM_SINGLE("Switch", TAS2764_PWR_CTRL, TAS2764_ISENSE_POWER_EN, 1, 1);
200827ed8a0SDan Murphy static const struct snd_kcontrol_new vsense_switch =
201827ed8a0SDan Murphy 	SOC_DAPM_SINGLE("Switch", TAS2764_PWR_CTRL, TAS2764_VSENSE_POWER_EN, 1, 1);
202827ed8a0SDan Murphy 
203827ed8a0SDan Murphy static const struct snd_soc_dapm_widget tas2764_dapm_widgets[] = {
204827ed8a0SDan Murphy 	SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0),
205827ed8a0SDan Murphy 	SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2764_asi1_mux),
206827ed8a0SDan Murphy 	SND_SOC_DAPM_SWITCH("ISENSE", TAS2764_PWR_CTRL, TAS2764_ISENSE_POWER_EN,
207827ed8a0SDan Murphy 			    1, &isense_switch),
208827ed8a0SDan Murphy 	SND_SOC_DAPM_SWITCH("VSENSE", TAS2764_PWR_CTRL, TAS2764_VSENSE_POWER_EN,
209827ed8a0SDan Murphy 			    1, &vsense_switch),
2101c3b5f37SHector Martin 	SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
211827ed8a0SDan Murphy 	SND_SOC_DAPM_OUTPUT("OUT"),
212827ed8a0SDan Murphy 	SND_SOC_DAPM_SIGGEN("VMON"),
213827ed8a0SDan Murphy 	SND_SOC_DAPM_SIGGEN("IMON")
214827ed8a0SDan Murphy };
215827ed8a0SDan Murphy 
216827ed8a0SDan Murphy static const struct snd_soc_dapm_route tas2764_audio_map[] = {
217827ed8a0SDan Murphy 	{"ASI1 Sel", "I2C offset", "ASI1"},
218827ed8a0SDan Murphy 	{"ASI1 Sel", "Left", "ASI1"},
219827ed8a0SDan Murphy 	{"ASI1 Sel", "Right", "ASI1"},
220827ed8a0SDan Murphy 	{"ASI1 Sel", "LeftRightDiv2", "ASI1"},
221827ed8a0SDan Murphy 	{"DAC", NULL, "ASI1 Sel"},
222827ed8a0SDan Murphy 	{"OUT", NULL, "DAC"},
223827ed8a0SDan Murphy 	{"ISENSE", "Switch", "IMON"},
224827ed8a0SDan Murphy 	{"VSENSE", "Switch", "VMON"},
225827ed8a0SDan Murphy };
226827ed8a0SDan Murphy 
tas2764_mute(struct snd_soc_dai * dai,int mute,int direction)227827ed8a0SDan Murphy static int tas2764_mute(struct snd_soc_dai *dai, int mute, int direction)
228827ed8a0SDan Murphy {
229f5ad67f1SMartin Povišer 	struct tas2764_priv *tas2764 =
230f5ad67f1SMartin Povišer 			snd_soc_component_get_drvdata(dai->component);
2311c3b5f37SHector Martin 	int ret;
2321c3b5f37SHector Martin 
2331c3b5f37SHector Martin 	if (!mute) {
2341c3b5f37SHector Martin 		tas2764->dac_powered = true;
2351c3b5f37SHector Martin 		ret = tas2764_update_pwr_ctrl(tas2764);
2361c3b5f37SHector Martin 		if (ret)
2371c3b5f37SHector Martin 			return ret;
2381c3b5f37SHector Martin 	}
239827ed8a0SDan Murphy 
240f5ad67f1SMartin Povišer 	tas2764->unmuted = !mute;
2411c3b5f37SHector Martin 	ret = tas2764_update_pwr_ctrl(tas2764);
2421c3b5f37SHector Martin 	if (ret)
2431c3b5f37SHector Martin 		return ret;
2441c3b5f37SHector Martin 
2451c3b5f37SHector Martin 	if (mute) {
24608a66f55SHector Martin 		/* Wait for ramp-down */
24708a66f55SHector Martin 		usleep_range(6000, 7000);
24808a66f55SHector Martin 
2491c3b5f37SHector Martin 		tas2764->dac_powered = false;
2501c3b5f37SHector Martin 		ret = tas2764_update_pwr_ctrl(tas2764);
2511c3b5f37SHector Martin 		if (ret)
2521c3b5f37SHector Martin 			return ret;
25308a66f55SHector Martin 
25408a66f55SHector Martin 		/* Wait a bit after shutdown */
25508a66f55SHector Martin 		usleep_range(2000, 3000);
2561c3b5f37SHector Martin 	}
2571c3b5f37SHector Martin 
2581c3b5f37SHector Martin 	return 0;
259827ed8a0SDan Murphy }
260827ed8a0SDan Murphy 
tas2764_set_bitwidth(struct tas2764_priv * tas2764,int bitwidth)261827ed8a0SDan Murphy static int tas2764_set_bitwidth(struct tas2764_priv *tas2764, int bitwidth)
262827ed8a0SDan Murphy {
263827ed8a0SDan Murphy 	struct snd_soc_component *component = tas2764->component;
264827ed8a0SDan Murphy 	int sense_en;
265827ed8a0SDan Murphy 	int val;
266827ed8a0SDan Murphy 	int ret;
267827ed8a0SDan Murphy 
268827ed8a0SDan Murphy 	switch (bitwidth) {
269827ed8a0SDan Murphy 	case SNDRV_PCM_FORMAT_S16_LE:
270827ed8a0SDan Murphy 		ret = snd_soc_component_update_bits(component,
271827ed8a0SDan Murphy 						    TAS2764_TDM_CFG2,
272827ed8a0SDan Murphy 						    TAS2764_TDM_CFG2_RXW_MASK,
273827ed8a0SDan Murphy 						    TAS2764_TDM_CFG2_RXW_16BITS);
274827ed8a0SDan Murphy 		break;
275827ed8a0SDan Murphy 	case SNDRV_PCM_FORMAT_S24_LE:
276827ed8a0SDan Murphy 		ret = snd_soc_component_update_bits(component,
277827ed8a0SDan Murphy 						    TAS2764_TDM_CFG2,
278827ed8a0SDan Murphy 						    TAS2764_TDM_CFG2_RXW_MASK,
279827ed8a0SDan Murphy 						    TAS2764_TDM_CFG2_RXW_24BITS);
280827ed8a0SDan Murphy 		break;
281827ed8a0SDan Murphy 	case SNDRV_PCM_FORMAT_S32_LE:
282827ed8a0SDan Murphy 		ret = snd_soc_component_update_bits(component,
283827ed8a0SDan Murphy 						    TAS2764_TDM_CFG2,
284827ed8a0SDan Murphy 						    TAS2764_TDM_CFG2_RXW_MASK,
285827ed8a0SDan Murphy 						    TAS2764_TDM_CFG2_RXW_32BITS);
286827ed8a0SDan Murphy 		break;
287827ed8a0SDan Murphy 
288827ed8a0SDan Murphy 	default:
289827ed8a0SDan Murphy 		return -EINVAL;
290827ed8a0SDan Murphy 	}
291827ed8a0SDan Murphy 
292827ed8a0SDan Murphy 	if (ret < 0)
293827ed8a0SDan Murphy 		return ret;
294827ed8a0SDan Murphy 
295827ed8a0SDan Murphy 	val = snd_soc_component_read(tas2764->component, TAS2764_PWR_CTRL);
296827ed8a0SDan Murphy 	if (val < 0)
297827ed8a0SDan Murphy 		return val;
298827ed8a0SDan Murphy 
299827ed8a0SDan Murphy 	if (val & (1 << TAS2764_VSENSE_POWER_EN))
300827ed8a0SDan Murphy 		sense_en = 0;
301827ed8a0SDan Murphy 	else
302827ed8a0SDan Murphy 		sense_en = TAS2764_TDM_CFG5_VSNS_ENABLE;
303827ed8a0SDan Murphy 
304827ed8a0SDan Murphy 	ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG5,
305827ed8a0SDan Murphy 					    TAS2764_TDM_CFG5_VSNS_ENABLE,
306827ed8a0SDan Murphy 					    sense_en);
307827ed8a0SDan Murphy 	if (ret < 0)
308827ed8a0SDan Murphy 		return ret;
309827ed8a0SDan Murphy 
310827ed8a0SDan Murphy 	if (val & (1 << TAS2764_ISENSE_POWER_EN))
311827ed8a0SDan Murphy 		sense_en = 0;
312827ed8a0SDan Murphy 	else
313827ed8a0SDan Murphy 		sense_en = TAS2764_TDM_CFG6_ISNS_ENABLE;
314827ed8a0SDan Murphy 
315827ed8a0SDan Murphy 	ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG6,
316827ed8a0SDan Murphy 					    TAS2764_TDM_CFG6_ISNS_ENABLE,
317827ed8a0SDan Murphy 					    sense_en);
318827ed8a0SDan Murphy 	if (ret < 0)
319827ed8a0SDan Murphy 		return ret;
320827ed8a0SDan Murphy 
321827ed8a0SDan Murphy 	return 0;
322827ed8a0SDan Murphy }
323827ed8a0SDan Murphy 
tas2764_set_samplerate(struct tas2764_priv * tas2764,int samplerate)324827ed8a0SDan Murphy static int tas2764_set_samplerate(struct tas2764_priv *tas2764, int samplerate)
325827ed8a0SDan Murphy {
326827ed8a0SDan Murphy 	struct snd_soc_component *component = tas2764->component;
327827ed8a0SDan Murphy 	int ramp_rate_val;
328827ed8a0SDan Murphy 	int ret;
329827ed8a0SDan Murphy 
330827ed8a0SDan Murphy 	switch (samplerate) {
331827ed8a0SDan Murphy 	case 48000:
332827ed8a0SDan Murphy 		ramp_rate_val = TAS2764_TDM_CFG0_SMP_48KHZ |
333827ed8a0SDan Murphy 				TAS2764_TDM_CFG0_44_1_48KHZ;
334827ed8a0SDan Murphy 		break;
335827ed8a0SDan Murphy 	case 44100:
336827ed8a0SDan Murphy 		ramp_rate_val = TAS2764_TDM_CFG0_SMP_44_1KHZ |
337827ed8a0SDan Murphy 				TAS2764_TDM_CFG0_44_1_48KHZ;
338827ed8a0SDan Murphy 		break;
339827ed8a0SDan Murphy 	case 96000:
340827ed8a0SDan Murphy 		ramp_rate_val = TAS2764_TDM_CFG0_SMP_48KHZ |
341827ed8a0SDan Murphy 				TAS2764_TDM_CFG0_88_2_96KHZ;
342827ed8a0SDan Murphy 		break;
343827ed8a0SDan Murphy 	case 88200:
344827ed8a0SDan Murphy 		ramp_rate_val = TAS2764_TDM_CFG0_SMP_44_1KHZ |
345827ed8a0SDan Murphy 				TAS2764_TDM_CFG0_88_2_96KHZ;
346827ed8a0SDan Murphy 		break;
347827ed8a0SDan Murphy 	default:
348827ed8a0SDan Murphy 		return -EINVAL;
349827ed8a0SDan Murphy 	}
350827ed8a0SDan Murphy 
351827ed8a0SDan Murphy 	ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG0,
352827ed8a0SDan Murphy 					    TAS2764_TDM_CFG0_SMP_MASK |
353827ed8a0SDan Murphy 					    TAS2764_TDM_CFG0_MASK,
354827ed8a0SDan Murphy 					    ramp_rate_val);
355827ed8a0SDan Murphy 	if (ret < 0)
356827ed8a0SDan Murphy 		return ret;
357827ed8a0SDan Murphy 
358827ed8a0SDan Murphy 	return 0;
359827ed8a0SDan Murphy }
360827ed8a0SDan Murphy 
tas2764_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)361827ed8a0SDan Murphy static int tas2764_hw_params(struct snd_pcm_substream *substream,
362827ed8a0SDan Murphy 			     struct snd_pcm_hw_params *params,
363827ed8a0SDan Murphy 			     struct snd_soc_dai *dai)
364827ed8a0SDan Murphy {
365827ed8a0SDan Murphy 	struct snd_soc_component *component = dai->component;
366827ed8a0SDan Murphy 	struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
367827ed8a0SDan Murphy 	int ret;
368827ed8a0SDan Murphy 
369827ed8a0SDan Murphy 	ret = tas2764_set_bitwidth(tas2764, params_format(params));
370827ed8a0SDan Murphy 	if (ret < 0)
371827ed8a0SDan Murphy 		return ret;
372827ed8a0SDan Murphy 
373827ed8a0SDan Murphy 	return tas2764_set_samplerate(tas2764, params_rate(params));
374827ed8a0SDan Murphy }
375827ed8a0SDan Murphy 
tas2764_set_fmt(struct snd_soc_dai * dai,unsigned int fmt)376827ed8a0SDan Murphy static int tas2764_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
377827ed8a0SDan Murphy {
378827ed8a0SDan Murphy 	struct snd_soc_component *component = dai->component;
379827ed8a0SDan Murphy 	struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
380f5468beeSHector Martin 	u8 tdm_rx_start_slot = 0, asi_cfg_0 = 0, asi_cfg_1 = 0, asi_cfg_4 = 0;
381827ed8a0SDan Murphy 	int ret;
382827ed8a0SDan Murphy 
383827ed8a0SDan Murphy 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
384d1a10f1bSMartin Povišer 	case SND_SOC_DAIFMT_NB_IF:
385d1a10f1bSMartin Povišer 		asi_cfg_0 ^= TAS2764_TDM_CFG0_FRAME_START;
386d1a10f1bSMartin Povišer 		fallthrough;
387827ed8a0SDan Murphy 	case SND_SOC_DAIFMT_NB_NF:
388827ed8a0SDan Murphy 		asi_cfg_1 = TAS2764_TDM_CFG1_RX_RISING;
389f5468beeSHector Martin 		asi_cfg_4 = TAS2764_TDM_CFG4_TX_FALLING;
390827ed8a0SDan Murphy 		break;
391d1a10f1bSMartin Povišer 	case SND_SOC_DAIFMT_IB_IF:
392d1a10f1bSMartin Povišer 		asi_cfg_0 ^= TAS2764_TDM_CFG0_FRAME_START;
393d1a10f1bSMartin Povišer 		fallthrough;
394827ed8a0SDan Murphy 	case SND_SOC_DAIFMT_IB_NF:
395827ed8a0SDan Murphy 		asi_cfg_1 = TAS2764_TDM_CFG1_RX_FALLING;
396f5468beeSHector Martin 		asi_cfg_4 = TAS2764_TDM_CFG4_TX_RISING;
397827ed8a0SDan Murphy 		break;
398827ed8a0SDan Murphy 	}
399827ed8a0SDan Murphy 
400827ed8a0SDan Murphy 	ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG1,
401827ed8a0SDan Murphy 					    TAS2764_TDM_CFG1_RX_MASK,
402827ed8a0SDan Murphy 					    asi_cfg_1);
403827ed8a0SDan Murphy 	if (ret < 0)
404827ed8a0SDan Murphy 		return ret;
405827ed8a0SDan Murphy 
406f5468beeSHector Martin 	ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG4,
407f5468beeSHector Martin 					    TAS2764_TDM_CFG4_TX_MASK,
408f5468beeSHector Martin 					    asi_cfg_4);
409f5468beeSHector Martin 	if (ret < 0)
410f5468beeSHector Martin 		return ret;
411f5468beeSHector Martin 
412827ed8a0SDan Murphy 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
413827ed8a0SDan Murphy 	case SND_SOC_DAIFMT_I2S:
414d1a10f1bSMartin Povišer 		asi_cfg_0 ^= TAS2764_TDM_CFG0_FRAME_START;
415d1a10f1bSMartin Povišer 		fallthrough;
416827ed8a0SDan Murphy 	case SND_SOC_DAIFMT_DSP_A:
417827ed8a0SDan Murphy 		tdm_rx_start_slot = 1;
418827ed8a0SDan Murphy 		break;
419827ed8a0SDan Murphy 	case SND_SOC_DAIFMT_DSP_B:
420827ed8a0SDan Murphy 	case SND_SOC_DAIFMT_LEFT_J:
421827ed8a0SDan Murphy 		tdm_rx_start_slot = 0;
422827ed8a0SDan Murphy 		break;
423827ed8a0SDan Murphy 	default:
424827ed8a0SDan Murphy 		dev_err(tas2764->dev,
425827ed8a0SDan Murphy 			"DAI Format is not found, fmt=0x%x\n", fmt);
426827ed8a0SDan Murphy 		return -EINVAL;
427827ed8a0SDan Murphy 	}
428827ed8a0SDan Murphy 
429d1a10f1bSMartin Povišer 	ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG0,
430d1a10f1bSMartin Povišer 					    TAS2764_TDM_CFG0_FRAME_START,
431d1a10f1bSMartin Povišer 					    asi_cfg_0);
432827ed8a0SDan Murphy 	if (ret < 0)
433827ed8a0SDan Murphy 		return ret;
434827ed8a0SDan Murphy 
435d1a10f1bSMartin Povišer 	ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG1,
436d1a10f1bSMartin Povišer 					    TAS2764_TDM_CFG1_MASK,
437d1a10f1bSMartin Povišer 					    (tdm_rx_start_slot << TAS2764_TDM_CFG1_51_SHIFT));
438827ed8a0SDan Murphy 	if (ret < 0)
439827ed8a0SDan Murphy 		return ret;
440827ed8a0SDan Murphy 
441827ed8a0SDan Murphy 	return 0;
442827ed8a0SDan Murphy }
443827ed8a0SDan Murphy 
tas2764_set_dai_tdm_slot(struct snd_soc_dai * dai,unsigned int tx_mask,unsigned int rx_mask,int slots,int slot_width)444827ed8a0SDan Murphy static int tas2764_set_dai_tdm_slot(struct snd_soc_dai *dai,
445827ed8a0SDan Murphy 				unsigned int tx_mask,
446827ed8a0SDan Murphy 				unsigned int rx_mask,
447827ed8a0SDan Murphy 				int slots, int slot_width)
448827ed8a0SDan Murphy {
449827ed8a0SDan Murphy 	struct snd_soc_component *component = dai->component;
450827ed8a0SDan Murphy 	struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
451827ed8a0SDan Murphy 	int left_slot, right_slot;
452827ed8a0SDan Murphy 	int slots_cfg;
453827ed8a0SDan Murphy 	int slot_size;
454827ed8a0SDan Murphy 	int ret;
455827ed8a0SDan Murphy 
456827ed8a0SDan Murphy 	if (tx_mask == 0 || rx_mask != 0)
457827ed8a0SDan Murphy 		return -EINVAL;
458827ed8a0SDan Murphy 
459827ed8a0SDan Murphy 	left_slot = __ffs(tx_mask);
460827ed8a0SDan Murphy 	tx_mask &= ~(1 << left_slot);
461827ed8a0SDan Murphy 	if (tx_mask == 0) {
462827ed8a0SDan Murphy 		right_slot = left_slot;
463827ed8a0SDan Murphy 	} else {
464827ed8a0SDan Murphy 		right_slot = __ffs(tx_mask);
465827ed8a0SDan Murphy 		tx_mask &= ~(1 << right_slot);
466827ed8a0SDan Murphy 	}
467827ed8a0SDan Murphy 
468827ed8a0SDan Murphy 	if (tx_mask != 0 || left_slot >= slots || right_slot >= slots)
469827ed8a0SDan Murphy 		return -EINVAL;
470827ed8a0SDan Murphy 
471827ed8a0SDan Murphy 	slots_cfg = (right_slot << TAS2764_TDM_CFG3_RXS_SHIFT) | left_slot;
472827ed8a0SDan Murphy 
473827ed8a0SDan Murphy 	ret = snd_soc_component_write(component, TAS2764_TDM_CFG3, slots_cfg);
474827ed8a0SDan Murphy 	if (ret)
475827ed8a0SDan Murphy 		return ret;
476827ed8a0SDan Murphy 
477827ed8a0SDan Murphy 	switch (slot_width) {
478827ed8a0SDan Murphy 	case 16:
479827ed8a0SDan Murphy 		slot_size = TAS2764_TDM_CFG2_RXS_16BITS;
480827ed8a0SDan Murphy 		break;
481827ed8a0SDan Murphy 	case 24:
482827ed8a0SDan Murphy 		slot_size = TAS2764_TDM_CFG2_RXS_24BITS;
483827ed8a0SDan Murphy 		break;
484827ed8a0SDan Murphy 	case 32:
485827ed8a0SDan Murphy 		slot_size = TAS2764_TDM_CFG2_RXS_32BITS;
486827ed8a0SDan Murphy 		break;
487827ed8a0SDan Murphy 	default:
488827ed8a0SDan Murphy 		return -EINVAL;
489827ed8a0SDan Murphy 	}
490827ed8a0SDan Murphy 
491827ed8a0SDan Murphy 	ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG2,
492827ed8a0SDan Murphy 					    TAS2764_TDM_CFG2_RXS_MASK,
493827ed8a0SDan Murphy 					    slot_size);
494827ed8a0SDan Murphy 	if (ret < 0)
495827ed8a0SDan Murphy 		return ret;
496827ed8a0SDan Murphy 
497827ed8a0SDan Murphy 	ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG5,
498827ed8a0SDan Murphy 					    TAS2764_TDM_CFG5_50_MASK,
499827ed8a0SDan Murphy 					    tas2764->v_sense_slot);
500827ed8a0SDan Murphy 	if (ret < 0)
501827ed8a0SDan Murphy 		return ret;
502827ed8a0SDan Murphy 
503827ed8a0SDan Murphy 	ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG6,
504827ed8a0SDan Murphy 					    TAS2764_TDM_CFG6_50_MASK,
505827ed8a0SDan Murphy 					    tas2764->i_sense_slot);
506827ed8a0SDan Murphy 	if (ret < 0)
507827ed8a0SDan Murphy 		return ret;
508827ed8a0SDan Murphy 
509827ed8a0SDan Murphy 	return 0;
510827ed8a0SDan Murphy }
511827ed8a0SDan Murphy 
512b186e7c1SYe Bin static const struct snd_soc_dai_ops tas2764_dai_ops = {
513827ed8a0SDan Murphy 	.mute_stream = tas2764_mute,
514827ed8a0SDan Murphy 	.hw_params  = tas2764_hw_params,
515827ed8a0SDan Murphy 	.set_fmt    = tas2764_set_fmt,
516827ed8a0SDan Murphy 	.set_tdm_slot = tas2764_set_dai_tdm_slot,
517827ed8a0SDan Murphy 	.no_capture_mute = 1,
518827ed8a0SDan Murphy };
519827ed8a0SDan Murphy 
520827ed8a0SDan Murphy #define TAS2764_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
521827ed8a0SDan Murphy 			 SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
522827ed8a0SDan Murphy 
523827ed8a0SDan Murphy #define TAS2764_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
524827ed8a0SDan Murphy 		       SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_88200)
525827ed8a0SDan Murphy 
526827ed8a0SDan Murphy static struct snd_soc_dai_driver tas2764_dai_driver[] = {
527827ed8a0SDan Murphy 	{
528827ed8a0SDan Murphy 		.name = "tas2764 ASI1",
529827ed8a0SDan Murphy 		.id = 0,
530827ed8a0SDan Murphy 		.playback = {
531827ed8a0SDan Murphy 			.stream_name    = "ASI1 Playback",
53223204d92SMartin Povišer 			.channels_min   = 1,
533827ed8a0SDan Murphy 			.channels_max   = 2,
534827ed8a0SDan Murphy 			.rates      = TAS2764_RATES,
535827ed8a0SDan Murphy 			.formats    = TAS2764_FORMATS,
536827ed8a0SDan Murphy 		},
537827ed8a0SDan Murphy 		.capture = {
538827ed8a0SDan Murphy 			.stream_name    = "ASI1 Capture",
539827ed8a0SDan Murphy 			.channels_min   = 0,
540827ed8a0SDan Murphy 			.channels_max   = 2,
541827ed8a0SDan Murphy 			.rates = TAS2764_RATES,
542827ed8a0SDan Murphy 			.formats = TAS2764_FORMATS,
543827ed8a0SDan Murphy 		},
544827ed8a0SDan Murphy 		.ops = &tas2764_dai_ops,
545fa056c07SKuninori Morimoto 		.symmetric_rate = 1,
546827ed8a0SDan Murphy 	},
547827ed8a0SDan Murphy };
548827ed8a0SDan Murphy 
549ad183929SMartin Povišer static uint8_t sn012776_bop_presets[] = {
550ad183929SMartin Povišer 	0x01, 0x32, 0x02, 0x22, 0x83, 0x2d, 0x80, 0x02, 0x06,
551ad183929SMartin Povišer 	0x32, 0x46, 0x30, 0x02, 0x06, 0x38, 0x40, 0x30, 0x02,
552ad183929SMartin Povišer 	0x06, 0x3e, 0x37, 0x30, 0xff, 0xe6
553ad183929SMartin Povišer };
554ad183929SMartin Povišer 
555592ab393SMartin Povišer static const struct regmap_config tas2764_i2c_regmap;
556592ab393SMartin Povišer 
tas2764_apply_init_quirks(struct tas2764_priv * tas2764)557f33b01e0SMartin Povišer static int tas2764_apply_init_quirks(struct tas2764_priv *tas2764)
558f33b01e0SMartin Povišer {
559f33b01e0SMartin Povišer 	int ret, i;
560f33b01e0SMartin Povišer 
561f33b01e0SMartin Povišer 	for (i = 0; i < ARRAY_SIZE(tas2764_quirk_init_sequences); i++) {
562f33b01e0SMartin Povišer 		const struct tas2764_quirk_init_sequence *init_seq =
563f33b01e0SMartin Povišer 						&tas2764_quirk_init_sequences[i];
564f33b01e0SMartin Povišer 
565f33b01e0SMartin Povišer 		if (!init_seq->seq)
566f33b01e0SMartin Povišer 			continue;
567f33b01e0SMartin Povišer 
568f33b01e0SMartin Povišer 		if (!(BIT(i) & ENABLED_APPLE_QUIRKS))
569f33b01e0SMartin Povišer 			continue;
570f33b01e0SMartin Povišer 
571f33b01e0SMartin Povišer 		ret = regmap_multi_reg_write(tas2764->regmap, init_seq->seq,
572f33b01e0SMartin Povišer 					     init_seq->len);
573f33b01e0SMartin Povišer 
574f33b01e0SMartin Povišer 		if (ret < 0)
575f33b01e0SMartin Povišer 			return ret;
576f33b01e0SMartin Povišer 	}
577f33b01e0SMartin Povišer 
578f33b01e0SMartin Povišer 	return 0;
579f33b01e0SMartin Povišer }
580f33b01e0SMartin Povišer 
tas2764_read_die_temp(struct tas2764_priv * tas2764,long * result)581186dfc85SJames Calligeros static int tas2764_read_die_temp(struct tas2764_priv *tas2764, long *result)
582186dfc85SJames Calligeros {
583186dfc85SJames Calligeros 	int ret, reg;
584186dfc85SJames Calligeros 
585186dfc85SJames Calligeros 	ret = regmap_read(tas2764->regmap, TAS2764_TEMP, &reg);
586186dfc85SJames Calligeros 	if (ret)
587186dfc85SJames Calligeros 		return ret;
588186dfc85SJames Calligeros 	/*
589186dfc85SJames Calligeros 	 * As per datasheet, subtract 93 from raw value to get degrees
590186dfc85SJames Calligeros 	 * Celsius. hwmon wants millidegrees.
591186dfc85SJames Calligeros 	 *
592186dfc85SJames Calligeros 	 * NOTE: The chip will initialise the TAS2764_TEMP register to
593186dfc85SJames Calligeros 	 * 2.6 *C to avoid triggering temperature protection. Since the
594186dfc85SJames Calligeros 	 * ADC is powered down during software shutdown, this value will
595186dfc85SJames Calligeros 	 * persist until the chip is fully powered up (e.g. the PCM it's
596186dfc85SJames Calligeros 	 * attached to is opened). The ADC will power down again when
597186dfc85SJames Calligeros 	 * the chip is put back into software shutdown, with the last
598186dfc85SJames Calligeros 	 * value sampled persisting in the ADC's register.
599186dfc85SJames Calligeros 	 */
600186dfc85SJames Calligeros 	*result = (reg - 93) * 1000;
601186dfc85SJames Calligeros 	return 0;
602186dfc85SJames Calligeros }
603186dfc85SJames Calligeros 
tas2764_hwmon_is_visible(const void * data,enum hwmon_sensor_types type,u32 attr,int channel)604186dfc85SJames Calligeros static umode_t tas2764_hwmon_is_visible(const void *data,
605186dfc85SJames Calligeros 					enum hwmon_sensor_types type, u32 attr,
606186dfc85SJames Calligeros 					int channel)
607186dfc85SJames Calligeros {
608186dfc85SJames Calligeros 	if (type != hwmon_temp)
609186dfc85SJames Calligeros 		return 0;
610186dfc85SJames Calligeros 
611186dfc85SJames Calligeros 	switch (attr) {
612186dfc85SJames Calligeros 	case hwmon_temp_input:
613186dfc85SJames Calligeros 		return 0444;
614186dfc85SJames Calligeros 	default:
615186dfc85SJames Calligeros 		break;
616186dfc85SJames Calligeros 	}
617186dfc85SJames Calligeros 
618186dfc85SJames Calligeros 	return 0;
619186dfc85SJames Calligeros }
620186dfc85SJames Calligeros 
tas2764_hwmon_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)621186dfc85SJames Calligeros static int tas2764_hwmon_read(struct device *dev,
622186dfc85SJames Calligeros 			      enum hwmon_sensor_types type,
623186dfc85SJames Calligeros 			      u32 attr, int channel, long *val)
624186dfc85SJames Calligeros {
625186dfc85SJames Calligeros 	struct tas2764_priv *tas2764 = dev_get_drvdata(dev);
626186dfc85SJames Calligeros 	int ret;
627186dfc85SJames Calligeros 
628186dfc85SJames Calligeros 	switch (attr) {
629186dfc85SJames Calligeros 	case hwmon_temp_input:
630186dfc85SJames Calligeros 		ret = tas2764_read_die_temp(tas2764, val);
631186dfc85SJames Calligeros 		break;
632186dfc85SJames Calligeros 	default:
633186dfc85SJames Calligeros 		ret = -EOPNOTSUPP;
634186dfc85SJames Calligeros 		break;
635186dfc85SJames Calligeros 	}
636186dfc85SJames Calligeros 
637186dfc85SJames Calligeros 	return ret;
638186dfc85SJames Calligeros }
639186dfc85SJames Calligeros 
640186dfc85SJames Calligeros static const struct hwmon_channel_info *const tas2764_hwmon_info[] = {
641186dfc85SJames Calligeros 	HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
642186dfc85SJames Calligeros 	NULL
643186dfc85SJames Calligeros };
644186dfc85SJames Calligeros 
645186dfc85SJames Calligeros static const struct hwmon_ops tas2764_hwmon_ops = {
646186dfc85SJames Calligeros 	.is_visible	= tas2764_hwmon_is_visible,
647186dfc85SJames Calligeros 	.read		= tas2764_hwmon_read,
648186dfc85SJames Calligeros };
649186dfc85SJames Calligeros 
650186dfc85SJames Calligeros static const struct hwmon_chip_info tas2764_hwmon_chip_info = {
651186dfc85SJames Calligeros 	.ops	= &tas2764_hwmon_ops,
652186dfc85SJames Calligeros 	.info	= tas2764_hwmon_info,
653186dfc85SJames Calligeros };
654186dfc85SJames Calligeros 
tas2764_codec_probe(struct snd_soc_component * component)655827ed8a0SDan Murphy static int tas2764_codec_probe(struct snd_soc_component *component)
656827ed8a0SDan Murphy {
657827ed8a0SDan Murphy 	struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
658ad183929SMartin Povišer 	int ret, i;
659827ed8a0SDan Murphy 
660827ed8a0SDan Murphy 	tas2764->component = component;
661827ed8a0SDan Murphy 
662cd10bb89SMartin Povišer 	if (tas2764->sdz_gpio) {
663827ed8a0SDan Murphy 		gpiod_set_value_cansleep(tas2764->sdz_gpio, 1);
664cd10bb89SMartin Povišer 		usleep_range(1000, 2000);
665cd10bb89SMartin Povišer 	}
666827ed8a0SDan Murphy 
667827ed8a0SDan Murphy 	tas2764_reset(tas2764);
668592ab393SMartin Povišer 	regmap_reinit_cache(tas2764->regmap, &tas2764_i2c_regmap);
669827ed8a0SDan Murphy 
670dae191fbSMartin Povišer 	if (tas2764->irq) {
671dd50f0e3SHector Martin 		ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK0, 0x00);
672dae191fbSMartin Povišer 		if (ret < 0)
673dae191fbSMartin Povišer 			return ret;
674dae191fbSMartin Povišer 
675dae191fbSMartin Povišer 		ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK1, 0xff);
676dae191fbSMartin Povišer 		if (ret < 0)
677dae191fbSMartin Povišer 			return ret;
678dae191fbSMartin Povišer 
679dae191fbSMartin Povišer 		ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK2, 0xff);
680dae191fbSMartin Povišer 		if (ret < 0)
681dae191fbSMartin Povišer 			return ret;
682dae191fbSMartin Povišer 
683dae191fbSMartin Povišer 		ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK3, 0xff);
684dae191fbSMartin Povišer 		if (ret < 0)
685dae191fbSMartin Povišer 			return ret;
686dae191fbSMartin Povišer 
687dae191fbSMartin Povišer 		ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK4, 0xff);
688dae191fbSMartin Povišer 		if (ret < 0)
689dae191fbSMartin Povišer 			return ret;
690dae191fbSMartin Povišer 
691dae191fbSMartin Povišer 		ret = devm_request_threaded_irq(tas2764->dev, tas2764->irq, NULL, tas2764_irq,
692dae191fbSMartin Povišer 						IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW,
693dae191fbSMartin Povišer 						"tas2764", tas2764);
694dae191fbSMartin Povišer 		if (ret)
695dae191fbSMartin Povišer 			dev_warn(tas2764->dev, "failed to request IRQ: %d\n", ret);
696dae191fbSMartin Povišer 	}
697dae191fbSMartin Povišer 
698827ed8a0SDan Murphy 	ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG5,
699827ed8a0SDan Murphy 					    TAS2764_TDM_CFG5_VSNS_ENABLE, 0);
700827ed8a0SDan Murphy 	if (ret < 0)
701827ed8a0SDan Murphy 		return ret;
702827ed8a0SDan Murphy 
703827ed8a0SDan Murphy 	ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG6,
704827ed8a0SDan Murphy 					    TAS2764_TDM_CFG6_ISNS_ENABLE, 0);
705827ed8a0SDan Murphy 	if (ret < 0)
706827ed8a0SDan Murphy 		return ret;
707827ed8a0SDan Murphy 
708ad183929SMartin Povišer 	switch (tas2764->devid) {
709ad183929SMartin Povišer 	case DEVID_SN012776:
710ad183929SMartin Povišer 		ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
711ad183929SMartin Povišer 					TAS2764_PWR_CTRL_BOP_SRC,
712ad183929SMartin Povišer 					TAS2764_PWR_CTRL_BOP_SRC);
713ad183929SMartin Povišer 		if (ret < 0)
714ad183929SMartin Povišer 			return ret;
715ad183929SMartin Povišer 
716ad183929SMartin Povišer 		for (i = 0; i < ARRAY_SIZE(sn012776_bop_presets); i++) {
717ad183929SMartin Povišer 			ret = snd_soc_component_write(component,
718ad183929SMartin Povišer 						TAS2764_BOP_CFG0 + i,
719ad183929SMartin Povišer 						sn012776_bop_presets[i]);
720ad183929SMartin Povišer 
721ad183929SMartin Povišer 			if (ret < 0)
722ad183929SMartin Povišer 				return ret;
723ad183929SMartin Povišer 		}
724f33b01e0SMartin Povišer 
725f33b01e0SMartin Povišer 		/* Apply all enabled Apple quirks */
726f33b01e0SMartin Povišer 		ret = tas2764_apply_init_quirks(tas2764);
727f33b01e0SMartin Povišer 
728f33b01e0SMartin Povišer 		if (ret < 0)
729f33b01e0SMartin Povišer 			return ret;
730f33b01e0SMartin Povišer 
731ad183929SMartin Povišer 		break;
732ad183929SMartin Povišer 	default:
733ad183929SMartin Povišer 		break;
734ad183929SMartin Povišer 	}
735ad183929SMartin Povišer 
736827ed8a0SDan Murphy 	return 0;
737827ed8a0SDan Murphy }
738827ed8a0SDan Murphy 
739827ed8a0SDan Murphy static DECLARE_TLV_DB_SCALE(tas2764_digital_tlv, 1100, 50, 0);
7403e99e569SHector Martin static DECLARE_TLV_DB_SCALE(tas2764_playback_volume, -10050, 50, 1);
741827ed8a0SDan Murphy 
742aca86ec9SMartin Povišer static const char * const tas2764_hpf_texts[] = {
743aca86ec9SMartin Povišer 	"Disabled", "2 Hz", "50 Hz", "100 Hz", "200 Hz",
744aca86ec9SMartin Povišer 	"400 Hz", "800 Hz"
745aca86ec9SMartin Povišer };
746aca86ec9SMartin Povišer 
747aca86ec9SMartin Povišer static SOC_ENUM_SINGLE_DECL(
748aca86ec9SMartin Povišer 	tas2764_hpf_enum, TAS2764_DC_BLK0,
749aca86ec9SMartin Povišer 	TAS2764_DC_BLK0_HPF_FREQ_PB_SHIFT, tas2764_hpf_texts);
750aca86ec9SMartin Povišer 
751f8d5f28eSMartin Povišer static const char * const tas2764_oce_texts[] = {
752f8d5f28eSMartin Povišer 	"Disable", "Retry",
753f8d5f28eSMartin Povišer };
754f8d5f28eSMartin Povišer 
755f8d5f28eSMartin Povišer static SOC_ENUM_SINGLE_DECL(
756f8d5f28eSMartin Povišer 	tas2764_oce_enum, TAS2764_MISC_CFG1,
757f8d5f28eSMartin Povišer 	TAS2764_MISC_CFG1_OCE_RETRY_SHIFT, tas2764_oce_texts);
758f8d5f28eSMartin Povišer 
759827ed8a0SDan Murphy static const struct snd_kcontrol_new tas2764_snd_controls[] = {
760827ed8a0SDan Murphy 	SOC_SINGLE_TLV("Speaker Volume", TAS2764_DVC, 0,
761827ed8a0SDan Murphy 		       TAS2764_DVC_MAX, 1, tas2764_playback_volume),
7621c4f29ecSHector Martin 	SOC_SINGLE_TLV("Amp Gain Volume", TAS2764_CHNL_0, 1, 0x14, 0,
763827ed8a0SDan Murphy 		       tas2764_digital_tlv),
764aca86ec9SMartin Povišer 	SOC_ENUM("HPF Corner Frequency", tas2764_hpf_enum),
765f8d5f28eSMartin Povišer 	SOC_ENUM("OCE Handling", tas2764_oce_enum),
766827ed8a0SDan Murphy };
767827ed8a0SDan Murphy 
768827ed8a0SDan Murphy static const struct snd_soc_component_driver soc_component_driver_tas2764 = {
769827ed8a0SDan Murphy 	.probe			= tas2764_codec_probe,
770827ed8a0SDan Murphy 	.suspend		= tas2764_codec_suspend,
771827ed8a0SDan Murphy 	.resume			= tas2764_codec_resume,
772827ed8a0SDan Murphy 	.controls		= tas2764_snd_controls,
773827ed8a0SDan Murphy 	.num_controls		= ARRAY_SIZE(tas2764_snd_controls),
774827ed8a0SDan Murphy 	.dapm_widgets		= tas2764_dapm_widgets,
775827ed8a0SDan Murphy 	.num_dapm_widgets	= ARRAY_SIZE(tas2764_dapm_widgets),
776827ed8a0SDan Murphy 	.dapm_routes		= tas2764_audio_map,
777827ed8a0SDan Murphy 	.num_dapm_routes	= ARRAY_SIZE(tas2764_audio_map),
778827ed8a0SDan Murphy 	.idle_bias_on		= 1,
779827ed8a0SDan Murphy 	.endianness		= 1,
780827ed8a0SDan Murphy };
781827ed8a0SDan Murphy 
782827ed8a0SDan Murphy static const struct reg_default tas2764_reg_defaults[] = {
783827ed8a0SDan Murphy 	{ TAS2764_PAGE, 0x00 },
784827ed8a0SDan Murphy 	{ TAS2764_SW_RST, 0x00 },
785827ed8a0SDan Murphy 	{ TAS2764_PWR_CTRL, 0x1a },
786827ed8a0SDan Murphy 	{ TAS2764_DVC, 0x00 },
7871c4f29ecSHector Martin 	{ TAS2764_CHNL_0, 0x28 },
788827ed8a0SDan Murphy 	{ TAS2764_TDM_CFG0, 0x09 },
789827ed8a0SDan Murphy 	{ TAS2764_TDM_CFG1, 0x02 },
790827ed8a0SDan Murphy 	{ TAS2764_TDM_CFG2, 0x0a },
791827ed8a0SDan Murphy 	{ TAS2764_TDM_CFG3, 0x10 },
792827ed8a0SDan Murphy 	{ TAS2764_TDM_CFG5, 0x42 },
793d64c4c3dSHector Martin 	{ TAS2764_INT_CLK_CFG, 0x19 },
794827ed8a0SDan Murphy };
795827ed8a0SDan Murphy 
796827ed8a0SDan Murphy static const struct regmap_range_cfg tas2764_regmap_ranges[] = {
797827ed8a0SDan Murphy 	{
798827ed8a0SDan Murphy 		.range_min = 0,
799f0aff451SMartin Povišer 		.range_max = 0xffff,
800827ed8a0SDan Murphy 		.selector_reg = TAS2764_PAGE,
801827ed8a0SDan Murphy 		.selector_mask = 0xff,
802827ed8a0SDan Murphy 		.selector_shift = 0,
803827ed8a0SDan Murphy 		.window_start = 0,
804827ed8a0SDan Murphy 		.window_len = 128,
805827ed8a0SDan Murphy 	},
806827ed8a0SDan Murphy };
807827ed8a0SDan Murphy 
tas2764_volatile_register(struct device * dev,unsigned int reg)808dae191fbSMartin Povišer static bool tas2764_volatile_register(struct device *dev, unsigned int reg)
809dae191fbSMartin Povišer {
810dae191fbSMartin Povišer 	switch (reg) {
811f37f1748SHector Martin 	case TAS2764_SW_RST:
812dae191fbSMartin Povišer 	case TAS2764_INT_LTCH0 ... TAS2764_INT_LTCH4:
813dae191fbSMartin Povišer 	case TAS2764_INT_CLK_CFG:
814dae191fbSMartin Povišer 		return true;
815f33b01e0SMartin Povišer 	case TAS2764_REG(0xf0, 0x0) ... TAS2764_REG(0xff, 0x0):
816f33b01e0SMartin Povišer 		/* TI's undocumented registers for the application of quirks */
817f33b01e0SMartin Povišer 		return true;
818dae191fbSMartin Povišer 	default:
819dae191fbSMartin Povišer 		return false;
820dae191fbSMartin Povišer 	}
821dae191fbSMartin Povišer }
822dae191fbSMartin Povišer 
823827ed8a0SDan Murphy static const struct regmap_config tas2764_i2c_regmap = {
824827ed8a0SDan Murphy 	.reg_bits = 8,
825827ed8a0SDan Murphy 	.val_bits = 8,
826dae191fbSMartin Povišer 	.volatile_reg = tas2764_volatile_register,
827827ed8a0SDan Murphy 	.reg_defaults = tas2764_reg_defaults,
828827ed8a0SDan Murphy 	.num_reg_defaults = ARRAY_SIZE(tas2764_reg_defaults),
829827ed8a0SDan Murphy 	.cache_type = REGCACHE_RBTREE,
830827ed8a0SDan Murphy 	.ranges = tas2764_regmap_ranges,
831827ed8a0SDan Murphy 	.num_ranges = ARRAY_SIZE(tas2764_regmap_ranges),
832f0aff451SMartin Povišer 	.max_register = 0xffff,
833827ed8a0SDan Murphy };
834827ed8a0SDan Murphy 
tas2764_parse_dt(struct device * dev,struct tas2764_priv * tas2764)835827ed8a0SDan Murphy static int tas2764_parse_dt(struct device *dev, struct tas2764_priv *tas2764)
836827ed8a0SDan Murphy {
837827ed8a0SDan Murphy 	int ret = 0;
838827ed8a0SDan Murphy 
839827ed8a0SDan Murphy 	tas2764->reset_gpio = devm_gpiod_get_optional(tas2764->dev, "reset",
840827ed8a0SDan Murphy 						      GPIOD_OUT_HIGH);
841827ed8a0SDan Murphy 	if (IS_ERR(tas2764->reset_gpio)) {
842827ed8a0SDan Murphy 		if (PTR_ERR(tas2764->reset_gpio) == -EPROBE_DEFER) {
843827ed8a0SDan Murphy 			tas2764->reset_gpio = NULL;
844827ed8a0SDan Murphy 			return -EPROBE_DEFER;
845827ed8a0SDan Murphy 		}
846827ed8a0SDan Murphy 	}
847827ed8a0SDan Murphy 
848827ed8a0SDan Murphy 	tas2764->sdz_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH);
849827ed8a0SDan Murphy 	if (IS_ERR(tas2764->sdz_gpio)) {
850827ed8a0SDan Murphy 		if (PTR_ERR(tas2764->sdz_gpio) == -EPROBE_DEFER)
851827ed8a0SDan Murphy 			return -EPROBE_DEFER;
852827ed8a0SDan Murphy 
853827ed8a0SDan Murphy 		tas2764->sdz_gpio = NULL;
854827ed8a0SDan Murphy 	}
855827ed8a0SDan Murphy 
856827ed8a0SDan Murphy 	ret = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no",
857827ed8a0SDan Murphy 				       &tas2764->i_sense_slot);
858827ed8a0SDan Murphy 	if (ret)
859827ed8a0SDan Murphy 		tas2764->i_sense_slot = 0;
860827ed8a0SDan Murphy 
861827ed8a0SDan Murphy 	ret = fwnode_property_read_u32(dev->fwnode, "ti,vmon-slot-no",
862827ed8a0SDan Murphy 				       &tas2764->v_sense_slot);
863827ed8a0SDan Murphy 	if (ret)
864827ed8a0SDan Murphy 		tas2764->v_sense_slot = 2;
865827ed8a0SDan Murphy 
866827ed8a0SDan Murphy 	return 0;
867827ed8a0SDan Murphy }
868827ed8a0SDan Murphy 
tas2764_i2c_probe(struct i2c_client * client)869ad11678fSStephen Kitt static int tas2764_i2c_probe(struct i2c_client *client)
870827ed8a0SDan Murphy {
871827ed8a0SDan Murphy 	struct tas2764_priv *tas2764;
872827ed8a0SDan Murphy 	int result;
873827ed8a0SDan Murphy 
874827ed8a0SDan Murphy 	tas2764 = devm_kzalloc(&client->dev, sizeof(struct tas2764_priv),
875827ed8a0SDan Murphy 			       GFP_KERNEL);
876827ed8a0SDan Murphy 	if (!tas2764)
877827ed8a0SDan Murphy 		return -ENOMEM;
878827ed8a0SDan Murphy 
879*c23c7c60SKrzysztof Kozlowski 	tas2764->devid = (kernel_ulong_t)of_device_get_match_data(&client->dev);
880ad183929SMartin Povišer 
881827ed8a0SDan Murphy 	tas2764->dev = &client->dev;
882dae191fbSMartin Povišer 	tas2764->irq = client->irq;
883827ed8a0SDan Murphy 	i2c_set_clientdata(client, tas2764);
884827ed8a0SDan Murphy 	dev_set_drvdata(&client->dev, tas2764);
885827ed8a0SDan Murphy 
886827ed8a0SDan Murphy 	tas2764->regmap = devm_regmap_init_i2c(client, &tas2764_i2c_regmap);
887827ed8a0SDan Murphy 	if (IS_ERR(tas2764->regmap)) {
888827ed8a0SDan Murphy 		result = PTR_ERR(tas2764->regmap);
889827ed8a0SDan Murphy 		dev_err(&client->dev, "Failed to allocate register map: %d\n",
890827ed8a0SDan Murphy 					result);
891827ed8a0SDan Murphy 		return result;
892827ed8a0SDan Murphy 	}
893827ed8a0SDan Murphy 
894827ed8a0SDan Murphy 	if (client->dev.of_node) {
895827ed8a0SDan Murphy 		result = tas2764_parse_dt(&client->dev, tas2764);
896827ed8a0SDan Murphy 		if (result) {
897827ed8a0SDan Murphy 			dev_err(tas2764->dev, "%s: Failed to parse devicetree\n",
898827ed8a0SDan Murphy 				__func__);
899827ed8a0SDan Murphy 			return result;
900827ed8a0SDan Murphy 		}
901827ed8a0SDan Murphy 	}
902827ed8a0SDan Murphy 
903186dfc85SJames Calligeros 	if (IS_REACHABLE(CONFIG_HWMON)) {
904186dfc85SJames Calligeros 		struct device *hwmon;
905186dfc85SJames Calligeros 
906186dfc85SJames Calligeros 		hwmon = devm_hwmon_device_register_with_info(&client->dev, "tas2764",
907186dfc85SJames Calligeros 							tas2764,
908186dfc85SJames Calligeros 							&tas2764_hwmon_chip_info,
909186dfc85SJames Calligeros 							NULL);
910186dfc85SJames Calligeros 		if (IS_ERR(hwmon)) {
911186dfc85SJames Calligeros 			return dev_err_probe(&client->dev, PTR_ERR(hwmon),
912186dfc85SJames Calligeros 					     "Failed to register temp sensor\n");
913186dfc85SJames Calligeros 		}
914186dfc85SJames Calligeros 	}
915186dfc85SJames Calligeros 
916186dfc85SJames Calligeros 
917827ed8a0SDan Murphy 	return devm_snd_soc_register_component(tas2764->dev,
918827ed8a0SDan Murphy 					       &soc_component_driver_tas2764,
919827ed8a0SDan Murphy 					       tas2764_dai_driver,
920827ed8a0SDan Murphy 					       ARRAY_SIZE(tas2764_dai_driver));
921827ed8a0SDan Murphy }
922827ed8a0SDan Murphy 
923827ed8a0SDan Murphy static const struct i2c_device_id tas2764_i2c_id[] = {
924ba2a2c37SUwe Kleine-König 	{ "tas2764"},
925827ed8a0SDan Murphy 	{ }
926827ed8a0SDan Murphy };
927827ed8a0SDan Murphy MODULE_DEVICE_TABLE(i2c, tas2764_i2c_id);
928827ed8a0SDan Murphy 
929827ed8a0SDan Murphy #if defined(CONFIG_OF)
930827ed8a0SDan Murphy static const struct of_device_id tas2764_of_match[] = {
931ad183929SMartin Povišer 	{ .compatible = "ti,tas2764",  .data = (void *)DEVID_TAS2764 },
932ad183929SMartin Povišer 	{ .compatible = "ti,sn012776", .data = (void *)DEVID_SN012776 },
933827ed8a0SDan Murphy 	{},
934827ed8a0SDan Murphy };
935827ed8a0SDan Murphy MODULE_DEVICE_TABLE(of, tas2764_of_match);
936827ed8a0SDan Murphy #endif
937827ed8a0SDan Murphy 
938827ed8a0SDan Murphy static struct i2c_driver tas2764_i2c_driver = {
939827ed8a0SDan Murphy 	.driver = {
940827ed8a0SDan Murphy 		.name   = "tas2764",
941827ed8a0SDan Murphy 		.of_match_table = of_match_ptr(tas2764_of_match),
942827ed8a0SDan Murphy 	},
9439abcd240SUwe Kleine-König 	.probe      = tas2764_i2c_probe,
944827ed8a0SDan Murphy 	.id_table   = tas2764_i2c_id,
945827ed8a0SDan Murphy };
946827ed8a0SDan Murphy module_i2c_driver(tas2764_i2c_driver);
947827ed8a0SDan Murphy 
948827ed8a0SDan Murphy MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
949827ed8a0SDan Murphy MODULE_DESCRIPTION("TAS2764 I2C Smart Amplifier driver");
950827ed8a0SDan Murphy MODULE_LICENSE("GPL v2");
951