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