162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * tas5720.c - ALSA SoC Texas Instruments TAS5720 Mono Audio Amplifier
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C)2015-2016 Texas Instruments Incorporated -  https://www.ti.com
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Andreas Dannenberg <dannenberg@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
1962306a36Sopenharmony_ci#include <sound/pcm.h>
2062306a36Sopenharmony_ci#include <sound/pcm_params.h>
2162306a36Sopenharmony_ci#include <sound/soc.h>
2262306a36Sopenharmony_ci#include <sound/soc-dapm.h>
2362306a36Sopenharmony_ci#include <sound/tlv.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include "tas5720.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/* Define how often to check (and clear) the fault status register (in ms) */
2862306a36Sopenharmony_ci#define TAS5720_FAULT_CHECK_INTERVAL		200
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cienum tas572x_type {
3162306a36Sopenharmony_ci	TAS5720,
3262306a36Sopenharmony_ci	TAS5720A_Q1,
3362306a36Sopenharmony_ci	TAS5722,
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic const char * const tas5720_supply_names[] = {
3762306a36Sopenharmony_ci	"dvdd",		/* Digital power supply. Connect to 3.3-V supply. */
3862306a36Sopenharmony_ci	"pvdd",		/* Class-D amp and analog power supply (connected). */
3962306a36Sopenharmony_ci};
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define TAS5720_NUM_SUPPLIES	ARRAY_SIZE(tas5720_supply_names)
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistruct tas5720_data {
4462306a36Sopenharmony_ci	struct snd_soc_component *component;
4562306a36Sopenharmony_ci	struct regmap *regmap;
4662306a36Sopenharmony_ci	struct i2c_client *tas5720_client;
4762306a36Sopenharmony_ci	enum tas572x_type devtype;
4862306a36Sopenharmony_ci	struct regulator_bulk_data supplies[TAS5720_NUM_SUPPLIES];
4962306a36Sopenharmony_ci	struct delayed_work fault_check_work;
5062306a36Sopenharmony_ci	unsigned int last_fault;
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic int tas5720_hw_params(struct snd_pcm_substream *substream,
5462306a36Sopenharmony_ci			     struct snd_pcm_hw_params *params,
5562306a36Sopenharmony_ci			     struct snd_soc_dai *dai)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	struct snd_soc_component *component = dai->component;
5862306a36Sopenharmony_ci	unsigned int rate = params_rate(params);
5962306a36Sopenharmony_ci	bool ssz_ds;
6062306a36Sopenharmony_ci	int ret;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	switch (rate) {
6362306a36Sopenharmony_ci	case 44100:
6462306a36Sopenharmony_ci	case 48000:
6562306a36Sopenharmony_ci		ssz_ds = false;
6662306a36Sopenharmony_ci		break;
6762306a36Sopenharmony_ci	case 88200:
6862306a36Sopenharmony_ci	case 96000:
6962306a36Sopenharmony_ci		ssz_ds = true;
7062306a36Sopenharmony_ci		break;
7162306a36Sopenharmony_ci	default:
7262306a36Sopenharmony_ci		dev_err(component->dev, "unsupported sample rate: %u\n", rate);
7362306a36Sopenharmony_ci		return -EINVAL;
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL1_REG,
7762306a36Sopenharmony_ci				  TAS5720_SSZ_DS, ssz_ds);
7862306a36Sopenharmony_ci	if (ret < 0) {
7962306a36Sopenharmony_ci		dev_err(component->dev, "error setting sample rate: %d\n", ret);
8062306a36Sopenharmony_ci		return ret;
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	return 0;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic int tas5720_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	struct snd_soc_component *component = dai->component;
8962306a36Sopenharmony_ci	u8 serial_format;
9062306a36Sopenharmony_ci	int ret;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) {
9362306a36Sopenharmony_ci		dev_vdbg(component->dev, "DAI clocking invalid\n");
9462306a36Sopenharmony_ci		return -EINVAL;
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK |
9862306a36Sopenharmony_ci		       SND_SOC_DAIFMT_INV_MASK)) {
9962306a36Sopenharmony_ci	case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF):
10062306a36Sopenharmony_ci		/* 1st data bit occur one BCLK cycle after the frame sync */
10162306a36Sopenharmony_ci		serial_format = TAS5720_SAIF_I2S;
10262306a36Sopenharmony_ci		break;
10362306a36Sopenharmony_ci	case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF):
10462306a36Sopenharmony_ci		/*
10562306a36Sopenharmony_ci		 * Note that although the TAS5720 does not have a dedicated DSP
10662306a36Sopenharmony_ci		 * mode it doesn't care about the LRCLK duty cycle during TDM
10762306a36Sopenharmony_ci		 * operation. Therefore we can use the device's I2S mode with
10862306a36Sopenharmony_ci		 * its delaying of the 1st data bit to receive DSP_A formatted
10962306a36Sopenharmony_ci		 * data. See device datasheet for additional details.
11062306a36Sopenharmony_ci		 */
11162306a36Sopenharmony_ci		serial_format = TAS5720_SAIF_I2S;
11262306a36Sopenharmony_ci		break;
11362306a36Sopenharmony_ci	case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF):
11462306a36Sopenharmony_ci		/*
11562306a36Sopenharmony_ci		 * Similar to DSP_A, we can use the fact that the TAS5720 does
11662306a36Sopenharmony_ci		 * not care about the LRCLK duty cycle during TDM to receive
11762306a36Sopenharmony_ci		 * DSP_B formatted data in LEFTJ mode (no delaying of the 1st
11862306a36Sopenharmony_ci		 * data bit).
11962306a36Sopenharmony_ci		 */
12062306a36Sopenharmony_ci		serial_format = TAS5720_SAIF_LEFTJ;
12162306a36Sopenharmony_ci		break;
12262306a36Sopenharmony_ci	case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF):
12362306a36Sopenharmony_ci		/* No delay after the frame sync */
12462306a36Sopenharmony_ci		serial_format = TAS5720_SAIF_LEFTJ;
12562306a36Sopenharmony_ci		break;
12662306a36Sopenharmony_ci	default:
12762306a36Sopenharmony_ci		dev_vdbg(component->dev, "DAI Format is not found\n");
12862306a36Sopenharmony_ci		return -EINVAL;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL1_REG,
13262306a36Sopenharmony_ci				  TAS5720_SAIF_FORMAT_MASK,
13362306a36Sopenharmony_ci				  serial_format);
13462306a36Sopenharmony_ci	if (ret < 0) {
13562306a36Sopenharmony_ci		dev_err(component->dev, "error setting SAIF format: %d\n", ret);
13662306a36Sopenharmony_ci		return ret;
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	return 0;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic int tas5720_set_dai_tdm_slot(struct snd_soc_dai *dai,
14362306a36Sopenharmony_ci				    unsigned int tx_mask, unsigned int rx_mask,
14462306a36Sopenharmony_ci				    int slots, int slot_width)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct snd_soc_component *component = dai->component;
14762306a36Sopenharmony_ci	struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component);
14862306a36Sopenharmony_ci	unsigned int first_slot;
14962306a36Sopenharmony_ci	int ret;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (!tx_mask) {
15262306a36Sopenharmony_ci		dev_err(component->dev, "tx masks must not be 0\n");
15362306a36Sopenharmony_ci		return -EINVAL;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	/*
15762306a36Sopenharmony_ci	 * Determine the first slot that is being requested. We will only
15862306a36Sopenharmony_ci	 * use the first slot that is found since the TAS5720 is a mono
15962306a36Sopenharmony_ci	 * amplifier.
16062306a36Sopenharmony_ci	 */
16162306a36Sopenharmony_ci	first_slot = __ffs(tx_mask);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	if (first_slot > 7) {
16462306a36Sopenharmony_ci		dev_err(component->dev, "slot selection out of bounds (%u)\n",
16562306a36Sopenharmony_ci			first_slot);
16662306a36Sopenharmony_ci		return -EINVAL;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	/*
17062306a36Sopenharmony_ci	 * Enable manual TDM slot selection (instead of I2C ID based).
17162306a36Sopenharmony_ci	 * This is not applicable to TAS5720A-Q1.
17262306a36Sopenharmony_ci	 */
17362306a36Sopenharmony_ci	switch (tas5720->devtype) {
17462306a36Sopenharmony_ci	case TAS5720A_Q1:
17562306a36Sopenharmony_ci		break;
17662306a36Sopenharmony_ci	default:
17762306a36Sopenharmony_ci		ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL1_REG,
17862306a36Sopenharmony_ci					  TAS5720_TDM_CFG_SRC, TAS5720_TDM_CFG_SRC);
17962306a36Sopenharmony_ci		if (ret < 0)
18062306a36Sopenharmony_ci			goto error_snd_soc_component_update_bits;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci		/* Configure the TDM slot to process audio from */
18362306a36Sopenharmony_ci		ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL2_REG,
18462306a36Sopenharmony_ci					  TAS5720_TDM_SLOT_SEL_MASK, first_slot);
18562306a36Sopenharmony_ci		if (ret < 0)
18662306a36Sopenharmony_ci			goto error_snd_soc_component_update_bits;
18762306a36Sopenharmony_ci		break;
18862306a36Sopenharmony_ci	}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	/* Configure TDM slot width. This is only applicable to TAS5722. */
19162306a36Sopenharmony_ci	switch (tas5720->devtype) {
19262306a36Sopenharmony_ci	case TAS5722:
19362306a36Sopenharmony_ci		ret = snd_soc_component_update_bits(component, TAS5722_DIGITAL_CTRL2_REG,
19462306a36Sopenharmony_ci						    TAS5722_TDM_SLOT_16B,
19562306a36Sopenharmony_ci						    slot_width == 16 ?
19662306a36Sopenharmony_ci						    TAS5722_TDM_SLOT_16B : 0);
19762306a36Sopenharmony_ci		if (ret < 0)
19862306a36Sopenharmony_ci			goto error_snd_soc_component_update_bits;
19962306a36Sopenharmony_ci		break;
20062306a36Sopenharmony_ci	default:
20162306a36Sopenharmony_ci		break;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	return 0;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cierror_snd_soc_component_update_bits:
20762306a36Sopenharmony_ci	dev_err(component->dev, "error configuring TDM mode: %d\n", ret);
20862306a36Sopenharmony_ci	return ret;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic int tas5720_mute_soc_component(struct snd_soc_component *component, int mute)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component);
21462306a36Sopenharmony_ci	unsigned int reg, mask;
21562306a36Sopenharmony_ci	int ret;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	switch (tas5720->devtype) {
21862306a36Sopenharmony_ci	case TAS5720A_Q1:
21962306a36Sopenharmony_ci		reg = TAS5720_Q1_VOLUME_CTRL_CFG_REG;
22062306a36Sopenharmony_ci		mask = TAS5720_Q1_MUTE;
22162306a36Sopenharmony_ci		break;
22262306a36Sopenharmony_ci	default:
22362306a36Sopenharmony_ci		reg = TAS5720_DIGITAL_CTRL2_REG;
22462306a36Sopenharmony_ci		mask = TAS5720_MUTE;
22562306a36Sopenharmony_ci		break;
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	ret = snd_soc_component_update_bits(component, reg, mask, mute ? mask : 0);
22962306a36Sopenharmony_ci	if (ret < 0) {
23062306a36Sopenharmony_ci		dev_err(component->dev, "error (un-)muting device: %d\n", ret);
23162306a36Sopenharmony_ci		return ret;
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	return 0;
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic int tas5720_mute(struct snd_soc_dai *dai, int mute, int direction)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	return tas5720_mute_soc_component(dai->component, mute);
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic void tas5720_fault_check_work(struct work_struct *work)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	struct tas5720_data *tas5720 = container_of(work, struct tas5720_data,
24562306a36Sopenharmony_ci			fault_check_work.work);
24662306a36Sopenharmony_ci	struct device *dev = tas5720->component->dev;
24762306a36Sopenharmony_ci	unsigned int curr_fault;
24862306a36Sopenharmony_ci	int ret;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	ret = regmap_read(tas5720->regmap, TAS5720_FAULT_REG, &curr_fault);
25162306a36Sopenharmony_ci	if (ret < 0) {
25262306a36Sopenharmony_ci		dev_err(dev, "failed to read FAULT register: %d\n", ret);
25362306a36Sopenharmony_ci		goto out;
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	/* Check/handle all errors except SAIF clock errors */
25762306a36Sopenharmony_ci	curr_fault &= TAS5720_OCE | TAS5720_DCE | TAS5720_OTE;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/*
26062306a36Sopenharmony_ci	 * Only flag errors once for a given occurrence. This is needed as
26162306a36Sopenharmony_ci	 * the TAS5720 will take time clearing the fault condition internally
26262306a36Sopenharmony_ci	 * during which we don't want to bombard the system with the same
26362306a36Sopenharmony_ci	 * error message over and over.
26462306a36Sopenharmony_ci	 */
26562306a36Sopenharmony_ci	if ((curr_fault & TAS5720_OCE) && !(tas5720->last_fault & TAS5720_OCE))
26662306a36Sopenharmony_ci		dev_crit(dev, "experienced an over current hardware fault\n");
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	if ((curr_fault & TAS5720_DCE) && !(tas5720->last_fault & TAS5720_DCE))
26962306a36Sopenharmony_ci		dev_crit(dev, "experienced a DC detection fault\n");
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	if ((curr_fault & TAS5720_OTE) && !(tas5720->last_fault & TAS5720_OTE))
27262306a36Sopenharmony_ci		dev_crit(dev, "experienced an over temperature fault\n");
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	/* Store current fault value so we can detect any changes next time */
27562306a36Sopenharmony_ci	tas5720->last_fault = curr_fault;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	if (!curr_fault)
27862306a36Sopenharmony_ci		goto out;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	/*
28162306a36Sopenharmony_ci	 * Periodically toggle SDZ (shutdown bit) H->L->H to clear any latching
28262306a36Sopenharmony_ci	 * faults as long as a fault condition persists. Always going through
28362306a36Sopenharmony_ci	 * the full sequence no matter the first return value to minimizes
28462306a36Sopenharmony_ci	 * chances for the device to end up in shutdown mode.
28562306a36Sopenharmony_ci	 */
28662306a36Sopenharmony_ci	ret = regmap_write_bits(tas5720->regmap, TAS5720_POWER_CTRL_REG,
28762306a36Sopenharmony_ci				TAS5720_SDZ, 0);
28862306a36Sopenharmony_ci	if (ret < 0)
28962306a36Sopenharmony_ci		dev_err(dev, "failed to write POWER_CTRL register: %d\n", ret);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	ret = regmap_write_bits(tas5720->regmap, TAS5720_POWER_CTRL_REG,
29262306a36Sopenharmony_ci				TAS5720_SDZ, TAS5720_SDZ);
29362306a36Sopenharmony_ci	if (ret < 0)
29462306a36Sopenharmony_ci		dev_err(dev, "failed to write POWER_CTRL register: %d\n", ret);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ciout:
29762306a36Sopenharmony_ci	/* Schedule the next fault check at the specified interval */
29862306a36Sopenharmony_ci	schedule_delayed_work(&tas5720->fault_check_work,
29962306a36Sopenharmony_ci			      msecs_to_jiffies(TAS5720_FAULT_CHECK_INTERVAL));
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic int tas5720_codec_probe(struct snd_soc_component *component)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component);
30562306a36Sopenharmony_ci	unsigned int device_id, expected_device_id;
30662306a36Sopenharmony_ci	int ret;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	tas5720->component = component;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	ret = regulator_bulk_enable(ARRAY_SIZE(tas5720->supplies),
31162306a36Sopenharmony_ci				    tas5720->supplies);
31262306a36Sopenharmony_ci	if (ret != 0) {
31362306a36Sopenharmony_ci		dev_err(component->dev, "failed to enable supplies: %d\n", ret);
31462306a36Sopenharmony_ci		return ret;
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	/*
31862306a36Sopenharmony_ci	 * Take a liberal approach to checking the device ID to allow the
31962306a36Sopenharmony_ci	 * driver to be used even if the device ID does not match, however
32062306a36Sopenharmony_ci	 * issue a warning if there is a mismatch.
32162306a36Sopenharmony_ci	 */
32262306a36Sopenharmony_ci	ret = regmap_read(tas5720->regmap, TAS5720_DEVICE_ID_REG, &device_id);
32362306a36Sopenharmony_ci	if (ret < 0) {
32462306a36Sopenharmony_ci		dev_err(component->dev, "failed to read device ID register: %d\n",
32562306a36Sopenharmony_ci			ret);
32662306a36Sopenharmony_ci		goto probe_fail;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	switch (tas5720->devtype) {
33062306a36Sopenharmony_ci	case TAS5720:
33162306a36Sopenharmony_ci		expected_device_id = TAS5720_DEVICE_ID;
33262306a36Sopenharmony_ci		break;
33362306a36Sopenharmony_ci	case TAS5720A_Q1:
33462306a36Sopenharmony_ci		expected_device_id = TAS5720A_Q1_DEVICE_ID;
33562306a36Sopenharmony_ci		break;
33662306a36Sopenharmony_ci	case TAS5722:
33762306a36Sopenharmony_ci		expected_device_id = TAS5722_DEVICE_ID;
33862306a36Sopenharmony_ci		break;
33962306a36Sopenharmony_ci	default:
34062306a36Sopenharmony_ci		dev_err(component->dev, "unexpected private driver data\n");
34162306a36Sopenharmony_ci		ret = -EINVAL;
34262306a36Sopenharmony_ci		goto probe_fail;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	if (device_id != expected_device_id)
34662306a36Sopenharmony_ci		dev_warn(component->dev, "wrong device ID. expected: %u read: %u\n",
34762306a36Sopenharmony_ci			 expected_device_id, device_id);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	/* Set device to mute */
35062306a36Sopenharmony_ci	ret = tas5720_mute_soc_component(component, 1);
35162306a36Sopenharmony_ci	if (ret < 0)
35262306a36Sopenharmony_ci		goto error_snd_soc_component_update_bits;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	/* Set Bit 7 in TAS5720_ANALOG_CTRL_REG to 1 for TAS5720A_Q1 */
35562306a36Sopenharmony_ci	switch (tas5720->devtype) {
35662306a36Sopenharmony_ci	case TAS5720A_Q1:
35762306a36Sopenharmony_ci		ret = snd_soc_component_update_bits(component, TAS5720_ANALOG_CTRL_REG,
35862306a36Sopenharmony_ci						    TAS5720_Q1_RESERVED7_BIT,
35962306a36Sopenharmony_ci						    TAS5720_Q1_RESERVED7_BIT);
36062306a36Sopenharmony_ci		break;
36162306a36Sopenharmony_ci	default:
36262306a36Sopenharmony_ci		break;
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci	if (ret < 0)
36562306a36Sopenharmony_ci		goto error_snd_soc_component_update_bits;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	/*
36862306a36Sopenharmony_ci	 * Enter shutdown mode - our default when not playing audio - to
36962306a36Sopenharmony_ci	 * minimize current consumption. On the TAS5720 there is no real down
37062306a36Sopenharmony_ci	 * side doing so as all device registers are preserved and the wakeup
37162306a36Sopenharmony_ci	 * of the codec is rather quick which we do using a dapm widget.
37262306a36Sopenharmony_ci	 */
37362306a36Sopenharmony_ci	ret = snd_soc_component_update_bits(component, TAS5720_POWER_CTRL_REG,
37462306a36Sopenharmony_ci				  TAS5720_SDZ, 0);
37562306a36Sopenharmony_ci	if (ret < 0)
37662306a36Sopenharmony_ci		goto error_snd_soc_component_update_bits;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	INIT_DELAYED_WORK(&tas5720->fault_check_work, tas5720_fault_check_work);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	return 0;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cierror_snd_soc_component_update_bits:
38362306a36Sopenharmony_ci	dev_err(component->dev, "error configuring device registers: %d\n", ret);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ciprobe_fail:
38662306a36Sopenharmony_ci	regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies),
38762306a36Sopenharmony_ci			       tas5720->supplies);
38862306a36Sopenharmony_ci	return ret;
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cistatic void tas5720_codec_remove(struct snd_soc_component *component)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component);
39462306a36Sopenharmony_ci	int ret;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	cancel_delayed_work_sync(&tas5720->fault_check_work);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	ret = regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies),
39962306a36Sopenharmony_ci				     tas5720->supplies);
40062306a36Sopenharmony_ci	if (ret < 0)
40162306a36Sopenharmony_ci		dev_err(component->dev, "failed to disable supplies: %d\n", ret);
40262306a36Sopenharmony_ci};
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic int tas5720_dac_event(struct snd_soc_dapm_widget *w,
40562306a36Sopenharmony_ci			     struct snd_kcontrol *kcontrol, int event)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
40862306a36Sopenharmony_ci	struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component);
40962306a36Sopenharmony_ci	int ret;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	if (event & SND_SOC_DAPM_POST_PMU) {
41262306a36Sopenharmony_ci		/* Take TAS5720 out of shutdown mode */
41362306a36Sopenharmony_ci		ret = snd_soc_component_update_bits(component, TAS5720_POWER_CTRL_REG,
41462306a36Sopenharmony_ci					  TAS5720_SDZ, TAS5720_SDZ);
41562306a36Sopenharmony_ci		if (ret < 0) {
41662306a36Sopenharmony_ci			dev_err(component->dev, "error waking component: %d\n", ret);
41762306a36Sopenharmony_ci			return ret;
41862306a36Sopenharmony_ci		}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci		/*
42162306a36Sopenharmony_ci		 * Observe codec shutdown-to-active time. The datasheet only
42262306a36Sopenharmony_ci		 * lists a nominal value however just use-it as-is without
42362306a36Sopenharmony_ci		 * additional padding to minimize the delay introduced in
42462306a36Sopenharmony_ci		 * starting to play audio (actually there is other setup done
42562306a36Sopenharmony_ci		 * by the ASoC framework that will provide additional delays,
42662306a36Sopenharmony_ci		 * so we should always be safe).
42762306a36Sopenharmony_ci		 */
42862306a36Sopenharmony_ci		msleep(25);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci		/* Turn on TAS5720 periodic fault checking/handling */
43162306a36Sopenharmony_ci		tas5720->last_fault = 0;
43262306a36Sopenharmony_ci		schedule_delayed_work(&tas5720->fault_check_work,
43362306a36Sopenharmony_ci				msecs_to_jiffies(TAS5720_FAULT_CHECK_INTERVAL));
43462306a36Sopenharmony_ci	} else if (event & SND_SOC_DAPM_PRE_PMD) {
43562306a36Sopenharmony_ci		/* Disable TAS5720 periodic fault checking/handling */
43662306a36Sopenharmony_ci		cancel_delayed_work_sync(&tas5720->fault_check_work);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci		/* Place TAS5720 in shutdown mode to minimize current draw */
43962306a36Sopenharmony_ci		ret = snd_soc_component_update_bits(component, TAS5720_POWER_CTRL_REG,
44062306a36Sopenharmony_ci					  TAS5720_SDZ, 0);
44162306a36Sopenharmony_ci		if (ret < 0) {
44262306a36Sopenharmony_ci			dev_err(component->dev, "error shutting down component: %d\n",
44362306a36Sopenharmony_ci				ret);
44462306a36Sopenharmony_ci			return ret;
44562306a36Sopenharmony_ci		}
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	return 0;
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci#ifdef CONFIG_PM
45262306a36Sopenharmony_cistatic int tas5720_suspend(struct snd_soc_component *component)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component);
45562306a36Sopenharmony_ci	int ret;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	regcache_cache_only(tas5720->regmap, true);
45862306a36Sopenharmony_ci	regcache_mark_dirty(tas5720->regmap);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	ret = regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies),
46162306a36Sopenharmony_ci				     tas5720->supplies);
46262306a36Sopenharmony_ci	if (ret < 0)
46362306a36Sopenharmony_ci		dev_err(component->dev, "failed to disable supplies: %d\n", ret);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	return ret;
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_cistatic int tas5720_resume(struct snd_soc_component *component)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component);
47162306a36Sopenharmony_ci	int ret;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	ret = regulator_bulk_enable(ARRAY_SIZE(tas5720->supplies),
47462306a36Sopenharmony_ci				    tas5720->supplies);
47562306a36Sopenharmony_ci	if (ret < 0) {
47662306a36Sopenharmony_ci		dev_err(component->dev, "failed to enable supplies: %d\n", ret);
47762306a36Sopenharmony_ci		return ret;
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	regcache_cache_only(tas5720->regmap, false);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	ret = regcache_sync(tas5720->regmap);
48362306a36Sopenharmony_ci	if (ret < 0) {
48462306a36Sopenharmony_ci		dev_err(component->dev, "failed to sync regcache: %d\n", ret);
48562306a36Sopenharmony_ci		return ret;
48662306a36Sopenharmony_ci	}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	return 0;
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci#else
49162306a36Sopenharmony_ci#define tas5720_suspend NULL
49262306a36Sopenharmony_ci#define tas5720_resume NULL
49362306a36Sopenharmony_ci#endif
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_cistatic bool tas5720_is_volatile_reg(struct device *dev, unsigned int reg)
49662306a36Sopenharmony_ci{
49762306a36Sopenharmony_ci	switch (reg) {
49862306a36Sopenharmony_ci	case TAS5720_DEVICE_ID_REG:
49962306a36Sopenharmony_ci	case TAS5720_FAULT_REG:
50062306a36Sopenharmony_ci		return true;
50162306a36Sopenharmony_ci	default:
50262306a36Sopenharmony_ci		return false;
50362306a36Sopenharmony_ci	}
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic const struct regmap_config tas5720_regmap_config = {
50762306a36Sopenharmony_ci	.reg_bits = 8,
50862306a36Sopenharmony_ci	.val_bits = 8,
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	.max_register = TAS5720_MAX_REG,
51162306a36Sopenharmony_ci	.cache_type = REGCACHE_RBTREE,
51262306a36Sopenharmony_ci	.volatile_reg = tas5720_is_volatile_reg,
51362306a36Sopenharmony_ci};
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_cistatic const struct regmap_config tas5720a_q1_regmap_config = {
51662306a36Sopenharmony_ci	.reg_bits = 8,
51762306a36Sopenharmony_ci	.val_bits = 8,
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	.max_register = TAS5720_MAX_REG,
52062306a36Sopenharmony_ci	.cache_type = REGCACHE_RBTREE,
52162306a36Sopenharmony_ci	.volatile_reg = tas5720_is_volatile_reg,
52262306a36Sopenharmony_ci};
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic const struct regmap_config tas5722_regmap_config = {
52562306a36Sopenharmony_ci	.reg_bits = 8,
52662306a36Sopenharmony_ci	.val_bits = 8,
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	.max_register = TAS5722_MAX_REG,
52962306a36Sopenharmony_ci	.cache_type = REGCACHE_RBTREE,
53062306a36Sopenharmony_ci	.volatile_reg = tas5720_is_volatile_reg,
53162306a36Sopenharmony_ci};
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci/*
53462306a36Sopenharmony_ci * DAC analog gain. There are four discrete values to select from, ranging
53562306a36Sopenharmony_ci * from 19.2 dB to 26.3dB.
53662306a36Sopenharmony_ci */
53762306a36Sopenharmony_cistatic const DECLARE_TLV_DB_RANGE(dac_analog_tlv,
53862306a36Sopenharmony_ci	0x0, 0x0, TLV_DB_SCALE_ITEM(1920, 0, 0),
53962306a36Sopenharmony_ci	0x1, 0x1, TLV_DB_SCALE_ITEM(2070, 0, 0),
54062306a36Sopenharmony_ci	0x2, 0x2, TLV_DB_SCALE_ITEM(2350, 0, 0),
54162306a36Sopenharmony_ci	0x3, 0x3, TLV_DB_SCALE_ITEM(2630, 0, 0),
54262306a36Sopenharmony_ci);
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci/*
54562306a36Sopenharmony_ci * DAC analog gain for TAS5720A-Q1. There are three discrete values to select from, ranging
54662306a36Sopenharmony_ci * from 19.2 dB to 25.0dB.
54762306a36Sopenharmony_ci */
54862306a36Sopenharmony_cistatic const DECLARE_TLV_DB_RANGE(dac_analog_tlv_a_q1,
54962306a36Sopenharmony_ci	0x0, 0x0, TLV_DB_SCALE_ITEM(1920, 0, 0),
55062306a36Sopenharmony_ci	0x1, 0x1, TLV_DB_SCALE_ITEM(2260, 0, 0),
55162306a36Sopenharmony_ci	0x2, 0x2, TLV_DB_SCALE_ITEM(2500, 0, 0),
55262306a36Sopenharmony_ci);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci/*
55562306a36Sopenharmony_ci * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB or 0.25 dB steps
55662306a36Sopenharmony_ci * depending on the device. Note that setting the gain below -100 dB
55762306a36Sopenharmony_ci * (register value <0x7) is effectively a MUTE as per device datasheet.
55862306a36Sopenharmony_ci *
55962306a36Sopenharmony_ci * Note that for the TAS5722 the digital volume controls are actually split
56062306a36Sopenharmony_ci * over two registers, so we need custom getters/setters for access.
56162306a36Sopenharmony_ci */
56262306a36Sopenharmony_cistatic DECLARE_TLV_DB_SCALE(tas5720_dac_tlv, -10350, 50, 0);
56362306a36Sopenharmony_cistatic DECLARE_TLV_DB_SCALE(tas5722_dac_tlv, -10350, 25, 0);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_cistatic int tas5722_volume_get(struct snd_kcontrol *kcontrol,
56662306a36Sopenharmony_ci			      struct snd_ctl_elem_value *ucontrol)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
56962306a36Sopenharmony_ci	unsigned int val;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	val = snd_soc_component_read(component, TAS5720_VOLUME_CTRL_REG);
57262306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = val << 1;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	val = snd_soc_component_read(component, TAS5722_DIGITAL_CTRL2_REG);
57562306a36Sopenharmony_ci	ucontrol->value.integer.value[0] |= val & TAS5722_VOL_CONTROL_LSB;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	return 0;
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic int tas5722_volume_set(struct snd_kcontrol *kcontrol,
58162306a36Sopenharmony_ci			      struct snd_ctl_elem_value *ucontrol)
58262306a36Sopenharmony_ci{
58362306a36Sopenharmony_ci	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
58462306a36Sopenharmony_ci	unsigned int sel = ucontrol->value.integer.value[0];
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	snd_soc_component_write(component, TAS5720_VOLUME_CTRL_REG, sel >> 1);
58762306a36Sopenharmony_ci	snd_soc_component_update_bits(component, TAS5722_DIGITAL_CTRL2_REG,
58862306a36Sopenharmony_ci				      TAS5722_VOL_CONTROL_LSB, sel);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	return 0;
59162306a36Sopenharmony_ci}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_cistatic const struct snd_kcontrol_new tas5720_snd_controls[] = {
59462306a36Sopenharmony_ci	SOC_SINGLE_TLV("Speaker Driver Playback Volume",
59562306a36Sopenharmony_ci		       TAS5720_VOLUME_CTRL_REG, 0, 0xff, 0, tas5720_dac_tlv),
59662306a36Sopenharmony_ci	SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG,
59762306a36Sopenharmony_ci		       TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv),
59862306a36Sopenharmony_ci};
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_cistatic const struct snd_kcontrol_new tas5720a_q1_snd_controls[] = {
60162306a36Sopenharmony_ci	SOC_DOUBLE_R_TLV("Speaker Driver Playback Volume",
60262306a36Sopenharmony_ci				TAS5720_Q1_VOLUME_CTRL_LEFT_REG,
60362306a36Sopenharmony_ci				TAS5720_Q1_VOLUME_CTRL_RIGHT_REG,
60462306a36Sopenharmony_ci				0, 0xff, 0, tas5720_dac_tlv),
60562306a36Sopenharmony_ci	SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG,
60662306a36Sopenharmony_ci				TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv_a_q1),
60762306a36Sopenharmony_ci};
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic const struct snd_kcontrol_new tas5722_snd_controls[] = {
61062306a36Sopenharmony_ci	SOC_SINGLE_EXT_TLV("Speaker Driver Playback Volume",
61162306a36Sopenharmony_ci			   0, 0, 511, 0,
61262306a36Sopenharmony_ci			   tas5722_volume_get, tas5722_volume_set,
61362306a36Sopenharmony_ci			   tas5722_dac_tlv),
61462306a36Sopenharmony_ci	SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG,
61562306a36Sopenharmony_ci		       TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv),
61662306a36Sopenharmony_ci};
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget tas5720_dapm_widgets[] = {
61962306a36Sopenharmony_ci	SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
62062306a36Sopenharmony_ci	SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas5720_dac_event,
62162306a36Sopenharmony_ci			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
62262306a36Sopenharmony_ci	SND_SOC_DAPM_OUTPUT("OUT")
62362306a36Sopenharmony_ci};
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic const struct snd_soc_dapm_route tas5720_audio_map[] = {
62662306a36Sopenharmony_ci	{ "DAC", NULL, "DAC IN" },
62762306a36Sopenharmony_ci	{ "OUT", NULL, "DAC" },
62862306a36Sopenharmony_ci};
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_cistatic const struct snd_soc_component_driver soc_component_dev_tas5720 = {
63162306a36Sopenharmony_ci	.probe			= tas5720_codec_probe,
63262306a36Sopenharmony_ci	.remove			= tas5720_codec_remove,
63362306a36Sopenharmony_ci	.suspend		= tas5720_suspend,
63462306a36Sopenharmony_ci	.resume			= tas5720_resume,
63562306a36Sopenharmony_ci	.controls		= tas5720_snd_controls,
63662306a36Sopenharmony_ci	.num_controls		= ARRAY_SIZE(tas5720_snd_controls),
63762306a36Sopenharmony_ci	.dapm_widgets		= tas5720_dapm_widgets,
63862306a36Sopenharmony_ci	.num_dapm_widgets	= ARRAY_SIZE(tas5720_dapm_widgets),
63962306a36Sopenharmony_ci	.dapm_routes		= tas5720_audio_map,
64062306a36Sopenharmony_ci	.num_dapm_routes	= ARRAY_SIZE(tas5720_audio_map),
64162306a36Sopenharmony_ci	.idle_bias_on		= 1,
64262306a36Sopenharmony_ci	.use_pmdown_time	= 1,
64362306a36Sopenharmony_ci	.endianness		= 1,
64462306a36Sopenharmony_ci};
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_cistatic const struct snd_soc_component_driver soc_component_dev_tas5720_a_q1 = {
64762306a36Sopenharmony_ci	.probe			= tas5720_codec_probe,
64862306a36Sopenharmony_ci	.remove			= tas5720_codec_remove,
64962306a36Sopenharmony_ci	.suspend		= tas5720_suspend,
65062306a36Sopenharmony_ci	.resume			= tas5720_resume,
65162306a36Sopenharmony_ci	.controls		= tas5720a_q1_snd_controls,
65262306a36Sopenharmony_ci	.num_controls		= ARRAY_SIZE(tas5720a_q1_snd_controls),
65362306a36Sopenharmony_ci	.dapm_widgets		= tas5720_dapm_widgets,
65462306a36Sopenharmony_ci	.num_dapm_widgets	= ARRAY_SIZE(tas5720_dapm_widgets),
65562306a36Sopenharmony_ci	.dapm_routes		= tas5720_audio_map,
65662306a36Sopenharmony_ci	.num_dapm_routes	= ARRAY_SIZE(tas5720_audio_map),
65762306a36Sopenharmony_ci	.idle_bias_on		= 1,
65862306a36Sopenharmony_ci	.use_pmdown_time	= 1,
65962306a36Sopenharmony_ci	.endianness		= 1,
66062306a36Sopenharmony_ci};
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cistatic const struct snd_soc_component_driver soc_component_dev_tas5722 = {
66362306a36Sopenharmony_ci	.probe = tas5720_codec_probe,
66462306a36Sopenharmony_ci	.remove = tas5720_codec_remove,
66562306a36Sopenharmony_ci	.suspend = tas5720_suspend,
66662306a36Sopenharmony_ci	.resume = tas5720_resume,
66762306a36Sopenharmony_ci	.controls = tas5722_snd_controls,
66862306a36Sopenharmony_ci	.num_controls = ARRAY_SIZE(tas5722_snd_controls),
66962306a36Sopenharmony_ci	.dapm_widgets = tas5720_dapm_widgets,
67062306a36Sopenharmony_ci	.num_dapm_widgets = ARRAY_SIZE(tas5720_dapm_widgets),
67162306a36Sopenharmony_ci	.dapm_routes = tas5720_audio_map,
67262306a36Sopenharmony_ci	.num_dapm_routes = ARRAY_SIZE(tas5720_audio_map),
67362306a36Sopenharmony_ci	.idle_bias_on		= 1,
67462306a36Sopenharmony_ci	.use_pmdown_time	= 1,
67562306a36Sopenharmony_ci	.endianness		= 1,
67662306a36Sopenharmony_ci};
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci/* PCM rates supported by the TAS5720 driver */
67962306a36Sopenharmony_ci#define TAS5720_RATES	(SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
68062306a36Sopenharmony_ci			 SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci/* Formats supported by TAS5720 driver */
68362306a36Sopenharmony_ci#define TAS5720_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE |\
68462306a36Sopenharmony_ci			 SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE)
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cistatic const struct snd_soc_dai_ops tas5720_speaker_dai_ops = {
68762306a36Sopenharmony_ci	.hw_params	= tas5720_hw_params,
68862306a36Sopenharmony_ci	.set_fmt	= tas5720_set_dai_fmt,
68962306a36Sopenharmony_ci	.set_tdm_slot	= tas5720_set_dai_tdm_slot,
69062306a36Sopenharmony_ci	.mute_stream	= tas5720_mute,
69162306a36Sopenharmony_ci	.no_capture_mute = 1,
69262306a36Sopenharmony_ci};
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci/*
69562306a36Sopenharmony_ci * TAS5720 DAI structure
69662306a36Sopenharmony_ci *
69762306a36Sopenharmony_ci * Note that were are advertising .playback.channels_max = 2 despite this being
69862306a36Sopenharmony_ci * a mono amplifier. The reason for that is that some serial ports such as TI's
69962306a36Sopenharmony_ci * McASP module have a minimum number of channels (2) that they can output.
70062306a36Sopenharmony_ci * Advertising more channels than we have will allow us to interface with such
70162306a36Sopenharmony_ci * a serial port without really any negative side effects as the TAS5720 will
70262306a36Sopenharmony_ci * simply ignore any extra channel(s) asides from the one channel that is
70362306a36Sopenharmony_ci * configured to be played back.
70462306a36Sopenharmony_ci */
70562306a36Sopenharmony_cistatic struct snd_soc_dai_driver tas5720_dai[] = {
70662306a36Sopenharmony_ci	{
70762306a36Sopenharmony_ci		.name = "tas5720-amplifier",
70862306a36Sopenharmony_ci		.playback = {
70962306a36Sopenharmony_ci			.stream_name = "Playback",
71062306a36Sopenharmony_ci			.channels_min = 1,
71162306a36Sopenharmony_ci			.channels_max = 2,
71262306a36Sopenharmony_ci			.rates = TAS5720_RATES,
71362306a36Sopenharmony_ci			.formats = TAS5720_FORMATS,
71462306a36Sopenharmony_ci		},
71562306a36Sopenharmony_ci		.ops = &tas5720_speaker_dai_ops,
71662306a36Sopenharmony_ci	},
71762306a36Sopenharmony_ci};
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_cistatic const struct i2c_device_id tas5720_id[] = {
72062306a36Sopenharmony_ci	{ "tas5720", TAS5720 },
72162306a36Sopenharmony_ci	{ "tas5720a-q1", TAS5720A_Q1 },
72262306a36Sopenharmony_ci	{ "tas5722", TAS5722 },
72362306a36Sopenharmony_ci	{ }
72462306a36Sopenharmony_ci};
72562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tas5720_id);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_cistatic int tas5720_probe(struct i2c_client *client)
72862306a36Sopenharmony_ci{
72962306a36Sopenharmony_ci	struct device *dev = &client->dev;
73062306a36Sopenharmony_ci	struct tas5720_data *data;
73162306a36Sopenharmony_ci	const struct regmap_config *regmap_config;
73262306a36Sopenharmony_ci	const struct i2c_device_id *id;
73362306a36Sopenharmony_ci	int ret;
73462306a36Sopenharmony_ci	int i;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
73762306a36Sopenharmony_ci	if (!data)
73862306a36Sopenharmony_ci		return -ENOMEM;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	id = i2c_match_id(tas5720_id, client);
74162306a36Sopenharmony_ci	data->tas5720_client = client;
74262306a36Sopenharmony_ci	data->devtype = id->driver_data;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	switch (id->driver_data) {
74562306a36Sopenharmony_ci	case TAS5720:
74662306a36Sopenharmony_ci		regmap_config = &tas5720_regmap_config;
74762306a36Sopenharmony_ci		break;
74862306a36Sopenharmony_ci	case TAS5720A_Q1:
74962306a36Sopenharmony_ci		regmap_config = &tas5720a_q1_regmap_config;
75062306a36Sopenharmony_ci		break;
75162306a36Sopenharmony_ci	case TAS5722:
75262306a36Sopenharmony_ci		regmap_config = &tas5722_regmap_config;
75362306a36Sopenharmony_ci		break;
75462306a36Sopenharmony_ci	default:
75562306a36Sopenharmony_ci		dev_err(dev, "unexpected private driver data\n");
75662306a36Sopenharmony_ci		return -EINVAL;
75762306a36Sopenharmony_ci	}
75862306a36Sopenharmony_ci	data->regmap = devm_regmap_init_i2c(client, regmap_config);
75962306a36Sopenharmony_ci	if (IS_ERR(data->regmap)) {
76062306a36Sopenharmony_ci		ret = PTR_ERR(data->regmap);
76162306a36Sopenharmony_ci		dev_err(dev, "failed to allocate register map: %d\n", ret);
76262306a36Sopenharmony_ci		return ret;
76362306a36Sopenharmony_ci	}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
76662306a36Sopenharmony_ci		data->supplies[i].supply = tas5720_supply_names[i];
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies),
76962306a36Sopenharmony_ci				      data->supplies);
77062306a36Sopenharmony_ci	if (ret != 0) {
77162306a36Sopenharmony_ci		dev_err(dev, "failed to request supplies: %d\n", ret);
77262306a36Sopenharmony_ci		return ret;
77362306a36Sopenharmony_ci	}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	dev_set_drvdata(dev, data);
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	switch (id->driver_data) {
77862306a36Sopenharmony_ci	case TAS5720:
77962306a36Sopenharmony_ci		ret = devm_snd_soc_register_component(&client->dev,
78062306a36Sopenharmony_ci					&soc_component_dev_tas5720,
78162306a36Sopenharmony_ci					tas5720_dai,
78262306a36Sopenharmony_ci					ARRAY_SIZE(tas5720_dai));
78362306a36Sopenharmony_ci		break;
78462306a36Sopenharmony_ci	case TAS5720A_Q1:
78562306a36Sopenharmony_ci		ret = devm_snd_soc_register_component(&client->dev,
78662306a36Sopenharmony_ci					&soc_component_dev_tas5720_a_q1,
78762306a36Sopenharmony_ci					tas5720_dai,
78862306a36Sopenharmony_ci					ARRAY_SIZE(tas5720_dai));
78962306a36Sopenharmony_ci		break;
79062306a36Sopenharmony_ci	case TAS5722:
79162306a36Sopenharmony_ci		ret = devm_snd_soc_register_component(&client->dev,
79262306a36Sopenharmony_ci					&soc_component_dev_tas5722,
79362306a36Sopenharmony_ci					tas5720_dai,
79462306a36Sopenharmony_ci					ARRAY_SIZE(tas5720_dai));
79562306a36Sopenharmony_ci		break;
79662306a36Sopenharmony_ci	default:
79762306a36Sopenharmony_ci		dev_err(dev, "unexpected private driver data\n");
79862306a36Sopenharmony_ci		return -EINVAL;
79962306a36Sopenharmony_ci	}
80062306a36Sopenharmony_ci	if (ret < 0) {
80162306a36Sopenharmony_ci		dev_err(dev, "failed to register component: %d\n", ret);
80262306a36Sopenharmony_ci		return ret;
80362306a36Sopenharmony_ci	}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	return 0;
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_OF)
80962306a36Sopenharmony_cistatic const struct of_device_id tas5720_of_match[] = {
81062306a36Sopenharmony_ci	{ .compatible = "ti,tas5720", },
81162306a36Sopenharmony_ci	{ .compatible = "ti,tas5720a-q1", },
81262306a36Sopenharmony_ci	{ .compatible = "ti,tas5722", },
81362306a36Sopenharmony_ci	{ },
81462306a36Sopenharmony_ci};
81562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tas5720_of_match);
81662306a36Sopenharmony_ci#endif
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_cistatic struct i2c_driver tas5720_i2c_driver = {
81962306a36Sopenharmony_ci	.driver = {
82062306a36Sopenharmony_ci		.name = "tas5720",
82162306a36Sopenharmony_ci		.of_match_table = of_match_ptr(tas5720_of_match),
82262306a36Sopenharmony_ci	},
82362306a36Sopenharmony_ci	.probe = tas5720_probe,
82462306a36Sopenharmony_ci	.id_table = tas5720_id,
82562306a36Sopenharmony_ci};
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_cimodule_i2c_driver(tas5720_i2c_driver);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ciMODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");
83062306a36Sopenharmony_ciMODULE_DESCRIPTION("TAS5720 Audio amplifier driver");
83162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
832