xref: /linux/sound/soc/codecs/tas2552.c (revision 5856d8bd308f9467cefa65d04e184a56a3977559)
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*5856d8bdSAlexander A. Klimov  * Copyright (C) 2014 Texas Instruments Incorporated -  https://www.ti.com
65df7f71dSDan Murphy  *
75df7f71dSDan Murphy  * Author: Dan Murphy <dmurphy@ti.com>
85df7f71dSDan Murphy  */
95df7f71dSDan Murphy 
105df7f71dSDan Murphy #include <linux/module.h>
115df7f71dSDan Murphy #include <linux/errno.h>
125df7f71dSDan Murphy #include <linux/device.h>
135df7f71dSDan Murphy #include <linux/i2c.h>
145df7f71dSDan Murphy #include <linux/gpio.h>
155df7f71dSDan Murphy #include <linux/of_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),
122a7a8e994SDan Murphy 	SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
123a7a8e994SDan Murphy 	SND_SOC_DAPM_OUT_DRV("ClassD", TAS2552_CFG_2, 7, 0, NULL, 0),
124a7a8e994SDan Murphy 	SND_SOC_DAPM_SUPPLY("PLL", TAS2552_CFG_2, 3, 0, NULL, 0),
1257d785025SPeter Ujfalusi 	SND_SOC_DAPM_POST("Post Event", tas2552_post_event),
126a7a8e994SDan Murphy 
127a7a8e994SDan Murphy 	SND_SOC_DAPM_OUTPUT("OUT")
128a7a8e994SDan Murphy };
129a7a8e994SDan Murphy 
130a7a8e994SDan Murphy static const struct snd_soc_dapm_route tas2552_audio_map[] = {
131a7a8e994SDan Murphy 	{"DAC", NULL, "DAC IN"},
132a7a8e994SDan Murphy 	{"Input selection", "Digital", "DAC"},
133a7a8e994SDan Murphy 	{"Input selection", "Analog", "IN"},
134a7a8e994SDan Murphy 	{"ClassD", NULL, "Input selection"},
135a7a8e994SDan Murphy 	{"OUT", NULL, "ClassD"},
136a7a8e994SDan Murphy 	{"ClassD", NULL, "PLL"},
137a7a8e994SDan Murphy };
138a7a8e994SDan Murphy 
139641d334bSRafael J. Wysocki #ifdef CONFIG_PM
140b94525bfSPeter Ujfalusi static void tas2552_sw_shutdown(struct tas2552_data *tas2552, int sw_shutdown)
1415df7f71dSDan Murphy {
142dd6e3053SPeter Ujfalusi 	u8 cfg1_reg = 0;
1435df7f71dSDan Murphy 
14479a4ad1eSKuninori Morimoto 	if (!tas2552->component)
14580ba2669SPeter Ujfalusi 		return;
14680ba2669SPeter Ujfalusi 
1475df7f71dSDan Murphy 	if (sw_shutdown)
1487de544fdSPeter Ujfalusi 		cfg1_reg = TAS2552_SWS;
1495df7f71dSDan Murphy 
15079a4ad1eSKuninori Morimoto 	snd_soc_component_update_bits(tas2552->component, TAS2552_CFG_1, TAS2552_SWS,
1517de544fdSPeter Ujfalusi 			    cfg1_reg);
1525df7f71dSDan Murphy }
153be1aa3eaSThierry Reding #endif
1545df7f71dSDan Murphy 
15579a4ad1eSKuninori Morimoto static int tas2552_setup_pll(struct snd_soc_component *component,
1561014f7efSPeter Ujfalusi 			     struct snd_pcm_hw_params *params)
1571014f7efSPeter Ujfalusi {
15879a4ad1eSKuninori Morimoto 	struct tas2552_data *tas2552 = dev_get_drvdata(component->dev);
1591014f7efSPeter Ujfalusi 	bool bypass_pll = false;
1601014f7efSPeter Ujfalusi 	unsigned int pll_clk = params_rate(params) * 512;
1611014f7efSPeter Ujfalusi 	unsigned int pll_clkin = tas2552->pll_clkin;
1621014f7efSPeter Ujfalusi 	u8 pll_enable;
1631014f7efSPeter Ujfalusi 
1641014f7efSPeter Ujfalusi 	if (!pll_clkin) {
1651014f7efSPeter Ujfalusi 		if (tas2552->pll_clk_id != TAS2552_PLL_CLKIN_BCLK)
1661014f7efSPeter Ujfalusi 			return -EINVAL;
1671014f7efSPeter Ujfalusi 
1681014f7efSPeter Ujfalusi 		pll_clkin = snd_soc_params_to_bclk(params);
1691014f7efSPeter Ujfalusi 		pll_clkin += tas2552->tdm_delay;
1701014f7efSPeter Ujfalusi 	}
1711014f7efSPeter Ujfalusi 
172981abdfeSKuninori Morimoto 	pll_enable = snd_soc_component_read(component, TAS2552_CFG_2) & TAS2552_PLL_ENABLE;
17379a4ad1eSKuninori Morimoto 	snd_soc_component_update_bits(component, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0);
1741014f7efSPeter Ujfalusi 
1751014f7efSPeter Ujfalusi 	if (pll_clkin == pll_clk)
1761014f7efSPeter Ujfalusi 		bypass_pll = true;
1771014f7efSPeter Ujfalusi 
1781014f7efSPeter Ujfalusi 	if (bypass_pll) {
1791014f7efSPeter Ujfalusi 		/* By pass the PLL configuration */
18079a4ad1eSKuninori Morimoto 		snd_soc_component_update_bits(component, TAS2552_PLL_CTRL_2,
1811014f7efSPeter Ujfalusi 				    TAS2552_PLL_BYPASS, TAS2552_PLL_BYPASS);
1821014f7efSPeter Ujfalusi 	} else {
1831014f7efSPeter Ujfalusi 		/* Fill in the PLL control registers for J & D
1841014f7efSPeter Ujfalusi 		 * pll_clk = (.5 * pll_clkin * J.D) / 2^p
1851014f7efSPeter Ujfalusi 		 * Need to fill in J and D here based on incoming freq
1861014f7efSPeter Ujfalusi 		 */
1871bb7cb68SOskar Schirmer 		unsigned int d, q, t;
1881014f7efSPeter Ujfalusi 		u8 j;
1891014f7efSPeter Ujfalusi 		u8 pll_sel = (tas2552->pll_clk_id << 3) & TAS2552_PLL_SRC_MASK;
190981abdfeSKuninori Morimoto 		u8 p = snd_soc_component_read(component, TAS2552_PLL_CTRL_1);
1911014f7efSPeter Ujfalusi 
1921014f7efSPeter Ujfalusi 		p = (p >> 7);
1931014f7efSPeter Ujfalusi 
1941014f7efSPeter Ujfalusi recalc:
1951bb7cb68SOskar Schirmer 		t = (pll_clk * 2) << p;
1961bb7cb68SOskar Schirmer 		j = t / pll_clkin;
1971bb7cb68SOskar Schirmer 		d = t % pll_clkin;
1981bb7cb68SOskar Schirmer 		t = pll_clkin / 10000;
1991bb7cb68SOskar Schirmer 		q = d / (t + 1);
2001bb7cb68SOskar Schirmer 		d = q + ((9999 - pll_clkin % 10000) * (d / t - q)) / 10000;
2011014f7efSPeter Ujfalusi 
2021014f7efSPeter Ujfalusi 		if (d && (pll_clkin < 512000 || pll_clkin > 9200000)) {
2031014f7efSPeter Ujfalusi 			if (tas2552->pll_clk_id == TAS2552_PLL_CLKIN_BCLK) {
2041014f7efSPeter Ujfalusi 				pll_clkin = 1800000;
2051014f7efSPeter Ujfalusi 				pll_sel = (TAS2552_PLL_CLKIN_1_8_FIXED << 3) &
2061014f7efSPeter Ujfalusi 							TAS2552_PLL_SRC_MASK;
2071014f7efSPeter Ujfalusi 			} else {
2081014f7efSPeter Ujfalusi 				pll_clkin = snd_soc_params_to_bclk(params);
2091014f7efSPeter Ujfalusi 				pll_clkin += tas2552->tdm_delay;
2101014f7efSPeter Ujfalusi 				pll_sel = (TAS2552_PLL_CLKIN_BCLK << 3) &
2111014f7efSPeter Ujfalusi 							TAS2552_PLL_SRC_MASK;
2121014f7efSPeter Ujfalusi 			}
2131014f7efSPeter Ujfalusi 			goto recalc;
2141014f7efSPeter Ujfalusi 		}
2151014f7efSPeter Ujfalusi 
21679a4ad1eSKuninori Morimoto 		snd_soc_component_update_bits(component, TAS2552_CFG_1, TAS2552_PLL_SRC_MASK,
2171014f7efSPeter Ujfalusi 				    pll_sel);
2181014f7efSPeter Ujfalusi 
21979a4ad1eSKuninori Morimoto 		snd_soc_component_update_bits(component, TAS2552_PLL_CTRL_1,
2201014f7efSPeter Ujfalusi 				    TAS2552_PLL_J_MASK, j);
2211014f7efSPeter Ujfalusi 		/* Will clear the PLL_BYPASS bit */
22279a4ad1eSKuninori Morimoto 		snd_soc_component_write(component, TAS2552_PLL_CTRL_2,
2231014f7efSPeter Ujfalusi 			      TAS2552_PLL_D_UPPER(d));
22479a4ad1eSKuninori Morimoto 		snd_soc_component_write(component, TAS2552_PLL_CTRL_3,
2251014f7efSPeter Ujfalusi 			      TAS2552_PLL_D_LOWER(d));
2261014f7efSPeter Ujfalusi 	}
2271014f7efSPeter Ujfalusi 
2281014f7efSPeter Ujfalusi 	/* Restore PLL status */
22979a4ad1eSKuninori Morimoto 	snd_soc_component_update_bits(component, TAS2552_CFG_2, TAS2552_PLL_ENABLE,
2301014f7efSPeter Ujfalusi 			    pll_enable);
2311014f7efSPeter Ujfalusi 
2321014f7efSPeter Ujfalusi 	return 0;
2331014f7efSPeter Ujfalusi }
2341014f7efSPeter Ujfalusi 
2355df7f71dSDan Murphy static int tas2552_hw_params(struct snd_pcm_substream *substream,
2365df7f71dSDan Murphy 			     struct snd_pcm_hw_params *params,
2375df7f71dSDan Murphy 			     struct snd_soc_dai *dai)
2385df7f71dSDan Murphy {
23979a4ad1eSKuninori Morimoto 	struct snd_soc_component *component = dai->component;
24079a4ad1eSKuninori Morimoto 	struct tas2552_data *tas2552 = dev_get_drvdata(component->dev);
241d20b098dSPeter Ujfalusi 	int cpf;
242a571cb17SPeter Ujfalusi 	u8 ser_ctrl1_reg, wclk_rate;
243d20b098dSPeter Ujfalusi 
244d20b098dSPeter Ujfalusi 	switch (params_width(params)) {
245d20b098dSPeter Ujfalusi 	case 16:
246d20b098dSPeter Ujfalusi 		ser_ctrl1_reg = TAS2552_WORDLENGTH_16BIT;
247d20b098dSPeter Ujfalusi 		cpf = 32 + tas2552->tdm_delay;
248d20b098dSPeter Ujfalusi 		break;
249d20b098dSPeter Ujfalusi 	case 20:
250d20b098dSPeter Ujfalusi 		ser_ctrl1_reg = TAS2552_WORDLENGTH_20BIT;
251d20b098dSPeter Ujfalusi 		cpf = 64 + tas2552->tdm_delay;
252d20b098dSPeter Ujfalusi 		break;
253d20b098dSPeter Ujfalusi 	case 24:
254d20b098dSPeter Ujfalusi 		ser_ctrl1_reg = TAS2552_WORDLENGTH_24BIT;
255d20b098dSPeter Ujfalusi 		cpf = 64 + tas2552->tdm_delay;
256d20b098dSPeter Ujfalusi 		break;
257d20b098dSPeter Ujfalusi 	case 32:
258d20b098dSPeter Ujfalusi 		ser_ctrl1_reg = TAS2552_WORDLENGTH_32BIT;
259d20b098dSPeter Ujfalusi 		cpf = 64 + tas2552->tdm_delay;
260d20b098dSPeter Ujfalusi 		break;
261d20b098dSPeter Ujfalusi 	default:
26279a4ad1eSKuninori Morimoto 		dev_err(component->dev, "Not supported sample size: %d\n",
263d20b098dSPeter Ujfalusi 			params_width(params));
264d20b098dSPeter Ujfalusi 		return -EINVAL;
265d20b098dSPeter Ujfalusi 	}
266d20b098dSPeter Ujfalusi 
267d20b098dSPeter Ujfalusi 	if (cpf <= 32)
268d20b098dSPeter Ujfalusi 		ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_32;
269d20b098dSPeter Ujfalusi 	else if (cpf <= 64)
270d20b098dSPeter Ujfalusi 		ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_64;
271d20b098dSPeter Ujfalusi 	else if (cpf <= 128)
272d20b098dSPeter Ujfalusi 		ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_128;
273d20b098dSPeter Ujfalusi 	else
274d20b098dSPeter Ujfalusi 		ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_256;
275d20b098dSPeter Ujfalusi 
27679a4ad1eSKuninori Morimoto 	snd_soc_component_update_bits(component, TAS2552_SER_CTRL_1,
277d20b098dSPeter Ujfalusi 			    TAS2552_WORDLENGTH_MASK | TAS2552_CLKSPERFRAME_MASK,
278d20b098dSPeter Ujfalusi 			    ser_ctrl1_reg);
2795df7f71dSDan Murphy 
280a571cb17SPeter Ujfalusi 	switch (params_rate(params)) {
281a571cb17SPeter Ujfalusi 	case 8000:
282a571cb17SPeter Ujfalusi 		wclk_rate = TAS2552_WCLK_FREQ_8KHZ;
283a571cb17SPeter Ujfalusi 		break;
284a571cb17SPeter Ujfalusi 	case 11025:
285a571cb17SPeter Ujfalusi 	case 12000:
286a571cb17SPeter Ujfalusi 		wclk_rate = TAS2552_WCLK_FREQ_11_12KHZ;
287a571cb17SPeter Ujfalusi 		break;
288a571cb17SPeter Ujfalusi 	case 16000:
289a571cb17SPeter Ujfalusi 		wclk_rate = TAS2552_WCLK_FREQ_16KHZ;
290a571cb17SPeter Ujfalusi 		break;
291a571cb17SPeter Ujfalusi 	case 22050:
292a571cb17SPeter Ujfalusi 	case 24000:
293a571cb17SPeter Ujfalusi 		wclk_rate = TAS2552_WCLK_FREQ_22_24KHZ;
294a571cb17SPeter Ujfalusi 		break;
295a571cb17SPeter Ujfalusi 	case 32000:
296a571cb17SPeter Ujfalusi 		wclk_rate = TAS2552_WCLK_FREQ_32KHZ;
297a571cb17SPeter Ujfalusi 		break;
298a571cb17SPeter Ujfalusi 	case 44100:
299a571cb17SPeter Ujfalusi 	case 48000:
300a571cb17SPeter Ujfalusi 		wclk_rate = TAS2552_WCLK_FREQ_44_48KHZ;
301a571cb17SPeter Ujfalusi 		break;
302a571cb17SPeter Ujfalusi 	case 88200:
303a571cb17SPeter Ujfalusi 	case 96000:
304a571cb17SPeter Ujfalusi 		wclk_rate = TAS2552_WCLK_FREQ_88_96KHZ;
305a571cb17SPeter Ujfalusi 		break;
306a571cb17SPeter Ujfalusi 	case 176400:
307a571cb17SPeter Ujfalusi 	case 192000:
308a571cb17SPeter Ujfalusi 		wclk_rate = TAS2552_WCLK_FREQ_176_192KHZ;
309a571cb17SPeter Ujfalusi 		break;
310a571cb17SPeter Ujfalusi 	default:
31179a4ad1eSKuninori Morimoto 		dev_err(component->dev, "Not supported sample rate: %d\n",
312a571cb17SPeter Ujfalusi 			params_rate(params));
313a571cb17SPeter Ujfalusi 		return -EINVAL;
314a571cb17SPeter Ujfalusi 	}
315a571cb17SPeter Ujfalusi 
31679a4ad1eSKuninori Morimoto 	snd_soc_component_update_bits(component, TAS2552_CFG_3, TAS2552_WCLK_FREQ_MASK,
317a571cb17SPeter Ujfalusi 			    wclk_rate);
318a571cb17SPeter Ujfalusi 
31979a4ad1eSKuninori Morimoto 	return tas2552_setup_pll(component, params);
3205df7f71dSDan Murphy }
3215df7f71dSDan Murphy 
3221b68c7dcSPeter Ujfalusi #define TAS2552_DAI_FMT_MASK	(TAS2552_BCLKDIR | \
3231b68c7dcSPeter Ujfalusi 				 TAS2552_WCLKDIR | \
3241b68c7dcSPeter Ujfalusi 				 TAS2552_DATAFORMAT_MASK)
3253f747a81SPeter Ujfalusi static int tas2552_prepare(struct snd_pcm_substream *substream,
3263f747a81SPeter Ujfalusi 			   struct snd_soc_dai *dai)
3273f747a81SPeter Ujfalusi {
32879a4ad1eSKuninori Morimoto 	struct snd_soc_component *component = dai->component;
32979a4ad1eSKuninori Morimoto 	struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
3303f747a81SPeter Ujfalusi 	int delay = 0;
3313f747a81SPeter Ujfalusi 
3323f747a81SPeter Ujfalusi 	/* TDM slot selection only valid in DSP_A/_B mode */
3333f747a81SPeter Ujfalusi 	if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_A)
3343f747a81SPeter Ujfalusi 		delay += (tas2552->tdm_delay + 1);
3353f747a81SPeter Ujfalusi 	else if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_B)
3363f747a81SPeter Ujfalusi 		delay += tas2552->tdm_delay;
3373f747a81SPeter Ujfalusi 
3383f747a81SPeter Ujfalusi 	/* Configure data delay */
33979a4ad1eSKuninori Morimoto 	snd_soc_component_write(component, TAS2552_SER_CTRL_2, delay);
3403f747a81SPeter Ujfalusi 
3413f747a81SPeter Ujfalusi 	return 0;
3423f747a81SPeter Ujfalusi }
3433f747a81SPeter Ujfalusi 
3445df7f71dSDan Murphy static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
3455df7f71dSDan Murphy {
34679a4ad1eSKuninori Morimoto 	struct snd_soc_component *component = dai->component;
34779a4ad1eSKuninori Morimoto 	struct tas2552_data *tas2552 = dev_get_drvdata(component->dev);
3485df7f71dSDan Murphy 	u8 serial_format;
3495df7f71dSDan Murphy 
3505df7f71dSDan Murphy 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
3515df7f71dSDan Murphy 	case SND_SOC_DAIFMT_CBS_CFS:
3525df7f71dSDan Murphy 		serial_format = 0x00;
3535df7f71dSDan Murphy 		break;
3545df7f71dSDan Murphy 	case SND_SOC_DAIFMT_CBS_CFM:
3551b68c7dcSPeter Ujfalusi 		serial_format = TAS2552_WCLKDIR;
3565df7f71dSDan Murphy 		break;
3575df7f71dSDan Murphy 	case SND_SOC_DAIFMT_CBM_CFS:
3581b68c7dcSPeter Ujfalusi 		serial_format = TAS2552_BCLKDIR;
3595df7f71dSDan Murphy 		break;
3605df7f71dSDan Murphy 	case SND_SOC_DAIFMT_CBM_CFM:
3611b68c7dcSPeter Ujfalusi 		serial_format = (TAS2552_BCLKDIR | TAS2552_WCLKDIR);
3625df7f71dSDan Murphy 		break;
3635df7f71dSDan Murphy 	default:
36479a4ad1eSKuninori Morimoto 		dev_vdbg(component->dev, "DAI Format master is not found\n");
3655df7f71dSDan Murphy 		return -EINVAL;
3665df7f71dSDan Murphy 	}
3675df7f71dSDan Murphy 
3684c331373SPeter Ujfalusi 	switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK |
3694c331373SPeter Ujfalusi 		       SND_SOC_DAIFMT_INV_MASK)) {
3704c331373SPeter Ujfalusi 	case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF):
3715df7f71dSDan Murphy 		break;
3724c331373SPeter Ujfalusi 	case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF):
3734c331373SPeter Ujfalusi 	case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF):
3741b68c7dcSPeter Ujfalusi 		serial_format |= TAS2552_DATAFORMAT_DSP;
3755df7f71dSDan Murphy 		break;
3764c331373SPeter Ujfalusi 	case (SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_NB_NF):
3771b68c7dcSPeter Ujfalusi 		serial_format |= TAS2552_DATAFORMAT_RIGHT_J;
3785df7f71dSDan Murphy 		break;
3794c331373SPeter Ujfalusi 	case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF):
3801b68c7dcSPeter Ujfalusi 		serial_format |= TAS2552_DATAFORMAT_LEFT_J;
3815df7f71dSDan Murphy 		break;
3825df7f71dSDan Murphy 	default:
38379a4ad1eSKuninori Morimoto 		dev_vdbg(component->dev, "DAI Format is not found\n");
3845df7f71dSDan Murphy 		return -EINVAL;
3855df7f71dSDan Murphy 	}
3863f747a81SPeter Ujfalusi 	tas2552->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
3875df7f71dSDan Murphy 
38879a4ad1eSKuninori Morimoto 	snd_soc_component_update_bits(component, TAS2552_SER_CTRL_1, TAS2552_DAI_FMT_MASK,
3895df7f71dSDan Murphy 			    serial_format);
3905df7f71dSDan Murphy 	return 0;
3915df7f71dSDan Murphy }
3925df7f71dSDan Murphy 
3935df7f71dSDan Murphy static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
3945df7f71dSDan Murphy 				  unsigned int freq, int dir)
3955df7f71dSDan Murphy {
39679a4ad1eSKuninori Morimoto 	struct snd_soc_component *component = dai->component;
39779a4ad1eSKuninori Morimoto 	struct tas2552_data *tas2552 = dev_get_drvdata(component->dev);
3989d87a888SPeter Ujfalusi 	u8 reg, mask, val;
3995df7f71dSDan Murphy 
4009d87a888SPeter Ujfalusi 	switch (clk_id) {
4019d87a888SPeter Ujfalusi 	case TAS2552_PLL_CLKIN_MCLK:
4029d87a888SPeter Ujfalusi 	case TAS2552_PLL_CLKIN_IVCLKIN:
4031014f7efSPeter Ujfalusi 		if (freq < 512000 || freq > 24576000) {
4041014f7efSPeter Ujfalusi 			/* out of range PLL_CLKIN, fall back to use BCLK */
40579a4ad1eSKuninori Morimoto 			dev_warn(component->dev, "Out of range PLL_CLKIN: %u\n",
4061014f7efSPeter Ujfalusi 				 freq);
4071014f7efSPeter Ujfalusi 			clk_id = TAS2552_PLL_CLKIN_BCLK;
4081014f7efSPeter Ujfalusi 			freq = 0;
4091014f7efSPeter Ujfalusi 		}
4103e146b55SGustavo A. R. Silva 		fallthrough;
4111014f7efSPeter Ujfalusi 	case TAS2552_PLL_CLKIN_BCLK:
4129d87a888SPeter Ujfalusi 	case TAS2552_PLL_CLKIN_1_8_FIXED:
4139d87a888SPeter Ujfalusi 		mask = TAS2552_PLL_SRC_MASK;
4149d87a888SPeter Ujfalusi 		val = (clk_id << 3) & mask; /* bit 4:5 in the register */
4159d87a888SPeter Ujfalusi 		reg = TAS2552_CFG_1;
4161014f7efSPeter Ujfalusi 		tas2552->pll_clk_id = clk_id;
41716bd3952SPeter Ujfalusi 		tas2552->pll_clkin = freq;
4189d87a888SPeter Ujfalusi 		break;
4199d87a888SPeter Ujfalusi 	case TAS2552_PDM_CLK_PLL:
4209d87a888SPeter Ujfalusi 	case TAS2552_PDM_CLK_IVCLKIN:
4219d87a888SPeter Ujfalusi 	case TAS2552_PDM_CLK_BCLK:
4229d87a888SPeter Ujfalusi 	case TAS2552_PDM_CLK_MCLK:
4239d87a888SPeter Ujfalusi 		mask = TAS2552_PDM_CLK_SEL_MASK;
4249d87a888SPeter Ujfalusi 		val = (clk_id >> 1) & mask; /* bit 0:1 in the register */
4259d87a888SPeter Ujfalusi 		reg = TAS2552_PDM_CFG;
4261014f7efSPeter Ujfalusi 		tas2552->pdm_clk_id = clk_id;
4279d87a888SPeter Ujfalusi 		tas2552->pdm_clk = freq;
4289d87a888SPeter Ujfalusi 		break;
4299d87a888SPeter Ujfalusi 	default:
43079a4ad1eSKuninori Morimoto 		dev_err(component->dev, "Invalid clk id: %d\n", clk_id);
4319d87a888SPeter Ujfalusi 		return -EINVAL;
4329d87a888SPeter Ujfalusi 	}
4339d87a888SPeter Ujfalusi 
43479a4ad1eSKuninori Morimoto 	snd_soc_component_update_bits(component, reg, mask, val);
4355df7f71dSDan Murphy 
4365df7f71dSDan Murphy 	return 0;
4375df7f71dSDan Murphy }
4385df7f71dSDan Murphy 
4393f747a81SPeter Ujfalusi static int tas2552_set_dai_tdm_slot(struct snd_soc_dai *dai,
4403f747a81SPeter Ujfalusi 				    unsigned int tx_mask, unsigned int rx_mask,
4413f747a81SPeter Ujfalusi 				    int slots, int slot_width)
4423f747a81SPeter Ujfalusi {
44379a4ad1eSKuninori Morimoto 	struct snd_soc_component *component = dai->component;
44479a4ad1eSKuninori Morimoto 	struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
4453f747a81SPeter Ujfalusi 	unsigned int lsb;
4463f747a81SPeter Ujfalusi 
4473f747a81SPeter Ujfalusi 	if (unlikely(!tx_mask)) {
44879a4ad1eSKuninori Morimoto 		dev_err(component->dev, "tx masks need to be non 0\n");
4493f747a81SPeter Ujfalusi 		return -EINVAL;
4503f747a81SPeter Ujfalusi 	}
4513f747a81SPeter Ujfalusi 
4523f747a81SPeter Ujfalusi 	/* TDM based on DSP mode requires slots to be adjacent */
4533f747a81SPeter Ujfalusi 	lsb = __ffs(tx_mask);
4543f747a81SPeter Ujfalusi 	if ((lsb + 1) != __fls(tx_mask)) {
45579a4ad1eSKuninori Morimoto 		dev_err(component->dev, "Invalid mask, slots must be adjacent\n");
4563f747a81SPeter Ujfalusi 		return -EINVAL;
4573f747a81SPeter Ujfalusi 	}
4583f747a81SPeter Ujfalusi 
4593f747a81SPeter Ujfalusi 	tas2552->tdm_delay = lsb * slot_width;
4603f747a81SPeter Ujfalusi 
4613f747a81SPeter Ujfalusi 	/* DOUT in high-impedance on inactive bit clocks */
46279a4ad1eSKuninori Morimoto 	snd_soc_component_update_bits(component, TAS2552_DOUT,
4633f747a81SPeter Ujfalusi 			    TAS2552_SDOUT_TRISTATE, TAS2552_SDOUT_TRISTATE);
4643f747a81SPeter Ujfalusi 
4653f747a81SPeter Ujfalusi 	return 0;
4663f747a81SPeter Ujfalusi }
4673f747a81SPeter Ujfalusi 
46838803ce7SKuninori Morimoto static int tas2552_mute(struct snd_soc_dai *dai, int mute, int direction)
4695df7f71dSDan Murphy {
470e3606aa4SPeter Ujfalusi 	u8 cfg1_reg = 0;
47179a4ad1eSKuninori Morimoto 	struct snd_soc_component *component = dai->component;
4725df7f71dSDan Murphy 
4735df7f71dSDan Murphy 	if (mute)
474e3606aa4SPeter Ujfalusi 		cfg1_reg |= TAS2552_MUTE;
4755df7f71dSDan Murphy 
47679a4ad1eSKuninori Morimoto 	snd_soc_component_update_bits(component, TAS2552_CFG_1, TAS2552_MUTE, cfg1_reg);
4775df7f71dSDan Murphy 
4785df7f71dSDan Murphy 	return 0;
4795df7f71dSDan Murphy }
4805df7f71dSDan Murphy 
481641d334bSRafael J. Wysocki #ifdef CONFIG_PM
4825df7f71dSDan Murphy static int tas2552_runtime_suspend(struct device *dev)
4835df7f71dSDan Murphy {
4845df7f71dSDan Murphy 	struct tas2552_data *tas2552 = dev_get_drvdata(dev);
4855df7f71dSDan Murphy 
486dd6e3053SPeter Ujfalusi 	tas2552_sw_shutdown(tas2552, 1);
4875df7f71dSDan Murphy 
4885df7f71dSDan Murphy 	regcache_cache_only(tas2552->regmap, true);
4895df7f71dSDan Murphy 	regcache_mark_dirty(tas2552->regmap);
4905df7f71dSDan Murphy 
491e295a4a4SDan Murphy 	gpiod_set_value(tas2552->enable_gpio, 0);
492e295a4a4SDan Murphy 
4935df7f71dSDan Murphy 	return 0;
4945df7f71dSDan Murphy }
4955df7f71dSDan Murphy 
4965df7f71dSDan Murphy static int tas2552_runtime_resume(struct device *dev)
4975df7f71dSDan Murphy {
4985df7f71dSDan Murphy 	struct tas2552_data *tas2552 = dev_get_drvdata(dev);
4995df7f71dSDan Murphy 
5005df7f71dSDan Murphy 	gpiod_set_value(tas2552->enable_gpio, 1);
5015df7f71dSDan Murphy 
502dd6e3053SPeter Ujfalusi 	tas2552_sw_shutdown(tas2552, 0);
5035df7f71dSDan Murphy 
5045df7f71dSDan Murphy 	regcache_cache_only(tas2552->regmap, false);
5055df7f71dSDan Murphy 	regcache_sync(tas2552->regmap);
5065df7f71dSDan Murphy 
5075df7f71dSDan Murphy 	return 0;
5085df7f71dSDan Murphy }
5095df7f71dSDan Murphy #endif
5105df7f71dSDan Murphy 
5115df7f71dSDan Murphy static const struct dev_pm_ops tas2552_pm = {
5125df7f71dSDan Murphy 	SET_RUNTIME_PM_OPS(tas2552_runtime_suspend, tas2552_runtime_resume,
5135df7f71dSDan Murphy 			   NULL)
5145df7f71dSDan Murphy };
5155df7f71dSDan Murphy 
51664793047SAxel Lin static const struct snd_soc_dai_ops tas2552_speaker_dai_ops = {
5175df7f71dSDan Murphy 	.hw_params	= tas2552_hw_params,
5183f747a81SPeter Ujfalusi 	.prepare	= tas2552_prepare,
5195df7f71dSDan Murphy 	.set_sysclk	= tas2552_set_dai_sysclk,
5205df7f71dSDan Murphy 	.set_fmt	= tas2552_set_dai_fmt,
5213f747a81SPeter Ujfalusi 	.set_tdm_slot	= tas2552_set_dai_tdm_slot,
52238803ce7SKuninori Morimoto 	.mute_stream	= tas2552_mute,
52338803ce7SKuninori Morimoto 	.no_capture_mute = 1,
5245df7f71dSDan Murphy };
5255df7f71dSDan Murphy 
5265df7f71dSDan Murphy /* Formats supported by TAS2552 driver. */
5275df7f71dSDan Murphy #define TAS2552_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
5285df7f71dSDan Murphy 			 SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
5295df7f71dSDan Murphy 
5305df7f71dSDan Murphy /* TAS2552 dai structure. */
5315df7f71dSDan Murphy static struct snd_soc_dai_driver tas2552_dai[] = {
5325df7f71dSDan Murphy 	{
5335df7f71dSDan Murphy 		.name = "tas2552-amplifier",
5345df7f71dSDan Murphy 		.playback = {
535a7a8e994SDan Murphy 			.stream_name = "Playback",
5365df7f71dSDan Murphy 			.channels_min = 2,
5375df7f71dSDan Murphy 			.channels_max = 2,
5385df7f71dSDan Murphy 			.rates = SNDRV_PCM_RATE_8000_192000,
5395df7f71dSDan Murphy 			.formats = TAS2552_FORMATS,
5405df7f71dSDan Murphy 		},
5415df7f71dSDan Murphy 		.ops = &tas2552_speaker_dai_ops,
5425df7f71dSDan Murphy 	},
5435df7f71dSDan Murphy };
5445df7f71dSDan Murphy 
5455df7f71dSDan Murphy /*
5465df7f71dSDan Murphy  * DAC digital volumes. From -7 to 24 dB in 1 dB steps
5475df7f71dSDan Murphy  */
548e2600460SAndreas Dannenberg static DECLARE_TLV_DB_SCALE(dac_tlv, -700, 100, 0);
5495df7f71dSDan Murphy 
5502962cb52SPeter Ujfalusi static const char * const tas2552_din_source_select[] = {
5512962cb52SPeter Ujfalusi 	"Muted",
5522962cb52SPeter Ujfalusi 	"Left",
5532962cb52SPeter Ujfalusi 	"Right",
5542962cb52SPeter Ujfalusi 	"Left + Right average",
5552962cb52SPeter Ujfalusi };
5562962cb52SPeter Ujfalusi static SOC_ENUM_SINGLE_DECL(tas2552_din_source_enum,
5572962cb52SPeter Ujfalusi 			    TAS2552_CFG_3, 3,
5582962cb52SPeter Ujfalusi 			    tas2552_din_source_select);
5592962cb52SPeter Ujfalusi 
5605df7f71dSDan Murphy static const struct snd_kcontrol_new tas2552_snd_controls[] = {
5615df7f71dSDan Murphy 	SOC_SINGLE_TLV("Speaker Driver Playback Volume",
562dd6ae3bcSPeter Ujfalusi 			 TAS2552_PGA_GAIN, 0, 0x1f, 0, dac_tlv),
5632962cb52SPeter Ujfalusi 	SOC_ENUM("DIN source", tas2552_din_source_enum),
5645df7f71dSDan Murphy };
5655df7f71dSDan Murphy 
56679a4ad1eSKuninori Morimoto static int tas2552_component_probe(struct snd_soc_component *component)
5675df7f71dSDan Murphy {
56879a4ad1eSKuninori Morimoto 	struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
5695df7f71dSDan Murphy 	int ret;
5705df7f71dSDan Murphy 
57179a4ad1eSKuninori Morimoto 	tas2552->component = component;
5725df7f71dSDan Murphy 
5735df7f71dSDan Murphy 	ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies),
5745df7f71dSDan Murphy 				    tas2552->supplies);
5755df7f71dSDan Murphy 
5765df7f71dSDan Murphy 	if (ret != 0) {
57779a4ad1eSKuninori Morimoto 		dev_err(component->dev, "Failed to enable supplies: %d\n",
5785df7f71dSDan Murphy 			ret);
5795df7f71dSDan Murphy 		return ret;
5805df7f71dSDan Murphy 	}
5815df7f71dSDan Murphy 
5825df7f71dSDan Murphy 	gpiod_set_value(tas2552->enable_gpio, 1);
5835df7f71dSDan Murphy 
58479a4ad1eSKuninori Morimoto 	ret = pm_runtime_get_sync(component->dev);
5855df7f71dSDan Murphy 	if (ret < 0) {
58679a4ad1eSKuninori Morimoto 		dev_err(component->dev, "Enabling device failed: %d\n",
5875df7f71dSDan Murphy 			ret);
5885df7f71dSDan Murphy 		goto probe_fail;
5895df7f71dSDan Murphy 	}
5905df7f71dSDan Murphy 
59179a4ad1eSKuninori Morimoto 	snd_soc_component_update_bits(component, TAS2552_CFG_1, TAS2552_MUTE, TAS2552_MUTE);
59279a4ad1eSKuninori Morimoto 	snd_soc_component_write(component, TAS2552_CFG_3, TAS2552_I2S_OUT_SEL |
593a571cb17SPeter Ujfalusi 					    TAS2552_DIN_SRC_SEL_AVG_L_R);
59479a4ad1eSKuninori Morimoto 	snd_soc_component_write(component, TAS2552_OUTPUT_DATA,
595b2822f19SPeter Ujfalusi 		      TAS2552_PDM_DATA_SEL_V_I |
596b2822f19SPeter Ujfalusi 		      TAS2552_R_DATA_OUT(TAS2552_DATA_OUT_V_DATA));
59779a4ad1eSKuninori Morimoto 	snd_soc_component_write(component, TAS2552_BOOST_APT_CTRL, TAS2552_APT_DELAY_200 |
5982a9dd1dbSPeter Ujfalusi 						     TAS2552_APT_THRESH_20_17);
5995df7f71dSDan Murphy 
60079a4ad1eSKuninori Morimoto 	snd_soc_component_write(component, TAS2552_CFG_2, TAS2552_BOOST_EN | TAS2552_APT_EN |
6014afdd89dSPeter Ujfalusi 					    TAS2552_LIM_EN);
602a7a8e994SDan Murphy 
6035df7f71dSDan Murphy 	return 0;
6045df7f71dSDan Murphy 
6055df7f71dSDan Murphy probe_fail:
6060d71a5cfSDinghao Liu 	pm_runtime_put_noidle(component->dev);
6075df7f71dSDan Murphy 	gpiod_set_value(tas2552->enable_gpio, 0);
6085df7f71dSDan Murphy 
6095df7f71dSDan Murphy 	regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies),
6105df7f71dSDan Murphy 					tas2552->supplies);
6116f2daf82SFabio Estevam 	return ret;
6125df7f71dSDan Murphy }
6135df7f71dSDan Murphy 
61479a4ad1eSKuninori Morimoto static void tas2552_component_remove(struct snd_soc_component *component)
6155df7f71dSDan Murphy {
61679a4ad1eSKuninori Morimoto 	struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
6175df7f71dSDan Murphy 
61879a4ad1eSKuninori Morimoto 	pm_runtime_put(component->dev);
619e295a4a4SDan Murphy 
6205df7f71dSDan Murphy 	gpiod_set_value(tas2552->enable_gpio, 0);
6215df7f71dSDan Murphy };
6225df7f71dSDan Murphy 
6235df7f71dSDan Murphy #ifdef CONFIG_PM
62479a4ad1eSKuninori Morimoto static int tas2552_suspend(struct snd_soc_component *component)
6255df7f71dSDan Murphy {
62679a4ad1eSKuninori Morimoto 	struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
6275df7f71dSDan Murphy 	int ret;
6285df7f71dSDan Murphy 
6295df7f71dSDan Murphy 	ret = regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies),
6305df7f71dSDan Murphy 					tas2552->supplies);
6315df7f71dSDan Murphy 
6325df7f71dSDan Murphy 	if (ret != 0)
63379a4ad1eSKuninori Morimoto 		dev_err(component->dev, "Failed to disable supplies: %d\n",
6345df7f71dSDan Murphy 			ret);
63512dc0f3bSFabio Estevam 	return ret;
6365df7f71dSDan Murphy }
6375df7f71dSDan Murphy 
63879a4ad1eSKuninori Morimoto static int tas2552_resume(struct snd_soc_component *component)
6395df7f71dSDan Murphy {
64079a4ad1eSKuninori Morimoto 	struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
6415df7f71dSDan Murphy 	int ret;
6425df7f71dSDan Murphy 
6435df7f71dSDan Murphy 	ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies),
6445df7f71dSDan Murphy 				    tas2552->supplies);
6455df7f71dSDan Murphy 
6465df7f71dSDan Murphy 	if (ret != 0) {
64779a4ad1eSKuninori Morimoto 		dev_err(component->dev, "Failed to enable supplies: %d\n",
6485df7f71dSDan Murphy 			ret);
6495df7f71dSDan Murphy 	}
6505df7f71dSDan Murphy 
65112dc0f3bSFabio Estevam 	return ret;
6525df7f71dSDan Murphy }
6535df7f71dSDan Murphy #else
6545df7f71dSDan Murphy #define tas2552_suspend NULL
6555df7f71dSDan Murphy #define tas2552_resume NULL
6565df7f71dSDan Murphy #endif
6575df7f71dSDan Murphy 
65879a4ad1eSKuninori Morimoto static const struct snd_soc_component_driver soc_component_dev_tas2552 = {
65979a4ad1eSKuninori Morimoto 	.probe			= tas2552_component_probe,
66079a4ad1eSKuninori Morimoto 	.remove			= tas2552_component_remove,
6615df7f71dSDan Murphy 	.suspend		= tas2552_suspend,
6625df7f71dSDan Murphy 	.resume			= tas2552_resume,
6635df7f71dSDan Murphy 	.controls		= tas2552_snd_controls,
6645df7f71dSDan Murphy 	.num_controls		= ARRAY_SIZE(tas2552_snd_controls),
665e3f1ff31SLars-Peter Clausen 	.dapm_widgets		= tas2552_dapm_widgets,
666e3f1ff31SLars-Peter Clausen 	.num_dapm_widgets	= ARRAY_SIZE(tas2552_dapm_widgets),
667e3f1ff31SLars-Peter Clausen 	.dapm_routes		= tas2552_audio_map,
668e3f1ff31SLars-Peter Clausen 	.num_dapm_routes	= ARRAY_SIZE(tas2552_audio_map),
66979a4ad1eSKuninori Morimoto 	.idle_bias_on		= 1,
67079a4ad1eSKuninori Morimoto 	.endianness		= 1,
67179a4ad1eSKuninori Morimoto 	.non_legacy_dai_naming	= 1,
6725df7f71dSDan Murphy };
6735df7f71dSDan Murphy 
6745df7f71dSDan Murphy static const struct regmap_config tas2552_regmap_config = {
6755df7f71dSDan Murphy 	.reg_bits = 8,
6765df7f71dSDan Murphy 	.val_bits = 8,
6775df7f71dSDan Murphy 
6785df7f71dSDan Murphy 	.max_register = TAS2552_MAX_REG,
6795df7f71dSDan Murphy 	.reg_defaults = tas2552_reg_defs,
6805df7f71dSDan Murphy 	.num_reg_defaults = ARRAY_SIZE(tas2552_reg_defs),
6815df7f71dSDan Murphy 	.cache_type = REGCACHE_RBTREE,
6825df7f71dSDan Murphy };
6835df7f71dSDan Murphy 
6845df7f71dSDan Murphy static int tas2552_probe(struct i2c_client *client,
6855df7f71dSDan Murphy 			   const struct i2c_device_id *id)
6865df7f71dSDan Murphy {
6875df7f71dSDan Murphy 	struct device *dev;
6885df7f71dSDan Murphy 	struct tas2552_data *data;
6895df7f71dSDan Murphy 	int ret;
6905df7f71dSDan Murphy 	int i;
6915df7f71dSDan Murphy 
6925df7f71dSDan Murphy 	dev = &client->dev;
6935df7f71dSDan Murphy 	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
6945df7f71dSDan Murphy 	if (data == NULL)
6955df7f71dSDan Murphy 		return -ENOMEM;
6965df7f71dSDan Murphy 
6978604bc28SAxel Lin 	data->enable_gpio = devm_gpiod_get_optional(dev, "enable",
6988604bc28SAxel Lin 						    GPIOD_OUT_LOW);
6998604bc28SAxel Lin 	if (IS_ERR(data->enable_gpio))
7008604bc28SAxel Lin 		return PTR_ERR(data->enable_gpio);
7015df7f71dSDan Murphy 
7025df7f71dSDan Murphy 	data->tas2552_client = client;
7035df7f71dSDan Murphy 	data->regmap = devm_regmap_init_i2c(client, &tas2552_regmap_config);
7045df7f71dSDan Murphy 	if (IS_ERR(data->regmap)) {
7055df7f71dSDan Murphy 		ret = PTR_ERR(data->regmap);
7065df7f71dSDan Murphy 		dev_err(&client->dev, "Failed to allocate register map: %d\n",
7075df7f71dSDan Murphy 			ret);
7085df7f71dSDan Murphy 		return ret;
7095df7f71dSDan Murphy 	}
7105df7f71dSDan Murphy 
7115df7f71dSDan Murphy 	for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
7125df7f71dSDan Murphy 		data->supplies[i].supply = tas2552_supply_names[i];
7135df7f71dSDan Murphy 
7145df7f71dSDan Murphy 	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies),
7155df7f71dSDan Murphy 				      data->supplies);
716c62f9d8fSAxel Lin 	if (ret != 0) {
7175df7f71dSDan Murphy 		dev_err(dev, "Failed to request supplies: %d\n", ret);
718c62f9d8fSAxel Lin 		return ret;
719c62f9d8fSAxel Lin 	}
7205df7f71dSDan Murphy 
7215df7f71dSDan Murphy 	pm_runtime_set_active(&client->dev);
7225df7f71dSDan Murphy 	pm_runtime_set_autosuspend_delay(&client->dev, 1000);
7235df7f71dSDan Murphy 	pm_runtime_use_autosuspend(&client->dev);
7245df7f71dSDan Murphy 	pm_runtime_enable(&client->dev);
7255df7f71dSDan Murphy 	pm_runtime_mark_last_busy(&client->dev);
7265df7f71dSDan Murphy 	pm_runtime_put_sync_autosuspend(&client->dev);
7275df7f71dSDan Murphy 
7285df7f71dSDan Murphy 	dev_set_drvdata(&client->dev, data);
7295df7f71dSDan Murphy 
73079a4ad1eSKuninori Morimoto 	ret = devm_snd_soc_register_component(&client->dev,
73179a4ad1eSKuninori Morimoto 				      &soc_component_dev_tas2552,
7325df7f71dSDan Murphy 				      tas2552_dai, ARRAY_SIZE(tas2552_dai));
7335df7f71dSDan Murphy 	if (ret < 0)
73479a4ad1eSKuninori Morimoto 		dev_err(&client->dev, "Failed to register component: %d\n", ret);
7355df7f71dSDan Murphy 
736c62f9d8fSAxel Lin 	return ret;
7375df7f71dSDan Murphy }
7385df7f71dSDan Murphy 
7395df7f71dSDan Murphy static int tas2552_i2c_remove(struct i2c_client *client)
7405df7f71dSDan Murphy {
7414785ed89SPeter Ujfalusi 	pm_runtime_disable(&client->dev);
7425df7f71dSDan Murphy 	return 0;
7435df7f71dSDan Murphy }
7445df7f71dSDan Murphy 
7455df7f71dSDan Murphy static const struct i2c_device_id tas2552_id[] = {
7465df7f71dSDan Murphy 	{ "tas2552", 0 },
7475df7f71dSDan Murphy 	{ }
7485df7f71dSDan Murphy };
7495df7f71dSDan Murphy MODULE_DEVICE_TABLE(i2c, tas2552_id);
7505df7f71dSDan Murphy 
7515df7f71dSDan Murphy #if IS_ENABLED(CONFIG_OF)
7525df7f71dSDan Murphy static const struct of_device_id tas2552_of_match[] = {
7535df7f71dSDan Murphy 	{ .compatible = "ti,tas2552", },
7545df7f71dSDan Murphy 	{},
7555df7f71dSDan Murphy };
7565df7f71dSDan Murphy MODULE_DEVICE_TABLE(of, tas2552_of_match);
7575df7f71dSDan Murphy #endif
7585df7f71dSDan Murphy 
7595df7f71dSDan Murphy static struct i2c_driver tas2552_i2c_driver = {
7605df7f71dSDan Murphy 	.driver = {
7615df7f71dSDan Murphy 		.name = "tas2552",
7625df7f71dSDan Murphy 		.of_match_table = of_match_ptr(tas2552_of_match),
7635df7f71dSDan Murphy 		.pm = &tas2552_pm,
7645df7f71dSDan Murphy 	},
7655df7f71dSDan Murphy 	.probe = tas2552_probe,
7665df7f71dSDan Murphy 	.remove = tas2552_i2c_remove,
7675df7f71dSDan Murphy 	.id_table = tas2552_id,
7685df7f71dSDan Murphy };
7695df7f71dSDan Murphy 
7705df7f71dSDan Murphy module_i2c_driver(tas2552_i2c_driver);
7715df7f71dSDan Murphy 
7725df7f71dSDan Murphy MODULE_AUTHOR("Dan Muprhy <dmurphy@ti.com>");
7735df7f71dSDan Murphy MODULE_DESCRIPTION("TAS2552 Audio amplifier driver");
7745df7f71dSDan Murphy MODULE_LICENSE("GPL");
775