xref: /linux/sound/soc/codecs/tas2552.c (revision 36ec807b627b4c0a0a382f0ae48eac7187d14b2b)
11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
25df7f71dSDan Murphy /*
35df7f71dSDan Murphy  * tas2552.c - ALSA SoC Texas Instruments TAS2552 Mono Audio Amplifier
45df7f71dSDan Murphy  *
5*7078ac4fSShenghao Ding  * Copyright (C) 2014 - 2024 Texas Instruments Incorporated -
6*7078ac4fSShenghao Ding  *	https://www.ti.com
75df7f71dSDan Murphy  *
85df7f71dSDan Murphy  * Author: Dan Murphy <dmurphy@ti.com>
95df7f71dSDan Murphy  */
105df7f71dSDan Murphy 
115df7f71dSDan Murphy #include <linux/module.h>
125df7f71dSDan Murphy #include <linux/errno.h>
135df7f71dSDan Murphy #include <linux/device.h>
145df7f71dSDan Murphy #include <linux/i2c.h>
155df7f71dSDan Murphy #include <linux/gpio.h>
165df7f71dSDan Murphy #include <linux/pm_runtime.h>
175df7f71dSDan Murphy #include <linux/regmap.h>
185df7f71dSDan Murphy #include <linux/slab.h>
195df7f71dSDan Murphy 
205df7f71dSDan Murphy #include <linux/gpio/consumer.h>
215df7f71dSDan Murphy #include <linux/regulator/consumer.h>
225df7f71dSDan Murphy 
235df7f71dSDan Murphy #include <sound/pcm.h>
245df7f71dSDan Murphy #include <sound/pcm_params.h>
255df7f71dSDan Murphy #include <sound/soc.h>
265df7f71dSDan Murphy #include <sound/soc-dapm.h>
275df7f71dSDan Murphy #include <sound/tlv.h>
285df7f71dSDan Murphy #include <sound/tas2552-plat.h>
299d87a888SPeter Ujfalusi #include <dt-bindings/sound/tas2552.h>
305df7f71dSDan Murphy 
315df7f71dSDan Murphy #include "tas2552.h"
325df7f71dSDan Murphy 
33c418a84aSAxel Lin static const struct reg_default tas2552_reg_defs[] = {
345df7f71dSDan Murphy 	{TAS2552_CFG_1, 0x22},
355df7f71dSDan Murphy 	{TAS2552_CFG_3, 0x80},
365df7f71dSDan Murphy 	{TAS2552_DOUT, 0x00},
375df7f71dSDan Murphy 	{TAS2552_OUTPUT_DATA, 0xc0},
385df7f71dSDan Murphy 	{TAS2552_PDM_CFG, 0x01},
395df7f71dSDan Murphy 	{TAS2552_PGA_GAIN, 0x00},
402a9dd1dbSPeter Ujfalusi 	{TAS2552_BOOST_APT_CTRL, 0x0f},
417d785025SPeter Ujfalusi 	{TAS2552_RESERVED_0D, 0xbe},
425df7f71dSDan Murphy 	{TAS2552_LIMIT_RATE_HYS, 0x08},
435df7f71dSDan Murphy 	{TAS2552_CFG_2, 0xef},
445df7f71dSDan Murphy 	{TAS2552_SER_CTRL_1, 0x00},
455df7f71dSDan Murphy 	{TAS2552_SER_CTRL_2, 0x00},
465df7f71dSDan Murphy 	{TAS2552_PLL_CTRL_1, 0x10},
475df7f71dSDan Murphy 	{TAS2552_PLL_CTRL_2, 0x00},
485df7f71dSDan Murphy 	{TAS2552_PLL_CTRL_3, 0x00},
495df7f71dSDan Murphy 	{TAS2552_BTIP, 0x8f},
505df7f71dSDan Murphy 	{TAS2552_BTS_CTRL, 0x80},
515df7f71dSDan Murphy 	{TAS2552_LIMIT_RELEASE, 0x04},
525df7f71dSDan Murphy 	{TAS2552_LIMIT_INT_COUNT, 0x00},
535df7f71dSDan Murphy 	{TAS2552_EDGE_RATE_CTRL, 0x40},
545df7f71dSDan Murphy 	{TAS2552_VBAT_DATA, 0x00},
555df7f71dSDan Murphy };
565df7f71dSDan Murphy 
575df7f71dSDan Murphy #define TAS2552_NUM_SUPPLIES	3
585df7f71dSDan Murphy static const char *tas2552_supply_names[TAS2552_NUM_SUPPLIES] = {
595df7f71dSDan Murphy 	"vbat",		/* vbat voltage */
605df7f71dSDan Murphy 	"iovdd",	/* I/O Voltage */
615df7f71dSDan Murphy 	"avdd",		/* Analog DAC Voltage */
625df7f71dSDan Murphy };
635df7f71dSDan Murphy 
645df7f71dSDan Murphy struct tas2552_data {
6579a4ad1eSKuninori Morimoto 	struct snd_soc_component *component;
665df7f71dSDan Murphy 	struct regmap *regmap;
675df7f71dSDan Murphy 	struct i2c_client *tas2552_client;
685df7f71dSDan Murphy 	struct regulator_bulk_data supplies[TAS2552_NUM_SUPPLIES];
695df7f71dSDan Murphy 	struct gpio_desc *enable_gpio;
705df7f71dSDan Murphy 	unsigned char regs[TAS2552_VBAT_DATA];
7116bd3952SPeter Ujfalusi 	unsigned int pll_clkin;
721014f7efSPeter Ujfalusi 	int pll_clk_id;
739d87a888SPeter Ujfalusi 	unsigned int pdm_clk;
741014f7efSPeter Ujfalusi 	int pdm_clk_id;
753f747a81SPeter Ujfalusi 
763f747a81SPeter Ujfalusi 	unsigned int dai_fmt;
773f747a81SPeter Ujfalusi 	unsigned int tdm_delay;
785df7f71dSDan Murphy };
795df7f71dSDan Murphy 
807d785025SPeter Ujfalusi static int tas2552_post_event(struct snd_soc_dapm_widget *w,
817d785025SPeter Ujfalusi 			      struct snd_kcontrol *kcontrol, int event)
827d785025SPeter Ujfalusi {
8379a4ad1eSKuninori Morimoto 	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
847d785025SPeter Ujfalusi 
857d785025SPeter Ujfalusi 	switch (event) {
867d785025SPeter Ujfalusi 	case SND_SOC_DAPM_POST_PMU:
8779a4ad1eSKuninori Morimoto 		snd_soc_component_write(component, TAS2552_RESERVED_0D, 0xc0);
8879a4ad1eSKuninori Morimoto 		snd_soc_component_update_bits(component, TAS2552_LIMIT_RATE_HYS, (1 << 5),
897d785025SPeter Ujfalusi 				    (1 << 5));
9079a4ad1eSKuninori Morimoto 		snd_soc_component_update_bits(component, TAS2552_CFG_2, 1, 0);
9179a4ad1eSKuninori Morimoto 		snd_soc_component_update_bits(component, TAS2552_CFG_1, TAS2552_SWS, 0);
927d785025SPeter Ujfalusi 		break;
937d785025SPeter Ujfalusi 	case SND_SOC_DAPM_POST_PMD:
9479a4ad1eSKuninori Morimoto 		snd_soc_component_update_bits(component, TAS2552_CFG_1, TAS2552_SWS,
957d785025SPeter Ujfalusi 				    TAS2552_SWS);
9679a4ad1eSKuninori Morimoto 		snd_soc_component_update_bits(component, TAS2552_CFG_2, 1, 1);
9779a4ad1eSKuninori Morimoto 		snd_soc_component_update_bits(component, TAS2552_LIMIT_RATE_HYS, (1 << 5), 0);
9879a4ad1eSKuninori Morimoto 		snd_soc_component_write(component, TAS2552_RESERVED_0D, 0xbe);
997d785025SPeter Ujfalusi 		break;
1007d785025SPeter Ujfalusi 	}
1017d785025SPeter Ujfalusi 	return 0;
1027d785025SPeter Ujfalusi }
103a7a8e994SDan Murphy 
104609e7131SPeter Ujfalusi /* Input mux controls */
105609e7131SPeter Ujfalusi static const char * const tas2552_input_texts[] = {
106609e7131SPeter Ujfalusi 	"Digital", "Analog" };
107a7a8e994SDan Murphy static SOC_ENUM_SINGLE_DECL(tas2552_input_mux_enum, TAS2552_CFG_3, 7,
108a7a8e994SDan Murphy 			    tas2552_input_texts);
109a7a8e994SDan Murphy 
110609e7131SPeter Ujfalusi static const struct snd_kcontrol_new tas2552_input_mux_control =
111609e7131SPeter Ujfalusi 	SOC_DAPM_ENUM("Route", tas2552_input_mux_enum);
112a7a8e994SDan Murphy 
113a7a8e994SDan Murphy static const struct snd_soc_dapm_widget tas2552_dapm_widgets[] =
114a7a8e994SDan Murphy {
115a7a8e994SDan Murphy 	SND_SOC_DAPM_INPUT("IN"),
116a7a8e994SDan Murphy 
117a7a8e994SDan Murphy 	/* MUX Controls */
118a7a8e994SDan Murphy 	SND_SOC_DAPM_MUX("Input selection", SND_SOC_NOPM, 0, 0,
119609e7131SPeter Ujfalusi 			 &tas2552_input_mux_control),
120a7a8e994SDan Murphy 
121a7a8e994SDan Murphy 	SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0, 0),
122*7078ac4fSShenghao Ding 	SND_SOC_DAPM_AIF_OUT("ASI OUT", "DAC Capture", 0, SND_SOC_NOPM, 0, 0),
123a7a8e994SDan Murphy 	SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
124a7a8e994SDan Murphy 	SND_SOC_DAPM_OUT_DRV("ClassD", TAS2552_CFG_2, 7, 0, NULL, 0),
125a7a8e994SDan Murphy 	SND_SOC_DAPM_SUPPLY("PLL", TAS2552_CFG_2, 3, 0, NULL, 0),
1267d785025SPeter Ujfalusi 	SND_SOC_DAPM_POST("Post Event", tas2552_post_event),
127a7a8e994SDan Murphy 
128*7078ac4fSShenghao Ding 	SND_SOC_DAPM_OUTPUT("OUT"),
129*7078ac4fSShenghao Ding 	SND_SOC_DAPM_INPUT("DMIC")
130a7a8e994SDan Murphy };
131a7a8e994SDan Murphy 
132a7a8e994SDan Murphy static const struct snd_soc_dapm_route tas2552_audio_map[] = {
133a7a8e994SDan Murphy 	{"DAC", NULL, "DAC IN"},
134a7a8e994SDan Murphy 	{"Input selection", "Digital", "DAC"},
135a7a8e994SDan Murphy 	{"Input selection", "Analog", "IN"},
136a7a8e994SDan Murphy 	{"ClassD", NULL, "Input selection"},
137a7a8e994SDan Murphy 	{"OUT", NULL, "ClassD"},
138a7a8e994SDan Murphy 	{"ClassD", NULL, "PLL"},
139*7078ac4fSShenghao Ding 	{"ASI OUT", NULL, "DMIC"}
140a7a8e994SDan Murphy };
141a7a8e994SDan Murphy 
142641d334bSRafael J. Wysocki #ifdef CONFIG_PM
143b94525bfSPeter Ujfalusi static void tas2552_sw_shutdown(struct tas2552_data *tas2552, int sw_shutdown)
1445df7f71dSDan Murphy {
145dd6e3053SPeter Ujfalusi 	u8 cfg1_reg = 0;
1465df7f71dSDan Murphy 
14779a4ad1eSKuninori Morimoto 	if (!tas2552->component)
14880ba2669SPeter Ujfalusi 		return;
14980ba2669SPeter Ujfalusi 
1505df7f71dSDan Murphy 	if (sw_shutdown)
1517de544fdSPeter Ujfalusi 		cfg1_reg = TAS2552_SWS;
1525df7f71dSDan Murphy 
15379a4ad1eSKuninori Morimoto 	snd_soc_component_update_bits(tas2552->component, TAS2552_CFG_1, TAS2552_SWS,
1547de544fdSPeter Ujfalusi 			    cfg1_reg);
1555df7f71dSDan Murphy }
156be1aa3eaSThierry Reding #endif
1575df7f71dSDan Murphy 
15879a4ad1eSKuninori Morimoto static int tas2552_setup_pll(struct snd_soc_component *component,
1591014f7efSPeter Ujfalusi 			     struct snd_pcm_hw_params *params)
1601014f7efSPeter Ujfalusi {
16179a4ad1eSKuninori Morimoto 	struct tas2552_data *tas2552 = dev_get_drvdata(component->dev);
1621014f7efSPeter Ujfalusi 	bool bypass_pll = false;
1631014f7efSPeter Ujfalusi 	unsigned int pll_clk = params_rate(params) * 512;
1641014f7efSPeter Ujfalusi 	unsigned int pll_clkin = tas2552->pll_clkin;
1651014f7efSPeter Ujfalusi 	u8 pll_enable;
1661014f7efSPeter Ujfalusi 
1671014f7efSPeter Ujfalusi 	if (!pll_clkin) {
1681014f7efSPeter Ujfalusi 		if (tas2552->pll_clk_id != TAS2552_PLL_CLKIN_BCLK)
1691014f7efSPeter Ujfalusi 			return -EINVAL;
1701014f7efSPeter Ujfalusi 
1711014f7efSPeter Ujfalusi 		pll_clkin = snd_soc_params_to_bclk(params);
1721014f7efSPeter Ujfalusi 		pll_clkin += tas2552->tdm_delay;
1731014f7efSPeter Ujfalusi 	}
1741014f7efSPeter Ujfalusi 
175981abdfeSKuninori Morimoto 	pll_enable = snd_soc_component_read(component, TAS2552_CFG_2) & TAS2552_PLL_ENABLE;
17679a4ad1eSKuninori Morimoto 	snd_soc_component_update_bits(component, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0);
1771014f7efSPeter Ujfalusi 
1781014f7efSPeter Ujfalusi 	if (pll_clkin == pll_clk)
1791014f7efSPeter Ujfalusi 		bypass_pll = true;
1801014f7efSPeter Ujfalusi 
1811014f7efSPeter Ujfalusi 	if (bypass_pll) {
1821014f7efSPeter Ujfalusi 		/* By pass the PLL configuration */
18379a4ad1eSKuninori Morimoto 		snd_soc_component_update_bits(component, TAS2552_PLL_CTRL_2,
1841014f7efSPeter Ujfalusi 				    TAS2552_PLL_BYPASS, TAS2552_PLL_BYPASS);
1851014f7efSPeter Ujfalusi 	} else {
1861014f7efSPeter Ujfalusi 		/* Fill in the PLL control registers for J & D
1871014f7efSPeter Ujfalusi 		 * pll_clk = (.5 * pll_clkin * J.D) / 2^p
1881014f7efSPeter Ujfalusi 		 * Need to fill in J and D here based on incoming freq
1891014f7efSPeter Ujfalusi 		 */
1901bb7cb68SOskar Schirmer 		unsigned int d, q, t;
1911014f7efSPeter Ujfalusi 		u8 j;
1921014f7efSPeter Ujfalusi 		u8 pll_sel = (tas2552->pll_clk_id << 3) & TAS2552_PLL_SRC_MASK;
193981abdfeSKuninori Morimoto 		u8 p = snd_soc_component_read(component, TAS2552_PLL_CTRL_1);
1941014f7efSPeter Ujfalusi 
1951014f7efSPeter Ujfalusi 		p = (p >> 7);
1961014f7efSPeter Ujfalusi 
1971014f7efSPeter Ujfalusi recalc:
1981bb7cb68SOskar Schirmer 		t = (pll_clk * 2) << p;
1991bb7cb68SOskar Schirmer 		j = t / pll_clkin;
2001bb7cb68SOskar Schirmer 		d = t % pll_clkin;
2011bb7cb68SOskar Schirmer 		t = pll_clkin / 10000;
2021bb7cb68SOskar Schirmer 		q = d / (t + 1);
2031bb7cb68SOskar Schirmer 		d = q + ((9999 - pll_clkin % 10000) * (d / t - q)) / 10000;
2041014f7efSPeter Ujfalusi 
2051014f7efSPeter Ujfalusi 		if (d && (pll_clkin < 512000 || pll_clkin > 9200000)) {
2061014f7efSPeter Ujfalusi 			if (tas2552->pll_clk_id == TAS2552_PLL_CLKIN_BCLK) {
2071014f7efSPeter Ujfalusi 				pll_clkin = 1800000;
2081014f7efSPeter Ujfalusi 				pll_sel = (TAS2552_PLL_CLKIN_1_8_FIXED << 3) &
2091014f7efSPeter Ujfalusi 							TAS2552_PLL_SRC_MASK;
2101014f7efSPeter Ujfalusi 			} else {
2111014f7efSPeter Ujfalusi 				pll_clkin = snd_soc_params_to_bclk(params);
2121014f7efSPeter Ujfalusi 				pll_clkin += tas2552->tdm_delay;
2131014f7efSPeter Ujfalusi 				pll_sel = (TAS2552_PLL_CLKIN_BCLK << 3) &
2141014f7efSPeter Ujfalusi 							TAS2552_PLL_SRC_MASK;
2151014f7efSPeter Ujfalusi 			}
2161014f7efSPeter Ujfalusi 			goto recalc;
2171014f7efSPeter Ujfalusi 		}
2181014f7efSPeter Ujfalusi 
21979a4ad1eSKuninori Morimoto 		snd_soc_component_update_bits(component, TAS2552_CFG_1, TAS2552_PLL_SRC_MASK,
2201014f7efSPeter Ujfalusi 				    pll_sel);
2211014f7efSPeter Ujfalusi 
22279a4ad1eSKuninori Morimoto 		snd_soc_component_update_bits(component, TAS2552_PLL_CTRL_1,
2231014f7efSPeter Ujfalusi 				    TAS2552_PLL_J_MASK, j);
2241014f7efSPeter Ujfalusi 		/* Will clear the PLL_BYPASS bit */
22579a4ad1eSKuninori Morimoto 		snd_soc_component_write(component, TAS2552_PLL_CTRL_2,
2261014f7efSPeter Ujfalusi 			      TAS2552_PLL_D_UPPER(d));
22779a4ad1eSKuninori Morimoto 		snd_soc_component_write(component, TAS2552_PLL_CTRL_3,
2281014f7efSPeter Ujfalusi 			      TAS2552_PLL_D_LOWER(d));
2291014f7efSPeter Ujfalusi 	}
2301014f7efSPeter Ujfalusi 
2311014f7efSPeter Ujfalusi 	/* Restore PLL status */
23279a4ad1eSKuninori Morimoto 	snd_soc_component_update_bits(component, TAS2552_CFG_2, TAS2552_PLL_ENABLE,
2331014f7efSPeter Ujfalusi 			    pll_enable);
2341014f7efSPeter Ujfalusi 
2351014f7efSPeter Ujfalusi 	return 0;
2361014f7efSPeter Ujfalusi }
2371014f7efSPeter Ujfalusi 
2385df7f71dSDan Murphy static int tas2552_hw_params(struct snd_pcm_substream *substream,
2395df7f71dSDan Murphy 			     struct snd_pcm_hw_params *params,
2405df7f71dSDan Murphy 			     struct snd_soc_dai *dai)
2415df7f71dSDan Murphy {
24279a4ad1eSKuninori Morimoto 	struct snd_soc_component *component = dai->component;
24379a4ad1eSKuninori Morimoto 	struct tas2552_data *tas2552 = dev_get_drvdata(component->dev);
244d20b098dSPeter Ujfalusi 	int cpf;
245a571cb17SPeter Ujfalusi 	u8 ser_ctrl1_reg, wclk_rate;
246d20b098dSPeter Ujfalusi 
247d20b098dSPeter Ujfalusi 	switch (params_width(params)) {
248d20b098dSPeter Ujfalusi 	case 16:
249d20b098dSPeter Ujfalusi 		ser_ctrl1_reg = TAS2552_WORDLENGTH_16BIT;
250d20b098dSPeter Ujfalusi 		cpf = 32 + tas2552->tdm_delay;
251d20b098dSPeter Ujfalusi 		break;
252d20b098dSPeter Ujfalusi 	case 20:
253d20b098dSPeter Ujfalusi 		ser_ctrl1_reg = TAS2552_WORDLENGTH_20BIT;
254d20b098dSPeter Ujfalusi 		cpf = 64 + tas2552->tdm_delay;
255d20b098dSPeter Ujfalusi 		break;
256d20b098dSPeter Ujfalusi 	case 24:
257d20b098dSPeter Ujfalusi 		ser_ctrl1_reg = TAS2552_WORDLENGTH_24BIT;
258d20b098dSPeter Ujfalusi 		cpf = 64 + tas2552->tdm_delay;
259d20b098dSPeter Ujfalusi 		break;
260d20b098dSPeter Ujfalusi 	case 32:
261d20b098dSPeter Ujfalusi 		ser_ctrl1_reg = TAS2552_WORDLENGTH_32BIT;
262d20b098dSPeter Ujfalusi 		cpf = 64 + tas2552->tdm_delay;
263d20b098dSPeter Ujfalusi 		break;
264d20b098dSPeter Ujfalusi 	default:
26579a4ad1eSKuninori Morimoto 		dev_err(component->dev, "Not supported sample size: %d\n",
266d20b098dSPeter Ujfalusi 			params_width(params));
267d20b098dSPeter Ujfalusi 		return -EINVAL;
268d20b098dSPeter Ujfalusi 	}
269d20b098dSPeter Ujfalusi 
270d20b098dSPeter Ujfalusi 	if (cpf <= 32)
271d20b098dSPeter Ujfalusi 		ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_32;
272d20b098dSPeter Ujfalusi 	else if (cpf <= 64)
273d20b098dSPeter Ujfalusi 		ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_64;
274d20b098dSPeter Ujfalusi 	else if (cpf <= 128)
275d20b098dSPeter Ujfalusi 		ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_128;
276d20b098dSPeter Ujfalusi 	else
277d20b098dSPeter Ujfalusi 		ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_256;
278d20b098dSPeter Ujfalusi 
27979a4ad1eSKuninori Morimoto 	snd_soc_component_update_bits(component, TAS2552_SER_CTRL_1,
280d20b098dSPeter Ujfalusi 			    TAS2552_WORDLENGTH_MASK | TAS2552_CLKSPERFRAME_MASK,
281d20b098dSPeter Ujfalusi 			    ser_ctrl1_reg);
2825df7f71dSDan Murphy 
283a571cb17SPeter Ujfalusi 	switch (params_rate(params)) {
284a571cb17SPeter Ujfalusi 	case 8000:
285a571cb17SPeter Ujfalusi 		wclk_rate = TAS2552_WCLK_FREQ_8KHZ;
286a571cb17SPeter Ujfalusi 		break;
287a571cb17SPeter Ujfalusi 	case 11025:
288a571cb17SPeter Ujfalusi 	case 12000:
289a571cb17SPeter Ujfalusi 		wclk_rate = TAS2552_WCLK_FREQ_11_12KHZ;
290a571cb17SPeter Ujfalusi 		break;
291a571cb17SPeter Ujfalusi 	case 16000:
292a571cb17SPeter Ujfalusi 		wclk_rate = TAS2552_WCLK_FREQ_16KHZ;
293a571cb17SPeter Ujfalusi 		break;
294a571cb17SPeter Ujfalusi 	case 22050:
295a571cb17SPeter Ujfalusi 	case 24000:
296a571cb17SPeter Ujfalusi 		wclk_rate = TAS2552_WCLK_FREQ_22_24KHZ;
297a571cb17SPeter Ujfalusi 		break;
298a571cb17SPeter Ujfalusi 	case 32000:
299a571cb17SPeter Ujfalusi 		wclk_rate = TAS2552_WCLK_FREQ_32KHZ;
300a571cb17SPeter Ujfalusi 		break;
301a571cb17SPeter Ujfalusi 	case 44100:
302a571cb17SPeter Ujfalusi 	case 48000:
303a571cb17SPeter Ujfalusi 		wclk_rate = TAS2552_WCLK_FREQ_44_48KHZ;
304a571cb17SPeter Ujfalusi 		break;
305a571cb17SPeter Ujfalusi 	case 88200:
306a571cb17SPeter Ujfalusi 	case 96000:
307a571cb17SPeter Ujfalusi 		wclk_rate = TAS2552_WCLK_FREQ_88_96KHZ;
308a571cb17SPeter Ujfalusi 		break;
309a571cb17SPeter Ujfalusi 	case 176400:
310a571cb17SPeter Ujfalusi 	case 192000:
311a571cb17SPeter Ujfalusi 		wclk_rate = TAS2552_WCLK_FREQ_176_192KHZ;
312a571cb17SPeter Ujfalusi 		break;
313a571cb17SPeter Ujfalusi 	default:
31479a4ad1eSKuninori Morimoto 		dev_err(component->dev, "Not supported sample rate: %d\n",
315a571cb17SPeter Ujfalusi 			params_rate(params));
316a571cb17SPeter Ujfalusi 		return -EINVAL;
317a571cb17SPeter Ujfalusi 	}
318a571cb17SPeter Ujfalusi 
31979a4ad1eSKuninori Morimoto 	snd_soc_component_update_bits(component, TAS2552_CFG_3, TAS2552_WCLK_FREQ_MASK,
320a571cb17SPeter Ujfalusi 			    wclk_rate);
321a571cb17SPeter Ujfalusi 
32279a4ad1eSKuninori Morimoto 	return tas2552_setup_pll(component, params);
3235df7f71dSDan Murphy }
3245df7f71dSDan Murphy 
3251b68c7dcSPeter Ujfalusi #define TAS2552_DAI_FMT_MASK	(TAS2552_BCLKDIR | \
3261b68c7dcSPeter Ujfalusi 				 TAS2552_WCLKDIR | \
3271b68c7dcSPeter Ujfalusi 				 TAS2552_DATAFORMAT_MASK)
3283f747a81SPeter Ujfalusi static int tas2552_prepare(struct snd_pcm_substream *substream,
3293f747a81SPeter Ujfalusi 			   struct snd_soc_dai *dai)
3303f747a81SPeter Ujfalusi {
33179a4ad1eSKuninori Morimoto 	struct snd_soc_component *component = dai->component;
33279a4ad1eSKuninori Morimoto 	struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
3333f747a81SPeter Ujfalusi 	int delay = 0;
3343f747a81SPeter Ujfalusi 
3353f747a81SPeter Ujfalusi 	/* TDM slot selection only valid in DSP_A/_B mode */
3363f747a81SPeter Ujfalusi 	if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_A)
3373f747a81SPeter Ujfalusi 		delay += (tas2552->tdm_delay + 1);
3383f747a81SPeter Ujfalusi 	else if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_B)
3393f747a81SPeter Ujfalusi 		delay += tas2552->tdm_delay;
3403f747a81SPeter Ujfalusi 
3413f747a81SPeter Ujfalusi 	/* Configure data delay */
34279a4ad1eSKuninori Morimoto 	snd_soc_component_write(component, TAS2552_SER_CTRL_2, delay);
3433f747a81SPeter Ujfalusi 
3443f747a81SPeter Ujfalusi 	return 0;
3453f747a81SPeter Ujfalusi }
3463f747a81SPeter Ujfalusi 
3475df7f71dSDan Murphy static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
3485df7f71dSDan Murphy {
34979a4ad1eSKuninori Morimoto 	struct snd_soc_component *component = dai->component;
35079a4ad1eSKuninori Morimoto 	struct tas2552_data *tas2552 = dev_get_drvdata(component->dev);
3515df7f71dSDan Murphy 	u8 serial_format;
3525df7f71dSDan Murphy 
3536b486af2SMark Brown 	switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
3546b486af2SMark Brown 	case SND_SOC_DAIFMT_CBC_CFC:
3555df7f71dSDan Murphy 		serial_format = 0x00;
3565df7f71dSDan Murphy 		break;
3576b486af2SMark Brown 	case SND_SOC_DAIFMT_CBC_CFP:
3581b68c7dcSPeter Ujfalusi 		serial_format = TAS2552_WCLKDIR;
3595df7f71dSDan Murphy 		break;
3606b486af2SMark Brown 	case SND_SOC_DAIFMT_CBP_CFC:
3611b68c7dcSPeter Ujfalusi 		serial_format = TAS2552_BCLKDIR;
3625df7f71dSDan Murphy 		break;
3636b486af2SMark Brown 	case SND_SOC_DAIFMT_CBP_CFP:
3641b68c7dcSPeter Ujfalusi 		serial_format = (TAS2552_BCLKDIR | TAS2552_WCLKDIR);
3655df7f71dSDan Murphy 		break;
3665df7f71dSDan Murphy 	default:
36779a4ad1eSKuninori Morimoto 		dev_vdbg(component->dev, "DAI Format master is not found\n");
3685df7f71dSDan Murphy 		return -EINVAL;
3695df7f71dSDan Murphy 	}
3705df7f71dSDan Murphy 
3714c331373SPeter Ujfalusi 	switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK |
3724c331373SPeter Ujfalusi 		       SND_SOC_DAIFMT_INV_MASK)) {
3734c331373SPeter Ujfalusi 	case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF):
3745df7f71dSDan Murphy 		break;
3754c331373SPeter Ujfalusi 	case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF):
3764c331373SPeter Ujfalusi 	case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF):
3771b68c7dcSPeter Ujfalusi 		serial_format |= TAS2552_DATAFORMAT_DSP;
3785df7f71dSDan Murphy 		break;
3794c331373SPeter Ujfalusi 	case (SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_NB_NF):
3801b68c7dcSPeter Ujfalusi 		serial_format |= TAS2552_DATAFORMAT_RIGHT_J;
3815df7f71dSDan Murphy 		break;
3824c331373SPeter Ujfalusi 	case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF):
3831b68c7dcSPeter Ujfalusi 		serial_format |= TAS2552_DATAFORMAT_LEFT_J;
3845df7f71dSDan Murphy 		break;
3855df7f71dSDan Murphy 	default:
38679a4ad1eSKuninori Morimoto 		dev_vdbg(component->dev, "DAI Format is not found\n");
3875df7f71dSDan Murphy 		return -EINVAL;
3885df7f71dSDan Murphy 	}
3893f747a81SPeter Ujfalusi 	tas2552->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
3905df7f71dSDan Murphy 
39179a4ad1eSKuninori Morimoto 	snd_soc_component_update_bits(component, TAS2552_SER_CTRL_1, TAS2552_DAI_FMT_MASK,
3925df7f71dSDan Murphy 			    serial_format);
3935df7f71dSDan Murphy 	return 0;
3945df7f71dSDan Murphy }
3955df7f71dSDan Murphy 
3965df7f71dSDan Murphy static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
3975df7f71dSDan Murphy 				  unsigned int freq, int dir)
3985df7f71dSDan Murphy {
39979a4ad1eSKuninori Morimoto 	struct snd_soc_component *component = dai->component;
40079a4ad1eSKuninori Morimoto 	struct tas2552_data *tas2552 = dev_get_drvdata(component->dev);
4019d87a888SPeter Ujfalusi 	u8 reg, mask, val;
4025df7f71dSDan Murphy 
4039d87a888SPeter Ujfalusi 	switch (clk_id) {
4049d87a888SPeter Ujfalusi 	case TAS2552_PLL_CLKIN_MCLK:
4059d87a888SPeter Ujfalusi 	case TAS2552_PLL_CLKIN_IVCLKIN:
4061014f7efSPeter Ujfalusi 		if (freq < 512000 || freq > 24576000) {
4071014f7efSPeter Ujfalusi 			/* out of range PLL_CLKIN, fall back to use BCLK */
40879a4ad1eSKuninori Morimoto 			dev_warn(component->dev, "Out of range PLL_CLKIN: %u\n",
4091014f7efSPeter Ujfalusi 				 freq);
4101014f7efSPeter Ujfalusi 			clk_id = TAS2552_PLL_CLKIN_BCLK;
4111014f7efSPeter Ujfalusi 			freq = 0;
4121014f7efSPeter Ujfalusi 		}
4133e146b55SGustavo A. R. Silva 		fallthrough;
4141014f7efSPeter Ujfalusi 	case TAS2552_PLL_CLKIN_BCLK:
4159d87a888SPeter Ujfalusi 	case TAS2552_PLL_CLKIN_1_8_FIXED:
4169d87a888SPeter Ujfalusi 		mask = TAS2552_PLL_SRC_MASK;
4179d87a888SPeter Ujfalusi 		val = (clk_id << 3) & mask; /* bit 4:5 in the register */
4189d87a888SPeter Ujfalusi 		reg = TAS2552_CFG_1;
4191014f7efSPeter Ujfalusi 		tas2552->pll_clk_id = clk_id;
42016bd3952SPeter Ujfalusi 		tas2552->pll_clkin = freq;
4219d87a888SPeter Ujfalusi 		break;
4229d87a888SPeter Ujfalusi 	case TAS2552_PDM_CLK_PLL:
4239d87a888SPeter Ujfalusi 	case TAS2552_PDM_CLK_IVCLKIN:
4249d87a888SPeter Ujfalusi 	case TAS2552_PDM_CLK_BCLK:
4259d87a888SPeter Ujfalusi 	case TAS2552_PDM_CLK_MCLK:
4269d87a888SPeter Ujfalusi 		mask = TAS2552_PDM_CLK_SEL_MASK;
4279d87a888SPeter Ujfalusi 		val = (clk_id >> 1) & mask; /* bit 0:1 in the register */
4289d87a888SPeter Ujfalusi 		reg = TAS2552_PDM_CFG;
4291014f7efSPeter Ujfalusi 		tas2552->pdm_clk_id = clk_id;
4309d87a888SPeter Ujfalusi 		tas2552->pdm_clk = freq;
4319d87a888SPeter Ujfalusi 		break;
4329d87a888SPeter Ujfalusi 	default:
43379a4ad1eSKuninori Morimoto 		dev_err(component->dev, "Invalid clk id: %d\n", clk_id);
4349d87a888SPeter Ujfalusi 		return -EINVAL;
4359d87a888SPeter Ujfalusi 	}
4369d87a888SPeter Ujfalusi 
43779a4ad1eSKuninori Morimoto 	snd_soc_component_update_bits(component, reg, mask, val);
4385df7f71dSDan Murphy 
4395df7f71dSDan Murphy 	return 0;
4405df7f71dSDan Murphy }
4415df7f71dSDan Murphy 
4423f747a81SPeter Ujfalusi static int tas2552_set_dai_tdm_slot(struct snd_soc_dai *dai,
4433f747a81SPeter Ujfalusi 				    unsigned int tx_mask, unsigned int rx_mask,
4443f747a81SPeter Ujfalusi 				    int slots, int slot_width)
4453f747a81SPeter Ujfalusi {
44679a4ad1eSKuninori Morimoto 	struct snd_soc_component *component = dai->component;
44779a4ad1eSKuninori Morimoto 	struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
4483f747a81SPeter Ujfalusi 	unsigned int lsb;
4493f747a81SPeter Ujfalusi 
4503f747a81SPeter Ujfalusi 	if (unlikely(!tx_mask)) {
45179a4ad1eSKuninori Morimoto 		dev_err(component->dev, "tx masks need to be non 0\n");
4523f747a81SPeter Ujfalusi 		return -EINVAL;
4533f747a81SPeter Ujfalusi 	}
4543f747a81SPeter Ujfalusi 
4553f747a81SPeter Ujfalusi 	/* TDM based on DSP mode requires slots to be adjacent */
4563f747a81SPeter Ujfalusi 	lsb = __ffs(tx_mask);
4573f747a81SPeter Ujfalusi 	if ((lsb + 1) != __fls(tx_mask)) {
45879a4ad1eSKuninori Morimoto 		dev_err(component->dev, "Invalid mask, slots must be adjacent\n");
4593f747a81SPeter Ujfalusi 		return -EINVAL;
4603f747a81SPeter Ujfalusi 	}
4613f747a81SPeter Ujfalusi 
4623f747a81SPeter Ujfalusi 	tas2552->tdm_delay = lsb * slot_width;
4633f747a81SPeter Ujfalusi 
4643f747a81SPeter Ujfalusi 	/* DOUT in high-impedance on inactive bit clocks */
46579a4ad1eSKuninori Morimoto 	snd_soc_component_update_bits(component, TAS2552_DOUT,
4663f747a81SPeter Ujfalusi 			    TAS2552_SDOUT_TRISTATE, TAS2552_SDOUT_TRISTATE);
4673f747a81SPeter Ujfalusi 
4683f747a81SPeter Ujfalusi 	return 0;
4693f747a81SPeter Ujfalusi }
4703f747a81SPeter Ujfalusi 
47138803ce7SKuninori Morimoto static int tas2552_mute(struct snd_soc_dai *dai, int mute, int direction)
4725df7f71dSDan Murphy {
473e3606aa4SPeter Ujfalusi 	u8 cfg1_reg = 0;
47479a4ad1eSKuninori Morimoto 	struct snd_soc_component *component = dai->component;
4755df7f71dSDan Murphy 
4765df7f71dSDan Murphy 	if (mute)
477e3606aa4SPeter Ujfalusi 		cfg1_reg |= TAS2552_MUTE;
4785df7f71dSDan Murphy 
47979a4ad1eSKuninori Morimoto 	snd_soc_component_update_bits(component, TAS2552_CFG_1, TAS2552_MUTE, cfg1_reg);
4805df7f71dSDan Murphy 
4815df7f71dSDan Murphy 	return 0;
4825df7f71dSDan Murphy }
4835df7f71dSDan Murphy 
484641d334bSRafael J. Wysocki #ifdef CONFIG_PM
4855df7f71dSDan Murphy static int tas2552_runtime_suspend(struct device *dev)
4865df7f71dSDan Murphy {
4875df7f71dSDan Murphy 	struct tas2552_data *tas2552 = dev_get_drvdata(dev);
4885df7f71dSDan Murphy 
489dd6e3053SPeter Ujfalusi 	tas2552_sw_shutdown(tas2552, 1);
4905df7f71dSDan Murphy 
4915df7f71dSDan Murphy 	regcache_cache_only(tas2552->regmap, true);
4925df7f71dSDan Murphy 	regcache_mark_dirty(tas2552->regmap);
4935df7f71dSDan Murphy 
494e295a4a4SDan Murphy 	gpiod_set_value(tas2552->enable_gpio, 0);
495e295a4a4SDan Murphy 
4965df7f71dSDan Murphy 	return 0;
4975df7f71dSDan Murphy }
4985df7f71dSDan Murphy 
4995df7f71dSDan Murphy static int tas2552_runtime_resume(struct device *dev)
5005df7f71dSDan Murphy {
5015df7f71dSDan Murphy 	struct tas2552_data *tas2552 = dev_get_drvdata(dev);
5025df7f71dSDan Murphy 
5035df7f71dSDan Murphy 	gpiod_set_value(tas2552->enable_gpio, 1);
5045df7f71dSDan Murphy 
505dd6e3053SPeter Ujfalusi 	tas2552_sw_shutdown(tas2552, 0);
5065df7f71dSDan Murphy 
5075df7f71dSDan Murphy 	regcache_cache_only(tas2552->regmap, false);
5085df7f71dSDan Murphy 	regcache_sync(tas2552->regmap);
5095df7f71dSDan Murphy 
5105df7f71dSDan Murphy 	return 0;
5115df7f71dSDan Murphy }
5125df7f71dSDan Murphy #endif
5135df7f71dSDan Murphy 
5145df7f71dSDan Murphy static const struct dev_pm_ops tas2552_pm = {
5155df7f71dSDan Murphy 	SET_RUNTIME_PM_OPS(tas2552_runtime_suspend, tas2552_runtime_resume,
5165df7f71dSDan Murphy 			   NULL)
5175df7f71dSDan Murphy };
5185df7f71dSDan Murphy 
51964793047SAxel Lin static const struct snd_soc_dai_ops tas2552_speaker_dai_ops = {
5205df7f71dSDan Murphy 	.hw_params	= tas2552_hw_params,
5213f747a81SPeter Ujfalusi 	.prepare	= tas2552_prepare,
5225df7f71dSDan Murphy 	.set_sysclk	= tas2552_set_dai_sysclk,
5235df7f71dSDan Murphy 	.set_fmt	= tas2552_set_dai_fmt,
5243f747a81SPeter Ujfalusi 	.set_tdm_slot	= tas2552_set_dai_tdm_slot,
52538803ce7SKuninori Morimoto 	.mute_stream	= tas2552_mute,
52638803ce7SKuninori Morimoto 	.no_capture_mute = 1,
5275df7f71dSDan Murphy };
5285df7f71dSDan Murphy 
5295df7f71dSDan Murphy /* Formats supported by TAS2552 driver. */
5305df7f71dSDan Murphy #define TAS2552_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
5315df7f71dSDan Murphy 			 SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
5325df7f71dSDan Murphy 
5335df7f71dSDan Murphy /* TAS2552 dai structure. */
5345df7f71dSDan Murphy static struct snd_soc_dai_driver tas2552_dai[] = {
5355df7f71dSDan Murphy 	{
5365df7f71dSDan Murphy 		.name = "tas2552-amplifier",
5375df7f71dSDan Murphy 		.playback = {
538a7a8e994SDan Murphy 			.stream_name = "Playback",
5395df7f71dSDan Murphy 			.channels_min = 2,
5405df7f71dSDan Murphy 			.channels_max = 2,
5415df7f71dSDan Murphy 			.rates = SNDRV_PCM_RATE_8000_192000,
5425df7f71dSDan Murphy 			.formats = TAS2552_FORMATS,
5435df7f71dSDan Murphy 		},
544*7078ac4fSShenghao Ding 		.capture = {
545*7078ac4fSShenghao Ding 			.stream_name = "Capture",
546*7078ac4fSShenghao Ding 			.channels_min = 2,
547*7078ac4fSShenghao Ding 			.channels_max = 2,
548*7078ac4fSShenghao Ding 			.rates = SNDRV_PCM_RATE_8000_192000,
549*7078ac4fSShenghao Ding 			.formats = TAS2552_FORMATS,
550*7078ac4fSShenghao Ding 		},
5515df7f71dSDan Murphy 		.ops = &tas2552_speaker_dai_ops,
5525df7f71dSDan Murphy 	},
5535df7f71dSDan Murphy };
5545df7f71dSDan Murphy 
5555df7f71dSDan Murphy /*
5565df7f71dSDan Murphy  * DAC digital volumes. From -7 to 24 dB in 1 dB steps
5575df7f71dSDan Murphy  */
558e2600460SAndreas Dannenberg static DECLARE_TLV_DB_SCALE(dac_tlv, -700, 100, 0);
5595df7f71dSDan Murphy 
5602962cb52SPeter Ujfalusi static const char * const tas2552_din_source_select[] = {
5612962cb52SPeter Ujfalusi 	"Muted",
5622962cb52SPeter Ujfalusi 	"Left",
5632962cb52SPeter Ujfalusi 	"Right",
5642962cb52SPeter Ujfalusi 	"Left + Right average",
5652962cb52SPeter Ujfalusi };
5662962cb52SPeter Ujfalusi static SOC_ENUM_SINGLE_DECL(tas2552_din_source_enum,
5672962cb52SPeter Ujfalusi 			    TAS2552_CFG_3, 3,
5682962cb52SPeter Ujfalusi 			    tas2552_din_source_select);
5692962cb52SPeter Ujfalusi 
5705df7f71dSDan Murphy static const struct snd_kcontrol_new tas2552_snd_controls[] = {
5715df7f71dSDan Murphy 	SOC_SINGLE_TLV("Speaker Driver Playback Volume",
572dd6ae3bcSPeter Ujfalusi 			 TAS2552_PGA_GAIN, 0, 0x1f, 0, dac_tlv),
5732962cb52SPeter Ujfalusi 	SOC_ENUM("DIN source", tas2552_din_source_enum),
5745df7f71dSDan Murphy };
5755df7f71dSDan Murphy 
57679a4ad1eSKuninori Morimoto static int tas2552_component_probe(struct snd_soc_component *component)
5775df7f71dSDan Murphy {
57879a4ad1eSKuninori Morimoto 	struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
5795df7f71dSDan Murphy 	int ret;
5805df7f71dSDan Murphy 
58179a4ad1eSKuninori Morimoto 	tas2552->component = component;
5825df7f71dSDan Murphy 
5835df7f71dSDan Murphy 	ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies),
5845df7f71dSDan Murphy 				    tas2552->supplies);
5855df7f71dSDan Murphy 
5865df7f71dSDan Murphy 	if (ret != 0) {
58779a4ad1eSKuninori Morimoto 		dev_err(component->dev, "Failed to enable supplies: %d\n",
5885df7f71dSDan Murphy 			ret);
5895df7f71dSDan Murphy 		return ret;
5905df7f71dSDan Murphy 	}
5915df7f71dSDan Murphy 
5925df7f71dSDan Murphy 	gpiod_set_value(tas2552->enable_gpio, 1);
5935df7f71dSDan Murphy 
59405b71fb2SPierre-Louis Bossart 	ret = pm_runtime_resume_and_get(component->dev);
5955df7f71dSDan Murphy 	if (ret < 0) {
59679a4ad1eSKuninori Morimoto 		dev_err(component->dev, "Enabling device failed: %d\n",
5975df7f71dSDan Murphy 			ret);
5985df7f71dSDan Murphy 		goto probe_fail;
5995df7f71dSDan Murphy 	}
6005df7f71dSDan Murphy 
60179a4ad1eSKuninori Morimoto 	snd_soc_component_update_bits(component, TAS2552_CFG_1, TAS2552_MUTE, TAS2552_MUTE);
60279a4ad1eSKuninori Morimoto 	snd_soc_component_write(component, TAS2552_CFG_3, TAS2552_I2S_OUT_SEL |
603a571cb17SPeter Ujfalusi 					    TAS2552_DIN_SRC_SEL_AVG_L_R);
60479a4ad1eSKuninori Morimoto 	snd_soc_component_write(component, TAS2552_OUTPUT_DATA,
605b2822f19SPeter Ujfalusi 		      TAS2552_PDM_DATA_SEL_V_I |
606b2822f19SPeter Ujfalusi 		      TAS2552_R_DATA_OUT(TAS2552_DATA_OUT_V_DATA));
60779a4ad1eSKuninori Morimoto 	snd_soc_component_write(component, TAS2552_BOOST_APT_CTRL, TAS2552_APT_DELAY_200 |
6082a9dd1dbSPeter Ujfalusi 						     TAS2552_APT_THRESH_20_17);
6095df7f71dSDan Murphy 
61079a4ad1eSKuninori Morimoto 	snd_soc_component_write(component, TAS2552_CFG_2, TAS2552_BOOST_EN | TAS2552_APT_EN |
6114afdd89dSPeter Ujfalusi 					    TAS2552_LIM_EN);
612a7a8e994SDan Murphy 
6135df7f71dSDan Murphy 	return 0;
6145df7f71dSDan Murphy 
6155df7f71dSDan Murphy probe_fail:
6160d71a5cfSDinghao Liu 	pm_runtime_put_noidle(component->dev);
6175df7f71dSDan Murphy 	gpiod_set_value(tas2552->enable_gpio, 0);
6185df7f71dSDan Murphy 
6195df7f71dSDan Murphy 	regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies),
6205df7f71dSDan Murphy 					tas2552->supplies);
6216f2daf82SFabio Estevam 	return ret;
6225df7f71dSDan Murphy }
6235df7f71dSDan Murphy 
62479a4ad1eSKuninori Morimoto static void tas2552_component_remove(struct snd_soc_component *component)
6255df7f71dSDan Murphy {
62679a4ad1eSKuninori Morimoto 	struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
6275df7f71dSDan Murphy 
62879a4ad1eSKuninori Morimoto 	pm_runtime_put(component->dev);
629e295a4a4SDan Murphy 
6305df7f71dSDan Murphy 	gpiod_set_value(tas2552->enable_gpio, 0);
6315df7f71dSDan Murphy };
6325df7f71dSDan Murphy 
6335df7f71dSDan Murphy #ifdef CONFIG_PM
63479a4ad1eSKuninori Morimoto static int tas2552_suspend(struct snd_soc_component *component)
6355df7f71dSDan Murphy {
63679a4ad1eSKuninori Morimoto 	struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
6375df7f71dSDan Murphy 	int ret;
6385df7f71dSDan Murphy 
6395df7f71dSDan Murphy 	ret = regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies),
6405df7f71dSDan Murphy 					tas2552->supplies);
6415df7f71dSDan Murphy 
6425df7f71dSDan Murphy 	if (ret != 0)
64379a4ad1eSKuninori Morimoto 		dev_err(component->dev, "Failed to disable supplies: %d\n",
6445df7f71dSDan Murphy 			ret);
64512dc0f3bSFabio Estevam 	return ret;
6465df7f71dSDan Murphy }
6475df7f71dSDan Murphy 
64879a4ad1eSKuninori Morimoto static int tas2552_resume(struct snd_soc_component *component)
6495df7f71dSDan Murphy {
65079a4ad1eSKuninori Morimoto 	struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
6515df7f71dSDan Murphy 	int ret;
6525df7f71dSDan Murphy 
6535df7f71dSDan Murphy 	ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies),
6545df7f71dSDan Murphy 				    tas2552->supplies);
6555df7f71dSDan Murphy 
6565df7f71dSDan Murphy 	if (ret != 0) {
65779a4ad1eSKuninori Morimoto 		dev_err(component->dev, "Failed to enable supplies: %d\n",
6585df7f71dSDan Murphy 			ret);
6595df7f71dSDan Murphy 	}
6605df7f71dSDan Murphy 
66112dc0f3bSFabio Estevam 	return ret;
6625df7f71dSDan Murphy }
6635df7f71dSDan Murphy #else
6645df7f71dSDan Murphy #define tas2552_suspend NULL
6655df7f71dSDan Murphy #define tas2552_resume NULL
6665df7f71dSDan Murphy #endif
6675df7f71dSDan Murphy 
66879a4ad1eSKuninori Morimoto static const struct snd_soc_component_driver soc_component_dev_tas2552 = {
66979a4ad1eSKuninori Morimoto 	.probe			= tas2552_component_probe,
67079a4ad1eSKuninori Morimoto 	.remove			= tas2552_component_remove,
6715df7f71dSDan Murphy 	.suspend		= tas2552_suspend,
6725df7f71dSDan Murphy 	.resume			= tas2552_resume,
6735df7f71dSDan Murphy 	.controls		= tas2552_snd_controls,
6745df7f71dSDan Murphy 	.num_controls		= ARRAY_SIZE(tas2552_snd_controls),
675e3f1ff31SLars-Peter Clausen 	.dapm_widgets		= tas2552_dapm_widgets,
676e3f1ff31SLars-Peter Clausen 	.num_dapm_widgets	= ARRAY_SIZE(tas2552_dapm_widgets),
677e3f1ff31SLars-Peter Clausen 	.dapm_routes		= tas2552_audio_map,
678e3f1ff31SLars-Peter Clausen 	.num_dapm_routes	= ARRAY_SIZE(tas2552_audio_map),
67979a4ad1eSKuninori Morimoto 	.idle_bias_on		= 1,
68079a4ad1eSKuninori Morimoto 	.endianness		= 1,
6815df7f71dSDan Murphy };
6825df7f71dSDan Murphy 
6835df7f71dSDan Murphy static const struct regmap_config tas2552_regmap_config = {
6845df7f71dSDan Murphy 	.reg_bits = 8,
6855df7f71dSDan Murphy 	.val_bits = 8,
6865df7f71dSDan Murphy 
6875df7f71dSDan Murphy 	.max_register = TAS2552_MAX_REG,
6885df7f71dSDan Murphy 	.reg_defaults = tas2552_reg_defs,
6895df7f71dSDan Murphy 	.num_reg_defaults = ARRAY_SIZE(tas2552_reg_defs),
6905df7f71dSDan Murphy 	.cache_type = REGCACHE_RBTREE,
6915df7f71dSDan Murphy };
6925df7f71dSDan Murphy 
693ad11678fSStephen Kitt static int tas2552_probe(struct i2c_client *client)
6945df7f71dSDan Murphy {
6955df7f71dSDan Murphy 	struct device *dev;
6965df7f71dSDan Murphy 	struct tas2552_data *data;
6975df7f71dSDan Murphy 	int ret;
6985df7f71dSDan Murphy 	int i;
6995df7f71dSDan Murphy 
7005df7f71dSDan Murphy 	dev = &client->dev;
7015df7f71dSDan Murphy 	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
7025df7f71dSDan Murphy 	if (data == NULL)
7035df7f71dSDan Murphy 		return -ENOMEM;
7045df7f71dSDan Murphy 
7058604bc28SAxel Lin 	data->enable_gpio = devm_gpiod_get_optional(dev, "enable",
7068604bc28SAxel Lin 						    GPIOD_OUT_LOW);
7078604bc28SAxel Lin 	if (IS_ERR(data->enable_gpio))
7088604bc28SAxel Lin 		return PTR_ERR(data->enable_gpio);
7095df7f71dSDan Murphy 
7105df7f71dSDan Murphy 	data->tas2552_client = client;
7115df7f71dSDan Murphy 	data->regmap = devm_regmap_init_i2c(client, &tas2552_regmap_config);
7125df7f71dSDan Murphy 	if (IS_ERR(data->regmap)) {
7135df7f71dSDan Murphy 		ret = PTR_ERR(data->regmap);
7145df7f71dSDan Murphy 		dev_err(&client->dev, "Failed to allocate register map: %d\n",
7155df7f71dSDan Murphy 			ret);
7165df7f71dSDan Murphy 		return ret;
7175df7f71dSDan Murphy 	}
7185df7f71dSDan Murphy 
7195df7f71dSDan Murphy 	for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
7205df7f71dSDan Murphy 		data->supplies[i].supply = tas2552_supply_names[i];
7215df7f71dSDan Murphy 
7225df7f71dSDan Murphy 	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies),
7235df7f71dSDan Murphy 				      data->supplies);
724c62f9d8fSAxel Lin 	if (ret != 0) {
7255df7f71dSDan Murphy 		dev_err(dev, "Failed to request supplies: %d\n", ret);
726c62f9d8fSAxel Lin 		return ret;
727c62f9d8fSAxel Lin 	}
7285df7f71dSDan Murphy 
7295df7f71dSDan Murphy 	pm_runtime_set_active(&client->dev);
7305df7f71dSDan Murphy 	pm_runtime_set_autosuspend_delay(&client->dev, 1000);
7315df7f71dSDan Murphy 	pm_runtime_use_autosuspend(&client->dev);
7325df7f71dSDan Murphy 	pm_runtime_enable(&client->dev);
7335df7f71dSDan Murphy 	pm_runtime_mark_last_busy(&client->dev);
7345df7f71dSDan Murphy 	pm_runtime_put_sync_autosuspend(&client->dev);
7355df7f71dSDan Murphy 
7365df7f71dSDan Murphy 	dev_set_drvdata(&client->dev, data);
7375df7f71dSDan Murphy 
73879a4ad1eSKuninori Morimoto 	ret = devm_snd_soc_register_component(&client->dev,
73979a4ad1eSKuninori Morimoto 				      &soc_component_dev_tas2552,
7405df7f71dSDan Murphy 				      tas2552_dai, ARRAY_SIZE(tas2552_dai));
7417b3f5b20SDinghao Liu 	if (ret < 0) {
74279a4ad1eSKuninori Morimoto 		dev_err(&client->dev, "Failed to register component: %d\n", ret);
7437b3f5b20SDinghao Liu 		pm_runtime_get_noresume(&client->dev);
7447b3f5b20SDinghao Liu 	}
7455df7f71dSDan Murphy 
746c62f9d8fSAxel Lin 	return ret;
7475df7f71dSDan Murphy }
7485df7f71dSDan Murphy 
749ed5c2f5fSUwe Kleine-König static void tas2552_i2c_remove(struct i2c_client *client)
7505df7f71dSDan Murphy {
7514785ed89SPeter Ujfalusi 	pm_runtime_disable(&client->dev);
7525df7f71dSDan Murphy }
7535df7f71dSDan Murphy 
7545df7f71dSDan Murphy static const struct i2c_device_id tas2552_id[] = {
755ba2a2c37SUwe Kleine-König 	{ "tas2552" },
7565df7f71dSDan Murphy 	{ }
7575df7f71dSDan Murphy };
7585df7f71dSDan Murphy MODULE_DEVICE_TABLE(i2c, tas2552_id);
7595df7f71dSDan Murphy 
7605df7f71dSDan Murphy #if IS_ENABLED(CONFIG_OF)
7615df7f71dSDan Murphy static const struct of_device_id tas2552_of_match[] = {
7625df7f71dSDan Murphy 	{ .compatible = "ti,tas2552", },
7635df7f71dSDan Murphy 	{},
7645df7f71dSDan Murphy };
7655df7f71dSDan Murphy MODULE_DEVICE_TABLE(of, tas2552_of_match);
7665df7f71dSDan Murphy #endif
7675df7f71dSDan Murphy 
7685df7f71dSDan Murphy static struct i2c_driver tas2552_i2c_driver = {
7695df7f71dSDan Murphy 	.driver = {
7705df7f71dSDan Murphy 		.name = "tas2552",
7715df7f71dSDan Murphy 		.of_match_table = of_match_ptr(tas2552_of_match),
7725df7f71dSDan Murphy 		.pm = &tas2552_pm,
7735df7f71dSDan Murphy 	},
7749abcd240SUwe Kleine-König 	.probe = tas2552_probe,
7755df7f71dSDan Murphy 	.remove = tas2552_i2c_remove,
7765df7f71dSDan Murphy 	.id_table = tas2552_id,
7775df7f71dSDan Murphy };
7785df7f71dSDan Murphy 
7795df7f71dSDan Murphy module_i2c_driver(tas2552_i2c_driver);
7805df7f71dSDan Murphy 
7815df7f71dSDan Murphy MODULE_AUTHOR("Dan Muprhy <dmurphy@ti.com>");
7825df7f71dSDan Murphy MODULE_DESCRIPTION("TAS2552 Audio amplifier driver");
7835df7f71dSDan Murphy MODULE_LICENSE("GPL");
784