162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ALSA SoC Texas Instruments TAS6424 Quad-Channel Audio Amplifier
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2016-2017 Texas Instruments Incorporated - https://www.ti.com/
662306a36Sopenharmony_ci *	Author: Andreas Dannenberg <dannenberg@ti.com>
762306a36Sopenharmony_ci *	Andrew F. Davis <afd@ti.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/errno.h>
1262306a36Sopenharmony_ci#include <linux/device.h>
1362306a36Sopenharmony_ci#include <linux/i2c.h>
1462306a36Sopenharmony_ci#include <linux/regmap.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
1762306a36Sopenharmony_ci#include <linux/delay.h>
1862306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <sound/pcm.h>
2162306a36Sopenharmony_ci#include <sound/pcm_params.h>
2262306a36Sopenharmony_ci#include <sound/soc.h>
2362306a36Sopenharmony_ci#include <sound/soc-dapm.h>
2462306a36Sopenharmony_ci#include <sound/tlv.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "tas6424.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/* Define how often to check (and clear) the fault status register (in ms) */
2962306a36Sopenharmony_ci#define TAS6424_FAULT_CHECK_INTERVAL 200
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic const char * const tas6424_supply_names[] = {
3262306a36Sopenharmony_ci	"dvdd", /* Digital power supply. Connect to 3.3-V supply. */
3362306a36Sopenharmony_ci	"vbat", /* Supply used for higher voltage analog circuits. */
3462306a36Sopenharmony_ci	"pvdd", /* Class-D amp output FETs supply. */
3562306a36Sopenharmony_ci};
3662306a36Sopenharmony_ci#define TAS6424_NUM_SUPPLIES ARRAY_SIZE(tas6424_supply_names)
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistruct tas6424_data {
3962306a36Sopenharmony_ci	struct device *dev;
4062306a36Sopenharmony_ci	struct regmap *regmap;
4162306a36Sopenharmony_ci	struct regulator_bulk_data supplies[TAS6424_NUM_SUPPLIES];
4262306a36Sopenharmony_ci	struct delayed_work fault_check_work;
4362306a36Sopenharmony_ci	unsigned int last_cfault;
4462306a36Sopenharmony_ci	unsigned int last_fault1;
4562306a36Sopenharmony_ci	unsigned int last_fault2;
4662306a36Sopenharmony_ci	unsigned int last_warn;
4762306a36Sopenharmony_ci	struct gpio_desc *standby_gpio;
4862306a36Sopenharmony_ci	struct gpio_desc *mute_gpio;
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/*
5262306a36Sopenharmony_ci * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB steps. Note that
5362306a36Sopenharmony_ci * setting the gain below -100 dB (register value <0x7) is effectively a MUTE
5462306a36Sopenharmony_ci * as per device datasheet.
5562306a36Sopenharmony_ci */
5662306a36Sopenharmony_cistatic DECLARE_TLV_DB_SCALE(dac_tlv, -10350, 50, 0);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic const struct snd_kcontrol_new tas6424_snd_controls[] = {
5962306a36Sopenharmony_ci	SOC_SINGLE_TLV("Speaker Driver CH1 Playback Volume",
6062306a36Sopenharmony_ci		       TAS6424_CH1_VOL_CTRL, 0, 0xff, 0, dac_tlv),
6162306a36Sopenharmony_ci	SOC_SINGLE_TLV("Speaker Driver CH2 Playback Volume",
6262306a36Sopenharmony_ci		       TAS6424_CH2_VOL_CTRL, 0, 0xff, 0, dac_tlv),
6362306a36Sopenharmony_ci	SOC_SINGLE_TLV("Speaker Driver CH3 Playback Volume",
6462306a36Sopenharmony_ci		       TAS6424_CH3_VOL_CTRL, 0, 0xff, 0, dac_tlv),
6562306a36Sopenharmony_ci	SOC_SINGLE_TLV("Speaker Driver CH4 Playback Volume",
6662306a36Sopenharmony_ci		       TAS6424_CH4_VOL_CTRL, 0, 0xff, 0, dac_tlv),
6762306a36Sopenharmony_ci	SOC_SINGLE_STROBE("Auto Diagnostics Switch", TAS6424_DC_DIAG_CTRL1,
6862306a36Sopenharmony_ci			  TAS6424_LDGBYPASS_SHIFT, 1),
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic int tas6424_dac_event(struct snd_soc_dapm_widget *w,
7262306a36Sopenharmony_ci			     struct snd_kcontrol *kcontrol, int event)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
7562306a36Sopenharmony_ci	struct tas6424_data *tas6424 = snd_soc_component_get_drvdata(component);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	dev_dbg(component->dev, "%s() event=0x%0x\n", __func__, event);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (event & SND_SOC_DAPM_POST_PMU) {
8062306a36Sopenharmony_ci		/* Observe codec shutdown-to-active time */
8162306a36Sopenharmony_ci		msleep(12);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci		/* Turn on TAS6424 periodic fault checking/handling */
8462306a36Sopenharmony_ci		tas6424->last_fault1 = 0;
8562306a36Sopenharmony_ci		tas6424->last_fault2 = 0;
8662306a36Sopenharmony_ci		tas6424->last_warn = 0;
8762306a36Sopenharmony_ci		schedule_delayed_work(&tas6424->fault_check_work,
8862306a36Sopenharmony_ci				      msecs_to_jiffies(TAS6424_FAULT_CHECK_INTERVAL));
8962306a36Sopenharmony_ci	} else if (event & SND_SOC_DAPM_PRE_PMD) {
9062306a36Sopenharmony_ci		/* Disable TAS6424 periodic fault checking/handling */
9162306a36Sopenharmony_ci		cancel_delayed_work_sync(&tas6424->fault_check_work);
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	return 0;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget tas6424_dapm_widgets[] = {
9862306a36Sopenharmony_ci	SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
9962306a36Sopenharmony_ci	SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas6424_dac_event,
10062306a36Sopenharmony_ci			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
10162306a36Sopenharmony_ci	SND_SOC_DAPM_OUTPUT("OUT")
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic const struct snd_soc_dapm_route tas6424_audio_map[] = {
10562306a36Sopenharmony_ci	{ "DAC", NULL, "DAC IN" },
10662306a36Sopenharmony_ci	{ "OUT", NULL, "DAC" },
10762306a36Sopenharmony_ci};
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic int tas6424_hw_params(struct snd_pcm_substream *substream,
11062306a36Sopenharmony_ci			     struct snd_pcm_hw_params *params,
11162306a36Sopenharmony_ci			     struct snd_soc_dai *dai)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct snd_soc_component *component = dai->component;
11462306a36Sopenharmony_ci	unsigned int rate = params_rate(params);
11562306a36Sopenharmony_ci	unsigned int width = params_width(params);
11662306a36Sopenharmony_ci	u8 sap_ctrl = 0;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	dev_dbg(component->dev, "%s() rate=%u width=%u\n", __func__, rate, width);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	switch (rate) {
12162306a36Sopenharmony_ci	case 44100:
12262306a36Sopenharmony_ci		sap_ctrl |= TAS6424_SAP_RATE_44100;
12362306a36Sopenharmony_ci		break;
12462306a36Sopenharmony_ci	case 48000:
12562306a36Sopenharmony_ci		sap_ctrl |= TAS6424_SAP_RATE_48000;
12662306a36Sopenharmony_ci		break;
12762306a36Sopenharmony_ci	case 96000:
12862306a36Sopenharmony_ci		sap_ctrl |= TAS6424_SAP_RATE_96000;
12962306a36Sopenharmony_ci		break;
13062306a36Sopenharmony_ci	default:
13162306a36Sopenharmony_ci		dev_err(component->dev, "unsupported sample rate: %u\n", rate);
13262306a36Sopenharmony_ci		return -EINVAL;
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	switch (width) {
13662306a36Sopenharmony_ci	case 16:
13762306a36Sopenharmony_ci		sap_ctrl |= TAS6424_SAP_TDM_SLOT_SZ_16;
13862306a36Sopenharmony_ci		break;
13962306a36Sopenharmony_ci	case 24:
14062306a36Sopenharmony_ci		break;
14162306a36Sopenharmony_ci	default:
14262306a36Sopenharmony_ci		dev_err(component->dev, "unsupported sample width: %u\n", width);
14362306a36Sopenharmony_ci		return -EINVAL;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	snd_soc_component_update_bits(component, TAS6424_SAP_CTRL,
14762306a36Sopenharmony_ci			    TAS6424_SAP_RATE_MASK |
14862306a36Sopenharmony_ci			    TAS6424_SAP_TDM_SLOT_SZ_16,
14962306a36Sopenharmony_ci			    sap_ctrl);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	return 0;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic int tas6424_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	struct snd_soc_component *component = dai->component;
15762306a36Sopenharmony_ci	u8 serial_format = 0;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	dev_dbg(component->dev, "%s() fmt=0x%0x\n", __func__, fmt);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/* clock masters */
16262306a36Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
16362306a36Sopenharmony_ci	case SND_SOC_DAIFMT_CBC_CFC:
16462306a36Sopenharmony_ci		break;
16562306a36Sopenharmony_ci	default:
16662306a36Sopenharmony_ci		dev_err(component->dev, "Invalid DAI clocking\n");
16762306a36Sopenharmony_ci		return -EINVAL;
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	/* signal polarity */
17162306a36Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
17262306a36Sopenharmony_ci	case SND_SOC_DAIFMT_NB_NF:
17362306a36Sopenharmony_ci		break;
17462306a36Sopenharmony_ci	default:
17562306a36Sopenharmony_ci		dev_err(component->dev, "Invalid DAI clock signal polarity\n");
17662306a36Sopenharmony_ci		return -EINVAL;
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/* interface format */
18062306a36Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
18162306a36Sopenharmony_ci	case SND_SOC_DAIFMT_I2S:
18262306a36Sopenharmony_ci		serial_format |= TAS6424_SAP_I2S;
18362306a36Sopenharmony_ci		break;
18462306a36Sopenharmony_ci	case SND_SOC_DAIFMT_DSP_A:
18562306a36Sopenharmony_ci		serial_format |= TAS6424_SAP_DSP;
18662306a36Sopenharmony_ci		break;
18762306a36Sopenharmony_ci	case SND_SOC_DAIFMT_DSP_B:
18862306a36Sopenharmony_ci		/*
18962306a36Sopenharmony_ci		 * We can use the fact that the TAS6424 does not care about the
19062306a36Sopenharmony_ci		 * LRCLK duty cycle during TDM to receive DSP_B formatted data
19162306a36Sopenharmony_ci		 * in LEFTJ mode (no delaying of the 1st data bit).
19262306a36Sopenharmony_ci		 */
19362306a36Sopenharmony_ci		serial_format |= TAS6424_SAP_LEFTJ;
19462306a36Sopenharmony_ci		break;
19562306a36Sopenharmony_ci	case SND_SOC_DAIFMT_LEFT_J:
19662306a36Sopenharmony_ci		serial_format |= TAS6424_SAP_LEFTJ;
19762306a36Sopenharmony_ci		break;
19862306a36Sopenharmony_ci	default:
19962306a36Sopenharmony_ci		dev_err(component->dev, "Invalid DAI interface format\n");
20062306a36Sopenharmony_ci		return -EINVAL;
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	snd_soc_component_update_bits(component, TAS6424_SAP_CTRL,
20462306a36Sopenharmony_ci			    TAS6424_SAP_FMT_MASK, serial_format);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	return 0;
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic int tas6424_set_dai_tdm_slot(struct snd_soc_dai *dai,
21062306a36Sopenharmony_ci				    unsigned int tx_mask, unsigned int rx_mask,
21162306a36Sopenharmony_ci				    int slots, int slot_width)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	struct snd_soc_component *component = dai->component;
21462306a36Sopenharmony_ci	unsigned int first_slot, last_slot;
21562306a36Sopenharmony_ci	bool sap_tdm_slot_last;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	dev_dbg(component->dev, "%s() tx_mask=%d rx_mask=%d\n", __func__,
21862306a36Sopenharmony_ci		tx_mask, rx_mask);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	if (!tx_mask || !rx_mask)
22162306a36Sopenharmony_ci		return 0; /* nothing needed to disable TDM mode */
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	/*
22462306a36Sopenharmony_ci	 * Determine the first slot and last slot that is being requested so
22562306a36Sopenharmony_ci	 * we'll be able to more easily enforce certain constraints as the
22662306a36Sopenharmony_ci	 * TAS6424's TDM interface is not fully configurable.
22762306a36Sopenharmony_ci	 */
22862306a36Sopenharmony_ci	first_slot = __ffs(tx_mask);
22962306a36Sopenharmony_ci	last_slot = __fls(rx_mask);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	if (last_slot - first_slot != 4) {
23262306a36Sopenharmony_ci		dev_err(component->dev, "tdm mask must cover 4 contiguous slots\n");
23362306a36Sopenharmony_ci		return -EINVAL;
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	switch (first_slot) {
23762306a36Sopenharmony_ci	case 0:
23862306a36Sopenharmony_ci		sap_tdm_slot_last = false;
23962306a36Sopenharmony_ci		break;
24062306a36Sopenharmony_ci	case 4:
24162306a36Sopenharmony_ci		sap_tdm_slot_last = true;
24262306a36Sopenharmony_ci		break;
24362306a36Sopenharmony_ci	default:
24462306a36Sopenharmony_ci		dev_err(component->dev, "tdm mask must start at slot 0 or 4\n");
24562306a36Sopenharmony_ci		return -EINVAL;
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	snd_soc_component_update_bits(component, TAS6424_SAP_CTRL, TAS6424_SAP_TDM_SLOT_LAST,
24962306a36Sopenharmony_ci			    sap_tdm_slot_last ? TAS6424_SAP_TDM_SLOT_LAST : 0);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	return 0;
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic int tas6424_mute(struct snd_soc_dai *dai, int mute, int direction)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	struct snd_soc_component *component = dai->component;
25762306a36Sopenharmony_ci	struct tas6424_data *tas6424 = snd_soc_component_get_drvdata(component);
25862306a36Sopenharmony_ci	unsigned int val;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	dev_dbg(component->dev, "%s() mute=%d\n", __func__, mute);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	if (tas6424->mute_gpio) {
26362306a36Sopenharmony_ci		gpiod_set_value_cansleep(tas6424->mute_gpio, mute);
26462306a36Sopenharmony_ci		return 0;
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	if (mute)
26862306a36Sopenharmony_ci		val = TAS6424_ALL_STATE_MUTE;
26962306a36Sopenharmony_ci	else
27062306a36Sopenharmony_ci		val = TAS6424_ALL_STATE_PLAY;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	snd_soc_component_write(component, TAS6424_CH_STATE_CTRL, val);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	return 0;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic int tas6424_power_off(struct snd_soc_component *component)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	struct tas6424_data *tas6424 = snd_soc_component_get_drvdata(component);
28062306a36Sopenharmony_ci	int ret;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	snd_soc_component_write(component, TAS6424_CH_STATE_CTRL, TAS6424_ALL_STATE_HIZ);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	regcache_cache_only(tas6424->regmap, true);
28562306a36Sopenharmony_ci	regcache_mark_dirty(tas6424->regmap);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	ret = regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies),
28862306a36Sopenharmony_ci				     tas6424->supplies);
28962306a36Sopenharmony_ci	if (ret < 0) {
29062306a36Sopenharmony_ci		dev_err(component->dev, "failed to disable supplies: %d\n", ret);
29162306a36Sopenharmony_ci		return ret;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	return 0;
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic int tas6424_power_on(struct snd_soc_component *component)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	struct tas6424_data *tas6424 = snd_soc_component_get_drvdata(component);
30062306a36Sopenharmony_ci	int ret;
30162306a36Sopenharmony_ci	u8 chan_states;
30262306a36Sopenharmony_ci	int no_auto_diags = 0;
30362306a36Sopenharmony_ci	unsigned int reg_val;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if (!regmap_read(tas6424->regmap, TAS6424_DC_DIAG_CTRL1, &reg_val))
30662306a36Sopenharmony_ci		no_auto_diags = reg_val & TAS6424_LDGBYPASS_MASK;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	ret = regulator_bulk_enable(ARRAY_SIZE(tas6424->supplies),
30962306a36Sopenharmony_ci				    tas6424->supplies);
31062306a36Sopenharmony_ci	if (ret < 0) {
31162306a36Sopenharmony_ci		dev_err(component->dev, "failed to enable supplies: %d\n", ret);
31262306a36Sopenharmony_ci		return ret;
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	regcache_cache_only(tas6424->regmap, false);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	ret = regcache_sync(tas6424->regmap);
31862306a36Sopenharmony_ci	if (ret < 0) {
31962306a36Sopenharmony_ci		dev_err(component->dev, "failed to sync regcache: %d\n", ret);
32062306a36Sopenharmony_ci		return ret;
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	if (tas6424->mute_gpio) {
32462306a36Sopenharmony_ci		gpiod_set_value_cansleep(tas6424->mute_gpio, 0);
32562306a36Sopenharmony_ci		/*
32662306a36Sopenharmony_ci		 * channels are muted via the mute pin.  Don't also mute
32762306a36Sopenharmony_ci		 * them via the registers so that subsequent register
32862306a36Sopenharmony_ci		 * access is not necessary to un-mute the channels
32962306a36Sopenharmony_ci		 */
33062306a36Sopenharmony_ci		chan_states = TAS6424_ALL_STATE_PLAY;
33162306a36Sopenharmony_ci	} else {
33262306a36Sopenharmony_ci		chan_states = TAS6424_ALL_STATE_MUTE;
33362306a36Sopenharmony_ci	}
33462306a36Sopenharmony_ci	snd_soc_component_write(component, TAS6424_CH_STATE_CTRL, chan_states);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	/* any time we come out of HIZ, the output channels automatically run DC
33762306a36Sopenharmony_ci	 * load diagnostics if autodiagnotics are enabled. wait here until this
33862306a36Sopenharmony_ci	 * completes.
33962306a36Sopenharmony_ci	 */
34062306a36Sopenharmony_ci	if (!no_auto_diags)
34162306a36Sopenharmony_ci		msleep(230);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	return 0;
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic int tas6424_set_bias_level(struct snd_soc_component *component,
34762306a36Sopenharmony_ci				  enum snd_soc_bias_level level)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	dev_dbg(component->dev, "%s() level=%d\n", __func__, level);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	switch (level) {
35262306a36Sopenharmony_ci	case SND_SOC_BIAS_ON:
35362306a36Sopenharmony_ci	case SND_SOC_BIAS_PREPARE:
35462306a36Sopenharmony_ci		break;
35562306a36Sopenharmony_ci	case SND_SOC_BIAS_STANDBY:
35662306a36Sopenharmony_ci		if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
35762306a36Sopenharmony_ci			tas6424_power_on(component);
35862306a36Sopenharmony_ci		break;
35962306a36Sopenharmony_ci	case SND_SOC_BIAS_OFF:
36062306a36Sopenharmony_ci		tas6424_power_off(component);
36162306a36Sopenharmony_ci		break;
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	return 0;
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic struct snd_soc_component_driver soc_codec_dev_tas6424 = {
36862306a36Sopenharmony_ci	.set_bias_level		= tas6424_set_bias_level,
36962306a36Sopenharmony_ci	.controls		= tas6424_snd_controls,
37062306a36Sopenharmony_ci	.num_controls		= ARRAY_SIZE(tas6424_snd_controls),
37162306a36Sopenharmony_ci	.dapm_widgets		= tas6424_dapm_widgets,
37262306a36Sopenharmony_ci	.num_dapm_widgets	= ARRAY_SIZE(tas6424_dapm_widgets),
37362306a36Sopenharmony_ci	.dapm_routes		= tas6424_audio_map,
37462306a36Sopenharmony_ci	.num_dapm_routes	= ARRAY_SIZE(tas6424_audio_map),
37562306a36Sopenharmony_ci	.use_pmdown_time	= 1,
37662306a36Sopenharmony_ci	.endianness		= 1,
37762306a36Sopenharmony_ci};
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic const struct snd_soc_dai_ops tas6424_speaker_dai_ops = {
38062306a36Sopenharmony_ci	.hw_params	= tas6424_hw_params,
38162306a36Sopenharmony_ci	.set_fmt	= tas6424_set_dai_fmt,
38262306a36Sopenharmony_ci	.set_tdm_slot	= tas6424_set_dai_tdm_slot,
38362306a36Sopenharmony_ci	.mute_stream	= tas6424_mute,
38462306a36Sopenharmony_ci	.no_capture_mute = 1,
38562306a36Sopenharmony_ci};
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cistatic struct snd_soc_dai_driver tas6424_dai[] = {
38862306a36Sopenharmony_ci	{
38962306a36Sopenharmony_ci		.name = "tas6424-amplifier",
39062306a36Sopenharmony_ci		.playback = {
39162306a36Sopenharmony_ci			.stream_name = "Playback",
39262306a36Sopenharmony_ci			.channels_min = 1,
39362306a36Sopenharmony_ci			.channels_max = 4,
39462306a36Sopenharmony_ci			.rates = TAS6424_RATES,
39562306a36Sopenharmony_ci			.formats = TAS6424_FORMATS,
39662306a36Sopenharmony_ci		},
39762306a36Sopenharmony_ci		.ops = &tas6424_speaker_dai_ops,
39862306a36Sopenharmony_ci	},
39962306a36Sopenharmony_ci};
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic void tas6424_fault_check_work(struct work_struct *work)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	struct tas6424_data *tas6424 = container_of(work, struct tas6424_data,
40462306a36Sopenharmony_ci						    fault_check_work.work);
40562306a36Sopenharmony_ci	struct device *dev = tas6424->dev;
40662306a36Sopenharmony_ci	unsigned int reg;
40762306a36Sopenharmony_ci	int ret;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	ret = regmap_read(tas6424->regmap, TAS6424_CHANNEL_FAULT, &reg);
41062306a36Sopenharmony_ci	if (ret < 0) {
41162306a36Sopenharmony_ci		dev_err(dev, "failed to read CHANNEL_FAULT register: %d\n", ret);
41262306a36Sopenharmony_ci		goto out;
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	if (!reg) {
41662306a36Sopenharmony_ci		tas6424->last_cfault = reg;
41762306a36Sopenharmony_ci		goto check_global_fault1_reg;
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	/*
42162306a36Sopenharmony_ci	 * Only flag errors once for a given occurrence. This is needed as
42262306a36Sopenharmony_ci	 * the TAS6424 will take time clearing the fault condition internally
42362306a36Sopenharmony_ci	 * during which we don't want to bombard the system with the same
42462306a36Sopenharmony_ci	 * error message over and over.
42562306a36Sopenharmony_ci	 */
42662306a36Sopenharmony_ci	if ((reg & TAS6424_FAULT_OC_CH1) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH1))
42762306a36Sopenharmony_ci		dev_crit(dev, "experienced a channel 1 overcurrent fault\n");
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if ((reg & TAS6424_FAULT_OC_CH2) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH2))
43062306a36Sopenharmony_ci		dev_crit(dev, "experienced a channel 2 overcurrent fault\n");
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	if ((reg & TAS6424_FAULT_OC_CH3) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH3))
43362306a36Sopenharmony_ci		dev_crit(dev, "experienced a channel 3 overcurrent fault\n");
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if ((reg & TAS6424_FAULT_OC_CH4) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH4))
43662306a36Sopenharmony_ci		dev_crit(dev, "experienced a channel 4 overcurrent fault\n");
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	if ((reg & TAS6424_FAULT_DC_CH1) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH1))
43962306a36Sopenharmony_ci		dev_crit(dev, "experienced a channel 1 DC fault\n");
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	if ((reg & TAS6424_FAULT_DC_CH2) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH2))
44262306a36Sopenharmony_ci		dev_crit(dev, "experienced a channel 2 DC fault\n");
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	if ((reg & TAS6424_FAULT_DC_CH3) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH3))
44562306a36Sopenharmony_ci		dev_crit(dev, "experienced a channel 3 DC fault\n");
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	if ((reg & TAS6424_FAULT_DC_CH4) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH4))
44862306a36Sopenharmony_ci		dev_crit(dev, "experienced a channel 4 DC fault\n");
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	/* Store current fault1 value so we can detect any changes next time */
45162306a36Sopenharmony_ci	tas6424->last_cfault = reg;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_cicheck_global_fault1_reg:
45462306a36Sopenharmony_ci	ret = regmap_read(tas6424->regmap, TAS6424_GLOB_FAULT1, &reg);
45562306a36Sopenharmony_ci	if (ret < 0) {
45662306a36Sopenharmony_ci		dev_err(dev, "failed to read GLOB_FAULT1 register: %d\n", ret);
45762306a36Sopenharmony_ci		goto out;
45862306a36Sopenharmony_ci	}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	/*
46162306a36Sopenharmony_ci	 * Ignore any clock faults as there is no clean way to check for them.
46262306a36Sopenharmony_ci	 * We would need to start checking for those faults *after* the SAIF
46362306a36Sopenharmony_ci	 * stream has been setup, and stop checking *before* the stream is
46462306a36Sopenharmony_ci	 * stopped to avoid any false-positives. However there are no
46562306a36Sopenharmony_ci	 * appropriate hooks to monitor these events.
46662306a36Sopenharmony_ci	 */
46762306a36Sopenharmony_ci	reg &= TAS6424_FAULT_PVDD_OV |
46862306a36Sopenharmony_ci	       TAS6424_FAULT_VBAT_OV |
46962306a36Sopenharmony_ci	       TAS6424_FAULT_PVDD_UV |
47062306a36Sopenharmony_ci	       TAS6424_FAULT_VBAT_UV;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	if (!reg) {
47362306a36Sopenharmony_ci		tas6424->last_fault1 = reg;
47462306a36Sopenharmony_ci		goto check_global_fault2_reg;
47562306a36Sopenharmony_ci	}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	if ((reg & TAS6424_FAULT_PVDD_OV) && !(tas6424->last_fault1 & TAS6424_FAULT_PVDD_OV))
47862306a36Sopenharmony_ci		dev_crit(dev, "experienced a PVDD overvoltage fault\n");
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	if ((reg & TAS6424_FAULT_VBAT_OV) && !(tas6424->last_fault1 & TAS6424_FAULT_VBAT_OV))
48162306a36Sopenharmony_ci		dev_crit(dev, "experienced a VBAT overvoltage fault\n");
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	if ((reg & TAS6424_FAULT_PVDD_UV) && !(tas6424->last_fault1 & TAS6424_FAULT_PVDD_UV))
48462306a36Sopenharmony_ci		dev_crit(dev, "experienced a PVDD undervoltage fault\n");
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	if ((reg & TAS6424_FAULT_VBAT_UV) && !(tas6424->last_fault1 & TAS6424_FAULT_VBAT_UV))
48762306a36Sopenharmony_ci		dev_crit(dev, "experienced a VBAT undervoltage fault\n");
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	/* Store current fault1 value so we can detect any changes next time */
49062306a36Sopenharmony_ci	tas6424->last_fault1 = reg;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_cicheck_global_fault2_reg:
49362306a36Sopenharmony_ci	ret = regmap_read(tas6424->regmap, TAS6424_GLOB_FAULT2, &reg);
49462306a36Sopenharmony_ci	if (ret < 0) {
49562306a36Sopenharmony_ci		dev_err(dev, "failed to read GLOB_FAULT2 register: %d\n", ret);
49662306a36Sopenharmony_ci		goto out;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	reg &= TAS6424_FAULT_OTSD |
50062306a36Sopenharmony_ci	       TAS6424_FAULT_OTSD_CH1 |
50162306a36Sopenharmony_ci	       TAS6424_FAULT_OTSD_CH2 |
50262306a36Sopenharmony_ci	       TAS6424_FAULT_OTSD_CH3 |
50362306a36Sopenharmony_ci	       TAS6424_FAULT_OTSD_CH4;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	if (!reg) {
50662306a36Sopenharmony_ci		tas6424->last_fault2 = reg;
50762306a36Sopenharmony_ci		goto check_warn_reg;
50862306a36Sopenharmony_ci	}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	if ((reg & TAS6424_FAULT_OTSD) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD))
51162306a36Sopenharmony_ci		dev_crit(dev, "experienced a global overtemp shutdown\n");
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	if ((reg & TAS6424_FAULT_OTSD_CH1) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH1))
51462306a36Sopenharmony_ci		dev_crit(dev, "experienced an overtemp shutdown on CH1\n");
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	if ((reg & TAS6424_FAULT_OTSD_CH2) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH2))
51762306a36Sopenharmony_ci		dev_crit(dev, "experienced an overtemp shutdown on CH2\n");
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	if ((reg & TAS6424_FAULT_OTSD_CH3) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH3))
52062306a36Sopenharmony_ci		dev_crit(dev, "experienced an overtemp shutdown on CH3\n");
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if ((reg & TAS6424_FAULT_OTSD_CH4) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH4))
52362306a36Sopenharmony_ci		dev_crit(dev, "experienced an overtemp shutdown on CH4\n");
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	/* Store current fault2 value so we can detect any changes next time */
52662306a36Sopenharmony_ci	tas6424->last_fault2 = reg;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_cicheck_warn_reg:
52962306a36Sopenharmony_ci	ret = regmap_read(tas6424->regmap, TAS6424_WARN, &reg);
53062306a36Sopenharmony_ci	if (ret < 0) {
53162306a36Sopenharmony_ci		dev_err(dev, "failed to read WARN register: %d\n", ret);
53262306a36Sopenharmony_ci		goto out;
53362306a36Sopenharmony_ci	}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	reg &= TAS6424_WARN_VDD_UV |
53662306a36Sopenharmony_ci	       TAS6424_WARN_VDD_POR |
53762306a36Sopenharmony_ci	       TAS6424_WARN_VDD_OTW |
53862306a36Sopenharmony_ci	       TAS6424_WARN_VDD_OTW_CH1 |
53962306a36Sopenharmony_ci	       TAS6424_WARN_VDD_OTW_CH2 |
54062306a36Sopenharmony_ci	       TAS6424_WARN_VDD_OTW_CH3 |
54162306a36Sopenharmony_ci	       TAS6424_WARN_VDD_OTW_CH4;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (!reg) {
54462306a36Sopenharmony_ci		tas6424->last_warn = reg;
54562306a36Sopenharmony_ci		goto out;
54662306a36Sopenharmony_ci	}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	if ((reg & TAS6424_WARN_VDD_UV) && !(tas6424->last_warn & TAS6424_WARN_VDD_UV))
54962306a36Sopenharmony_ci		dev_warn(dev, "experienced a VDD under voltage condition\n");
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	if ((reg & TAS6424_WARN_VDD_POR) && !(tas6424->last_warn & TAS6424_WARN_VDD_POR))
55262306a36Sopenharmony_ci		dev_warn(dev, "experienced a VDD POR condition\n");
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	if ((reg & TAS6424_WARN_VDD_OTW) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW))
55562306a36Sopenharmony_ci		dev_warn(dev, "experienced a global overtemp warning\n");
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	if ((reg & TAS6424_WARN_VDD_OTW_CH1) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH1))
55862306a36Sopenharmony_ci		dev_warn(dev, "experienced an overtemp warning on CH1\n");
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	if ((reg & TAS6424_WARN_VDD_OTW_CH2) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH2))
56162306a36Sopenharmony_ci		dev_warn(dev, "experienced an overtemp warning on CH2\n");
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	if ((reg & TAS6424_WARN_VDD_OTW_CH3) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH3))
56462306a36Sopenharmony_ci		dev_warn(dev, "experienced an overtemp warning on CH3\n");
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	if ((reg & TAS6424_WARN_VDD_OTW_CH4) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH4))
56762306a36Sopenharmony_ci		dev_warn(dev, "experienced an overtemp warning on CH4\n");
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	/* Store current warn value so we can detect any changes next time */
57062306a36Sopenharmony_ci	tas6424->last_warn = reg;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	/* Clear any warnings by toggling the CLEAR_FAULT control bit */
57362306a36Sopenharmony_ci	ret = regmap_write_bits(tas6424->regmap, TAS6424_MISC_CTRL3,
57462306a36Sopenharmony_ci				TAS6424_CLEAR_FAULT, TAS6424_CLEAR_FAULT);
57562306a36Sopenharmony_ci	if (ret < 0)
57662306a36Sopenharmony_ci		dev_err(dev, "failed to write MISC_CTRL3 register: %d\n", ret);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	ret = regmap_write_bits(tas6424->regmap, TAS6424_MISC_CTRL3,
57962306a36Sopenharmony_ci				TAS6424_CLEAR_FAULT, 0);
58062306a36Sopenharmony_ci	if (ret < 0)
58162306a36Sopenharmony_ci		dev_err(dev, "failed to write MISC_CTRL3 register: %d\n", ret);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ciout:
58462306a36Sopenharmony_ci	/* Schedule the next fault check at the specified interval */
58562306a36Sopenharmony_ci	schedule_delayed_work(&tas6424->fault_check_work,
58662306a36Sopenharmony_ci			      msecs_to_jiffies(TAS6424_FAULT_CHECK_INTERVAL));
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_cistatic const struct reg_default tas6424_reg_defaults[] = {
59062306a36Sopenharmony_ci	{ TAS6424_MODE_CTRL,		0x00 },
59162306a36Sopenharmony_ci	{ TAS6424_MISC_CTRL1,		0x32 },
59262306a36Sopenharmony_ci	{ TAS6424_MISC_CTRL2,		0x62 },
59362306a36Sopenharmony_ci	{ TAS6424_SAP_CTRL,		0x04 },
59462306a36Sopenharmony_ci	{ TAS6424_CH_STATE_CTRL,	0x55 },
59562306a36Sopenharmony_ci	{ TAS6424_CH1_VOL_CTRL,		0xcf },
59662306a36Sopenharmony_ci	{ TAS6424_CH2_VOL_CTRL,		0xcf },
59762306a36Sopenharmony_ci	{ TAS6424_CH3_VOL_CTRL,		0xcf },
59862306a36Sopenharmony_ci	{ TAS6424_CH4_VOL_CTRL,		0xcf },
59962306a36Sopenharmony_ci	{ TAS6424_DC_DIAG_CTRL1,	0x00 },
60062306a36Sopenharmony_ci	{ TAS6424_DC_DIAG_CTRL2,	0x11 },
60162306a36Sopenharmony_ci	{ TAS6424_DC_DIAG_CTRL3,	0x11 },
60262306a36Sopenharmony_ci	{ TAS6424_PIN_CTRL,		0xff },
60362306a36Sopenharmony_ci	{ TAS6424_AC_DIAG_CTRL1,	0x00 },
60462306a36Sopenharmony_ci	{ TAS6424_MISC_CTRL3,		0x00 },
60562306a36Sopenharmony_ci	{ TAS6424_CLIP_CTRL,		0x01 },
60662306a36Sopenharmony_ci	{ TAS6424_CLIP_WINDOW,		0x14 },
60762306a36Sopenharmony_ci	{ TAS6424_CLIP_WARN,		0x00 },
60862306a36Sopenharmony_ci	{ TAS6424_CBC_STAT,		0x00 },
60962306a36Sopenharmony_ci	{ TAS6424_MISC_CTRL4,		0x40 },
61062306a36Sopenharmony_ci};
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic bool tas6424_is_writable_reg(struct device *dev, unsigned int reg)
61362306a36Sopenharmony_ci{
61462306a36Sopenharmony_ci	switch (reg) {
61562306a36Sopenharmony_ci	case TAS6424_MODE_CTRL:
61662306a36Sopenharmony_ci	case TAS6424_MISC_CTRL1:
61762306a36Sopenharmony_ci	case TAS6424_MISC_CTRL2:
61862306a36Sopenharmony_ci	case TAS6424_SAP_CTRL:
61962306a36Sopenharmony_ci	case TAS6424_CH_STATE_CTRL:
62062306a36Sopenharmony_ci	case TAS6424_CH1_VOL_CTRL:
62162306a36Sopenharmony_ci	case TAS6424_CH2_VOL_CTRL:
62262306a36Sopenharmony_ci	case TAS6424_CH3_VOL_CTRL:
62362306a36Sopenharmony_ci	case TAS6424_CH4_VOL_CTRL:
62462306a36Sopenharmony_ci	case TAS6424_DC_DIAG_CTRL1:
62562306a36Sopenharmony_ci	case TAS6424_DC_DIAG_CTRL2:
62662306a36Sopenharmony_ci	case TAS6424_DC_DIAG_CTRL3:
62762306a36Sopenharmony_ci	case TAS6424_PIN_CTRL:
62862306a36Sopenharmony_ci	case TAS6424_AC_DIAG_CTRL1:
62962306a36Sopenharmony_ci	case TAS6424_MISC_CTRL3:
63062306a36Sopenharmony_ci	case TAS6424_CLIP_CTRL:
63162306a36Sopenharmony_ci	case TAS6424_CLIP_WINDOW:
63262306a36Sopenharmony_ci	case TAS6424_CLIP_WARN:
63362306a36Sopenharmony_ci	case TAS6424_CBC_STAT:
63462306a36Sopenharmony_ci	case TAS6424_MISC_CTRL4:
63562306a36Sopenharmony_ci		return true;
63662306a36Sopenharmony_ci	default:
63762306a36Sopenharmony_ci		return false;
63862306a36Sopenharmony_ci	}
63962306a36Sopenharmony_ci}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_cistatic bool tas6424_is_volatile_reg(struct device *dev, unsigned int reg)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	switch (reg) {
64462306a36Sopenharmony_ci	case TAS6424_DC_LOAD_DIAG_REP12:
64562306a36Sopenharmony_ci	case TAS6424_DC_LOAD_DIAG_REP34:
64662306a36Sopenharmony_ci	case TAS6424_DC_LOAD_DIAG_REPLO:
64762306a36Sopenharmony_ci	case TAS6424_CHANNEL_STATE:
64862306a36Sopenharmony_ci	case TAS6424_CHANNEL_FAULT:
64962306a36Sopenharmony_ci	case TAS6424_GLOB_FAULT1:
65062306a36Sopenharmony_ci	case TAS6424_GLOB_FAULT2:
65162306a36Sopenharmony_ci	case TAS6424_WARN:
65262306a36Sopenharmony_ci	case TAS6424_AC_LOAD_DIAG_REP1:
65362306a36Sopenharmony_ci	case TAS6424_AC_LOAD_DIAG_REP2:
65462306a36Sopenharmony_ci	case TAS6424_AC_LOAD_DIAG_REP3:
65562306a36Sopenharmony_ci	case TAS6424_AC_LOAD_DIAG_REP4:
65662306a36Sopenharmony_ci		return true;
65762306a36Sopenharmony_ci	default:
65862306a36Sopenharmony_ci		return false;
65962306a36Sopenharmony_ci	}
66062306a36Sopenharmony_ci}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cistatic const struct regmap_config tas6424_regmap_config = {
66362306a36Sopenharmony_ci	.reg_bits = 8,
66462306a36Sopenharmony_ci	.val_bits = 8,
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	.writeable_reg = tas6424_is_writable_reg,
66762306a36Sopenharmony_ci	.volatile_reg = tas6424_is_volatile_reg,
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	.max_register = TAS6424_MAX,
67062306a36Sopenharmony_ci	.reg_defaults = tas6424_reg_defaults,
67162306a36Sopenharmony_ci	.num_reg_defaults = ARRAY_SIZE(tas6424_reg_defaults),
67262306a36Sopenharmony_ci	.cache_type = REGCACHE_RBTREE,
67362306a36Sopenharmony_ci};
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_OF)
67662306a36Sopenharmony_cistatic const struct of_device_id tas6424_of_ids[] = {
67762306a36Sopenharmony_ci	{ .compatible = "ti,tas6424", },
67862306a36Sopenharmony_ci	{ },
67962306a36Sopenharmony_ci};
68062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tas6424_of_ids);
68162306a36Sopenharmony_ci#endif
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_cistatic int tas6424_i2c_probe(struct i2c_client *client)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	struct device *dev = &client->dev;
68662306a36Sopenharmony_ci	struct tas6424_data *tas6424;
68762306a36Sopenharmony_ci	int ret;
68862306a36Sopenharmony_ci	int i;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	tas6424 = devm_kzalloc(dev, sizeof(*tas6424), GFP_KERNEL);
69162306a36Sopenharmony_ci	if (!tas6424)
69262306a36Sopenharmony_ci		return -ENOMEM;
69362306a36Sopenharmony_ci	dev_set_drvdata(dev, tas6424);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	tas6424->dev = dev;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	tas6424->regmap = devm_regmap_init_i2c(client, &tas6424_regmap_config);
69862306a36Sopenharmony_ci	if (IS_ERR(tas6424->regmap)) {
69962306a36Sopenharmony_ci		ret = PTR_ERR(tas6424->regmap);
70062306a36Sopenharmony_ci		dev_err(dev, "unable to allocate register map: %d\n", ret);
70162306a36Sopenharmony_ci		return ret;
70262306a36Sopenharmony_ci	}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	/*
70562306a36Sopenharmony_ci	 * Get control of the standby pin and set it LOW to take the codec
70662306a36Sopenharmony_ci	 * out of the stand-by mode.
70762306a36Sopenharmony_ci	 * Note: The actual pin polarity is taken care of in the GPIO lib
70862306a36Sopenharmony_ci	 * according the polarity specified in the DTS.
70962306a36Sopenharmony_ci	 */
71062306a36Sopenharmony_ci	tas6424->standby_gpio = devm_gpiod_get_optional(dev, "standby",
71162306a36Sopenharmony_ci						      GPIOD_OUT_LOW);
71262306a36Sopenharmony_ci	if (IS_ERR(tas6424->standby_gpio)) {
71362306a36Sopenharmony_ci		if (PTR_ERR(tas6424->standby_gpio) == -EPROBE_DEFER)
71462306a36Sopenharmony_ci			return -EPROBE_DEFER;
71562306a36Sopenharmony_ci		dev_info(dev, "failed to get standby GPIO: %ld\n",
71662306a36Sopenharmony_ci			PTR_ERR(tas6424->standby_gpio));
71762306a36Sopenharmony_ci		tas6424->standby_gpio = NULL;
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	/*
72162306a36Sopenharmony_ci	 * Get control of the mute pin and set it HIGH in order to start with
72262306a36Sopenharmony_ci	 * all the output muted.
72362306a36Sopenharmony_ci	 * Note: The actual pin polarity is taken care of in the GPIO lib
72462306a36Sopenharmony_ci	 * according the polarity specified in the DTS.
72562306a36Sopenharmony_ci	 */
72662306a36Sopenharmony_ci	tas6424->mute_gpio = devm_gpiod_get_optional(dev, "mute",
72762306a36Sopenharmony_ci						      GPIOD_OUT_HIGH);
72862306a36Sopenharmony_ci	if (IS_ERR(tas6424->mute_gpio)) {
72962306a36Sopenharmony_ci		if (PTR_ERR(tas6424->mute_gpio) == -EPROBE_DEFER)
73062306a36Sopenharmony_ci			return -EPROBE_DEFER;
73162306a36Sopenharmony_ci		dev_info(dev, "failed to get nmute GPIO: %ld\n",
73262306a36Sopenharmony_ci			PTR_ERR(tas6424->mute_gpio));
73362306a36Sopenharmony_ci		tas6424->mute_gpio = NULL;
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(tas6424->supplies); i++)
73762306a36Sopenharmony_ci		tas6424->supplies[i].supply = tas6424_supply_names[i];
73862306a36Sopenharmony_ci	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(tas6424->supplies),
73962306a36Sopenharmony_ci				      tas6424->supplies);
74062306a36Sopenharmony_ci	if (ret) {
74162306a36Sopenharmony_ci		dev_err(dev, "unable to request supplies: %d\n", ret);
74262306a36Sopenharmony_ci		return ret;
74362306a36Sopenharmony_ci	}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	ret = regulator_bulk_enable(ARRAY_SIZE(tas6424->supplies),
74662306a36Sopenharmony_ci				    tas6424->supplies);
74762306a36Sopenharmony_ci	if (ret) {
74862306a36Sopenharmony_ci		dev_err(dev, "unable to enable supplies: %d\n", ret);
74962306a36Sopenharmony_ci		return ret;
75062306a36Sopenharmony_ci	}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	/* Reset device to establish well-defined startup state */
75362306a36Sopenharmony_ci	ret = regmap_update_bits(tas6424->regmap, TAS6424_MODE_CTRL,
75462306a36Sopenharmony_ci				 TAS6424_RESET, TAS6424_RESET);
75562306a36Sopenharmony_ci	if (ret) {
75662306a36Sopenharmony_ci		dev_err(dev, "unable to reset device: %d\n", ret);
75762306a36Sopenharmony_ci		goto disable_regs;
75862306a36Sopenharmony_ci	}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	INIT_DELAYED_WORK(&tas6424->fault_check_work, tas6424_fault_check_work);
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	ret = devm_snd_soc_register_component(dev, &soc_codec_dev_tas6424,
76362306a36Sopenharmony_ci				     tas6424_dai, ARRAY_SIZE(tas6424_dai));
76462306a36Sopenharmony_ci	if (ret < 0) {
76562306a36Sopenharmony_ci		dev_err(dev, "unable to register codec: %d\n", ret);
76662306a36Sopenharmony_ci		goto disable_regs;
76762306a36Sopenharmony_ci	}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	return 0;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_cidisable_regs:
77262306a36Sopenharmony_ci	regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies), tas6424->supplies);
77362306a36Sopenharmony_ci	return ret;
77462306a36Sopenharmony_ci}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_cistatic void tas6424_i2c_remove(struct i2c_client *client)
77762306a36Sopenharmony_ci{
77862306a36Sopenharmony_ci	struct device *dev = &client->dev;
77962306a36Sopenharmony_ci	struct tas6424_data *tas6424 = dev_get_drvdata(dev);
78062306a36Sopenharmony_ci	int ret;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	cancel_delayed_work_sync(&tas6424->fault_check_work);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	/* put the codec in stand-by */
78562306a36Sopenharmony_ci	if (tas6424->standby_gpio)
78662306a36Sopenharmony_ci		gpiod_set_value_cansleep(tas6424->standby_gpio, 1);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	ret = regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies),
78962306a36Sopenharmony_ci				     tas6424->supplies);
79062306a36Sopenharmony_ci	if (ret < 0)
79162306a36Sopenharmony_ci		dev_err(dev, "unable to disable supplies: %d\n", ret);
79262306a36Sopenharmony_ci}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_cistatic const struct i2c_device_id tas6424_i2c_ids[] = {
79562306a36Sopenharmony_ci	{ "tas6424", 0 },
79662306a36Sopenharmony_ci	{ }
79762306a36Sopenharmony_ci};
79862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tas6424_i2c_ids);
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_cistatic struct i2c_driver tas6424_i2c_driver = {
80162306a36Sopenharmony_ci	.driver = {
80262306a36Sopenharmony_ci		.name = "tas6424",
80362306a36Sopenharmony_ci		.of_match_table = of_match_ptr(tas6424_of_ids),
80462306a36Sopenharmony_ci	},
80562306a36Sopenharmony_ci	.probe = tas6424_i2c_probe,
80662306a36Sopenharmony_ci	.remove = tas6424_i2c_remove,
80762306a36Sopenharmony_ci	.id_table = tas6424_i2c_ids,
80862306a36Sopenharmony_ci};
80962306a36Sopenharmony_cimodule_i2c_driver(tas6424_i2c_driver);
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ciMODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");
81262306a36Sopenharmony_ciMODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
81362306a36Sopenharmony_ciMODULE_DESCRIPTION("TAS6424 Audio amplifier driver");
81462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
815