xref: /linux/sound/soc/codecs/tas2552.c (revision 5df7f71d5cdfbcbfd7e1b68df9994609d33f7e58)
1*5df7f71dSDan Murphy /*
2*5df7f71dSDan Murphy  * tas2552.c - ALSA SoC Texas Instruments TAS2552 Mono Audio Amplifier
3*5df7f71dSDan Murphy  *
4*5df7f71dSDan Murphy  * Copyright (C) 2014 Texas Instruments Incorporated -  http://www.ti.com
5*5df7f71dSDan Murphy  *
6*5df7f71dSDan Murphy  * Author: Dan Murphy <dmurphy@ti.com>
7*5df7f71dSDan Murphy  *
8*5df7f71dSDan Murphy  * This program is free software; you can redistribute it and/or
9*5df7f71dSDan Murphy  * modify it under the terms of the GNU General Public License
10*5df7f71dSDan Murphy  * version 2 as published by the Free Software Foundation.
11*5df7f71dSDan Murphy  *
12*5df7f71dSDan Murphy  * This program is distributed in the hope that it will be useful, but
13*5df7f71dSDan Murphy  * WITHOUT ANY WARRANTY; without even the implied warranty of
14*5df7f71dSDan Murphy  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15*5df7f71dSDan Murphy  * General Public License for more details.
16*5df7f71dSDan Murphy  */
17*5df7f71dSDan Murphy 
18*5df7f71dSDan Murphy #include <linux/module.h>
19*5df7f71dSDan Murphy #include <linux/errno.h>
20*5df7f71dSDan Murphy #include <linux/device.h>
21*5df7f71dSDan Murphy #include <linux/i2c.h>
22*5df7f71dSDan Murphy #include <linux/gpio.h>
23*5df7f71dSDan Murphy #include <linux/of_gpio.h>
24*5df7f71dSDan Murphy #include <linux/pm_runtime.h>
25*5df7f71dSDan Murphy #include <linux/regmap.h>
26*5df7f71dSDan Murphy #include <linux/slab.h>
27*5df7f71dSDan Murphy 
28*5df7f71dSDan Murphy #include <linux/gpio/consumer.h>
29*5df7f71dSDan Murphy #include <linux/regulator/consumer.h>
30*5df7f71dSDan Murphy 
31*5df7f71dSDan Murphy #include <sound/pcm.h>
32*5df7f71dSDan Murphy #include <sound/pcm_params.h>
33*5df7f71dSDan Murphy #include <sound/soc.h>
34*5df7f71dSDan Murphy #include <sound/soc-dapm.h>
35*5df7f71dSDan Murphy #include <sound/tlv.h>
36*5df7f71dSDan Murphy #include <sound/tas2552-plat.h>
37*5df7f71dSDan Murphy 
38*5df7f71dSDan Murphy #include "tas2552.h"
39*5df7f71dSDan Murphy 
40*5df7f71dSDan Murphy static struct reg_default tas2552_reg_defs[] = {
41*5df7f71dSDan Murphy 	{TAS2552_CFG_1, 0x22},
42*5df7f71dSDan Murphy 	{TAS2552_CFG_3, 0x80},
43*5df7f71dSDan Murphy 	{TAS2552_DOUT, 0x00},
44*5df7f71dSDan Murphy 	{TAS2552_OUTPUT_DATA, 0xc0},
45*5df7f71dSDan Murphy 	{TAS2552_PDM_CFG, 0x01},
46*5df7f71dSDan Murphy 	{TAS2552_PGA_GAIN, 0x00},
47*5df7f71dSDan Murphy 	{TAS2552_BOOST_PT_CTRL, 0x0f},
48*5df7f71dSDan Murphy 	{TAS2552_RESERVED_0D, 0x00},
49*5df7f71dSDan Murphy 	{TAS2552_LIMIT_RATE_HYS, 0x08},
50*5df7f71dSDan Murphy 	{TAS2552_CFG_2, 0xef},
51*5df7f71dSDan Murphy 	{TAS2552_SER_CTRL_1, 0x00},
52*5df7f71dSDan Murphy 	{TAS2552_SER_CTRL_2, 0x00},
53*5df7f71dSDan Murphy 	{TAS2552_PLL_CTRL_1, 0x10},
54*5df7f71dSDan Murphy 	{TAS2552_PLL_CTRL_2, 0x00},
55*5df7f71dSDan Murphy 	{TAS2552_PLL_CTRL_3, 0x00},
56*5df7f71dSDan Murphy 	{TAS2552_BTIP, 0x8f},
57*5df7f71dSDan Murphy 	{TAS2552_BTS_CTRL, 0x80},
58*5df7f71dSDan Murphy 	{TAS2552_LIMIT_RELEASE, 0x04},
59*5df7f71dSDan Murphy 	{TAS2552_LIMIT_INT_COUNT, 0x00},
60*5df7f71dSDan Murphy 	{TAS2552_EDGE_RATE_CTRL, 0x40},
61*5df7f71dSDan Murphy 	{TAS2552_VBAT_DATA, 0x00},
62*5df7f71dSDan Murphy };
63*5df7f71dSDan Murphy 
64*5df7f71dSDan Murphy #define TAS2552_NUM_SUPPLIES	3
65*5df7f71dSDan Murphy static const char *tas2552_supply_names[TAS2552_NUM_SUPPLIES] = {
66*5df7f71dSDan Murphy 	"vbat",		/* vbat voltage */
67*5df7f71dSDan Murphy 	"iovdd",	/* I/O Voltage */
68*5df7f71dSDan Murphy 	"avdd",		/* Analog DAC Voltage */
69*5df7f71dSDan Murphy };
70*5df7f71dSDan Murphy 
71*5df7f71dSDan Murphy struct tas2552_data {
72*5df7f71dSDan Murphy 	struct snd_soc_codec *codec;
73*5df7f71dSDan Murphy 	struct regmap *regmap;
74*5df7f71dSDan Murphy 	struct i2c_client *tas2552_client;
75*5df7f71dSDan Murphy 	struct regulator_bulk_data supplies[TAS2552_NUM_SUPPLIES];
76*5df7f71dSDan Murphy 	struct gpio_desc *enable_gpio;
77*5df7f71dSDan Murphy 	unsigned char regs[TAS2552_VBAT_DATA];
78*5df7f71dSDan Murphy 	unsigned int mclk;
79*5df7f71dSDan Murphy };
80*5df7f71dSDan Murphy 
81*5df7f71dSDan Murphy static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown)
82*5df7f71dSDan Murphy {
83*5df7f71dSDan Murphy 	u8 cfg1_reg;
84*5df7f71dSDan Murphy 
85*5df7f71dSDan Murphy 	if (sw_shutdown)
86*5df7f71dSDan Murphy 		cfg1_reg = 0;
87*5df7f71dSDan Murphy 	else
88*5df7f71dSDan Murphy 		cfg1_reg = TAS2552_SWS_MASK;
89*5df7f71dSDan Murphy 
90*5df7f71dSDan Murphy 	snd_soc_update_bits(tas_data->codec, TAS2552_CFG_1,
91*5df7f71dSDan Murphy 						 TAS2552_SWS_MASK, cfg1_reg);
92*5df7f71dSDan Murphy }
93*5df7f71dSDan Murphy 
94*5df7f71dSDan Murphy static int tas2552_hw_params(struct snd_pcm_substream *substream,
95*5df7f71dSDan Murphy 			     struct snd_pcm_hw_params *params,
96*5df7f71dSDan Murphy 			     struct snd_soc_dai *dai)
97*5df7f71dSDan Murphy {
98*5df7f71dSDan Murphy 	struct snd_soc_codec *codec = dai->codec;
99*5df7f71dSDan Murphy 	struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
100*5df7f71dSDan Murphy 	int sample_rate, pll_clk;
101*5df7f71dSDan Murphy 	int d;
102*5df7f71dSDan Murphy 	u8 p, j;
103*5df7f71dSDan Murphy 
104*5df7f71dSDan Murphy 	/* Turn on Class D amplifier */
105*5df7f71dSDan Murphy 	snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_CLASSD_EN_MASK,
106*5df7f71dSDan Murphy 						TAS2552_CLASSD_EN);
107*5df7f71dSDan Murphy 
108*5df7f71dSDan Murphy 	if (!tas2552->mclk)
109*5df7f71dSDan Murphy 		return -EINVAL;
110*5df7f71dSDan Murphy 
111*5df7f71dSDan Murphy 	snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0);
112*5df7f71dSDan Murphy 
113*5df7f71dSDan Murphy 	if (tas2552->mclk == TAS2552_245MHZ_CLK ||
114*5df7f71dSDan Murphy 		tas2552->mclk == TAS2552_225MHZ_CLK) {
115*5df7f71dSDan Murphy 		/* By pass the PLL configuration */
116*5df7f71dSDan Murphy 		snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2,
117*5df7f71dSDan Murphy 				    TAS2552_PLL_BYPASS_MASK,
118*5df7f71dSDan Murphy 				    TAS2552_PLL_BYPASS);
119*5df7f71dSDan Murphy 	} else {
120*5df7f71dSDan Murphy 		/* Fill in the PLL control registers for J & D
121*5df7f71dSDan Murphy 		 * PLL_CLK = (.5 * freq * J.D) / 2^p
122*5df7f71dSDan Murphy 		 * Need to fill in J and D here based on incoming freq
123*5df7f71dSDan Murphy 		 */
124*5df7f71dSDan Murphy 		p = snd_soc_read(codec, TAS2552_PLL_CTRL_1);
125*5df7f71dSDan Murphy 		p = (p >> 7);
126*5df7f71dSDan Murphy 		sample_rate = params_rate(params);
127*5df7f71dSDan Murphy 
128*5df7f71dSDan Murphy 		if (sample_rate == 48000)
129*5df7f71dSDan Murphy 			pll_clk = TAS2552_245MHZ_CLK;
130*5df7f71dSDan Murphy 		else if (sample_rate == 44100)
131*5df7f71dSDan Murphy 			pll_clk = TAS2552_225MHZ_CLK;
132*5df7f71dSDan Murphy 		else {
133*5df7f71dSDan Murphy 			dev_vdbg(codec->dev, "Substream sample rate is not found %i\n",
134*5df7f71dSDan Murphy 					params_rate(params));
135*5df7f71dSDan Murphy 			return -EINVAL;
136*5df7f71dSDan Murphy 		}
137*5df7f71dSDan Murphy 
138*5df7f71dSDan Murphy 		j = (pll_clk * 2 * (1 << p)) / tas2552->mclk;
139*5df7f71dSDan Murphy 		d = (pll_clk * 2 * (1 << p)) % tas2552->mclk;
140*5df7f71dSDan Murphy 
141*5df7f71dSDan Murphy 		snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1,
142*5df7f71dSDan Murphy 				TAS2552_PLL_J_MASK, j);
143*5df7f71dSDan Murphy 		snd_soc_write(codec, TAS2552_PLL_CTRL_2,
144*5df7f71dSDan Murphy 					(d >> 7) & TAS2552_PLL_D_UPPER_MASK);
145*5df7f71dSDan Murphy 		snd_soc_write(codec, TAS2552_PLL_CTRL_3,
146*5df7f71dSDan Murphy 				d & TAS2552_PLL_D_LOWER_MASK);
147*5df7f71dSDan Murphy 
148*5df7f71dSDan Murphy 	}
149*5df7f71dSDan Murphy 
150*5df7f71dSDan Murphy 	snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE,
151*5df7f71dSDan Murphy 						TAS2552_PLL_ENABLE);
152*5df7f71dSDan Murphy 
153*5df7f71dSDan Murphy 	return 0;
154*5df7f71dSDan Murphy }
155*5df7f71dSDan Murphy 
156*5df7f71dSDan Murphy static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
157*5df7f71dSDan Murphy {
158*5df7f71dSDan Murphy 	struct snd_soc_codec *codec = dai->codec;
159*5df7f71dSDan Murphy 	u8 serial_format;
160*5df7f71dSDan Murphy 	u8 serial_control_mask;
161*5df7f71dSDan Murphy 
162*5df7f71dSDan Murphy 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
163*5df7f71dSDan Murphy 	case SND_SOC_DAIFMT_CBS_CFS:
164*5df7f71dSDan Murphy 		serial_format = 0x00;
165*5df7f71dSDan Murphy 		break;
166*5df7f71dSDan Murphy 	case SND_SOC_DAIFMT_CBS_CFM:
167*5df7f71dSDan Murphy 		serial_format = TAS2552_WORD_CLK_MASK;
168*5df7f71dSDan Murphy 		break;
169*5df7f71dSDan Murphy 	case SND_SOC_DAIFMT_CBM_CFS:
170*5df7f71dSDan Murphy 		serial_format = TAS2552_BIT_CLK_MASK;
171*5df7f71dSDan Murphy 		break;
172*5df7f71dSDan Murphy 	case SND_SOC_DAIFMT_CBM_CFM:
173*5df7f71dSDan Murphy 		serial_format = (TAS2552_BIT_CLK_MASK | TAS2552_WORD_CLK_MASK);
174*5df7f71dSDan Murphy 		break;
175*5df7f71dSDan Murphy 	default:
176*5df7f71dSDan Murphy 		dev_vdbg(codec->dev, "DAI Format master is not found\n");
177*5df7f71dSDan Murphy 		return -EINVAL;
178*5df7f71dSDan Murphy 	}
179*5df7f71dSDan Murphy 
180*5df7f71dSDan Murphy 	serial_control_mask = TAS2552_BIT_CLK_MASK | TAS2552_WORD_CLK_MASK;
181*5df7f71dSDan Murphy 
182*5df7f71dSDan Murphy 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
183*5df7f71dSDan Murphy 	case SND_SOC_DAIFMT_I2S:
184*5df7f71dSDan Murphy 		serial_format &= TAS2552_DAIFMT_I2S_MASK;
185*5df7f71dSDan Murphy 		break;
186*5df7f71dSDan Murphy 	case SND_SOC_DAIFMT_DSP_A:
187*5df7f71dSDan Murphy 		serial_format |= TAS2552_DAIFMT_DSP;
188*5df7f71dSDan Murphy 		break;
189*5df7f71dSDan Murphy 	case SND_SOC_DAIFMT_RIGHT_J:
190*5df7f71dSDan Murphy 		serial_format |= TAS2552_DAIFMT_RIGHT_J;
191*5df7f71dSDan Murphy 		break;
192*5df7f71dSDan Murphy 	case SND_SOC_DAIFMT_LEFT_J:
193*5df7f71dSDan Murphy 		serial_format |= TAS2552_DAIFMT_LEFT_J;
194*5df7f71dSDan Murphy 		break;
195*5df7f71dSDan Murphy 	default:
196*5df7f71dSDan Murphy 		dev_vdbg(codec->dev, "DAI Format is not found\n");
197*5df7f71dSDan Murphy 		return -EINVAL;
198*5df7f71dSDan Murphy 	}
199*5df7f71dSDan Murphy 
200*5df7f71dSDan Murphy 	if (fmt & SND_SOC_DAIFMT_FORMAT_MASK)
201*5df7f71dSDan Murphy 		serial_control_mask |= TAS2552_DATA_FORMAT_MASK;
202*5df7f71dSDan Murphy 
203*5df7f71dSDan Murphy 	snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, serial_control_mask,
204*5df7f71dSDan Murphy 						serial_format);
205*5df7f71dSDan Murphy 
206*5df7f71dSDan Murphy 	return 0;
207*5df7f71dSDan Murphy }
208*5df7f71dSDan Murphy 
209*5df7f71dSDan Murphy static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
210*5df7f71dSDan Murphy 				  unsigned int freq, int dir)
211*5df7f71dSDan Murphy {
212*5df7f71dSDan Murphy 	struct snd_soc_codec *codec = dai->codec;
213*5df7f71dSDan Murphy 	struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
214*5df7f71dSDan Murphy 
215*5df7f71dSDan Murphy 	tas2552->mclk = freq;
216*5df7f71dSDan Murphy 
217*5df7f71dSDan Murphy 	return 0;
218*5df7f71dSDan Murphy }
219*5df7f71dSDan Murphy 
220*5df7f71dSDan Murphy static int tas2552_mute(struct snd_soc_dai *dai, int mute)
221*5df7f71dSDan Murphy {
222*5df7f71dSDan Murphy 	u8 cfg1_reg;
223*5df7f71dSDan Murphy 	struct snd_soc_codec *codec = dai->codec;
224*5df7f71dSDan Murphy 
225*5df7f71dSDan Murphy 	if (mute)
226*5df7f71dSDan Murphy 		cfg1_reg = TAS2552_MUTE_MASK;
227*5df7f71dSDan Murphy 	else
228*5df7f71dSDan Murphy 		cfg1_reg = ~TAS2552_MUTE_MASK;
229*5df7f71dSDan Murphy 
230*5df7f71dSDan Murphy 	snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE_MASK, cfg1_reg);
231*5df7f71dSDan Murphy 
232*5df7f71dSDan Murphy 	return 0;
233*5df7f71dSDan Murphy }
234*5df7f71dSDan Murphy 
235*5df7f71dSDan Murphy #ifdef CONFIG_PM_RUNTIME
236*5df7f71dSDan Murphy static int tas2552_runtime_suspend(struct device *dev)
237*5df7f71dSDan Murphy {
238*5df7f71dSDan Murphy 	struct tas2552_data *tas2552 = dev_get_drvdata(dev);
239*5df7f71dSDan Murphy 
240*5df7f71dSDan Murphy 	tas2552_sw_shutdown(tas2552, 0);
241*5df7f71dSDan Murphy 
242*5df7f71dSDan Murphy 	if (tas2552->enable_gpio)
243*5df7f71dSDan Murphy 		gpiod_set_value(tas2552->enable_gpio, 0);
244*5df7f71dSDan Murphy 
245*5df7f71dSDan Murphy 	regcache_cache_only(tas2552->regmap, true);
246*5df7f71dSDan Murphy 	regcache_mark_dirty(tas2552->regmap);
247*5df7f71dSDan Murphy 
248*5df7f71dSDan Murphy 	return 0;
249*5df7f71dSDan Murphy }
250*5df7f71dSDan Murphy 
251*5df7f71dSDan Murphy static int tas2552_runtime_resume(struct device *dev)
252*5df7f71dSDan Murphy {
253*5df7f71dSDan Murphy 	struct tas2552_data *tas2552 = dev_get_drvdata(dev);
254*5df7f71dSDan Murphy 
255*5df7f71dSDan Murphy 	if (tas2552->enable_gpio)
256*5df7f71dSDan Murphy 		gpiod_set_value(tas2552->enable_gpio, 1);
257*5df7f71dSDan Murphy 
258*5df7f71dSDan Murphy 	tas2552_sw_shutdown(tas2552, 1);
259*5df7f71dSDan Murphy 
260*5df7f71dSDan Murphy 	regcache_cache_only(tas2552->regmap, false);
261*5df7f71dSDan Murphy 	regcache_sync(tas2552->regmap);
262*5df7f71dSDan Murphy 
263*5df7f71dSDan Murphy 	return 0;
264*5df7f71dSDan Murphy }
265*5df7f71dSDan Murphy #endif
266*5df7f71dSDan Murphy 
267*5df7f71dSDan Murphy static const struct dev_pm_ops tas2552_pm = {
268*5df7f71dSDan Murphy 	SET_RUNTIME_PM_OPS(tas2552_runtime_suspend, tas2552_runtime_resume,
269*5df7f71dSDan Murphy 			   NULL)
270*5df7f71dSDan Murphy };
271*5df7f71dSDan Murphy 
272*5df7f71dSDan Murphy static void tas2552_shutdown(struct snd_pcm_substream *substream,
273*5df7f71dSDan Murphy 			   struct snd_soc_dai *dai)
274*5df7f71dSDan Murphy {
275*5df7f71dSDan Murphy 	struct snd_soc_codec *codec = dai->codec;
276*5df7f71dSDan Murphy 
277*5df7f71dSDan Murphy 	snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0);
278*5df7f71dSDan Murphy }
279*5df7f71dSDan Murphy 
280*5df7f71dSDan Murphy static struct snd_soc_dai_ops tas2552_speaker_dai_ops = {
281*5df7f71dSDan Murphy 	.hw_params	= tas2552_hw_params,
282*5df7f71dSDan Murphy 	.set_sysclk	= tas2552_set_dai_sysclk,
283*5df7f71dSDan Murphy 	.set_fmt	= tas2552_set_dai_fmt,
284*5df7f71dSDan Murphy 	.shutdown	= tas2552_shutdown,
285*5df7f71dSDan Murphy 	.digital_mute = tas2552_mute,
286*5df7f71dSDan Murphy };
287*5df7f71dSDan Murphy 
288*5df7f71dSDan Murphy /* Formats supported by TAS2552 driver. */
289*5df7f71dSDan Murphy #define TAS2552_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
290*5df7f71dSDan Murphy 			 SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
291*5df7f71dSDan Murphy 
292*5df7f71dSDan Murphy /* TAS2552 dai structure. */
293*5df7f71dSDan Murphy static struct snd_soc_dai_driver tas2552_dai[] = {
294*5df7f71dSDan Murphy 	{
295*5df7f71dSDan Murphy 		.name = "tas2552-amplifier",
296*5df7f71dSDan Murphy 		.playback = {
297*5df7f71dSDan Murphy 			.stream_name = "Speaker",
298*5df7f71dSDan Murphy 			.channels_min = 2,
299*5df7f71dSDan Murphy 			.channels_max = 2,
300*5df7f71dSDan Murphy 			.rates = SNDRV_PCM_RATE_8000_192000,
301*5df7f71dSDan Murphy 			.formats = TAS2552_FORMATS,
302*5df7f71dSDan Murphy 		},
303*5df7f71dSDan Murphy 		.ops = &tas2552_speaker_dai_ops,
304*5df7f71dSDan Murphy 	},
305*5df7f71dSDan Murphy };
306*5df7f71dSDan Murphy 
307*5df7f71dSDan Murphy /*
308*5df7f71dSDan Murphy  * DAC digital volumes. From -7 to 24 dB in 1 dB steps
309*5df7f71dSDan Murphy  */
310*5df7f71dSDan Murphy static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 24);
311*5df7f71dSDan Murphy 
312*5df7f71dSDan Murphy static const struct snd_kcontrol_new tas2552_snd_controls[] = {
313*5df7f71dSDan Murphy 	SOC_SINGLE_TLV("Speaker Driver Playback Volume",
314*5df7f71dSDan Murphy 			 TAS2552_PGA_GAIN, 0, 0x1f, 1, dac_tlv),
315*5df7f71dSDan Murphy };
316*5df7f71dSDan Murphy 
317*5df7f71dSDan Murphy static const struct reg_default tas2552_init_regs[] = {
318*5df7f71dSDan Murphy 	{ TAS2552_RESERVED_0D, 0xc0 },
319*5df7f71dSDan Murphy };
320*5df7f71dSDan Murphy 
321*5df7f71dSDan Murphy static int tas2552_codec_probe(struct snd_soc_codec *codec)
322*5df7f71dSDan Murphy {
323*5df7f71dSDan Murphy 	struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
324*5df7f71dSDan Murphy 	int ret;
325*5df7f71dSDan Murphy 
326*5df7f71dSDan Murphy 	tas2552->codec = codec;
327*5df7f71dSDan Murphy 
328*5df7f71dSDan Murphy 	ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies),
329*5df7f71dSDan Murphy 				    tas2552->supplies);
330*5df7f71dSDan Murphy 
331*5df7f71dSDan Murphy 	if (ret != 0) {
332*5df7f71dSDan Murphy 		dev_err(codec->dev, "Failed to enable supplies: %d\n",
333*5df7f71dSDan Murphy 			ret);
334*5df7f71dSDan Murphy 		return ret;
335*5df7f71dSDan Murphy 	}
336*5df7f71dSDan Murphy 
337*5df7f71dSDan Murphy 	if (tas2552->enable_gpio)
338*5df7f71dSDan Murphy 		gpiod_set_value(tas2552->enable_gpio, 1);
339*5df7f71dSDan Murphy 
340*5df7f71dSDan Murphy 	ret = pm_runtime_get_sync(codec->dev);
341*5df7f71dSDan Murphy 	if (ret < 0) {
342*5df7f71dSDan Murphy 		dev_err(codec->dev, "Enabling device failed: %d\n",
343*5df7f71dSDan Murphy 			ret);
344*5df7f71dSDan Murphy 		goto probe_fail;
345*5df7f71dSDan Murphy 	}
346*5df7f71dSDan Murphy 
347*5df7f71dSDan Murphy 	snd_soc_write(codec, TAS2552_CFG_1, TAS2552_MUTE_MASK |
348*5df7f71dSDan Murphy 				TAS2552_PLL_SRC_BCLK);
349*5df7f71dSDan Murphy 	snd_soc_write(codec, TAS2552_CFG_3, TAS2552_I2S_OUT_SEL |
350*5df7f71dSDan Murphy 				TAS2552_DIN_SRC_SEL_AVG_L_R | TAS2552_88_96KHZ);
351*5df7f71dSDan Murphy 	snd_soc_write(codec, TAS2552_DOUT, TAS2552_PDM_DATA_I);
352*5df7f71dSDan Murphy 	snd_soc_write(codec, TAS2552_OUTPUT_DATA, TAS2552_PDM_DATA_V_I | 0x8);
353*5df7f71dSDan Murphy 	snd_soc_write(codec, TAS2552_PDM_CFG, TAS2552_PDM_BCLK_SEL);
354*5df7f71dSDan Murphy 	snd_soc_write(codec, TAS2552_BOOST_PT_CTRL, TAS2552_APT_DELAY_200 |
355*5df7f71dSDan Murphy 				TAS2552_APT_THRESH_2_1_7);
356*5df7f71dSDan Murphy 
357*5df7f71dSDan Murphy 	ret = regmap_register_patch(tas2552->regmap, tas2552_init_regs,
358*5df7f71dSDan Murphy 					    ARRAY_SIZE(tas2552_init_regs));
359*5df7f71dSDan Murphy 	if (ret != 0) {
360*5df7f71dSDan Murphy 		dev_err(codec->dev, "Failed to write init registers: %d\n",
361*5df7f71dSDan Murphy 			ret);
362*5df7f71dSDan Murphy 		goto patch_fail;
363*5df7f71dSDan Murphy 	}
364*5df7f71dSDan Murphy 
365*5df7f71dSDan Murphy 	snd_soc_write(codec, TAS2552_CFG_2, TAS2552_CLASSD_EN |
366*5df7f71dSDan Murphy 				  TAS2552_BOOST_EN | TAS2552_APT_EN |
367*5df7f71dSDan Murphy 				  TAS2552_LIM_EN);
368*5df7f71dSDan Murphy 	return 0;
369*5df7f71dSDan Murphy 
370*5df7f71dSDan Murphy patch_fail:
371*5df7f71dSDan Murphy 	pm_runtime_put(codec->dev);
372*5df7f71dSDan Murphy probe_fail:
373*5df7f71dSDan Murphy 	if (tas2552->enable_gpio)
374*5df7f71dSDan Murphy 		gpiod_set_value(tas2552->enable_gpio, 0);
375*5df7f71dSDan Murphy 
376*5df7f71dSDan Murphy 	regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies),
377*5df7f71dSDan Murphy 					tas2552->supplies);
378*5df7f71dSDan Murphy 	return -EIO;
379*5df7f71dSDan Murphy }
380*5df7f71dSDan Murphy 
381*5df7f71dSDan Murphy static int tas2552_codec_remove(struct snd_soc_codec *codec)
382*5df7f71dSDan Murphy {
383*5df7f71dSDan Murphy 	struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
384*5df7f71dSDan Murphy 
385*5df7f71dSDan Murphy 	if (tas2552->enable_gpio)
386*5df7f71dSDan Murphy 		gpiod_set_value(tas2552->enable_gpio, 0);
387*5df7f71dSDan Murphy 
388*5df7f71dSDan Murphy 	return 0;
389*5df7f71dSDan Murphy };
390*5df7f71dSDan Murphy 
391*5df7f71dSDan Murphy #ifdef CONFIG_PM
392*5df7f71dSDan Murphy static int tas2552_suspend(struct snd_soc_codec *codec)
393*5df7f71dSDan Murphy {
394*5df7f71dSDan Murphy 	struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
395*5df7f71dSDan Murphy 	int ret;
396*5df7f71dSDan Murphy 
397*5df7f71dSDan Murphy 	ret = regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies),
398*5df7f71dSDan Murphy 					tas2552->supplies);
399*5df7f71dSDan Murphy 
400*5df7f71dSDan Murphy 	if (ret != 0)
401*5df7f71dSDan Murphy 		dev_err(codec->dev, "Failed to disable supplies: %d\n",
402*5df7f71dSDan Murphy 			ret);
403*5df7f71dSDan Murphy 	return 0;
404*5df7f71dSDan Murphy }
405*5df7f71dSDan Murphy 
406*5df7f71dSDan Murphy static int tas2552_resume(struct snd_soc_codec *codec)
407*5df7f71dSDan Murphy {
408*5df7f71dSDan Murphy 	struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
409*5df7f71dSDan Murphy 	int ret;
410*5df7f71dSDan Murphy 
411*5df7f71dSDan Murphy 	ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies),
412*5df7f71dSDan Murphy 				    tas2552->supplies);
413*5df7f71dSDan Murphy 
414*5df7f71dSDan Murphy 	if (ret != 0) {
415*5df7f71dSDan Murphy 		dev_err(codec->dev, "Failed to enable supplies: %d\n",
416*5df7f71dSDan Murphy 			ret);
417*5df7f71dSDan Murphy 	}
418*5df7f71dSDan Murphy 
419*5df7f71dSDan Murphy 	return 0;
420*5df7f71dSDan Murphy }
421*5df7f71dSDan Murphy #else
422*5df7f71dSDan Murphy #define tas2552_suspend NULL
423*5df7f71dSDan Murphy #define tas2552_resume NULL
424*5df7f71dSDan Murphy #endif
425*5df7f71dSDan Murphy 
426*5df7f71dSDan Murphy static struct snd_soc_codec_driver soc_codec_dev_tas2552 = {
427*5df7f71dSDan Murphy 	.probe = tas2552_codec_probe,
428*5df7f71dSDan Murphy 	.remove = tas2552_codec_remove,
429*5df7f71dSDan Murphy 	.suspend =	tas2552_suspend,
430*5df7f71dSDan Murphy 	.resume = tas2552_resume,
431*5df7f71dSDan Murphy 	.controls = tas2552_snd_controls,
432*5df7f71dSDan Murphy 	.num_controls = ARRAY_SIZE(tas2552_snd_controls),
433*5df7f71dSDan Murphy };
434*5df7f71dSDan Murphy 
435*5df7f71dSDan Murphy static const struct regmap_config tas2552_regmap_config = {
436*5df7f71dSDan Murphy 	.reg_bits = 8,
437*5df7f71dSDan Murphy 	.val_bits = 8,
438*5df7f71dSDan Murphy 
439*5df7f71dSDan Murphy 	.max_register = TAS2552_MAX_REG,
440*5df7f71dSDan Murphy 	.reg_defaults = tas2552_reg_defs,
441*5df7f71dSDan Murphy 	.num_reg_defaults = ARRAY_SIZE(tas2552_reg_defs),
442*5df7f71dSDan Murphy 	.cache_type = REGCACHE_RBTREE,
443*5df7f71dSDan Murphy };
444*5df7f71dSDan Murphy 
445*5df7f71dSDan Murphy static int tas2552_probe(struct i2c_client *client,
446*5df7f71dSDan Murphy 			   const struct i2c_device_id *id)
447*5df7f71dSDan Murphy {
448*5df7f71dSDan Murphy 	struct device *dev;
449*5df7f71dSDan Murphy 	struct tas2552_data *data;
450*5df7f71dSDan Murphy 	int ret;
451*5df7f71dSDan Murphy 	int i;
452*5df7f71dSDan Murphy 
453*5df7f71dSDan Murphy 	dev = &client->dev;
454*5df7f71dSDan Murphy 	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
455*5df7f71dSDan Murphy 	if (data == NULL)
456*5df7f71dSDan Murphy 		return -ENOMEM;
457*5df7f71dSDan Murphy 
458*5df7f71dSDan Murphy 	data->enable_gpio = devm_gpiod_get(dev, "enable");
459*5df7f71dSDan Murphy 	if (IS_ERR(data->enable_gpio)) {
460*5df7f71dSDan Murphy 		ret = PTR_ERR(data->enable_gpio);
461*5df7f71dSDan Murphy 		if (ret != -ENOENT && ret != -ENOSYS)
462*5df7f71dSDan Murphy 			return ret;
463*5df7f71dSDan Murphy 
464*5df7f71dSDan Murphy 		data->enable_gpio = NULL;
465*5df7f71dSDan Murphy 	} else {
466*5df7f71dSDan Murphy 		gpiod_direction_output(data->enable_gpio, 0);
467*5df7f71dSDan Murphy 	}
468*5df7f71dSDan Murphy 
469*5df7f71dSDan Murphy 	data->tas2552_client = client;
470*5df7f71dSDan Murphy 	data->regmap = devm_regmap_init_i2c(client, &tas2552_regmap_config);
471*5df7f71dSDan Murphy 	if (IS_ERR(data->regmap)) {
472*5df7f71dSDan Murphy 		ret = PTR_ERR(data->regmap);
473*5df7f71dSDan Murphy 		dev_err(&client->dev, "Failed to allocate register map: %d\n",
474*5df7f71dSDan Murphy 			ret);
475*5df7f71dSDan Murphy 		return ret;
476*5df7f71dSDan Murphy 	}
477*5df7f71dSDan Murphy 
478*5df7f71dSDan Murphy 	for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
479*5df7f71dSDan Murphy 		data->supplies[i].supply = tas2552_supply_names[i];
480*5df7f71dSDan Murphy 
481*5df7f71dSDan Murphy 	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies),
482*5df7f71dSDan Murphy 				      data->supplies);
483*5df7f71dSDan Murphy 	if (ret != 0)
484*5df7f71dSDan Murphy 		dev_err(dev, "Failed to request supplies: %d\n", ret);
485*5df7f71dSDan Murphy 
486*5df7f71dSDan Murphy 	pm_runtime_set_active(&client->dev);
487*5df7f71dSDan Murphy 	pm_runtime_set_autosuspend_delay(&client->dev, 1000);
488*5df7f71dSDan Murphy 	pm_runtime_use_autosuspend(&client->dev);
489*5df7f71dSDan Murphy 	pm_runtime_enable(&client->dev);
490*5df7f71dSDan Murphy 	pm_runtime_mark_last_busy(&client->dev);
491*5df7f71dSDan Murphy 	pm_runtime_put_sync_autosuspend(&client->dev);
492*5df7f71dSDan Murphy 
493*5df7f71dSDan Murphy 	dev_set_drvdata(&client->dev, data);
494*5df7f71dSDan Murphy 
495*5df7f71dSDan Murphy 	ret = snd_soc_register_codec(&client->dev,
496*5df7f71dSDan Murphy 				      &soc_codec_dev_tas2552,
497*5df7f71dSDan Murphy 				      tas2552_dai, ARRAY_SIZE(tas2552_dai));
498*5df7f71dSDan Murphy 	if (ret < 0)
499*5df7f71dSDan Murphy 		dev_err(&client->dev, "Failed to register codec: %d\n", ret);
500*5df7f71dSDan Murphy 
501*5df7f71dSDan Murphy 	return 0;
502*5df7f71dSDan Murphy }
503*5df7f71dSDan Murphy 
504*5df7f71dSDan Murphy static int tas2552_i2c_remove(struct i2c_client *client)
505*5df7f71dSDan Murphy {
506*5df7f71dSDan Murphy 	snd_soc_unregister_codec(&client->dev);
507*5df7f71dSDan Murphy 	return 0;
508*5df7f71dSDan Murphy }
509*5df7f71dSDan Murphy 
510*5df7f71dSDan Murphy static const struct i2c_device_id tas2552_id[] = {
511*5df7f71dSDan Murphy 	{ "tas2552", 0 },
512*5df7f71dSDan Murphy 	{ }
513*5df7f71dSDan Murphy };
514*5df7f71dSDan Murphy MODULE_DEVICE_TABLE(i2c, tas2552_id);
515*5df7f71dSDan Murphy 
516*5df7f71dSDan Murphy #if IS_ENABLED(CONFIG_OF)
517*5df7f71dSDan Murphy static const struct of_device_id tas2552_of_match[] = {
518*5df7f71dSDan Murphy 	{ .compatible = "ti,tas2552", },
519*5df7f71dSDan Murphy 	{},
520*5df7f71dSDan Murphy };
521*5df7f71dSDan Murphy MODULE_DEVICE_TABLE(of, tas2552_of_match);
522*5df7f71dSDan Murphy #endif
523*5df7f71dSDan Murphy 
524*5df7f71dSDan Murphy static struct i2c_driver tas2552_i2c_driver = {
525*5df7f71dSDan Murphy 	.driver = {
526*5df7f71dSDan Murphy 		.name = "tas2552",
527*5df7f71dSDan Murphy 		.owner = THIS_MODULE,
528*5df7f71dSDan Murphy 		.of_match_table = of_match_ptr(tas2552_of_match),
529*5df7f71dSDan Murphy 		.pm = &tas2552_pm,
530*5df7f71dSDan Murphy 	},
531*5df7f71dSDan Murphy 	.probe = tas2552_probe,
532*5df7f71dSDan Murphy 	.remove = tas2552_i2c_remove,
533*5df7f71dSDan Murphy 	.id_table = tas2552_id,
534*5df7f71dSDan Murphy };
535*5df7f71dSDan Murphy 
536*5df7f71dSDan Murphy module_i2c_driver(tas2552_i2c_driver);
537*5df7f71dSDan Murphy 
538*5df7f71dSDan Murphy MODULE_AUTHOR("Dan Muprhy <dmurphy@ti.com>");
539*5df7f71dSDan Murphy MODULE_DESCRIPTION("TAS2552 Audio amplifier driver");
540*5df7f71dSDan Murphy MODULE_LICENSE("GPL");
541