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 41c418a84aSAxel Lin static const 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 148b94525bfSPeter Ujfalusi static void tas2552_sw_shutdown(struct tas2552_data *tas2552, int sw_shutdown) 1495df7f71dSDan Murphy { 150dd6e3053SPeter Ujfalusi u8 cfg1_reg = 0; 1515df7f71dSDan Murphy 152b94525bfSPeter Ujfalusi if (!tas2552->codec) 15380ba2669SPeter Ujfalusi return; 15480ba2669SPeter Ujfalusi 1555df7f71dSDan Murphy if (sw_shutdown) 1567de544fdSPeter Ujfalusi cfg1_reg = TAS2552_SWS; 1575df7f71dSDan Murphy 158b94525bfSPeter Ujfalusi snd_soc_update_bits(tas2552->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 */ 195*1bb7cb68SOskar Schirmer unsigned int d, q, t; 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: 203*1bb7cb68SOskar Schirmer t = (pll_clk * 2) << p; 204*1bb7cb68SOskar Schirmer j = t / pll_clkin; 205*1bb7cb68SOskar Schirmer d = t % pll_clkin; 206*1bb7cb68SOskar Schirmer t = pll_clkin / 10000; 207*1bb7cb68SOskar Schirmer q = d / (t + 1); 208*1bb7cb68SOskar Schirmer d = q + ((9999 - pll_clkin % 10000) * (d / t - q)) / 10000; 2091014f7efSPeter Ujfalusi 2101014f7efSPeter Ujfalusi if (d && (pll_clkin < 512000 || pll_clkin > 9200000)) { 2111014f7efSPeter Ujfalusi if (tas2552->pll_clk_id == TAS2552_PLL_CLKIN_BCLK) { 2121014f7efSPeter Ujfalusi pll_clkin = 1800000; 2131014f7efSPeter Ujfalusi pll_sel = (TAS2552_PLL_CLKIN_1_8_FIXED << 3) & 2141014f7efSPeter Ujfalusi TAS2552_PLL_SRC_MASK; 2151014f7efSPeter Ujfalusi } else { 2161014f7efSPeter Ujfalusi pll_clkin = snd_soc_params_to_bclk(params); 2171014f7efSPeter Ujfalusi pll_clkin += tas2552->tdm_delay; 2181014f7efSPeter Ujfalusi pll_sel = (TAS2552_PLL_CLKIN_BCLK << 3) & 2191014f7efSPeter Ujfalusi TAS2552_PLL_SRC_MASK; 2201014f7efSPeter Ujfalusi } 2211014f7efSPeter Ujfalusi goto recalc; 2221014f7efSPeter Ujfalusi } 2231014f7efSPeter Ujfalusi 2241014f7efSPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_PLL_SRC_MASK, 2251014f7efSPeter Ujfalusi pll_sel); 2261014f7efSPeter Ujfalusi 2271014f7efSPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1, 2281014f7efSPeter Ujfalusi TAS2552_PLL_J_MASK, j); 2291014f7efSPeter Ujfalusi /* Will clear the PLL_BYPASS bit */ 2301014f7efSPeter Ujfalusi snd_soc_write(codec, TAS2552_PLL_CTRL_2, 2311014f7efSPeter Ujfalusi TAS2552_PLL_D_UPPER(d)); 2321014f7efSPeter Ujfalusi snd_soc_write(codec, TAS2552_PLL_CTRL_3, 2331014f7efSPeter Ujfalusi TAS2552_PLL_D_LOWER(d)); 2341014f7efSPeter Ujfalusi } 2351014f7efSPeter Ujfalusi 2361014f7efSPeter Ujfalusi /* Restore PLL status */ 2371014f7efSPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 2381014f7efSPeter Ujfalusi pll_enable); 2391014f7efSPeter Ujfalusi 2401014f7efSPeter Ujfalusi return 0; 2411014f7efSPeter Ujfalusi } 2421014f7efSPeter Ujfalusi 2435df7f71dSDan Murphy static int tas2552_hw_params(struct snd_pcm_substream *substream, 2445df7f71dSDan Murphy struct snd_pcm_hw_params *params, 2455df7f71dSDan Murphy struct snd_soc_dai *dai) 2465df7f71dSDan Murphy { 2475df7f71dSDan Murphy struct snd_soc_codec *codec = dai->codec; 2485df7f71dSDan Murphy struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); 249d20b098dSPeter Ujfalusi int cpf; 250a571cb17SPeter Ujfalusi u8 ser_ctrl1_reg, wclk_rate; 251d20b098dSPeter Ujfalusi 252d20b098dSPeter Ujfalusi switch (params_width(params)) { 253d20b098dSPeter Ujfalusi case 16: 254d20b098dSPeter Ujfalusi ser_ctrl1_reg = TAS2552_WORDLENGTH_16BIT; 255d20b098dSPeter Ujfalusi cpf = 32 + tas2552->tdm_delay; 256d20b098dSPeter Ujfalusi break; 257d20b098dSPeter Ujfalusi case 20: 258d20b098dSPeter Ujfalusi ser_ctrl1_reg = TAS2552_WORDLENGTH_20BIT; 259d20b098dSPeter Ujfalusi cpf = 64 + tas2552->tdm_delay; 260d20b098dSPeter Ujfalusi break; 261d20b098dSPeter Ujfalusi case 24: 262d20b098dSPeter Ujfalusi ser_ctrl1_reg = TAS2552_WORDLENGTH_24BIT; 263d20b098dSPeter Ujfalusi cpf = 64 + tas2552->tdm_delay; 264d20b098dSPeter Ujfalusi break; 265d20b098dSPeter Ujfalusi case 32: 266d20b098dSPeter Ujfalusi ser_ctrl1_reg = TAS2552_WORDLENGTH_32BIT; 267d20b098dSPeter Ujfalusi cpf = 64 + tas2552->tdm_delay; 268d20b098dSPeter Ujfalusi break; 269d20b098dSPeter Ujfalusi default: 270d20b098dSPeter Ujfalusi dev_err(codec->dev, "Not supported sample size: %d\n", 271d20b098dSPeter Ujfalusi params_width(params)); 272d20b098dSPeter Ujfalusi return -EINVAL; 273d20b098dSPeter Ujfalusi } 274d20b098dSPeter Ujfalusi 275d20b098dSPeter Ujfalusi if (cpf <= 32) 276d20b098dSPeter Ujfalusi ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_32; 277d20b098dSPeter Ujfalusi else if (cpf <= 64) 278d20b098dSPeter Ujfalusi ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_64; 279d20b098dSPeter Ujfalusi else if (cpf <= 128) 280d20b098dSPeter Ujfalusi ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_128; 281d20b098dSPeter Ujfalusi else 282d20b098dSPeter Ujfalusi ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_256; 283d20b098dSPeter Ujfalusi 284d20b098dSPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, 285d20b098dSPeter Ujfalusi TAS2552_WORDLENGTH_MASK | TAS2552_CLKSPERFRAME_MASK, 286d20b098dSPeter Ujfalusi ser_ctrl1_reg); 2875df7f71dSDan Murphy 288a571cb17SPeter Ujfalusi switch (params_rate(params)) { 289a571cb17SPeter Ujfalusi case 8000: 290a571cb17SPeter Ujfalusi wclk_rate = TAS2552_WCLK_FREQ_8KHZ; 291a571cb17SPeter Ujfalusi break; 292a571cb17SPeter Ujfalusi case 11025: 293a571cb17SPeter Ujfalusi case 12000: 294a571cb17SPeter Ujfalusi wclk_rate = TAS2552_WCLK_FREQ_11_12KHZ; 295a571cb17SPeter Ujfalusi break; 296a571cb17SPeter Ujfalusi case 16000: 297a571cb17SPeter Ujfalusi wclk_rate = TAS2552_WCLK_FREQ_16KHZ; 298a571cb17SPeter Ujfalusi break; 299a571cb17SPeter Ujfalusi case 22050: 300a571cb17SPeter Ujfalusi case 24000: 301a571cb17SPeter Ujfalusi wclk_rate = TAS2552_WCLK_FREQ_22_24KHZ; 302a571cb17SPeter Ujfalusi break; 303a571cb17SPeter Ujfalusi case 32000: 304a571cb17SPeter Ujfalusi wclk_rate = TAS2552_WCLK_FREQ_32KHZ; 305a571cb17SPeter Ujfalusi break; 306a571cb17SPeter Ujfalusi case 44100: 307a571cb17SPeter Ujfalusi case 48000: 308a571cb17SPeter Ujfalusi wclk_rate = TAS2552_WCLK_FREQ_44_48KHZ; 309a571cb17SPeter Ujfalusi break; 310a571cb17SPeter Ujfalusi case 88200: 311a571cb17SPeter Ujfalusi case 96000: 312a571cb17SPeter Ujfalusi wclk_rate = TAS2552_WCLK_FREQ_88_96KHZ; 313a571cb17SPeter Ujfalusi break; 314a571cb17SPeter Ujfalusi case 176400: 315a571cb17SPeter Ujfalusi case 192000: 316a571cb17SPeter Ujfalusi wclk_rate = TAS2552_WCLK_FREQ_176_192KHZ; 317a571cb17SPeter Ujfalusi break; 318a571cb17SPeter Ujfalusi default: 319a571cb17SPeter Ujfalusi dev_err(codec->dev, "Not supported sample rate: %d\n", 320a571cb17SPeter Ujfalusi params_rate(params)); 321a571cb17SPeter Ujfalusi return -EINVAL; 322a571cb17SPeter Ujfalusi } 323a571cb17SPeter Ujfalusi 324a571cb17SPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_CFG_3, TAS2552_WCLK_FREQ_MASK, 325a571cb17SPeter Ujfalusi wclk_rate); 326a571cb17SPeter Ujfalusi 3271014f7efSPeter Ujfalusi return tas2552_setup_pll(codec, params); 3285df7f71dSDan Murphy } 3295df7f71dSDan Murphy 3301b68c7dcSPeter Ujfalusi #define TAS2552_DAI_FMT_MASK (TAS2552_BCLKDIR | \ 3311b68c7dcSPeter Ujfalusi TAS2552_WCLKDIR | \ 3321b68c7dcSPeter Ujfalusi TAS2552_DATAFORMAT_MASK) 3333f747a81SPeter Ujfalusi static int tas2552_prepare(struct snd_pcm_substream *substream, 3343f747a81SPeter Ujfalusi struct snd_soc_dai *dai) 3353f747a81SPeter Ujfalusi { 3363f747a81SPeter Ujfalusi struct snd_soc_codec *codec = dai->codec; 3373f747a81SPeter Ujfalusi struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); 3383f747a81SPeter Ujfalusi int delay = 0; 3393f747a81SPeter Ujfalusi 3403f747a81SPeter Ujfalusi /* TDM slot selection only valid in DSP_A/_B mode */ 3413f747a81SPeter Ujfalusi if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_A) 3423f747a81SPeter Ujfalusi delay += (tas2552->tdm_delay + 1); 3433f747a81SPeter Ujfalusi else if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_B) 3443f747a81SPeter Ujfalusi delay += tas2552->tdm_delay; 3453f747a81SPeter Ujfalusi 3463f747a81SPeter Ujfalusi /* Configure data delay */ 3473f747a81SPeter Ujfalusi snd_soc_write(codec, TAS2552_SER_CTRL_2, delay); 3483f747a81SPeter Ujfalusi 3493f747a81SPeter Ujfalusi return 0; 3503f747a81SPeter Ujfalusi } 3513f747a81SPeter Ujfalusi 3525df7f71dSDan Murphy static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) 3535df7f71dSDan Murphy { 3545df7f71dSDan Murphy struct snd_soc_codec *codec = dai->codec; 3553f747a81SPeter Ujfalusi struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); 3565df7f71dSDan Murphy u8 serial_format; 3575df7f71dSDan Murphy 3585df7f71dSDan Murphy switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 3595df7f71dSDan Murphy case SND_SOC_DAIFMT_CBS_CFS: 3605df7f71dSDan Murphy serial_format = 0x00; 3615df7f71dSDan Murphy break; 3625df7f71dSDan Murphy case SND_SOC_DAIFMT_CBS_CFM: 3631b68c7dcSPeter Ujfalusi serial_format = TAS2552_WCLKDIR; 3645df7f71dSDan Murphy break; 3655df7f71dSDan Murphy case SND_SOC_DAIFMT_CBM_CFS: 3661b68c7dcSPeter Ujfalusi serial_format = TAS2552_BCLKDIR; 3675df7f71dSDan Murphy break; 3685df7f71dSDan Murphy case SND_SOC_DAIFMT_CBM_CFM: 3691b68c7dcSPeter Ujfalusi serial_format = (TAS2552_BCLKDIR | TAS2552_WCLKDIR); 3705df7f71dSDan Murphy break; 3715df7f71dSDan Murphy default: 3725df7f71dSDan Murphy dev_vdbg(codec->dev, "DAI Format master is not found\n"); 3735df7f71dSDan Murphy return -EINVAL; 3745df7f71dSDan Murphy } 3755df7f71dSDan Murphy 3764c331373SPeter Ujfalusi switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK | 3774c331373SPeter Ujfalusi SND_SOC_DAIFMT_INV_MASK)) { 3784c331373SPeter Ujfalusi case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF): 3795df7f71dSDan Murphy break; 3804c331373SPeter Ujfalusi case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF): 3814c331373SPeter Ujfalusi case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF): 3821b68c7dcSPeter Ujfalusi serial_format |= TAS2552_DATAFORMAT_DSP; 3835df7f71dSDan Murphy break; 3844c331373SPeter Ujfalusi case (SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_NB_NF): 3851b68c7dcSPeter Ujfalusi serial_format |= TAS2552_DATAFORMAT_RIGHT_J; 3865df7f71dSDan Murphy break; 3874c331373SPeter Ujfalusi case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF): 3881b68c7dcSPeter Ujfalusi serial_format |= TAS2552_DATAFORMAT_LEFT_J; 3895df7f71dSDan Murphy break; 3905df7f71dSDan Murphy default: 3915df7f71dSDan Murphy dev_vdbg(codec->dev, "DAI Format is not found\n"); 3925df7f71dSDan Murphy return -EINVAL; 3935df7f71dSDan Murphy } 3943f747a81SPeter Ujfalusi tas2552->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; 3955df7f71dSDan Murphy 3964c331373SPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, TAS2552_DAI_FMT_MASK, 3975df7f71dSDan Murphy serial_format); 3985df7f71dSDan Murphy return 0; 3995df7f71dSDan Murphy } 4005df7f71dSDan Murphy 4015df7f71dSDan Murphy static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, 4025df7f71dSDan Murphy unsigned int freq, int dir) 4035df7f71dSDan Murphy { 4045df7f71dSDan Murphy struct snd_soc_codec *codec = dai->codec; 4055df7f71dSDan Murphy struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); 4069d87a888SPeter Ujfalusi u8 reg, mask, val; 4075df7f71dSDan Murphy 4089d87a888SPeter Ujfalusi switch (clk_id) { 4099d87a888SPeter Ujfalusi case TAS2552_PLL_CLKIN_MCLK: 4109d87a888SPeter Ujfalusi case TAS2552_PLL_CLKIN_IVCLKIN: 4111014f7efSPeter Ujfalusi if (freq < 512000 || freq > 24576000) { 4121014f7efSPeter Ujfalusi /* out of range PLL_CLKIN, fall back to use BCLK */ 4131014f7efSPeter Ujfalusi dev_warn(codec->dev, "Out of range PLL_CLKIN: %u\n", 4141014f7efSPeter Ujfalusi freq); 4151014f7efSPeter Ujfalusi clk_id = TAS2552_PLL_CLKIN_BCLK; 4161014f7efSPeter Ujfalusi freq = 0; 4171014f7efSPeter Ujfalusi } 4181014f7efSPeter Ujfalusi /* fall through */ 4191014f7efSPeter Ujfalusi case TAS2552_PLL_CLKIN_BCLK: 4209d87a888SPeter Ujfalusi case TAS2552_PLL_CLKIN_1_8_FIXED: 4219d87a888SPeter Ujfalusi mask = TAS2552_PLL_SRC_MASK; 4229d87a888SPeter Ujfalusi val = (clk_id << 3) & mask; /* bit 4:5 in the register */ 4239d87a888SPeter Ujfalusi reg = TAS2552_CFG_1; 4241014f7efSPeter Ujfalusi tas2552->pll_clk_id = clk_id; 42516bd3952SPeter Ujfalusi tas2552->pll_clkin = freq; 4269d87a888SPeter Ujfalusi break; 4279d87a888SPeter Ujfalusi case TAS2552_PDM_CLK_PLL: 4289d87a888SPeter Ujfalusi case TAS2552_PDM_CLK_IVCLKIN: 4299d87a888SPeter Ujfalusi case TAS2552_PDM_CLK_BCLK: 4309d87a888SPeter Ujfalusi case TAS2552_PDM_CLK_MCLK: 4319d87a888SPeter Ujfalusi mask = TAS2552_PDM_CLK_SEL_MASK; 4329d87a888SPeter Ujfalusi val = (clk_id >> 1) & mask; /* bit 0:1 in the register */ 4339d87a888SPeter Ujfalusi reg = TAS2552_PDM_CFG; 4341014f7efSPeter Ujfalusi tas2552->pdm_clk_id = clk_id; 4359d87a888SPeter Ujfalusi tas2552->pdm_clk = freq; 4369d87a888SPeter Ujfalusi break; 4379d87a888SPeter Ujfalusi default: 4389d87a888SPeter Ujfalusi dev_err(codec->dev, "Invalid clk id: %d\n", clk_id); 4399d87a888SPeter Ujfalusi return -EINVAL; 4409d87a888SPeter Ujfalusi } 4419d87a888SPeter Ujfalusi 4429d87a888SPeter Ujfalusi snd_soc_update_bits(codec, reg, mask, val); 4435df7f71dSDan Murphy 4445df7f71dSDan Murphy return 0; 4455df7f71dSDan Murphy } 4465df7f71dSDan Murphy 4473f747a81SPeter Ujfalusi static int tas2552_set_dai_tdm_slot(struct snd_soc_dai *dai, 4483f747a81SPeter Ujfalusi unsigned int tx_mask, unsigned int rx_mask, 4493f747a81SPeter Ujfalusi int slots, int slot_width) 4503f747a81SPeter Ujfalusi { 4513f747a81SPeter Ujfalusi struct snd_soc_codec *codec = dai->codec; 4523f747a81SPeter Ujfalusi struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); 4533f747a81SPeter Ujfalusi unsigned int lsb; 4543f747a81SPeter Ujfalusi 4553f747a81SPeter Ujfalusi if (unlikely(!tx_mask)) { 4563f747a81SPeter Ujfalusi dev_err(codec->dev, "tx masks need to be non 0\n"); 4573f747a81SPeter Ujfalusi return -EINVAL; 4583f747a81SPeter Ujfalusi } 4593f747a81SPeter Ujfalusi 4603f747a81SPeter Ujfalusi /* TDM based on DSP mode requires slots to be adjacent */ 4613f747a81SPeter Ujfalusi lsb = __ffs(tx_mask); 4623f747a81SPeter Ujfalusi if ((lsb + 1) != __fls(tx_mask)) { 4633f747a81SPeter Ujfalusi dev_err(codec->dev, "Invalid mask, slots must be adjacent\n"); 4643f747a81SPeter Ujfalusi return -EINVAL; 4653f747a81SPeter Ujfalusi } 4663f747a81SPeter Ujfalusi 4673f747a81SPeter Ujfalusi tas2552->tdm_delay = lsb * slot_width; 4683f747a81SPeter Ujfalusi 4693f747a81SPeter Ujfalusi /* DOUT in high-impedance on inactive bit clocks */ 4703f747a81SPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_DOUT, 4713f747a81SPeter Ujfalusi TAS2552_SDOUT_TRISTATE, TAS2552_SDOUT_TRISTATE); 4723f747a81SPeter Ujfalusi 4733f747a81SPeter Ujfalusi return 0; 4743f747a81SPeter Ujfalusi } 4753f747a81SPeter Ujfalusi 4765df7f71dSDan Murphy static int tas2552_mute(struct snd_soc_dai *dai, int mute) 4775df7f71dSDan Murphy { 478e3606aa4SPeter Ujfalusi u8 cfg1_reg = 0; 4795df7f71dSDan Murphy struct snd_soc_codec *codec = dai->codec; 4805df7f71dSDan Murphy 4815df7f71dSDan Murphy if (mute) 482e3606aa4SPeter Ujfalusi cfg1_reg |= TAS2552_MUTE; 4835df7f71dSDan Murphy 4847de544fdSPeter Ujfalusi snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE, cfg1_reg); 4855df7f71dSDan Murphy 4865df7f71dSDan Murphy return 0; 4875df7f71dSDan Murphy } 4885df7f71dSDan Murphy 489641d334bSRafael J. Wysocki #ifdef CONFIG_PM 4905df7f71dSDan Murphy static int tas2552_runtime_suspend(struct device *dev) 4915df7f71dSDan Murphy { 4925df7f71dSDan Murphy struct tas2552_data *tas2552 = dev_get_drvdata(dev); 4935df7f71dSDan Murphy 494dd6e3053SPeter Ujfalusi tas2552_sw_shutdown(tas2552, 1); 4955df7f71dSDan Murphy 4965df7f71dSDan Murphy regcache_cache_only(tas2552->regmap, true); 4975df7f71dSDan Murphy regcache_mark_dirty(tas2552->regmap); 4985df7f71dSDan Murphy 499e295a4a4SDan Murphy gpiod_set_value(tas2552->enable_gpio, 0); 500e295a4a4SDan Murphy 5015df7f71dSDan Murphy return 0; 5025df7f71dSDan Murphy } 5035df7f71dSDan Murphy 5045df7f71dSDan Murphy static int tas2552_runtime_resume(struct device *dev) 5055df7f71dSDan Murphy { 5065df7f71dSDan Murphy struct tas2552_data *tas2552 = dev_get_drvdata(dev); 5075df7f71dSDan Murphy 5085df7f71dSDan Murphy gpiod_set_value(tas2552->enable_gpio, 1); 5095df7f71dSDan Murphy 510dd6e3053SPeter Ujfalusi tas2552_sw_shutdown(tas2552, 0); 5115df7f71dSDan Murphy 5125df7f71dSDan Murphy regcache_cache_only(tas2552->regmap, false); 5135df7f71dSDan Murphy regcache_sync(tas2552->regmap); 5145df7f71dSDan Murphy 5155df7f71dSDan Murphy return 0; 5165df7f71dSDan Murphy } 5175df7f71dSDan Murphy #endif 5185df7f71dSDan Murphy 5195df7f71dSDan Murphy static const struct dev_pm_ops tas2552_pm = { 5205df7f71dSDan Murphy SET_RUNTIME_PM_OPS(tas2552_runtime_suspend, tas2552_runtime_resume, 5215df7f71dSDan Murphy NULL) 5225df7f71dSDan Murphy }; 5235df7f71dSDan Murphy 52464793047SAxel Lin static const struct snd_soc_dai_ops tas2552_speaker_dai_ops = { 5255df7f71dSDan Murphy .hw_params = tas2552_hw_params, 5263f747a81SPeter Ujfalusi .prepare = tas2552_prepare, 5275df7f71dSDan Murphy .set_sysclk = tas2552_set_dai_sysclk, 5285df7f71dSDan Murphy .set_fmt = tas2552_set_dai_fmt, 5293f747a81SPeter Ujfalusi .set_tdm_slot = tas2552_set_dai_tdm_slot, 5305df7f71dSDan Murphy .digital_mute = tas2552_mute, 5315df7f71dSDan Murphy }; 5325df7f71dSDan Murphy 5335df7f71dSDan Murphy /* Formats supported by TAS2552 driver. */ 5345df7f71dSDan Murphy #define TAS2552_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ 5355df7f71dSDan Murphy SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) 5365df7f71dSDan Murphy 5375df7f71dSDan Murphy /* TAS2552 dai structure. */ 5385df7f71dSDan Murphy static struct snd_soc_dai_driver tas2552_dai[] = { 5395df7f71dSDan Murphy { 5405df7f71dSDan Murphy .name = "tas2552-amplifier", 5415df7f71dSDan Murphy .playback = { 542a7a8e994SDan Murphy .stream_name = "Playback", 5435df7f71dSDan Murphy .channels_min = 2, 5445df7f71dSDan Murphy .channels_max = 2, 5455df7f71dSDan Murphy .rates = SNDRV_PCM_RATE_8000_192000, 5465df7f71dSDan Murphy .formats = TAS2552_FORMATS, 5475df7f71dSDan Murphy }, 5485df7f71dSDan Murphy .ops = &tas2552_speaker_dai_ops, 5495df7f71dSDan Murphy }, 5505df7f71dSDan Murphy }; 5515df7f71dSDan Murphy 5525df7f71dSDan Murphy /* 5535df7f71dSDan Murphy * DAC digital volumes. From -7 to 24 dB in 1 dB steps 5545df7f71dSDan Murphy */ 555e2600460SAndreas Dannenberg static DECLARE_TLV_DB_SCALE(dac_tlv, -700, 100, 0); 5565df7f71dSDan Murphy 5572962cb52SPeter Ujfalusi static const char * const tas2552_din_source_select[] = { 5582962cb52SPeter Ujfalusi "Muted", 5592962cb52SPeter Ujfalusi "Left", 5602962cb52SPeter Ujfalusi "Right", 5612962cb52SPeter Ujfalusi "Left + Right average", 5622962cb52SPeter Ujfalusi }; 5632962cb52SPeter Ujfalusi static SOC_ENUM_SINGLE_DECL(tas2552_din_source_enum, 5642962cb52SPeter Ujfalusi TAS2552_CFG_3, 3, 5652962cb52SPeter Ujfalusi tas2552_din_source_select); 5662962cb52SPeter Ujfalusi 5675df7f71dSDan Murphy static const struct snd_kcontrol_new tas2552_snd_controls[] = { 5685df7f71dSDan Murphy SOC_SINGLE_TLV("Speaker Driver Playback Volume", 569dd6ae3bcSPeter Ujfalusi TAS2552_PGA_GAIN, 0, 0x1f, 0, dac_tlv), 5702962cb52SPeter Ujfalusi SOC_ENUM("DIN source", tas2552_din_source_enum), 5715df7f71dSDan Murphy }; 5725df7f71dSDan Murphy 5735df7f71dSDan Murphy static int tas2552_codec_probe(struct snd_soc_codec *codec) 5745df7f71dSDan Murphy { 5755df7f71dSDan Murphy struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); 5765df7f71dSDan Murphy int ret; 5775df7f71dSDan Murphy 5785df7f71dSDan Murphy tas2552->codec = codec; 5795df7f71dSDan Murphy 5805df7f71dSDan Murphy ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies), 5815df7f71dSDan Murphy tas2552->supplies); 5825df7f71dSDan Murphy 5835df7f71dSDan Murphy if (ret != 0) { 5845df7f71dSDan Murphy dev_err(codec->dev, "Failed to enable supplies: %d\n", 5855df7f71dSDan Murphy ret); 5865df7f71dSDan Murphy return ret; 5875df7f71dSDan Murphy } 5885df7f71dSDan Murphy 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 gpiod_set_value(tas2552->enable_gpio, 0); 6145df7f71dSDan Murphy 6155df7f71dSDan Murphy regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies), 6165df7f71dSDan Murphy tas2552->supplies); 6176f2daf82SFabio Estevam return ret; 6185df7f71dSDan Murphy } 6195df7f71dSDan Murphy 6205df7f71dSDan Murphy static int tas2552_codec_remove(struct snd_soc_codec *codec) 6215df7f71dSDan Murphy { 6225df7f71dSDan Murphy struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); 6235df7f71dSDan Murphy 624e295a4a4SDan Murphy pm_runtime_put(codec->dev); 625e295a4a4SDan Murphy 6265df7f71dSDan Murphy gpiod_set_value(tas2552->enable_gpio, 0); 6275df7f71dSDan Murphy 6285df7f71dSDan Murphy return 0; 6295df7f71dSDan Murphy }; 6305df7f71dSDan Murphy 6315df7f71dSDan Murphy #ifdef CONFIG_PM 6325df7f71dSDan Murphy static int tas2552_suspend(struct snd_soc_codec *codec) 6335df7f71dSDan Murphy { 6345df7f71dSDan Murphy struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); 6355df7f71dSDan Murphy int ret; 6365df7f71dSDan Murphy 6375df7f71dSDan Murphy ret = regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies), 6385df7f71dSDan Murphy tas2552->supplies); 6395df7f71dSDan Murphy 6405df7f71dSDan Murphy if (ret != 0) 6415df7f71dSDan Murphy dev_err(codec->dev, "Failed to disable supplies: %d\n", 6425df7f71dSDan Murphy ret); 64312dc0f3bSFabio Estevam return ret; 6445df7f71dSDan Murphy } 6455df7f71dSDan Murphy 6465df7f71dSDan Murphy static int tas2552_resume(struct snd_soc_codec *codec) 6475df7f71dSDan Murphy { 6485df7f71dSDan Murphy struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); 6495df7f71dSDan Murphy int ret; 6505df7f71dSDan Murphy 6515df7f71dSDan Murphy ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies), 6525df7f71dSDan Murphy tas2552->supplies); 6535df7f71dSDan Murphy 6545df7f71dSDan Murphy if (ret != 0) { 6555df7f71dSDan Murphy dev_err(codec->dev, "Failed to enable supplies: %d\n", 6565df7f71dSDan Murphy ret); 6575df7f71dSDan Murphy } 6585df7f71dSDan Murphy 65912dc0f3bSFabio Estevam return ret; 6605df7f71dSDan Murphy } 6615df7f71dSDan Murphy #else 6625df7f71dSDan Murphy #define tas2552_suspend NULL 6635df7f71dSDan Murphy #define tas2552_resume NULL 6645df7f71dSDan Murphy #endif 6655df7f71dSDan Murphy 6665df7f71dSDan Murphy static struct snd_soc_codec_driver soc_codec_dev_tas2552 = { 6675df7f71dSDan Murphy .probe = tas2552_codec_probe, 6685df7f71dSDan Murphy .remove = tas2552_codec_remove, 6695df7f71dSDan Murphy .suspend = tas2552_suspend, 6705df7f71dSDan Murphy .resume = tas2552_resume, 6717d785025SPeter Ujfalusi .ignore_pmdown_time = true, 6727d785025SPeter Ujfalusi 673e5538659SKuninori Morimoto .component_driver = { 6745df7f71dSDan Murphy .controls = tas2552_snd_controls, 6755df7f71dSDan Murphy .num_controls = ARRAY_SIZE(tas2552_snd_controls), 676e3f1ff31SLars-Peter Clausen .dapm_widgets = tas2552_dapm_widgets, 677e3f1ff31SLars-Peter Clausen .num_dapm_widgets = ARRAY_SIZE(tas2552_dapm_widgets), 678e3f1ff31SLars-Peter Clausen .dapm_routes = tas2552_audio_map, 679e3f1ff31SLars-Peter Clausen .num_dapm_routes = ARRAY_SIZE(tas2552_audio_map), 680e5538659SKuninori Morimoto }, 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 7068604bc28SAxel Lin data->enable_gpio = devm_gpiod_get_optional(dev, "enable", 7078604bc28SAxel Lin GPIOD_OUT_LOW); 7088604bc28SAxel Lin if (IS_ERR(data->enable_gpio)) 7098604bc28SAxel Lin return PTR_ERR(data->enable_gpio); 7105df7f71dSDan Murphy 7115df7f71dSDan Murphy data->tas2552_client = client; 7125df7f71dSDan Murphy data->regmap = devm_regmap_init_i2c(client, &tas2552_regmap_config); 7135df7f71dSDan Murphy if (IS_ERR(data->regmap)) { 7145df7f71dSDan Murphy ret = PTR_ERR(data->regmap); 7155df7f71dSDan Murphy dev_err(&client->dev, "Failed to allocate register map: %d\n", 7165df7f71dSDan Murphy ret); 7175df7f71dSDan Murphy return ret; 7185df7f71dSDan Murphy } 7195df7f71dSDan Murphy 7205df7f71dSDan Murphy for (i = 0; i < ARRAY_SIZE(data->supplies); i++) 7215df7f71dSDan Murphy data->supplies[i].supply = tas2552_supply_names[i]; 7225df7f71dSDan Murphy 7235df7f71dSDan Murphy ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies), 7245df7f71dSDan Murphy data->supplies); 725c62f9d8fSAxel Lin if (ret != 0) { 7265df7f71dSDan Murphy dev_err(dev, "Failed to request supplies: %d\n", ret); 727c62f9d8fSAxel Lin return ret; 728c62f9d8fSAxel Lin } 7295df7f71dSDan Murphy 7305df7f71dSDan Murphy pm_runtime_set_active(&client->dev); 7315df7f71dSDan Murphy pm_runtime_set_autosuspend_delay(&client->dev, 1000); 7325df7f71dSDan Murphy pm_runtime_use_autosuspend(&client->dev); 7335df7f71dSDan Murphy pm_runtime_enable(&client->dev); 7345df7f71dSDan Murphy pm_runtime_mark_last_busy(&client->dev); 7355df7f71dSDan Murphy pm_runtime_put_sync_autosuspend(&client->dev); 7365df7f71dSDan Murphy 7375df7f71dSDan Murphy dev_set_drvdata(&client->dev, data); 7385df7f71dSDan Murphy 7395df7f71dSDan Murphy ret = snd_soc_register_codec(&client->dev, 7405df7f71dSDan Murphy &soc_codec_dev_tas2552, 7415df7f71dSDan Murphy tas2552_dai, ARRAY_SIZE(tas2552_dai)); 7425df7f71dSDan Murphy if (ret < 0) 7435df7f71dSDan Murphy dev_err(&client->dev, "Failed to register codec: %d\n", ret); 7445df7f71dSDan Murphy 745c62f9d8fSAxel Lin return ret; 7465df7f71dSDan Murphy } 7475df7f71dSDan Murphy 7485df7f71dSDan Murphy static int tas2552_i2c_remove(struct i2c_client *client) 7495df7f71dSDan Murphy { 7505df7f71dSDan Murphy snd_soc_unregister_codec(&client->dev); 7514785ed89SPeter Ujfalusi pm_runtime_disable(&client->dev); 7525df7f71dSDan Murphy return 0; 7535df7f71dSDan Murphy } 7545df7f71dSDan Murphy 7555df7f71dSDan Murphy static const struct i2c_device_id tas2552_id[] = { 7565df7f71dSDan Murphy { "tas2552", 0 }, 7575df7f71dSDan Murphy { } 7585df7f71dSDan Murphy }; 7595df7f71dSDan Murphy MODULE_DEVICE_TABLE(i2c, tas2552_id); 7605df7f71dSDan Murphy 7615df7f71dSDan Murphy #if IS_ENABLED(CONFIG_OF) 7625df7f71dSDan Murphy static const struct of_device_id tas2552_of_match[] = { 7635df7f71dSDan Murphy { .compatible = "ti,tas2552", }, 7645df7f71dSDan Murphy {}, 7655df7f71dSDan Murphy }; 7665df7f71dSDan Murphy MODULE_DEVICE_TABLE(of, tas2552_of_match); 7675df7f71dSDan Murphy #endif 7685df7f71dSDan Murphy 7695df7f71dSDan Murphy static struct i2c_driver tas2552_i2c_driver = { 7705df7f71dSDan Murphy .driver = { 7715df7f71dSDan Murphy .name = "tas2552", 7725df7f71dSDan Murphy .of_match_table = of_match_ptr(tas2552_of_match), 7735df7f71dSDan Murphy .pm = &tas2552_pm, 7745df7f71dSDan Murphy }, 7755df7f71dSDan Murphy .probe = tas2552_probe, 7765df7f71dSDan Murphy .remove = tas2552_i2c_remove, 7775df7f71dSDan Murphy .id_table = tas2552_id, 7785df7f71dSDan Murphy }; 7795df7f71dSDan Murphy 7805df7f71dSDan Murphy module_i2c_driver(tas2552_i2c_driver); 7815df7f71dSDan Murphy 7825df7f71dSDan Murphy MODULE_AUTHOR("Dan Muprhy <dmurphy@ti.com>"); 7835df7f71dSDan Murphy MODULE_DESCRIPTION("TAS2552 Audio amplifier driver"); 7845df7f71dSDan Murphy MODULE_LICENSE("GPL"); 785