xref: /linux/sound/soc/codecs/tas2552.c (revision 1014f7eff9a1d4f3f796c83e933adf2462c79005)
15df7f71dSDan Murphy /*
25df7f71dSDan Murphy  * tas2552.c - ALSA SoC Texas Instruments TAS2552 Mono Audio Amplifier
35df7f71dSDan Murphy  *
45df7f71dSDan Murphy  * Copyright (C) 2014 Texas Instruments Incorporated -  http://www.ti.com
55df7f71dSDan Murphy  *
65df7f71dSDan Murphy  * Author: Dan Murphy <dmurphy@ti.com>
75df7f71dSDan Murphy  *
85df7f71dSDan Murphy  * This program is free software; you can redistribute it and/or
95df7f71dSDan Murphy  * modify it under the terms of the GNU General Public License
105df7f71dSDan Murphy  * version 2 as published by the Free Software Foundation.
115df7f71dSDan Murphy  *
125df7f71dSDan Murphy  * This program is distributed in the hope that it will be useful, but
135df7f71dSDan Murphy  * WITHOUT ANY WARRANTY; without even the implied warranty of
145df7f71dSDan Murphy  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
155df7f71dSDan Murphy  * General Public License for more details.
165df7f71dSDan Murphy  */
175df7f71dSDan Murphy 
185df7f71dSDan Murphy #include <linux/module.h>
195df7f71dSDan Murphy #include <linux/errno.h>
205df7f71dSDan Murphy #include <linux/device.h>
215df7f71dSDan Murphy #include <linux/i2c.h>
225df7f71dSDan Murphy #include <linux/gpio.h>
235df7f71dSDan Murphy #include <linux/of_gpio.h>
245df7f71dSDan Murphy #include <linux/pm_runtime.h>
255df7f71dSDan Murphy #include <linux/regmap.h>
265df7f71dSDan Murphy #include <linux/slab.h>
275df7f71dSDan Murphy 
285df7f71dSDan Murphy #include <linux/gpio/consumer.h>
295df7f71dSDan Murphy #include <linux/regulator/consumer.h>
305df7f71dSDan Murphy 
315df7f71dSDan Murphy #include <sound/pcm.h>
325df7f71dSDan Murphy #include <sound/pcm_params.h>
335df7f71dSDan Murphy #include <sound/soc.h>
345df7f71dSDan Murphy #include <sound/soc-dapm.h>
355df7f71dSDan Murphy #include <sound/tlv.h>
365df7f71dSDan Murphy #include <sound/tas2552-plat.h>
379d87a888SPeter Ujfalusi #include <dt-bindings/sound/tas2552.h>
385df7f71dSDan Murphy 
395df7f71dSDan Murphy #include "tas2552.h"
405df7f71dSDan Murphy 
415df7f71dSDan Murphy static struct reg_default tas2552_reg_defs[] = {
425df7f71dSDan Murphy 	{TAS2552_CFG_1, 0x22},
435df7f71dSDan Murphy 	{TAS2552_CFG_3, 0x80},
445df7f71dSDan Murphy 	{TAS2552_DOUT, 0x00},
455df7f71dSDan Murphy 	{TAS2552_OUTPUT_DATA, 0xc0},
465df7f71dSDan Murphy 	{TAS2552_PDM_CFG, 0x01},
475df7f71dSDan Murphy 	{TAS2552_PGA_GAIN, 0x00},
485df7f71dSDan Murphy 	{TAS2552_BOOST_PT_CTRL, 0x0f},
497d785025SPeter Ujfalusi 	{TAS2552_RESERVED_0D, 0xbe},
505df7f71dSDan Murphy 	{TAS2552_LIMIT_RATE_HYS, 0x08},
515df7f71dSDan Murphy 	{TAS2552_CFG_2, 0xef},
525df7f71dSDan Murphy 	{TAS2552_SER_CTRL_1, 0x00},
535df7f71dSDan Murphy 	{TAS2552_SER_CTRL_2, 0x00},
545df7f71dSDan Murphy 	{TAS2552_PLL_CTRL_1, 0x10},
555df7f71dSDan Murphy 	{TAS2552_PLL_CTRL_2, 0x00},
565df7f71dSDan Murphy 	{TAS2552_PLL_CTRL_3, 0x00},
575df7f71dSDan Murphy 	{TAS2552_BTIP, 0x8f},
585df7f71dSDan Murphy 	{TAS2552_BTS_CTRL, 0x80},
595df7f71dSDan Murphy 	{TAS2552_LIMIT_RELEASE, 0x04},
605df7f71dSDan Murphy 	{TAS2552_LIMIT_INT_COUNT, 0x00},
615df7f71dSDan Murphy 	{TAS2552_EDGE_RATE_CTRL, 0x40},
625df7f71dSDan Murphy 	{TAS2552_VBAT_DATA, 0x00},
635df7f71dSDan Murphy };
645df7f71dSDan Murphy 
655df7f71dSDan Murphy #define TAS2552_NUM_SUPPLIES	3
665df7f71dSDan Murphy static const char *tas2552_supply_names[TAS2552_NUM_SUPPLIES] = {
675df7f71dSDan Murphy 	"vbat",		/* vbat voltage */
685df7f71dSDan Murphy 	"iovdd",	/* I/O Voltage */
695df7f71dSDan Murphy 	"avdd",		/* Analog DAC Voltage */
705df7f71dSDan Murphy };
715df7f71dSDan Murphy 
725df7f71dSDan Murphy struct tas2552_data {
735df7f71dSDan Murphy 	struct snd_soc_codec *codec;
745df7f71dSDan Murphy 	struct regmap *regmap;
755df7f71dSDan Murphy 	struct i2c_client *tas2552_client;
765df7f71dSDan Murphy 	struct regulator_bulk_data supplies[TAS2552_NUM_SUPPLIES];
775df7f71dSDan Murphy 	struct gpio_desc *enable_gpio;
785df7f71dSDan Murphy 	unsigned char regs[TAS2552_VBAT_DATA];
7916bd3952SPeter Ujfalusi 	unsigned int pll_clkin;
80*1014f7efSPeter Ujfalusi 	int pll_clk_id;
819d87a888SPeter Ujfalusi 	unsigned int pdm_clk;
82*1014f7efSPeter Ujfalusi 	int pdm_clk_id;
833f747a81SPeter Ujfalusi 
843f747a81SPeter Ujfalusi 	unsigned int dai_fmt;
853f747a81SPeter Ujfalusi 	unsigned int tdm_delay;
865df7f71dSDan Murphy };
875df7f71dSDan Murphy 
887d785025SPeter Ujfalusi static int tas2552_post_event(struct snd_soc_dapm_widget *w,
897d785025SPeter Ujfalusi 			      struct snd_kcontrol *kcontrol, int event)
907d785025SPeter Ujfalusi {
917d785025SPeter Ujfalusi 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
927d785025SPeter Ujfalusi 
937d785025SPeter Ujfalusi 	switch (event) {
947d785025SPeter Ujfalusi 	case SND_SOC_DAPM_POST_PMU:
957d785025SPeter Ujfalusi 		snd_soc_write(codec, TAS2552_RESERVED_0D, 0xc0);
967d785025SPeter Ujfalusi 		snd_soc_update_bits(codec, TAS2552_LIMIT_RATE_HYS, (1 << 5),
977d785025SPeter Ujfalusi 				    (1 << 5));
987d785025SPeter Ujfalusi 		snd_soc_update_bits(codec, TAS2552_CFG_2, 1, 0);
997d785025SPeter Ujfalusi 		snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_SWS, 0);
1007d785025SPeter Ujfalusi 		break;
1017d785025SPeter Ujfalusi 	case SND_SOC_DAPM_POST_PMD:
1027d785025SPeter Ujfalusi 		snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_SWS,
1037d785025SPeter Ujfalusi 				    TAS2552_SWS);
1047d785025SPeter Ujfalusi 		snd_soc_update_bits(codec, TAS2552_CFG_2, 1, 1);
1057d785025SPeter Ujfalusi 		snd_soc_update_bits(codec, TAS2552_LIMIT_RATE_HYS, (1 << 5), 0);
1067d785025SPeter Ujfalusi 		snd_soc_write(codec, TAS2552_RESERVED_0D, 0xbe);
1077d785025SPeter Ujfalusi 		break;
1087d785025SPeter Ujfalusi 	}
1097d785025SPeter Ujfalusi 	return 0;
1107d785025SPeter Ujfalusi }
111a7a8e994SDan Murphy 
112609e7131SPeter Ujfalusi /* Input mux controls */
113609e7131SPeter Ujfalusi static const char * const tas2552_input_texts[] = {
114609e7131SPeter Ujfalusi 	"Digital", "Analog" };
115a7a8e994SDan Murphy static SOC_ENUM_SINGLE_DECL(tas2552_input_mux_enum, TAS2552_CFG_3, 7,
116a7a8e994SDan Murphy 			    tas2552_input_texts);
117a7a8e994SDan Murphy 
118609e7131SPeter Ujfalusi static const struct snd_kcontrol_new tas2552_input_mux_control =
119609e7131SPeter Ujfalusi 	SOC_DAPM_ENUM("Route", tas2552_input_mux_enum);
120a7a8e994SDan Murphy 
121a7a8e994SDan Murphy static const struct snd_soc_dapm_widget tas2552_dapm_widgets[] =
122a7a8e994SDan Murphy {
123a7a8e994SDan Murphy 	SND_SOC_DAPM_INPUT("IN"),
124a7a8e994SDan Murphy 
125a7a8e994SDan Murphy 	/* MUX Controls */
126a7a8e994SDan Murphy 	SND_SOC_DAPM_MUX("Input selection", SND_SOC_NOPM, 0, 0,
127609e7131SPeter Ujfalusi 			 &tas2552_input_mux_control),
128a7a8e994SDan Murphy 
129a7a8e994SDan Murphy 	SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0, 0),
130a7a8e994SDan Murphy 	SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
131a7a8e994SDan Murphy 	SND_SOC_DAPM_OUT_DRV("ClassD", TAS2552_CFG_2, 7, 0, NULL, 0),
132a7a8e994SDan Murphy 	SND_SOC_DAPM_SUPPLY("PLL", TAS2552_CFG_2, 3, 0, NULL, 0),
1337d785025SPeter Ujfalusi 	SND_SOC_DAPM_POST("Post Event", tas2552_post_event),
134a7a8e994SDan Murphy 
135a7a8e994SDan Murphy 	SND_SOC_DAPM_OUTPUT("OUT")
136a7a8e994SDan Murphy };
137a7a8e994SDan Murphy 
138a7a8e994SDan Murphy static const struct snd_soc_dapm_route tas2552_audio_map[] = {
139a7a8e994SDan Murphy 	{"DAC", NULL, "DAC IN"},
140a7a8e994SDan Murphy 	{"Input selection", "Digital", "DAC"},
141a7a8e994SDan Murphy 	{"Input selection", "Analog", "IN"},
142a7a8e994SDan Murphy 	{"ClassD", NULL, "Input selection"},
143a7a8e994SDan Murphy 	{"OUT", NULL, "ClassD"},
144a7a8e994SDan Murphy 	{"ClassD", NULL, "PLL"},
145a7a8e994SDan Murphy };
146a7a8e994SDan Murphy 
147641d334bSRafael J. Wysocki #ifdef CONFIG_PM
1485df7f71dSDan Murphy static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown)
1495df7f71dSDan Murphy {
150dd6e3053SPeter Ujfalusi 	u8 cfg1_reg = 0;
1515df7f71dSDan Murphy 
15280ba2669SPeter Ujfalusi 	if (!tas_data->codec)
15380ba2669SPeter Ujfalusi 		return;
15480ba2669SPeter Ujfalusi 
1555df7f71dSDan Murphy 	if (sw_shutdown)
1567de544fdSPeter Ujfalusi 		cfg1_reg = TAS2552_SWS;
1575df7f71dSDan Murphy 
1587de544fdSPeter Ujfalusi 	snd_soc_update_bits(tas_data->codec, TAS2552_CFG_1, TAS2552_SWS,
1597de544fdSPeter Ujfalusi 			    cfg1_reg);
1605df7f71dSDan Murphy }
161be1aa3eaSThierry Reding #endif
1625df7f71dSDan Murphy 
163*1014f7efSPeter Ujfalusi static int tas2552_setup_pll(struct snd_soc_codec *codec,
164*1014f7efSPeter Ujfalusi 			     struct snd_pcm_hw_params *params)
165*1014f7efSPeter Ujfalusi {
166*1014f7efSPeter Ujfalusi 	struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
167*1014f7efSPeter Ujfalusi 	bool bypass_pll = false;
168*1014f7efSPeter Ujfalusi 	unsigned int pll_clk = params_rate(params) * 512;
169*1014f7efSPeter Ujfalusi 	unsigned int pll_clkin = tas2552->pll_clkin;
170*1014f7efSPeter Ujfalusi 	u8 pll_enable;
171*1014f7efSPeter Ujfalusi 
172*1014f7efSPeter Ujfalusi 	if (!pll_clkin) {
173*1014f7efSPeter Ujfalusi 		if (tas2552->pll_clk_id != TAS2552_PLL_CLKIN_BCLK)
174*1014f7efSPeter Ujfalusi 			return -EINVAL;
175*1014f7efSPeter Ujfalusi 
176*1014f7efSPeter Ujfalusi 		pll_clkin = snd_soc_params_to_bclk(params);
177*1014f7efSPeter Ujfalusi 		pll_clkin += tas2552->tdm_delay;
178*1014f7efSPeter Ujfalusi 	}
179*1014f7efSPeter Ujfalusi 
180*1014f7efSPeter Ujfalusi 	pll_enable = snd_soc_read(codec, TAS2552_CFG_2) & TAS2552_PLL_ENABLE;
181*1014f7efSPeter Ujfalusi 	snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0);
182*1014f7efSPeter Ujfalusi 
183*1014f7efSPeter Ujfalusi 	if (pll_clkin == pll_clk)
184*1014f7efSPeter Ujfalusi 		bypass_pll = true;
185*1014f7efSPeter Ujfalusi 
186*1014f7efSPeter Ujfalusi 	if (bypass_pll) {
187*1014f7efSPeter Ujfalusi 		/* By pass the PLL configuration */
188*1014f7efSPeter Ujfalusi 		snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2,
189*1014f7efSPeter Ujfalusi 				    TAS2552_PLL_BYPASS, TAS2552_PLL_BYPASS);
190*1014f7efSPeter Ujfalusi 	} else {
191*1014f7efSPeter Ujfalusi 		/* Fill in the PLL control registers for J & D
192*1014f7efSPeter Ujfalusi 		 * pll_clk = (.5 * pll_clkin * J.D) / 2^p
193*1014f7efSPeter Ujfalusi 		 * Need to fill in J and D here based on incoming freq
194*1014f7efSPeter Ujfalusi 		 */
195*1014f7efSPeter Ujfalusi 		unsigned int d;
196*1014f7efSPeter Ujfalusi 		u8 j;
197*1014f7efSPeter Ujfalusi 		u8 pll_sel = (tas2552->pll_clk_id << 3) & TAS2552_PLL_SRC_MASK;
198*1014f7efSPeter Ujfalusi 		u8 p = snd_soc_read(codec, TAS2552_PLL_CTRL_1);
199*1014f7efSPeter Ujfalusi 
200*1014f7efSPeter Ujfalusi 		p = (p >> 7);
201*1014f7efSPeter Ujfalusi 
202*1014f7efSPeter Ujfalusi recalc:
203*1014f7efSPeter Ujfalusi 		j = (pll_clk * 2 * (1 << p)) / pll_clkin;
204*1014f7efSPeter Ujfalusi 		d = (pll_clk * 2 * (1 << p)) % pll_clkin;
205*1014f7efSPeter Ujfalusi 		d /= (pll_clkin / 10000);
206*1014f7efSPeter Ujfalusi 
207*1014f7efSPeter Ujfalusi 		if (d && (pll_clkin < 512000 || pll_clkin > 9200000)) {
208*1014f7efSPeter Ujfalusi 			if (tas2552->pll_clk_id == TAS2552_PLL_CLKIN_BCLK) {
209*1014f7efSPeter Ujfalusi 				pll_clkin = 1800000;
210*1014f7efSPeter Ujfalusi 				pll_sel = (TAS2552_PLL_CLKIN_1_8_FIXED << 3) &
211*1014f7efSPeter Ujfalusi 							TAS2552_PLL_SRC_MASK;
212*1014f7efSPeter Ujfalusi 			} else {
213*1014f7efSPeter Ujfalusi 				pll_clkin = snd_soc_params_to_bclk(params);
214*1014f7efSPeter Ujfalusi 				pll_clkin += tas2552->tdm_delay;
215*1014f7efSPeter Ujfalusi 				pll_sel = (TAS2552_PLL_CLKIN_BCLK << 3) &
216*1014f7efSPeter Ujfalusi 							TAS2552_PLL_SRC_MASK;
217*1014f7efSPeter Ujfalusi 			}
218*1014f7efSPeter Ujfalusi 			goto recalc;
219*1014f7efSPeter Ujfalusi 		}
220*1014f7efSPeter Ujfalusi 
221*1014f7efSPeter Ujfalusi 		snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_PLL_SRC_MASK,
222*1014f7efSPeter Ujfalusi 				    pll_sel);
223*1014f7efSPeter Ujfalusi 
224*1014f7efSPeter Ujfalusi 		snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1,
225*1014f7efSPeter Ujfalusi 				    TAS2552_PLL_J_MASK, j);
226*1014f7efSPeter Ujfalusi 		/* Will clear the PLL_BYPASS bit */
227*1014f7efSPeter Ujfalusi 		snd_soc_write(codec, TAS2552_PLL_CTRL_2,
228*1014f7efSPeter Ujfalusi 			      TAS2552_PLL_D_UPPER(d));
229*1014f7efSPeter Ujfalusi 		snd_soc_write(codec, TAS2552_PLL_CTRL_3,
230*1014f7efSPeter Ujfalusi 			      TAS2552_PLL_D_LOWER(d));
231*1014f7efSPeter Ujfalusi 	}
232*1014f7efSPeter Ujfalusi 
233*1014f7efSPeter Ujfalusi 	/* Restore PLL status */
234*1014f7efSPeter Ujfalusi 	snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE,
235*1014f7efSPeter Ujfalusi 			    pll_enable);
236*1014f7efSPeter Ujfalusi 
237*1014f7efSPeter Ujfalusi 	return 0;
238*1014f7efSPeter Ujfalusi }
239*1014f7efSPeter Ujfalusi 
2405df7f71dSDan Murphy static int tas2552_hw_params(struct snd_pcm_substream *substream,
2415df7f71dSDan Murphy 			     struct snd_pcm_hw_params *params,
2425df7f71dSDan Murphy 			     struct snd_soc_dai *dai)
2435df7f71dSDan Murphy {
2445df7f71dSDan Murphy 	struct snd_soc_codec *codec = dai->codec;
2455df7f71dSDan Murphy 	struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
246d20b098dSPeter Ujfalusi 	int cpf;
247a571cb17SPeter Ujfalusi 	u8 ser_ctrl1_reg, wclk_rate;
248d20b098dSPeter Ujfalusi 
249d20b098dSPeter Ujfalusi 	switch (params_width(params)) {
250d20b098dSPeter Ujfalusi 	case 16:
251d20b098dSPeter Ujfalusi 		ser_ctrl1_reg = TAS2552_WORDLENGTH_16BIT;
252d20b098dSPeter Ujfalusi 		cpf = 32 + tas2552->tdm_delay;
253d20b098dSPeter Ujfalusi 		break;
254d20b098dSPeter Ujfalusi 	case 20:
255d20b098dSPeter Ujfalusi 		ser_ctrl1_reg = TAS2552_WORDLENGTH_20BIT;
256d20b098dSPeter Ujfalusi 		cpf = 64 + tas2552->tdm_delay;
257d20b098dSPeter Ujfalusi 		break;
258d20b098dSPeter Ujfalusi 	case 24:
259d20b098dSPeter Ujfalusi 		ser_ctrl1_reg = TAS2552_WORDLENGTH_24BIT;
260d20b098dSPeter Ujfalusi 		cpf = 64 + tas2552->tdm_delay;
261d20b098dSPeter Ujfalusi 		break;
262d20b098dSPeter Ujfalusi 	case 32:
263d20b098dSPeter Ujfalusi 		ser_ctrl1_reg = TAS2552_WORDLENGTH_32BIT;
264d20b098dSPeter Ujfalusi 		cpf = 64 + tas2552->tdm_delay;
265d20b098dSPeter Ujfalusi 		break;
266d20b098dSPeter Ujfalusi 	default:
267d20b098dSPeter Ujfalusi 		dev_err(codec->dev, "Not supported sample size: %d\n",
268d20b098dSPeter Ujfalusi 			params_width(params));
269d20b098dSPeter Ujfalusi 		return -EINVAL;
270d20b098dSPeter Ujfalusi 	}
271d20b098dSPeter Ujfalusi 
272d20b098dSPeter Ujfalusi 	if (cpf <= 32)
273d20b098dSPeter Ujfalusi 		ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_32;
274d20b098dSPeter Ujfalusi 	else if (cpf <= 64)
275d20b098dSPeter Ujfalusi 		ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_64;
276d20b098dSPeter Ujfalusi 	else if (cpf <= 128)
277d20b098dSPeter Ujfalusi 		ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_128;
278d20b098dSPeter Ujfalusi 	else
279d20b098dSPeter Ujfalusi 		ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_256;
280d20b098dSPeter Ujfalusi 
281d20b098dSPeter Ujfalusi 	snd_soc_update_bits(codec, TAS2552_SER_CTRL_1,
282d20b098dSPeter Ujfalusi 			    TAS2552_WORDLENGTH_MASK | TAS2552_CLKSPERFRAME_MASK,
283d20b098dSPeter Ujfalusi 			    ser_ctrl1_reg);
2845df7f71dSDan Murphy 
285a571cb17SPeter Ujfalusi 	switch (params_rate(params)) {
286a571cb17SPeter Ujfalusi 	case 8000:
287a571cb17SPeter Ujfalusi 		wclk_rate = TAS2552_WCLK_FREQ_8KHZ;
288a571cb17SPeter Ujfalusi 		break;
289a571cb17SPeter Ujfalusi 	case 11025:
290a571cb17SPeter Ujfalusi 	case 12000:
291a571cb17SPeter Ujfalusi 		wclk_rate = TAS2552_WCLK_FREQ_11_12KHZ;
292a571cb17SPeter Ujfalusi 		break;
293a571cb17SPeter Ujfalusi 	case 16000:
294a571cb17SPeter Ujfalusi 		wclk_rate = TAS2552_WCLK_FREQ_16KHZ;
295a571cb17SPeter Ujfalusi 		break;
296a571cb17SPeter Ujfalusi 	case 22050:
297a571cb17SPeter Ujfalusi 	case 24000:
298a571cb17SPeter Ujfalusi 		wclk_rate = TAS2552_WCLK_FREQ_22_24KHZ;
299a571cb17SPeter Ujfalusi 		break;
300a571cb17SPeter Ujfalusi 	case 32000:
301a571cb17SPeter Ujfalusi 		wclk_rate = TAS2552_WCLK_FREQ_32KHZ;
302a571cb17SPeter Ujfalusi 		break;
303a571cb17SPeter Ujfalusi 	case 44100:
304a571cb17SPeter Ujfalusi 	case 48000:
305a571cb17SPeter Ujfalusi 		wclk_rate = TAS2552_WCLK_FREQ_44_48KHZ;
306a571cb17SPeter Ujfalusi 		break;
307a571cb17SPeter Ujfalusi 	case 88200:
308a571cb17SPeter Ujfalusi 	case 96000:
309a571cb17SPeter Ujfalusi 		wclk_rate = TAS2552_WCLK_FREQ_88_96KHZ;
310a571cb17SPeter Ujfalusi 		break;
311a571cb17SPeter Ujfalusi 	case 176400:
312a571cb17SPeter Ujfalusi 	case 192000:
313a571cb17SPeter Ujfalusi 		wclk_rate = TAS2552_WCLK_FREQ_176_192KHZ;
314a571cb17SPeter Ujfalusi 		break;
315a571cb17SPeter Ujfalusi 	default:
316a571cb17SPeter Ujfalusi 		dev_err(codec->dev, "Not supported sample rate: %d\n",
317a571cb17SPeter Ujfalusi 			params_rate(params));
318a571cb17SPeter Ujfalusi 		return -EINVAL;
319a571cb17SPeter Ujfalusi 	}
320a571cb17SPeter Ujfalusi 
321a571cb17SPeter Ujfalusi 	snd_soc_update_bits(codec, TAS2552_CFG_3, TAS2552_WCLK_FREQ_MASK,
322a571cb17SPeter Ujfalusi 			    wclk_rate);
323a571cb17SPeter Ujfalusi 
324*1014f7efSPeter Ujfalusi 	return tas2552_setup_pll(codec, params);
3255df7f71dSDan Murphy }
3265df7f71dSDan Murphy 
3271b68c7dcSPeter Ujfalusi #define TAS2552_DAI_FMT_MASK	(TAS2552_BCLKDIR | \
3281b68c7dcSPeter Ujfalusi 				 TAS2552_WCLKDIR | \
3291b68c7dcSPeter Ujfalusi 				 TAS2552_DATAFORMAT_MASK)
3303f747a81SPeter Ujfalusi static int tas2552_prepare(struct snd_pcm_substream *substream,
3313f747a81SPeter Ujfalusi 			   struct snd_soc_dai *dai)
3323f747a81SPeter Ujfalusi {
3333f747a81SPeter Ujfalusi 	struct snd_soc_codec *codec = dai->codec;
3343f747a81SPeter Ujfalusi 	struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
3353f747a81SPeter Ujfalusi 	int delay = 0;
3363f747a81SPeter Ujfalusi 
3373f747a81SPeter Ujfalusi 	/* TDM slot selection only valid in DSP_A/_B mode */
3383f747a81SPeter Ujfalusi 	if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_A)
3393f747a81SPeter Ujfalusi 		delay += (tas2552->tdm_delay + 1);
3403f747a81SPeter Ujfalusi 	else if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_B)
3413f747a81SPeter Ujfalusi 		delay += tas2552->tdm_delay;
3423f747a81SPeter Ujfalusi 
3433f747a81SPeter Ujfalusi 	/* Configure data delay */
3443f747a81SPeter Ujfalusi 	snd_soc_write(codec, TAS2552_SER_CTRL_2, delay);
3453f747a81SPeter Ujfalusi 
3463f747a81SPeter Ujfalusi 	return 0;
3473f747a81SPeter Ujfalusi }
3483f747a81SPeter Ujfalusi 
3495df7f71dSDan Murphy static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
3505df7f71dSDan Murphy {
3515df7f71dSDan Murphy 	struct snd_soc_codec *codec = dai->codec;
3523f747a81SPeter Ujfalusi 	struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
3535df7f71dSDan Murphy 	u8 serial_format;
3545df7f71dSDan Murphy 
3555df7f71dSDan Murphy 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
3565df7f71dSDan Murphy 	case SND_SOC_DAIFMT_CBS_CFS:
3575df7f71dSDan Murphy 		serial_format = 0x00;
3585df7f71dSDan Murphy 		break;
3595df7f71dSDan Murphy 	case SND_SOC_DAIFMT_CBS_CFM:
3601b68c7dcSPeter Ujfalusi 		serial_format = TAS2552_WCLKDIR;
3615df7f71dSDan Murphy 		break;
3625df7f71dSDan Murphy 	case SND_SOC_DAIFMT_CBM_CFS:
3631b68c7dcSPeter Ujfalusi 		serial_format = TAS2552_BCLKDIR;
3645df7f71dSDan Murphy 		break;
3655df7f71dSDan Murphy 	case SND_SOC_DAIFMT_CBM_CFM:
3661b68c7dcSPeter Ujfalusi 		serial_format = (TAS2552_BCLKDIR | TAS2552_WCLKDIR);
3675df7f71dSDan Murphy 		break;
3685df7f71dSDan Murphy 	default:
3695df7f71dSDan Murphy 		dev_vdbg(codec->dev, "DAI Format master is not found\n");
3705df7f71dSDan Murphy 		return -EINVAL;
3715df7f71dSDan Murphy 	}
3725df7f71dSDan Murphy 
3734c331373SPeter Ujfalusi 	switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK |
3744c331373SPeter Ujfalusi 		       SND_SOC_DAIFMT_INV_MASK)) {
3754c331373SPeter Ujfalusi 	case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF):
3765df7f71dSDan Murphy 		break;
3774c331373SPeter Ujfalusi 	case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF):
3784c331373SPeter Ujfalusi 	case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF):
3791b68c7dcSPeter Ujfalusi 		serial_format |= TAS2552_DATAFORMAT_DSP;
3805df7f71dSDan Murphy 		break;
3814c331373SPeter Ujfalusi 	case (SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_NB_NF):
3821b68c7dcSPeter Ujfalusi 		serial_format |= TAS2552_DATAFORMAT_RIGHT_J;
3835df7f71dSDan Murphy 		break;
3844c331373SPeter Ujfalusi 	case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF):
3851b68c7dcSPeter Ujfalusi 		serial_format |= TAS2552_DATAFORMAT_LEFT_J;
3865df7f71dSDan Murphy 		break;
3875df7f71dSDan Murphy 	default:
3885df7f71dSDan Murphy 		dev_vdbg(codec->dev, "DAI Format is not found\n");
3895df7f71dSDan Murphy 		return -EINVAL;
3905df7f71dSDan Murphy 	}
3913f747a81SPeter Ujfalusi 	tas2552->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
3925df7f71dSDan Murphy 
3934c331373SPeter Ujfalusi 	snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, TAS2552_DAI_FMT_MASK,
3945df7f71dSDan Murphy 			    serial_format);
3955df7f71dSDan Murphy 	return 0;
3965df7f71dSDan Murphy }
3975df7f71dSDan Murphy 
3985df7f71dSDan Murphy static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
3995df7f71dSDan Murphy 				  unsigned int freq, int dir)
4005df7f71dSDan Murphy {
4015df7f71dSDan Murphy 	struct snd_soc_codec *codec = dai->codec;
4025df7f71dSDan Murphy 	struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
4039d87a888SPeter Ujfalusi 	u8 reg, mask, val;
4045df7f71dSDan Murphy 
4059d87a888SPeter Ujfalusi 	switch (clk_id) {
4069d87a888SPeter Ujfalusi 	case TAS2552_PLL_CLKIN_MCLK:
4079d87a888SPeter Ujfalusi 	case TAS2552_PLL_CLKIN_IVCLKIN:
408*1014f7efSPeter Ujfalusi 		if (freq < 512000 || freq > 24576000) {
409*1014f7efSPeter Ujfalusi 			/* out of range PLL_CLKIN, fall back to use BCLK */
410*1014f7efSPeter Ujfalusi 			dev_warn(codec->dev, "Out of range PLL_CLKIN: %u\n",
411*1014f7efSPeter Ujfalusi 				 freq);
412*1014f7efSPeter Ujfalusi 			clk_id = TAS2552_PLL_CLKIN_BCLK;
413*1014f7efSPeter Ujfalusi 			freq = 0;
414*1014f7efSPeter Ujfalusi 		}
415*1014f7efSPeter Ujfalusi 		/* fall through */
416*1014f7efSPeter Ujfalusi 	case TAS2552_PLL_CLKIN_BCLK:
4179d87a888SPeter Ujfalusi 	case TAS2552_PLL_CLKIN_1_8_FIXED:
4189d87a888SPeter Ujfalusi 		mask = TAS2552_PLL_SRC_MASK;
4199d87a888SPeter Ujfalusi 		val = (clk_id << 3) & mask; /* bit 4:5 in the register */
4209d87a888SPeter Ujfalusi 		reg = TAS2552_CFG_1;
421*1014f7efSPeter Ujfalusi 		tas2552->pll_clk_id = clk_id;
42216bd3952SPeter Ujfalusi 		tas2552->pll_clkin = freq;
4239d87a888SPeter Ujfalusi 		break;
4249d87a888SPeter Ujfalusi 	case TAS2552_PDM_CLK_PLL:
4259d87a888SPeter Ujfalusi 	case TAS2552_PDM_CLK_IVCLKIN:
4269d87a888SPeter Ujfalusi 	case TAS2552_PDM_CLK_BCLK:
4279d87a888SPeter Ujfalusi 	case TAS2552_PDM_CLK_MCLK:
4289d87a888SPeter Ujfalusi 		mask = TAS2552_PDM_CLK_SEL_MASK;
4299d87a888SPeter Ujfalusi 		val = (clk_id >> 1) & mask; /* bit 0:1 in the register */
4309d87a888SPeter Ujfalusi 		reg = TAS2552_PDM_CFG;
431*1014f7efSPeter Ujfalusi 		tas2552->pdm_clk_id = clk_id;
4329d87a888SPeter Ujfalusi 		tas2552->pdm_clk = freq;
4339d87a888SPeter Ujfalusi 		break;
4349d87a888SPeter Ujfalusi 	default:
4359d87a888SPeter Ujfalusi 		dev_err(codec->dev, "Invalid clk id: %d\n", clk_id);
4369d87a888SPeter Ujfalusi 		return -EINVAL;
4379d87a888SPeter Ujfalusi 	}
4389d87a888SPeter Ujfalusi 
4399d87a888SPeter Ujfalusi 	snd_soc_update_bits(codec, reg, mask, val);
4405df7f71dSDan Murphy 
4415df7f71dSDan Murphy 	return 0;
4425df7f71dSDan Murphy }
4435df7f71dSDan Murphy 
4443f747a81SPeter Ujfalusi static int tas2552_set_dai_tdm_slot(struct snd_soc_dai *dai,
4453f747a81SPeter Ujfalusi 				    unsigned int tx_mask, unsigned int rx_mask,
4463f747a81SPeter Ujfalusi 				    int slots, int slot_width)
4473f747a81SPeter Ujfalusi {
4483f747a81SPeter Ujfalusi 	struct snd_soc_codec *codec = dai->codec;
4493f747a81SPeter Ujfalusi 	struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
4503f747a81SPeter Ujfalusi 	unsigned int lsb;
4513f747a81SPeter Ujfalusi 
4523f747a81SPeter Ujfalusi 	if (unlikely(!tx_mask)) {
4533f747a81SPeter Ujfalusi 		dev_err(codec->dev, "tx masks need to be non 0\n");
4543f747a81SPeter Ujfalusi 		return -EINVAL;
4553f747a81SPeter Ujfalusi 	}
4563f747a81SPeter Ujfalusi 
4573f747a81SPeter Ujfalusi 	/* TDM based on DSP mode requires slots to be adjacent */
4583f747a81SPeter Ujfalusi 	lsb = __ffs(tx_mask);
4593f747a81SPeter Ujfalusi 	if ((lsb + 1) != __fls(tx_mask)) {
4603f747a81SPeter Ujfalusi 		dev_err(codec->dev, "Invalid mask, slots must be adjacent\n");
4613f747a81SPeter Ujfalusi 		return -EINVAL;
4623f747a81SPeter Ujfalusi 	}
4633f747a81SPeter Ujfalusi 
4643f747a81SPeter Ujfalusi 	tas2552->tdm_delay = lsb * slot_width;
4653f747a81SPeter Ujfalusi 
4663f747a81SPeter Ujfalusi 	/* DOUT in high-impedance on inactive bit clocks */
4673f747a81SPeter Ujfalusi 	snd_soc_update_bits(codec, TAS2552_DOUT,
4683f747a81SPeter Ujfalusi 			    TAS2552_SDOUT_TRISTATE, TAS2552_SDOUT_TRISTATE);
4693f747a81SPeter Ujfalusi 
4703f747a81SPeter Ujfalusi 	return 0;
4713f747a81SPeter Ujfalusi }
4723f747a81SPeter Ujfalusi 
4735df7f71dSDan Murphy static int tas2552_mute(struct snd_soc_dai *dai, int mute)
4745df7f71dSDan Murphy {
475e3606aa4SPeter Ujfalusi 	u8 cfg1_reg = 0;
4765df7f71dSDan Murphy 	struct snd_soc_codec *codec = dai->codec;
4775df7f71dSDan Murphy 
4785df7f71dSDan Murphy 	if (mute)
479e3606aa4SPeter Ujfalusi 		cfg1_reg |= TAS2552_MUTE;
4805df7f71dSDan Murphy 
4817de544fdSPeter Ujfalusi 	snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE, cfg1_reg);
4825df7f71dSDan Murphy 
4835df7f71dSDan Murphy 	return 0;
4845df7f71dSDan Murphy }
4855df7f71dSDan Murphy 
486641d334bSRafael J. Wysocki #ifdef CONFIG_PM
4875df7f71dSDan Murphy static int tas2552_runtime_suspend(struct device *dev)
4885df7f71dSDan Murphy {
4895df7f71dSDan Murphy 	struct tas2552_data *tas2552 = dev_get_drvdata(dev);
4905df7f71dSDan Murphy 
491dd6e3053SPeter Ujfalusi 	tas2552_sw_shutdown(tas2552, 1);
4925df7f71dSDan Murphy 
4935df7f71dSDan Murphy 	regcache_cache_only(tas2552->regmap, true);
4945df7f71dSDan Murphy 	regcache_mark_dirty(tas2552->regmap);
4955df7f71dSDan Murphy 
496e295a4a4SDan Murphy 	if (tas2552->enable_gpio)
497e295a4a4SDan Murphy 		gpiod_set_value(tas2552->enable_gpio, 0);
498e295a4a4SDan Murphy 
4995df7f71dSDan Murphy 	return 0;
5005df7f71dSDan Murphy }
5015df7f71dSDan Murphy 
5025df7f71dSDan Murphy static int tas2552_runtime_resume(struct device *dev)
5035df7f71dSDan Murphy {
5045df7f71dSDan Murphy 	struct tas2552_data *tas2552 = dev_get_drvdata(dev);
5055df7f71dSDan Murphy 
5065df7f71dSDan Murphy 	if (tas2552->enable_gpio)
5075df7f71dSDan Murphy 		gpiod_set_value(tas2552->enable_gpio, 1);
5085df7f71dSDan Murphy 
509dd6e3053SPeter Ujfalusi 	tas2552_sw_shutdown(tas2552, 0);
5105df7f71dSDan Murphy 
5115df7f71dSDan Murphy 	regcache_cache_only(tas2552->regmap, false);
5125df7f71dSDan Murphy 	regcache_sync(tas2552->regmap);
5135df7f71dSDan Murphy 
5145df7f71dSDan Murphy 	return 0;
5155df7f71dSDan Murphy }
5165df7f71dSDan Murphy #endif
5175df7f71dSDan Murphy 
5185df7f71dSDan Murphy static const struct dev_pm_ops tas2552_pm = {
5195df7f71dSDan Murphy 	SET_RUNTIME_PM_OPS(tas2552_runtime_suspend, tas2552_runtime_resume,
5205df7f71dSDan Murphy 			   NULL)
5215df7f71dSDan Murphy };
5225df7f71dSDan Murphy 
5235df7f71dSDan Murphy static struct snd_soc_dai_ops tas2552_speaker_dai_ops = {
5245df7f71dSDan Murphy 	.hw_params	= tas2552_hw_params,
5253f747a81SPeter Ujfalusi 	.prepare	= tas2552_prepare,
5265df7f71dSDan Murphy 	.set_sysclk	= tas2552_set_dai_sysclk,
5275df7f71dSDan Murphy 	.set_fmt	= tas2552_set_dai_fmt,
5283f747a81SPeter Ujfalusi 	.set_tdm_slot	= tas2552_set_dai_tdm_slot,
5295df7f71dSDan Murphy 	.digital_mute = tas2552_mute,
5305df7f71dSDan Murphy };
5315df7f71dSDan Murphy 
5325df7f71dSDan Murphy /* Formats supported by TAS2552 driver. */
5335df7f71dSDan Murphy #define TAS2552_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
5345df7f71dSDan Murphy 			 SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
5355df7f71dSDan Murphy 
5365df7f71dSDan Murphy /* TAS2552 dai structure. */
5375df7f71dSDan Murphy static struct snd_soc_dai_driver tas2552_dai[] = {
5385df7f71dSDan Murphy 	{
5395df7f71dSDan Murphy 		.name = "tas2552-amplifier",
5405df7f71dSDan Murphy 		.playback = {
541a7a8e994SDan Murphy 			.stream_name = "Playback",
5425df7f71dSDan Murphy 			.channels_min = 2,
5435df7f71dSDan Murphy 			.channels_max = 2,
5445df7f71dSDan Murphy 			.rates = SNDRV_PCM_RATE_8000_192000,
5455df7f71dSDan Murphy 			.formats = TAS2552_FORMATS,
5465df7f71dSDan Murphy 		},
5475df7f71dSDan Murphy 		.ops = &tas2552_speaker_dai_ops,
5485df7f71dSDan Murphy 	},
5495df7f71dSDan Murphy };
5505df7f71dSDan Murphy 
5515df7f71dSDan Murphy /*
5525df7f71dSDan Murphy  * DAC digital volumes. From -7 to 24 dB in 1 dB steps
5535df7f71dSDan Murphy  */
554dd6ae3bcSPeter Ujfalusi static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 0);
5555df7f71dSDan Murphy 
5565df7f71dSDan Murphy static const struct snd_kcontrol_new tas2552_snd_controls[] = {
5575df7f71dSDan Murphy 	SOC_SINGLE_TLV("Speaker Driver Playback Volume",
558dd6ae3bcSPeter Ujfalusi 			 TAS2552_PGA_GAIN, 0, 0x1f, 0, dac_tlv),
5595df7f71dSDan Murphy };
5605df7f71dSDan Murphy 
5615df7f71dSDan Murphy static int tas2552_codec_probe(struct snd_soc_codec *codec)
5625df7f71dSDan Murphy {
5635df7f71dSDan Murphy 	struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
5645df7f71dSDan Murphy 	int ret;
5655df7f71dSDan Murphy 
5665df7f71dSDan Murphy 	tas2552->codec = codec;
5675df7f71dSDan Murphy 
5685df7f71dSDan Murphy 	ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies),
5695df7f71dSDan Murphy 				    tas2552->supplies);
5705df7f71dSDan Murphy 
5715df7f71dSDan Murphy 	if (ret != 0) {
5725df7f71dSDan Murphy 		dev_err(codec->dev, "Failed to enable supplies: %d\n",
5735df7f71dSDan Murphy 			ret);
5745df7f71dSDan Murphy 		return ret;
5755df7f71dSDan Murphy 	}
5765df7f71dSDan Murphy 
5775df7f71dSDan Murphy 	if (tas2552->enable_gpio)
5785df7f71dSDan Murphy 		gpiod_set_value(tas2552->enable_gpio, 1);
5795df7f71dSDan Murphy 
5805df7f71dSDan Murphy 	ret = pm_runtime_get_sync(codec->dev);
5815df7f71dSDan Murphy 	if (ret < 0) {
5825df7f71dSDan Murphy 		dev_err(codec->dev, "Enabling device failed: %d\n",
5835df7f71dSDan Murphy 			ret);
5845df7f71dSDan Murphy 		goto probe_fail;
5855df7f71dSDan Murphy 	}
5865df7f71dSDan Murphy 
5877d785025SPeter Ujfalusi 	snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE, TAS2552_MUTE);
5885df7f71dSDan Murphy 	snd_soc_write(codec, TAS2552_CFG_3, TAS2552_I2S_OUT_SEL |
589a571cb17SPeter Ujfalusi 					    TAS2552_DIN_SRC_SEL_AVG_L_R);
5905df7f71dSDan Murphy 	snd_soc_write(codec, TAS2552_DOUT, TAS2552_PDM_DATA_I);
5915df7f71dSDan Murphy 	snd_soc_write(codec, TAS2552_OUTPUT_DATA, TAS2552_PDM_DATA_V_I | 0x8);
5925df7f71dSDan Murphy 	snd_soc_write(codec, TAS2552_BOOST_PT_CTRL, TAS2552_APT_DELAY_200 |
5935df7f71dSDan Murphy 				TAS2552_APT_THRESH_2_1_7);
5945df7f71dSDan Murphy 
595a7a8e994SDan Murphy 	snd_soc_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN |
596a7a8e994SDan Murphy 				  TAS2552_APT_EN | TAS2552_LIM_EN);
597a7a8e994SDan Murphy 
5985df7f71dSDan Murphy 	return 0;
5995df7f71dSDan Murphy 
6005df7f71dSDan Murphy probe_fail:
6015df7f71dSDan Murphy 	if (tas2552->enable_gpio)
6025df7f71dSDan Murphy 		gpiod_set_value(tas2552->enable_gpio, 0);
6035df7f71dSDan Murphy 
6045df7f71dSDan Murphy 	regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies),
6055df7f71dSDan Murphy 					tas2552->supplies);
6065df7f71dSDan Murphy 	return -EIO;
6075df7f71dSDan Murphy }
6085df7f71dSDan Murphy 
6095df7f71dSDan Murphy static int tas2552_codec_remove(struct snd_soc_codec *codec)
6105df7f71dSDan Murphy {
6115df7f71dSDan Murphy 	struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
6125df7f71dSDan Murphy 
613e295a4a4SDan Murphy 	pm_runtime_put(codec->dev);
614e295a4a4SDan Murphy 
6155df7f71dSDan Murphy 	if (tas2552->enable_gpio)
6165df7f71dSDan Murphy 		gpiod_set_value(tas2552->enable_gpio, 0);
6175df7f71dSDan Murphy 
6185df7f71dSDan Murphy 	return 0;
6195df7f71dSDan Murphy };
6205df7f71dSDan Murphy 
6215df7f71dSDan Murphy #ifdef CONFIG_PM
6225df7f71dSDan Murphy static int tas2552_suspend(struct snd_soc_codec *codec)
6235df7f71dSDan Murphy {
6245df7f71dSDan Murphy 	struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
6255df7f71dSDan Murphy 	int ret;
6265df7f71dSDan Murphy 
6275df7f71dSDan Murphy 	ret = regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies),
6285df7f71dSDan Murphy 					tas2552->supplies);
6295df7f71dSDan Murphy 
6305df7f71dSDan Murphy 	if (ret != 0)
6315df7f71dSDan Murphy 		dev_err(codec->dev, "Failed to disable supplies: %d\n",
6325df7f71dSDan Murphy 			ret);
6335df7f71dSDan Murphy 	return 0;
6345df7f71dSDan Murphy }
6355df7f71dSDan Murphy 
6365df7f71dSDan Murphy static int tas2552_resume(struct snd_soc_codec *codec)
6375df7f71dSDan Murphy {
6385df7f71dSDan Murphy 	struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
6395df7f71dSDan Murphy 	int ret;
6405df7f71dSDan Murphy 
6415df7f71dSDan Murphy 	ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies),
6425df7f71dSDan Murphy 				    tas2552->supplies);
6435df7f71dSDan Murphy 
6445df7f71dSDan Murphy 	if (ret != 0) {
6455df7f71dSDan Murphy 		dev_err(codec->dev, "Failed to enable supplies: %d\n",
6465df7f71dSDan Murphy 			ret);
6475df7f71dSDan Murphy 	}
6485df7f71dSDan Murphy 
6495df7f71dSDan Murphy 	return 0;
6505df7f71dSDan Murphy }
6515df7f71dSDan Murphy #else
6525df7f71dSDan Murphy #define tas2552_suspend NULL
6535df7f71dSDan Murphy #define tas2552_resume NULL
6545df7f71dSDan Murphy #endif
6555df7f71dSDan Murphy 
6565df7f71dSDan Murphy static struct snd_soc_codec_driver soc_codec_dev_tas2552 = {
6575df7f71dSDan Murphy 	.probe = tas2552_codec_probe,
6585df7f71dSDan Murphy 	.remove = tas2552_codec_remove,
6595df7f71dSDan Murphy 	.suspend =	tas2552_suspend,
6605df7f71dSDan Murphy 	.resume = tas2552_resume,
6617d785025SPeter Ujfalusi 	.ignore_pmdown_time = true,
6627d785025SPeter Ujfalusi 
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),
6695df7f71dSDan Murphy };
6705df7f71dSDan Murphy 
6715df7f71dSDan Murphy static const struct regmap_config tas2552_regmap_config = {
6725df7f71dSDan Murphy 	.reg_bits = 8,
6735df7f71dSDan Murphy 	.val_bits = 8,
6745df7f71dSDan Murphy 
6755df7f71dSDan Murphy 	.max_register = TAS2552_MAX_REG,
6765df7f71dSDan Murphy 	.reg_defaults = tas2552_reg_defs,
6775df7f71dSDan Murphy 	.num_reg_defaults = ARRAY_SIZE(tas2552_reg_defs),
6785df7f71dSDan Murphy 	.cache_type = REGCACHE_RBTREE,
6795df7f71dSDan Murphy };
6805df7f71dSDan Murphy 
6815df7f71dSDan Murphy static int tas2552_probe(struct i2c_client *client,
6825df7f71dSDan Murphy 			   const struct i2c_device_id *id)
6835df7f71dSDan Murphy {
6845df7f71dSDan Murphy 	struct device *dev;
6855df7f71dSDan Murphy 	struct tas2552_data *data;
6865df7f71dSDan Murphy 	int ret;
6875df7f71dSDan Murphy 	int i;
6885df7f71dSDan Murphy 
6895df7f71dSDan Murphy 	dev = &client->dev;
6905df7f71dSDan Murphy 	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
6915df7f71dSDan Murphy 	if (data == NULL)
6925df7f71dSDan Murphy 		return -ENOMEM;
6935df7f71dSDan Murphy 
69434d7c390SUwe Kleine-König 	data->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
695ea178d14SPeter Ujfalusi 	if (IS_ERR(data->enable_gpio)) {
696ea178d14SPeter Ujfalusi 		if (PTR_ERR(data->enable_gpio) == -EPROBE_DEFER)
697ea178d14SPeter Ujfalusi 			return -EPROBE_DEFER;
698ea178d14SPeter Ujfalusi 
699ea178d14SPeter Ujfalusi 		data->enable_gpio = NULL;;
700ea178d14SPeter Ujfalusi 	}
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 
7305df7f71dSDan Murphy 	ret = snd_soc_register_codec(&client->dev,
7315df7f71dSDan Murphy 				      &soc_codec_dev_tas2552,
7325df7f71dSDan Murphy 				      tas2552_dai, ARRAY_SIZE(tas2552_dai));
7335df7f71dSDan Murphy 	if (ret < 0)
7345df7f71dSDan Murphy 		dev_err(&client->dev, "Failed to register codec: %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 {
7415df7f71dSDan Murphy 	snd_soc_unregister_codec(&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 		.owner = THIS_MODULE,
7635df7f71dSDan Murphy 		.of_match_table = of_match_ptr(tas2552_of_match),
7645df7f71dSDan Murphy 		.pm = &tas2552_pm,
7655df7f71dSDan Murphy 	},
7665df7f71dSDan Murphy 	.probe = tas2552_probe,
7675df7f71dSDan Murphy 	.remove = tas2552_i2c_remove,
7685df7f71dSDan Murphy 	.id_table = tas2552_id,
7695df7f71dSDan Murphy };
7705df7f71dSDan Murphy 
7715df7f71dSDan Murphy module_i2c_driver(tas2552_i2c_driver);
7725df7f71dSDan Murphy 
7735df7f71dSDan Murphy MODULE_AUTHOR("Dan Muprhy <dmurphy@ti.com>");
7745df7f71dSDan Murphy MODULE_DESCRIPTION("TAS2552 Audio amplifier driver");
7755df7f71dSDan Murphy MODULE_LICENSE("GPL");
776