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, ®);
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