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*92503b5cSShenghao Ding * Copyright (C) 2014 - 2024 Texas Instruments Incorporated -
6*92503b5cSShenghao Ding * https://www.ti.com
75df7f71dSDan Murphy *
85df7f71dSDan Murphy * Author: Dan Murphy <dmurphy@ti.com>
95df7f71dSDan Murphy */
105df7f71dSDan Murphy
115df7f71dSDan Murphy #include <linux/module.h>
125df7f71dSDan Murphy #include <linux/errno.h>
135df7f71dSDan Murphy #include <linux/device.h>
145df7f71dSDan Murphy #include <linux/i2c.h>
155df7f71dSDan Murphy #include <linux/gpio.h>
165df7f71dSDan Murphy #include <linux/of_gpio.h>
175df7f71dSDan Murphy #include <linux/pm_runtime.h>
185df7f71dSDan Murphy #include <linux/regmap.h>
195df7f71dSDan Murphy #include <linux/slab.h>
205df7f71dSDan Murphy
215df7f71dSDan Murphy #include <linux/gpio/consumer.h>
225df7f71dSDan Murphy #include <linux/regulator/consumer.h>
235df7f71dSDan Murphy
245df7f71dSDan Murphy #include <sound/pcm.h>
255df7f71dSDan Murphy #include <sound/pcm_params.h>
265df7f71dSDan Murphy #include <sound/soc.h>
275df7f71dSDan Murphy #include <sound/soc-dapm.h>
285df7f71dSDan Murphy #include <sound/tlv.h>
295df7f71dSDan Murphy #include <sound/tas2552-plat.h>
309d87a888SPeter Ujfalusi #include <dt-bindings/sound/tas2552.h>
315df7f71dSDan Murphy
325df7f71dSDan Murphy #include "tas2552.h"
335df7f71dSDan Murphy
34c418a84aSAxel Lin static const struct reg_default tas2552_reg_defs[] = {
355df7f71dSDan Murphy {TAS2552_CFG_1, 0x22},
365df7f71dSDan Murphy {TAS2552_CFG_3, 0x80},
375df7f71dSDan Murphy {TAS2552_DOUT, 0x00},
385df7f71dSDan Murphy {TAS2552_OUTPUT_DATA, 0xc0},
395df7f71dSDan Murphy {TAS2552_PDM_CFG, 0x01},
405df7f71dSDan Murphy {TAS2552_PGA_GAIN, 0x00},
412a9dd1dbSPeter Ujfalusi {TAS2552_BOOST_APT_CTRL, 0x0f},
427d785025SPeter Ujfalusi {TAS2552_RESERVED_0D, 0xbe},
435df7f71dSDan Murphy {TAS2552_LIMIT_RATE_HYS, 0x08},
445df7f71dSDan Murphy {TAS2552_CFG_2, 0xef},
455df7f71dSDan Murphy {TAS2552_SER_CTRL_1, 0x00},
465df7f71dSDan Murphy {TAS2552_SER_CTRL_2, 0x00},
475df7f71dSDan Murphy {TAS2552_PLL_CTRL_1, 0x10},
485df7f71dSDan Murphy {TAS2552_PLL_CTRL_2, 0x00},
495df7f71dSDan Murphy {TAS2552_PLL_CTRL_3, 0x00},
505df7f71dSDan Murphy {TAS2552_BTIP, 0x8f},
515df7f71dSDan Murphy {TAS2552_BTS_CTRL, 0x80},
525df7f71dSDan Murphy {TAS2552_LIMIT_RELEASE, 0x04},
535df7f71dSDan Murphy {TAS2552_LIMIT_INT_COUNT, 0x00},
545df7f71dSDan Murphy {TAS2552_EDGE_RATE_CTRL, 0x40},
555df7f71dSDan Murphy {TAS2552_VBAT_DATA, 0x00},
565df7f71dSDan Murphy };
575df7f71dSDan Murphy
585df7f71dSDan Murphy #define TAS2552_NUM_SUPPLIES 3
595df7f71dSDan Murphy static const char *tas2552_supply_names[TAS2552_NUM_SUPPLIES] = {
605df7f71dSDan Murphy "vbat", /* vbat voltage */
615df7f71dSDan Murphy "iovdd", /* I/O Voltage */
625df7f71dSDan Murphy "avdd", /* Analog DAC Voltage */
635df7f71dSDan Murphy };
645df7f71dSDan Murphy
655df7f71dSDan Murphy struct tas2552_data {
6679a4ad1eSKuninori Morimoto struct snd_soc_component *component;
675df7f71dSDan Murphy struct regmap *regmap;
685df7f71dSDan Murphy struct i2c_client *tas2552_client;
695df7f71dSDan Murphy struct regulator_bulk_data supplies[TAS2552_NUM_SUPPLIES];
705df7f71dSDan Murphy struct gpio_desc *enable_gpio;
715df7f71dSDan Murphy unsigned char regs[TAS2552_VBAT_DATA];
7216bd3952SPeter Ujfalusi unsigned int pll_clkin;
731014f7efSPeter Ujfalusi int pll_clk_id;
749d87a888SPeter Ujfalusi unsigned int pdm_clk;
751014f7efSPeter Ujfalusi int pdm_clk_id;
763f747a81SPeter Ujfalusi
773f747a81SPeter Ujfalusi unsigned int dai_fmt;
783f747a81SPeter Ujfalusi unsigned int tdm_delay;
795df7f71dSDan Murphy };
805df7f71dSDan Murphy
tas2552_post_event(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)817d785025SPeter Ujfalusi static int tas2552_post_event(struct snd_soc_dapm_widget *w,
827d785025SPeter Ujfalusi struct snd_kcontrol *kcontrol, int event)
837d785025SPeter Ujfalusi {
8479a4ad1eSKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
857d785025SPeter Ujfalusi
867d785025SPeter Ujfalusi switch (event) {
877d785025SPeter Ujfalusi case SND_SOC_DAPM_POST_PMU:
8879a4ad1eSKuninori Morimoto snd_soc_component_write(component, TAS2552_RESERVED_0D, 0xc0);
8979a4ad1eSKuninori Morimoto snd_soc_component_update_bits(component, TAS2552_LIMIT_RATE_HYS, (1 << 5),
907d785025SPeter Ujfalusi (1 << 5));
9179a4ad1eSKuninori Morimoto snd_soc_component_update_bits(component, TAS2552_CFG_2, 1, 0);
9279a4ad1eSKuninori Morimoto snd_soc_component_update_bits(component, TAS2552_CFG_1, TAS2552_SWS, 0);
937d785025SPeter Ujfalusi break;
947d785025SPeter Ujfalusi case SND_SOC_DAPM_POST_PMD:
9579a4ad1eSKuninori Morimoto snd_soc_component_update_bits(component, TAS2552_CFG_1, TAS2552_SWS,
967d785025SPeter Ujfalusi TAS2552_SWS);
9779a4ad1eSKuninori Morimoto snd_soc_component_update_bits(component, TAS2552_CFG_2, 1, 1);
9879a4ad1eSKuninori Morimoto snd_soc_component_update_bits(component, TAS2552_LIMIT_RATE_HYS, (1 << 5), 0);
9979a4ad1eSKuninori Morimoto snd_soc_component_write(component, TAS2552_RESERVED_0D, 0xbe);
1007d785025SPeter Ujfalusi break;
1017d785025SPeter Ujfalusi }
1027d785025SPeter Ujfalusi return 0;
1037d785025SPeter Ujfalusi }
104a7a8e994SDan Murphy
105609e7131SPeter Ujfalusi /* Input mux controls */
106609e7131SPeter Ujfalusi static const char * const tas2552_input_texts[] = {
107609e7131SPeter Ujfalusi "Digital", "Analog" };
108a7a8e994SDan Murphy static SOC_ENUM_SINGLE_DECL(tas2552_input_mux_enum, TAS2552_CFG_3, 7,
109a7a8e994SDan Murphy tas2552_input_texts);
110a7a8e994SDan Murphy
111609e7131SPeter Ujfalusi static const struct snd_kcontrol_new tas2552_input_mux_control =
112609e7131SPeter Ujfalusi SOC_DAPM_ENUM("Route", tas2552_input_mux_enum);
113a7a8e994SDan Murphy
114a7a8e994SDan Murphy static const struct snd_soc_dapm_widget tas2552_dapm_widgets[] =
115a7a8e994SDan Murphy {
116a7a8e994SDan Murphy SND_SOC_DAPM_INPUT("IN"),
117a7a8e994SDan Murphy
118a7a8e994SDan Murphy /* MUX Controls */
119a7a8e994SDan Murphy SND_SOC_DAPM_MUX("Input selection", SND_SOC_NOPM, 0, 0,
120609e7131SPeter Ujfalusi &tas2552_input_mux_control),
121a7a8e994SDan Murphy
122a7a8e994SDan Murphy SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0, 0),
123*92503b5cSShenghao Ding SND_SOC_DAPM_AIF_OUT("ASI OUT", "DAC Capture", 0, SND_SOC_NOPM, 0, 0),
124a7a8e994SDan Murphy SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
125a7a8e994SDan Murphy SND_SOC_DAPM_OUT_DRV("ClassD", TAS2552_CFG_2, 7, 0, NULL, 0),
126a7a8e994SDan Murphy SND_SOC_DAPM_SUPPLY("PLL", TAS2552_CFG_2, 3, 0, NULL, 0),
1277d785025SPeter Ujfalusi SND_SOC_DAPM_POST("Post Event", tas2552_post_event),
128a7a8e994SDan Murphy
129*92503b5cSShenghao Ding SND_SOC_DAPM_OUTPUT("OUT"),
130*92503b5cSShenghao Ding SND_SOC_DAPM_INPUT("DMIC")
131a7a8e994SDan Murphy };
132a7a8e994SDan Murphy
133a7a8e994SDan Murphy static const struct snd_soc_dapm_route tas2552_audio_map[] = {
134a7a8e994SDan Murphy {"DAC", NULL, "DAC IN"},
135a7a8e994SDan Murphy {"Input selection", "Digital", "DAC"},
136a7a8e994SDan Murphy {"Input selection", "Analog", "IN"},
137a7a8e994SDan Murphy {"ClassD", NULL, "Input selection"},
138a7a8e994SDan Murphy {"OUT", NULL, "ClassD"},
139a7a8e994SDan Murphy {"ClassD", NULL, "PLL"},
140*92503b5cSShenghao Ding {"ASI OUT", NULL, "DMIC"}
141a7a8e994SDan Murphy };
142a7a8e994SDan Murphy
143641d334bSRafael J. Wysocki #ifdef CONFIG_PM
tas2552_sw_shutdown(struct tas2552_data * tas2552,int sw_shutdown)144b94525bfSPeter Ujfalusi static void tas2552_sw_shutdown(struct tas2552_data *tas2552, int sw_shutdown)
1455df7f71dSDan Murphy {
146dd6e3053SPeter Ujfalusi u8 cfg1_reg = 0;
1475df7f71dSDan Murphy
14879a4ad1eSKuninori Morimoto if (!tas2552->component)
14980ba2669SPeter Ujfalusi return;
15080ba2669SPeter Ujfalusi
1515df7f71dSDan Murphy if (sw_shutdown)
1527de544fdSPeter Ujfalusi cfg1_reg = TAS2552_SWS;
1535df7f71dSDan Murphy
15479a4ad1eSKuninori Morimoto snd_soc_component_update_bits(tas2552->component, TAS2552_CFG_1, TAS2552_SWS,
1557de544fdSPeter Ujfalusi cfg1_reg);
1565df7f71dSDan Murphy }
157be1aa3eaSThierry Reding #endif
1585df7f71dSDan Murphy
tas2552_setup_pll(struct snd_soc_component * component,struct snd_pcm_hw_params * params)15979a4ad1eSKuninori Morimoto static int tas2552_setup_pll(struct snd_soc_component *component,
1601014f7efSPeter Ujfalusi struct snd_pcm_hw_params *params)
1611014f7efSPeter Ujfalusi {
16279a4ad1eSKuninori Morimoto struct tas2552_data *tas2552 = dev_get_drvdata(component->dev);
1631014f7efSPeter Ujfalusi bool bypass_pll = false;
1641014f7efSPeter Ujfalusi unsigned int pll_clk = params_rate(params) * 512;
1651014f7efSPeter Ujfalusi unsigned int pll_clkin = tas2552->pll_clkin;
1661014f7efSPeter Ujfalusi u8 pll_enable;
1671014f7efSPeter Ujfalusi
1681014f7efSPeter Ujfalusi if (!pll_clkin) {
1691014f7efSPeter Ujfalusi if (tas2552->pll_clk_id != TAS2552_PLL_CLKIN_BCLK)
1701014f7efSPeter Ujfalusi return -EINVAL;
1711014f7efSPeter Ujfalusi
1721014f7efSPeter Ujfalusi pll_clkin = snd_soc_params_to_bclk(params);
1731014f7efSPeter Ujfalusi pll_clkin += tas2552->tdm_delay;
1741014f7efSPeter Ujfalusi }
1751014f7efSPeter Ujfalusi
176981abdfeSKuninori Morimoto pll_enable = snd_soc_component_read(component, TAS2552_CFG_2) & TAS2552_PLL_ENABLE;
17779a4ad1eSKuninori Morimoto snd_soc_component_update_bits(component, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0);
1781014f7efSPeter Ujfalusi
1791014f7efSPeter Ujfalusi if (pll_clkin == pll_clk)
1801014f7efSPeter Ujfalusi bypass_pll = true;
1811014f7efSPeter Ujfalusi
1821014f7efSPeter Ujfalusi if (bypass_pll) {
1831014f7efSPeter Ujfalusi /* By pass the PLL configuration */
18479a4ad1eSKuninori Morimoto snd_soc_component_update_bits(component, TAS2552_PLL_CTRL_2,
1851014f7efSPeter Ujfalusi TAS2552_PLL_BYPASS, TAS2552_PLL_BYPASS);
1861014f7efSPeter Ujfalusi } else {
1871014f7efSPeter Ujfalusi /* Fill in the PLL control registers for J & D
1881014f7efSPeter Ujfalusi * pll_clk = (.5 * pll_clkin * J.D) / 2^p
1891014f7efSPeter Ujfalusi * Need to fill in J and D here based on incoming freq
1901014f7efSPeter Ujfalusi */
1911bb7cb68SOskar Schirmer unsigned int d, q, t;
1921014f7efSPeter Ujfalusi u8 j;
1931014f7efSPeter Ujfalusi u8 pll_sel = (tas2552->pll_clk_id << 3) & TAS2552_PLL_SRC_MASK;
194981abdfeSKuninori Morimoto u8 p = snd_soc_component_read(component, TAS2552_PLL_CTRL_1);
1951014f7efSPeter Ujfalusi
1961014f7efSPeter Ujfalusi p = (p >> 7);
1971014f7efSPeter Ujfalusi
1981014f7efSPeter Ujfalusi recalc:
1991bb7cb68SOskar Schirmer t = (pll_clk * 2) << p;
2001bb7cb68SOskar Schirmer j = t / pll_clkin;
2011bb7cb68SOskar Schirmer d = t % pll_clkin;
2021bb7cb68SOskar Schirmer t = pll_clkin / 10000;
2031bb7cb68SOskar Schirmer q = d / (t + 1);
2041bb7cb68SOskar Schirmer d = q + ((9999 - pll_clkin % 10000) * (d / t - q)) / 10000;
2051014f7efSPeter Ujfalusi
2061014f7efSPeter Ujfalusi if (d && (pll_clkin < 512000 || pll_clkin > 9200000)) {
2071014f7efSPeter Ujfalusi if (tas2552->pll_clk_id == TAS2552_PLL_CLKIN_BCLK) {
2081014f7efSPeter Ujfalusi pll_clkin = 1800000;
2091014f7efSPeter Ujfalusi pll_sel = (TAS2552_PLL_CLKIN_1_8_FIXED << 3) &
2101014f7efSPeter Ujfalusi TAS2552_PLL_SRC_MASK;
2111014f7efSPeter Ujfalusi } else {
2121014f7efSPeter Ujfalusi pll_clkin = snd_soc_params_to_bclk(params);
2131014f7efSPeter Ujfalusi pll_clkin += tas2552->tdm_delay;
2141014f7efSPeter Ujfalusi pll_sel = (TAS2552_PLL_CLKIN_BCLK << 3) &
2151014f7efSPeter Ujfalusi TAS2552_PLL_SRC_MASK;
2161014f7efSPeter Ujfalusi }
2171014f7efSPeter Ujfalusi goto recalc;
2181014f7efSPeter Ujfalusi }
2191014f7efSPeter Ujfalusi
22079a4ad1eSKuninori Morimoto snd_soc_component_update_bits(component, TAS2552_CFG_1, TAS2552_PLL_SRC_MASK,
2211014f7efSPeter Ujfalusi pll_sel);
2221014f7efSPeter Ujfalusi
22379a4ad1eSKuninori Morimoto snd_soc_component_update_bits(component, TAS2552_PLL_CTRL_1,
2241014f7efSPeter Ujfalusi TAS2552_PLL_J_MASK, j);
2251014f7efSPeter Ujfalusi /* Will clear the PLL_BYPASS bit */
22679a4ad1eSKuninori Morimoto snd_soc_component_write(component, TAS2552_PLL_CTRL_2,
2271014f7efSPeter Ujfalusi TAS2552_PLL_D_UPPER(d));
22879a4ad1eSKuninori Morimoto snd_soc_component_write(component, TAS2552_PLL_CTRL_3,
2291014f7efSPeter Ujfalusi TAS2552_PLL_D_LOWER(d));
2301014f7efSPeter Ujfalusi }
2311014f7efSPeter Ujfalusi
2321014f7efSPeter Ujfalusi /* Restore PLL status */
23379a4ad1eSKuninori Morimoto snd_soc_component_update_bits(component, TAS2552_CFG_2, TAS2552_PLL_ENABLE,
2341014f7efSPeter Ujfalusi pll_enable);
2351014f7efSPeter Ujfalusi
2361014f7efSPeter Ujfalusi return 0;
2371014f7efSPeter Ujfalusi }
2381014f7efSPeter Ujfalusi
tas2552_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)2395df7f71dSDan Murphy static int tas2552_hw_params(struct snd_pcm_substream *substream,
2405df7f71dSDan Murphy struct snd_pcm_hw_params *params,
2415df7f71dSDan Murphy struct snd_soc_dai *dai)
2425df7f71dSDan Murphy {
24379a4ad1eSKuninori Morimoto struct snd_soc_component *component = dai->component;
24479a4ad1eSKuninori Morimoto struct tas2552_data *tas2552 = dev_get_drvdata(component->dev);
245d20b098dSPeter Ujfalusi int cpf;
246a571cb17SPeter Ujfalusi u8 ser_ctrl1_reg, wclk_rate;
247d20b098dSPeter Ujfalusi
248d20b098dSPeter Ujfalusi switch (params_width(params)) {
249d20b098dSPeter Ujfalusi case 16:
250d20b098dSPeter Ujfalusi ser_ctrl1_reg = TAS2552_WORDLENGTH_16BIT;
251d20b098dSPeter Ujfalusi cpf = 32 + tas2552->tdm_delay;
252d20b098dSPeter Ujfalusi break;
253d20b098dSPeter Ujfalusi case 20:
254d20b098dSPeter Ujfalusi ser_ctrl1_reg = TAS2552_WORDLENGTH_20BIT;
255d20b098dSPeter Ujfalusi cpf = 64 + tas2552->tdm_delay;
256d20b098dSPeter Ujfalusi break;
257d20b098dSPeter Ujfalusi case 24:
258d20b098dSPeter Ujfalusi ser_ctrl1_reg = TAS2552_WORDLENGTH_24BIT;
259d20b098dSPeter Ujfalusi cpf = 64 + tas2552->tdm_delay;
260d20b098dSPeter Ujfalusi break;
261d20b098dSPeter Ujfalusi case 32:
262d20b098dSPeter Ujfalusi ser_ctrl1_reg = TAS2552_WORDLENGTH_32BIT;
263d20b098dSPeter Ujfalusi cpf = 64 + tas2552->tdm_delay;
264d20b098dSPeter Ujfalusi break;
265d20b098dSPeter Ujfalusi default:
26679a4ad1eSKuninori Morimoto dev_err(component->dev, "Not supported sample size: %d\n",
267d20b098dSPeter Ujfalusi params_width(params));
268d20b098dSPeter Ujfalusi return -EINVAL;
269d20b098dSPeter Ujfalusi }
270d20b098dSPeter Ujfalusi
271d20b098dSPeter Ujfalusi if (cpf <= 32)
272d20b098dSPeter Ujfalusi ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_32;
273d20b098dSPeter Ujfalusi else if (cpf <= 64)
274d20b098dSPeter Ujfalusi ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_64;
275d20b098dSPeter Ujfalusi else if (cpf <= 128)
276d20b098dSPeter Ujfalusi ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_128;
277d20b098dSPeter Ujfalusi else
278d20b098dSPeter Ujfalusi ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_256;
279d20b098dSPeter Ujfalusi
28079a4ad1eSKuninori Morimoto snd_soc_component_update_bits(component, TAS2552_SER_CTRL_1,
281d20b098dSPeter Ujfalusi TAS2552_WORDLENGTH_MASK | TAS2552_CLKSPERFRAME_MASK,
282d20b098dSPeter Ujfalusi ser_ctrl1_reg);
2835df7f71dSDan Murphy
284a571cb17SPeter Ujfalusi switch (params_rate(params)) {
285a571cb17SPeter Ujfalusi case 8000:
286a571cb17SPeter Ujfalusi wclk_rate = TAS2552_WCLK_FREQ_8KHZ;
287a571cb17SPeter Ujfalusi break;
288a571cb17SPeter Ujfalusi case 11025:
289a571cb17SPeter Ujfalusi case 12000:
290a571cb17SPeter Ujfalusi wclk_rate = TAS2552_WCLK_FREQ_11_12KHZ;
291a571cb17SPeter Ujfalusi break;
292a571cb17SPeter Ujfalusi case 16000:
293a571cb17SPeter Ujfalusi wclk_rate = TAS2552_WCLK_FREQ_16KHZ;
294a571cb17SPeter Ujfalusi break;
295a571cb17SPeter Ujfalusi case 22050:
296a571cb17SPeter Ujfalusi case 24000:
297a571cb17SPeter Ujfalusi wclk_rate = TAS2552_WCLK_FREQ_22_24KHZ;
298a571cb17SPeter Ujfalusi break;
299a571cb17SPeter Ujfalusi case 32000:
300a571cb17SPeter Ujfalusi wclk_rate = TAS2552_WCLK_FREQ_32KHZ;
301a571cb17SPeter Ujfalusi break;
302a571cb17SPeter Ujfalusi case 44100:
303a571cb17SPeter Ujfalusi case 48000:
304a571cb17SPeter Ujfalusi wclk_rate = TAS2552_WCLK_FREQ_44_48KHZ;
305a571cb17SPeter Ujfalusi break;
306a571cb17SPeter Ujfalusi case 88200:
307a571cb17SPeter Ujfalusi case 96000:
308a571cb17SPeter Ujfalusi wclk_rate = TAS2552_WCLK_FREQ_88_96KHZ;
309a571cb17SPeter Ujfalusi break;
310a571cb17SPeter Ujfalusi case 176400:
311a571cb17SPeter Ujfalusi case 192000:
312a571cb17SPeter Ujfalusi wclk_rate = TAS2552_WCLK_FREQ_176_192KHZ;
313a571cb17SPeter Ujfalusi break;
314a571cb17SPeter Ujfalusi default:
31579a4ad1eSKuninori Morimoto dev_err(component->dev, "Not supported sample rate: %d\n",
316a571cb17SPeter Ujfalusi params_rate(params));
317a571cb17SPeter Ujfalusi return -EINVAL;
318a571cb17SPeter Ujfalusi }
319a571cb17SPeter Ujfalusi
32079a4ad1eSKuninori Morimoto snd_soc_component_update_bits(component, TAS2552_CFG_3, TAS2552_WCLK_FREQ_MASK,
321a571cb17SPeter Ujfalusi wclk_rate);
322a571cb17SPeter Ujfalusi
32379a4ad1eSKuninori Morimoto return tas2552_setup_pll(component, params);
3245df7f71dSDan Murphy }
3255df7f71dSDan Murphy
3261b68c7dcSPeter Ujfalusi #define TAS2552_DAI_FMT_MASK (TAS2552_BCLKDIR | \
3271b68c7dcSPeter Ujfalusi TAS2552_WCLKDIR | \
3281b68c7dcSPeter Ujfalusi TAS2552_DATAFORMAT_MASK)
tas2552_prepare(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)3293f747a81SPeter Ujfalusi static int tas2552_prepare(struct snd_pcm_substream *substream,
3303f747a81SPeter Ujfalusi struct snd_soc_dai *dai)
3313f747a81SPeter Ujfalusi {
33279a4ad1eSKuninori Morimoto struct snd_soc_component *component = dai->component;
33379a4ad1eSKuninori Morimoto struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
3343f747a81SPeter Ujfalusi int delay = 0;
3353f747a81SPeter Ujfalusi
3363f747a81SPeter Ujfalusi /* TDM slot selection only valid in DSP_A/_B mode */
3373f747a81SPeter Ujfalusi if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_A)
3383f747a81SPeter Ujfalusi delay += (tas2552->tdm_delay + 1);
3393f747a81SPeter Ujfalusi else if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_B)
3403f747a81SPeter Ujfalusi delay += tas2552->tdm_delay;
3413f747a81SPeter Ujfalusi
3423f747a81SPeter Ujfalusi /* Configure data delay */
34379a4ad1eSKuninori Morimoto snd_soc_component_write(component, TAS2552_SER_CTRL_2, delay);
3443f747a81SPeter Ujfalusi
3453f747a81SPeter Ujfalusi return 0;
3463f747a81SPeter Ujfalusi }
3473f747a81SPeter Ujfalusi
tas2552_set_dai_fmt(struct snd_soc_dai * dai,unsigned int fmt)3485df7f71dSDan Murphy static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
3495df7f71dSDan Murphy {
35079a4ad1eSKuninori Morimoto struct snd_soc_component *component = dai->component;
35179a4ad1eSKuninori Morimoto struct tas2552_data *tas2552 = dev_get_drvdata(component->dev);
3525df7f71dSDan Murphy u8 serial_format;
3535df7f71dSDan Murphy
3546b486af2SMark Brown switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
3556b486af2SMark Brown case SND_SOC_DAIFMT_CBC_CFC:
3565df7f71dSDan Murphy serial_format = 0x00;
3575df7f71dSDan Murphy break;
3586b486af2SMark Brown case SND_SOC_DAIFMT_CBC_CFP:
3591b68c7dcSPeter Ujfalusi serial_format = TAS2552_WCLKDIR;
3605df7f71dSDan Murphy break;
3616b486af2SMark Brown case SND_SOC_DAIFMT_CBP_CFC:
3621b68c7dcSPeter Ujfalusi serial_format = TAS2552_BCLKDIR;
3635df7f71dSDan Murphy break;
3646b486af2SMark Brown case SND_SOC_DAIFMT_CBP_CFP:
3651b68c7dcSPeter Ujfalusi serial_format = (TAS2552_BCLKDIR | TAS2552_WCLKDIR);
3665df7f71dSDan Murphy break;
3675df7f71dSDan Murphy default:
36879a4ad1eSKuninori Morimoto dev_vdbg(component->dev, "DAI Format master is not found\n");
3695df7f71dSDan Murphy return -EINVAL;
3705df7f71dSDan Murphy }
3715df7f71dSDan Murphy
3724c331373SPeter Ujfalusi switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK |
3734c331373SPeter Ujfalusi SND_SOC_DAIFMT_INV_MASK)) {
3744c331373SPeter Ujfalusi case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF):
3755df7f71dSDan Murphy break;
3764c331373SPeter Ujfalusi case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF):
3774c331373SPeter Ujfalusi case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF):
3781b68c7dcSPeter Ujfalusi serial_format |= TAS2552_DATAFORMAT_DSP;
3795df7f71dSDan Murphy break;
3804c331373SPeter Ujfalusi case (SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_NB_NF):
3811b68c7dcSPeter Ujfalusi serial_format |= TAS2552_DATAFORMAT_RIGHT_J;
3825df7f71dSDan Murphy break;
3834c331373SPeter Ujfalusi case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF):
3841b68c7dcSPeter Ujfalusi serial_format |= TAS2552_DATAFORMAT_LEFT_J;
3855df7f71dSDan Murphy break;
3865df7f71dSDan Murphy default:
38779a4ad1eSKuninori Morimoto dev_vdbg(component->dev, "DAI Format is not found\n");
3885df7f71dSDan Murphy return -EINVAL;
3895df7f71dSDan Murphy }
3903f747a81SPeter Ujfalusi tas2552->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
3915df7f71dSDan Murphy
39279a4ad1eSKuninori Morimoto snd_soc_component_update_bits(component, TAS2552_SER_CTRL_1, TAS2552_DAI_FMT_MASK,
3935df7f71dSDan Murphy serial_format);
3945df7f71dSDan Murphy return 0;
3955df7f71dSDan Murphy }
3965df7f71dSDan Murphy
tas2552_set_dai_sysclk(struct snd_soc_dai * dai,int clk_id,unsigned int freq,int dir)3975df7f71dSDan Murphy static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
3985df7f71dSDan Murphy unsigned int freq, int dir)
3995df7f71dSDan Murphy {
40079a4ad1eSKuninori Morimoto struct snd_soc_component *component = dai->component;
40179a4ad1eSKuninori Morimoto struct tas2552_data *tas2552 = dev_get_drvdata(component->dev);
4029d87a888SPeter Ujfalusi u8 reg, mask, val;
4035df7f71dSDan Murphy
4049d87a888SPeter Ujfalusi switch (clk_id) {
4059d87a888SPeter Ujfalusi case TAS2552_PLL_CLKIN_MCLK:
4069d87a888SPeter Ujfalusi case TAS2552_PLL_CLKIN_IVCLKIN:
4071014f7efSPeter Ujfalusi if (freq < 512000 || freq > 24576000) {
4081014f7efSPeter Ujfalusi /* out of range PLL_CLKIN, fall back to use BCLK */
40979a4ad1eSKuninori Morimoto dev_warn(component->dev, "Out of range PLL_CLKIN: %u\n",
4101014f7efSPeter Ujfalusi freq);
4111014f7efSPeter Ujfalusi clk_id = TAS2552_PLL_CLKIN_BCLK;
4121014f7efSPeter Ujfalusi freq = 0;
4131014f7efSPeter Ujfalusi }
4143e146b55SGustavo A. R. Silva fallthrough;
4151014f7efSPeter Ujfalusi case TAS2552_PLL_CLKIN_BCLK:
4169d87a888SPeter Ujfalusi case TAS2552_PLL_CLKIN_1_8_FIXED:
4179d87a888SPeter Ujfalusi mask = TAS2552_PLL_SRC_MASK;
4189d87a888SPeter Ujfalusi val = (clk_id << 3) & mask; /* bit 4:5 in the register */
4199d87a888SPeter Ujfalusi reg = TAS2552_CFG_1;
4201014f7efSPeter Ujfalusi tas2552->pll_clk_id = clk_id;
42116bd3952SPeter Ujfalusi tas2552->pll_clkin = freq;
4229d87a888SPeter Ujfalusi break;
4239d87a888SPeter Ujfalusi case TAS2552_PDM_CLK_PLL:
4249d87a888SPeter Ujfalusi case TAS2552_PDM_CLK_IVCLKIN:
4259d87a888SPeter Ujfalusi case TAS2552_PDM_CLK_BCLK:
4269d87a888SPeter Ujfalusi case TAS2552_PDM_CLK_MCLK:
4279d87a888SPeter Ujfalusi mask = TAS2552_PDM_CLK_SEL_MASK;
4289d87a888SPeter Ujfalusi val = (clk_id >> 1) & mask; /* bit 0:1 in the register */
4299d87a888SPeter Ujfalusi reg = TAS2552_PDM_CFG;
4301014f7efSPeter Ujfalusi tas2552->pdm_clk_id = clk_id;
4319d87a888SPeter Ujfalusi tas2552->pdm_clk = freq;
4329d87a888SPeter Ujfalusi break;
4339d87a888SPeter Ujfalusi default:
43479a4ad1eSKuninori Morimoto dev_err(component->dev, "Invalid clk id: %d\n", clk_id);
4359d87a888SPeter Ujfalusi return -EINVAL;
4369d87a888SPeter Ujfalusi }
4379d87a888SPeter Ujfalusi
43879a4ad1eSKuninori Morimoto snd_soc_component_update_bits(component, reg, mask, val);
4395df7f71dSDan Murphy
4405df7f71dSDan Murphy return 0;
4415df7f71dSDan Murphy }
4425df7f71dSDan Murphy
tas2552_set_dai_tdm_slot(struct snd_soc_dai * dai,unsigned int tx_mask,unsigned int rx_mask,int slots,int slot_width)4433f747a81SPeter Ujfalusi static int tas2552_set_dai_tdm_slot(struct snd_soc_dai *dai,
4443f747a81SPeter Ujfalusi unsigned int tx_mask, unsigned int rx_mask,
4453f747a81SPeter Ujfalusi int slots, int slot_width)
4463f747a81SPeter Ujfalusi {
44779a4ad1eSKuninori Morimoto struct snd_soc_component *component = dai->component;
44879a4ad1eSKuninori Morimoto struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
4493f747a81SPeter Ujfalusi unsigned int lsb;
4503f747a81SPeter Ujfalusi
4513f747a81SPeter Ujfalusi if (unlikely(!tx_mask)) {
45279a4ad1eSKuninori Morimoto dev_err(component->dev, "tx masks need to be non 0\n");
4533f747a81SPeter Ujfalusi return -EINVAL;
4543f747a81SPeter Ujfalusi }
4553f747a81SPeter Ujfalusi
4563f747a81SPeter Ujfalusi /* TDM based on DSP mode requires slots to be adjacent */
4573f747a81SPeter Ujfalusi lsb = __ffs(tx_mask);
4583f747a81SPeter Ujfalusi if ((lsb + 1) != __fls(tx_mask)) {
45979a4ad1eSKuninori Morimoto dev_err(component->dev, "Invalid mask, slots must be adjacent\n");
4603f747a81SPeter Ujfalusi return -EINVAL;
4613f747a81SPeter Ujfalusi }
4623f747a81SPeter Ujfalusi
4633f747a81SPeter Ujfalusi tas2552->tdm_delay = lsb * slot_width;
4643f747a81SPeter Ujfalusi
4653f747a81SPeter Ujfalusi /* DOUT in high-impedance on inactive bit clocks */
46679a4ad1eSKuninori Morimoto snd_soc_component_update_bits(component, TAS2552_DOUT,
4673f747a81SPeter Ujfalusi TAS2552_SDOUT_TRISTATE, TAS2552_SDOUT_TRISTATE);
4683f747a81SPeter Ujfalusi
4693f747a81SPeter Ujfalusi return 0;
4703f747a81SPeter Ujfalusi }
4713f747a81SPeter Ujfalusi
tas2552_mute(struct snd_soc_dai * dai,int mute,int direction)47238803ce7SKuninori Morimoto static int tas2552_mute(struct snd_soc_dai *dai, int mute, int direction)
4735df7f71dSDan Murphy {
474e3606aa4SPeter Ujfalusi u8 cfg1_reg = 0;
47579a4ad1eSKuninori Morimoto struct snd_soc_component *component = dai->component;
4765df7f71dSDan Murphy
4775df7f71dSDan Murphy if (mute)
478e3606aa4SPeter Ujfalusi cfg1_reg |= TAS2552_MUTE;
4795df7f71dSDan Murphy
48079a4ad1eSKuninori Morimoto snd_soc_component_update_bits(component, TAS2552_CFG_1, TAS2552_MUTE, cfg1_reg);
4815df7f71dSDan Murphy
4825df7f71dSDan Murphy return 0;
4835df7f71dSDan Murphy }
4845df7f71dSDan Murphy
485641d334bSRafael J. Wysocki #ifdef CONFIG_PM
tas2552_runtime_suspend(struct device * dev)4865df7f71dSDan Murphy static int tas2552_runtime_suspend(struct device *dev)
4875df7f71dSDan Murphy {
4885df7f71dSDan Murphy struct tas2552_data *tas2552 = dev_get_drvdata(dev);
4895df7f71dSDan Murphy
490dd6e3053SPeter Ujfalusi tas2552_sw_shutdown(tas2552, 1);
4915df7f71dSDan Murphy
4925df7f71dSDan Murphy regcache_cache_only(tas2552->regmap, true);
4935df7f71dSDan Murphy regcache_mark_dirty(tas2552->regmap);
4945df7f71dSDan Murphy
495e295a4a4SDan Murphy gpiod_set_value(tas2552->enable_gpio, 0);
496e295a4a4SDan Murphy
4975df7f71dSDan Murphy return 0;
4985df7f71dSDan Murphy }
4995df7f71dSDan Murphy
tas2552_runtime_resume(struct device * dev)5005df7f71dSDan Murphy static int tas2552_runtime_resume(struct device *dev)
5015df7f71dSDan Murphy {
5025df7f71dSDan Murphy struct tas2552_data *tas2552 = dev_get_drvdata(dev);
5035df7f71dSDan Murphy
5045df7f71dSDan Murphy gpiod_set_value(tas2552->enable_gpio, 1);
5055df7f71dSDan Murphy
506dd6e3053SPeter Ujfalusi tas2552_sw_shutdown(tas2552, 0);
5075df7f71dSDan Murphy
5085df7f71dSDan Murphy regcache_cache_only(tas2552->regmap, false);
5095df7f71dSDan Murphy regcache_sync(tas2552->regmap);
5105df7f71dSDan Murphy
5115df7f71dSDan Murphy return 0;
5125df7f71dSDan Murphy }
5135df7f71dSDan Murphy #endif
5145df7f71dSDan Murphy
5155df7f71dSDan Murphy static const struct dev_pm_ops tas2552_pm = {
5165df7f71dSDan Murphy SET_RUNTIME_PM_OPS(tas2552_runtime_suspend, tas2552_runtime_resume,
5175df7f71dSDan Murphy NULL)
5185df7f71dSDan Murphy };
5195df7f71dSDan Murphy
52064793047SAxel Lin static const struct snd_soc_dai_ops tas2552_speaker_dai_ops = {
5215df7f71dSDan Murphy .hw_params = tas2552_hw_params,
5223f747a81SPeter Ujfalusi .prepare = tas2552_prepare,
5235df7f71dSDan Murphy .set_sysclk = tas2552_set_dai_sysclk,
5245df7f71dSDan Murphy .set_fmt = tas2552_set_dai_fmt,
5253f747a81SPeter Ujfalusi .set_tdm_slot = tas2552_set_dai_tdm_slot,
52638803ce7SKuninori Morimoto .mute_stream = tas2552_mute,
52738803ce7SKuninori Morimoto .no_capture_mute = 1,
5285df7f71dSDan Murphy };
5295df7f71dSDan Murphy
5305df7f71dSDan Murphy /* Formats supported by TAS2552 driver. */
5315df7f71dSDan Murphy #define TAS2552_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
5325df7f71dSDan Murphy SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
5335df7f71dSDan Murphy
5345df7f71dSDan Murphy /* TAS2552 dai structure. */
5355df7f71dSDan Murphy static struct snd_soc_dai_driver tas2552_dai[] = {
5365df7f71dSDan Murphy {
5375df7f71dSDan Murphy .name = "tas2552-amplifier",
5385df7f71dSDan Murphy .playback = {
539a7a8e994SDan Murphy .stream_name = "Playback",
5405df7f71dSDan Murphy .channels_min = 2,
5415df7f71dSDan Murphy .channels_max = 2,
5425df7f71dSDan Murphy .rates = SNDRV_PCM_RATE_8000_192000,
5435df7f71dSDan Murphy .formats = TAS2552_FORMATS,
5445df7f71dSDan Murphy },
545*92503b5cSShenghao Ding .capture = {
546*92503b5cSShenghao Ding .stream_name = "Capture",
547*92503b5cSShenghao Ding .channels_min = 2,
548*92503b5cSShenghao Ding .channels_max = 2,
549*92503b5cSShenghao Ding .rates = SNDRV_PCM_RATE_8000_192000,
550*92503b5cSShenghao Ding .formats = TAS2552_FORMATS,
551*92503b5cSShenghao Ding },
5525df7f71dSDan Murphy .ops = &tas2552_speaker_dai_ops,
5535df7f71dSDan Murphy },
5545df7f71dSDan Murphy };
5555df7f71dSDan Murphy
5565df7f71dSDan Murphy /*
5575df7f71dSDan Murphy * DAC digital volumes. From -7 to 24 dB in 1 dB steps
5585df7f71dSDan Murphy */
559e2600460SAndreas Dannenberg static DECLARE_TLV_DB_SCALE(dac_tlv, -700, 100, 0);
5605df7f71dSDan Murphy
5612962cb52SPeter Ujfalusi static const char * const tas2552_din_source_select[] = {
5622962cb52SPeter Ujfalusi "Muted",
5632962cb52SPeter Ujfalusi "Left",
5642962cb52SPeter Ujfalusi "Right",
5652962cb52SPeter Ujfalusi "Left + Right average",
5662962cb52SPeter Ujfalusi };
5672962cb52SPeter Ujfalusi static SOC_ENUM_SINGLE_DECL(tas2552_din_source_enum,
5682962cb52SPeter Ujfalusi TAS2552_CFG_3, 3,
5692962cb52SPeter Ujfalusi tas2552_din_source_select);
5702962cb52SPeter Ujfalusi
5715df7f71dSDan Murphy static const struct snd_kcontrol_new tas2552_snd_controls[] = {
5725df7f71dSDan Murphy SOC_SINGLE_TLV("Speaker Driver Playback Volume",
573dd6ae3bcSPeter Ujfalusi TAS2552_PGA_GAIN, 0, 0x1f, 0, dac_tlv),
5742962cb52SPeter Ujfalusi SOC_ENUM("DIN source", tas2552_din_source_enum),
5755df7f71dSDan Murphy };
5765df7f71dSDan Murphy
tas2552_component_probe(struct snd_soc_component * component)57779a4ad1eSKuninori Morimoto static int tas2552_component_probe(struct snd_soc_component *component)
5785df7f71dSDan Murphy {
57979a4ad1eSKuninori Morimoto struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
5805df7f71dSDan Murphy int ret;
5815df7f71dSDan Murphy
58279a4ad1eSKuninori Morimoto tas2552->component = component;
5835df7f71dSDan Murphy
5845df7f71dSDan Murphy ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies),
5855df7f71dSDan Murphy tas2552->supplies);
5865df7f71dSDan Murphy
5875df7f71dSDan Murphy if (ret != 0) {
58879a4ad1eSKuninori Morimoto dev_err(component->dev, "Failed to enable supplies: %d\n",
5895df7f71dSDan Murphy ret);
5905df7f71dSDan Murphy return ret;
5915df7f71dSDan Murphy }
5925df7f71dSDan Murphy
5935df7f71dSDan Murphy gpiod_set_value(tas2552->enable_gpio, 1);
5945df7f71dSDan Murphy
59505b71fb2SPierre-Louis Bossart ret = pm_runtime_resume_and_get(component->dev);
5965df7f71dSDan Murphy if (ret < 0) {
59779a4ad1eSKuninori Morimoto dev_err(component->dev, "Enabling device failed: %d\n",
5985df7f71dSDan Murphy ret);
5995df7f71dSDan Murphy goto probe_fail;
6005df7f71dSDan Murphy }
6015df7f71dSDan Murphy
60279a4ad1eSKuninori Morimoto snd_soc_component_update_bits(component, TAS2552_CFG_1, TAS2552_MUTE, TAS2552_MUTE);
60379a4ad1eSKuninori Morimoto snd_soc_component_write(component, TAS2552_CFG_3, TAS2552_I2S_OUT_SEL |
604a571cb17SPeter Ujfalusi TAS2552_DIN_SRC_SEL_AVG_L_R);
60579a4ad1eSKuninori Morimoto snd_soc_component_write(component, TAS2552_OUTPUT_DATA,
606b2822f19SPeter Ujfalusi TAS2552_PDM_DATA_SEL_V_I |
607b2822f19SPeter Ujfalusi TAS2552_R_DATA_OUT(TAS2552_DATA_OUT_V_DATA));
60879a4ad1eSKuninori Morimoto snd_soc_component_write(component, TAS2552_BOOST_APT_CTRL, TAS2552_APT_DELAY_200 |
6092a9dd1dbSPeter Ujfalusi TAS2552_APT_THRESH_20_17);
6105df7f71dSDan Murphy
61179a4ad1eSKuninori Morimoto snd_soc_component_write(component, TAS2552_CFG_2, TAS2552_BOOST_EN | TAS2552_APT_EN |
6124afdd89dSPeter Ujfalusi TAS2552_LIM_EN);
613a7a8e994SDan Murphy
6145df7f71dSDan Murphy return 0;
6155df7f71dSDan Murphy
6165df7f71dSDan Murphy probe_fail:
6170d71a5cfSDinghao Liu pm_runtime_put_noidle(component->dev);
6185df7f71dSDan Murphy gpiod_set_value(tas2552->enable_gpio, 0);
6195df7f71dSDan Murphy
6205df7f71dSDan Murphy regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies),
6215df7f71dSDan Murphy tas2552->supplies);
6226f2daf82SFabio Estevam return ret;
6235df7f71dSDan Murphy }
6245df7f71dSDan Murphy
tas2552_component_remove(struct snd_soc_component * component)62579a4ad1eSKuninori Morimoto static void tas2552_component_remove(struct snd_soc_component *component)
6265df7f71dSDan Murphy {
62779a4ad1eSKuninori Morimoto struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
6285df7f71dSDan Murphy
62979a4ad1eSKuninori Morimoto pm_runtime_put(component->dev);
630e295a4a4SDan Murphy
6315df7f71dSDan Murphy gpiod_set_value(tas2552->enable_gpio, 0);
6325df7f71dSDan Murphy };
6335df7f71dSDan Murphy
6345df7f71dSDan Murphy #ifdef CONFIG_PM
tas2552_suspend(struct snd_soc_component * component)63579a4ad1eSKuninori Morimoto static int tas2552_suspend(struct snd_soc_component *component)
6365df7f71dSDan Murphy {
63779a4ad1eSKuninori Morimoto struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
6385df7f71dSDan Murphy int ret;
6395df7f71dSDan Murphy
6405df7f71dSDan Murphy ret = regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies),
6415df7f71dSDan Murphy tas2552->supplies);
6425df7f71dSDan Murphy
6435df7f71dSDan Murphy if (ret != 0)
64479a4ad1eSKuninori Morimoto dev_err(component->dev, "Failed to disable supplies: %d\n",
6455df7f71dSDan Murphy ret);
64612dc0f3bSFabio Estevam return ret;
6475df7f71dSDan Murphy }
6485df7f71dSDan Murphy
tas2552_resume(struct snd_soc_component * component)64979a4ad1eSKuninori Morimoto static int tas2552_resume(struct snd_soc_component *component)
6505df7f71dSDan Murphy {
65179a4ad1eSKuninori Morimoto struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
6525df7f71dSDan Murphy int ret;
6535df7f71dSDan Murphy
6545df7f71dSDan Murphy ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies),
6555df7f71dSDan Murphy tas2552->supplies);
6565df7f71dSDan Murphy
6575df7f71dSDan Murphy if (ret != 0) {
65879a4ad1eSKuninori Morimoto dev_err(component->dev, "Failed to enable supplies: %d\n",
6595df7f71dSDan Murphy ret);
6605df7f71dSDan Murphy }
6615df7f71dSDan Murphy
66212dc0f3bSFabio Estevam return ret;
6635df7f71dSDan Murphy }
6645df7f71dSDan Murphy #else
6655df7f71dSDan Murphy #define tas2552_suspend NULL
6665df7f71dSDan Murphy #define tas2552_resume NULL
6675df7f71dSDan Murphy #endif
6685df7f71dSDan Murphy
66979a4ad1eSKuninori Morimoto static const struct snd_soc_component_driver soc_component_dev_tas2552 = {
67079a4ad1eSKuninori Morimoto .probe = tas2552_component_probe,
67179a4ad1eSKuninori Morimoto .remove = tas2552_component_remove,
6725df7f71dSDan Murphy .suspend = tas2552_suspend,
6735df7f71dSDan Murphy .resume = tas2552_resume,
6745df7f71dSDan Murphy .controls = tas2552_snd_controls,
6755df7f71dSDan Murphy .num_controls = ARRAY_SIZE(tas2552_snd_controls),
676e3f1ff31SLars-Peter Clausen .dapm_widgets = tas2552_dapm_widgets,
677e3f1ff31SLars-Peter Clausen .num_dapm_widgets = ARRAY_SIZE(tas2552_dapm_widgets),
678e3f1ff31SLars-Peter Clausen .dapm_routes = tas2552_audio_map,
679e3f1ff31SLars-Peter Clausen .num_dapm_routes = ARRAY_SIZE(tas2552_audio_map),
68079a4ad1eSKuninori Morimoto .idle_bias_on = 1,
68179a4ad1eSKuninori Morimoto .endianness = 1,
6825df7f71dSDan Murphy };
6835df7f71dSDan Murphy
6845df7f71dSDan Murphy static const struct regmap_config tas2552_regmap_config = {
6855df7f71dSDan Murphy .reg_bits = 8,
6865df7f71dSDan Murphy .val_bits = 8,
6875df7f71dSDan Murphy
6885df7f71dSDan Murphy .max_register = TAS2552_MAX_REG,
6895df7f71dSDan Murphy .reg_defaults = tas2552_reg_defs,
6905df7f71dSDan Murphy .num_reg_defaults = ARRAY_SIZE(tas2552_reg_defs),
6915df7f71dSDan Murphy .cache_type = REGCACHE_RBTREE,
6925df7f71dSDan Murphy };
6935df7f71dSDan Murphy
tas2552_probe(struct i2c_client * client)694ad11678fSStephen Kitt static int tas2552_probe(struct i2c_client *client)
6955df7f71dSDan Murphy {
6965df7f71dSDan Murphy struct device *dev;
6975df7f71dSDan Murphy struct tas2552_data *data;
6985df7f71dSDan Murphy int ret;
6995df7f71dSDan Murphy int i;
7005df7f71dSDan Murphy
7015df7f71dSDan Murphy dev = &client->dev;
7025df7f71dSDan Murphy data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
7035df7f71dSDan Murphy if (data == NULL)
7045df7f71dSDan Murphy return -ENOMEM;
7055df7f71dSDan Murphy
7068604bc28SAxel Lin data->enable_gpio = devm_gpiod_get_optional(dev, "enable",
7078604bc28SAxel Lin GPIOD_OUT_LOW);
7088604bc28SAxel Lin if (IS_ERR(data->enable_gpio))
7098604bc28SAxel Lin return PTR_ERR(data->enable_gpio);
7105df7f71dSDan Murphy
7115df7f71dSDan Murphy data->tas2552_client = client;
7125df7f71dSDan Murphy data->regmap = devm_regmap_init_i2c(client, &tas2552_regmap_config);
7135df7f71dSDan Murphy if (IS_ERR(data->regmap)) {
7145df7f71dSDan Murphy ret = PTR_ERR(data->regmap);
7155df7f71dSDan Murphy dev_err(&client->dev, "Failed to allocate register map: %d\n",
7165df7f71dSDan Murphy ret);
7175df7f71dSDan Murphy return ret;
7185df7f71dSDan Murphy }
7195df7f71dSDan Murphy
7205df7f71dSDan Murphy for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
7215df7f71dSDan Murphy data->supplies[i].supply = tas2552_supply_names[i];
7225df7f71dSDan Murphy
7235df7f71dSDan Murphy ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies),
7245df7f71dSDan Murphy data->supplies);
725c62f9d8fSAxel Lin if (ret != 0) {
7265df7f71dSDan Murphy dev_err(dev, "Failed to request supplies: %d\n", ret);
727c62f9d8fSAxel Lin return ret;
728c62f9d8fSAxel Lin }
7295df7f71dSDan Murphy
7305df7f71dSDan Murphy pm_runtime_set_active(&client->dev);
7315df7f71dSDan Murphy pm_runtime_set_autosuspend_delay(&client->dev, 1000);
7325df7f71dSDan Murphy pm_runtime_use_autosuspend(&client->dev);
7335df7f71dSDan Murphy pm_runtime_enable(&client->dev);
7345df7f71dSDan Murphy pm_runtime_mark_last_busy(&client->dev);
7355df7f71dSDan Murphy pm_runtime_put_sync_autosuspend(&client->dev);
7365df7f71dSDan Murphy
7375df7f71dSDan Murphy dev_set_drvdata(&client->dev, data);
7385df7f71dSDan Murphy
73979a4ad1eSKuninori Morimoto ret = devm_snd_soc_register_component(&client->dev,
74079a4ad1eSKuninori Morimoto &soc_component_dev_tas2552,
7415df7f71dSDan Murphy tas2552_dai, ARRAY_SIZE(tas2552_dai));
7427b3f5b20SDinghao Liu if (ret < 0) {
74379a4ad1eSKuninori Morimoto dev_err(&client->dev, "Failed to register component: %d\n", ret);
7447b3f5b20SDinghao Liu pm_runtime_get_noresume(&client->dev);
7457b3f5b20SDinghao Liu }
7465df7f71dSDan Murphy
747c62f9d8fSAxel Lin return ret;
7485df7f71dSDan Murphy }
7495df7f71dSDan Murphy
tas2552_i2c_remove(struct i2c_client * client)750ed5c2f5fSUwe Kleine-König static void tas2552_i2c_remove(struct i2c_client *client)
7515df7f71dSDan Murphy {
7524785ed89SPeter Ujfalusi pm_runtime_disable(&client->dev);
7535df7f71dSDan Murphy }
7545df7f71dSDan Murphy
7555df7f71dSDan Murphy static const struct i2c_device_id tas2552_id[] = {
7565df7f71dSDan Murphy { "tas2552", 0 },
7575df7f71dSDan Murphy { }
7585df7f71dSDan Murphy };
7595df7f71dSDan Murphy MODULE_DEVICE_TABLE(i2c, tas2552_id);
7605df7f71dSDan Murphy
7615df7f71dSDan Murphy #if IS_ENABLED(CONFIG_OF)
7625df7f71dSDan Murphy static const struct of_device_id tas2552_of_match[] = {
7635df7f71dSDan Murphy { .compatible = "ti,tas2552", },
7645df7f71dSDan Murphy {},
7655df7f71dSDan Murphy };
7665df7f71dSDan Murphy MODULE_DEVICE_TABLE(of, tas2552_of_match);
7675df7f71dSDan Murphy #endif
7685df7f71dSDan Murphy
7695df7f71dSDan Murphy static struct i2c_driver tas2552_i2c_driver = {
7705df7f71dSDan Murphy .driver = {
7715df7f71dSDan Murphy .name = "tas2552",
7725df7f71dSDan Murphy .of_match_table = of_match_ptr(tas2552_of_match),
7735df7f71dSDan Murphy .pm = &tas2552_pm,
7745df7f71dSDan Murphy },
7759abcd240SUwe Kleine-König .probe = tas2552_probe,
7765df7f71dSDan Murphy .remove = tas2552_i2c_remove,
7775df7f71dSDan Murphy .id_table = tas2552_id,
7785df7f71dSDan Murphy };
7795df7f71dSDan Murphy
7805df7f71dSDan Murphy module_i2c_driver(tas2552_i2c_driver);
7815df7f71dSDan Murphy
7825df7f71dSDan Murphy MODULE_AUTHOR("Dan Muprhy <dmurphy@ti.com>");
7835df7f71dSDan Murphy MODULE_DESCRIPTION("TAS2552 Audio amplifier driver");
7845df7f71dSDan Murphy MODULE_LICENSE("GPL");
785