diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/codecs/Kconfig | 5 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/codecs/sta350.c | 4 | ||||
-rw-r--r-- | sound/soc/codecs/tas2552.c | 312 | ||||
-rw-r--r-- | sound/soc/codecs/tas2552.h | 90 | ||||
-rw-r--r-- | sound/soc/codecs/tas571x.c | 514 | ||||
-rw-r--r-- | sound/soc/codecs/tas571x.h | 33 | ||||
-rw-r--r-- | sound/soc/codecs/ts3a227e.c | 15 |
8 files changed, 852 insertions, 123 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index e0b375e..9b36011 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -104,6 +104,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_STAC9766 if SND_SOC_AC97_BUS select SND_SOC_TAS2552 if I2C select SND_SOC_TAS5086 if I2C + select SND_SOC_TAS571X if I2C select SND_SOC_TFA9879 if I2C select SND_SOC_TLV320AIC23_I2C if I2C select SND_SOC_TLV320AIC23_SPI if SPI_MASTER @@ -612,6 +613,10 @@ config SND_SOC_TAS5086 tristate "Texas Instruments TAS5086 speaker amplifier" depends on I2C +config SND_SOC_TAS571X + tristate "Texas Instruments TAS5711/TAS5717/TAS5719 power amplifiers" + depends on I2C + config SND_SOC_TFA9879 tristate "NXP Semiconductors TFA9879 amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index abe2d7e..3dcf5ac 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -106,6 +106,7 @@ snd-soc-sta350-objs := sta350.o snd-soc-sta529-objs := sta529.o snd-soc-stac9766-objs := stac9766.o snd-soc-tas5086-objs := tas5086.o +snd-soc-tas571x-objs := tas571x.o snd-soc-tfa9879-objs := tfa9879.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o @@ -288,6 +289,7 @@ obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o +obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c index 025f663..bd819a3 100644 --- a/sound/soc/codecs/sta350.c +++ b/sound/soc/codecs/sta350.c @@ -1217,8 +1217,8 @@ static int sta350_i2c_probe(struct i2c_client *i2c, if (IS_ERR(sta350->gpiod_nreset)) return PTR_ERR(sta350->gpiod_nreset); - sta350->gpiod_power_down = devm_gpiod_get(dev, "power-down", - GPIOD_OUT_LOW); + sta350->gpiod_power_down = devm_gpiod_get_optional(dev, "power-down", + GPIOD_OUT_LOW); if (IS_ERR(sta350->gpiod_power_down)) return PTR_ERR(sta350->gpiod_power_down); diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index dfb4ff5..891e2c5 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -34,6 +34,7 @@ #include <sound/soc-dapm.h> #include <sound/tlv.h> #include <sound/tas2552-plat.h> +#include <dt-bindings/sound/tas2552.h> #include "tas2552.h" @@ -45,7 +46,7 @@ static struct reg_default tas2552_reg_defs[] = { {TAS2552_PDM_CFG, 0x01}, {TAS2552_PGA_GAIN, 0x00}, {TAS2552_BOOST_PT_CTRL, 0x0f}, - {TAS2552_RESERVED_0D, 0x00}, + {TAS2552_RESERVED_0D, 0xbe}, {TAS2552_LIMIT_RATE_HYS, 0x08}, {TAS2552_CFG_2, 0xef}, {TAS2552_SER_CTRL_1, 0x00}, @@ -75,20 +76,45 @@ struct tas2552_data { struct regulator_bulk_data supplies[TAS2552_NUM_SUPPLIES]; struct gpio_desc *enable_gpio; unsigned char regs[TAS2552_VBAT_DATA]; - unsigned int mclk; -}; + unsigned int pll_clkin; + unsigned int pdm_clk; -/* Input mux controls */ -static const char *tas2552_input_texts[] = { - "Digital", "Analog" + unsigned int dai_fmt; + unsigned int tdm_delay; }; +static int tas2552_post_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_write(codec, TAS2552_RESERVED_0D, 0xc0); + snd_soc_update_bits(codec, TAS2552_LIMIT_RATE_HYS, (1 << 5), + (1 << 5)); + snd_soc_update_bits(codec, TAS2552_CFG_2, 1, 0); + snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_SWS, 0); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_SWS, + TAS2552_SWS); + snd_soc_update_bits(codec, TAS2552_CFG_2, 1, 1); + snd_soc_update_bits(codec, TAS2552_LIMIT_RATE_HYS, (1 << 5), 0); + snd_soc_write(codec, TAS2552_RESERVED_0D, 0xbe); + break; + } + return 0; +} + +/* Input mux controls */ +static const char * const tas2552_input_texts[] = { + "Digital", "Analog" }; static SOC_ENUM_SINGLE_DECL(tas2552_input_mux_enum, TAS2552_CFG_3, 7, tas2552_input_texts); -static const struct snd_kcontrol_new tas2552_input_mux_control[] = { - SOC_DAPM_ENUM("Input selection", tas2552_input_mux_enum) -}; +static const struct snd_kcontrol_new tas2552_input_mux_control = + SOC_DAPM_ENUM("Route", tas2552_input_mux_enum); static const struct snd_soc_dapm_widget tas2552_dapm_widgets[] = { @@ -96,12 +122,13 @@ static const struct snd_soc_dapm_widget tas2552_dapm_widgets[] = /* MUX Controls */ SND_SOC_DAPM_MUX("Input selection", SND_SOC_NOPM, 0, 0, - tas2552_input_mux_control), + &tas2552_input_mux_control), SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_OUT_DRV("ClassD", TAS2552_CFG_2, 7, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("PLL", TAS2552_CFG_2, 3, 0, NULL, 0), + SND_SOC_DAPM_POST("Post Event", tas2552_post_event), SND_SOC_DAPM_OUTPUT("OUT") }; @@ -118,15 +145,16 @@ static const struct snd_soc_dapm_route tas2552_audio_map[] = { #ifdef CONFIG_PM static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown) { - u8 cfg1_reg; + u8 cfg1_reg = 0; + + if (!tas_data->codec) + return; if (sw_shutdown) - cfg1_reg = 0; - else - cfg1_reg = TAS2552_SWS_MASK; + cfg1_reg = TAS2552_SWS; - snd_soc_update_bits(tas_data->codec, TAS2552_CFG_1, - TAS2552_SWS_MASK, cfg1_reg); + snd_soc_update_bits(tas_data->codec, TAS2552_CFG_1, TAS2552_SWS, + cfg1_reg); } #endif @@ -138,15 +166,92 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream, struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); int sample_rate, pll_clk; int d; + int cpf; u8 p, j; + u8 ser_ctrl1_reg, wclk_rate; - if (!tas2552->mclk) + switch (params_width(params)) { + case 16: + ser_ctrl1_reg = TAS2552_WORDLENGTH_16BIT; + cpf = 32 + tas2552->tdm_delay; + break; + case 20: + ser_ctrl1_reg = TAS2552_WORDLENGTH_20BIT; + cpf = 64 + tas2552->tdm_delay; + break; + case 24: + ser_ctrl1_reg = TAS2552_WORDLENGTH_24BIT; + cpf = 64 + tas2552->tdm_delay; + break; + case 32: + ser_ctrl1_reg = TAS2552_WORDLENGTH_32BIT; + cpf = 64 + tas2552->tdm_delay; + break; + default: + dev_err(codec->dev, "Not supported sample size: %d\n", + params_width(params)); + return -EINVAL; + } + + if (cpf <= 32) + ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_32; + else if (cpf <= 64) + ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_64; + else if (cpf <= 128) + ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_128; + else + ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_256; + + snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, + TAS2552_WORDLENGTH_MASK | TAS2552_CLKSPERFRAME_MASK, + ser_ctrl1_reg); + + switch (params_rate(params)) { + case 8000: + wclk_rate = TAS2552_WCLK_FREQ_8KHZ; + break; + case 11025: + case 12000: + wclk_rate = TAS2552_WCLK_FREQ_11_12KHZ; + break; + case 16000: + wclk_rate = TAS2552_WCLK_FREQ_16KHZ; + break; + case 22050: + case 24000: + wclk_rate = TAS2552_WCLK_FREQ_22_24KHZ; + break; + case 32000: + wclk_rate = TAS2552_WCLK_FREQ_32KHZ; + break; + case 44100: + case 48000: + wclk_rate = TAS2552_WCLK_FREQ_44_48KHZ; + break; + case 88200: + case 96000: + wclk_rate = TAS2552_WCLK_FREQ_88_96KHZ; + break; + case 176400: + case 192000: + wclk_rate = TAS2552_WCLK_FREQ_176_192KHZ; + break; + default: + dev_err(codec->dev, "Not supported sample rate: %d\n", + params_rate(params)); + return -EINVAL; + } + + snd_soc_update_bits(codec, TAS2552_CFG_3, TAS2552_WCLK_FREQ_MASK, + wclk_rate); + + if (!tas2552->pll_clkin) return -EINVAL; snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0); - if (tas2552->mclk == TAS2552_245MHZ_CLK || - tas2552->mclk == TAS2552_225MHZ_CLK) { + if (tas2552->pll_clkin == TAS2552_245MHZ_CLK || + tas2552->pll_clkin == TAS2552_225MHZ_CLK) { /* By pass the PLL configuration */ snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2, TAS2552_PLL_BYPASS_MASK, @@ -170,8 +275,8 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - j = (pll_clk * 2 * (1 << p)) / tas2552->mclk; - d = (pll_clk * 2 * (1 << p)) % tas2552->mclk; + j = (pll_clk * 2 * (1 << p)) / tas2552->pll_clkin; + d = (pll_clk * 2 * (1 << p)) % tas2552->pll_clkin; snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1, TAS2552_PLL_J_MASK, j); @@ -185,56 +290,74 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream, return 0; } +#define TAS2552_DAI_FMT_MASK (TAS2552_BCLKDIR | \ + TAS2552_WCLKDIR | \ + TAS2552_DATAFORMAT_MASK) +static int tas2552_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); + int delay = 0; + + /* TDM slot selection only valid in DSP_A/_B mode */ + if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_A) + delay += (tas2552->tdm_delay + 1); + else if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_B) + delay += tas2552->tdm_delay; + + /* Configure data delay */ + snd_soc_write(codec, TAS2552_SER_CTRL_2, delay); + + return 0; +} + static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_codec *codec = dai->codec; + struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); u8 serial_format; - u8 serial_control_mask; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: serial_format = 0x00; break; case SND_SOC_DAIFMT_CBS_CFM: - serial_format = TAS2552_WORD_CLK_MASK; + serial_format = TAS2552_WCLKDIR; break; case SND_SOC_DAIFMT_CBM_CFS: - serial_format = TAS2552_BIT_CLK_MASK; + serial_format = TAS2552_BCLKDIR; break; case SND_SOC_DAIFMT_CBM_CFM: - serial_format = (TAS2552_BIT_CLK_MASK | TAS2552_WORD_CLK_MASK); + serial_format = (TAS2552_BCLKDIR | TAS2552_WCLKDIR); break; default: dev_vdbg(codec->dev, "DAI Format master is not found\n"); return -EINVAL; } - serial_control_mask = TAS2552_BIT_CLK_MASK | TAS2552_WORD_CLK_MASK; - - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - serial_format &= TAS2552_DAIFMT_I2S_MASK; + switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK | + SND_SOC_DAIFMT_INV_MASK)) { + case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF): break; - case SND_SOC_DAIFMT_DSP_A: - serial_format |= TAS2552_DAIFMT_DSP; + case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF): + case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF): + serial_format |= TAS2552_DATAFORMAT_DSP; break; - case SND_SOC_DAIFMT_RIGHT_J: - serial_format |= TAS2552_DAIFMT_RIGHT_J; + case (SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_NB_NF): + serial_format |= TAS2552_DATAFORMAT_RIGHT_J; break; - case SND_SOC_DAIFMT_LEFT_J: - serial_format |= TAS2552_DAIFMT_LEFT_J; + case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF): + serial_format |= TAS2552_DATAFORMAT_LEFT_J; break; default: dev_vdbg(codec->dev, "DAI Format is not found\n"); return -EINVAL; } + tas2552->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; - if (fmt & SND_SOC_DAIFMT_FORMAT_MASK) - serial_control_mask |= TAS2552_DATA_FORMAT_MASK; - - snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, serial_control_mask, - serial_format); - + snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, TAS2552_DAI_FMT_MASK, + serial_format); return 0; } @@ -243,23 +366,75 @@ static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, { struct snd_soc_codec *codec = dai->codec; struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); + u8 reg, mask, val; + + switch (clk_id) { + case TAS2552_PLL_CLKIN_MCLK: + case TAS2552_PLL_CLKIN_BCLK: + case TAS2552_PLL_CLKIN_IVCLKIN: + case TAS2552_PLL_CLKIN_1_8_FIXED: + mask = TAS2552_PLL_SRC_MASK; + val = (clk_id << 3) & mask; /* bit 4:5 in the register */ + reg = TAS2552_CFG_1; + tas2552->pll_clkin = freq; + break; + case TAS2552_PDM_CLK_PLL: + case TAS2552_PDM_CLK_IVCLKIN: + case TAS2552_PDM_CLK_BCLK: + case TAS2552_PDM_CLK_MCLK: + mask = TAS2552_PDM_CLK_SEL_MASK; + val = (clk_id >> 1) & mask; /* bit 0:1 in the register */ + reg = TAS2552_PDM_CFG; + tas2552->pdm_clk = freq; + break; + default: + dev_err(codec->dev, "Invalid clk id: %d\n", clk_id); + return -EINVAL; + } + + snd_soc_update_bits(codec, reg, mask, val); + + return 0; +} + +static int tas2552_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_codec *codec = dai->codec; + struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); + unsigned int lsb; + + if (unlikely(!tx_mask)) { + dev_err(codec->dev, "tx masks need to be non 0\n"); + return -EINVAL; + } + + /* TDM based on DSP mode requires slots to be adjacent */ + lsb = __ffs(tx_mask); + if ((lsb + 1) != __fls(tx_mask)) { + dev_err(codec->dev, "Invalid mask, slots must be adjacent\n"); + return -EINVAL; + } + + tas2552->tdm_delay = lsb * slot_width; - tas2552->mclk = freq; + /* DOUT in high-impedance on inactive bit clocks */ + snd_soc_update_bits(codec, TAS2552_DOUT, + TAS2552_SDOUT_TRISTATE, TAS2552_SDOUT_TRISTATE); return 0; } static int tas2552_mute(struct snd_soc_dai *dai, int mute) { - u8 cfg1_reg; + u8 cfg1_reg = 0; struct snd_soc_codec *codec = dai->codec; if (mute) - cfg1_reg = TAS2552_MUTE_MASK; - else - cfg1_reg = ~TAS2552_MUTE_MASK; + cfg1_reg |= TAS2552_MUTE; - snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE_MASK, cfg1_reg); + snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE, cfg1_reg); return 0; } @@ -269,7 +444,7 @@ static int tas2552_runtime_suspend(struct device *dev) { struct tas2552_data *tas2552 = dev_get_drvdata(dev); - tas2552_sw_shutdown(tas2552, 0); + tas2552_sw_shutdown(tas2552, 1); regcache_cache_only(tas2552->regmap, true); regcache_mark_dirty(tas2552->regmap); @@ -287,7 +462,7 @@ static int tas2552_runtime_resume(struct device *dev) if (tas2552->enable_gpio) gpiod_set_value(tas2552->enable_gpio, 1); - tas2552_sw_shutdown(tas2552, 1); + tas2552_sw_shutdown(tas2552, 0); regcache_cache_only(tas2552->regmap, false); regcache_sync(tas2552->regmap); @@ -303,8 +478,10 @@ static const struct dev_pm_ops tas2552_pm = { static struct snd_soc_dai_ops tas2552_speaker_dai_ops = { .hw_params = tas2552_hw_params, + .prepare = tas2552_prepare, .set_sysclk = tas2552_set_dai_sysclk, .set_fmt = tas2552_set_dai_fmt, + .set_tdm_slot = tas2552_set_dai_tdm_slot, .digital_mute = tas2552_mute, }; @@ -330,16 +507,11 @@ static struct snd_soc_dai_driver tas2552_dai[] = { /* * DAC digital volumes. From -7 to 24 dB in 1 dB steps */ -static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 24); +static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 0); static const struct snd_kcontrol_new tas2552_snd_controls[] = { SOC_SINGLE_TLV("Speaker Driver Playback Volume", - TAS2552_PGA_GAIN, 0, 0x1f, 1, dac_tlv), - SOC_DAPM_SINGLE("Playback AMP", SND_SOC_NOPM, 0, 1, 0), -}; - -static const struct reg_default tas2552_init_regs[] = { - { TAS2552_RESERVED_0D, 0xc0 }, + TAS2552_PGA_GAIN, 0, 0x1f, 0, dac_tlv), }; static int tas2552_codec_probe(struct snd_soc_codec *codec) @@ -368,31 +540,19 @@ static int tas2552_codec_probe(struct snd_soc_codec *codec) goto probe_fail; } - snd_soc_write(codec, TAS2552_CFG_1, TAS2552_MUTE_MASK | - TAS2552_PLL_SRC_BCLK); + snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE, TAS2552_MUTE); snd_soc_write(codec, TAS2552_CFG_3, TAS2552_I2S_OUT_SEL | - TAS2552_DIN_SRC_SEL_AVG_L_R | TAS2552_88_96KHZ); + TAS2552_DIN_SRC_SEL_AVG_L_R); snd_soc_write(codec, TAS2552_DOUT, TAS2552_PDM_DATA_I); snd_soc_write(codec, TAS2552_OUTPUT_DATA, TAS2552_PDM_DATA_V_I | 0x8); - snd_soc_write(codec, TAS2552_PDM_CFG, TAS2552_PDM_BCLK_SEL); snd_soc_write(codec, TAS2552_BOOST_PT_CTRL, TAS2552_APT_DELAY_200 | TAS2552_APT_THRESH_2_1_7); - ret = regmap_register_patch(tas2552->regmap, tas2552_init_regs, - ARRAY_SIZE(tas2552_init_regs)); - if (ret != 0) { - dev_err(codec->dev, "Failed to write init registers: %d\n", - ret); - goto patch_fail; - } - snd_soc_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN | TAS2552_APT_EN | TAS2552_LIM_EN); return 0; -patch_fail: - pm_runtime_put(codec->dev); probe_fail: if (tas2552->enable_gpio) gpiod_set_value(tas2552->enable_gpio, 0); @@ -454,6 +614,8 @@ static struct snd_soc_codec_driver soc_codec_dev_tas2552 = { .remove = tas2552_codec_remove, .suspend = tas2552_suspend, .resume = tas2552_resume, + .ignore_pmdown_time = true, + .controls = tas2552_snd_controls, .num_controls = ARRAY_SIZE(tas2552_snd_controls), .dapm_widgets = tas2552_dapm_widgets, @@ -486,8 +648,12 @@ static int tas2552_probe(struct i2c_client *client, return -ENOMEM; data->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); - if (IS_ERR(data->enable_gpio)) - return PTR_ERR(data->enable_gpio); + if (IS_ERR(data->enable_gpio)) { + if (PTR_ERR(data->enable_gpio) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + data->enable_gpio = NULL;; + } data->tas2552_client = client; data->regmap = devm_regmap_init_i2c(client, &tas2552_regmap_config); diff --git a/sound/soc/codecs/tas2552.h b/sound/soc/codecs/tas2552.h index 6cea8f3..bbb8204 100644 --- a/sound/soc/codecs/tas2552.h +++ b/sound/soc/codecs/tas2552.h @@ -45,10 +45,14 @@ #define TAS2552_MAX_REG 0x20 /* CFG1 Register Masks */ -#define TAS2552_MUTE_MASK (1 << 2) -#define TAS2552_SWS_MASK (1 << 1) -#define TAS2552_WCLK_MASK 0x07 -#define TAS2552_CLASSD_EN_MASK (1 << 7) +#define TAS2552_DEV_RESET (1 << 0) +#define TAS2552_SWS (1 << 1) +#define TAS2552_MUTE (1 << 2) +#define TAS2552_PLL_SRC_MCLK (0x0 << 4) +#define TAS2552_PLL_SRC_BCLK (0x1 << 4) +#define TAS2552_PLL_SRC_IVCLKIN (0x2 << 4) +#define TAS2552_PLL_SRC_1_8_FIXED (0x3 << 4) +#define TAS2552_PLL_SRC_MASK TAS2552_PLL_SRC_1_8_FIXED /* CFG2 Register Masks */ #define TAS2552_CLASSD_EN (1 << 7) @@ -59,38 +63,44 @@ #define TAS2552_IVSENSE_EN (1 << 1) /* CFG3 Register Masks */ -#define TAS2552_WORD_CLK_MASK (1 << 7) -#define TAS2552_BIT_CLK_MASK (1 << 6) -#define TAS2552_DATA_FORMAT_MASK (0x11 << 2) - -#define TAS2552_DAIFMT_I2S_MASK 0xf3 -#define TAS2552_DAIFMT_DSP (1 << 3) -#define TAS2552_DAIFMT_RIGHT_J (1 << 4) -#define TAS2552_DAIFMT_LEFT_J (0x11 << 3) - -#define TAS2552_PLL_SRC_MCLK 0x00 -#define TAS2552_PLL_SRC_BCLK (1 << 3) -#define TAS2552_PLL_SRC_IVCLKIN (1 << 4) -#define TAS2552_PLL_SRC_1_8_FIXED (0x11 << 3) - -#define TAS2552_DIN_SRC_SEL_MUTED 0x00 -#define TAS2552_DIN_SRC_SEL_LEFT (1 << 4) -#define TAS2552_DIN_SRC_SEL_RIGHT (1 << 5) -#define TAS2552_DIN_SRC_SEL_AVG_L_R (0x11 << 4) - +#define TAS2552_WCLK_FREQ_8KHZ (0x0 << 0) +#define TAS2552_WCLK_FREQ_11_12KHZ (0x1 << 0) +#define TAS2552_WCLK_FREQ_16KHZ (0x2 << 0) +#define TAS2552_WCLK_FREQ_22_24KHZ (0x3 << 0) +#define TAS2552_WCLK_FREQ_32KHZ (0x4 << 0) +#define TAS2552_WCLK_FREQ_44_48KHZ (0x5 << 0) +#define TAS2552_WCLK_FREQ_88_96KHZ (0x6 << 0) +#define TAS2552_WCLK_FREQ_176_192KHZ (0x7 << 0) +#define TAS2552_WCLK_FREQ_MASK TAS2552_WCLK_FREQ_176_192KHZ +#define TAS2552_DIN_SRC_SEL_MUTED (0x0 << 3) +#define TAS2552_DIN_SRC_SEL_LEFT (0x1 << 3) +#define TAS2552_DIN_SRC_SEL_RIGHT (0x2 << 3) +#define TAS2552_DIN_SRC_SEL_AVG_L_R (0x3 << 3) #define TAS2552_PDM_IN_SEL (1 << 5) #define TAS2552_I2S_OUT_SEL (1 << 6) -#define TAS2552_ANALOG_IN_SEL (1 << 7) - -/* CFG3 WCLK Dividers */ -#define TAS2552_8KHZ 0x00 -#define TAS2552_11_12KHZ (1 << 1) -#define TAS2552_16KHZ (1 << 2) -#define TAS2552_22_24KHZ (1 << 3) -#define TAS2552_32KHZ (1 << 4) -#define TAS2552_44_48KHZ (1 << 5) -#define TAS2552_88_96KHZ (1 << 6) -#define TAS2552_176_192KHZ (1 << 7) +#define TAS2552_ANALOG_IN_SEL (1 << 7) + +/* DOUT Register Masks */ +#define TAS2552_SDOUT_TRISTATE (1 << 2) + +/* Serial Interface Control Register Masks */ +#define TAS2552_WORDLENGTH_16BIT (0x0 << 0) +#define TAS2552_WORDLENGTH_20BIT (0x1 << 0) +#define TAS2552_WORDLENGTH_24BIT (0x2 << 0) +#define TAS2552_WORDLENGTH_32BIT (0x3 << 0) +#define TAS2552_WORDLENGTH_MASK TAS2552_WORDLENGTH_32BIT +#define TAS2552_DATAFORMAT_I2S (0x0 << 2) +#define TAS2552_DATAFORMAT_DSP (0x1 << 2) +#define TAS2552_DATAFORMAT_RIGHT_J (0x2 << 2) +#define TAS2552_DATAFORMAT_LEFT_J (0x3 << 2) +#define TAS2552_DATAFORMAT_MASK TAS2552_DATAFORMAT_LEFT_J +#define TAS2552_CLKSPERFRAME_32 (0x0 << 4) +#define TAS2552_CLKSPERFRAME_64 (0x1 << 4) +#define TAS2552_CLKSPERFRAME_128 (0x2 << 4) +#define TAS2552_CLKSPERFRAME_256 (0x3 << 4) +#define TAS2552_CLKSPERFRAME_MASK TAS2552_CLKSPERFRAME_256 +#define TAS2552_BCLKDIR (1 << 6) +#define TAS2552_WCLKDIR (1 << 7) /* OUTPUT_DATA register */ #define TAS2552_PDM_DATA_I 0x00 @@ -99,12 +109,12 @@ #define TAS2552_PDM_DATA_V_I (0x11 << 6) /* PDM CFG Register */ -#define TAS2552_PDM_DATA_ES_RISE 0x4 - -#define TAS2552_PDM_PLL_CLK_SEL 0x00 -#define TAS2552_PDM_IV_CLK_SEL (1 << 1) -#define TAS2552_PDM_BCLK_SEL (1 << 2) -#define TAS2552_PDM_MCLK_SEL (1 << 3) +#define TAS2552_PDM_CLK_SEL_PLL (0x0 << 0) +#define TAS2552_PDM_CLK_SEL_IVCLKIN (0x1 << 0) +#define TAS2552_PDM_CLK_SEL_BCLK (0x2 << 0) +#define TAS2552_PDM_CLK_SEL_MCLK (0x3 << 0) +#define TAS2552_PDM_CLK_SEL_MASK TAS2552_PDM_CLK_SEL_MCLK +#define TAS2552_PDM_DATA_ES (1 << 2) /* Boost pass-through register */ #define TAS2552_APT_DELAY_50 0x00 diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c new file mode 100644 index 0000000..85bcc37 --- /dev/null +++ b/sound/soc/codecs/tas571x.c @@ -0,0 +1,514 @@ +/* + * TAS571x amplifier audio driver + * + * Copyright (C) 2015 Google, Inc. + * Copyright (c) 2013 Daniel Mack <zonque@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/stddef.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#include "tas571x.h" + +#define TAS571X_MAX_SUPPLIES 6 + +struct tas571x_chip { + const char *const *supply_names; + int num_supply_names; + const struct snd_kcontrol_new *controls; + int num_controls; + const struct regmap_config *regmap_config; + int vol_reg_size; +}; + +struct tas571x_private { + const struct tas571x_chip *chip; + struct regmap *regmap; + struct regulator_bulk_data supplies[TAS571X_MAX_SUPPLIES]; + struct clk *mclk; + unsigned int format; + struct gpio_desc *reset_gpio; + struct gpio_desc *pdn_gpio; + struct snd_soc_codec_driver codec_driver; +}; + +static int tas571x_register_size(struct tas571x_private *priv, unsigned int reg) +{ + switch (reg) { + case TAS571X_MVOL_REG: + case TAS571X_CH1_VOL_REG: + case TAS571X_CH2_VOL_REG: + return priv->chip->vol_reg_size; + default: + return 1; + } +} + +static int tas571x_reg_write(void *context, unsigned int reg, + unsigned int value) +{ + struct i2c_client *client = context; + struct tas571x_private *priv = i2c_get_clientdata(client); + unsigned int i, size; + uint8_t buf[5]; + int ret; + + size = tas571x_register_size(priv, reg); + buf[0] = reg; + + for (i = size; i >= 1; --i) { + buf[i] = value; + value >>= 8; + } + + ret = i2c_master_send(client, buf, size + 1); + if (ret == size + 1) + return 0; + else if (ret < 0) + return ret; + else + return -EIO; +} + +static int tas571x_reg_read(void *context, unsigned int reg, + unsigned int *value) +{ + struct i2c_client *client = context; + struct tas571x_private *priv = i2c_get_clientdata(client); + uint8_t send_buf, recv_buf[4]; + struct i2c_msg msgs[2]; + unsigned int size; + unsigned int i; + int ret; + + size = tas571x_register_size(priv, reg); + send_buf = reg; + + msgs[0].addr = client->addr; + msgs[0].len = sizeof(send_buf); + msgs[0].buf = &send_buf; + msgs[0].flags = 0; + + msgs[1].addr = client->addr; + msgs[1].len = size; + msgs[1].buf = recv_buf; + msgs[1].flags = I2C_M_RD; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return ret; + else if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *value = 0; + + for (i = 0; i < size; i++) { + *value <<= 8; + *value |= recv_buf[i]; + } + + return 0; +} + +static int tas571x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format) +{ + struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec); + + priv->format = format; + + return 0; +} + +static int tas571x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec); + u32 val; + + switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + val = 0x00; + break; + case SND_SOC_DAIFMT_I2S: + val = 0x03; + break; + case SND_SOC_DAIFMT_LEFT_J: + val = 0x06; + break; + default: + return -EINVAL; + } + + if (params_width(params) >= 24) + val += 2; + else if (params_width(params) >= 20) + val += 1; + + return regmap_update_bits(priv->regmap, TAS571X_SDI_REG, + TAS571X_SDI_FMT_MASK, val); +} + +static int tas571x_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct tas571x_private *priv = snd_soc_codec_get_drvdata(codec); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (!IS_ERR(priv->mclk)) { + ret = clk_prepare_enable(priv->mclk); + if (ret) { + dev_err(codec->dev, + "Failed to enable master clock: %d\n", + ret); + return ret; + } + } + + gpiod_set_value(priv->pdn_gpio, 0); + usleep_range(5000, 6000); + + regcache_cache_only(priv->regmap, false); + ret = regcache_sync(priv->regmap); + if (ret) + return ret; + } + break; + case SND_SOC_BIAS_OFF: + regcache_cache_only(priv->regmap, true); + gpiod_set_value(priv->pdn_gpio, 1); + + if (!IS_ERR(priv->mclk)) + clk_disable_unprepare(priv->mclk); + break; + } + + return 0; +} + +static const struct snd_soc_dai_ops tas571x_dai_ops = { + .set_fmt = tas571x_set_dai_fmt, + .hw_params = tas571x_hw_params, +}; + +static const char *const tas5711_supply_names[] = { + "AVDD", + "DVDD", + "PVDD_A", + "PVDD_B", + "PVDD_C", + "PVDD_D", +}; + +static const DECLARE_TLV_DB_SCALE(tas5711_volume_tlv, -10350, 50, 1); + +static const struct snd_kcontrol_new tas5711_controls[] = { + SOC_SINGLE_TLV("Master Volume", + TAS571X_MVOL_REG, + 0, 0xff, 1, tas5711_volume_tlv), + SOC_DOUBLE_R_TLV("Speaker Volume", + TAS571X_CH1_VOL_REG, + TAS571X_CH2_VOL_REG, + 0, 0xff, 1, tas5711_volume_tlv), + SOC_DOUBLE("Speaker Switch", + TAS571X_SOFT_MUTE_REG, + TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, + 1, 1), +}; + +static const struct reg_default tas5711_reg_defaults[] = { + { 0x04, 0x05 }, + { 0x05, 0x40 }, + { 0x06, 0x00 }, + { 0x07, 0xff }, + { 0x08, 0x30 }, + { 0x09, 0x30 }, + { 0x1b, 0x82 }, +}; + +static const struct regmap_config tas5711_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .max_register = 0xff, + .reg_read = tas571x_reg_read, + .reg_write = tas571x_reg_write, + .reg_defaults = tas5711_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas5711_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; + +static const struct tas571x_chip tas5711_chip = { + .supply_names = tas5711_supply_names, + .num_supply_names = ARRAY_SIZE(tas5711_supply_names), + .controls = tas5711_controls, + .num_controls = ARRAY_SIZE(tas5711_controls), + .regmap_config = &tas5711_regmap_config, + .vol_reg_size = 1, +}; + +static const char *const tas5717_supply_names[] = { + "AVDD", + "DVDD", + "HPVDD", + "PVDD_AB", + "PVDD_CD", +}; + +static const DECLARE_TLV_DB_SCALE(tas5717_volume_tlv, -10375, 25, 0); + +static const struct snd_kcontrol_new tas5717_controls[] = { + /* MVOL LSB is ignored - see comments in tas571x_i2c_probe() */ + SOC_SINGLE_TLV("Master Volume", + TAS571X_MVOL_REG, 1, 0x1ff, 1, + tas5717_volume_tlv), + SOC_DOUBLE_R_TLV("Speaker Volume", + TAS571X_CH1_VOL_REG, TAS571X_CH2_VOL_REG, + 1, 0x1ff, 1, tas5717_volume_tlv), + SOC_DOUBLE("Speaker Switch", + TAS571X_SOFT_MUTE_REG, + TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, + 1, 1), +}; + +static const struct reg_default tas5717_reg_defaults[] = { + { 0x04, 0x05 }, + { 0x05, 0x40 }, + { 0x06, 0x00 }, + { 0x07, 0x03ff }, + { 0x08, 0x00c0 }, + { 0x09, 0x00c0 }, + { 0x1b, 0x82 }, +}; + +static const struct regmap_config tas5717_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .max_register = 0xff, + .reg_read = tas571x_reg_read, + .reg_write = tas571x_reg_write, + .reg_defaults = tas5717_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas5717_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; + +/* This entry is reused for tas5719 as the software interface is identical. */ +static const struct tas571x_chip tas5717_chip = { + .supply_names = tas5717_supply_names, + .num_supply_names = ARRAY_SIZE(tas5717_supply_names), + .controls = tas5717_controls, + .num_controls = ARRAY_SIZE(tas5717_controls), + .regmap_config = &tas5717_regmap_config, + .vol_reg_size = 2, +}; + +static const struct snd_soc_dapm_widget tas571x_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_OUTPUT("OUT_A"), + SND_SOC_DAPM_OUTPUT("OUT_B"), + SND_SOC_DAPM_OUTPUT("OUT_C"), + SND_SOC_DAPM_OUTPUT("OUT_D"), +}; + +static const struct snd_soc_dapm_route tas571x_dapm_routes[] = { + { "DACL", NULL, "Playback" }, + { "DACR", NULL, "Playback" }, + + { "OUT_A", NULL, "DACL" }, + { "OUT_B", NULL, "DACL" }, + { "OUT_C", NULL, "DACR" }, + { "OUT_D", NULL, "DACR" }, +}; + +static const struct snd_soc_codec_driver tas571x_codec = { + .set_bias_level = tas571x_set_bias_level, + .idle_bias_off = true, + + .dapm_widgets = tas571x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas571x_dapm_widgets), + .dapm_routes = tas571x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(tas571x_dapm_routes), +}; + +static struct snd_soc_dai_driver tas571x_dai = { + .name = "tas571x-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &tas571x_dai_ops, +}; + +static const struct of_device_id tas571x_of_match[]; + +static int tas571x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tas571x_private *priv; + struct device *dev = &client->dev; + const struct of_device_id *of_id; + int i, ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + i2c_set_clientdata(client, priv); + + of_id = of_match_device(tas571x_of_match, dev); + if (!of_id) { + dev_err(dev, "Unknown device type\n"); + return -EINVAL; + } + priv->chip = of_id->data; + + priv->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(priv->mclk) && PTR_ERR(priv->mclk) != -ENOENT) { + dev_err(dev, "Failed to request mclk: %ld\n", + PTR_ERR(priv->mclk)); + return PTR_ERR(priv->mclk); + } + + BUG_ON(priv->chip->num_supply_names > TAS571X_MAX_SUPPLIES); + for (i = 0; i < priv->chip->num_supply_names; i++) + priv->supplies[i].supply = priv->chip->supply_names[i]; + + ret = devm_regulator_bulk_get(dev, priv->chip->num_supply_names, + priv->supplies); + if (ret) { + dev_err(dev, "Failed to get supplies: %d\n", ret); + return ret; + } + ret = regulator_bulk_enable(priv->chip->num_supply_names, + priv->supplies); + if (ret) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + priv->regmap = devm_regmap_init(dev, NULL, client, + priv->chip->regmap_config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->pdn_gpio = devm_gpiod_get_optional(dev, "pdn", GPIOD_OUT_LOW); + if (IS_ERR(priv->pdn_gpio)) { + dev_err(dev, "error requesting pdn_gpio: %ld\n", + PTR_ERR(priv->pdn_gpio)); + return PTR_ERR(priv->pdn_gpio); + } + + priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(priv->reset_gpio)) { + dev_err(dev, "error requesting reset_gpio: %ld\n", + PTR_ERR(priv->reset_gpio)); + return PTR_ERR(priv->reset_gpio); + } else if (priv->reset_gpio) { + /* pulse the active low reset line for ~100us */ + usleep_range(100, 200); + gpiod_set_value(priv->reset_gpio, 0); + usleep_range(12000, 20000); + } + + ret = regmap_write(priv->regmap, TAS571X_OSC_TRIM_REG, 0); + if (ret) + return ret; + + ret = regmap_update_bits(priv->regmap, TAS571X_SYS_CTRL_2_REG, + TAS571X_SYS_CTRL_2_SDN_MASK, 0); + if (ret) + return ret; + + memcpy(&priv->codec_driver, &tas571x_codec, sizeof(priv->codec_driver)); + priv->codec_driver.controls = priv->chip->controls; + priv->codec_driver.num_controls = priv->chip->num_controls; + + if (priv->chip->vol_reg_size == 2) { + /* + * The master volume defaults to 0x3ff (mute), but we ignore + * (zero) the LSB because the hardware step size is 0.125 dB + * and TLV_DB_SCALE_ITEM has a resolution of 0.01 dB. + */ + ret = regmap_update_bits(priv->regmap, TAS571X_MVOL_REG, 1, 0); + if (ret) + return ret; + } + + regcache_cache_only(priv->regmap, true); + gpiod_set_value(priv->pdn_gpio, 1); + + return snd_soc_register_codec(&client->dev, &priv->codec_driver, + &tas571x_dai, 1); +} + +static int tas571x_i2c_remove(struct i2c_client *client) +{ + struct tas571x_private *priv = i2c_get_clientdata(client); + + snd_soc_unregister_codec(&client->dev); + regulator_bulk_disable(priv->chip->num_supply_names, priv->supplies); + + return 0; +} + +static const struct of_device_id tas571x_of_match[] = { + { .compatible = "ti,tas5711", .data = &tas5711_chip, }, + { .compatible = "ti,tas5717", .data = &tas5717_chip, }, + { .compatible = "ti,tas5719", .data = &tas5717_chip, }, + { } +}; +MODULE_DEVICE_TABLE(of, tas571x_of_match); + +static const struct i2c_device_id tas571x_i2c_id[] = { + { "tas5711", 0 }, + { "tas5717", 0 }, + { "tas5719", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas571x_i2c_id); + +static struct i2c_driver tas571x_i2c_driver = { + .driver = { + .name = "tas571x", + .of_match_table = of_match_ptr(tas571x_of_match), + }, + .probe = tas571x_i2c_probe, + .remove = tas571x_i2c_remove, + .id_table = tas571x_i2c_id, +}; +module_i2c_driver(tas571x_i2c_driver); + +MODULE_DESCRIPTION("ASoC TAS571x driver"); +MODULE_AUTHOR("Kevin Cernekee <cernekee@chromium.org>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas571x.h b/sound/soc/codecs/tas571x.h new file mode 100644 index 0000000..0aee471 --- /dev/null +++ b/sound/soc/codecs/tas571x.h @@ -0,0 +1,33 @@ +/* + * TAS571x amplifier audio driver + * + * Copyright (C) 2015 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _TAS571X_H +#define _TAS571X_H + +/* device registers */ +#define TAS571X_SDI_REG 0x04 +#define TAS571X_SDI_FMT_MASK 0x0f + +#define TAS571X_SYS_CTRL_2_REG 0x05 +#define TAS571X_SYS_CTRL_2_SDN_MASK 0x40 + +#define TAS571X_SOFT_MUTE_REG 0x06 +#define TAS571X_SOFT_MUTE_CH1_SHIFT 0 +#define TAS571X_SOFT_MUTE_CH2_SHIFT 1 +#define TAS571X_SOFT_MUTE_CH3_SHIFT 2 + +#define TAS571X_MVOL_REG 0x07 +#define TAS571X_CH1_VOL_REG 0x08 +#define TAS571X_CH2_VOL_REG 0x09 + +#define TAS571X_OSC_TRIM_REG 0x1b + +#endif /* _TAS571X_H */ diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c index 9fd80ac..12232d7 100644 --- a/sound/soc/codecs/ts3a227e.c +++ b/sound/soc/codecs/ts3a227e.c @@ -254,12 +254,13 @@ static const struct regmap_config ts3a227e_regmap_config = { .num_reg_defaults = ARRAY_SIZE(ts3a227e_reg_defaults), }; -static int ts3a227e_parse_dt(struct ts3a227e *ts3a227e, struct device_node *np) +static int ts3a227e_parse_device_property(struct ts3a227e *ts3a227e, + struct device *dev) { u32 micbias; int err; - err = of_property_read_u32(np, "ti,micbias", &micbias); + err = device_property_read_u32(dev, "ti,micbias", &micbias); if (!err) { regmap_update_bits(ts3a227e->regmap, TS3A227E_REG_SETTING_3, MICBIAS_SETTING_MASK, @@ -287,12 +288,10 @@ static int ts3a227e_i2c_probe(struct i2c_client *i2c, if (IS_ERR(ts3a227e->regmap)) return PTR_ERR(ts3a227e->regmap); - if (dev->of_node) { - ret = ts3a227e_parse_dt(ts3a227e, dev->of_node); - if (ret) { - dev_err(dev, "Failed to parse device tree: %d\n", ret); - return ret; - } + ret = ts3a227e_parse_device_property(ts3a227e, dev); + if (ret) { + dev_err(dev, "Failed to parse device property: %d\n", ret); + return ret; } ret = devm_request_threaded_irq(dev, i2c->irq, NULL, ts3a227e_interrupt, |