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