15df7f71dSDan Murphy /* 25df7f71dSDan Murphy * tas2552.c - ALSA SoC Texas Instruments TAS2552 Mono Audio Amplifier 35df7f71dSDan Murphy * 45df7f71dSDan Murphy * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com 55df7f71dSDan Murphy * 65df7f71dSDan Murphy * Author: Dan Murphy <dmurphy@ti.com> 75df7f71dSDan Murphy * 85df7f71dSDan Murphy * This program is free software; you can redistribute it and/or 95df7f71dSDan Murphy * modify it under the terms of the GNU General Public License 105df7f71dSDan Murphy * version 2 as published by the Free Software Foundation. 115df7f71dSDan Murphy * 125df7f71dSDan Murphy * This program is distributed in the hope that it will be useful, but 135df7f71dSDan Murphy * WITHOUT ANY WARRANTY; without even the implied warranty of 145df7f71dSDan Murphy * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 155df7f71dSDan Murphy * General Public License for more details. 165df7f71dSDan Murphy */ 175df7f71dSDan Murphy 185df7f71dSDan Murphy #include <linux/module.h> 195df7f71dSDan Murphy #include <linux/errno.h> 205df7f71dSDan Murphy #include <linux/device.h> 215df7f71dSDan Murphy #include <linux/i2c.h> 225df7f71dSDan Murphy #include <linux/gpio.h> 235df7f71dSDan Murphy #include <linux/of_gpio.h> 245df7f71dSDan Murphy #include <linux/pm_runtime.h> 255df7f71dSDan Murphy #include <linux/regmap.h> 265df7f71dSDan Murphy #include <linux/slab.h> 275df7f71dSDan Murphy 285df7f71dSDan Murphy #include <linux/gpio/consumer.h> 295df7f71dSDan Murphy #include <linux/regulator/consumer.h> 305df7f71dSDan Murphy 315df7f71dSDan Murphy #include <sound/pcm.h> 325df7f71dSDan Murphy #include <sound/pcm_params.h> 335df7f71dSDan Murphy #include <sound/soc.h> 345df7f71dSDan Murphy #include <sound/soc-dapm.h> 355df7f71dSDan Murphy #include <sound/tlv.h> 365df7f71dSDan Murphy #include <sound/tas2552-plat.h> 379d87a888SPeter Ujfalusi #include <dt-bindings/sound/tas2552.h> 385df7f71dSDan Murphy 395df7f71dSDan Murphy #include "tas2552.h" 405df7f71dSDan Murphy 415df7f71dSDan Murphy static struct reg_default tas2552_reg_defs[] = { 425df7f71dSDan Murphy {TAS2552_CFG_1, 0x22}, 435df7f71dSDan Murphy {TAS2552_CFG_3, 0x80}, 445df7f71dSDan Murphy {TAS2552_DOUT, 0x00}, 455df7f71dSDan Murphy {TAS2552_OUTPUT_DATA, 0xc0}, 465df7f71dSDan Murphy {TAS2552_PDM_CFG, 0x01}, 475df7f71dSDan Murphy {TAS2552_PGA_GAIN, 0x00}, 482a9dd1dbSPeter Ujfalusi {TAS2552_BOOST_APT_CTRL, 0x0f}, 497d785025SPeter Ujfalusi {TAS2552_RESERVED_0D, 0xbe}, 505df7f71dSDan Murphy {TAS2552_LIMIT_RATE_HYS, 0x08}, 515df7f71dSDan Murphy {TAS2552_CFG_2, 0xef}, 525df7f71dSDan Murphy {TAS2552_SER_CTRL_1, 0x00}, 535df7f71dSDan Murphy {TAS2552_SER_CTRL_2, 0x00}, 545df7f71dSDan Murphy {TAS2552_PLL_CTRL_1, 0x10}, 555df7f71dSDan Murphy {TAS2552_PLL_CTRL_2, 0x00}, 565df7f71dSDan Murphy {TAS2552_PLL_CTRL_3, 0x00}, 575df7f71dSDan Murphy {TAS2552_BTIP, 0x8f}, 585df7f71dSDan Murphy {TAS2552_BTS_CTRL, 0x80}, 595df7f71dSDan Murphy {TAS2552_LIMIT_RELEASE, 0x04}, 605df7f71dSDan Murphy {TAS2552_LIMIT_INT_COUNT, 0x00}, 615df7f71dSDan Murphy {TAS2552_EDGE_RATE_CTRL, 0x40}, 625df7f71dSDan Murphy {TAS2552_VBAT_DATA, 0x00}, 635df7f71dSDan Murphy }; 645df7f71dSDan Murphy 655df7f71dSDan Murphy #define TAS2552_NUM_SUPPLIES 3 665df7f71dSDan Murphy static const char *tas2552_supply_names[TAS2552_NUM_SUPPLIES] = { 675df7f71dSDan Murphy "vbat", /* vbat voltage */ 685df7f71dSDan Murphy "iovdd", /* I/O Voltage */ 695df7f71dSDan Murphy "avdd", /* Analog DAC Voltage */ 705df7f71dSDan Murphy }; 715df7f71dSDan Murphy 725df7f71dSDan Murphy struct tas2552_data { 735df7f71dSDan Murphy struct snd_soc_codec *codec; 745df7f71dSDan Murphy struct regmap *regmap; 755df7f71dSDan Murphy struct i2c_client *tas2552_client; 765df7f71dSDan Murphy struct regulator_bulk_data supplies[TAS2552_NUM_SUPPLIES]; 775df7f71dSDan Murphy struct gpio_desc *enable_gpio; 785df7f71dSDan Murphy unsigned char regs[TAS2552_VBAT_DATA]; 7916bd3952SPeter Ujfalusi unsigned int pll_clkin; 801014f7efSPeter Ujfalusi int pll_clk_id; 819d87a888SPeter Ujfalusi unsigned int pdm_clk; 821014f7efSPeter Ujfalusi int pdm_clk_id; 833f747a81SPeter Ujfalusi 843f747a81SPeter Ujfalusi unsigned int dai_fmt; 853f747a81SPeter Ujfalusi unsigned int tdm_delay; 865df7f71dSDan Murphy }; 875df7f71dSDan Murphy 887d785025SPeter Ujfalusi static int tas2552_post_event(struct snd_soc_dapm_widget *w, 897d785025SPeter Ujfalusi struct snd_kcontrol *kcontrol, int event) 907d785025SPeter Ujfalusi { 917d785025SPeter Ujfalusi struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 927d785025SPeter Ujfalusi 937d785025SPeter Ujfalusi switch (event) { 947d785025SPeter Ujfalusi case SND_SOC_DAPM_POST_PMU: 957d785025SPeter Ujfalusi snd_soc_write(codec, TAS2552_RESERVED_0D, 0xc0); 967d785025SPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_LIMIT_RATE_HYS, (1 << 5), 977d785025SPeter Ujfalusi (1 << 5)); 987d785025SPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_CFG_2, 1, 0); 997d785025SPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_SWS, 0); 1007d785025SPeter Ujfalusi break; 1017d785025SPeter Ujfalusi case SND_SOC_DAPM_POST_PMD: 1027d785025SPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_SWS, 1037d785025SPeter Ujfalusi TAS2552_SWS); 1047d785025SPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_CFG_2, 1, 1); 1057d785025SPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_LIMIT_RATE_HYS, (1 << 5), 0); 1067d785025SPeter Ujfalusi snd_soc_write(codec, TAS2552_RESERVED_0D, 0xbe); 1077d785025SPeter Ujfalusi break; 1087d785025SPeter Ujfalusi } 1097d785025SPeter Ujfalusi return 0; 1107d785025SPeter Ujfalusi } 111a7a8e994SDan Murphy 112609e7131SPeter Ujfalusi /* Input mux controls */ 113609e7131SPeter Ujfalusi static const char * const tas2552_input_texts[] = { 114609e7131SPeter Ujfalusi "Digital", "Analog" }; 115a7a8e994SDan Murphy static SOC_ENUM_SINGLE_DECL(tas2552_input_mux_enum, TAS2552_CFG_3, 7, 116a7a8e994SDan Murphy tas2552_input_texts); 117a7a8e994SDan Murphy 118609e7131SPeter Ujfalusi static const struct snd_kcontrol_new tas2552_input_mux_control = 119609e7131SPeter Ujfalusi SOC_DAPM_ENUM("Route", tas2552_input_mux_enum); 120a7a8e994SDan Murphy 121a7a8e994SDan Murphy static const struct snd_soc_dapm_widget tas2552_dapm_widgets[] = 122a7a8e994SDan Murphy { 123a7a8e994SDan Murphy SND_SOC_DAPM_INPUT("IN"), 124a7a8e994SDan Murphy 125a7a8e994SDan Murphy /* MUX Controls */ 126a7a8e994SDan Murphy SND_SOC_DAPM_MUX("Input selection", SND_SOC_NOPM, 0, 0, 127609e7131SPeter Ujfalusi &tas2552_input_mux_control), 128a7a8e994SDan Murphy 129a7a8e994SDan Murphy SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0, 0), 130a7a8e994SDan Murphy SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), 131a7a8e994SDan Murphy SND_SOC_DAPM_OUT_DRV("ClassD", TAS2552_CFG_2, 7, 0, NULL, 0), 132a7a8e994SDan Murphy SND_SOC_DAPM_SUPPLY("PLL", TAS2552_CFG_2, 3, 0, NULL, 0), 1337d785025SPeter Ujfalusi SND_SOC_DAPM_POST("Post Event", tas2552_post_event), 134a7a8e994SDan Murphy 135a7a8e994SDan Murphy SND_SOC_DAPM_OUTPUT("OUT") 136a7a8e994SDan Murphy }; 137a7a8e994SDan Murphy 138a7a8e994SDan Murphy static const struct snd_soc_dapm_route tas2552_audio_map[] = { 139a7a8e994SDan Murphy {"DAC", NULL, "DAC IN"}, 140a7a8e994SDan Murphy {"Input selection", "Digital", "DAC"}, 141a7a8e994SDan Murphy {"Input selection", "Analog", "IN"}, 142a7a8e994SDan Murphy {"ClassD", NULL, "Input selection"}, 143a7a8e994SDan Murphy {"OUT", NULL, "ClassD"}, 144a7a8e994SDan Murphy {"ClassD", NULL, "PLL"}, 145a7a8e994SDan Murphy }; 146a7a8e994SDan Murphy 147641d334bSRafael J. Wysocki #ifdef CONFIG_PM 1485df7f71dSDan Murphy static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown) 1495df7f71dSDan Murphy { 150dd6e3053SPeter Ujfalusi u8 cfg1_reg = 0; 1515df7f71dSDan Murphy 15280ba2669SPeter Ujfalusi if (!tas_data->codec) 15380ba2669SPeter Ujfalusi return; 15480ba2669SPeter Ujfalusi 1555df7f71dSDan Murphy if (sw_shutdown) 1567de544fdSPeter Ujfalusi cfg1_reg = TAS2552_SWS; 1575df7f71dSDan Murphy 1587de544fdSPeter Ujfalusi snd_soc_update_bits(tas_data->codec, TAS2552_CFG_1, TAS2552_SWS, 1597de544fdSPeter Ujfalusi cfg1_reg); 1605df7f71dSDan Murphy } 161be1aa3eaSThierry Reding #endif 1625df7f71dSDan Murphy 1631014f7efSPeter Ujfalusi static int tas2552_setup_pll(struct snd_soc_codec *codec, 1641014f7efSPeter Ujfalusi struct snd_pcm_hw_params *params) 1651014f7efSPeter Ujfalusi { 1661014f7efSPeter Ujfalusi struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); 1671014f7efSPeter Ujfalusi bool bypass_pll = false; 1681014f7efSPeter Ujfalusi unsigned int pll_clk = params_rate(params) * 512; 1691014f7efSPeter Ujfalusi unsigned int pll_clkin = tas2552->pll_clkin; 1701014f7efSPeter Ujfalusi u8 pll_enable; 1711014f7efSPeter Ujfalusi 1721014f7efSPeter Ujfalusi if (!pll_clkin) { 1731014f7efSPeter Ujfalusi if (tas2552->pll_clk_id != TAS2552_PLL_CLKIN_BCLK) 1741014f7efSPeter Ujfalusi return -EINVAL; 1751014f7efSPeter Ujfalusi 1761014f7efSPeter Ujfalusi pll_clkin = snd_soc_params_to_bclk(params); 1771014f7efSPeter Ujfalusi pll_clkin += tas2552->tdm_delay; 1781014f7efSPeter Ujfalusi } 1791014f7efSPeter Ujfalusi 1801014f7efSPeter Ujfalusi pll_enable = snd_soc_read(codec, TAS2552_CFG_2) & TAS2552_PLL_ENABLE; 1811014f7efSPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0); 1821014f7efSPeter Ujfalusi 1831014f7efSPeter Ujfalusi if (pll_clkin == pll_clk) 1841014f7efSPeter Ujfalusi bypass_pll = true; 1851014f7efSPeter Ujfalusi 1861014f7efSPeter Ujfalusi if (bypass_pll) { 1871014f7efSPeter Ujfalusi /* By pass the PLL configuration */ 1881014f7efSPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2, 1891014f7efSPeter Ujfalusi TAS2552_PLL_BYPASS, TAS2552_PLL_BYPASS); 1901014f7efSPeter Ujfalusi } else { 1911014f7efSPeter Ujfalusi /* Fill in the PLL control registers for J & D 1921014f7efSPeter Ujfalusi * pll_clk = (.5 * pll_clkin * J.D) / 2^p 1931014f7efSPeter Ujfalusi * Need to fill in J and D here based on incoming freq 1941014f7efSPeter Ujfalusi */ 1951014f7efSPeter Ujfalusi unsigned int d; 1961014f7efSPeter Ujfalusi u8 j; 1971014f7efSPeter Ujfalusi u8 pll_sel = (tas2552->pll_clk_id << 3) & TAS2552_PLL_SRC_MASK; 1981014f7efSPeter Ujfalusi u8 p = snd_soc_read(codec, TAS2552_PLL_CTRL_1); 1991014f7efSPeter Ujfalusi 2001014f7efSPeter Ujfalusi p = (p >> 7); 2011014f7efSPeter Ujfalusi 2021014f7efSPeter Ujfalusi recalc: 2031014f7efSPeter Ujfalusi j = (pll_clk * 2 * (1 << p)) / pll_clkin; 2041014f7efSPeter Ujfalusi d = (pll_clk * 2 * (1 << p)) % pll_clkin; 2051014f7efSPeter Ujfalusi d /= (pll_clkin / 10000); 2061014f7efSPeter Ujfalusi 2071014f7efSPeter Ujfalusi if (d && (pll_clkin < 512000 || pll_clkin > 9200000)) { 2081014f7efSPeter Ujfalusi if (tas2552->pll_clk_id == TAS2552_PLL_CLKIN_BCLK) { 2091014f7efSPeter Ujfalusi pll_clkin = 1800000; 2101014f7efSPeter Ujfalusi pll_sel = (TAS2552_PLL_CLKIN_1_8_FIXED << 3) & 2111014f7efSPeter Ujfalusi TAS2552_PLL_SRC_MASK; 2121014f7efSPeter Ujfalusi } else { 2131014f7efSPeter Ujfalusi pll_clkin = snd_soc_params_to_bclk(params); 2141014f7efSPeter Ujfalusi pll_clkin += tas2552->tdm_delay; 2151014f7efSPeter Ujfalusi pll_sel = (TAS2552_PLL_CLKIN_BCLK << 3) & 2161014f7efSPeter Ujfalusi TAS2552_PLL_SRC_MASK; 2171014f7efSPeter Ujfalusi } 2181014f7efSPeter Ujfalusi goto recalc; 2191014f7efSPeter Ujfalusi } 2201014f7efSPeter Ujfalusi 2211014f7efSPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_PLL_SRC_MASK, 2221014f7efSPeter Ujfalusi pll_sel); 2231014f7efSPeter Ujfalusi 2241014f7efSPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1, 2251014f7efSPeter Ujfalusi TAS2552_PLL_J_MASK, j); 2261014f7efSPeter Ujfalusi /* Will clear the PLL_BYPASS bit */ 2271014f7efSPeter Ujfalusi snd_soc_write(codec, TAS2552_PLL_CTRL_2, 2281014f7efSPeter Ujfalusi TAS2552_PLL_D_UPPER(d)); 2291014f7efSPeter Ujfalusi snd_soc_write(codec, TAS2552_PLL_CTRL_3, 2301014f7efSPeter Ujfalusi TAS2552_PLL_D_LOWER(d)); 2311014f7efSPeter Ujfalusi } 2321014f7efSPeter Ujfalusi 2331014f7efSPeter Ujfalusi /* Restore PLL status */ 2341014f7efSPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 2351014f7efSPeter Ujfalusi pll_enable); 2361014f7efSPeter Ujfalusi 2371014f7efSPeter Ujfalusi return 0; 2381014f7efSPeter Ujfalusi } 2391014f7efSPeter Ujfalusi 2405df7f71dSDan Murphy static int tas2552_hw_params(struct snd_pcm_substream *substream, 2415df7f71dSDan Murphy struct snd_pcm_hw_params *params, 2425df7f71dSDan Murphy struct snd_soc_dai *dai) 2435df7f71dSDan Murphy { 2445df7f71dSDan Murphy struct snd_soc_codec *codec = dai->codec; 2455df7f71dSDan Murphy struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); 246d20b098dSPeter Ujfalusi int cpf; 247a571cb17SPeter Ujfalusi u8 ser_ctrl1_reg, wclk_rate; 248d20b098dSPeter Ujfalusi 249d20b098dSPeter Ujfalusi switch (params_width(params)) { 250d20b098dSPeter Ujfalusi case 16: 251d20b098dSPeter Ujfalusi ser_ctrl1_reg = TAS2552_WORDLENGTH_16BIT; 252d20b098dSPeter Ujfalusi cpf = 32 + tas2552->tdm_delay; 253d20b098dSPeter Ujfalusi break; 254d20b098dSPeter Ujfalusi case 20: 255d20b098dSPeter Ujfalusi ser_ctrl1_reg = TAS2552_WORDLENGTH_20BIT; 256d20b098dSPeter Ujfalusi cpf = 64 + tas2552->tdm_delay; 257d20b098dSPeter Ujfalusi break; 258d20b098dSPeter Ujfalusi case 24: 259d20b098dSPeter Ujfalusi ser_ctrl1_reg = TAS2552_WORDLENGTH_24BIT; 260d20b098dSPeter Ujfalusi cpf = 64 + tas2552->tdm_delay; 261d20b098dSPeter Ujfalusi break; 262d20b098dSPeter Ujfalusi case 32: 263d20b098dSPeter Ujfalusi ser_ctrl1_reg = TAS2552_WORDLENGTH_32BIT; 264d20b098dSPeter Ujfalusi cpf = 64 + tas2552->tdm_delay; 265d20b098dSPeter Ujfalusi break; 266d20b098dSPeter Ujfalusi default: 267d20b098dSPeter Ujfalusi dev_err(codec->dev, "Not supported sample size: %d\n", 268d20b098dSPeter Ujfalusi params_width(params)); 269d20b098dSPeter Ujfalusi return -EINVAL; 270d20b098dSPeter Ujfalusi } 271d20b098dSPeter Ujfalusi 272d20b098dSPeter Ujfalusi if (cpf <= 32) 273d20b098dSPeter Ujfalusi ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_32; 274d20b098dSPeter Ujfalusi else if (cpf <= 64) 275d20b098dSPeter Ujfalusi ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_64; 276d20b098dSPeter Ujfalusi else if (cpf <= 128) 277d20b098dSPeter Ujfalusi ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_128; 278d20b098dSPeter Ujfalusi else 279d20b098dSPeter Ujfalusi ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_256; 280d20b098dSPeter Ujfalusi 281d20b098dSPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, 282d20b098dSPeter Ujfalusi TAS2552_WORDLENGTH_MASK | TAS2552_CLKSPERFRAME_MASK, 283d20b098dSPeter Ujfalusi ser_ctrl1_reg); 2845df7f71dSDan Murphy 285a571cb17SPeter Ujfalusi switch (params_rate(params)) { 286a571cb17SPeter Ujfalusi case 8000: 287a571cb17SPeter Ujfalusi wclk_rate = TAS2552_WCLK_FREQ_8KHZ; 288a571cb17SPeter Ujfalusi break; 289a571cb17SPeter Ujfalusi case 11025: 290a571cb17SPeter Ujfalusi case 12000: 291a571cb17SPeter Ujfalusi wclk_rate = TAS2552_WCLK_FREQ_11_12KHZ; 292a571cb17SPeter Ujfalusi break; 293a571cb17SPeter Ujfalusi case 16000: 294a571cb17SPeter Ujfalusi wclk_rate = TAS2552_WCLK_FREQ_16KHZ; 295a571cb17SPeter Ujfalusi break; 296a571cb17SPeter Ujfalusi case 22050: 297a571cb17SPeter Ujfalusi case 24000: 298a571cb17SPeter Ujfalusi wclk_rate = TAS2552_WCLK_FREQ_22_24KHZ; 299a571cb17SPeter Ujfalusi break; 300a571cb17SPeter Ujfalusi case 32000: 301a571cb17SPeter Ujfalusi wclk_rate = TAS2552_WCLK_FREQ_32KHZ; 302a571cb17SPeter Ujfalusi break; 303a571cb17SPeter Ujfalusi case 44100: 304a571cb17SPeter Ujfalusi case 48000: 305a571cb17SPeter Ujfalusi wclk_rate = TAS2552_WCLK_FREQ_44_48KHZ; 306a571cb17SPeter Ujfalusi break; 307a571cb17SPeter Ujfalusi case 88200: 308a571cb17SPeter Ujfalusi case 96000: 309a571cb17SPeter Ujfalusi wclk_rate = TAS2552_WCLK_FREQ_88_96KHZ; 310a571cb17SPeter Ujfalusi break; 311a571cb17SPeter Ujfalusi case 176400: 312a571cb17SPeter Ujfalusi case 192000: 313a571cb17SPeter Ujfalusi wclk_rate = TAS2552_WCLK_FREQ_176_192KHZ; 314a571cb17SPeter Ujfalusi break; 315a571cb17SPeter Ujfalusi default: 316a571cb17SPeter Ujfalusi dev_err(codec->dev, "Not supported sample rate: %d\n", 317a571cb17SPeter Ujfalusi params_rate(params)); 318a571cb17SPeter Ujfalusi return -EINVAL; 319a571cb17SPeter Ujfalusi } 320a571cb17SPeter Ujfalusi 321a571cb17SPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_CFG_3, TAS2552_WCLK_FREQ_MASK, 322a571cb17SPeter Ujfalusi wclk_rate); 323a571cb17SPeter Ujfalusi 3241014f7efSPeter Ujfalusi return tas2552_setup_pll(codec, params); 3255df7f71dSDan Murphy } 3265df7f71dSDan Murphy 3271b68c7dcSPeter Ujfalusi #define TAS2552_DAI_FMT_MASK (TAS2552_BCLKDIR | \ 3281b68c7dcSPeter Ujfalusi TAS2552_WCLKDIR | \ 3291b68c7dcSPeter Ujfalusi TAS2552_DATAFORMAT_MASK) 3303f747a81SPeter Ujfalusi static int tas2552_prepare(struct snd_pcm_substream *substream, 3313f747a81SPeter Ujfalusi struct snd_soc_dai *dai) 3323f747a81SPeter Ujfalusi { 3333f747a81SPeter Ujfalusi struct snd_soc_codec *codec = dai->codec; 3343f747a81SPeter Ujfalusi struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); 3353f747a81SPeter Ujfalusi int delay = 0; 3363f747a81SPeter Ujfalusi 3373f747a81SPeter Ujfalusi /* TDM slot selection only valid in DSP_A/_B mode */ 3383f747a81SPeter Ujfalusi if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_A) 3393f747a81SPeter Ujfalusi delay += (tas2552->tdm_delay + 1); 3403f747a81SPeter Ujfalusi else if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_B) 3413f747a81SPeter Ujfalusi delay += tas2552->tdm_delay; 3423f747a81SPeter Ujfalusi 3433f747a81SPeter Ujfalusi /* Configure data delay */ 3443f747a81SPeter Ujfalusi snd_soc_write(codec, TAS2552_SER_CTRL_2, delay); 3453f747a81SPeter Ujfalusi 3463f747a81SPeter Ujfalusi return 0; 3473f747a81SPeter Ujfalusi } 3483f747a81SPeter Ujfalusi 3495df7f71dSDan Murphy static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) 3505df7f71dSDan Murphy { 3515df7f71dSDan Murphy struct snd_soc_codec *codec = dai->codec; 3523f747a81SPeter Ujfalusi struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); 3535df7f71dSDan Murphy u8 serial_format; 3545df7f71dSDan Murphy 3555df7f71dSDan Murphy switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 3565df7f71dSDan Murphy case SND_SOC_DAIFMT_CBS_CFS: 3575df7f71dSDan Murphy serial_format = 0x00; 3585df7f71dSDan Murphy break; 3595df7f71dSDan Murphy case SND_SOC_DAIFMT_CBS_CFM: 3601b68c7dcSPeter Ujfalusi serial_format = TAS2552_WCLKDIR; 3615df7f71dSDan Murphy break; 3625df7f71dSDan Murphy case SND_SOC_DAIFMT_CBM_CFS: 3631b68c7dcSPeter Ujfalusi serial_format = TAS2552_BCLKDIR; 3645df7f71dSDan Murphy break; 3655df7f71dSDan Murphy case SND_SOC_DAIFMT_CBM_CFM: 3661b68c7dcSPeter Ujfalusi serial_format = (TAS2552_BCLKDIR | TAS2552_WCLKDIR); 3675df7f71dSDan Murphy break; 3685df7f71dSDan Murphy default: 3695df7f71dSDan Murphy dev_vdbg(codec->dev, "DAI Format master is not found\n"); 3705df7f71dSDan Murphy return -EINVAL; 3715df7f71dSDan Murphy } 3725df7f71dSDan Murphy 3734c331373SPeter Ujfalusi switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK | 3744c331373SPeter Ujfalusi SND_SOC_DAIFMT_INV_MASK)) { 3754c331373SPeter Ujfalusi case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF): 3765df7f71dSDan Murphy break; 3774c331373SPeter Ujfalusi case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF): 3784c331373SPeter Ujfalusi case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF): 3791b68c7dcSPeter Ujfalusi serial_format |= TAS2552_DATAFORMAT_DSP; 3805df7f71dSDan Murphy break; 3814c331373SPeter Ujfalusi case (SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_NB_NF): 3821b68c7dcSPeter Ujfalusi serial_format |= TAS2552_DATAFORMAT_RIGHT_J; 3835df7f71dSDan Murphy break; 3844c331373SPeter Ujfalusi case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF): 3851b68c7dcSPeter Ujfalusi serial_format |= TAS2552_DATAFORMAT_LEFT_J; 3865df7f71dSDan Murphy break; 3875df7f71dSDan Murphy default: 3885df7f71dSDan Murphy dev_vdbg(codec->dev, "DAI Format is not found\n"); 3895df7f71dSDan Murphy return -EINVAL; 3905df7f71dSDan Murphy } 3913f747a81SPeter Ujfalusi tas2552->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; 3925df7f71dSDan Murphy 3934c331373SPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, TAS2552_DAI_FMT_MASK, 3945df7f71dSDan Murphy serial_format); 3955df7f71dSDan Murphy return 0; 3965df7f71dSDan Murphy } 3975df7f71dSDan Murphy 3985df7f71dSDan Murphy static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, 3995df7f71dSDan Murphy unsigned int freq, int dir) 4005df7f71dSDan Murphy { 4015df7f71dSDan Murphy struct snd_soc_codec *codec = dai->codec; 4025df7f71dSDan Murphy struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); 4039d87a888SPeter Ujfalusi u8 reg, mask, val; 4045df7f71dSDan Murphy 4059d87a888SPeter Ujfalusi switch (clk_id) { 4069d87a888SPeter Ujfalusi case TAS2552_PLL_CLKIN_MCLK: 4079d87a888SPeter Ujfalusi case TAS2552_PLL_CLKIN_IVCLKIN: 4081014f7efSPeter Ujfalusi if (freq < 512000 || freq > 24576000) { 4091014f7efSPeter Ujfalusi /* out of range PLL_CLKIN, fall back to use BCLK */ 4101014f7efSPeter Ujfalusi dev_warn(codec->dev, "Out of range PLL_CLKIN: %u\n", 4111014f7efSPeter Ujfalusi freq); 4121014f7efSPeter Ujfalusi clk_id = TAS2552_PLL_CLKIN_BCLK; 4131014f7efSPeter Ujfalusi freq = 0; 4141014f7efSPeter Ujfalusi } 4151014f7efSPeter Ujfalusi /* fall through */ 4161014f7efSPeter Ujfalusi case TAS2552_PLL_CLKIN_BCLK: 4179d87a888SPeter Ujfalusi case TAS2552_PLL_CLKIN_1_8_FIXED: 4189d87a888SPeter Ujfalusi mask = TAS2552_PLL_SRC_MASK; 4199d87a888SPeter Ujfalusi val = (clk_id << 3) & mask; /* bit 4:5 in the register */ 4209d87a888SPeter Ujfalusi reg = TAS2552_CFG_1; 4211014f7efSPeter Ujfalusi tas2552->pll_clk_id = clk_id; 42216bd3952SPeter Ujfalusi tas2552->pll_clkin = freq; 4239d87a888SPeter Ujfalusi break; 4249d87a888SPeter Ujfalusi case TAS2552_PDM_CLK_PLL: 4259d87a888SPeter Ujfalusi case TAS2552_PDM_CLK_IVCLKIN: 4269d87a888SPeter Ujfalusi case TAS2552_PDM_CLK_BCLK: 4279d87a888SPeter Ujfalusi case TAS2552_PDM_CLK_MCLK: 4289d87a888SPeter Ujfalusi mask = TAS2552_PDM_CLK_SEL_MASK; 4299d87a888SPeter Ujfalusi val = (clk_id >> 1) & mask; /* bit 0:1 in the register */ 4309d87a888SPeter Ujfalusi reg = TAS2552_PDM_CFG; 4311014f7efSPeter Ujfalusi tas2552->pdm_clk_id = clk_id; 4329d87a888SPeter Ujfalusi tas2552->pdm_clk = freq; 4339d87a888SPeter Ujfalusi break; 4349d87a888SPeter Ujfalusi default: 4359d87a888SPeter Ujfalusi dev_err(codec->dev, "Invalid clk id: %d\n", clk_id); 4369d87a888SPeter Ujfalusi return -EINVAL; 4379d87a888SPeter Ujfalusi } 4389d87a888SPeter Ujfalusi 4399d87a888SPeter Ujfalusi snd_soc_update_bits(codec, reg, mask, val); 4405df7f71dSDan Murphy 4415df7f71dSDan Murphy return 0; 4425df7f71dSDan Murphy } 4435df7f71dSDan Murphy 4443f747a81SPeter Ujfalusi static int tas2552_set_dai_tdm_slot(struct snd_soc_dai *dai, 4453f747a81SPeter Ujfalusi unsigned int tx_mask, unsigned int rx_mask, 4463f747a81SPeter Ujfalusi int slots, int slot_width) 4473f747a81SPeter Ujfalusi { 4483f747a81SPeter Ujfalusi struct snd_soc_codec *codec = dai->codec; 4493f747a81SPeter Ujfalusi struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); 4503f747a81SPeter Ujfalusi unsigned int lsb; 4513f747a81SPeter Ujfalusi 4523f747a81SPeter Ujfalusi if (unlikely(!tx_mask)) { 4533f747a81SPeter Ujfalusi dev_err(codec->dev, "tx masks need to be non 0\n"); 4543f747a81SPeter Ujfalusi return -EINVAL; 4553f747a81SPeter Ujfalusi } 4563f747a81SPeter Ujfalusi 4573f747a81SPeter Ujfalusi /* TDM based on DSP mode requires slots to be adjacent */ 4583f747a81SPeter Ujfalusi lsb = __ffs(tx_mask); 4593f747a81SPeter Ujfalusi if ((lsb + 1) != __fls(tx_mask)) { 4603f747a81SPeter Ujfalusi dev_err(codec->dev, "Invalid mask, slots must be adjacent\n"); 4613f747a81SPeter Ujfalusi return -EINVAL; 4623f747a81SPeter Ujfalusi } 4633f747a81SPeter Ujfalusi 4643f747a81SPeter Ujfalusi tas2552->tdm_delay = lsb * slot_width; 4653f747a81SPeter Ujfalusi 4663f747a81SPeter Ujfalusi /* DOUT in high-impedance on inactive bit clocks */ 4673f747a81SPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_DOUT, 4683f747a81SPeter Ujfalusi TAS2552_SDOUT_TRISTATE, TAS2552_SDOUT_TRISTATE); 4693f747a81SPeter Ujfalusi 4703f747a81SPeter Ujfalusi return 0; 4713f747a81SPeter Ujfalusi } 4723f747a81SPeter Ujfalusi 4735df7f71dSDan Murphy static int tas2552_mute(struct snd_soc_dai *dai, int mute) 4745df7f71dSDan Murphy { 475e3606aa4SPeter Ujfalusi u8 cfg1_reg = 0; 4765df7f71dSDan Murphy struct snd_soc_codec *codec = dai->codec; 4775df7f71dSDan Murphy 4785df7f71dSDan Murphy if (mute) 479e3606aa4SPeter Ujfalusi cfg1_reg |= TAS2552_MUTE; 4805df7f71dSDan Murphy 4817de544fdSPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE, cfg1_reg); 4825df7f71dSDan Murphy 4835df7f71dSDan Murphy return 0; 4845df7f71dSDan Murphy } 4855df7f71dSDan Murphy 486641d334bSRafael J. Wysocki #ifdef CONFIG_PM 4875df7f71dSDan Murphy static int tas2552_runtime_suspend(struct device *dev) 4885df7f71dSDan Murphy { 4895df7f71dSDan Murphy struct tas2552_data *tas2552 = dev_get_drvdata(dev); 4905df7f71dSDan Murphy 491dd6e3053SPeter Ujfalusi tas2552_sw_shutdown(tas2552, 1); 4925df7f71dSDan Murphy 4935df7f71dSDan Murphy regcache_cache_only(tas2552->regmap, true); 4945df7f71dSDan Murphy regcache_mark_dirty(tas2552->regmap); 4955df7f71dSDan Murphy 496e295a4a4SDan Murphy if (tas2552->enable_gpio) 497e295a4a4SDan Murphy gpiod_set_value(tas2552->enable_gpio, 0); 498e295a4a4SDan Murphy 4995df7f71dSDan Murphy return 0; 5005df7f71dSDan Murphy } 5015df7f71dSDan Murphy 5025df7f71dSDan Murphy static int tas2552_runtime_resume(struct device *dev) 5035df7f71dSDan Murphy { 5045df7f71dSDan Murphy struct tas2552_data *tas2552 = dev_get_drvdata(dev); 5055df7f71dSDan Murphy 5065df7f71dSDan Murphy if (tas2552->enable_gpio) 5075df7f71dSDan Murphy gpiod_set_value(tas2552->enable_gpio, 1); 5085df7f71dSDan Murphy 509dd6e3053SPeter Ujfalusi tas2552_sw_shutdown(tas2552, 0); 5105df7f71dSDan Murphy 5115df7f71dSDan Murphy regcache_cache_only(tas2552->regmap, false); 5125df7f71dSDan Murphy regcache_sync(tas2552->regmap); 5135df7f71dSDan Murphy 5145df7f71dSDan Murphy return 0; 5155df7f71dSDan Murphy } 5165df7f71dSDan Murphy #endif 5175df7f71dSDan Murphy 5185df7f71dSDan Murphy static const struct dev_pm_ops tas2552_pm = { 5195df7f71dSDan Murphy SET_RUNTIME_PM_OPS(tas2552_runtime_suspend, tas2552_runtime_resume, 5205df7f71dSDan Murphy NULL) 5215df7f71dSDan Murphy }; 5225df7f71dSDan Murphy 5235df7f71dSDan Murphy static struct snd_soc_dai_ops tas2552_speaker_dai_ops = { 5245df7f71dSDan Murphy .hw_params = tas2552_hw_params, 5253f747a81SPeter Ujfalusi .prepare = tas2552_prepare, 5265df7f71dSDan Murphy .set_sysclk = tas2552_set_dai_sysclk, 5275df7f71dSDan Murphy .set_fmt = tas2552_set_dai_fmt, 5283f747a81SPeter Ujfalusi .set_tdm_slot = tas2552_set_dai_tdm_slot, 5295df7f71dSDan Murphy .digital_mute = tas2552_mute, 5305df7f71dSDan Murphy }; 5315df7f71dSDan Murphy 5325df7f71dSDan Murphy /* Formats supported by TAS2552 driver. */ 5335df7f71dSDan Murphy #define TAS2552_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ 5345df7f71dSDan Murphy SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) 5355df7f71dSDan Murphy 5365df7f71dSDan Murphy /* TAS2552 dai structure. */ 5375df7f71dSDan Murphy static struct snd_soc_dai_driver tas2552_dai[] = { 5385df7f71dSDan Murphy { 5395df7f71dSDan Murphy .name = "tas2552-amplifier", 5405df7f71dSDan Murphy .playback = { 541a7a8e994SDan Murphy .stream_name = "Playback", 5425df7f71dSDan Murphy .channels_min = 2, 5435df7f71dSDan Murphy .channels_max = 2, 5445df7f71dSDan Murphy .rates = SNDRV_PCM_RATE_8000_192000, 5455df7f71dSDan Murphy .formats = TAS2552_FORMATS, 5465df7f71dSDan Murphy }, 5475df7f71dSDan Murphy .ops = &tas2552_speaker_dai_ops, 5485df7f71dSDan Murphy }, 5495df7f71dSDan Murphy }; 5505df7f71dSDan Murphy 5515df7f71dSDan Murphy /* 5525df7f71dSDan Murphy * DAC digital volumes. From -7 to 24 dB in 1 dB steps 5535df7f71dSDan Murphy */ 554dd6ae3bcSPeter Ujfalusi static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 0); 5555df7f71dSDan Murphy 5562962cb52SPeter Ujfalusi static const char * const tas2552_din_source_select[] = { 5572962cb52SPeter Ujfalusi "Muted", 5582962cb52SPeter Ujfalusi "Left", 5592962cb52SPeter Ujfalusi "Right", 5602962cb52SPeter Ujfalusi "Left + Right average", 5612962cb52SPeter Ujfalusi }; 5622962cb52SPeter Ujfalusi static SOC_ENUM_SINGLE_DECL(tas2552_din_source_enum, 5632962cb52SPeter Ujfalusi TAS2552_CFG_3, 3, 5642962cb52SPeter Ujfalusi tas2552_din_source_select); 5652962cb52SPeter Ujfalusi 5665df7f71dSDan Murphy static const struct snd_kcontrol_new tas2552_snd_controls[] = { 5675df7f71dSDan Murphy SOC_SINGLE_TLV("Speaker Driver Playback Volume", 568dd6ae3bcSPeter Ujfalusi TAS2552_PGA_GAIN, 0, 0x1f, 0, dac_tlv), 5692962cb52SPeter Ujfalusi SOC_ENUM("DIN source", tas2552_din_source_enum), 5705df7f71dSDan Murphy }; 5715df7f71dSDan Murphy 5725df7f71dSDan Murphy static int tas2552_codec_probe(struct snd_soc_codec *codec) 5735df7f71dSDan Murphy { 5745df7f71dSDan Murphy struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); 5755df7f71dSDan Murphy int ret; 5765df7f71dSDan Murphy 5775df7f71dSDan Murphy tas2552->codec = codec; 5785df7f71dSDan Murphy 5795df7f71dSDan Murphy ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies), 5805df7f71dSDan Murphy tas2552->supplies); 5815df7f71dSDan Murphy 5825df7f71dSDan Murphy if (ret != 0) { 5835df7f71dSDan Murphy dev_err(codec->dev, "Failed to enable supplies: %d\n", 5845df7f71dSDan Murphy ret); 5855df7f71dSDan Murphy return ret; 5865df7f71dSDan Murphy } 5875df7f71dSDan Murphy 5885df7f71dSDan Murphy if (tas2552->enable_gpio) 5895df7f71dSDan Murphy gpiod_set_value(tas2552->enable_gpio, 1); 5905df7f71dSDan Murphy 5915df7f71dSDan Murphy ret = pm_runtime_get_sync(codec->dev); 5925df7f71dSDan Murphy if (ret < 0) { 5935df7f71dSDan Murphy dev_err(codec->dev, "Enabling device failed: %d\n", 5945df7f71dSDan Murphy ret); 5955df7f71dSDan Murphy goto probe_fail; 5965df7f71dSDan Murphy } 5975df7f71dSDan Murphy 5987d785025SPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE, TAS2552_MUTE); 5995df7f71dSDan Murphy snd_soc_write(codec, TAS2552_CFG_3, TAS2552_I2S_OUT_SEL | 600a571cb17SPeter Ujfalusi TAS2552_DIN_SRC_SEL_AVG_L_R); 601b2822f19SPeter Ujfalusi snd_soc_write(codec, TAS2552_OUTPUT_DATA, 602b2822f19SPeter Ujfalusi TAS2552_PDM_DATA_SEL_V_I | 603b2822f19SPeter Ujfalusi TAS2552_R_DATA_OUT(TAS2552_DATA_OUT_V_DATA)); 6042a9dd1dbSPeter Ujfalusi snd_soc_write(codec, TAS2552_BOOST_APT_CTRL, TAS2552_APT_DELAY_200 | 6052a9dd1dbSPeter Ujfalusi TAS2552_APT_THRESH_20_17); 6065df7f71dSDan Murphy 6074afdd89dSPeter Ujfalusi snd_soc_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN | TAS2552_APT_EN | 6084afdd89dSPeter Ujfalusi TAS2552_LIM_EN); 609a7a8e994SDan Murphy 6105df7f71dSDan Murphy return 0; 6115df7f71dSDan Murphy 6125df7f71dSDan Murphy probe_fail: 6135df7f71dSDan Murphy if (tas2552->enable_gpio) 6145df7f71dSDan Murphy gpiod_set_value(tas2552->enable_gpio, 0); 6155df7f71dSDan Murphy 6165df7f71dSDan Murphy regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies), 6175df7f71dSDan Murphy tas2552->supplies); 6185df7f71dSDan Murphy return -EIO; 6195df7f71dSDan Murphy } 6205df7f71dSDan Murphy 6215df7f71dSDan Murphy static int tas2552_codec_remove(struct snd_soc_codec *codec) 6225df7f71dSDan Murphy { 6235df7f71dSDan Murphy struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); 6245df7f71dSDan Murphy 625e295a4a4SDan Murphy pm_runtime_put(codec->dev); 626e295a4a4SDan Murphy 6275df7f71dSDan Murphy if (tas2552->enable_gpio) 6285df7f71dSDan Murphy gpiod_set_value(tas2552->enable_gpio, 0); 6295df7f71dSDan Murphy 6305df7f71dSDan Murphy return 0; 6315df7f71dSDan Murphy }; 6325df7f71dSDan Murphy 6335df7f71dSDan Murphy #ifdef CONFIG_PM 6345df7f71dSDan Murphy static int tas2552_suspend(struct snd_soc_codec *codec) 6355df7f71dSDan Murphy { 6365df7f71dSDan Murphy struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); 6375df7f71dSDan Murphy int ret; 6385df7f71dSDan Murphy 6395df7f71dSDan Murphy ret = regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies), 6405df7f71dSDan Murphy tas2552->supplies); 6415df7f71dSDan Murphy 6425df7f71dSDan Murphy if (ret != 0) 6435df7f71dSDan Murphy dev_err(codec->dev, "Failed to disable supplies: %d\n", 6445df7f71dSDan Murphy ret); 6455df7f71dSDan Murphy return 0; 6465df7f71dSDan Murphy } 6475df7f71dSDan Murphy 6485df7f71dSDan Murphy static int tas2552_resume(struct snd_soc_codec *codec) 6495df7f71dSDan Murphy { 6505df7f71dSDan Murphy struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); 6515df7f71dSDan Murphy int ret; 6525df7f71dSDan Murphy 6535df7f71dSDan Murphy ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies), 6545df7f71dSDan Murphy tas2552->supplies); 6555df7f71dSDan Murphy 6565df7f71dSDan Murphy if (ret != 0) { 6575df7f71dSDan Murphy dev_err(codec->dev, "Failed to enable supplies: %d\n", 6585df7f71dSDan Murphy ret); 6595df7f71dSDan Murphy } 6605df7f71dSDan Murphy 6615df7f71dSDan Murphy return 0; 6625df7f71dSDan Murphy } 6635df7f71dSDan Murphy #else 6645df7f71dSDan Murphy #define tas2552_suspend NULL 6655df7f71dSDan Murphy #define tas2552_resume NULL 6665df7f71dSDan Murphy #endif 6675df7f71dSDan Murphy 6685df7f71dSDan Murphy static struct snd_soc_codec_driver soc_codec_dev_tas2552 = { 6695df7f71dSDan Murphy .probe = tas2552_codec_probe, 6705df7f71dSDan Murphy .remove = tas2552_codec_remove, 6715df7f71dSDan Murphy .suspend = tas2552_suspend, 6725df7f71dSDan Murphy .resume = tas2552_resume, 6737d785025SPeter Ujfalusi .ignore_pmdown_time = true, 6747d785025SPeter Ujfalusi 6755df7f71dSDan Murphy .controls = tas2552_snd_controls, 6765df7f71dSDan Murphy .num_controls = ARRAY_SIZE(tas2552_snd_controls), 677e3f1ff31SLars-Peter Clausen .dapm_widgets = tas2552_dapm_widgets, 678e3f1ff31SLars-Peter Clausen .num_dapm_widgets = ARRAY_SIZE(tas2552_dapm_widgets), 679e3f1ff31SLars-Peter Clausen .dapm_routes = tas2552_audio_map, 680e3f1ff31SLars-Peter Clausen .num_dapm_routes = ARRAY_SIZE(tas2552_audio_map), 6815df7f71dSDan Murphy }; 6825df7f71dSDan Murphy 6835df7f71dSDan Murphy static const struct regmap_config tas2552_regmap_config = { 6845df7f71dSDan Murphy .reg_bits = 8, 6855df7f71dSDan Murphy .val_bits = 8, 6865df7f71dSDan Murphy 6875df7f71dSDan Murphy .max_register = TAS2552_MAX_REG, 6885df7f71dSDan Murphy .reg_defaults = tas2552_reg_defs, 6895df7f71dSDan Murphy .num_reg_defaults = ARRAY_SIZE(tas2552_reg_defs), 6905df7f71dSDan Murphy .cache_type = REGCACHE_RBTREE, 6915df7f71dSDan Murphy }; 6925df7f71dSDan Murphy 6935df7f71dSDan Murphy static int tas2552_probe(struct i2c_client *client, 6945df7f71dSDan Murphy const struct i2c_device_id *id) 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 70634d7c390SUwe Kleine-König data->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); 707ea178d14SPeter Ujfalusi if (IS_ERR(data->enable_gpio)) { 708ea178d14SPeter Ujfalusi if (PTR_ERR(data->enable_gpio) == -EPROBE_DEFER) 709ea178d14SPeter Ujfalusi return -EPROBE_DEFER; 710ea178d14SPeter Ujfalusi 711ea178d14SPeter Ujfalusi data->enable_gpio = NULL;; 712ea178d14SPeter Ujfalusi } 7135df7f71dSDan Murphy 7145df7f71dSDan Murphy data->tas2552_client = client; 7155df7f71dSDan Murphy data->regmap = devm_regmap_init_i2c(client, &tas2552_regmap_config); 7165df7f71dSDan Murphy if (IS_ERR(data->regmap)) { 7175df7f71dSDan Murphy ret = PTR_ERR(data->regmap); 7185df7f71dSDan Murphy dev_err(&client->dev, "Failed to allocate register map: %d\n", 7195df7f71dSDan Murphy ret); 7205df7f71dSDan Murphy return ret; 7215df7f71dSDan Murphy } 7225df7f71dSDan Murphy 7235df7f71dSDan Murphy for (i = 0; i < ARRAY_SIZE(data->supplies); i++) 7245df7f71dSDan Murphy data->supplies[i].supply = tas2552_supply_names[i]; 7255df7f71dSDan Murphy 7265df7f71dSDan Murphy ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies), 7275df7f71dSDan Murphy data->supplies); 728c62f9d8fSAxel Lin if (ret != 0) { 7295df7f71dSDan Murphy dev_err(dev, "Failed to request supplies: %d\n", ret); 730c62f9d8fSAxel Lin return ret; 731c62f9d8fSAxel Lin } 7325df7f71dSDan Murphy 7335df7f71dSDan Murphy pm_runtime_set_active(&client->dev); 7345df7f71dSDan Murphy pm_runtime_set_autosuspend_delay(&client->dev, 1000); 7355df7f71dSDan Murphy pm_runtime_use_autosuspend(&client->dev); 7365df7f71dSDan Murphy pm_runtime_enable(&client->dev); 7375df7f71dSDan Murphy pm_runtime_mark_last_busy(&client->dev); 7385df7f71dSDan Murphy pm_runtime_put_sync_autosuspend(&client->dev); 7395df7f71dSDan Murphy 7405df7f71dSDan Murphy dev_set_drvdata(&client->dev, data); 7415df7f71dSDan Murphy 7425df7f71dSDan Murphy ret = snd_soc_register_codec(&client->dev, 7435df7f71dSDan Murphy &soc_codec_dev_tas2552, 7445df7f71dSDan Murphy tas2552_dai, ARRAY_SIZE(tas2552_dai)); 7455df7f71dSDan Murphy if (ret < 0) 7465df7f71dSDan Murphy dev_err(&client->dev, "Failed to register codec: %d\n", ret); 7475df7f71dSDan Murphy 748c62f9d8fSAxel Lin return ret; 7495df7f71dSDan Murphy } 7505df7f71dSDan Murphy 7515df7f71dSDan Murphy static int tas2552_i2c_remove(struct i2c_client *client) 7525df7f71dSDan Murphy { 7535df7f71dSDan Murphy snd_soc_unregister_codec(&client->dev); 754*4785ed89SPeter Ujfalusi pm_runtime_disable(&client->dev); 7555df7f71dSDan Murphy return 0; 7565df7f71dSDan Murphy } 7575df7f71dSDan Murphy 7585df7f71dSDan Murphy static const struct i2c_device_id tas2552_id[] = { 7595df7f71dSDan Murphy { "tas2552", 0 }, 7605df7f71dSDan Murphy { } 7615df7f71dSDan Murphy }; 7625df7f71dSDan Murphy MODULE_DEVICE_TABLE(i2c, tas2552_id); 7635df7f71dSDan Murphy 7645df7f71dSDan Murphy #if IS_ENABLED(CONFIG_OF) 7655df7f71dSDan Murphy static const struct of_device_id tas2552_of_match[] = { 7665df7f71dSDan Murphy { .compatible = "ti,tas2552", }, 7675df7f71dSDan Murphy {}, 7685df7f71dSDan Murphy }; 7695df7f71dSDan Murphy MODULE_DEVICE_TABLE(of, tas2552_of_match); 7705df7f71dSDan Murphy #endif 7715df7f71dSDan Murphy 7725df7f71dSDan Murphy static struct i2c_driver tas2552_i2c_driver = { 7735df7f71dSDan Murphy .driver = { 7745df7f71dSDan Murphy .name = "tas2552", 7755df7f71dSDan Murphy .owner = THIS_MODULE, 7765df7f71dSDan Murphy .of_match_table = of_match_ptr(tas2552_of_match), 7775df7f71dSDan Murphy .pm = &tas2552_pm, 7785df7f71dSDan Murphy }, 7795df7f71dSDan Murphy .probe = tas2552_probe, 7805df7f71dSDan Murphy .remove = tas2552_i2c_remove, 7815df7f71dSDan Murphy .id_table = tas2552_id, 7825df7f71dSDan Murphy }; 7835df7f71dSDan Murphy 7845df7f71dSDan Murphy module_i2c_driver(tas2552_i2c_driver); 7855df7f71dSDan Murphy 7865df7f71dSDan Murphy MODULE_AUTHOR("Dan Muprhy <dmurphy@ti.com>"); 7875df7f71dSDan Murphy MODULE_DESCRIPTION("TAS2552 Audio amplifier driver"); 7885df7f71dSDan Murphy MODULE_LICENSE("GPL"); 789