1133c81f8SSen Wang // SPDX-License-Identifier: GPL-2.0
2133c81f8SSen Wang /*
3133c81f8SSen Wang * ALSA SoC Texas Instruments TAS67524 Quad-Channel Audio Amplifier
4133c81f8SSen Wang *
5133c81f8SSen Wang * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/
6133c81f8SSen Wang * Author: Sen Wang <sen@ti.com>
7133c81f8SSen Wang */
8133c81f8SSen Wang
9133c81f8SSen Wang #include <linux/bitfield.h>
10133c81f8SSen Wang #include <linux/module.h>
11133c81f8SSen Wang #include <linux/mutex.h>
12133c81f8SSen Wang #include <linux/i2c.h>
13133c81f8SSen Wang #include <linux/regmap.h>
14133c81f8SSen Wang #include <linux/gpio/consumer.h>
15133c81f8SSen Wang #include <linux/regulator/consumer.h>
16133c81f8SSen Wang #include <linux/delay.h>
17133c81f8SSen Wang #include <linux/property.h>
18133c81f8SSen Wang #include <linux/interrupt.h>
19133c81f8SSen Wang #include <linux/workqueue.h>
20133c81f8SSen Wang #include <linux/pm_runtime.h>
21133c81f8SSen Wang #include <linux/iopoll.h>
22133c81f8SSen Wang #include <sound/soc.h>
23133c81f8SSen Wang #include <sound/tlv.h>
24133c81f8SSen Wang #include <sound/pcm_params.h>
25133c81f8SSen Wang
26133c81f8SSen Wang #include "tas675x.h"
27133c81f8SSen Wang
28133c81f8SSen Wang #define TAS675X_FAULT_CHECK_INTERVAL_MS 200
29133c81f8SSen Wang
30133c81f8SSen Wang enum tas675x_type {
31133c81f8SSen Wang TAS67524,
32133c81f8SSen Wang };
33133c81f8SSen Wang
34133c81f8SSen Wang struct tas675x_reg_param {
35133c81f8SSen Wang u8 page;
36133c81f8SSen Wang u8 reg;
37133c81f8SSen Wang u32 val;
38133c81f8SSen Wang };
39133c81f8SSen Wang
40133c81f8SSen Wang struct tas675x_priv {
41133c81f8SSen Wang struct device *dev;
42133c81f8SSen Wang struct regmap *regmap;
43133c81f8SSen Wang enum tas675x_type dev_type;
44133c81f8SSen Wang /* Custom regmap lock; protects writes across books */
45133c81f8SSen Wang struct mutex io_lock;
46133c81f8SSen Wang
47133c81f8SSen Wang struct gpio_desc *pd_gpio;
48133c81f8SSen Wang struct gpio_desc *stby_gpio;
49133c81f8SSen Wang struct regulator_bulk_data supplies[2];
50133c81f8SSen Wang struct regulator *vbat;
51133c81f8SSen Wang bool fast_boot;
52133c81f8SSen Wang
53133c81f8SSen Wang int audio_slot;
54133c81f8SSen Wang int llp_slot;
55133c81f8SSen Wang int vpredict_slot;
56133c81f8SSen Wang int isense_slot;
57133c81f8SSen Wang int bclk_offset;
58133c81f8SSen Wang int slot_width;
59133c81f8SSen Wang unsigned int tx_mask;
60133c81f8SSen Wang
61133c81f8SSen Wang int gpio1_func;
62133c81f8SSen Wang int gpio2_func;
63133c81f8SSen Wang
64133c81f8SSen Wang unsigned long active_playback_dais;
65133c81f8SSen Wang unsigned long active_capture_dais;
66133c81f8SSen Wang unsigned int rate;
67133c81f8SSen Wang unsigned int saved_rtldg_en;
68133c81f8SSen Wang #define TAS675X_DSP_PARAM_NUM 2
69133c81f8SSen Wang struct tas675x_reg_param dsp_params[TAS675X_DSP_PARAM_NUM];
70133c81f8SSen Wang
71133c81f8SSen Wang /* Fault monitor, disabled when Fault IRQ is used */
72133c81f8SSen Wang struct delayed_work fault_check_work;
73133c81f8SSen Wang #define TAS675X_FAULT_REGS_NUM 9
74133c81f8SSen Wang unsigned int last_status[TAS675X_FAULT_REGS_NUM];
75133c81f8SSen Wang };
76133c81f8SSen Wang
77133c81f8SSen Wang static const char * const tas675x_supply_names[] = {
78133c81f8SSen Wang "dvdd", /* Digital power supply */
79133c81f8SSen Wang "pvdd", /* Output powerstage supply */
80133c81f8SSen Wang };
81133c81f8SSen Wang
82133c81f8SSen Wang /* Page 1 setup initialization defaults */
83133c81f8SSen Wang static const struct reg_sequence tas675x_page1_init[] = {
84133c81f8SSen Wang REG_SEQ0(TAS675X_PAGE_REG(1, 0xC8), 0x20), /* Charge pump clock */
85133c81f8SSen Wang REG_SEQ0(TAS675X_PAGE_REG(1, 0x2F), 0x90), /* VBAT idle */
86133c81f8SSen Wang REG_SEQ0(TAS675X_PAGE_REG(1, 0x29), 0x40), /* OC/CBC threshold */
87133c81f8SSen Wang REG_SEQ0(TAS675X_PAGE_REG(1, 0x2E), 0x0C), /* OC/CBC config */
88133c81f8SSen Wang REG_SEQ0(TAS675X_PAGE_REG(1, 0xC5), 0x02), /* OC/CBC config */
89133c81f8SSen Wang REG_SEQ0(TAS675X_PAGE_REG(1, 0xC6), 0x10), /* OC/CBC config */
90133c81f8SSen Wang REG_SEQ0(TAS675X_PAGE_REG(1, 0x1F), 0x20), /* OC/CBC config */
91133c81f8SSen Wang REG_SEQ0(TAS675X_PAGE_REG(1, 0x16), 0x01), /* OC/CBC config */
92133c81f8SSen Wang REG_SEQ0(TAS675X_PAGE_REG(1, 0x1E), 0x04), /* OC/CBC config */
93133c81f8SSen Wang REG_SEQ0(TAS675X_PAGE_REG(1, 0xC1), 0x00), /* CH1 DC fault */
94133c81f8SSen Wang REG_SEQ0(TAS675X_PAGE_REG(1, 0xC2), 0x04), /* CH2 DC fault */
95133c81f8SSen Wang REG_SEQ0(TAS675X_PAGE_REG(1, 0xC3), 0x00), /* CH3 DC fault */
96133c81f8SSen Wang REG_SEQ0(TAS675X_PAGE_REG(1, 0xC4), 0x00), /* CH4 DC fault */
97133c81f8SSen Wang };
98133c81f8SSen Wang
tas675x_state_name(unsigned int state)99133c81f8SSen Wang static inline const char *tas675x_state_name(unsigned int state)
100133c81f8SSen Wang {
101133c81f8SSen Wang switch (state & 0x0F) {
102133c81f8SSen Wang case TAS675X_STATE_DEEPSLEEP: return "DEEPSLEEP";
103133c81f8SSen Wang case TAS675X_STATE_LOAD_DIAG: return "LOAD_DIAG";
104133c81f8SSen Wang case TAS675X_STATE_SLEEP: return "SLEEP";
105133c81f8SSen Wang case TAS675X_STATE_HIZ: return "HIZ";
106133c81f8SSen Wang case TAS675X_STATE_PLAY: return "PLAY";
107133c81f8SSen Wang case TAS675X_STATE_FAULT: return "FAULT";
108133c81f8SSen Wang case TAS675X_STATE_AUTOREC: return "AUTOREC";
109133c81f8SSen Wang default: return "UNKNOWN";
110133c81f8SSen Wang }
111133c81f8SSen Wang }
112133c81f8SSen Wang
tas675x_set_state_all(struct tas675x_priv * tas,u8 state)113133c81f8SSen Wang static inline int tas675x_set_state_all(struct tas675x_priv *tas, u8 state)
114133c81f8SSen Wang {
115133c81f8SSen Wang const struct reg_sequence seq[] = {
116133c81f8SSen Wang REG_SEQ0(TAS675X_STATE_CTRL_CH1_CH2_REG, state),
117133c81f8SSen Wang REG_SEQ0(TAS675X_STATE_CTRL_CH3_CH4_REG, state),
118133c81f8SSen Wang };
119133c81f8SSen Wang
120133c81f8SSen Wang return regmap_multi_reg_write(tas->regmap, seq, ARRAY_SIZE(seq));
121133c81f8SSen Wang }
122133c81f8SSen Wang
tas675x_select_book(struct regmap * regmap,u8 book)123133c81f8SSen Wang static inline int tas675x_select_book(struct regmap *regmap, u8 book)
124133c81f8SSen Wang {
125133c81f8SSen Wang int ret;
126133c81f8SSen Wang
127133c81f8SSen Wang /* Reset page to 0 before switching books */
128133c81f8SSen Wang ret = regmap_write(regmap, TAS675X_PAGE_CTRL_REG, 0x00);
129133c81f8SSen Wang if (!ret)
130133c81f8SSen Wang ret = regmap_write(regmap, TAS675X_BOOK_CTRL_REG, book);
131133c81f8SSen Wang
132133c81f8SSen Wang return ret;
133133c81f8SSen Wang }
134133c81f8SSen Wang
135133c81f8SSen Wang /* Raw I2C version of tas675x_select_book, must be called with io_lock held */
__tas675x_select_book(struct tas675x_priv * tas,u8 book)136133c81f8SSen Wang static inline int __tas675x_select_book(struct tas675x_priv *tas, u8 book)
137133c81f8SSen Wang {
138133c81f8SSen Wang struct i2c_client *client = to_i2c_client(tas->dev);
139133c81f8SSen Wang int ret;
140133c81f8SSen Wang
141133c81f8SSen Wang /* Reset page to 0 before switching books */
142133c81f8SSen Wang ret = i2c_smbus_write_byte_data(client, TAS675X_PAGE_CTRL_REG, 0x00);
143133c81f8SSen Wang if (ret)
144133c81f8SSen Wang return ret;
145133c81f8SSen Wang
146133c81f8SSen Wang return i2c_smbus_write_byte_data(client, TAS675X_BOOK_CTRL_REG, book);
147133c81f8SSen Wang }
148133c81f8SSen Wang
tas675x_dsp_mem_write(struct tas675x_priv * tas,u8 page,u8 reg,u32 val)149133c81f8SSen Wang static int tas675x_dsp_mem_write(struct tas675x_priv *tas, u8 page, u8 reg, u32 val)
150133c81f8SSen Wang {
151133c81f8SSen Wang struct i2c_client *client = to_i2c_client(tas->dev);
152133c81f8SSen Wang u8 buf[4];
153133c81f8SSen Wang int ret;
154133c81f8SSen Wang
155133c81f8SSen Wang /* DSP registers are 32 bit big-endian */
156133c81f8SSen Wang buf[0] = (val >> 24) & 0xFF;
157133c81f8SSen Wang buf[1] = (val >> 16) & 0xFF;
158133c81f8SSen Wang buf[2] = (val >> 8) & 0xFF;
159133c81f8SSen Wang buf[3] = val & 0xFF;
160133c81f8SSen Wang
161133c81f8SSen Wang /*
162133c81f8SSen Wang * DSP regs in a different book, therefore block
163133c81f8SSen Wang * regmap access before completion.
164133c81f8SSen Wang */
165133c81f8SSen Wang mutex_lock(&tas->io_lock);
166133c81f8SSen Wang
167133c81f8SSen Wang ret = __tas675x_select_book(tas, TAS675X_BOOK_DSP);
168133c81f8SSen Wang if (ret)
169133c81f8SSen Wang goto out;
170133c81f8SSen Wang
171133c81f8SSen Wang ret = i2c_smbus_write_byte_data(client, TAS675X_PAGE_CTRL_REG, page);
172133c81f8SSen Wang if (ret)
173133c81f8SSen Wang goto out;
174133c81f8SSen Wang
175133c81f8SSen Wang ret = i2c_smbus_write_i2c_block_data(client, reg, sizeof(buf), buf);
176133c81f8SSen Wang
177133c81f8SSen Wang out:
178133c81f8SSen Wang __tas675x_select_book(tas, TAS675X_BOOK_DEFAULT);
179133c81f8SSen Wang mutex_unlock(&tas->io_lock);
180133c81f8SSen Wang
181133c81f8SSen Wang return ret;
182133c81f8SSen Wang }
183133c81f8SSen Wang
tas675x_dsp_mem_read(struct tas675x_priv * tas,u8 page,u8 reg,u32 * val)184133c81f8SSen Wang static int tas675x_dsp_mem_read(struct tas675x_priv *tas, u8 page, u8 reg, u32 *val)
185133c81f8SSen Wang {
186133c81f8SSen Wang struct i2c_client *client = to_i2c_client(tas->dev);
187133c81f8SSen Wang u8 buf[4];
188133c81f8SSen Wang int ret;
189133c81f8SSen Wang
190133c81f8SSen Wang /*
191133c81f8SSen Wang * DSP regs in a different book, therefore block
192133c81f8SSen Wang * regmap access before completion.
193133c81f8SSen Wang */
194133c81f8SSen Wang mutex_lock(&tas->io_lock);
195133c81f8SSen Wang
196133c81f8SSen Wang ret = __tas675x_select_book(tas, TAS675X_BOOK_DSP);
197133c81f8SSen Wang if (ret)
198133c81f8SSen Wang goto out;
199133c81f8SSen Wang
200133c81f8SSen Wang ret = i2c_smbus_write_byte_data(client, TAS675X_PAGE_CTRL_REG, page);
201133c81f8SSen Wang if (ret)
202133c81f8SSen Wang goto out;
203133c81f8SSen Wang
204133c81f8SSen Wang ret = i2c_smbus_read_i2c_block_data(client, reg, sizeof(buf), buf);
205133c81f8SSen Wang if (ret == sizeof(buf)) {
206133c81f8SSen Wang *val = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
207133c81f8SSen Wang ret = 0;
208133c81f8SSen Wang } else if (ret >= 0) {
209133c81f8SSen Wang ret = -EIO;
210133c81f8SSen Wang }
211133c81f8SSen Wang
212133c81f8SSen Wang out:
213133c81f8SSen Wang __tas675x_select_book(tas, TAS675X_BOOK_DEFAULT);
214133c81f8SSen Wang mutex_unlock(&tas->io_lock);
215133c81f8SSen Wang
216133c81f8SSen Wang return ret;
217133c81f8SSen Wang }
218133c81f8SSen Wang
219133c81f8SSen Wang static const struct {
220133c81f8SSen Wang const char *name;
221133c81f8SSen Wang int val;
222133c81f8SSen Wang } tas675x_gpio_func_map[] = {
223133c81f8SSen Wang /* Output functions */
224133c81f8SSen Wang { "low", TAS675X_GPIO_SEL_LOW },
225133c81f8SSen Wang { "auto-mute", TAS675X_GPIO_SEL_AUTO_MUTE_ALL },
226133c81f8SSen Wang { "auto-mute-ch4", TAS675X_GPIO_SEL_AUTO_MUTE_CH4 },
227133c81f8SSen Wang { "auto-mute-ch3", TAS675X_GPIO_SEL_AUTO_MUTE_CH3 },
228133c81f8SSen Wang { "auto-mute-ch2", TAS675X_GPIO_SEL_AUTO_MUTE_CH2 },
229133c81f8SSen Wang { "auto-mute-ch1", TAS675X_GPIO_SEL_AUTO_MUTE_CH1 },
230133c81f8SSen Wang { "sdout2", TAS675X_GPIO_SEL_SDOUT2 },
231133c81f8SSen Wang { "sdout1", TAS675X_GPIO_SEL_SDOUT1 },
232133c81f8SSen Wang { "warn", TAS675X_GPIO_SEL_WARN },
233133c81f8SSen Wang { "fault", TAS675X_GPIO_SEL_FAULT },
234133c81f8SSen Wang { "clock-sync", TAS675X_GPIO_SEL_CLOCK_SYNC },
235133c81f8SSen Wang { "invalid-clock", TAS675X_GPIO_SEL_INVALID_CLK },
236133c81f8SSen Wang { "high", TAS675X_GPIO_SEL_HIGH },
237133c81f8SSen Wang /* Input functions */
238133c81f8SSen Wang { "mute", TAS675X_GPIO_IN_MUTE },
239133c81f8SSen Wang { "phase-sync", TAS675X_GPIO_IN_PHASE_SYNC },
240133c81f8SSen Wang { "sdin2", TAS675X_GPIO_IN_SDIN2 },
241133c81f8SSen Wang { "deep-sleep", TAS675X_GPIO_IN_DEEP_SLEEP },
242133c81f8SSen Wang { "hiz", TAS675X_GPIO_IN_HIZ },
243133c81f8SSen Wang { "play", TAS675X_GPIO_IN_PLAY },
244133c81f8SSen Wang { "sleep", TAS675X_GPIO_IN_SLEEP },
245133c81f8SSen Wang };
246133c81f8SSen Wang
tas675x_gpio_func_parse(struct device * dev,const char * propname)247133c81f8SSen Wang static int tas675x_gpio_func_parse(struct device *dev, const char *propname)
248133c81f8SSen Wang {
249133c81f8SSen Wang const char *str;
250133c81f8SSen Wang int i, ret;
251133c81f8SSen Wang
252133c81f8SSen Wang ret = device_property_read_string(dev, propname, &str);
253133c81f8SSen Wang if (ret)
254133c81f8SSen Wang return -1;
255133c81f8SSen Wang
256133c81f8SSen Wang for (i = 0; i < ARRAY_SIZE(tas675x_gpio_func_map); i++) {
257133c81f8SSen Wang if (!strcmp(str, tas675x_gpio_func_map[i].name))
258133c81f8SSen Wang return tas675x_gpio_func_map[i].val;
259133c81f8SSen Wang }
260133c81f8SSen Wang
261133c81f8SSen Wang dev_warn(dev, "Invalid %s value '%s'\n", propname, str);
262133c81f8SSen Wang return -1;
263133c81f8SSen Wang }
264133c81f8SSen Wang
265133c81f8SSen Wang static const struct {
266133c81f8SSen Wang unsigned int reg;
267133c81f8SSen Wang unsigned int mask;
268133c81f8SSen Wang } tas675x_gpio_input_table[TAS675X_GPIO_IN_NUM] = {
269133c81f8SSen Wang [TAS675X_GPIO_IN_ID_MUTE] = {
270133c81f8SSen Wang TAS675X_GPIO_INPUT_MUTE_REG, TAS675X_GPIO_IN_MUTE_MASK },
271133c81f8SSen Wang [TAS675X_GPIO_IN_ID_PHASE_SYNC] = {
272133c81f8SSen Wang TAS675X_GPIO_INPUT_SYNC_REG, TAS675X_GPIO_IN_SYNC_MASK },
273133c81f8SSen Wang [TAS675X_GPIO_IN_ID_SDIN2] = {
274133c81f8SSen Wang TAS675X_GPIO_INPUT_SDIN2_REG, TAS675X_GPIO_IN_SDIN2_MASK },
275133c81f8SSen Wang [TAS675X_GPIO_IN_ID_DEEP_SLEEP] = {
276133c81f8SSen Wang TAS675X_GPIO_INPUT_SLEEP_HIZ_REG, TAS675X_GPIO_IN_DEEP_SLEEP_MASK },
277133c81f8SSen Wang [TAS675X_GPIO_IN_ID_HIZ] = {
278133c81f8SSen Wang TAS675X_GPIO_INPUT_SLEEP_HIZ_REG, TAS675X_GPIO_IN_HIZ_MASK },
279133c81f8SSen Wang [TAS675X_GPIO_IN_ID_PLAY] = {
280133c81f8SSen Wang TAS675X_GPIO_INPUT_PLAY_SLEEP_REG, TAS675X_GPIO_IN_PLAY_MASK },
281133c81f8SSen Wang [TAS675X_GPIO_IN_ID_SLEEP] = {
282133c81f8SSen Wang TAS675X_GPIO_INPUT_PLAY_SLEEP_REG, TAS675X_GPIO_IN_SLEEP_MASK },
283133c81f8SSen Wang };
284133c81f8SSen Wang
tas675x_config_gpio_pin(struct regmap * regmap,int func_id,unsigned int out_sel_reg,unsigned int pin_idx,unsigned int * gpio_ctrl)285133c81f8SSen Wang static void tas675x_config_gpio_pin(struct regmap *regmap, int func_id,
286133c81f8SSen Wang unsigned int out_sel_reg,
287133c81f8SSen Wang unsigned int pin_idx,
288133c81f8SSen Wang unsigned int *gpio_ctrl)
289133c81f8SSen Wang {
290133c81f8SSen Wang int id;
291133c81f8SSen Wang
292133c81f8SSen Wang if (func_id < 0)
293133c81f8SSen Wang return;
294133c81f8SSen Wang
295133c81f8SSen Wang if (func_id & TAS675X_GPIO_FUNC_INPUT) {
296133c81f8SSen Wang /* 3-bit mux: 0 = disabled, 0b1 = GPIO1, 0b10 = GPIO2 */
297133c81f8SSen Wang id = func_id & ~TAS675X_GPIO_FUNC_INPUT;
298133c81f8SSen Wang regmap_update_bits(regmap,
299133c81f8SSen Wang tas675x_gpio_input_table[id].reg,
300133c81f8SSen Wang tas675x_gpio_input_table[id].mask,
301133c81f8SSen Wang (pin_idx + 1) << __ffs(tas675x_gpio_input_table[id].mask));
302133c81f8SSen Wang } else {
303133c81f8SSen Wang /* Output GPIO, update selection register and enable bit */
304133c81f8SSen Wang regmap_write(regmap, out_sel_reg, func_id);
305133c81f8SSen Wang *gpio_ctrl |= pin_idx ? TAS675X_GPIO2_OUTPUT_EN : TAS675X_GPIO1_OUTPUT_EN;
306133c81f8SSen Wang }
307133c81f8SSen Wang }
308133c81f8SSen Wang
tas675x_rtldg_thresh_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)309133c81f8SSen Wang static int tas675x_rtldg_thresh_info(struct snd_kcontrol *kcontrol,
310133c81f8SSen Wang struct snd_ctl_elem_info *uinfo)
311133c81f8SSen Wang {
312133c81f8SSen Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
313133c81f8SSen Wang uinfo->count = 1;
314133c81f8SSen Wang uinfo->value.integer.min = 0;
315133c81f8SSen Wang /* threshold reg ranges up to 24bit */
316133c81f8SSen Wang uinfo->value.integer.max = 0x00FFFFFF;
317133c81f8SSen Wang return 0;
318133c81f8SSen Wang }
319133c81f8SSen Wang
tas675x_set_rtldg_thresh(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)320133c81f8SSen Wang static int tas675x_set_rtldg_thresh(struct snd_kcontrol *kcontrol,
321133c81f8SSen Wang struct snd_ctl_elem_value *ucontrol)
322133c81f8SSen Wang {
323133c81f8SSen Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
324133c81f8SSen Wang struct tas675x_priv *tas = snd_soc_component_get_drvdata(comp);
325133c81f8SSen Wang const struct tas675x_reg_param *t =
326133c81f8SSen Wang (const struct tas675x_reg_param *)kcontrol->private_value;
327133c81f8SSen Wang u32 val = ucontrol->value.integer.value[0];
328133c81f8SSen Wang int ret;
329133c81f8SSen Wang
330133c81f8SSen Wang ret = tas675x_dsp_mem_write(tas, t->page, t->reg, val);
331133c81f8SSen Wang
332133c81f8SSen Wang /* Cache the value */
333133c81f8SSen Wang if (!ret) {
334133c81f8SSen Wang int i;
335133c81f8SSen Wang
336133c81f8SSen Wang for (i = 0; i < ARRAY_SIZE(tas->dsp_params); i++) {
337133c81f8SSen Wang if (tas->dsp_params[i].page == t->page &&
338133c81f8SSen Wang tas->dsp_params[i].reg == t->reg) {
339133c81f8SSen Wang tas->dsp_params[i].val = val;
340133c81f8SSen Wang break;
341133c81f8SSen Wang }
342133c81f8SSen Wang }
343133c81f8SSen Wang }
344133c81f8SSen Wang
345133c81f8SSen Wang /* Return 1 to notify change, or propagate error */
346133c81f8SSen Wang return ret ? ret : 1;
347133c81f8SSen Wang }
348133c81f8SSen Wang
tas675x_get_rtldg_thresh(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)349133c81f8SSen Wang static int tas675x_get_rtldg_thresh(struct snd_kcontrol *kcontrol,
350133c81f8SSen Wang struct snd_ctl_elem_value *ucontrol)
351133c81f8SSen Wang {
352133c81f8SSen Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
353133c81f8SSen Wang struct tas675x_priv *tas = snd_soc_component_get_drvdata(comp);
354133c81f8SSen Wang const struct tas675x_reg_param *t =
355133c81f8SSen Wang (const struct tas675x_reg_param *)kcontrol->private_value;
356133c81f8SSen Wang u32 val = 0;
357133c81f8SSen Wang int ret;
358133c81f8SSen Wang
359133c81f8SSen Wang ret = tas675x_dsp_mem_read(tas, t->page, t->reg, &val);
360133c81f8SSen Wang if (!ret)
361133c81f8SSen Wang ucontrol->value.integer.value[0] = val;
362133c81f8SSen Wang
363133c81f8SSen Wang return ret;
364133c81f8SSen Wang }
365133c81f8SSen Wang
366133c81f8SSen Wang static const struct tas675x_reg_param tas675x_dsp_defaults[] = {
367133c81f8SSen Wang [TAS675X_DSP_PARAM_ID_OL_THRESH] = {
368133c81f8SSen Wang TAS675X_DSP_PAGE_RTLDG, TAS675X_DSP_RTLDG_OL_THRESH_REG },
369133c81f8SSen Wang [TAS675X_DSP_PARAM_ID_SL_THRESH] = {
370133c81f8SSen Wang TAS675X_DSP_PAGE_RTLDG, TAS675X_DSP_RTLDG_SL_THRESH_REG },
371133c81f8SSen Wang };
372133c81f8SSen Wang
373133c81f8SSen Wang static_assert(ARRAY_SIZE(tas675x_dsp_defaults) == TAS675X_DSP_PARAM_NUM);
374133c81f8SSen Wang
tas675x_set_dcldg_trigger(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)375133c81f8SSen Wang static int tas675x_set_dcldg_trigger(struct snd_kcontrol *kcontrol,
376133c81f8SSen Wang struct snd_ctl_elem_value *ucontrol)
377133c81f8SSen Wang {
378133c81f8SSen Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
379133c81f8SSen Wang struct tas675x_priv *tas = snd_soc_component_get_drvdata(comp);
380133c81f8SSen Wang unsigned int state, state34;
381133c81f8SSen Wang int ret;
382133c81f8SSen Wang
383133c81f8SSen Wang if (!ucontrol->value.integer.value[0])
384133c81f8SSen Wang return 0;
385133c81f8SSen Wang
386133c81f8SSen Wang if (snd_soc_component_active(comp))
387133c81f8SSen Wang return -EBUSY;
388133c81f8SSen Wang
389133c81f8SSen Wang ret = pm_runtime_resume_and_get(tas->dev);
390133c81f8SSen Wang if (ret < 0)
391133c81f8SSen Wang return ret;
392133c81f8SSen Wang
393133c81f8SSen Wang /*
394133c81f8SSen Wang * Abort automatic DC LDG retry loops (startup or init-after-fault)
395133c81f8SSen Wang * and clear faults before manual diagnostics.
396133c81f8SSen Wang */
397133c81f8SSen Wang regmap_update_bits(tas->regmap, TAS675X_DC_LDG_CTRL_REG,
398133c81f8SSen Wang TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT,
399133c81f8SSen Wang TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT);
400133c81f8SSen Wang regmap_write(tas->regmap, TAS675X_RESET_REG, TAS675X_FAULT_CLEAR);
401133c81f8SSen Wang
402133c81f8SSen Wang /* Wait for LOAD_DIAG to exit */
403133c81f8SSen Wang ret = regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH1_CH2_REG,
404133c81f8SSen Wang state, (state & 0x0F) != TAS675X_STATE_LOAD_DIAG &&
405133c81f8SSen Wang (state >> 4) != TAS675X_STATE_LOAD_DIAG,
406133c81f8SSen Wang TAS675X_POLL_INTERVAL_US,
407133c81f8SSen Wang TAS675X_STATE_TRANSITION_TIMEOUT_US);
408133c81f8SSen Wang ret |= regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH3_CH4_REG,
409133c81f8SSen Wang state34, (state34 & 0x0F) != TAS675X_STATE_LOAD_DIAG &&
410133c81f8SSen Wang (state34 >> 4) != TAS675X_STATE_LOAD_DIAG,
411133c81f8SSen Wang TAS675X_POLL_INTERVAL_US,
412133c81f8SSen Wang TAS675X_STATE_TRANSITION_TIMEOUT_US);
413133c81f8SSen Wang if (ret) {
414133c81f8SSen Wang dev_err(tas->dev,
415133c81f8SSen Wang "DC LDG: abort timeout (CH1/2=0x%02x [%s/%s], CH3/4=0x%02x [%s/%s])\n",
416133c81f8SSen Wang state, tas675x_state_name(state), tas675x_state_name(state >> 4),
417133c81f8SSen Wang state34, tas675x_state_name(state34), tas675x_state_name(state34 >> 4));
418133c81f8SSen Wang goto out_restore_ldg_ctrl;
419133c81f8SSen Wang }
420133c81f8SSen Wang
421133c81f8SSen Wang /* Transition to HIZ state */
422133c81f8SSen Wang ret = tas675x_set_state_all(tas, TAS675X_STATE_HIZ_BOTH);
423133c81f8SSen Wang if (ret)
424133c81f8SSen Wang goto out_restore_ldg_ctrl;
425133c81f8SSen Wang
426133c81f8SSen Wang /* Set LOAD_DIAG state for manual DC LDG */
427133c81f8SSen Wang ret = tas675x_set_state_all(tas, TAS675X_STATE_LOAD_DIAG_BOTH);
428133c81f8SSen Wang if (ret)
429133c81f8SSen Wang goto out_restore_ldg_ctrl;
430133c81f8SSen Wang
431133c81f8SSen Wang /* Wait for device to transition to LOAD_DIAG state */
432133c81f8SSen Wang ret = regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH1_CH2_REG,
433133c81f8SSen Wang state, state == TAS675X_STATE_LOAD_DIAG_BOTH,
434133c81f8SSen Wang TAS675X_POLL_INTERVAL_US,
435133c81f8SSen Wang TAS675X_STATE_TRANSITION_TIMEOUT_US);
436133c81f8SSen Wang ret |= regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH3_CH4_REG,
437133c81f8SSen Wang state34, state34 == TAS675X_STATE_LOAD_DIAG_BOTH,
438133c81f8SSen Wang TAS675X_POLL_INTERVAL_US,
439133c81f8SSen Wang TAS675X_STATE_TRANSITION_TIMEOUT_US);
440133c81f8SSen Wang if (ret) {
441133c81f8SSen Wang dev_err(tas->dev,
442133c81f8SSen Wang "DC LDG: LOAD_DIAG timeout (CH1/2=0x%02x [%s/%s], CH3/4=0x%02x [%s/%s])\n",
443133c81f8SSen Wang state, tas675x_state_name(state), tas675x_state_name(state >> 4),
444133c81f8SSen Wang state34, tas675x_state_name(state34), tas675x_state_name(state34 >> 4));
445133c81f8SSen Wang goto out_restore_hiz;
446133c81f8SSen Wang }
447133c81f8SSen Wang
448133c81f8SSen Wang /* Clear ABORT and BYPASS bits to enable manual DC LDG */
449133c81f8SSen Wang ret = regmap_update_bits(tas->regmap, TAS675X_DC_LDG_CTRL_REG,
450133c81f8SSen Wang TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT,
451133c81f8SSen Wang 0);
452133c81f8SSen Wang if (ret)
453133c81f8SSen Wang goto out_restore_hiz;
454133c81f8SSen Wang
455133c81f8SSen Wang dev_dbg(tas->dev, "DC LDG: Started\n");
456133c81f8SSen Wang
457133c81f8SSen Wang /* Poll all channels for SLEEP state */
458133c81f8SSen Wang ret = regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH1_CH2_REG,
459133c81f8SSen Wang state, state == TAS675X_STATE_SLEEP_BOTH,
460133c81f8SSen Wang TAS675X_POLL_INTERVAL_US,
461133c81f8SSen Wang TAS675X_DC_LDG_TIMEOUT_US);
462133c81f8SSen Wang ret |= regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH3_CH4_REG,
463133c81f8SSen Wang state34, state34 == TAS675X_STATE_SLEEP_BOTH,
464133c81f8SSen Wang TAS675X_POLL_INTERVAL_US,
465133c81f8SSen Wang TAS675X_DC_LDG_TIMEOUT_US);
466133c81f8SSen Wang if (ret) {
467133c81f8SSen Wang dev_err(tas->dev,
468133c81f8SSen Wang "DC LDG: SLEEP timeout (CH1/2=0x%02x [%s/%s], CH3/4=0x%02x [%s/%s])\n",
469133c81f8SSen Wang state, tas675x_state_name(state), tas675x_state_name(state >> 4),
470133c81f8SSen Wang state34, tas675x_state_name(state34), tas675x_state_name(state34 >> 4));
471133c81f8SSen Wang goto out_restore_hiz;
472133c81f8SSen Wang }
473133c81f8SSen Wang
474133c81f8SSen Wang dev_dbg(tas->dev, "DC LDG: Completed successfully (CH1/2=0x%02x, CH3/4=0x%02x)\n",
475133c81f8SSen Wang state, state34);
476133c81f8SSen Wang
477133c81f8SSen Wang out_restore_hiz:
478133c81f8SSen Wang tas675x_set_state_all(tas, TAS675X_STATE_HIZ_BOTH);
479133c81f8SSen Wang
480133c81f8SSen Wang out_restore_ldg_ctrl:
481133c81f8SSen Wang regmap_update_bits(tas->regmap, TAS675X_DC_LDG_CTRL_REG,
482133c81f8SSen Wang TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT,
483133c81f8SSen Wang 0);
484133c81f8SSen Wang
485133c81f8SSen Wang pm_runtime_mark_last_busy(tas->dev);
486133c81f8SSen Wang pm_runtime_put_autosuspend(tas->dev);
487133c81f8SSen Wang
488133c81f8SSen Wang return ret;
489133c81f8SSen Wang }
490133c81f8SSen Wang
tas675x_set_acldg_trigger(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)491133c81f8SSen Wang static int tas675x_set_acldg_trigger(struct snd_kcontrol *kcontrol,
492133c81f8SSen Wang struct snd_ctl_elem_value *ucontrol)
493133c81f8SSen Wang {
494133c81f8SSen Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
495133c81f8SSen Wang struct tas675x_priv *tas = snd_soc_component_get_drvdata(comp);
496133c81f8SSen Wang unsigned int state, state34;
497133c81f8SSen Wang int ret;
498133c81f8SSen Wang
499133c81f8SSen Wang if (!ucontrol->value.integer.value[0])
500133c81f8SSen Wang return 0;
501133c81f8SSen Wang
502133c81f8SSen Wang if (snd_soc_component_active(comp))
503133c81f8SSen Wang return -EBUSY;
504133c81f8SSen Wang
505133c81f8SSen Wang ret = pm_runtime_resume_and_get(tas->dev);
506133c81f8SSen Wang if (ret < 0)
507133c81f8SSen Wang return ret;
508133c81f8SSen Wang
509133c81f8SSen Wang /* AC Load Diagnostics requires SLEEP state */
510133c81f8SSen Wang ret = tas675x_set_state_all(tas, TAS675X_STATE_SLEEP_BOTH);
511133c81f8SSen Wang if (ret) {
512133c81f8SSen Wang dev_err(tas->dev, "AC LDG: Failed to set SLEEP state: %d\n", ret);
513133c81f8SSen Wang goto out;
514133c81f8SSen Wang }
515133c81f8SSen Wang
516133c81f8SSen Wang /* Start AC LDG on all 4 channels (0x0F) */
517133c81f8SSen Wang ret = regmap_write(tas->regmap, TAS675X_AC_LDG_CTRL_REG, 0x0F);
518133c81f8SSen Wang if (ret) {
519133c81f8SSen Wang dev_err(tas->dev, "AC LDG: Failed to start: %d\n", ret);
520133c81f8SSen Wang goto out;
521133c81f8SSen Wang }
522133c81f8SSen Wang
523133c81f8SSen Wang dev_dbg(tas->dev, "AC LDG: Started\n");
524133c81f8SSen Wang
525133c81f8SSen Wang /* Poll all channels for SLEEP state */
526133c81f8SSen Wang ret = regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH1_CH2_REG,
527133c81f8SSen Wang state, (state == TAS675X_STATE_SLEEP_BOTH),
528133c81f8SSen Wang TAS675X_POLL_INTERVAL_US,
529133c81f8SSen Wang TAS675X_AC_LDG_TIMEOUT_US);
530133c81f8SSen Wang if (ret) {
531133c81f8SSen Wang dev_err(tas->dev,
532133c81f8SSen Wang "AC LDG: CH1/CH2 timeout: %d (state=0x%02x [%s/%s])\n",
533133c81f8SSen Wang ret, state, tas675x_state_name(state),
534133c81f8SSen Wang tas675x_state_name(state >> 4));
535133c81f8SSen Wang regmap_write(tas->regmap, TAS675X_AC_LDG_CTRL_REG, 0x00);
536133c81f8SSen Wang goto out;
537133c81f8SSen Wang }
538133c81f8SSen Wang
539133c81f8SSen Wang ret = regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH3_CH4_REG,
540133c81f8SSen Wang state34, (state34 == TAS675X_STATE_SLEEP_BOTH),
541133c81f8SSen Wang TAS675X_POLL_INTERVAL_US,
542133c81f8SSen Wang TAS675X_AC_LDG_TIMEOUT_US);
543133c81f8SSen Wang if (ret) {
544133c81f8SSen Wang dev_err(tas->dev,
545133c81f8SSen Wang "AC LDG: CH3/CH4 timeout: %d (state=0x%02x [%s/%s])\n",
546133c81f8SSen Wang ret, state34, tas675x_state_name(state34),
547133c81f8SSen Wang tas675x_state_name(state34 >> 4));
548133c81f8SSen Wang regmap_write(tas->regmap, TAS675X_AC_LDG_CTRL_REG, 0x00);
549133c81f8SSen Wang goto out;
550133c81f8SSen Wang }
551133c81f8SSen Wang
552133c81f8SSen Wang dev_dbg(tas->dev, "AC LDG: Completed successfully (CH1/2=0x%02x, CH3/4=0x%02x)\n",
553133c81f8SSen Wang state, state34);
554133c81f8SSen Wang regmap_write(tas->regmap, TAS675X_AC_LDG_CTRL_REG, 0x00);
555133c81f8SSen Wang
556133c81f8SSen Wang out:
557133c81f8SSen Wang pm_runtime_mark_last_busy(tas->dev);
558133c81f8SSen Wang pm_runtime_put_autosuspend(tas->dev);
559133c81f8SSen Wang
560133c81f8SSen Wang return ret;
561133c81f8SSen Wang }
562133c81f8SSen Wang
tas675x_rtldg_impedance_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)563133c81f8SSen Wang static int tas675x_rtldg_impedance_info(struct snd_kcontrol *kcontrol,
564133c81f8SSen Wang struct snd_ctl_elem_info *uinfo)
565133c81f8SSen Wang {
566133c81f8SSen Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
567133c81f8SSen Wang uinfo->count = 1;
568133c81f8SSen Wang uinfo->value.integer.min = 0;
569133c81f8SSen Wang uinfo->value.integer.max = 0xFFFF;
570133c81f8SSen Wang return 0;
571133c81f8SSen Wang }
572133c81f8SSen Wang
tas675x_get_rtldg_impedance(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)573133c81f8SSen Wang static int tas675x_get_rtldg_impedance(struct snd_kcontrol *kcontrol,
574133c81f8SSen Wang struct snd_ctl_elem_value *ucontrol)
575133c81f8SSen Wang {
576133c81f8SSen Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
577133c81f8SSen Wang struct tas675x_priv *tas = snd_soc_component_get_drvdata(comp);
578133c81f8SSen Wang unsigned int msb_reg = (unsigned int)kcontrol->private_value;
579133c81f8SSen Wang u8 buf[2];
580133c81f8SSen Wang int ret;
581133c81f8SSen Wang
582133c81f8SSen Wang ret = regmap_bulk_read(tas->regmap, msb_reg, buf, 2);
583133c81f8SSen Wang if (ret)
584133c81f8SSen Wang return ret;
585133c81f8SSen Wang
586133c81f8SSen Wang ucontrol->value.integer.value[0] = (buf[0] << 8) | buf[1];
587133c81f8SSen Wang return 0;
588133c81f8SSen Wang }
589133c81f8SSen Wang
tas675x_dc_resistance_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)590133c81f8SSen Wang static int tas675x_dc_resistance_info(struct snd_kcontrol *kcontrol,
591133c81f8SSen Wang struct snd_ctl_elem_info *uinfo)
592133c81f8SSen Wang {
593133c81f8SSen Wang /* 10-bit: 2-bit MSB + 8-bit LSB, 0.1 ohm/code, 0-102.3 ohm */
594133c81f8SSen Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
595133c81f8SSen Wang uinfo->count = 1;
596133c81f8SSen Wang uinfo->value.integer.min = 0;
597133c81f8SSen Wang uinfo->value.integer.max = 1023;
598133c81f8SSen Wang return 0;
599133c81f8SSen Wang }
600133c81f8SSen Wang
tas675x_get_dc_resistance(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)601133c81f8SSen Wang static int tas675x_get_dc_resistance(struct snd_kcontrol *kcontrol,
602133c81f8SSen Wang struct snd_ctl_elem_value *ucontrol)
603133c81f8SSen Wang {
604133c81f8SSen Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
605133c81f8SSen Wang struct tas675x_priv *tas = snd_soc_component_get_drvdata(comp);
606133c81f8SSen Wang unsigned int lsb_reg = (unsigned int)kcontrol->private_value;
607133c81f8SSen Wang unsigned int msb, lsb, shift;
608133c81f8SSen Wang int ret;
609133c81f8SSen Wang
610133c81f8SSen Wang ret = regmap_read(tas->regmap, TAS675X_DC_LDG_DCR_MSB_REG, &msb);
611133c81f8SSen Wang if (ret)
612133c81f8SSen Wang return ret;
613133c81f8SSen Wang
614133c81f8SSen Wang ret = regmap_read(tas->regmap, lsb_reg, &lsb);
615133c81f8SSen Wang if (ret)
616133c81f8SSen Wang return ret;
617133c81f8SSen Wang
618133c81f8SSen Wang /* 2-bit MSB: CH1=[7:6], CH2=[5:4], CH3=[3:2], CH4=[1:0] */
619133c81f8SSen Wang shift = 6 - (lsb_reg - TAS675X_CH1_DC_LDG_DCR_LSB_REG) * 2;
620133c81f8SSen Wang msb = (msb >> shift) & 0x3;
621133c81f8SSen Wang
622133c81f8SSen Wang ucontrol->value.integer.value[0] = (msb << 8) | lsb;
623133c81f8SSen Wang return 0;
624133c81f8SSen Wang }
625133c81f8SSen Wang
626133c81f8SSen Wang /* Counterparts with read-only access */
627133c81f8SSen Wang #define SOC_SINGLE_RO(xname, xreg, xshift, xmax) \
628133c81f8SSen Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
629133c81f8SSen Wang .name = xname, \
630133c81f8SSen Wang .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
631133c81f8SSen Wang .info = snd_soc_info_volsw, \
632133c81f8SSen Wang .get = snd_soc_get_volsw, \
633133c81f8SSen Wang .private_value = SOC_SINGLE_VALUE(xreg, xshift, 0, xmax, 0, 0) }
634133c81f8SSen Wang #define SOC_DC_RESIST_RO(xname, xlsb_reg) \
635133c81f8SSen Wang { .name = xname, \
636133c81f8SSen Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
637133c81f8SSen Wang .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
638133c81f8SSen Wang .info = tas675x_dc_resistance_info, \
639133c81f8SSen Wang .get = tas675x_get_dc_resistance, \
640133c81f8SSen Wang .private_value = (xlsb_reg) }
641133c81f8SSen Wang #define SOC_RTLDG_IMP_RO(xname, xreg) \
642133c81f8SSen Wang { .name = xname, \
643133c81f8SSen Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
644133c81f8SSen Wang .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
645133c81f8SSen Wang .info = tas675x_rtldg_impedance_info, \
646133c81f8SSen Wang .get = tas675x_get_rtldg_impedance, \
647133c81f8SSen Wang .private_value = (xreg) }
648133c81f8SSen Wang
649133c81f8SSen Wang #define SOC_DSP_THRESH_EXT(xname, xthresh) \
650133c81f8SSen Wang { .name = xname, \
651133c81f8SSen Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
652133c81f8SSen Wang .info = tas675x_rtldg_thresh_info, \
653133c81f8SSen Wang .get = tas675x_get_rtldg_thresh, \
654133c81f8SSen Wang .put = tas675x_set_rtldg_thresh, \
655133c81f8SSen Wang .private_value = (unsigned long)&(xthresh) }
656133c81f8SSen Wang
657133c81f8SSen Wang /*
658133c81f8SSen Wang * DAC digital volumes. From -103 to 0 dB in 0.5 dB steps, -103.5 dB means mute.
659133c81f8SSen Wang * DAC analog gain. From -15.5 to 0 dB in 0.5 dB steps, no mute.
660133c81f8SSen Wang */
661133c81f8SSen Wang static const DECLARE_TLV_DB_SCALE(tas675x_dig_vol_tlv, -10350, 50, 1);
662133c81f8SSen Wang static const DECLARE_TLV_DB_SCALE(tas675x_ana_gain_tlv, -1550, 50, 0);
663133c81f8SSen Wang
664133c81f8SSen Wang static const char * const tas675x_ss_texts[] = {
665133c81f8SSen Wang "Disabled", "Triangle", "Random", "Triangle and Random"
666133c81f8SSen Wang };
667133c81f8SSen Wang
668133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_ss_enum, TAS675X_SS_CTRL_REG, 0, tas675x_ss_texts);
669133c81f8SSen Wang
670133c81f8SSen Wang static const char * const tas675x_ss_tri_range_texts[] = {
671133c81f8SSen Wang "6.5%", "13.5%", "5%", "10%"
672133c81f8SSen Wang };
673133c81f8SSen Wang
674133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_ss_tri_range_enum,
675133c81f8SSen Wang TAS675X_SS_RANGE_CTRL_REG, 0,
676133c81f8SSen Wang tas675x_ss_tri_range_texts);
677133c81f8SSen Wang
678133c81f8SSen Wang static const char * const tas675x_ss_rdm_range_texts[] = {
679133c81f8SSen Wang "0.83%", "2.50%", "5.83%", "12.50%", "25.83%"
680133c81f8SSen Wang };
681133c81f8SSen Wang
682133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_ss_rdm_range_enum,
683133c81f8SSen Wang TAS675X_SS_RANGE_CTRL_REG, 4,
684133c81f8SSen Wang tas675x_ss_rdm_range_texts);
685133c81f8SSen Wang
686133c81f8SSen Wang static const char * const tas675x_ss_rdm_dwell_texts[] = {
687133c81f8SSen Wang "1/FSS to 2/FSS", "1/FSS to 4/FSS", "1/FSS to 8/FSS", "1/FSS to 15/FSS"
688133c81f8SSen Wang };
689133c81f8SSen Wang
690133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_ss_rdm_dwell_enum,
691133c81f8SSen Wang TAS675X_SS_RANGE_CTRL_REG, 2,
692133c81f8SSen Wang tas675x_ss_rdm_dwell_texts);
693133c81f8SSen Wang
694133c81f8SSen Wang static const char * const tas675x_oc_limit_texts[] = {
695133c81f8SSen Wang "Level 4", "Level 3", "Level 2", "Level 1"
696133c81f8SSen Wang };
697133c81f8SSen Wang
698133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_oc_limit_enum, TAS675X_CURRENT_LIMIT_CTRL_REG,
699133c81f8SSen Wang 0, tas675x_oc_limit_texts);
700133c81f8SSen Wang
701133c81f8SSen Wang static const char * const tas675x_otw_texts[] = {
702133c81f8SSen Wang "Disabled", ">95C", ">110C", ">125C", ">135C", ">145C", ">155C", ">165C"
703133c81f8SSen Wang };
704133c81f8SSen Wang
705133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_ch1_otw_enum,
706133c81f8SSen Wang TAS675X_OTW_CTRL_CH1_CH2_REG, 4,
707133c81f8SSen Wang tas675x_otw_texts);
708133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_ch2_otw_enum,
709133c81f8SSen Wang TAS675X_OTW_CTRL_CH1_CH2_REG, 0,
710133c81f8SSen Wang tas675x_otw_texts);
711133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_ch3_otw_enum,
712133c81f8SSen Wang TAS675X_OTW_CTRL_CH3_CH4_REG, 4,
713133c81f8SSen Wang tas675x_otw_texts);
714133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_ch4_otw_enum,
715133c81f8SSen Wang TAS675X_OTW_CTRL_CH3_CH4_REG, 0,
716133c81f8SSen Wang tas675x_otw_texts);
717133c81f8SSen Wang
718133c81f8SSen Wang static const char * const tas675x_dc_ldg_sl_texts[] = {
719133c81f8SSen Wang "0.5 Ohm", "1 Ohm", "1.5 Ohm", "2 Ohm", "2.5 Ohm",
720133c81f8SSen Wang "3 Ohm", "3.5 Ohm", "4 Ohm", "4.5 Ohm", "5 Ohm"
721133c81f8SSen Wang };
722133c81f8SSen Wang
723133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_ch1_dc_ldg_sl_enum,
724133c81f8SSen Wang TAS675X_DC_LDG_SL_CH1_CH2_CTRL_REG, 4,
725133c81f8SSen Wang tas675x_dc_ldg_sl_texts);
726133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_ch2_dc_ldg_sl_enum,
727133c81f8SSen Wang TAS675X_DC_LDG_SL_CH1_CH2_CTRL_REG, 0,
728133c81f8SSen Wang tas675x_dc_ldg_sl_texts);
729133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_ch3_dc_ldg_sl_enum,
730133c81f8SSen Wang TAS675X_DC_LDG_SL_CH3_CH4_CTRL_REG, 4,
731133c81f8SSen Wang tas675x_dc_ldg_sl_texts);
732133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_ch4_dc_ldg_sl_enum,
733133c81f8SSen Wang TAS675X_DC_LDG_SL_CH3_CH4_CTRL_REG, 0,
734133c81f8SSen Wang tas675x_dc_ldg_sl_texts);
735133c81f8SSen Wang
736133c81f8SSen Wang static const char * const tas675x_dc_slol_ramp_texts[] = {
737133c81f8SSen Wang "15 ms", "30 ms", "10 ms", "20 ms"
738133c81f8SSen Wang };
739133c81f8SSen Wang
740133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_dc_slol_ramp_enum,
741133c81f8SSen Wang TAS675X_DC_LDG_TIME_CTRL_REG, 6,
742133c81f8SSen Wang tas675x_dc_slol_ramp_texts);
743133c81f8SSen Wang
744133c81f8SSen Wang static const char * const tas675x_dc_slol_settling_texts[] = {
745133c81f8SSen Wang "10 ms", "5 ms", "20 ms", "15 ms"
746133c81f8SSen Wang };
747133c81f8SSen Wang
748133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_dc_slol_settling_enum,
749133c81f8SSen Wang TAS675X_DC_LDG_TIME_CTRL_REG, 4,
750133c81f8SSen Wang tas675x_dc_slol_settling_texts);
751133c81f8SSen Wang
752133c81f8SSen Wang static const char * const tas675x_dc_s2pg_ramp_texts[] = {
753133c81f8SSen Wang "5 ms", "2.5 ms", "10 ms", "15 ms"
754133c81f8SSen Wang };
755133c81f8SSen Wang
756133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_dc_s2pg_ramp_enum,
757133c81f8SSen Wang TAS675X_DC_LDG_TIME_CTRL_REG, 2,
758133c81f8SSen Wang tas675x_dc_s2pg_ramp_texts);
759133c81f8SSen Wang
760133c81f8SSen Wang static const char * const tas675x_dc_s2pg_settling_texts[] = {
761133c81f8SSen Wang "10 ms", "5 ms", "20 ms", "30 ms"
762133c81f8SSen Wang };
763133c81f8SSen Wang
764133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_dc_s2pg_settling_enum,
765133c81f8SSen Wang TAS675X_DC_LDG_TIME_CTRL_REG, 0,
766133c81f8SSen Wang tas675x_dc_s2pg_settling_texts);
767133c81f8SSen Wang
768133c81f8SSen Wang static const char * const tas675x_dsp_mode_texts[] = {
769133c81f8SSen Wang "Normal", "LLP", "FFLP"
770133c81f8SSen Wang };
771133c81f8SSen Wang
772133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_dsp_mode_enum,
773133c81f8SSen Wang TAS675X_LL_EN_REG, 0,
774133c81f8SSen Wang tas675x_dsp_mode_texts);
775133c81f8SSen Wang
776133c81f8SSen Wang static const char * const tas675x_ana_ramp_texts[] = {
777133c81f8SSen Wang "15us", "60us", "200us", "400us"
778133c81f8SSen Wang };
779133c81f8SSen Wang
780133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_ana_ramp_enum,
781133c81f8SSen Wang TAS675X_ANALOG_GAIN_RAMP_CTRL_REG, 2,
782133c81f8SSen Wang tas675x_ana_ramp_texts);
783133c81f8SSen Wang
784133c81f8SSen Wang static const char * const tas675x_ramp_rate_texts[] = {
785133c81f8SSen Wang "4 FS", "16 FS", "32 FS", "Instant"
786133c81f8SSen Wang };
787133c81f8SSen Wang
788133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_ramp_down_rate_enum,
789133c81f8SSen Wang TAS675X_DIG_VOL_RAMP_CTRL_REG, 6,
790133c81f8SSen Wang tas675x_ramp_rate_texts);
791133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_ramp_up_rate_enum,
792133c81f8SSen Wang TAS675X_DIG_VOL_RAMP_CTRL_REG, 2,
793133c81f8SSen Wang tas675x_ramp_rate_texts);
794133c81f8SSen Wang
795133c81f8SSen Wang static const char * const tas675x_ramp_step_texts[] = {
796133c81f8SSen Wang "4dB", "2dB", "1dB", "0.5dB"
797133c81f8SSen Wang };
798133c81f8SSen Wang
799133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_ramp_down_step_enum,
800133c81f8SSen Wang TAS675X_DIG_VOL_RAMP_CTRL_REG, 4,
801133c81f8SSen Wang tas675x_ramp_step_texts);
802133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_ramp_up_step_enum,
803133c81f8SSen Wang TAS675X_DIG_VOL_RAMP_CTRL_REG, 0,
804133c81f8SSen Wang tas675x_ramp_step_texts);
805133c81f8SSen Wang
806133c81f8SSen Wang static const char * const tas675x_vol_combine_ch12_texts[] = {
807133c81f8SSen Wang "Independent", "CH2 follows CH1", "CH1 follows CH2"
808133c81f8SSen Wang };
809133c81f8SSen Wang
810133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_vol_combine_ch12_enum,
811133c81f8SSen Wang TAS675X_DIG_VOL_COMBINE_CTRL_REG, 0,
812133c81f8SSen Wang tas675x_vol_combine_ch12_texts);
813133c81f8SSen Wang
814133c81f8SSen Wang static const char * const tas675x_vol_combine_ch34_texts[] = {
815133c81f8SSen Wang "Independent", "CH4 follows CH3", "CH3 follows CH4"
816133c81f8SSen Wang };
817133c81f8SSen Wang
818133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_vol_combine_ch34_enum,
819133c81f8SSen Wang TAS675X_DIG_VOL_COMBINE_CTRL_REG, 2,
820133c81f8SSen Wang tas675x_vol_combine_ch34_texts);
821133c81f8SSen Wang
822133c81f8SSen Wang static const char * const tas675x_auto_mute_time_texts[] = {
823133c81f8SSen Wang "11.5ms", "53ms", "106.5ms", "266.5ms",
824133c81f8SSen Wang "535ms", "1065ms", "2665ms", "5330ms"
825133c81f8SSen Wang };
826133c81f8SSen Wang
827133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_ch1_mute_time_enum,
828133c81f8SSen Wang TAS675X_AUTO_MUTE_TIMING_CH1_CH2_REG, 4,
829133c81f8SSen Wang tas675x_auto_mute_time_texts);
830133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_ch2_mute_time_enum,
831133c81f8SSen Wang TAS675X_AUTO_MUTE_TIMING_CH1_CH2_REG, 0,
832133c81f8SSen Wang tas675x_auto_mute_time_texts);
833133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_ch3_mute_time_enum,
834133c81f8SSen Wang TAS675X_AUTO_MUTE_TIMING_CH3_CH4_REG, 4,
835133c81f8SSen Wang tas675x_auto_mute_time_texts);
836133c81f8SSen Wang static SOC_ENUM_SINGLE_DECL(tas675x_ch4_mute_time_enum,
837133c81f8SSen Wang TAS675X_AUTO_MUTE_TIMING_CH3_CH4_REG, 0,
838133c81f8SSen Wang tas675x_auto_mute_time_texts);
839133c81f8SSen Wang
840133c81f8SSen Wang /*
841133c81f8SSen Wang * ALSA Mixer Controls
842133c81f8SSen Wang *
843133c81f8SSen Wang * For detailed documentation of each control see:
844133c81f8SSen Wang * Documentation/sound/codecs/tas675x.rst
845133c81f8SSen Wang */
846133c81f8SSen Wang static const struct snd_kcontrol_new tas675x_snd_controls[] = {
847133c81f8SSen Wang /* Volume & Gain Control */
848133c81f8SSen Wang SOC_DOUBLE_R_TLV("Analog Playback Volume", TAS675X_ANALOG_GAIN_CH1_CH2_REG,
849133c81f8SSen Wang TAS675X_ANALOG_GAIN_CH3_CH4_REG, 1, 0x1F, 1, tas675x_ana_gain_tlv),
850133c81f8SSen Wang SOC_ENUM("Analog Gain Ramp Step", tas675x_ana_ramp_enum),
851133c81f8SSen Wang SOC_SINGLE_RANGE_TLV("CH1 Digital Playback Volume",
852133c81f8SSen Wang TAS675X_DIG_VOL_CH1_REG, 0, 0x30, 0xFF, 1,
853133c81f8SSen Wang tas675x_dig_vol_tlv),
854133c81f8SSen Wang SOC_SINGLE_RANGE_TLV("CH2 Digital Playback Volume",
855133c81f8SSen Wang TAS675X_DIG_VOL_CH2_REG, 0, 0x30, 0xFF, 1,
856133c81f8SSen Wang tas675x_dig_vol_tlv),
857133c81f8SSen Wang SOC_SINGLE_RANGE_TLV("CH3 Digital Playback Volume",
858133c81f8SSen Wang TAS675X_DIG_VOL_CH3_REG, 0, 0x30, 0xFF, 1,
859133c81f8SSen Wang tas675x_dig_vol_tlv),
860133c81f8SSen Wang SOC_SINGLE_RANGE_TLV("CH4 Digital Playback Volume",
861133c81f8SSen Wang TAS675X_DIG_VOL_CH4_REG, 0, 0x30, 0xFF, 1,
862133c81f8SSen Wang tas675x_dig_vol_tlv),
863133c81f8SSen Wang SOC_ENUM("Volume Ramp Down Rate", tas675x_ramp_down_rate_enum),
864133c81f8SSen Wang SOC_ENUM("Volume Ramp Down Step", tas675x_ramp_down_step_enum),
865133c81f8SSen Wang SOC_ENUM("Volume Ramp Up Rate", tas675x_ramp_up_rate_enum),
866133c81f8SSen Wang SOC_ENUM("Volume Ramp Up Step", tas675x_ramp_up_step_enum),
867133c81f8SSen Wang SOC_ENUM("CH1/2 Volume Combine", tas675x_vol_combine_ch12_enum),
868133c81f8SSen Wang SOC_ENUM("CH3/4 Volume Combine", tas675x_vol_combine_ch34_enum),
869133c81f8SSen Wang
870133c81f8SSen Wang /* Auto Mute & Silence Detection */
871133c81f8SSen Wang SOC_SINGLE("CH1 Auto Mute Switch", TAS675X_AUTO_MUTE_EN_REG, 0, 1, 0),
872133c81f8SSen Wang SOC_SINGLE("CH2 Auto Mute Switch", TAS675X_AUTO_MUTE_EN_REG, 1, 1, 0),
873133c81f8SSen Wang SOC_SINGLE("CH3 Auto Mute Switch", TAS675X_AUTO_MUTE_EN_REG, 2, 1, 0),
874133c81f8SSen Wang SOC_SINGLE("CH4 Auto Mute Switch", TAS675X_AUTO_MUTE_EN_REG, 3, 1, 0),
875133c81f8SSen Wang SOC_SINGLE("Auto Mute Combine Switch", TAS675X_AUTO_MUTE_EN_REG, 4, 1, 0),
876133c81f8SSen Wang SOC_ENUM("CH1 Auto Mute Time", tas675x_ch1_mute_time_enum),
877133c81f8SSen Wang SOC_ENUM("CH2 Auto Mute Time", tas675x_ch2_mute_time_enum),
878133c81f8SSen Wang SOC_ENUM("CH3 Auto Mute Time", tas675x_ch3_mute_time_enum),
879133c81f8SSen Wang SOC_ENUM("CH4 Auto Mute Time", tas675x_ch4_mute_time_enum),
880133c81f8SSen Wang
881133c81f8SSen Wang /* Clock & EMI Management */
882133c81f8SSen Wang SOC_ENUM("Spread Spectrum Mode", tas675x_ss_enum),
883133c81f8SSen Wang SOC_ENUM("SS Triangle Range", tas675x_ss_tri_range_enum),
884133c81f8SSen Wang SOC_ENUM("SS Random Range", tas675x_ss_rdm_range_enum),
885133c81f8SSen Wang SOC_ENUM("SS Random Dwell Range", tas675x_ss_rdm_dwell_enum),
886133c81f8SSen Wang SOC_SINGLE("SS Triangle Dwell Min", TAS675X_SS_DWELL_CTRL_REG, 4, 15, 0),
887133c81f8SSen Wang SOC_SINGLE("SS Triangle Dwell Max", TAS675X_SS_DWELL_CTRL_REG, 0, 15, 0),
888133c81f8SSen Wang
889133c81f8SSen Wang /* Hardware Protection */
890133c81f8SSen Wang SOC_SINGLE("OTSD Auto Recovery Switch", TAS675X_OTSD_RECOVERY_EN_REG, 1, 1, 0),
891133c81f8SSen Wang SOC_ENUM("Overcurrent Limit Level", tas675x_oc_limit_enum),
892133c81f8SSen Wang SOC_ENUM("CH1 OTW Threshold", tas675x_ch1_otw_enum),
893133c81f8SSen Wang SOC_ENUM("CH2 OTW Threshold", tas675x_ch2_otw_enum),
894133c81f8SSen Wang SOC_ENUM("CH3 OTW Threshold", tas675x_ch3_otw_enum),
895133c81f8SSen Wang SOC_ENUM("CH4 OTW Threshold", tas675x_ch4_otw_enum),
896133c81f8SSen Wang
897133c81f8SSen Wang /* DSP Signal Path & Mode */
898133c81f8SSen Wang SOC_ENUM("DSP Signal Path Mode", tas675x_dsp_mode_enum),
899133c81f8SSen Wang
900133c81f8SSen Wang /* DC Load Diagnostics */
901133c81f8SSen Wang {
902133c81f8SSen Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
903133c81f8SSen Wang .name = "DC LDG Trigger",
904133c81f8SSen Wang .access = SNDRV_CTL_ELEM_ACCESS_WRITE,
905133c81f8SSen Wang .info = snd_ctl_boolean_mono_info,
906133c81f8SSen Wang .put = tas675x_set_dcldg_trigger,
907133c81f8SSen Wang },
908133c81f8SSen Wang SOC_SINGLE("DC LDG Auto Diagnostics Switch", TAS675X_DC_LDG_CTRL_REG, 0, 1, 1),
909133c81f8SSen Wang SOC_SINGLE("CH1 LO LDG Switch", TAS675X_DC_LDG_LO_CTRL_REG, 3, 1, 0),
910133c81f8SSen Wang SOC_SINGLE("CH2 LO LDG Switch", TAS675X_DC_LDG_LO_CTRL_REG, 2, 1, 0),
911133c81f8SSen Wang SOC_SINGLE("CH3 LO LDG Switch", TAS675X_DC_LDG_LO_CTRL_REG, 1, 1, 0),
912133c81f8SSen Wang SOC_SINGLE("CH4 LO LDG Switch", TAS675X_DC_LDG_LO_CTRL_REG, 0, 1, 0),
913133c81f8SSen Wang SOC_ENUM("DC LDG SLOL Ramp Time", tas675x_dc_slol_ramp_enum),
914133c81f8SSen Wang SOC_ENUM("DC LDG SLOL Settling Time", tas675x_dc_slol_settling_enum),
915133c81f8SSen Wang SOC_ENUM("DC LDG S2PG Ramp Time", tas675x_dc_s2pg_ramp_enum),
916133c81f8SSen Wang SOC_ENUM("DC LDG S2PG Settling Time", tas675x_dc_s2pg_settling_enum),
917133c81f8SSen Wang SOC_ENUM("CH1 DC LDG SL Threshold", tas675x_ch1_dc_ldg_sl_enum),
918133c81f8SSen Wang SOC_ENUM("CH2 DC LDG SL Threshold", tas675x_ch2_dc_ldg_sl_enum),
919133c81f8SSen Wang SOC_ENUM("CH3 DC LDG SL Threshold", tas675x_ch3_dc_ldg_sl_enum),
920133c81f8SSen Wang SOC_ENUM("CH4 DC LDG SL Threshold", tas675x_ch4_dc_ldg_sl_enum),
921133c81f8SSen Wang SOC_SINGLE_RO("DC LDG Result", TAS675X_DC_LDG_RESULT_REG, 0, 0xFF),
922133c81f8SSen Wang SOC_SINGLE_RO("CH1 DC LDG Report", TAS675X_DC_LDG_REPORT_CH1_CH2_REG, 4, 0x0F),
923133c81f8SSen Wang SOC_SINGLE_RO("CH2 DC LDG Report", TAS675X_DC_LDG_REPORT_CH1_CH2_REG, 0, 0x0F),
924133c81f8SSen Wang SOC_SINGLE_RO("CH3 DC LDG Report", TAS675X_DC_LDG_REPORT_CH3_CH4_REG, 4, 0x0F),
925133c81f8SSen Wang SOC_SINGLE_RO("CH4 DC LDG Report", TAS675X_DC_LDG_REPORT_CH3_CH4_REG, 0, 0x0F),
926133c81f8SSen Wang SOC_SINGLE_RO("CH1 LO LDG Report", TAS675X_DC_LDG_RESULT_REG, 7, 1),
927133c81f8SSen Wang SOC_SINGLE_RO("CH2 LO LDG Report", TAS675X_DC_LDG_RESULT_REG, 6, 1),
928133c81f8SSen Wang SOC_SINGLE_RO("CH3 LO LDG Report", TAS675X_DC_LDG_RESULT_REG, 5, 1),
929133c81f8SSen Wang SOC_SINGLE_RO("CH4 LO LDG Report", TAS675X_DC_LDG_RESULT_REG, 4, 1),
930133c81f8SSen Wang SOC_DC_RESIST_RO("CH1 DC Resistance", TAS675X_CH1_DC_LDG_DCR_LSB_REG),
931133c81f8SSen Wang SOC_DC_RESIST_RO("CH2 DC Resistance", TAS675X_CH2_DC_LDG_DCR_LSB_REG),
932133c81f8SSen Wang SOC_DC_RESIST_RO("CH3 DC Resistance", TAS675X_CH3_DC_LDG_DCR_LSB_REG),
933133c81f8SSen Wang SOC_DC_RESIST_RO("CH4 DC Resistance", TAS675X_CH4_DC_LDG_DCR_LSB_REG),
934133c81f8SSen Wang
935133c81f8SSen Wang /* AC Load Diagnostics */
936133c81f8SSen Wang {
937133c81f8SSen Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
938133c81f8SSen Wang .name = "AC LDG Trigger",
939133c81f8SSen Wang .access = SNDRV_CTL_ELEM_ACCESS_WRITE,
940133c81f8SSen Wang .info = snd_ctl_boolean_mono_info,
941133c81f8SSen Wang .put = tas675x_set_acldg_trigger,
942133c81f8SSen Wang },
943133c81f8SSen Wang SOC_SINGLE("AC LDG Gain", TAS675X_AC_LDG_CTRL_REG, 4, 1, 0),
944133c81f8SSen Wang SOC_SINGLE("AC LDG Test Frequency", TAS675X_AC_LDG_FREQ_CTRL_REG, 0, 0xFF, 0),
945133c81f8SSen Wang SOC_SINGLE_RO("CH1 AC LDG Real", TAS675X_AC_LDG_REPORT_CH1_R_REG, 0, 0xFF),
946133c81f8SSen Wang SOC_SINGLE_RO("CH1 AC LDG Imag", TAS675X_AC_LDG_REPORT_CH1_I_REG, 0, 0xFF),
947133c81f8SSen Wang SOC_SINGLE_RO("CH2 AC LDG Real", TAS675X_AC_LDG_REPORT_CH2_R_REG, 0, 0xFF),
948133c81f8SSen Wang SOC_SINGLE_RO("CH2 AC LDG Imag", TAS675X_AC_LDG_REPORT_CH2_I_REG, 0, 0xFF),
949133c81f8SSen Wang SOC_SINGLE_RO("CH3 AC LDG Real", TAS675X_AC_LDG_REPORT_CH3_R_REG, 0, 0xFF),
950133c81f8SSen Wang SOC_SINGLE_RO("CH3 AC LDG Imag", TAS675X_AC_LDG_REPORT_CH3_I_REG, 0, 0xFF),
951133c81f8SSen Wang SOC_SINGLE_RO("CH4 AC LDG Real", TAS675X_AC_LDG_REPORT_CH4_R_REG, 0, 0xFF),
952133c81f8SSen Wang SOC_SINGLE_RO("CH4 AC LDG Imag", TAS675X_AC_LDG_REPORT_CH4_I_REG, 0, 0xFF),
953133c81f8SSen Wang
954133c81f8SSen Wang /* Temperature and Voltage Monitoring */
955133c81f8SSen Wang SOC_SINGLE_RO("PVDD Sense", TAS675X_PVDD_SENSE_REG, 0, 0xFF),
956133c81f8SSen Wang SOC_SINGLE_RO("Global Temperature", TAS675X_TEMP_GLOBAL_REG, 0, 0xFF),
957*a044f99dSSen Wang SOC_SINGLE_RO("CH1 Temperature Range", TAS675X_TEMP_CH1_CH2_REG, 0, 7),
958*a044f99dSSen Wang SOC_SINGLE_RO("CH2 Temperature Range", TAS675X_TEMP_CH1_CH2_REG, 3, 7),
959*a044f99dSSen Wang SOC_SINGLE_RO("CH3 Temperature Range", TAS675X_TEMP_CH3_CH4_REG, 0, 7),
960*a044f99dSSen Wang SOC_SINGLE_RO("CH4 Temperature Range", TAS675X_TEMP_CH3_CH4_REG, 3, 7),
961133c81f8SSen Wang
962133c81f8SSen Wang /* Speaker Protection & Detection */
963133c81f8SSen Wang SOC_SINGLE("Tweeter Detection Switch", TAS675X_TWEETER_DETECT_CTRL_REG, 0, 1, 1),
964133c81f8SSen Wang SOC_SINGLE("Tweeter Detect Threshold", TAS675X_TWEETER_DETECT_THRESH_REG, 0, 0xFF, 0),
965133c81f8SSen Wang SOC_SINGLE_RO("CH1 Tweeter Detect Report", TAS675X_TWEETER_REPORT_REG, 3, 1),
966133c81f8SSen Wang SOC_SINGLE_RO("CH2 Tweeter Detect Report", TAS675X_TWEETER_REPORT_REG, 2, 1),
967133c81f8SSen Wang SOC_SINGLE_RO("CH3 Tweeter Detect Report", TAS675X_TWEETER_REPORT_REG, 1, 1),
968133c81f8SSen Wang SOC_SINGLE_RO("CH4 Tweeter Detect Report", TAS675X_TWEETER_REPORT_REG, 0, 1),
969133c81f8SSen Wang
970133c81f8SSen Wang /*
971133c81f8SSen Wang * Unavailable in LLP, available in Normal & FFLP
972133c81f8SSen Wang */
973133c81f8SSen Wang SOC_SINGLE("Thermal Foldback Switch", TAS675X_DSP_CTRL_REG, 0, 1, 0),
974133c81f8SSen Wang SOC_SINGLE("PVDD Foldback Switch", TAS675X_DSP_CTRL_REG, 4, 1, 0),
975133c81f8SSen Wang SOC_SINGLE("DC Blocker Bypass Switch", TAS675X_DC_BLOCK_BYP_REG, 0, 1, 0),
976133c81f8SSen Wang SOC_SINGLE("Clip Detect Switch", TAS675X_CLIP_DETECT_CTRL_REG, 6, 1, 0),
977133c81f8SSen Wang SOC_SINGLE("Audio SDOUT Switch", TAS675X_DSP_CTRL_REG, 5, 1, 0),
978133c81f8SSen Wang
979133c81f8SSen Wang /*
980133c81f8SSen Wang * Unavailable in both FFLP and LLP, Normal mode only
981133c81f8SSen Wang */
982133c81f8SSen Wang /* Real-Time Load Diagnostics */
983133c81f8SSen Wang SOC_SINGLE("CH1 RTLDG Switch", TAS675X_RTLDG_EN_REG, 3, 1, 0),
984133c81f8SSen Wang SOC_SINGLE("CH2 RTLDG Switch", TAS675X_RTLDG_EN_REG, 2, 1, 0),
985133c81f8SSen Wang SOC_SINGLE("CH3 RTLDG Switch", TAS675X_RTLDG_EN_REG, 1, 1, 0),
986133c81f8SSen Wang SOC_SINGLE("CH4 RTLDG Switch", TAS675X_RTLDG_EN_REG, 0, 1, 0),
987133c81f8SSen Wang SOC_SINGLE("RTLDG Clip Mask Switch", TAS675X_RTLDG_EN_REG, 4, 1, 0),
988133c81f8SSen Wang SOC_SINGLE("ISENSE Calibration Switch", TAS675X_ISENSE_CAL_REG, 3, 1, 0),
989133c81f8SSen Wang SOC_DSP_THRESH_EXT("RTLDG Open Load Threshold",
990133c81f8SSen Wang tas675x_dsp_defaults[TAS675X_DSP_PARAM_ID_OL_THRESH]),
991133c81f8SSen Wang SOC_DSP_THRESH_EXT("RTLDG Short Load Threshold",
992133c81f8SSen Wang tas675x_dsp_defaults[TAS675X_DSP_PARAM_ID_SL_THRESH]),
993133c81f8SSen Wang SOC_RTLDG_IMP_RO("CH1 RTLDG Impedance", TAS675X_CH1_RTLDG_IMP_MSB_REG),
994133c81f8SSen Wang SOC_RTLDG_IMP_RO("CH2 RTLDG Impedance", TAS675X_CH2_RTLDG_IMP_MSB_REG),
995133c81f8SSen Wang SOC_RTLDG_IMP_RO("CH3 RTLDG Impedance", TAS675X_CH3_RTLDG_IMP_MSB_REG),
996133c81f8SSen Wang SOC_RTLDG_IMP_RO("CH4 RTLDG Impedance", TAS675X_CH4_RTLDG_IMP_MSB_REG),
997133c81f8SSen Wang };
998133c81f8SSen Wang
999133c81f8SSen Wang static const struct snd_kcontrol_new tas675x_audio_path_switch =
1000133c81f8SSen Wang SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 1);
1001133c81f8SSen Wang
1002133c81f8SSen Wang static const struct snd_kcontrol_new tas675x_anc_path_switch =
1003133c81f8SSen Wang SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 1);
1004133c81f8SSen Wang
1005133c81f8SSen Wang static const struct snd_soc_dapm_widget tas675x_dapm_widgets[] = {
1006133c81f8SSen Wang SND_SOC_DAPM_SUPPLY("Analog Core", SND_SOC_NOPM, 0, 0, NULL, 0),
1007133c81f8SSen Wang SND_SOC_DAPM_SUPPLY("SDOUT Vpredict", SND_SOC_NOPM, 0, 0, NULL, 0),
1008133c81f8SSen Wang SND_SOC_DAPM_SUPPLY("SDOUT Isense", SND_SOC_NOPM, 0, 0, NULL, 0),
1009133c81f8SSen Wang
1010133c81f8SSen Wang SND_SOC_DAPM_DAC("Audio DAC", "Playback", SND_SOC_NOPM, 0, 0),
1011133c81f8SSen Wang SND_SOC_DAPM_DAC("ANC DAC", "ANC Playback", SND_SOC_NOPM, 0, 0),
1012133c81f8SSen Wang SND_SOC_DAPM_ADC("Feedback ADC", "Feedback Capture", SND_SOC_NOPM, 0, 0),
1013133c81f8SSen Wang
1014133c81f8SSen Wang SND_SOC_DAPM_SWITCH("Audio Path", SND_SOC_NOPM, 0, 0,
1015133c81f8SSen Wang &tas675x_audio_path_switch),
1016133c81f8SSen Wang SND_SOC_DAPM_SWITCH("ANC Path", SND_SOC_NOPM, 0, 0,
1017133c81f8SSen Wang &tas675x_anc_path_switch),
1018133c81f8SSen Wang
1019133c81f8SSen Wang /*
1020133c81f8SSen Wang * Even though all channels are coupled in terms of power control,
1021133c81f8SSen Wang * use logical outputs for each channel to allow independent routing
1022133c81f8SSen Wang * and DAPM controls if needed.
1023133c81f8SSen Wang */
1024133c81f8SSen Wang SND_SOC_DAPM_OUTPUT("OUT_CH1"),
1025133c81f8SSen Wang SND_SOC_DAPM_OUTPUT("OUT_CH2"),
1026133c81f8SSen Wang SND_SOC_DAPM_OUTPUT("OUT_CH3"),
1027133c81f8SSen Wang SND_SOC_DAPM_OUTPUT("OUT_CH4"),
1028133c81f8SSen Wang SND_SOC_DAPM_INPUT("SPEAKER_LOAD"),
1029133c81f8SSen Wang };
1030133c81f8SSen Wang
1031133c81f8SSen Wang static const struct snd_soc_dapm_route tas675x_dapm_routes[] = {
1032133c81f8SSen Wang { "Audio DAC", NULL, "Analog Core" },
1033133c81f8SSen Wang { "Audio Path", "Switch", "Audio DAC" },
1034133c81f8SSen Wang { "OUT_CH1", NULL, "Audio Path" },
1035133c81f8SSen Wang { "OUT_CH2", NULL, "Audio Path" },
1036133c81f8SSen Wang { "OUT_CH3", NULL, "Audio Path" },
1037133c81f8SSen Wang { "OUT_CH4", NULL, "Audio Path" },
1038133c81f8SSen Wang
1039133c81f8SSen Wang { "ANC DAC", NULL, "Analog Core" },
1040133c81f8SSen Wang { "ANC Path", "Switch", "ANC DAC" },
1041133c81f8SSen Wang { "OUT_CH1", NULL, "ANC Path" },
1042133c81f8SSen Wang { "OUT_CH2", NULL, "ANC Path" },
1043133c81f8SSen Wang { "OUT_CH3", NULL, "ANC Path" },
1044133c81f8SSen Wang { "OUT_CH4", NULL, "ANC Path" },
1045133c81f8SSen Wang
1046133c81f8SSen Wang { "Feedback ADC", NULL, "Analog Core" },
1047133c81f8SSen Wang { "Feedback ADC", NULL, "SDOUT Vpredict" },
1048133c81f8SSen Wang { "Feedback ADC", NULL, "SDOUT Isense" },
1049133c81f8SSen Wang { "Feedback ADC", NULL, "SPEAKER_LOAD" },
1050133c81f8SSen Wang };
1051133c81f8SSen Wang
tas675x_program_slot_offsets(struct tas675x_priv * tas,int dai_id,int slot_width)1052133c81f8SSen Wang static void tas675x_program_slot_offsets(struct tas675x_priv *tas,
1053133c81f8SSen Wang int dai_id, int slot_width)
1054133c81f8SSen Wang {
1055133c81f8SSen Wang int offset = 0;
1056133c81f8SSen Wang
1057133c81f8SSen Wang switch (dai_id) {
1058133c81f8SSen Wang case 0:
1059133c81f8SSen Wang /* Standard Audio on SDIN */
1060133c81f8SSen Wang if (tas->audio_slot >= 0)
1061133c81f8SSen Wang offset = tas->audio_slot * slot_width;
1062133c81f8SSen Wang else if (tas->tx_mask)
1063133c81f8SSen Wang offset = __ffs(tas->tx_mask) * slot_width;
1064133c81f8SSen Wang else
1065133c81f8SSen Wang return;
1066133c81f8SSen Wang offset += tas->bclk_offset;
1067133c81f8SSen Wang regmap_update_bits(tas->regmap, TAS675X_SDIN_OFFSET_MSB_REG,
1068133c81f8SSen Wang TAS675X_SDIN_AUDIO_OFF_MSB_MASK,
1069133c81f8SSen Wang FIELD_PREP(TAS675X_SDIN_AUDIO_OFF_MSB_MASK, offset >> 8));
1070133c81f8SSen Wang regmap_write(tas->regmap, TAS675X_SDIN_AUDIO_OFFSET_REG,
1071133c81f8SSen Wang offset & 0xFF);
1072133c81f8SSen Wang break;
1073133c81f8SSen Wang case 1:
1074133c81f8SSen Wang /*
1075133c81f8SSen Wang * Low-Latency Playback on SDIN, **only** enabled in LLP mode
1076133c81f8SSen Wang * and to be mixed with main audio before output amplification
1077133c81f8SSen Wang * to achieve ANC/RNC.
1078133c81f8SSen Wang */
1079133c81f8SSen Wang if (tas->llp_slot >= 0)
1080133c81f8SSen Wang offset = tas->llp_slot * slot_width;
1081133c81f8SSen Wang else if (tas->tx_mask)
1082133c81f8SSen Wang offset = __ffs(tas->tx_mask) * slot_width;
1083133c81f8SSen Wang else
1084133c81f8SSen Wang return;
1085133c81f8SSen Wang offset += tas->bclk_offset;
1086133c81f8SSen Wang regmap_update_bits(tas->regmap, TAS675X_SDIN_OFFSET_MSB_REG,
1087133c81f8SSen Wang TAS675X_SDIN_LL_OFF_MSB_MASK,
1088133c81f8SSen Wang FIELD_PREP(TAS675X_SDIN_LL_OFF_MSB_MASK, offset >> 8));
1089133c81f8SSen Wang regmap_write(tas->regmap, TAS675X_SDIN_LL_OFFSET_REG,
1090133c81f8SSen Wang offset & 0xFF);
1091133c81f8SSen Wang break;
1092133c81f8SSen Wang case 2:
1093133c81f8SSen Wang /* SDOUT Data Output (Vpredict + Isense feedback) */
1094133c81f8SSen Wang if (!tas->slot_width)
1095133c81f8SSen Wang break;
1096133c81f8SSen Wang if (tas->vpredict_slot >= 0) {
1097133c81f8SSen Wang offset = tas->vpredict_slot * slot_width;
1098133c81f8SSen Wang offset += tas->bclk_offset;
1099133c81f8SSen Wang regmap_update_bits(tas->regmap, TAS675X_SDOUT_OFFSET_MSB_REG,
1100133c81f8SSen Wang TAS675X_SDOUT_VP_OFF_MSB_MASK,
1101133c81f8SSen Wang FIELD_PREP(TAS675X_SDOUT_VP_OFF_MSB_MASK, offset >> 8));
1102133c81f8SSen Wang regmap_write(tas->regmap, TAS675X_VPREDICT_OFFSET_REG,
1103133c81f8SSen Wang offset & 0xFF);
1104133c81f8SSen Wang }
1105133c81f8SSen Wang if (tas->isense_slot >= 0) {
1106133c81f8SSen Wang offset = tas->isense_slot * slot_width;
1107133c81f8SSen Wang offset += tas->bclk_offset;
1108133c81f8SSen Wang regmap_update_bits(tas->regmap, TAS675X_SDOUT_OFFSET_MSB_REG,
1109133c81f8SSen Wang TAS675X_SDOUT_IS_OFF_MSB_MASK,
1110133c81f8SSen Wang FIELD_PREP(TAS675X_SDOUT_IS_OFF_MSB_MASK, offset >> 8));
1111133c81f8SSen Wang regmap_write(tas->regmap, TAS675X_ISENSE_OFFSET_REG,
1112133c81f8SSen Wang offset & 0xFF);
1113133c81f8SSen Wang }
1114133c81f8SSen Wang break;
1115133c81f8SSen Wang }
1116133c81f8SSen Wang
1117133c81f8SSen Wang if (offset > 511)
1118133c81f8SSen Wang dev_warn(tas->dev,
1119133c81f8SSen Wang "DAI %d slot offset %d exceeds 511 SCLK limit\n",
1120133c81f8SSen Wang dai_id, offset);
1121133c81f8SSen Wang }
1122133c81f8SSen Wang
tas675x_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)1123133c81f8SSen Wang static int tas675x_hw_params(struct snd_pcm_substream *substream,
1124133c81f8SSen Wang struct snd_pcm_hw_params *params,
1125133c81f8SSen Wang struct snd_soc_dai *dai)
1126133c81f8SSen Wang {
1127133c81f8SSen Wang struct snd_soc_component *component = dai->component;
1128133c81f8SSen Wang struct tas675x_priv *tas = snd_soc_component_get_drvdata(component);
1129133c81f8SSen Wang unsigned int rate = params_rate(params);
1130133c81f8SSen Wang u8 word_length;
1131133c81f8SSen Wang
1132133c81f8SSen Wang /*
1133133c81f8SSen Wang * Single clock domain: SDIN and SDOUT share one SCLK/FSYNC pair,
1134133c81f8SSen Wang * so all active DAIs must use the same sample rate.
1135133c81f8SSen Wang */
113612272cb1SSen Wang if ((READ_ONCE(tas->active_playback_dais) || READ_ONCE(tas->active_capture_dais)) &&
1137133c81f8SSen Wang tas->rate && tas->rate != rate) {
1138133c81f8SSen Wang dev_err(component->dev,
1139133c81f8SSen Wang "Rate %u conflicts with active rate %u\n",
1140133c81f8SSen Wang rate, tas->rate);
1141133c81f8SSen Wang return -EINVAL;
1142133c81f8SSen Wang }
1143133c81f8SSen Wang
1144133c81f8SSen Wang switch (params_width(params)) {
1145133c81f8SSen Wang case 16:
1146133c81f8SSen Wang word_length = TAS675X_WL_16BIT;
1147133c81f8SSen Wang break;
1148133c81f8SSen Wang case 20:
1149133c81f8SSen Wang word_length = TAS675X_WL_20BIT;
1150133c81f8SSen Wang break;
1151133c81f8SSen Wang case 24:
1152133c81f8SSen Wang word_length = TAS675X_WL_24BIT;
1153133c81f8SSen Wang break;
1154133c81f8SSen Wang case 32:
1155133c81f8SSen Wang word_length = TAS675X_WL_32BIT;
1156133c81f8SSen Wang break;
1157133c81f8SSen Wang default:
1158133c81f8SSen Wang return -EINVAL;
1159133c81f8SSen Wang }
1160133c81f8SSen Wang
1161133c81f8SSen Wang if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
1162133c81f8SSen Wang /*
1163133c81f8SSen Wang * RTLDG is not supported above 96kHz. Auto-disable to
1164133c81f8SSen Wang * prevent DSP overload and restore when rate drops back.
1165133c81f8SSen Wang */
1166133c81f8SSen Wang if (rate > 96000) {
1167133c81f8SSen Wang unsigned int val;
1168133c81f8SSen Wang
1169133c81f8SSen Wang regmap_read(component->regmap, TAS675X_RTLDG_EN_REG,
1170133c81f8SSen Wang &val);
1171133c81f8SSen Wang if (val & TAS675X_RTLDG_CH_EN_MASK) {
1172133c81f8SSen Wang tas->saved_rtldg_en = val;
1173133c81f8SSen Wang dev_dbg(component->dev,
1174133c81f8SSen Wang "Sample rate %dHz > 96kHz: Auto-disabling RTLDG\n",
1175133c81f8SSen Wang rate);
1176133c81f8SSen Wang regmap_update_bits(component->regmap,
1177133c81f8SSen Wang TAS675X_RTLDG_EN_REG,
1178133c81f8SSen Wang TAS675X_RTLDG_CH_EN_MASK,
1179133c81f8SSen Wang 0x00);
1180133c81f8SSen Wang }
1181133c81f8SSen Wang } else if (tas->saved_rtldg_en) {
1182133c81f8SSen Wang unsigned int cur;
1183133c81f8SSen Wang
1184133c81f8SSen Wang /*
1185133c81f8SSen Wang * Respect overrides and only restore if RTLDG is still auto-disabled
1186133c81f8SSen Wang */
1187133c81f8SSen Wang regmap_read(component->regmap, TAS675X_RTLDG_EN_REG,
1188133c81f8SSen Wang &cur);
1189133c81f8SSen Wang if (!(cur & TAS675X_RTLDG_CH_EN_MASK)) {
1190133c81f8SSen Wang dev_dbg(component->dev,
1191133c81f8SSen Wang "Restoring RTLDG config after high-rate stream\n");
1192133c81f8SSen Wang regmap_update_bits(component->regmap,
1193133c81f8SSen Wang TAS675X_RTLDG_EN_REG,
1194133c81f8SSen Wang TAS675X_RTLDG_CH_EN_MASK,
1195133c81f8SSen Wang TAS675X_RTLDG_CH_EN_MASK &
1196133c81f8SSen Wang tas->saved_rtldg_en);
1197133c81f8SSen Wang }
1198133c81f8SSen Wang tas->saved_rtldg_en = 0;
1199133c81f8SSen Wang }
1200133c81f8SSen Wang
1201133c81f8SSen Wang /* Set SDIN word length (audio path + low-latency path) */
1202133c81f8SSen Wang regmap_update_bits(component->regmap, TAS675X_SDIN_CTRL_REG,
1203133c81f8SSen Wang TAS675X_SDIN_WL_MASK,
1204133c81f8SSen Wang FIELD_PREP(TAS675X_SDIN_AUDIO_WL_MASK, word_length) |
1205133c81f8SSen Wang FIELD_PREP(TAS675X_SDIN_LL_WL_MASK, word_length));
1206133c81f8SSen Wang } else {
1207133c81f8SSen Wang /* Set SDOUT word length (VPREDICT + ISENSE) for capture */
1208133c81f8SSen Wang regmap_update_bits(component->regmap, TAS675X_SDOUT_CTRL_REG,
1209133c81f8SSen Wang TAS675X_SDOUT_WL_MASK,
1210133c81f8SSen Wang FIELD_PREP(TAS675X_SDOUT_VP_WL_MASK, word_length) |
1211133c81f8SSen Wang FIELD_PREP(TAS675X_SDOUT_IS_WL_MASK, word_length));
1212133c81f8SSen Wang }
1213133c81f8SSen Wang
1214133c81f8SSen Wang tas675x_program_slot_offsets(tas, dai->id,
1215133c81f8SSen Wang tas->slot_width ?: params_width(params));
1216133c81f8SSen Wang
1217133c81f8SSen Wang tas->rate = rate;
1218133c81f8SSen Wang
1219133c81f8SSen Wang return 0;
1220133c81f8SSen Wang }
1221133c81f8SSen Wang
tas675x_set_fmt(struct snd_soc_dai * dai,unsigned int fmt)1222133c81f8SSen Wang static int tas675x_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
1223133c81f8SSen Wang {
1224133c81f8SSen Wang struct snd_soc_component *component = dai->component;
1225133c81f8SSen Wang struct tas675x_priv *tas = snd_soc_component_get_drvdata(component);
1226133c81f8SSen Wang bool tdm_mode = false, i2s_mode = false;
1227133c81f8SSen Wang
1228133c81f8SSen Wang /* Enforce Clocking Direction (Codec is strictly a consumer) */
1229133c81f8SSen Wang switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
1230133c81f8SSen Wang case SND_SOC_DAIFMT_BC_FC:
1231133c81f8SSen Wang break;
1232133c81f8SSen Wang default:
1233133c81f8SSen Wang dev_err(component->dev, "Unsupported clock provider format\n");
1234133c81f8SSen Wang return -EINVAL;
1235133c81f8SSen Wang }
1236133c81f8SSen Wang
1237133c81f8SSen Wang /* SCLK polarity: NB_NF or IB_NF only (no FSYNC inversion support) */
1238133c81f8SSen Wang switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
1239133c81f8SSen Wang case SND_SOC_DAIFMT_NB_NF:
1240133c81f8SSen Wang regmap_update_bits(component->regmap, TAS675X_SCLK_INV_CTRL_REG,
1241133c81f8SSen Wang TAS675X_SCLK_INV_MASK, 0x00);
1242133c81f8SSen Wang break;
1243133c81f8SSen Wang case SND_SOC_DAIFMT_IB_NF:
1244133c81f8SSen Wang regmap_update_bits(component->regmap, TAS675X_SCLK_INV_CTRL_REG,
1245133c81f8SSen Wang TAS675X_SCLK_INV_MASK, TAS675X_SCLK_INV_MASK);
1246133c81f8SSen Wang break;
1247133c81f8SSen Wang default:
1248133c81f8SSen Wang dev_err(component->dev, "Unsupported clock inversion\n");
1249133c81f8SSen Wang return -EINVAL;
1250133c81f8SSen Wang }
1251133c81f8SSen Wang
1252133c81f8SSen Wang /* Configure Audio Format and TDM Enable */
1253133c81f8SSen Wang switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
1254133c81f8SSen Wang case SND_SOC_DAIFMT_I2S:
1255133c81f8SSen Wang i2s_mode = true;
1256133c81f8SSen Wang tas->bclk_offset = 0;
1257133c81f8SSen Wang regmap_update_bits(component->regmap, TAS675X_AUDIO_IF_CTRL_REG,
1258133c81f8SSen Wang TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_MASK |
1259133c81f8SSen Wang TAS675X_FS_PULSE_MASK,
1260133c81f8SSen Wang TAS675X_SAP_FMT_I2S);
1261133c81f8SSen Wang regmap_update_bits(component->regmap, TAS675X_SDOUT_CTRL_REG,
1262133c81f8SSen Wang TAS675X_SDOUT_SELECT_MASK,
1263133c81f8SSen Wang TAS675X_SDOUT_SELECT_NON_TDM);
1264133c81f8SSen Wang break;
1265133c81f8SSen Wang case SND_SOC_DAIFMT_RIGHT_J:
1266133c81f8SSen Wang tas->bclk_offset = 0;
1267133c81f8SSen Wang regmap_update_bits(component->regmap, TAS675X_AUDIO_IF_CTRL_REG,
1268133c81f8SSen Wang TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_MASK |
1269133c81f8SSen Wang TAS675X_FS_PULSE_MASK,
1270133c81f8SSen Wang TAS675X_SAP_FMT_RIGHT_J);
1271133c81f8SSen Wang regmap_update_bits(component->regmap, TAS675X_SDOUT_CTRL_REG,
1272133c81f8SSen Wang TAS675X_SDOUT_SELECT_MASK,
1273133c81f8SSen Wang TAS675X_SDOUT_SELECT_NON_TDM);
1274133c81f8SSen Wang break;
1275133c81f8SSen Wang case SND_SOC_DAIFMT_LEFT_J:
1276133c81f8SSen Wang tas->bclk_offset = 0;
1277133c81f8SSen Wang regmap_update_bits(component->regmap, TAS675X_AUDIO_IF_CTRL_REG,
1278133c81f8SSen Wang TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_MASK |
1279133c81f8SSen Wang TAS675X_FS_PULSE_MASK,
1280133c81f8SSen Wang TAS675X_SAP_FMT_LEFT_J);
1281133c81f8SSen Wang regmap_update_bits(component->regmap, TAS675X_SDOUT_CTRL_REG,
1282133c81f8SSen Wang TAS675X_SDOUT_SELECT_MASK,
1283133c81f8SSen Wang TAS675X_SDOUT_SELECT_NON_TDM);
1284133c81f8SSen Wang break;
1285133c81f8SSen Wang case SND_SOC_DAIFMT_DSP_A:
1286133c81f8SSen Wang tdm_mode = true;
1287133c81f8SSen Wang tas->bclk_offset = 1;
1288133c81f8SSen Wang regmap_update_bits(component->regmap, TAS675X_AUDIO_IF_CTRL_REG,
1289133c81f8SSen Wang TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_MASK |
1290133c81f8SSen Wang TAS675X_FS_PULSE_MASK,
1291133c81f8SSen Wang TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_TDM |
1292133c81f8SSen Wang TAS675X_FS_PULSE_SHORT);
1293133c81f8SSen Wang regmap_update_bits(component->regmap, TAS675X_SDOUT_CTRL_REG,
1294133c81f8SSen Wang TAS675X_SDOUT_SELECT_MASK,
1295133c81f8SSen Wang TAS675X_SDOUT_SELECT_TDM_SDOUT1);
1296133c81f8SSen Wang break;
1297133c81f8SSen Wang case SND_SOC_DAIFMT_DSP_B:
1298133c81f8SSen Wang tdm_mode = true;
1299133c81f8SSen Wang tas->bclk_offset = 0;
1300133c81f8SSen Wang regmap_update_bits(component->regmap, TAS675X_AUDIO_IF_CTRL_REG,
1301133c81f8SSen Wang TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_MASK |
1302133c81f8SSen Wang TAS675X_FS_PULSE_MASK,
1303133c81f8SSen Wang TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_TDM |
1304133c81f8SSen Wang TAS675X_FS_PULSE_SHORT);
1305133c81f8SSen Wang regmap_update_bits(component->regmap, TAS675X_SDOUT_CTRL_REG,
1306133c81f8SSen Wang TAS675X_SDOUT_SELECT_MASK,
1307133c81f8SSen Wang TAS675X_SDOUT_SELECT_TDM_SDOUT1);
1308133c81f8SSen Wang break;
1309133c81f8SSen Wang default:
1310133c81f8SSen Wang dev_err(component->dev, "Unsupported DAI format\n");
1311133c81f8SSen Wang return -EINVAL;
1312133c81f8SSen Wang }
1313133c81f8SSen Wang
1314133c81f8SSen Wang /* Setup Vpredict and Isense outputs */
1315133c81f8SSen Wang if (dai->id == 2) {
1316133c81f8SSen Wang unsigned int sdout_en = 0;
1317133c81f8SSen Wang
1318133c81f8SSen Wang if (tdm_mode) {
1319133c81f8SSen Wang /* TDM: Vpredict and Isense may coexist on separate slots */
1320133c81f8SSen Wang if (tas->vpredict_slot >= 0)
1321133c81f8SSen Wang sdout_en |= TAS675X_SDOUT_EN_VPREDICT;
1322133c81f8SSen Wang if (tas->isense_slot >= 0)
1323133c81f8SSen Wang sdout_en |= TAS675X_SDOUT_EN_ISENSE;
1324133c81f8SSen Wang regmap_update_bits(component->regmap,
1325133c81f8SSen Wang TAS675X_SDOUT_EN_REG,
1326133c81f8SSen Wang TAS675X_SDOUT_EN_VPREDICT |
1327133c81f8SSen Wang TAS675X_SDOUT_EN_ISENSE,
1328133c81f8SSen Wang sdout_en);
1329133c81f8SSen Wang if (tas->vpredict_slot >= 0 && tas->isense_slot >= 0 &&
1330133c81f8SSen Wang abs(tas->vpredict_slot - tas->isense_slot) < 4)
1331133c81f8SSen Wang dev_warn(component->dev,
1332133c81f8SSen Wang "ti,vpredict-slot-no and ti,isense-slot-no overlaps (each occupies 4 consecutive slots)\n");
1333133c81f8SSen Wang } else if (i2s_mode) {
1334133c81f8SSen Wang /* I2S: only one source at a time; Vpredict takes priority */
1335133c81f8SSen Wang if (tas->vpredict_slot >= 0)
1336133c81f8SSen Wang sdout_en = TAS675X_SDOUT_NON_TDM_SEL_VPREDICT |
1337133c81f8SSen Wang TAS675X_SDOUT_EN_NON_TDM_ALL;
1338133c81f8SSen Wang else if (tas->isense_slot >= 0)
1339133c81f8SSen Wang sdout_en = TAS675X_SDOUT_NON_TDM_SEL_ISENSE |
1340133c81f8SSen Wang TAS675X_SDOUT_EN_NON_TDM_ALL;
1341133c81f8SSen Wang regmap_update_bits(component->regmap,
1342133c81f8SSen Wang TAS675X_SDOUT_EN_REG,
1343133c81f8SSen Wang TAS675X_SDOUT_NON_TDM_SEL_MASK |
1344133c81f8SSen Wang TAS675X_SDOUT_EN_NON_TDM_ALL,
1345133c81f8SSen Wang sdout_en);
1346133c81f8SSen Wang if (sdout_en &&
1347133c81f8SSen Wang tas->gpio1_func != TAS675X_GPIO_SEL_SDOUT2 &&
1348133c81f8SSen Wang tas->gpio2_func != TAS675X_GPIO_SEL_SDOUT2)
1349133c81f8SSen Wang dev_warn(component->dev,
1350133c81f8SSen Wang "sdout enabled in I2S mode but no GPIO configured as SDOUT2; Ch3/Ch4 will be absent\n");
1351133c81f8SSen Wang }
1352133c81f8SSen Wang }
1353133c81f8SSen Wang
1354133c81f8SSen Wang return 0;
1355133c81f8SSen Wang }
1356133c81f8SSen Wang
tas675x_set_tdm_slot(struct snd_soc_dai * dai,unsigned int tx_mask,unsigned int rx_mask,int slots,int slot_width)1357133c81f8SSen Wang static int tas675x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
1358133c81f8SSen Wang unsigned int rx_mask, int slots, int slot_width)
1359133c81f8SSen Wang {
1360133c81f8SSen Wang struct tas675x_priv *tas = snd_soc_component_get_drvdata(dai->component);
1361133c81f8SSen Wang
1362133c81f8SSen Wang if (slots == 0) {
1363133c81f8SSen Wang tas->slot_width = 0;
1364133c81f8SSen Wang tas->tx_mask = 0;
1365133c81f8SSen Wang return 0;
1366133c81f8SSen Wang }
1367133c81f8SSen Wang
1368133c81f8SSen Wang /* No rx_mask as hardware does not support channel muxing for capture */
1369133c81f8SSen Wang tas->slot_width = slot_width;
1370133c81f8SSen Wang tas->tx_mask = tx_mask;
1371133c81f8SSen Wang return 0;
1372133c81f8SSen Wang }
1373133c81f8SSen Wang
tas675x_mute_stream(struct snd_soc_dai * dai,int mute,int direction)1374133c81f8SSen Wang static int tas675x_mute_stream(struct snd_soc_dai *dai, int mute, int direction)
1375133c81f8SSen Wang {
1376133c81f8SSen Wang struct snd_soc_component *component = dai->component;
1377133c81f8SSen Wang struct tas675x_priv *tas = snd_soc_component_get_drvdata(component);
1378133c81f8SSen Wang unsigned int discard;
1379133c81f8SSen Wang int ret;
1380133c81f8SSen Wang
1381133c81f8SSen Wang if (direction == SNDRV_PCM_STREAM_CAPTURE) {
1382133c81f8SSen Wang if (mute)
1383133c81f8SSen Wang clear_bit(dai->id, &tas->active_capture_dais);
1384133c81f8SSen Wang else
1385133c81f8SSen Wang set_bit(dai->id, &tas->active_capture_dais);
1386133c81f8SSen Wang return 0;
1387133c81f8SSen Wang }
1388133c81f8SSen Wang
1389133c81f8SSen Wang /*
1390133c81f8SSen Wang * Track which playback DAIs are active.
1391133c81f8SSen Wang * The TAS675x has two playback DAIs (main audio and LLP).
1392133c81f8SSen Wang * Only transition to SLEEP when ALL are muted.
1393133c81f8SSen Wang */
1394133c81f8SSen Wang if (mute)
1395133c81f8SSen Wang clear_bit(dai->id, &tas->active_playback_dais);
1396133c81f8SSen Wang else
1397133c81f8SSen Wang set_bit(dai->id, &tas->active_playback_dais);
1398133c81f8SSen Wang
1399133c81f8SSen Wang /* Last playback stream */
140012272cb1SSen Wang if (mute && !READ_ONCE(tas->active_playback_dais)) {
1401133c81f8SSen Wang ret = tas675x_set_state_all(tas, TAS675X_STATE_SLEEP_BOTH);
1402133c81f8SSen Wang regmap_read(tas->regmap, TAS675X_CLK_FAULT_LATCHED_REG, &discard);
1403133c81f8SSen Wang return ret;
1404133c81f8SSen Wang }
1405133c81f8SSen Wang
1406133c81f8SSen Wang return tas675x_set_state_all(tas,
140712272cb1SSen Wang READ_ONCE(tas->active_playback_dais) ?
1408133c81f8SSen Wang TAS675X_STATE_PLAY_BOTH :
1409133c81f8SSen Wang TAS675X_STATE_SLEEP_BOTH);
1410133c81f8SSen Wang }
1411133c81f8SSen Wang
1412133c81f8SSen Wang static const struct snd_soc_dai_ops tas675x_dai_ops = {
1413133c81f8SSen Wang .hw_params = tas675x_hw_params,
1414133c81f8SSen Wang .set_fmt = tas675x_set_fmt,
1415133c81f8SSen Wang .set_tdm_slot = tas675x_set_tdm_slot,
1416133c81f8SSen Wang .mute_stream = tas675x_mute_stream,
1417133c81f8SSen Wang };
1418133c81f8SSen Wang
1419133c81f8SSen Wang static struct snd_soc_dai_driver tas675x_dais[] = {
1420133c81f8SSen Wang {
1421133c81f8SSen Wang .name = "tas675x-audio",
1422133c81f8SSen Wang .id = 0,
1423133c81f8SSen Wang .playback = {
1424133c81f8SSen Wang .stream_name = "Playback",
1425133c81f8SSen Wang .channels_min = 2,
1426133c81f8SSen Wang .channels_max = 4,
1427133c81f8SSen Wang .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
1428133c81f8SSen Wang SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000,
1429133c81f8SSen Wang .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_LE |
1430133c81f8SSen Wang SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
1431133c81f8SSen Wang },
1432133c81f8SSen Wang .ops = &tas675x_dai_ops,
1433133c81f8SSen Wang },
1434133c81f8SSen Wang /* Only available when Low Latency Path (LLP) is enabled */
1435133c81f8SSen Wang {
1436133c81f8SSen Wang .name = "tas675x-anc",
1437133c81f8SSen Wang .id = 1,
1438133c81f8SSen Wang .playback = {
1439133c81f8SSen Wang .stream_name = "ANC Playback",
1440133c81f8SSen Wang .channels_min = 2,
1441133c81f8SSen Wang .channels_max = 4,
1442133c81f8SSen Wang .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000,
1443133c81f8SSen Wang .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_LE |
1444133c81f8SSen Wang SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
1445133c81f8SSen Wang },
1446133c81f8SSen Wang .ops = &tas675x_dai_ops,
1447133c81f8SSen Wang },
1448133c81f8SSen Wang {
1449133c81f8SSen Wang .name = "tas675x-feedback",
1450133c81f8SSen Wang .id = 2,
1451133c81f8SSen Wang .capture = {
1452133c81f8SSen Wang .stream_name = "Feedback Capture",
1453133c81f8SSen Wang .channels_min = 2,
1454133c81f8SSen Wang .channels_max = 8,
1455133c81f8SSen Wang .rates = SNDRV_PCM_RATE_48000,
1456133c81f8SSen Wang .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_LE |
1457133c81f8SSen Wang SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
1458133c81f8SSen Wang },
1459133c81f8SSen Wang .ops = &tas675x_dai_ops,
1460133c81f8SSen Wang }
1461133c81f8SSen Wang };
1462133c81f8SSen Wang
1463133c81f8SSen Wang /*
1464133c81f8SSen Wang * Enable regulators and release hardware reset GPIOs.
1465133c81f8SSen Wang * The device is not I2C-accessible until this returns.
1466133c81f8SSen Wang */
tas675x_hw_enable(struct tas675x_priv * tas)1467133c81f8SSen Wang static int tas675x_hw_enable(struct tas675x_priv *tas)
1468133c81f8SSen Wang {
1469133c81f8SSen Wang int ret;
1470133c81f8SSen Wang
1471133c81f8SSen Wang ret = regulator_bulk_enable(ARRAY_SIZE(tas->supplies), tas->supplies);
1472133c81f8SSen Wang if (ret) {
1473133c81f8SSen Wang dev_err(tas->dev, "Failed to enable regulators: %d\n", ret);
1474133c81f8SSen Wang return ret;
1475133c81f8SSen Wang }
1476133c81f8SSen Wang
1477133c81f8SSen Wang if (!IS_ERR(tas->vbat)) {
1478133c81f8SSen Wang ret = regulator_enable(tas->vbat);
1479133c81f8SSen Wang if (ret) {
1480133c81f8SSen Wang dev_err(tas->dev, "Failed to enable vbat: %d\n", ret);
1481133c81f8SSen Wang regulator_bulk_disable(ARRAY_SIZE(tas->supplies), tas->supplies);
1482133c81f8SSen Wang return ret;
1483133c81f8SSen Wang }
1484133c81f8SSen Wang }
1485133c81f8SSen Wang
1486133c81f8SSen Wang if (tas->pd_gpio && tas->stby_gpio) {
1487133c81f8SSen Wang /*
1488133c81f8SSen Wang * Independent Pin Control
1489133c81f8SSen Wang * Deassert PD first to boot digital, then STBY for analog.
1490133c81f8SSen Wang */
1491133c81f8SSen Wang /* Min 4ms digital boot wait */
1492133c81f8SSen Wang gpiod_set_value_cansleep(tas->pd_gpio, 0);
1493133c81f8SSen Wang usleep_range(4000, 5000);
1494133c81f8SSen Wang
1495133c81f8SSen Wang /* ~2ms analog stabilization */
1496133c81f8SSen Wang gpiod_set_value_cansleep(tas->stby_gpio, 0);
1497133c81f8SSen Wang usleep_range(2000, 3000);
1498133c81f8SSen Wang } else if (tas->pd_gpio) {
1499133c81f8SSen Wang /*
1500133c81f8SSen Wang * Simultaneous Pin Release
1501133c81f8SSen Wang * STBY tied to PD or hardwired HIGH.
1502133c81f8SSen Wang */
1503133c81f8SSen Wang /* 6ms wait for simultaneous release transition */
1504133c81f8SSen Wang gpiod_set_value_cansleep(tas->pd_gpio, 0);
1505133c81f8SSen Wang usleep_range(6000, 7000);
1506133c81f8SSen Wang } else {
1507133c81f8SSen Wang /*
1508133c81f8SSen Wang * PD hardwired, device in DEEP_SLEEP.
1509133c81f8SSen Wang * Digital core already booted, I2C active. Deassert STBY
1510133c81f8SSen Wang * to bring up the analog output stage.
1511133c81f8SSen Wang */
1512133c81f8SSen Wang /* ~2ms analog stabilization */
1513133c81f8SSen Wang gpiod_set_value_cansleep(tas->stby_gpio, 0);
1514133c81f8SSen Wang usleep_range(2000, 3000);
1515133c81f8SSen Wang }
1516133c81f8SSen Wang
1517133c81f8SSen Wang return 0;
1518133c81f8SSen Wang }
1519133c81f8SSen Wang
tas675x_hw_disable(struct tas675x_priv * tas)1520133c81f8SSen Wang static void tas675x_hw_disable(struct tas675x_priv *tas)
1521133c81f8SSen Wang {
1522133c81f8SSen Wang if (tas->stby_gpio)
1523133c81f8SSen Wang gpiod_set_value_cansleep(tas->stby_gpio, 1);
1524133c81f8SSen Wang
1525133c81f8SSen Wang if (tas->pd_gpio)
1526133c81f8SSen Wang gpiod_set_value_cansleep(tas->pd_gpio, 1);
1527133c81f8SSen Wang
1528133c81f8SSen Wang /*
1529133c81f8SSen Wang * Hold PD/STBY asserted for at least 10ms
1530133c81f8SSen Wang * before removing PVDD, VBAT or DVDD.
1531133c81f8SSen Wang */
1532133c81f8SSen Wang usleep_range(10000, 11000);
1533133c81f8SSen Wang
1534133c81f8SSen Wang if (!IS_ERR(tas->vbat))
1535133c81f8SSen Wang regulator_disable(tas->vbat);
1536133c81f8SSen Wang
1537133c81f8SSen Wang regulator_bulk_disable(ARRAY_SIZE(tas->supplies), tas->supplies);
1538133c81f8SSen Wang }
1539133c81f8SSen Wang
1540133c81f8SSen Wang /*
1541133c81f8SSen Wang * Write device start-up defaults.
1542133c81f8SSen Wang * Must be called after tas675x_hw_enable() and after regcache is enabled.
1543133c81f8SSen Wang */
tas675x_init_device(struct tas675x_priv * tas)1544133c81f8SSen Wang static int tas675x_init_device(struct tas675x_priv *tas)
1545133c81f8SSen Wang {
1546133c81f8SSen Wang struct regmap *regmap = tas->regmap;
1547133c81f8SSen Wang unsigned int val;
1548133c81f8SSen Wang int ret, i;
1549133c81f8SSen Wang
1550133c81f8SSen Wang /* Clear POR fault flag to prevent IRQ storm */
1551133c81f8SSen Wang regmap_read(regmap, TAS675X_POWER_FAULT_LATCHED_REG, &val);
1552133c81f8SSen Wang
1553133c81f8SSen Wang /* Bypass DC Load Diagnostics for fast boot */
1554133c81f8SSen Wang if (tas->fast_boot)
1555133c81f8SSen Wang regmap_update_bits(regmap, TAS675X_DC_LDG_CTRL_REG,
1556133c81f8SSen Wang TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT,
1557133c81f8SSen Wang TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT);
1558133c81f8SSen Wang
1559133c81f8SSen Wang tas675x_select_book(regmap, TAS675X_BOOK_DEFAULT);
1560133c81f8SSen Wang
1561133c81f8SSen Wang /* Enter setup mode */
1562133c81f8SSen Wang ret = regmap_write(regmap, TAS675X_SETUP_REG1, TAS675X_SETUP_ENTER_VAL1);
1563133c81f8SSen Wang if (ret)
1564133c81f8SSen Wang goto err;
1565133c81f8SSen Wang ret = regmap_write(regmap, TAS675X_SETUP_REG2, TAS675X_SETUP_ENTER_VAL2);
1566133c81f8SSen Wang if (ret)
1567133c81f8SSen Wang goto err;
1568133c81f8SSen Wang
1569133c81f8SSen Wang /* Set all channels to Sleep (required before Page 1 config) */
1570133c81f8SSen Wang tas675x_set_state_all(tas, TAS675X_STATE_SLEEP_BOTH);
1571133c81f8SSen Wang
1572133c81f8SSen Wang /* Set DAC clock per TRM startup script */
1573133c81f8SSen Wang regmap_write(regmap, TAS675X_DAC_CLK_REG, 0x00);
1574133c81f8SSen Wang
1575133c81f8SSen Wang /*
1576133c81f8SSen Wang * Switch to Page 1 for safety-critical OC/CBC configuration,
1577133c81f8SSen Wang * while bypassing regcache. (Page 1 not accessible post setup)
1578133c81f8SSen Wang */
1579133c81f8SSen Wang regcache_cache_bypass(regmap, true);
1580133c81f8SSen Wang ret = regmap_multi_reg_write(regmap, tas675x_page1_init,
1581133c81f8SSen Wang ARRAY_SIZE(tas675x_page1_init));
1582133c81f8SSen Wang regcache_cache_bypass(regmap, false);
1583133c81f8SSen Wang if (ret)
1584133c81f8SSen Wang goto err_setup;
1585133c81f8SSen Wang
1586133c81f8SSen Wang /* Resync regmap's cached page selector */
1587133c81f8SSen Wang regmap_write(regmap, TAS675X_PAGE_CTRL_REG, 0x00);
1588133c81f8SSen Wang
1589133c81f8SSen Wang /* Exit setup mode */
1590133c81f8SSen Wang regmap_write(regmap, TAS675X_SETUP_REG1, TAS675X_SETUP_EXIT_VAL);
1591133c81f8SSen Wang regmap_write(regmap, TAS675X_SETUP_REG2, TAS675X_SETUP_EXIT_VAL);
1592133c81f8SSen Wang
1593133c81f8SSen Wang /* Write DSP parameters if cached */
1594133c81f8SSen Wang for (i = 0; i < ARRAY_SIZE(tas->dsp_params); i++) {
1595133c81f8SSen Wang if (tas->dsp_params[i].val)
1596133c81f8SSen Wang tas675x_dsp_mem_write(tas,
1597133c81f8SSen Wang tas->dsp_params[i].page,
1598133c81f8SSen Wang tas->dsp_params[i].reg,
1599133c81f8SSen Wang tas->dsp_params[i].val);
1600133c81f8SSen Wang }
1601133c81f8SSen Wang
1602133c81f8SSen Wang /*
1603133c81f8SSen Wang * Configure fault and warning event routing:
1604133c81f8SSen Wang *
1605133c81f8SSen Wang * ROUTING_1: CP fault/UVLO latch, OUTM soft short latch
1606133c81f8SSen Wang * ROUTING_2: CBC latch, OTSD latch, OTSD, power fault
1607133c81f8SSen Wang * ROUTING_3: CBC latch, OTSD latch, power latch, DC LDG,
1608133c81f8SSen Wang * OTSD, power warnings
1609133c81f8SSen Wang * ROUTING_4: OC latch, DC latch, protection shutdown
1610133c81f8SSen Wang * OTW latch, OTW, clip latch
1611133c81f8SSen Wang * ROUTING_5: clock latch+non-latch, RTLDG latch
1612133c81f8SSen Wang * CBC warning, clip warning
1613133c81f8SSen Wang */
1614133c81f8SSen Wang regmap_write(regmap, TAS675X_REPORT_ROUTING_1_REG, 0x70);
1615133c81f8SSen Wang regmap_write(regmap, TAS675X_REPORT_ROUTING_2_REG, 0xA3);
1616133c81f8SSen Wang regmap_write(regmap, TAS675X_REPORT_ROUTING_3_REG, 0xBB);
1617133c81f8SSen Wang regmap_write(regmap, TAS675X_REPORT_ROUTING_4_REG, 0x7E);
1618133c81f8SSen Wang regmap_write(regmap, TAS675X_REPORT_ROUTING_5_REG, 0xF3);
1619133c81f8SSen Wang
1620133c81f8SSen Wang /* Configure GPIO pins if specified in DT */
1621133c81f8SSen Wang if (tas->gpio1_func >= 0 || tas->gpio2_func >= 0) {
1622133c81f8SSen Wang unsigned int gpio_ctrl = TAS675X_GPIO_CTRL_RSTVAL;
1623133c81f8SSen Wang
1624133c81f8SSen Wang tas675x_config_gpio_pin(regmap, tas->gpio1_func,
1625133c81f8SSen Wang TAS675X_GPIO1_OUTPUT_SEL_REG,
1626133c81f8SSen Wang 0, &gpio_ctrl);
1627133c81f8SSen Wang tas675x_config_gpio_pin(regmap, tas->gpio2_func,
1628133c81f8SSen Wang TAS675X_GPIO2_OUTPUT_SEL_REG,
1629133c81f8SSen Wang 1, &gpio_ctrl);
1630133c81f8SSen Wang regmap_write(regmap, TAS675X_GPIO_CTRL_REG, gpio_ctrl);
1631133c81f8SSen Wang }
1632133c81f8SSen Wang
1633133c81f8SSen Wang /* Clear fast boot bits */
1634133c81f8SSen Wang if (tas->fast_boot)
1635133c81f8SSen Wang regmap_update_bits(regmap, TAS675X_DC_LDG_CTRL_REG,
1636133c81f8SSen Wang TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT,
1637133c81f8SSen Wang 0);
1638133c81f8SSen Wang
1639133c81f8SSen Wang /* Clear any stale faults from the boot sequence */
1640133c81f8SSen Wang regmap_read(regmap, TAS675X_POWER_FAULT_STATUS_1_REG, &val);
1641133c81f8SSen Wang regmap_read(regmap, TAS675X_POWER_FAULT_LATCHED_REG, &val);
1642133c81f8SSen Wang regmap_read(regmap, TAS675X_CLK_FAULT_LATCHED_REG, &val);
1643133c81f8SSen Wang regmap_write(regmap, TAS675X_RESET_REG, TAS675X_FAULT_CLEAR);
1644133c81f8SSen Wang
1645133c81f8SSen Wang return 0;
1646133c81f8SSen Wang
1647133c81f8SSen Wang err_setup:
1648133c81f8SSen Wang regmap_write(regmap, TAS675X_SETUP_REG1, TAS675X_SETUP_EXIT_VAL);
1649133c81f8SSen Wang regmap_write(regmap, TAS675X_SETUP_REG2, TAS675X_SETUP_EXIT_VAL);
1650133c81f8SSen Wang err:
1651133c81f8SSen Wang dev_err(tas->dev, "Init device failed: %d\n", ret);
1652133c81f8SSen Wang return ret;
1653133c81f8SSen Wang }
1654133c81f8SSen Wang
tas675x_power_off(struct tas675x_priv * tas)1655133c81f8SSen Wang static void tas675x_power_off(struct tas675x_priv *tas)
1656133c81f8SSen Wang {
1657133c81f8SSen Wang regcache_cache_only(tas->regmap, true);
1658133c81f8SSen Wang regcache_mark_dirty(tas->regmap);
1659133c81f8SSen Wang tas675x_hw_disable(tas);
1660133c81f8SSen Wang }
1661133c81f8SSen Wang
tas675x_power_on(struct tas675x_priv * tas)1662133c81f8SSen Wang static int tas675x_power_on(struct tas675x_priv *tas)
1663133c81f8SSen Wang {
1664133c81f8SSen Wang int ret;
1665133c81f8SSen Wang
1666133c81f8SSen Wang ret = tas675x_hw_enable(tas);
1667133c81f8SSen Wang if (ret)
1668133c81f8SSen Wang return ret;
1669133c81f8SSen Wang
1670133c81f8SSen Wang regcache_cache_only(tas->regmap, false);
1671133c81f8SSen Wang regcache_mark_dirty(tas->regmap);
1672133c81f8SSen Wang
1673133c81f8SSen Wang ret = tas675x_init_device(tas);
1674133c81f8SSen Wang if (ret)
1675133c81f8SSen Wang goto err_disable;
1676133c81f8SSen Wang
1677133c81f8SSen Wang ret = regcache_sync(tas->regmap);
1678133c81f8SSen Wang if (ret) {
1679133c81f8SSen Wang dev_err(tas->dev, "Failed to sync regcache: %d\n", ret);
1680133c81f8SSen Wang goto err_disable;
1681133c81f8SSen Wang }
1682133c81f8SSen Wang
1683133c81f8SSen Wang /* Reset fault tracking */
1684133c81f8SSen Wang memset(tas->last_status, 0, sizeof(tas->last_status));
1685133c81f8SSen Wang
1686133c81f8SSen Wang return 0;
1687133c81f8SSen Wang
1688133c81f8SSen Wang err_disable:
1689133c81f8SSen Wang tas675x_power_off(tas);
1690133c81f8SSen Wang return ret;
1691133c81f8SSen Wang }
1692133c81f8SSen Wang
tas675x_runtime_suspend(struct device * dev)1693133c81f8SSen Wang static int tas675x_runtime_suspend(struct device *dev)
1694133c81f8SSen Wang {
1695133c81f8SSen Wang struct tas675x_priv *tas = dev_get_drvdata(dev);
1696133c81f8SSen Wang
1697133c81f8SSen Wang disable_delayed_work_sync(&tas->fault_check_work);
1698133c81f8SSen Wang tas675x_set_state_all(tas, TAS675X_STATE_SLEEP_BOTH);
1699133c81f8SSen Wang
1700133c81f8SSen Wang return 0;
1701133c81f8SSen Wang }
1702133c81f8SSen Wang
tas675x_runtime_resume(struct device * dev)1703133c81f8SSen Wang static int tas675x_runtime_resume(struct device *dev)
1704133c81f8SSen Wang {
1705133c81f8SSen Wang struct tas675x_priv *tas = dev_get_drvdata(dev);
1706133c81f8SSen Wang
1707133c81f8SSen Wang tas675x_set_state_all(tas, TAS675X_STATE_SLEEP_BOTH);
1708133c81f8SSen Wang
1709133c81f8SSen Wang if (!to_i2c_client(dev)->irq) {
1710133c81f8SSen Wang enable_delayed_work(&tas->fault_check_work);
1711133c81f8SSen Wang schedule_delayed_work(&tas->fault_check_work,
1712133c81f8SSen Wang msecs_to_jiffies(TAS675X_FAULT_CHECK_INTERVAL_MS));
1713133c81f8SSen Wang }
1714133c81f8SSen Wang
1715133c81f8SSen Wang return 0;
1716133c81f8SSen Wang }
1717133c81f8SSen Wang
tas675x_system_suspend(struct device * dev)1718133c81f8SSen Wang static int tas675x_system_suspend(struct device *dev)
1719133c81f8SSen Wang {
1720133c81f8SSen Wang struct tas675x_priv *tas = dev_get_drvdata(dev);
1721133c81f8SSen Wang int ret;
1722133c81f8SSen Wang
1723133c81f8SSen Wang ret = tas675x_runtime_suspend(dev);
1724133c81f8SSen Wang if (ret)
1725133c81f8SSen Wang return ret;
1726133c81f8SSen Wang
1727133c81f8SSen Wang if (to_i2c_client(dev)->irq)
1728133c81f8SSen Wang disable_irq(to_i2c_client(dev)->irq);
1729133c81f8SSen Wang
1730133c81f8SSen Wang tas675x_power_off(tas);
1731133c81f8SSen Wang return 0;
1732133c81f8SSen Wang }
1733133c81f8SSen Wang
tas675x_system_resume(struct device * dev)1734133c81f8SSen Wang static int tas675x_system_resume(struct device *dev)
1735133c81f8SSen Wang {
1736133c81f8SSen Wang struct tas675x_priv *tas = dev_get_drvdata(dev);
1737133c81f8SSen Wang int ret;
1738133c81f8SSen Wang
1739133c81f8SSen Wang ret = tas675x_power_on(tas);
1740133c81f8SSen Wang if (ret)
1741133c81f8SSen Wang return ret;
1742133c81f8SSen Wang
1743133c81f8SSen Wang if (to_i2c_client(dev)->irq)
1744133c81f8SSen Wang enable_irq(to_i2c_client(dev)->irq);
1745133c81f8SSen Wang
1746133c81f8SSen Wang return tas675x_runtime_resume(dev);
1747133c81f8SSen Wang }
1748133c81f8SSen Wang
1749133c81f8SSen Wang static const struct snd_soc_component_driver soc_codec_dev_tas675x = {
1750133c81f8SSen Wang .controls = tas675x_snd_controls,
1751133c81f8SSen Wang .num_controls = ARRAY_SIZE(tas675x_snd_controls),
1752133c81f8SSen Wang .dapm_widgets = tas675x_dapm_widgets,
1753133c81f8SSen Wang .num_dapm_widgets = ARRAY_SIZE(tas675x_dapm_widgets),
1754133c81f8SSen Wang .dapm_routes = tas675x_dapm_routes,
1755133c81f8SSen Wang .num_dapm_routes = ARRAY_SIZE(tas675x_dapm_routes),
1756133c81f8SSen Wang .endianness = 1,
1757133c81f8SSen Wang };
1758133c81f8SSen Wang
1759133c81f8SSen Wang /* Fault register flags */
1760133c81f8SSen Wang #define TAS675X_FAULT_CRITICAL BIT(0) /* causes FAULT state, FAULT_CLEAR required */
1761133c81f8SSen Wang #define TAS675X_FAULT_TRACK BIT(1) /* track last value, only log on change */
1762133c81f8SSen Wang #define TAS675X_FAULT_ACTIVE BIT(2) /* skip when no stream is active */
1763133c81f8SSen Wang
1764133c81f8SSen Wang struct tas675x_fault_reg {
1765133c81f8SSen Wang unsigned int reg;
1766133c81f8SSen Wang unsigned int flags;
1767133c81f8SSen Wang const char *name;
1768133c81f8SSen Wang };
1769133c81f8SSen Wang
1770133c81f8SSen Wang static const struct tas675x_fault_reg tas675x_fault_table[] = {
1771133c81f8SSen Wang /* Critical */
1772133c81f8SSen Wang { TAS675X_OTSD_LATCHED_REG, TAS675X_FAULT_CRITICAL | TAS675X_FAULT_TRACK,
1773133c81f8SSen Wang "Overtemperature Shutdown" },
1774133c81f8SSen Wang { TAS675X_OC_DC_FAULT_LATCHED_REG, TAS675X_FAULT_CRITICAL | TAS675X_FAULT_TRACK,
1775133c81f8SSen Wang "Overcurrent / DC Fault" },
1776133c81f8SSen Wang { TAS675X_RTLDG_OL_SL_FAULT_LATCHED_REG, TAS675X_FAULT_CRITICAL | TAS675X_FAULT_TRACK,
1777133c81f8SSen Wang "Real-Time Load Diagnostic Fault" },
1778133c81f8SSen Wang { TAS675X_CBC_FAULT_WARN_LATCHED_REG, TAS675X_FAULT_CRITICAL | TAS675X_FAULT_TRACK,
1779133c81f8SSen Wang "CBC Fault/Warning" },
1780133c81f8SSen Wang /* Warning */
1781133c81f8SSen Wang { TAS675X_POWER_FAULT_STATUS_1_REG, TAS675X_FAULT_TRACK,
1782133c81f8SSen Wang "CP / OUTM Fault" },
1783133c81f8SSen Wang { TAS675X_POWER_FAULT_LATCHED_REG, TAS675X_FAULT_TRACK,
1784133c81f8SSen Wang "Power Fault" },
1785133c81f8SSen Wang { TAS675X_CLK_FAULT_LATCHED_REG, TAS675X_FAULT_TRACK | TAS675X_FAULT_ACTIVE,
1786133c81f8SSen Wang "Clock Fault" },
1787133c81f8SSen Wang { TAS675X_OTW_LATCHED_REG, TAS675X_FAULT_TRACK,
1788133c81f8SSen Wang "Overtemperature Warning" },
1789133c81f8SSen Wang { TAS675X_CLIP_WARN_LATCHED_REG, TAS675X_FAULT_ACTIVE,
1790133c81f8SSen Wang "Clip Warning" },
1791133c81f8SSen Wang };
1792133c81f8SSen Wang
1793133c81f8SSen Wang static_assert(ARRAY_SIZE(tas675x_fault_table) == TAS675X_FAULT_REGS_NUM);
1794133c81f8SSen Wang
1795133c81f8SSen Wang /*
1796133c81f8SSen Wang * Read and log all latched fault registers.
1797133c81f8SSen Wang * Shared by both the polled fault_check_work and IRQ handler paths
1798133c81f8SSen Wang * (which are mutually exclusive, only one is active per device).
1799133c81f8SSen Wang * Returns true if any fault register needs to be cleared.
1800133c81f8SSen Wang *
1801133c81f8SSen Wang * For deciphering fault messages, see "Fault Monitoring" in
1802133c81f8SSen Wang * Documentation/sound/codecs/tas675x.rst
1803133c81f8SSen Wang */
tas675x_check_faults(struct tas675x_priv * tas)1804133c81f8SSen Wang static bool tas675x_check_faults(struct tas675x_priv *tas)
1805133c81f8SSen Wang {
1806133c81f8SSen Wang struct device *dev = tas->dev;
1807133c81f8SSen Wang bool needs_clear = false;
1808133c81f8SSen Wang unsigned int reg;
1809133c81f8SSen Wang int i, ret;
1810133c81f8SSen Wang
1811133c81f8SSen Wang for (i = 0; i < ARRAY_SIZE(tas675x_fault_table); i++) {
1812133c81f8SSen Wang const struct tas675x_fault_reg *f = &tas675x_fault_table[i];
1813133c81f8SSen Wang
1814133c81f8SSen Wang ret = regmap_read(tas->regmap, f->reg, ®);
1815133c81f8SSen Wang if (ret) {
1816133c81f8SSen Wang if (f->flags & TAS675X_FAULT_CRITICAL) {
1817133c81f8SSen Wang dev_err(dev, "failed to read %s: %d\n", f->name, ret);
1818133c81f8SSen Wang return needs_clear;
1819133c81f8SSen Wang }
1820133c81f8SSen Wang continue;
1821133c81f8SSen Wang }
1822133c81f8SSen Wang
1823133c81f8SSen Wang if (reg)
1824133c81f8SSen Wang needs_clear = true;
1825133c81f8SSen Wang
1826133c81f8SSen Wang /* Skip logging stream-dependent events when no stream is active */
1827133c81f8SSen Wang if ((f->flags & TAS675X_FAULT_ACTIVE) &&
1828133c81f8SSen Wang !READ_ONCE(tas->active_playback_dais) &&
1829133c81f8SSen Wang !READ_ONCE(tas->active_capture_dais))
1830133c81f8SSen Wang continue;
1831133c81f8SSen Wang
1832133c81f8SSen Wang /* Log on change or on every non-zero read */
1833133c81f8SSen Wang if (reg && (!(f->flags & TAS675X_FAULT_TRACK) ||
1834133c81f8SSen Wang reg != tas->last_status[i])) {
1835133c81f8SSen Wang if (f->flags & TAS675X_FAULT_CRITICAL)
1836133c81f8SSen Wang dev_crit(dev, "%s Latched: 0x%02x\n", f->name, reg);
1837133c81f8SSen Wang else
1838133c81f8SSen Wang dev_warn(dev, "%s Latched: 0x%02x\n", f->name, reg);
1839133c81f8SSen Wang }
1840133c81f8SSen Wang
1841133c81f8SSen Wang if (f->flags & TAS675X_FAULT_TRACK)
1842133c81f8SSen Wang tas->last_status[i] = reg;
1843133c81f8SSen Wang }
1844133c81f8SSen Wang
1845133c81f8SSen Wang return needs_clear;
1846133c81f8SSen Wang }
1847133c81f8SSen Wang
tas675x_fault_check_work(struct work_struct * work)1848133c81f8SSen Wang static void tas675x_fault_check_work(struct work_struct *work)
1849133c81f8SSen Wang {
1850133c81f8SSen Wang struct tas675x_priv *tas = container_of(work, struct tas675x_priv,
1851133c81f8SSen Wang fault_check_work.work);
1852133c81f8SSen Wang
1853133c81f8SSen Wang if (tas675x_check_faults(tas))
1854133c81f8SSen Wang regmap_write(tas->regmap, TAS675X_RESET_REG, TAS675X_FAULT_CLEAR);
1855133c81f8SSen Wang
1856133c81f8SSen Wang schedule_delayed_work(&tas->fault_check_work,
1857133c81f8SSen Wang msecs_to_jiffies(TAS675X_FAULT_CHECK_INTERVAL_MS));
1858133c81f8SSen Wang }
1859133c81f8SSen Wang
tas675x_irq_handler(int irq,void * data)1860133c81f8SSen Wang static irqreturn_t tas675x_irq_handler(int irq, void *data)
1861133c81f8SSen Wang {
1862133c81f8SSen Wang struct tas675x_priv *tas = data;
1863133c81f8SSen Wang irqreturn_t ret = IRQ_NONE;
1864133c81f8SSen Wang
1865133c81f8SSen Wang if (pm_runtime_resume_and_get(tas->dev) < 0)
1866133c81f8SSen Wang return IRQ_NONE;
1867133c81f8SSen Wang
1868133c81f8SSen Wang if (tas675x_check_faults(tas)) {
1869133c81f8SSen Wang regmap_write(tas->regmap, TAS675X_RESET_REG, TAS675X_FAULT_CLEAR);
1870133c81f8SSen Wang ret = IRQ_HANDLED;
1871133c81f8SSen Wang }
1872133c81f8SSen Wang
1873133c81f8SSen Wang pm_runtime_mark_last_busy(tas->dev);
1874133c81f8SSen Wang pm_runtime_put_autosuspend(tas->dev);
1875133c81f8SSen Wang return ret;
1876133c81f8SSen Wang }
1877133c81f8SSen Wang
1878133c81f8SSen Wang static const struct reg_default tas675x_reg_defaults[] = {
1879133c81f8SSen Wang { TAS675X_PAGE_CTRL_REG, 0x00 },
1880133c81f8SSen Wang { TAS675X_OUTPUT_CTRL_REG, 0x00 },
1881133c81f8SSen Wang { TAS675X_STATE_CTRL_CH1_CH2_REG, TAS675X_STATE_SLEEP_BOTH },
1882133c81f8SSen Wang { TAS675X_STATE_CTRL_CH3_CH4_REG, TAS675X_STATE_SLEEP_BOTH },
1883133c81f8SSen Wang { TAS675X_ISENSE_CTRL_REG, 0x0F },
1884133c81f8SSen Wang { TAS675X_DC_DETECT_CTRL_REG, 0x00 },
1885133c81f8SSen Wang { TAS675X_SCLK_INV_CTRL_REG, 0x00 },
1886133c81f8SSen Wang { TAS675X_AUDIO_IF_CTRL_REG, 0x00 },
1887133c81f8SSen Wang { TAS675X_SDIN_CTRL_REG, 0x0A },
1888133c81f8SSen Wang { TAS675X_SDOUT_CTRL_REG, 0x1A },
1889133c81f8SSen Wang { TAS675X_SDIN_OFFSET_MSB_REG, 0x00 },
1890133c81f8SSen Wang { TAS675X_SDIN_AUDIO_OFFSET_REG, 0x00 },
1891133c81f8SSen Wang { TAS675X_SDIN_LL_OFFSET_REG, 0x60 },
1892133c81f8SSen Wang { TAS675X_SDIN_CH_SWAP_REG, 0x00 },
1893133c81f8SSen Wang { TAS675X_SDOUT_OFFSET_MSB_REG, 0xCF },
1894133c81f8SSen Wang { TAS675X_VPREDICT_OFFSET_REG, 0xFF },
1895133c81f8SSen Wang { TAS675X_ISENSE_OFFSET_REG, 0x00 },
1896133c81f8SSen Wang { TAS675X_SDOUT_EN_REG, 0x00 },
1897133c81f8SSen Wang { TAS675X_LL_EN_REG, 0x00 },
1898133c81f8SSen Wang { TAS675X_RTLDG_EN_REG, 0x10 },
1899133c81f8SSen Wang { TAS675X_DC_BLOCK_BYP_REG, 0x00 },
1900133c81f8SSen Wang { TAS675X_DSP_CTRL_REG, 0x00 },
1901133c81f8SSen Wang { TAS675X_PAGE_AUTO_INC_REG, 0x00 },
1902133c81f8SSen Wang { TAS675X_DIG_VOL_CH1_REG, 0x30 },
1903133c81f8SSen Wang { TAS675X_DIG_VOL_CH2_REG, 0x30 },
1904133c81f8SSen Wang { TAS675X_DIG_VOL_CH3_REG, 0x30 },
1905133c81f8SSen Wang { TAS675X_DIG_VOL_CH4_REG, 0x30 },
1906133c81f8SSen Wang { TAS675X_DIG_VOL_RAMP_CTRL_REG, 0x77 },
1907133c81f8SSen Wang { TAS675X_DIG_VOL_COMBINE_CTRL_REG, 0x00 },
1908133c81f8SSen Wang { TAS675X_AUTO_MUTE_EN_REG, 0x00 },
1909133c81f8SSen Wang { TAS675X_AUTO_MUTE_TIMING_CH1_CH2_REG, 0x00 },
1910133c81f8SSen Wang { TAS675X_AUTO_MUTE_TIMING_CH3_CH4_REG, 0x00 },
1911133c81f8SSen Wang { TAS675X_ANALOG_GAIN_CH1_CH2_REG, 0x00 },
1912133c81f8SSen Wang { TAS675X_ANALOG_GAIN_CH3_CH4_REG, 0x00 },
1913133c81f8SSen Wang { TAS675X_ANALOG_GAIN_RAMP_CTRL_REG, 0x00 },
1914133c81f8SSen Wang { TAS675X_PULSE_INJECTION_EN_REG, 0x03 },
1915133c81f8SSen Wang { TAS675X_CBC_CTRL_REG, 0x07 },
1916133c81f8SSen Wang { TAS675X_CURRENT_LIMIT_CTRL_REG, 0x00 },
1917133c81f8SSen Wang { TAS675X_ISENSE_CAL_REG, 0x00 },
1918133c81f8SSen Wang { TAS675X_PWM_PHASE_CTRL_REG, 0x00 },
1919133c81f8SSen Wang { TAS675X_SS_CTRL_REG, 0x00 },
1920133c81f8SSen Wang { TAS675X_SS_RANGE_CTRL_REG, 0x00 },
1921133c81f8SSen Wang { TAS675X_SS_DWELL_CTRL_REG, 0x00 },
1922133c81f8SSen Wang { TAS675X_RAMP_PHASE_CTRL_GPO_REG, 0x00 },
1923133c81f8SSen Wang { TAS675X_PWM_PHASE_M_CTRL_CH1_REG, 0x00 },
1924133c81f8SSen Wang { TAS675X_PWM_PHASE_M_CTRL_CH2_REG, 0x00 },
1925133c81f8SSen Wang { TAS675X_PWM_PHASE_M_CTRL_CH3_REG, 0x00 },
1926133c81f8SSen Wang { TAS675X_PWM_PHASE_M_CTRL_CH4_REG, 0x00 },
1927133c81f8SSen Wang { TAS675X_REPORT_ROUTING_1_REG, 0x00 },
1928133c81f8SSen Wang { TAS675X_OTSD_RECOVERY_EN_REG, 0x00 },
1929133c81f8SSen Wang { TAS675X_REPORT_ROUTING_2_REG, 0xA2 },
1930133c81f8SSen Wang { TAS675X_REPORT_ROUTING_3_REG, 0x00 },
1931133c81f8SSen Wang { TAS675X_REPORT_ROUTING_4_REG, 0x06 },
1932133c81f8SSen Wang { TAS675X_CLIP_DETECT_CTRL_REG, 0x00 },
1933133c81f8SSen Wang { TAS675X_REPORT_ROUTING_5_REG, 0x00 },
1934133c81f8SSen Wang { TAS675X_GPIO1_OUTPUT_SEL_REG, 0x00 },
1935133c81f8SSen Wang { TAS675X_GPIO2_OUTPUT_SEL_REG, 0x00 },
1936133c81f8SSen Wang { TAS675X_GPIO_CTRL_REG, TAS675X_GPIO_CTRL_RSTVAL },
1937133c81f8SSen Wang { TAS675X_DC_LDG_CTRL_REG, 0x00 },
1938133c81f8SSen Wang { TAS675X_DC_LDG_LO_CTRL_REG, 0x00 },
1939133c81f8SSen Wang { TAS675X_DC_LDG_TIME_CTRL_REG, 0x00 },
1940133c81f8SSen Wang { TAS675X_DC_LDG_SL_CH1_CH2_CTRL_REG, 0x11 },
1941133c81f8SSen Wang { TAS675X_DC_LDG_SL_CH3_CH4_CTRL_REG, 0x11 },
1942133c81f8SSen Wang { TAS675X_AC_LDG_CTRL_REG, 0x10 },
1943133c81f8SSen Wang { TAS675X_TWEETER_DETECT_CTRL_REG, 0x08 },
1944133c81f8SSen Wang { TAS675X_TWEETER_DETECT_THRESH_REG, 0x00 },
1945133c81f8SSen Wang { TAS675X_AC_LDG_FREQ_CTRL_REG, 0xC8 },
1946133c81f8SSen Wang { TAS675X_OTW_CTRL_CH1_CH2_REG, 0x11 },
1947133c81f8SSen Wang { TAS675X_OTW_CTRL_CH3_CH4_REG, 0x11 },
1948133c81f8SSen Wang };
1949133c81f8SSen Wang
tas675x_is_readable_register(struct device * dev,unsigned int reg)1950133c81f8SSen Wang static bool tas675x_is_readable_register(struct device *dev, unsigned int reg)
1951133c81f8SSen Wang {
1952133c81f8SSen Wang switch (reg) {
1953133c81f8SSen Wang case TAS675X_RESET_REG:
1954133c81f8SSen Wang return false;
1955133c81f8SSen Wang default:
1956133c81f8SSen Wang return true;
1957133c81f8SSen Wang }
1958133c81f8SSen Wang }
1959133c81f8SSen Wang
tas675x_is_volatile_register(struct device * dev,unsigned int reg)1960133c81f8SSen Wang static bool tas675x_is_volatile_register(struct device *dev, unsigned int reg)
1961133c81f8SSen Wang {
1962133c81f8SSen Wang switch (reg) {
1963133c81f8SSen Wang case TAS675X_RESET_REG:
1964133c81f8SSen Wang case TAS675X_BOOK_CTRL_REG:
1965133c81f8SSen Wang case TAS675X_AUTO_MUTE_STATUS_REG:
1966133c81f8SSen Wang case TAS675X_STATE_REPORT_CH1_CH2_REG:
1967133c81f8SSen Wang case TAS675X_STATE_REPORT_CH3_CH4_REG:
1968133c81f8SSen Wang case TAS675X_PVDD_SENSE_REG:
1969133c81f8SSen Wang case TAS675X_TEMP_GLOBAL_REG:
1970133c81f8SSen Wang case TAS675X_TEMP_CH1_CH2_REG:
1971133c81f8SSen Wang case TAS675X_TEMP_CH3_CH4_REG:
1972133c81f8SSen Wang case TAS675X_FS_MON_REG:
1973133c81f8SSen Wang case TAS675X_SCLK_MON_REG:
1974133c81f8SSen Wang case TAS675X_POWER_FAULT_STATUS_1_REG:
1975133c81f8SSen Wang case TAS675X_POWER_FAULT_STATUS_2_REG:
1976133c81f8SSen Wang case TAS675X_OT_FAULT_REG:
1977133c81f8SSen Wang case TAS675X_OTW_STATUS_REG:
1978133c81f8SSen Wang case TAS675X_CLIP_WARN_STATUS_REG:
1979133c81f8SSen Wang case TAS675X_CBC_WARNING_STATUS_REG:
1980133c81f8SSen Wang case TAS675X_POWER_FAULT_LATCHED_REG:
1981133c81f8SSen Wang case TAS675X_OTSD_LATCHED_REG:
1982133c81f8SSen Wang case TAS675X_OTW_LATCHED_REG:
1983133c81f8SSen Wang case TAS675X_CLIP_WARN_LATCHED_REG:
1984133c81f8SSen Wang case TAS675X_CLK_FAULT_LATCHED_REG:
1985133c81f8SSen Wang case TAS675X_RTLDG_OL_SL_FAULT_LATCHED_REG:
1986133c81f8SSen Wang case TAS675X_CBC_FAULT_WARN_LATCHED_REG:
1987133c81f8SSen Wang case TAS675X_OC_DC_FAULT_LATCHED_REG:
1988133c81f8SSen Wang case TAS675X_WARN_OT_MAX_FLAG_REG:
1989133c81f8SSen Wang case TAS675X_DC_LDG_REPORT_CH1_CH2_REG ... TAS675X_TWEETER_REPORT_REG:
1990133c81f8SSen Wang case TAS675X_CH1_RTLDG_IMP_MSB_REG ... TAS675X_CH4_DC_LDG_DCR_LSB_REG:
1991133c81f8SSen Wang return true;
1992133c81f8SSen Wang default:
1993133c81f8SSen Wang return false;
1994133c81f8SSen Wang }
1995133c81f8SSen Wang }
1996133c81f8SSen Wang
1997133c81f8SSen Wang static const struct regmap_range_cfg tas675x_ranges[] = {
1998133c81f8SSen Wang {
1999133c81f8SSen Wang .name = "Pages",
2000133c81f8SSen Wang .range_min = 0,
2001133c81f8SSen Wang .range_max = TAS675X_PAGE_SIZE * TAS675X_PAGE_SIZE - 1,
2002133c81f8SSen Wang .selector_reg = TAS675X_PAGE_CTRL_REG,
2003133c81f8SSen Wang .selector_mask = 0xff,
2004133c81f8SSen Wang .selector_shift = 0,
2005133c81f8SSen Wang .window_start = 0,
2006133c81f8SSen Wang .window_len = TAS675X_PAGE_SIZE,
2007133c81f8SSen Wang },
2008133c81f8SSen Wang };
2009133c81f8SSen Wang
tas675x_regmap_lock(void * lock_arg)2010133c81f8SSen Wang static void tas675x_regmap_lock(void *lock_arg)
2011133c81f8SSen Wang {
2012133c81f8SSen Wang struct tas675x_priv *tas = lock_arg;
2013133c81f8SSen Wang
2014133c81f8SSen Wang mutex_lock(&tas->io_lock);
2015133c81f8SSen Wang }
2016133c81f8SSen Wang
tas675x_regmap_unlock(void * lock_arg)2017133c81f8SSen Wang static void tas675x_regmap_unlock(void *lock_arg)
2018133c81f8SSen Wang {
2019133c81f8SSen Wang struct tas675x_priv *tas = lock_arg;
2020133c81f8SSen Wang
2021133c81f8SSen Wang mutex_unlock(&tas->io_lock);
2022133c81f8SSen Wang }
2023133c81f8SSen Wang
2024133c81f8SSen Wang static const struct regmap_config tas675x_regmap_config = {
2025133c81f8SSen Wang .reg_bits = 8,
2026133c81f8SSen Wang .val_bits = 8,
2027133c81f8SSen Wang .max_register = TAS675X_PAGE_SIZE * TAS675X_PAGE_SIZE - 1,
2028133c81f8SSen Wang .ranges = tas675x_ranges,
2029133c81f8SSen Wang .num_ranges = ARRAY_SIZE(tas675x_ranges),
2030133c81f8SSen Wang .cache_type = REGCACHE_MAPLE,
2031133c81f8SSen Wang .reg_defaults = tas675x_reg_defaults,
2032133c81f8SSen Wang .num_reg_defaults = ARRAY_SIZE(tas675x_reg_defaults),
2033133c81f8SSen Wang .readable_reg = tas675x_is_readable_register,
2034133c81f8SSen Wang .volatile_reg = tas675x_is_volatile_register,
2035133c81f8SSen Wang };
2036133c81f8SSen Wang
tas675x_i2c_probe(struct i2c_client * client)2037133c81f8SSen Wang static int tas675x_i2c_probe(struct i2c_client *client)
2038133c81f8SSen Wang {
2039133c81f8SSen Wang struct regmap_config cfg = tas675x_regmap_config;
2040133c81f8SSen Wang struct tas675x_priv *tas;
2041133c81f8SSen Wang u32 val;
2042133c81f8SSen Wang int i, ret;
2043133c81f8SSen Wang
2044133c81f8SSen Wang tas = devm_kzalloc(&client->dev, sizeof(*tas), GFP_KERNEL);
2045133c81f8SSen Wang if (!tas)
2046133c81f8SSen Wang return -ENOMEM;
2047133c81f8SSen Wang
2048133c81f8SSen Wang tas->dev = &client->dev;
2049133c81f8SSen Wang i2c_set_clientdata(client, tas);
2050133c81f8SSen Wang
2051133c81f8SSen Wang mutex_init(&tas->io_lock);
2052133c81f8SSen Wang cfg.lock = tas675x_regmap_lock;
2053133c81f8SSen Wang cfg.unlock = tas675x_regmap_unlock;
2054133c81f8SSen Wang cfg.lock_arg = tas;
2055133c81f8SSen Wang
2056133c81f8SSen Wang memcpy(tas->dsp_params, tas675x_dsp_defaults, sizeof(tas->dsp_params));
2057133c81f8SSen Wang INIT_DELAYED_WORK(&tas->fault_check_work, tas675x_fault_check_work);
2058133c81f8SSen Wang
2059133c81f8SSen Wang tas->regmap = devm_regmap_init_i2c(client, &cfg);
2060133c81f8SSen Wang if (IS_ERR(tas->regmap))
2061133c81f8SSen Wang return PTR_ERR(tas->regmap);
2062133c81f8SSen Wang
2063133c81f8SSen Wang /* Keep regmap cache-only until hardware is powered on */
2064133c81f8SSen Wang regcache_cache_only(tas->regmap, true);
2065133c81f8SSen Wang
2066133c81f8SSen Wang tas->dev_type = (enum tas675x_type)(unsigned long)device_get_match_data(tas->dev);
2067133c81f8SSen Wang tas->fast_boot = device_property_read_bool(tas->dev, "ti,fast-boot");
2068133c81f8SSen Wang
2069133c81f8SSen Wang tas->audio_slot = -1;
2070133c81f8SSen Wang tas->llp_slot = -1;
2071133c81f8SSen Wang tas->vpredict_slot = -1;
2072133c81f8SSen Wang tas->isense_slot = -1;
2073133c81f8SSen Wang if (!device_property_read_u32(tas->dev, "ti,audio-slot-no", &val))
2074133c81f8SSen Wang tas->audio_slot = val;
2075133c81f8SSen Wang if (!device_property_read_u32(tas->dev, "ti,llp-slot-no", &val))
2076133c81f8SSen Wang tas->llp_slot = val;
2077133c81f8SSen Wang if (!device_property_read_u32(tas->dev, "ti,vpredict-slot-no", &val))
2078133c81f8SSen Wang tas->vpredict_slot = val;
2079133c81f8SSen Wang if (!device_property_read_u32(tas->dev, "ti,isense-slot-no", &val))
2080133c81f8SSen Wang tas->isense_slot = val;
2081133c81f8SSen Wang
2082133c81f8SSen Wang tas->gpio1_func = tas675x_gpio_func_parse(tas->dev, "ti,gpio1-function");
2083133c81f8SSen Wang tas->gpio2_func = tas675x_gpio_func_parse(tas->dev, "ti,gpio2-function");
2084133c81f8SSen Wang
2085133c81f8SSen Wang for (i = 0; i < ARRAY_SIZE(tas675x_supply_names); i++)
2086133c81f8SSen Wang tas->supplies[i].supply = tas675x_supply_names[i];
2087133c81f8SSen Wang
2088133c81f8SSen Wang ret = devm_regulator_bulk_get(tas->dev, ARRAY_SIZE(tas->supplies), tas->supplies);
2089133c81f8SSen Wang if (ret)
2090133c81f8SSen Wang return dev_err_probe(tas->dev, ret, "Failed to request supplies\n");
2091133c81f8SSen Wang
2092133c81f8SSen Wang tas->vbat = devm_regulator_get_optional(tas->dev, "vbat");
2093133c81f8SSen Wang if (IS_ERR(tas->vbat) && PTR_ERR(tas->vbat) != -ENODEV)
2094133c81f8SSen Wang return dev_err_probe(tas->dev, PTR_ERR(tas->vbat),
2095133c81f8SSen Wang "Failed to get vbat supply\n");
2096133c81f8SSen Wang
2097133c81f8SSen Wang tas->pd_gpio = devm_gpiod_get_optional(tas->dev, "powerdown", GPIOD_OUT_HIGH);
2098133c81f8SSen Wang if (IS_ERR(tas->pd_gpio))
2099133c81f8SSen Wang return dev_err_probe(tas->dev, PTR_ERR(tas->pd_gpio), "Failed powerdown-gpios\n");
2100133c81f8SSen Wang
2101133c81f8SSen Wang tas->stby_gpio = devm_gpiod_get_optional(tas->dev, "standby", GPIOD_OUT_HIGH);
2102133c81f8SSen Wang if (IS_ERR(tas->stby_gpio))
2103133c81f8SSen Wang return dev_err_probe(tas->dev, PTR_ERR(tas->stby_gpio), "Failed standby-gpios\n");
2104133c81f8SSen Wang
2105133c81f8SSen Wang if (!tas->pd_gpio && !tas->stby_gpio)
2106133c81f8SSen Wang return dev_err_probe(tas->dev, -EINVAL,
2107133c81f8SSen Wang "At least one of powerdown-gpios or standby-gpios is required\n");
2108133c81f8SSen Wang
2109133c81f8SSen Wang ret = tas675x_power_on(tas);
2110133c81f8SSen Wang if (ret)
2111133c81f8SSen Wang return ret;
2112133c81f8SSen Wang
2113133c81f8SSen Wang if (client->irq) {
2114133c81f8SSen Wang ret = devm_request_threaded_irq(tas->dev, client->irq, NULL,
2115133c81f8SSen Wang tas675x_irq_handler,
2116133c81f8SSen Wang IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
2117133c81f8SSen Wang "tas675x-fault", tas);
2118133c81f8SSen Wang if (ret) {
2119133c81f8SSen Wang tas675x_power_off(tas);
2120133c81f8SSen Wang return dev_err_probe(tas->dev, ret, "Failed to request IRQ\n");
2121133c81f8SSen Wang }
2122133c81f8SSen Wang } else {
2123133c81f8SSen Wang /* Schedule delayed work for fault checking at probe and runtime resume */
2124133c81f8SSen Wang schedule_delayed_work(&tas->fault_check_work,
2125133c81f8SSen Wang msecs_to_jiffies(TAS675X_FAULT_CHECK_INTERVAL_MS));
2126133c81f8SSen Wang }
2127133c81f8SSen Wang
2128133c81f8SSen Wang /* Enable runtime PM with 2s autosuspend */
2129133c81f8SSen Wang pm_runtime_set_autosuspend_delay(tas->dev, 2000);
2130133c81f8SSen Wang pm_runtime_use_autosuspend(tas->dev);
2131133c81f8SSen Wang pm_runtime_set_active(tas->dev);
2132133c81f8SSen Wang pm_runtime_mark_last_busy(tas->dev);
2133133c81f8SSen Wang pm_runtime_enable(tas->dev);
2134133c81f8SSen Wang
2135133c81f8SSen Wang ret = devm_snd_soc_register_component(tas->dev, &soc_codec_dev_tas675x,
2136133c81f8SSen Wang tas675x_dais, ARRAY_SIZE(tas675x_dais));
2137133c81f8SSen Wang if (ret)
2138133c81f8SSen Wang goto err_pm_disable;
2139133c81f8SSen Wang
2140133c81f8SSen Wang return 0;
2141133c81f8SSen Wang
2142133c81f8SSen Wang err_pm_disable:
2143133c81f8SSen Wang pm_runtime_force_suspend(tas->dev);
2144133c81f8SSen Wang pm_runtime_disable(tas->dev);
2145133c81f8SSen Wang tas675x_power_off(tas);
2146133c81f8SSen Wang return ret;
2147133c81f8SSen Wang }
2148133c81f8SSen Wang
tas675x_i2c_remove(struct i2c_client * client)2149133c81f8SSen Wang static void tas675x_i2c_remove(struct i2c_client *client)
2150133c81f8SSen Wang {
2151133c81f8SSen Wang struct tas675x_priv *tas = dev_get_drvdata(&client->dev);
2152133c81f8SSen Wang
2153133c81f8SSen Wang disable_delayed_work_sync(&tas->fault_check_work);
2154133c81f8SSen Wang if (client->irq)
2155133c81f8SSen Wang disable_irq(client->irq);
2156133c81f8SSen Wang
2157133c81f8SSen Wang pm_runtime_force_suspend(&client->dev);
2158133c81f8SSen Wang pm_runtime_disable(&client->dev);
2159133c81f8SSen Wang tas675x_power_off(tas);
2160133c81f8SSen Wang }
2161133c81f8SSen Wang
2162133c81f8SSen Wang static const struct dev_pm_ops tas675x_pm_ops = {
2163133c81f8SSen Wang SYSTEM_SLEEP_PM_OPS(tas675x_system_suspend, tas675x_system_resume)
2164133c81f8SSen Wang RUNTIME_PM_OPS(tas675x_runtime_suspend, tas675x_runtime_resume, NULL)
2165133c81f8SSen Wang };
2166133c81f8SSen Wang
2167133c81f8SSen Wang static const struct of_device_id tas675x_of_match[] = {
2168133c81f8SSen Wang { .compatible = "ti,tas67524", .data = (void *)TAS67524 },
2169133c81f8SSen Wang { }
2170133c81f8SSen Wang };
2171133c81f8SSen Wang MODULE_DEVICE_TABLE(of, tas675x_of_match);
2172133c81f8SSen Wang
2173133c81f8SSen Wang static const struct i2c_device_id tas675x_i2c_id[] = {
2174133c81f8SSen Wang { "tas67524", TAS67524 },
2175133c81f8SSen Wang { }
2176133c81f8SSen Wang };
2177133c81f8SSen Wang MODULE_DEVICE_TABLE(i2c, tas675x_i2c_id);
2178133c81f8SSen Wang
2179133c81f8SSen Wang static struct i2c_driver tas675x_i2c_driver = {
2180133c81f8SSen Wang .driver = {
2181133c81f8SSen Wang .name = "tas675x",
2182133c81f8SSen Wang .of_match_table = tas675x_of_match,
2183133c81f8SSen Wang .pm = pm_ptr(&tas675x_pm_ops),
2184133c81f8SSen Wang },
2185133c81f8SSen Wang .probe = tas675x_i2c_probe,
2186133c81f8SSen Wang .remove = tas675x_i2c_remove,
2187133c81f8SSen Wang .id_table = tas675x_i2c_id,
2188133c81f8SSen Wang };
2189133c81f8SSen Wang
2190133c81f8SSen Wang module_i2c_driver(tas675x_i2c_driver);
2191133c81f8SSen Wang
2192133c81f8SSen Wang MODULE_AUTHOR("Sen Wang <sen@ti.com>");
2193133c81f8SSen Wang MODULE_DESCRIPTION("ASoC TAS675x Audio Amplifier Driver");
2194133c81f8SSen Wang MODULE_LICENSE("GPL");
2195