Because of the holidays, TI E2E™ design support forum responses will be delayed from Dec. 25 through Jan. 2. Thank you for your patience.

This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TAS2505: linux driver loaded but no signals on pins

Part Number: TAS2505
Hello, I am developing an application for a Beaglebone Black and Linux, and I need to interface the BBB to a TAS2505.
So this is what I have so far:

> Linux 4.19.129 build with Yocto
> Tas2505 Driver from here: ti-tas2505-driver
> Fixed a bug in the above driver:
>tas2505.c line 37: #if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE

> patched the driver inside the kernel kconfig and Makefile (see attached)
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index efb095d..0da26ea 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -158,6 +158,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
 	select SND_SOC_STI_SAS
 	select SND_SOC_TAS2552 if I2C
+        select SND_SOC_TAS2505 if I2C
 	select SND_SOC_TAS5086 if I2C
 	select SND_SOC_TAS571X if I2C
 	select SND_SOC_TAS5720 if I2C
@@ -958,6 +959,10 @@ config SND_SOC_TAS2552
 	tristate "Texas Instruments TAS2552 Mono Audio amplifier"
 	depends on I2C
 
+config SND_SOC_TAS2505
+        tristate "Texas Instruments TAS2505 Mono Audio Amplifier for Automac"
+        depends on I2C
+
 config SND_SOC_TAS5086
 	tristate "Texas Instruments TAS5086 speaker amplifier"
 	depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 7ae7c85..e8d0d34 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -256,6 +256,7 @@ snd-soc-max98504-objs := max98504.o
 snd-soc-simple-amplifier-objs := simple-amplifier.o
 snd-soc-tpa6130a2-objs := tpa6130a2.o
 snd-soc-tas2552-objs := tas2552.o
+snd-soc-tas2505-objs := tas2505.o
 
 obj-$(CONFIG_SND_SOC_88PM860X)	+= snd-soc-88pm860x.o
 obj-$(CONFIG_SND_SOC_AB8500_CODEC)	+= snd-soc-ab8500-codec.o
@@ -428,6 +429,7 @@ obj-$(CONFIG_SND_SOC_STA529)   += snd-soc-sta529.o
 obj-$(CONFIG_SND_SOC_STAC9766)	+= snd-soc-stac9766.o
 obj-$(CONFIG_SND_SOC_STI_SAS)	+= snd-soc-sti-sas.o
 obj-$(CONFIG_SND_SOC_TAS2552)	+= snd-soc-tas2552.o
+obj-$(CONFIG_SND_SOC_TAS2505)   += snd-soc-tas2505.o
 obj-$(CONFIG_SND_SOC_TAS5086)	+= snd-soc-tas5086.o
 obj-$(CONFIG_SND_SOC_TAS571X)	+= snd-soc-tas571x.o
 obj-$(CONFIG_SND_SOC_TAS5720)	+= snd-soc-tas5720.o
diff --git a/sound/soc/codecs/tas2505.c b/sound/soc/codecs/tas2505.c
new file mode 100644
index 0000000..20437b6
--- /dev/null
+++ b/sound/soc/codecs/tas2505.c
@@ -0,0 +1,857 @@
+/*
+ * ALSA SoC TAS2505 codec driver
+ *
+ * Author: Hieu Tran Dang <dangtranhieu2012@gmail.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED AS IS AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * Modificado por Fede 22/9/2022
+*/
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/pcm_params.h>
+#include "tas2505.h"
+
+static int tas2505_spkdrv_getvol(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component =
+		snd_soc_kcontrol_component(kcontrol);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	unsigned int val;
+#if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE
+	val = snd_soc_component_read(component, TAS2505_SPKVOL1);
+#else
+	snd_soc_component_read(component, TAS2505_SPKVOL1, &val);
+#endif
+	val = (val > mc->max) ? mc->max : val;
+	val = mc->invert ? mc->max - val : val;
+	ucontrol->value.integer.value[0] = val;
+
+	return 0;
+}
+
+static int tas2505_spkdrv_putvol(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component =
+		snd_soc_kcontrol_component(kcontrol);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	u8 val;
+
+	val = (ucontrol->value.integer.value[0] & 0x7f);
+	val = mc->invert ? mc->max - val : val;
+	val = (val < 0) ? 0 : val;
+	snd_soc_component_write(component, TAS2505_SPKVOL1, val);
+
+	return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6350, 50, 0);
+static const DECLARE_TLV_DB_LINEAR(spk_drv_vol_tlv, TLV_DB_GAIN_MUTE, 0);
+static const DECLARE_TLV_DB_SCALE(spk_amp_vol_tlv, 0, 600, 1);
+
+static const struct snd_kcontrol_new tas2505_snd_controls[] = {
+	SOC_SINGLE_S8_TLV("DAC Playback Volume", TAS2505_DACVOL,
+		-127, 48, dac_vol_tlv),
+	SOC_SINGLE_RANGE_EXT_TLV("Speaker Driver Volume", TAS2505_SPKVOL1,
+		0, 0, 117, 1,
+		tas2505_spkdrv_getvol, tas2505_spkdrv_putvol, spk_drv_vol_tlv),
+	SOC_SINGLE_TLV("Speaker Amplifer Volume", TAS2505_SPKVOL2,
+		4, 5, 0, spk_amp_vol_tlv),
+};
+
+static const struct snd_soc_dapm_widget tas2505_dapm_widgets[] = {
+	SND_SOC_DAPM_DAC("DAC Channel", "Playback",
+		TAS2505_DACSETUP1, 7, 0),
+	SND_SOC_DAPM_OUT_DRV("Speaker Driver", TAS2505_SPKAMPCTRL1,
+		1, 0, NULL, 0),
+	SND_SOC_DAPM_OUTPUT("Speaker"),
+};
+
+static const struct snd_soc_dapm_route tas2505_audio_map[] = {
+	{ "Speaker Driver", NULL, "DAC Channel" },
+	{ "Speaker", NULL, "Speaker Driver" },
+};
+
+static const struct reg_default tas2505_reg_defaults[] = {
+	{ TAS2505_CLKMUX, 0x00 },
+	{ TAS2505_PLLPR, 0x11 },
+	{ TAS2505_PLLJ, 0x04 },
+	{ TAS2505_PLLDMSB, 0x00 },
+	{ TAS2505_PLLDLSB, 0x00 },
+	{ TAS2505_NDAC, 0x01 },
+	{ TAS2505_MDAC, 0x01 },
+	{ TAS2505_DOSRMSB, 0x00 },
+	{ TAS2505_DOSRLSB, 0x80 },
+	{ TAS2505_IFACE1, 0x00 },
+	{ TAS2505_IFACE3, 0x00 },
+	{ TAS2505_DACSETUP1, 0x14 },
+	{ TAS2505_DACSETUP2, 0x0c },
+	{ TAS2505_DACVOL, 0x00 },
+	{ TAS2505_REF_POR_LDO_BGAP_CTRL, 0x00 },
+	{ TAS2505_LDO_CTRL, 0x0c },
+	{ TAS2505_SPKAMPCTRL1, 0x00 },
+	{ TAS2505_SPKVOL1, 0x00 },
+	{ TAS2505_SPKVOL2, 0x00 },
+	{ TAS2505_DACFLAG1, 0x00 },
+	{ TAS2505_DACFLAG2, 0x00 },
+	{ TAS2505_STICKYFLAG1, 0x00 },
+	{ TAS2505_STICKYFLAG2, 0x00 },
+	{ TAS2505_INTFLAG1, 0x00 },
+	{ TAS2505_INTFLAG2, 0x00 },
+	{ TAS2505_DACANLGAINFLAG, 0x00 },
+};
+
+static bool tas2505_volatile(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+		case TAS2505_PAGECTL:
+		case TAS2505_RESET:
+		case TAS2505_DACFLAG1:
+		case TAS2505_DACFLAG2:
+		case TAS2505_STICKYFLAG1:
+		case TAS2505_STICKYFLAG2:
+		case TAS2505_INTFLAG1:
+		case TAS2505_INTFLAG2:
+		case TAS2505_DACANLGAINFLAG:
+			return true;
+	}
+	return false;
+}
+
+static bool tas2505_writeable(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+		case TAS2505_DACFLAG1:
+		case TAS2505_DACFLAG2:
+		case TAS2505_STICKYFLAG1:
+		case TAS2505_STICKYFLAG2:
+		case TAS2505_INTFLAG1:
+		case TAS2505_INTFLAG2:
+		case TAS2505_DACANLGAINFLAG:
+			return false;
+	}
+	return true;
+}
+
+static const struct regmap_range_cfg tas2505_ranges[] = {
+	{
+		.range_min = 0,
+		.range_max = 69 * 128,
+		.selector_reg = TAS2505_PAGECTL,
+		.selector_mask = 0xff,
+		.selector_shift = 0,
+		.window_start = 0,
+		.window_len = 128,
+	},
+};
+
+static const struct regmap_config tas2505_i2c_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.writeable_reg = tas2505_writeable,
+	.volatile_reg = tas2505_volatile,
+	.reg_defaults = tas2505_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(tas2505_reg_defaults),
+	.cache_type = REGCACHE_RBTREE,
+	.ranges = tas2505_ranges,
+	.num_ranges = ARRAY_SIZE(tas2505_ranges),
+	.max_register = 69 * 128,
+};
+
+struct tas2505_rate_divs {
+	u32 mclk_p;
+	u32 rate;
+	u8 pll_r;
+	u8 pll_j;
+	u16 pll_d;
+	u8 mdac;
+	u8 ndac;
+	u16 dosr;
+};
+
+static const struct tas2505_rate_divs tas2505_divs[] = {
+	{ 12288000, 44100, 1, 7, 35, 4, 4, 128 },
+	{ 12288000, 48000, 1, 7, 0, 7, 2, 128 },
+};
+
+struct tas2505_priv {
+	void *codec;
+	struct device *dev;
+	struct regmap *regmap;
+	u32 sysclk;
+	u8 p_div;
+	int rate_div_line;
+};
+
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+static int tas2505_setup_pll(struct snd_soc_component *codec,
+	struct snd_pcm_hw_params *params)
+{
+	struct tas2505_priv *tas2505 = snd_soc_component_get_drvdata(codec);
+	int mclk_p = tas2505->sysclk / tas2505->p_div;
+	int match = -1;
+	u8 p_div;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(tas2505_divs); i++) {
+		if (
+			tas2505_divs[i].rate == params_rate(params) &&
+			tas2505_divs[i].mclk_p == mclk_p
+		) {
+			match = i;
+			break;
+		}
+	}
+
+	if (match == -1) {
+		dev_err(codec->dev,
+			"Sample rate (%u), sysclk (%u), div (%u) and format not supported\n",
+			params_rate(params), tas2505->sysclk, tas2505->p_div);
+		return -EINVAL;
+	}
+
+	tas2505->rate_div_line = match;
+
+	p_div = (tas2505->p_div == 8) ? 0 : tas2505->p_div;
+	p_div <<= TAS2505_PLLPR_P_SHIFT;
+
+	snd_soc_component_update_bits(codec, TAS2505_PLLPR, TAS2505_PLLPR_P_MASK,
+		p_div);
+	snd_soc_component_update_bits(codec, TAS2505_PLLPR, TAS2505_PLLPR_R_MASK,
+		tas2505_divs[match].pll_r);
+	snd_soc_component_write(codec, TAS2505_PLLJ, tas2505_divs[match].pll_j);
+	snd_soc_component_write(codec, TAS2505_PLLDMSB, tas2505_divs[match].pll_d >> 8);
+	snd_soc_component_write(codec, TAS2505_PLLDLSB, tas2505_divs[match].pll_d & 0xff);
+	snd_soc_component_update_bits(codec, TAS2505_NDAC, TAS2505_PLL_DAC_MASK,
+		tas2505_divs[match].ndac);
+	snd_soc_component_update_bits(codec, TAS2505_MDAC, TAS2505_PLL_DAC_MASK,
+		tas2505_divs[match].mdac);
+	snd_soc_component_write(codec, TAS2505_DOSRMSB, tas2505_divs[match].dosr >> 8);
+	snd_soc_component_write(codec, TAS2505_DOSRLSB, tas2505_divs[match].dosr & 0xff);
+	snd_soc_component_update_bits(codec, TAS2505_BCLKNDIV, TAS2505_BCLKNDIV_MASK,
+		(tas2505_divs[match].dosr * tas2505_divs[match].mdac) / snd_soc_params_to_frame_size(params));
+
+	return 0;
+}
+
+static int tas2505_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *codec = dai->component;
+	u8 data = 0;
+
+	switch (params_width(params)) {
+		case 16:
+			break;
+
+		case 20:
+			data = TAS2505_WORD_LEN_20BITS;
+			break;
+
+		case 24:
+			data = TAS2505_WORD_LEN_24BITS;
+			break;
+
+		case 32:
+			data = TAS2505_WORD_LEN_32BITS;
+			break;
+
+		default:
+			dev_err(codec->dev, "Unsupported width %d\n",
+				params_width(params));
+			return -EINVAL;
+	}
+
+	data <<= TAS2505_IFACE1_DATALEN_SHIFT;
+
+	snd_soc_component_update_bits(codec, TAS2505_IFACE1,
+		TAS2505_IFACE1_DATALEN_MASK, data);
+
+	return tas2505_setup_pll(codec, params);
+}
+
+static int tas2505_dac_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+	struct snd_soc_component *codec = dai->component;
+
+	if (mute) {
+		snd_soc_component_update_bits(codec, TAS2505_DACSETUP2,
+			TAS2505_DACSETUP2_MUTE_MASK,
+			TAS2505_DACSETUP2_MUTE_MASK);
+	} else {
+		snd_soc_component_update_bits(codec, TAS2505_DACSETUP2,
+			TAS2505_DACSETUP2_MUTE_MASK, 0x0);
+	}
+
+	return 0;
+}
+
+static int tas2505_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+	struct snd_soc_component *codec = codec_dai->component;
+	u8 iface_reg1 = 0;
+	u8 iface_reg3 = 0;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+		case SND_SOC_DAIFMT_CBS_CFS:
+			break;
+
+		case SND_SOC_DAIFMT_CBM_CFM:
+			iface_reg1 |= TAS2505_IFACE1_BCLKDIR_MASK;
+			iface_reg1 |= TAS2505_IFACE1_WCLKDIR_MASK;
+			break;
+
+		default:
+			return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+		case SND_SOC_DAIFMT_NB_NF:
+			break;
+
+		case SND_SOC_DAIFMT_IB_NF:
+			iface_reg3 |= TAS2505_IFACE3_BCLKINV_MASK;
+			break;
+
+		default:
+			return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+		case SND_SOC_DAIFMT_I2S:
+			break;
+
+		case SND_SOC_DAIFMT_DSP_A:
+		case SND_SOC_DAIFMT_DSP_B:
+			iface_reg1 |= (TAS2505_DSP_MODE <<
+				TAS2505_IFACE1_INTERFACE_SHIFT);
+			break;
+
+		case SND_SOC_DAIFMT_RIGHT_J:
+			iface_reg1 |= (TAS2505_RJF_MODE <<
+				TAS2505_IFACE1_INTERFACE_SHIFT);
+			break;
+
+		case SND_SOC_DAIFMT_LEFT_J:
+			iface_reg1 |= (TAS2505_LJF_MODE <<
+				TAS2505_IFACE1_INTERFACE_SHIFT);
+			break;
+
+		default:
+			dev_err(codec->dev, "Invalid DAI interface format\n");
+			return -EINVAL;
+	}
+
+	snd_soc_component_write(codec, TAS2505_IFACE1, iface_reg1);
+	snd_soc_component_update_bits(codec, TAS2505_IFACE3,
+		TAS2505_IFACE3_BCLKINV_MASK | TAS2505_IFACE3_BDIVCLKIN_MASK,
+		iface_reg3);
+
+	return 0;
+}
+
+static int tas2505_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
+	unsigned int freq, int dir)
+{
+	struct snd_soc_component *codec = codec_dai->component;
+	struct tas2505_priv *tas2505 = snd_soc_component_get_drvdata(codec);
+	int i, x;
+
+	dev_dbg(tas2505->dev, "clk_id: %d, freq: %d\n", clk_id, freq);
+
+	for (i = 0; i < ARRAY_SIZE(tas2505_divs); i++) {
+		for (x = 1; x < 9; x++) {
+			if ((freq / x) == tas2505_divs[i].mclk_p) {
+				tas2505->p_div = x;
+				goto found_p_div;
+			}
+		}
+	}
+
+	dev_err(tas2505->dev, "Can't produce required PLL_CLKIN frequency\n");
+	return -EINVAL;
+
+found_p_div:
+	snd_soc_component_write(codec, TAS2505_CLKMUX,
+		(clk_id << TAS2505_PLL_CLKIN_SHIFT) | TAS2505_CODEC_CLKIN_PLL);
+
+	tas2505->sysclk = freq;
+
+	return 0;
+}
+
+static void tas2505_clk_on(struct snd_soc_component *codec)
+{
+	u8 mask = TAS2505_PM_MASK;
+	u8 on = TAS2505_PM_MASK;
+
+	snd_soc_component_update_bits(codec, TAS2505_PLLPR, mask, on);
+	mdelay(15);
+	snd_soc_component_update_bits(codec, TAS2505_NDAC, mask, on);
+	snd_soc_component_update_bits(codec, TAS2505_MDAC, mask, on);
+	snd_soc_component_update_bits(codec, TAS2505_BCLKNDIV, mask, on);
+}
+
+static void tas2505_clk_off(struct snd_soc_component *codec)
+{
+	u8 mask = TAS2505_PM_MASK;
+
+	snd_soc_component_update_bits(codec, TAS2505_BCLKNDIV, mask, 0);
+	snd_soc_component_update_bits(codec, TAS2505_MDAC, mask, 0);
+	snd_soc_component_update_bits(codec, TAS2505_NDAC, mask, 0);
+	snd_soc_component_update_bits(codec, TAS2505_PLLPR, mask, 0);
+}
+
+static void tas2505_power_on(struct snd_soc_component *codec)
+{
+	snd_soc_component_write(codec, TAS2505_RESET, 1);
+	usleep_range(500, 1000);
+	snd_soc_component_update_bits(codec, TAS2505_LDO_CTRL,
+		TAS2505_LDO_PLL_HP_LVL_MASK, 0);
+	snd_soc_component_update_bits(codec, TAS2505_REF_POR_LDO_BGAP_CTRL,
+		TAS2505_REF_POR_LDO_BGAP_MASTER_REF_MASK,
+		TAS2505_REF_POR_LDO_BGAP_MASTER_REF_MASK);
+}
+
+static void tas2505_power_off(struct snd_soc_component *codec)
+{
+	snd_soc_component_update_bits(codec, TAS2505_REF_POR_LDO_BGAP_CTRL,
+		TAS2505_REF_POR_LDO_BGAP_MASTER_REF_MASK, 0);
+	snd_soc_component_update_bits(codec, TAS2505_LDO_CTRL,
+		TAS2505_LDO_PLL_HP_LVL_MASK,
+		TAS2505_LDO_PLL_HP_LVL_MASK);
+}
+#else
+static int tas2505_setup_pll(struct snd_soc_codec *codec,
+	struct snd_pcm_hw_params *params)
+{
+	struct tas2505_priv *tas2505 = snd_soc_codec_get_drvdata(codec);
+	int mclk_p = tas2505->sysclk / tas2505->p_div;
+	int match = -1;
+	u8 p_div;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(tas2505_divs); i++) {
+		if (
+			tas2505_divs[i].rate == params_rate(params) &&
+			tas2505_divs[i].mclk_p == mclk_p
+		) {
+			match = i;
+			break;
+		}
+	}
+
+	if (match == -1) {
+		dev_err(codec->dev,
+			"Sample rate (%u) and format not supported\n",
+			params_rate(params));
+		return -EINVAL;
+	}
+
+	tas2505->rate_div_line = match;
+
+	p_div = (tas2505->p_div == 8) ? 0 : tas2505->p_div;
+	p_div <<= TAS2505_PLLPR_P_SHIFT;
+
+	snd_soc_update_bits(codec, TAS2505_PLLPR, TAS2505_PLLPR_P_MASK,
+		p_div);
+	snd_soc_update_bits(codec, TAS2505_PLLPR, TAS2505_PLLPR_R_MASK,
+		tas2505_divs[match].pll_r);
+	snd_soc_write(codec, TAS2505_PLLJ, tas2505_divs[match].pll_j);
+	snd_soc_write(codec, TAS2505_PLLDMSB, tas2505_divs[match].pll_d >> 8);
+	snd_soc_write(codec, TAS2505_PLLDLSB, tas2505_divs[match].pll_d & 0xff);
+	snd_soc_update_bits(codec, TAS2505_NDAC, TAS2505_PLL_DAC_MASK,
+		tas2505_divs[match].ndac);
+	snd_soc_update_bits(codec, TAS2505_MDAC, TAS2505_PLL_DAC_MASK,
+		tas2505_divs[match].mdac);
+	snd_soc_write(codec, TAS2505_DOSRMSB, tas2505_divs[match].dosr >> 8);
+	snd_soc_write(codec, TAS2505_DOSRLSB, tas2505_divs[match].dosr & 0xff);
+	snd_soc_update_bits(codec, TAS2505_BCLKNDIV, TAS2505_BCLKNDIV_MASK,
+		(tas2505_divs[match].dosr * tas2505_divs[match].mdac) / snd_soc_params_to_frame_size(params));
+
+	return 0;
+}
+
+static int tas2505_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u8 data = 0;
+
+	switch (params_width(params)) {
+		case 16:
+			break;
+
+		case 20:
+			data = TAS2505_WORD_LEN_20BITS;
+			break;
+
+		case 24:
+			data = TAS2505_WORD_LEN_24BITS;
+			break;
+
+		case 32:
+			data = TAS2505_WORD_LEN_32BITS;
+			break;
+
+		default:
+			dev_err(codec->dev, "Unsupported width %d\n",
+				params_width(params));
+			return -EINVAL;
+	}
+
+	data <<= TAS2505_IFACE1_DATALEN_SHIFT;
+
+	snd_soc_update_bits(codec, TAS2505_IFACE1,
+		TAS2505_IFACE1_DATALEN_MASK, data);
+
+	return tas2505_setup_pll(codec, params);
+}
+
+static int tas2505_dac_mute(struct snd_soc_dai *codec_dai, int mute, int stream)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+
+	if (mute) {
+		snd_soc_update_bits(codec, TAS2505_DACSETUP2,
+			TAS2505_DACSETUP2_MUTE_MASK,
+			TAS2505_DACSETUP2_MUTE_MASK);
+	} else {
+		snd_soc_update_bits(codec, TAS2505_DACSETUP2,
+			TAS2505_DACSETUP2_MUTE_MASK, 0x0);
+	}
+
+	return 0;
+}
+
+static int tas2505_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u8 iface_reg1 = 0;
+	u8 iface_reg3 = 0;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+		case SND_SOC_DAIFMT_CBS_CFS:
+			break;
+
+		case SND_SOC_DAIFMT_CBM_CFM:
+			iface_reg1 |= TAS2505_IFACE1_BCLKDIR_MASK;
+			iface_reg1 |= TAS2505_IFACE1_WCLKDIR_MASK;
+			break;
+
+		default:
+			return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+		case SND_SOC_DAIFMT_NB_NF:
+			break;
+
+		case SND_SOC_DAIFMT_IB_NF:
+			iface_reg3 |= TAS2505_IFACE3_BCLKINV_MASK;
+			break;
+
+		default:
+			return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+		case SND_SOC_DAIFMT_I2S:
+			break;
+
+		case SND_SOC_DAIFMT_DSP_A:
+		case SND_SOC_DAIFMT_DSP_B:
+			iface_reg1 |= (TAS2505_DSP_MODE <<
+				TAS2505_IFACE1_INTERFACE_SHIFT);
+			break;
+
+		case SND_SOC_DAIFMT_RIGHT_J:
+			iface_reg1 |= (TAS2505_RJF_MODE <<
+				TAS2505_IFACE1_INTERFACE_SHIFT);
+			break;
+
+		case SND_SOC_DAIFMT_LEFT_J:
+			iface_reg1 |= (TAS2505_LJF_MODE <<
+				TAS2505_IFACE1_INTERFACE_SHIFT);
+			break;
+
+		default:
+			dev_err(codec->dev, "Invalid DAI interface format\n");
+			return -EINVAL;
+	}
+
+	snd_soc_write(codec, TAS2505_IFACE1, iface_reg1);
+	snd_soc_update_bits(codec, TAS2505_IFACE3,
+		TAS2505_IFACE3_BCLKINV_MASK | TAS2505_IFACE3_BDIVCLKIN_MASK,
+		iface_reg3);
+
+	return 0;
+}
+
+static int tas2505_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
+	unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct tas2505_priv *tas2505 = snd_soc_codec_get_drvdata(codec);
+	int i, x;
+
+	dev_dbg(tas2505->dev, "clk_id: %d, freq: %d\n", clk_id, freq);
+
+	for (i = 0; i < ARRAY_SIZE(tas2505_divs); i++) {
+		for (x = 1; x < 9; x++) {
+			if ((freq / x) == tas2505_divs[i].mclk_p) {
+				tas2505->p_div = x;
+				goto found_p_div;
+			}
+		}
+	}
+
+	dev_err(tas2505->dev, "Can't produce required PLL_CLKIN frequency\n");
+	return -EINVAL;
+
+found_p_div:
+	snd_soc_write(codec, TAS2505_CLKMUX,
+		(clk_id << TAS2505_PLL_CLKIN_SHIFT) | TAS2505_CODEC_CLKIN_PLL);
+
+	tas2505->sysclk = freq;
+
+	return 0;
+}
+
+static void tas2505_clk_on(struct snd_soc_codec *codec)
+{
+	u8 mask = TAS2505_PM_MASK;
+	u8 on = TAS2505_PM_MASK;
+
+	snd_soc_update_bits(codec, TAS2505_PLLPR, mask, on);
+	mdelay(15);
+	snd_soc_update_bits(codec, TAS2505_NDAC, mask, on);
+	snd_soc_update_bits(codec, TAS2505_MDAC, mask, on);
+	snd_soc_update_bits(codec, TAS2505_BCLKNDIV, mask, on);
+}
+
+static void tas2505_clk_off(struct snd_soc_codec *codec)
+{
+	u8 mask = TAS2505_PM_MASK;
+
+	snd_soc_update_bits(codec, TAS2505_BCLKNDIV, mask, 0);
+	snd_soc_update_bits(codec, TAS2505_MDAC, mask, 0);
+	snd_soc_update_bits(codec, TAS2505_NDAC, mask, 0);
+	snd_soc_update_bits(codec, TAS2505_PLLPR, mask, 0);
+}
+
+static void tas2505_power_on(struct snd_soc_codec *codec)
+{
+	snd_soc_write(codec, TAS2505_RESET, 1);
+	usleep_range(500, 1000);
+	snd_soc_update_bits(codec, TAS2505_LDO_CTRL,
+		TAS2505_LDO_PLL_HP_LVL_MASK, 0);
+	snd_soc_update_bits(codec, TAS2505_REF_POR_LDO_BGAP_CTRL,
+		TAS2505_REF_POR_LDO_BGAP_MASTER_REF_MASK,
+		TAS2505_REF_POR_LDO_BGAP_MASTER_REF_MASK);
+}
+
+static void tas2505_power_off(struct snd_soc_codec *codec)
+{
+	snd_soc_update_bits(codec, TAS2505_REF_POR_LDO_BGAP_CTRL,
+		TAS2505_REF_POR_LDO_BGAP_MASTER_REF_MASK, 0);
+	snd_soc_update_bits(codec, TAS2505_LDO_CTRL,
+		TAS2505_LDO_PLL_HP_LVL_MASK,
+		TAS2505_LDO_PLL_HP_LVL_MASK);
+}
+#endif
+
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+static int tas2505_set_bias_level(struct snd_soc_component *codec,
+	enum snd_soc_bias_level level)
+{
+	int current_lvl = snd_soc_component_get_bias_level(codec);
+#else
+static int tas2505_set_bias_level(struct snd_soc_codec *codec,
+	enum snd_soc_bias_level level)
+{
+	int current_lvl = snd_soc_codec_get_bias_level(codec);
+#endif
+	switch (level) {
+		case SND_SOC_BIAS_ON:
+			break;
+		case SND_SOC_BIAS_PREPARE:
+			if (current_lvl == SND_SOC_BIAS_STANDBY)
+				tas2505_clk_on(codec);
+			break;
+		case SND_SOC_BIAS_STANDBY:
+			if (current_lvl == SND_SOC_BIAS_OFF)
+				tas2505_power_on(codec);
+			else if (current_lvl == SND_SOC_BIAS_PREPARE)
+				tas2505_clk_off(codec);
+			else
+				BUG();
+			break;
+		case SND_SOC_BIAS_OFF:
+			if (current_lvl == SND_SOC_BIAS_STANDBY)
+				tas2505_power_off(codec);
+			break;
+		default:
+			dev_err(codec->dev, "Invalid bias level\n");
+			return -EINVAL;
+	}
+
+	return 0;
+}
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+static int tas2505_codec_probe(struct snd_soc_component *codec)
+{
+	struct tas2505_priv *tas2505 = snd_soc_component_get_drvdata(codec);
+#else
+static int tas2505_codec_probe(struct snd_soc_codec *codec)
+{
+	struct tas2505_priv *tas2505 = snd_soc_codec_get_drvdata(codec);
+#endif
+	tas2505->codec = codec;
+	return 0;
+}
+
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+static struct snd_soc_component_driver soc_codec_driver_tas2505 = {
+	.probe			= tas2505_codec_probe,
+	.set_bias_level		= tas2505_set_bias_level,
+	.suspend_bias_off	= true,
+
+	.controls		= tas2505_snd_controls,
+	.num_controls		= ARRAY_SIZE(tas2505_snd_controls),
+	.dapm_widgets		= tas2505_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(tas2505_dapm_widgets),
+	.dapm_routes		= tas2505_audio_map,
+	.num_dapm_routes	= ARRAY_SIZE(tas2505_audio_map),
+};
+#else
+static struct snd_soc_codec_driver soc_codec_driver_tas2505 = {
+	.probe			= tas2505_codec_probe,
+	.set_bias_level		= tas2505_set_bias_level,
+	.suspend_bias_off	= true,
+
+	.component_driver = {
+		.controls		= tas2505_snd_controls,
+		.num_controls		= ARRAY_SIZE(tas2505_snd_controls),
+		.dapm_widgets		= tas2505_dapm_widgets,
+		.num_dapm_widgets	= ARRAY_SIZE(tas2505_dapm_widgets),
+		.dapm_routes		= tas2505_audio_map,
+		.num_dapm_routes	= ARRAY_SIZE(tas2505_audio_map),
+	},
+};
+#endif
+
+static const struct snd_soc_dai_ops tas2505_dai_ops = {
+	.hw_params	= tas2505_hw_params,
+	.set_sysclk	= tas2505_set_dai_sysclk,
+	.set_fmt	= tas2505_set_dai_fmt,
+	.mute_stream	= tas2505_dac_mute,
+};
+
+static struct snd_soc_dai_driver tas2505_dai_driver[] = {
+	{
+		.name = "tas2505-hifi",
+		.playback = {
+			.stream_name	= "Playback",
+			.channels_min	= 1,
+			.channels_max	= 2,
+			.rates		= TAS2505_RATES,
+			.formats	= TAS2505_FORMATS,
+		},
+		.ops = &tas2505_dai_ops,
+		.symmetric_rates = 1,
+	},
+};
+
+static int tas2505_i2c_probe(struct i2c_client *i2c,
+	const struct i2c_device_id *id)
+{
+	struct tas2505_priv *tas2505;
+	struct device_node *np = i2c->dev.of_node;
+	const struct regmap_config *regmap_config = &tas2505_i2c_regmap;
+	int ret;
+
+	tas2505 = devm_kzalloc(&i2c->dev, sizeof(*tas2505), GFP_KERNEL);
+	if (tas2505 == NULL)
+		return -ENOMEM;
+
+	tas2505->regmap = devm_regmap_init_i2c(i2c, regmap_config);
+	if (IS_ERR(tas2505->regmap)) {
+		ret = PTR_ERR(tas2505->regmap);
+		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = of_get_named_gpio(np, "gpio-reset", 0);
+	if ((ret > 0) && gpio_is_valid(ret)) {
+		devm_gpio_request_one(&i2c->dev, ret, GPIOF_OUT_INIT_HIGH, "reset");
+	}
+
+	tas2505->dev = &i2c->dev;
+
+	dev_set_drvdata(tas2505->dev, tas2505);
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+	return snd_soc_register_component( &i2c->dev, &soc_codec_driver_tas2505,
+		tas2505_dai_driver, ARRAY_SIZE(tas2505_dai_driver));
+#else
+	return snd_soc_register_codec( &i2c->dev, &soc_codec_driver_tas2505,
+		tas2505_dai_driver, ARRAY_SIZE(tas2505_dai_driver));
+#endif
+}
+
+static int tas2505_i2c_remove(struct i2c_client *i2c)
+{
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+	snd_soc_unregister_component(&i2c->dev);
+#else
+	snd_soc_unregister_codec(&i2c->dev);
+#endif
+	return 0;
+}
+
+static const struct of_device_id tas2505_of_match[] = {
+	{ .compatible = "ti,tas2505" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, tas2505_of_match);
+
+static const struct i2c_device_id tas2505_i2c_id[] = {
+	{ "tas2505", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2ic, tas2505_i2c_id);
+
+static struct i2c_driver tas2505_i2c_driver = {
+	.driver = {
+		.name		= "tas2505-codec",
+		.of_match_table	= of_match_ptr(tas2505_of_match),
+	},
+	.probe		= tas2505_i2c_probe,
+	.remove		= tas2505_i2c_remove,
+	.id_table	= tas2505_i2c_id,
+};
+
+module_i2c_driver(tas2505_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC TAS2505 codec driver");
+MODULE_AUTHOR("Hieu Tran Dang <dangtranhieu2012@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas2505.h b/sound/soc/codecs/tas2505.h
new file mode 100644
index 0000000..496574d
--- /dev/null
+++ b/sound/soc/codecs/tas2505.h
@@ -0,0 +1,90 @@
+/*
+ * ALSA SoC TAS2505 codec driver
+ *
+ * Author: Hieu Tran Dang <dangtranhieu2012@gmail.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED AS IS AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef _TAS2505_H
+#define _TAS2505_H
+
+#define TAS2505_RATES	SNDRV_PCM_RATE_8000_96000
+#define TAS2505_FORMATS SNDRV_PCM_FMTBIT_S24_LE
+
+#define TAS2505_REG(page, reg)	((page * 128) + reg)
+
+#define TAS2505_PAGECTL			TAS2505_REG(0, 0)
+#define TAS2505_RESET			TAS2505_REG(0, 1)
+#define TAS2505_CLKMUX			TAS2505_REG(0, 4)
+#define TAS2505_PLLPR			TAS2505_REG(0, 5)
+#define TAS2505_PLLJ			TAS2505_REG(0, 6)
+#define TAS2505_PLLDMSB			TAS2505_REG(0, 7)
+#define TAS2505_PLLDLSB			TAS2505_REG(0, 8)
+#define TAS2505_NDAC			TAS2505_REG(0, 11)
+#define TAS2505_MDAC			TAS2505_REG(0, 12)
+#define TAS2505_DOSRMSB			TAS2505_REG(0, 13)
+#define TAS2505_DOSRLSB			TAS2505_REG(0, 14)
+#define TAS2505_IFACE1			TAS2505_REG(0, 27)
+#define TAS2505_IFACE3			TAS2505_REG(0, 29)
+#define TAS2505_BCLKNDIV		TAS2505_REG(0, 30)
+#define TAS2505_DACFLAG1		TAS2505_REG(0, 37)
+#define TAS2505_DACFLAG2		TAS2505_REG(0, 38)
+#define TAS2505_STICKYFLAG1		TAS2505_REG(0, 42)
+#define TAS2505_INTFLAG1		TAS2505_REG(0, 43)
+#define TAS2505_STICKYFLAG2		TAS2505_REG(0, 44)
+#define TAS2505_INTFLAG2		TAS2505_REG(0, 46)
+#define TAS2505_DACINSTRSET		TAS2505_REG(0, 60)
+#define TAS2505_DACSETUP1		TAS2505_REG(0, 63)
+#define TAS2505_DACSETUP2		TAS2505_REG(0, 64)
+#define TAS2505_DACVOL			TAS2505_REG(0, 65)
+#define TAS2505_REF_POR_LDO_BGAP_CTRL	TAS2505_REG(1, 1)
+#define TAS2505_LDO_CTRL		TAS2505_REG(1, 2)
+#define TAS2505_PLAYBACKCONF1		TAS2505_REG(1, 3)
+#define TAS2505_SPKAMPCTRL1		TAS2505_REG(1, 45)
+#define TAS2505_SPKVOL1			TAS2505_REG(1, 46)
+#define TAS2505_SPKVOL2			TAS2505_REG(1, 48)
+#define TAS2505_DACANLGAINFLAG		TAS2505_REG(1, 63)
+
+#define TAS2505_PLLPR_P_MASK				0x70
+#define TAS2505_PLLPR_R_MASK				0xf
+#define TAS2505_PLL_DAC_MASK				0x7f
+#define TAS2505_BCLKNDIV_MASK				0x7f
+#define TAS2505_IFACE1_DATALEN_MASK			0x30
+#define TAS2505_IFACE1_WCLKDIR_MASK			0x4
+#define TAS2505_IFACE1_BCLKDIR_MASK			0x8
+#define TAS2505_IFACE1_INTERFACE_MASK			0xc0
+#define TAS2505_IFACE3_BDIVCLKIN_MASK			0x1
+#define TAS2505_IFACE3_BCLKINV_MASK			0x8
+#define TAS2505_DACSETUP2_MUTE_MASK			0x8
+#define TAS2505_PM_MASK					0x80
+#define TAS2505_LDO_PLL_HP_LVL_MASK			0x8
+#define TAS2505_REF_POR_LDO_BGAP_MASTER_REF_MASK	0x10
+
+#define TAS2505_PLLPR_P_SHIFT		4
+#define TAS2505_PLL_CLKIN_SHIFT		2
+#define TAS2505_IFACE1_DATALEN_SHIFT	4
+#define TAS2505_IFACE1_INTERFACE_SHIFT	6
+#define TAS2505_IFACE3_BCLKINV_SHIFT	4
+
+#define TAS2505_WORD_LEN_20BITS		1
+#define TAS2505_WORD_LEN_24BITS		2
+#define TAS2505_WORD_LEN_32BITS		3
+
+#define TAS2505_DSP_MODE		1
+#define TAS2505_RJF_MODE		2
+#define TAS2505_LJF_MODE		3
+
+#define TAS2505_PLL_CLKIN_MCLK		0
+#define TAS2505_PLL_CLKIN_BCLK		1
+#define TAS2505_PLL_CLKIN_GPIO		2
+#define TAS2505_PLL_CLKIN_DIN		3
+#define TAS2505_CODEC_CLKIN_PLL		3
+
+#endif

> I enabled the driver in the kernel menuconfig

> patched the device tree to add the driver (see attached)
/*
 * 23/9/2022
 * Ing. Federico Arias
 * Device tree para botonera y preasignador con pantalla HDMI
 * version 5

 * configura señales de HDMI y audio por i2s
 */

/dts-v1/;

#include "am33xx.dtsi"
#include "am335x-bone-common.dtsi"
#include <dt-bindings/display/tda998x.h>


/ {
	model = "TI AM335x BeagleBone Black";
	compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";
};

&cpu0_opp_table {
	oppnitro-1000000000 {
		opp-supported-hw = <0x06 0x0100>;
	};
};

&ldo3_reg {
	regulator-min-microvolt = <1800000>;
	regulator-max-microvolt = <1800000>;
	regulator-always-on;
};

&mmc1 {
	vmmc-supply = <&vmmcsd_fixed>;
};

&mmc2 {
	vmmc-supply = <&vmmcsd_fixed>;
	pinctrl-names = "default";
	pinctrl-0 = <&emmc_pins>;
	bus-width = <8>;
	status = "okay";
};

&am33xx_pinmux {
	nxp_hdmi_bonelt_pins: nxp_hdmi_bonelt_pins {
		pinctrl-single,pins = <
			AM33XX_IOPAD(0x9b0, PIN_OUTPUT_PULLDOWN | MUX_MODE3)	/* xdma_event_intr0 */
			AM33XX_IOPAD(0x8a0, PIN_OUTPUT | MUX_MODE0)		/* lcd_data0.lcd_data0 */
			AM33XX_IOPAD(0x8a4, PIN_OUTPUT | MUX_MODE0)		/* lcd_data1.lcd_data1 */
			AM33XX_IOPAD(0x8a8, PIN_OUTPUT | MUX_MODE0)		/* lcd_data2.lcd_data2 */
			AM33XX_IOPAD(0x8ac, PIN_OUTPUT | MUX_MODE0)		/* lcd_data3.lcd_data3 */
			AM33XX_IOPAD(0x8b0, PIN_OUTPUT | MUX_MODE0)		/* lcd_data4.lcd_data4 */
			AM33XX_IOPAD(0x8b4, PIN_OUTPUT | MUX_MODE0)		/* lcd_data5.lcd_data5 */
			AM33XX_IOPAD(0x8b8, PIN_OUTPUT | MUX_MODE0)		/* lcd_data6.lcd_data6 */
			AM33XX_IOPAD(0x8bc, PIN_OUTPUT | MUX_MODE0)		/* lcd_data7.lcd_data7 */
			AM33XX_IOPAD(0x8c0, PIN_OUTPUT | MUX_MODE0)		/* lcd_data8.lcd_data8 */
			AM33XX_IOPAD(0x8c4, PIN_OUTPUT | MUX_MODE0)		/* lcd_data9.lcd_data9 */
			AM33XX_IOPAD(0x8c8, PIN_OUTPUT | MUX_MODE0)		/* lcd_data10.lcd_data10 */
			AM33XX_IOPAD(0x8cc, PIN_OUTPUT | MUX_MODE0)		/* lcd_data11.lcd_data11 */
			AM33XX_IOPAD(0x8d0, PIN_OUTPUT | MUX_MODE0)		/* lcd_data12.lcd_data12 */
			AM33XX_IOPAD(0x8d4, PIN_OUTPUT | MUX_MODE0)		/* lcd_data13.lcd_data13 */
			AM33XX_IOPAD(0x8d8, PIN_OUTPUT | MUX_MODE0)		/* lcd_data14.lcd_data14 */
			AM33XX_IOPAD(0x8dc, PIN_OUTPUT | MUX_MODE0)		/* lcd_data15.lcd_data15 */
			AM33XX_IOPAD(0x8e0, PIN_OUTPUT_PULLDOWN | MUX_MODE0)	/* lcd_vsync.lcd_vsync */
			AM33XX_IOPAD(0x8e4, PIN_OUTPUT_PULLDOWN | MUX_MODE0)	/* lcd_hsync.lcd_hsync */
			AM33XX_IOPAD(0x8e8, PIN_OUTPUT_PULLDOWN | MUX_MODE0)	/* lcd_pclk.lcd_pclk */
			AM33XX_IOPAD(0x8ec, PIN_OUTPUT_PULLDOWN | MUX_MODE0)	/* lcd_ac_bias_en.lcd_ac_bias_en */
		>;
	};
	nxp_hdmi_bonelt_off_pins: nxp_hdmi_bonelt_off_pins {
		pinctrl-single,pins = <
			AM33XX_IOPAD(0x9b0, PIN_OUTPUT_PULLDOWN | MUX_MODE3)	/* xdma_event_intr0 */
		>;
	};

	mcasp0_pins: mcasp0_pins {
		pinctrl-single,pins = <
			AM33XX_IOPAD(0x9ac, PIN_INPUT_PULLUP | MUX_MODE0) /* mcasp0_ahcklx.mcasp0_ahclkx */
			AM33XX_IOPAD(0x99c, PIN_OUTPUT_PULLDOWN | MUX_MODE2) /* mcasp0_ahclkr.mcasp0_axr2*/
			AM33XX_IOPAD(0x994, PIN_OUTPUT_PULLUP | MUX_MODE0) /* mcasp0_fsx.mcasp0_fsx */
			AM33XX_IOPAD(0x990, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mcasp0_aclkx.mcasp0_aclkx */
			AM33XX_IOPAD(0x86c, PIN_OUTPUT_PULLDOWN | MUX_MODE7) /* gpmc_a11.GPIO1_27 */
		>;
	};
	tas_pins: tas_pins {
		pinctrl-single,pins = <
			AM33XX_IOPAD(0x84c, PIN_OUTPUT_PULLDOWN | MUX_MODE7) /* tas2505 reset pin gpio1.19 */
		>;
	};
};

&lcdc {
	status = "okay";

	/* para 24 bits cambiar por crossed*/

	blue-and-red-wiring = "straight";

	port {
		lcdc_0: endpoint@0 {
			remote-endpoint = <&hdmi_0>;
		};
	};
};

&i2c0 {
	tda19988: tda19988 {
		compatible = "nxp,tda998x";
		reg = <0x70>;

		pinctrl-names = "default", "off";
		pinctrl-0 = <&nxp_hdmi_bonelt_pins>;
		pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>;

		/* para 24 bits descomentar siguiente linea*/
		/* video-ports = <0x234501>; */

		#sound-dai-cells = <0>;
		audio-ports = <	TDA998x_I2S	0x03>;

		ports {
			port@0 {
				hdmi_0: endpoint@0 {
					remote-endpoint = <&lcdc_0>;
				};
			};
		};
	};
};

&i2c2 {
	tas2505: tas2505@18 {
		#sound-dai-cells = <0>;
		compatible = "ti,tas2505";		
		reg = <0x18>;
		gpio-reset = <&gpio1 19 GPIO_ACTIVE_LOW>;
		status = "ok";
	};
};

&rtc {
	system-power-controller;
};

&mcasp0	{
	#sound-dai-cells = <0>;
	pinctrl-names = "default";
	pinctrl-0 = <&mcasp0_pins>;
	status = "okay";
	op-mode = <0>;	/* MCASP_IIS_MODE */
	tdm-slots = <8>;
	serial-dir = <	/* 0: INACTIVE, 1: TX, 2: RX */
			2 2 1 0
		>;
	tx-num-evt = <32>;
	rx-num-evt = <32>;
};

/ {
	memory@80000000 {
		device_type = "memory";
		reg = <0x80000000 0x20000000>; /* 512 MB */
	};

	clk_mcasp0_fixed: clk_mcasp0_fixed {
		#clock-cells = <0>;
		compatible = "fixed-clock";
		clock-frequency = <24576000>;
	};

	clk_mcasp0: clk_mcasp0 {
		#clock-cells = <0>;
		compatible = "gpio-gate-clock";
		clocks = <&clk_mcasp0_fixed>;
		enable-gpios = <&gpio1 27 0>; /* BeagleBone Black Clk enable on GPIO1_27 */
	};

	sound {
		compatible = "simple-audio-card";
		simple-audio-card,name = "TAS2505";
		simple-audio-card,dai-link@0 {
			format = "i2s";
			bitclock-master = <&sound0_master>;
			frame-master = <&sound0_master>;
			sound0_master: cpu {
				sound-dai = <&mcasp0>;
				clocks = <&clk_mcasp0>;
			};

			codec {
				sound-dai = <&tas2505>;
			};
		};
	};
};

> compiled, flashed and booted the image on the BBB successfully.

The driver is loaded (checked with aplay -L), but speaker-test throws this error:
Rate 48000Hz not available for playback: Invalid argument
Setting of hwparams failed: Invalid argument
aplay show this error:
main:828: audio open error: Device or resource busy

> Apparently there is some misconfiguration of the driver / registers

this is what dsmeg show:

tas2505-codec 2-0018: Sample rate (32000), and format not supported
tas2505-codec 2-0018: ASoC: can't set tas2505-codec.2-0018 hw params: -22
Division by zero in kernel.

> i2cdetect -r 2 show UU on reg 0x18

> On the hardware side I have connected the lines of I2C2 (SCA and SCL), RESET, I2S (DIN, WCLK, BCLK).
> I am not using the MCLK or SCLK lines.
> The oscilloscope does not show anything in the lines mentioned above, except the I2C, when an Alsa command is instructed

I've  been getting into embedded software with Linux for a short time, so I don't know much more than this and I'm running out of ideas.
Could you please help me move forward with this?
Thank you very much for your time

  • Hi Federico,

    We'll check your questions and provide further comments within the next couple of days.

    Best regards,
    -Ivan Salazar
    Applications Engineer

  • Hi Federico,

    Glad to answer your question, can you share your test bitstream or test wav file? It seemed the sample rate of your test file is 32KHZ, we need your analyze test file.

  • Hi Kevin,

    Thanks for helping with this. This is the test file I used with aplay:

    I've also tried other wav files and the speaker-test utility (noise, wav and sine mode) but always get the same error.

    I want to clarify that if I use the BBB hdmi sound card (TDA19988) or a USB card, the sound works fine, so it is not an internal kernel problem.

    I also want to mention, in case it is relevant, that I have configured and compiled the driver as build-in, not as kernel module. Since I could not get it to load the driver module on boot.

    Kind regards

  • Hi Federico,

    It's correct to configure and compile the driver as build-in.

    The raw driver is only support MCLK, we made some modifies in current code, and added BCLK support in current version, Please compare the difference of these two drivers, and use the new code in your platform.

    Also, you should add "clock-source = <1>;/* 0: MCLK, 1: BCLK */" under the node tas2505@18 in DTS. So that the code can identify which clock-source used. After these are done well, please share the print log if there's any question or issues.

    tas2505.c

    /*
     * ALSA SoC TAS2505 codec driver
     *
     * Author: Hieu Tran Dang <dangtranhieu2012@gmail.com>
     *
     * This package is free software; you can redistribute it and/or modify
     * it under the terms of the GNU General Public License version 2 as
     * published by the Free Software Foundation.
     *
     * THIS PACKAGE IS PROVIDED AS IS AND WITHOUT ANY EXPRESS OR
     * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
     * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
     */
    #include <linux/version.h>
    #include <linux/module.h>
    #include <linux/i2c.h>
    #include <linux/clk.h>
    #include <linux/of.h>
    #include <linux/of_gpio.h>
    #include <sound/soc.h>
    #include <sound/tlv.h>
    #include <sound/pcm_params.h>
    #include "tas2505.h"
    
    enum {
    	MCLK = 0,
    	BCLK = 1,
    };
    
    static int tas2505_spkdrv_getvol(struct snd_kcontrol *kcontrol,
    	struct snd_ctl_elem_value *ucontrol)
    {
    	struct snd_soc_component *component =
    		snd_soc_kcontrol_component(kcontrol);
    	struct soc_mixer_control *mc =
    		(struct soc_mixer_control *)kcontrol->private_value;
    	unsigned int val;
    
    	val = snd_soc_component_read(component, TAS2505_SPKVOL1);
    
    	val = (val > mc->max) ? mc->max : val;
    	val = mc->invert ? mc->max - val : val;
    	ucontrol->value.integer.value[0] = val;
    
    	return 0;
    }
    
    static int tas2505_spkdrv_putvol(struct snd_kcontrol *kcontrol,
    	struct snd_ctl_elem_value *ucontrol)
    {
    	struct snd_soc_component *component =
    		snd_soc_kcontrol_component(kcontrol);
    	struct soc_mixer_control *mc =
    		(struct soc_mixer_control *)kcontrol->private_value;
    	u8 val;
    
    	val = (ucontrol->value.integer.value[0] & 0x7f);
    	val = mc->invert ? mc->max - val : val;
    	val = (val < 0) ? 0 : val;
    	snd_soc_component_write(component, TAS2505_SPKVOL1, val);
    
    	return 0;
    }
    
    static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6350, 50, 0);
    static const DECLARE_TLV_DB_LINEAR(spk_drv_vol_tlv, TLV_DB_GAIN_MUTE, 0);
    static const DECLARE_TLV_DB_SCALE(spk_amp_vol_tlv, 0, 600, 1);
    
    static const struct snd_kcontrol_new tas2505_snd_controls[] = {
    	SOC_SINGLE_S8_TLV("DAC Playback Volume", TAS2505_DACVOL,
    		-127, 48, dac_vol_tlv),
    	SOC_SINGLE_RANGE_EXT_TLV("Speaker Driver Volume", TAS2505_SPKVOL1,
    		0, 0, 117, 1,
    		tas2505_spkdrv_getvol, tas2505_spkdrv_putvol, spk_drv_vol_tlv),
    	SOC_SINGLE_TLV("Speaker Amplifer Volume", TAS2505_SPKVOL2,
    		4, 5, 0, spk_amp_vol_tlv),
    };
    
    static const struct snd_soc_dapm_widget tas2505_dapm_widgets[] = {
    	SND_SOC_DAPM_DAC("DAC Channel", "Playback",
    		TAS2505_DACSETUP1, 7, 0),
    	SND_SOC_DAPM_OUT_DRV("Speaker Driver", TAS2505_SPKAMPCTRL1,
    		1, 0, NULL, 0),
    	SND_SOC_DAPM_OUTPUT("Speaker"),
    };
    
    static const struct snd_soc_dapm_route tas2505_audio_map[] = {
    	{ "Speaker Driver", NULL, "DAC Channel" },
    	{ "Speaker", NULL, "Speaker Driver" },
    };
    
    static const struct reg_default tas2505_reg_defaults[] = {
    	{ TAS2505_CLKMUX, 0x00 },
    	{ TAS2505_PLLPR, 0x11 },
    	{ TAS2505_PLLJ, 0x04 },
    	{ TAS2505_PLLDMSB, 0x00 },
    	{ TAS2505_PLLDLSB, 0x00 },
    	{ TAS2505_NDAC, 0x01 },
    	{ TAS2505_MDAC, 0x01 },
    	{ TAS2505_DOSRMSB, 0x00 },
    	{ TAS2505_DOSRLSB, 0x80 },
    	{ TAS2505_IFACE1, 0x00 },
    	{ TAS2505_IFACE3, 0x00 },
    	{ TAS2505_DACSETUP1, 0x14 },
    	{ TAS2505_DACSETUP2, 0x0c },
    	{ TAS2505_DACVOL, 0x00 },
    	{ TAS2505_REF_POR_LDO_BGAP_CTRL, 0x00 },
    	{ TAS2505_LDO_CTRL, 0x0c },
    	{ TAS2505_SPKAMPCTRL1, 0x00 },
    	{ TAS2505_SPKVOL1, 0x00 },
    	{ TAS2505_SPKVOL2, 0x00 },
    	{ TAS2505_DACFLAG1, 0x00 },
    	{ TAS2505_DACFLAG2, 0x00 },
    	{ TAS2505_STICKYFLAG1, 0x00 },
    	{ TAS2505_STICKYFLAG2, 0x00 },
    	{ TAS2505_INTFLAG1, 0x00 },
    	{ TAS2505_INTFLAG2, 0x00 },
    	{ TAS2505_DACANLGAINFLAG, 0x00 },
    };
    
    static bool tas2505_volatile(struct device *dev, unsigned int reg)
    {
    	switch (reg) {
    		case TAS2505_PAGECTL:
    		case TAS2505_RESET:
    		case TAS2505_DACFLAG1:
    		case TAS2505_DACFLAG2:
    		case TAS2505_STICKYFLAG1:
    		case TAS2505_STICKYFLAG2:
    		case TAS2505_INTFLAG1:
    		case TAS2505_INTFLAG2:
    		case TAS2505_DACANLGAINFLAG:
    			return true;
    	}
    	return false;
    }
    
    static bool tas2505_writeable(struct device *dev, unsigned int reg)
    {
    	switch (reg) {
    		case TAS2505_DACFLAG1:
    		case TAS2505_DACFLAG2:
    		case TAS2505_STICKYFLAG1:
    		case TAS2505_STICKYFLAG2:
    		case TAS2505_INTFLAG1:
    		case TAS2505_INTFLAG2:
    		case TAS2505_DACANLGAINFLAG:
    			return false;
    	}
    	return true;
    }
    
    static const struct regmap_range_cfg tas2505_ranges[] = {
    	{
    		.range_min = 0,
    		.range_max = 69 * 128,
    		.selector_reg = TAS2505_PAGECTL,
    		.selector_mask = 0xff,
    		.selector_shift = 0,
    		.window_start = 0,
    		.window_len = 128,
    	},
    };
    
    static const struct regmap_config tas2505_i2c_regmap = {
    	.reg_bits = 8,
    	.val_bits = 8,
    	.writeable_reg = tas2505_writeable,
    	.volatile_reg = tas2505_volatile,
    	.reg_defaults = tas2505_reg_defaults,
    	.num_reg_defaults = ARRAY_SIZE(tas2505_reg_defaults),
    	.cache_type = REGCACHE_RBTREE,
    	.ranges = tas2505_ranges,
    	.num_ranges = ARRAY_SIZE(tas2505_ranges),
    	.max_register = 69 * 128,
    };
    
    struct tas2505_rate_divs {
    	u32 mclk_p;
    	u32 rate;
    	u8 pll_r;
    	u8 pll_j;
    	u16 pll_d;
    	u8 mdac;
    	u8 ndac;
    	u16 dosr;
    };
    
    static const struct tas2505_rate_divs tas2505_divs[] = {
    	{ 12288000, 44100, 1, 7, 35, 4, 4, 128 },
    	{ 12288000, 48000, 1, 7, 0, 7, 2, 128 },
    };
    
    static const struct tas2505_rate_divs tas2505_divs_bclksrc[] = {
    	{ 1536000, 48000, 1, 56, 0, 2, 7, 128 },
    	{ 1411200, 44100, 1, 60, 0, 5, 3, 128 },
    	{ 3072000, 48000, 1, 28, 0, 2, 7, 128 },
    	{ 2822400, 44100, 1, 30, 0, 5, 3, 128 },
    	{ 1024000, 32000, 2, 40, 0, 5, 4, 128 },
    	{ 2048000, 32000, 1, 40, 0, 5, 4, 128 },
    
    };
    
    struct tas2505_priv {
    	void *codec;
    	struct device *dev;
    	struct regmap *regmap;
    	u32 sysclk;
    	u32 clk_src;
    	u32 rate;
    	int rate_div_line;
    	int pll_clkin;
    	int frame_size;
    	u8 p_div;
    };
    
    static int tas2505_setup_pll(struct snd_soc_component *codec)
    {
    	struct tas2505_priv *tas2505 = snd_soc_component_get_drvdata(codec);
    	int mclk_p = tas2505->sysclk / tas2505->p_div;
    	struct tas2505_rate_divs *rate_divs;
    	int match = -1;
    	u8 p_div;
    	int i;
    
    	if (tas2505->clk_src == MCLK) {
    		for (i = 0; i < ARRAY_SIZE(tas2505_divs); i++) {
    			if (
    				tas2505_divs[i].rate == tas2505->rate &&
    				tas2505_divs[i].mclk_p == mclk_p
    			) {
    				match = i;
    				break;
    			}
    		}
    
    		if (match == -1) {
    			dev_err(codec->dev,
    				"Sample rate (%u) and format not supported\n",
    				tas2505->rate);
    			return -EINVAL;
    		}
    
    		tas2505->rate_div_line = match;
    		rate_divs = (struct tas2505_rate_divs *)tas2505_divs;
    	}else {
    		for (i = 0; i < ARRAY_SIZE(tas2505_divs_bclksrc); i++) {
    			if (tas2505_divs_bclksrc[i].rate ==
    				tas2505->rate) {
    				match = i;
    				break;
    			}
    		}
    
    		if (match == -1) {
    			dev_err(codec->dev,
    				"Sample rate (%u) and format not supported\n",
    				tas2505->rate);
    			return -EINVAL;
    		}
    		rate_divs = (struct tas2505_rate_divs *)tas2505_divs_bclksrc;
    	}
    
    	p_div = (tas2505->p_div == 8) ? 0 : tas2505->p_div;
    	p_div <<= TAS2505_PLLPR_P_SHIFT;
    
    	snd_soc_component_update_bits(codec,
    		TAS2505_PLLPR, TAS2505_PLLPR_P_MASK,
    		p_div);
    	snd_soc_component_update_bits(codec, TAS2505_PLLPR,
    		TAS2505_PLLPR_R_MASK,
    		rate_divs[match].pll_r);
    	snd_soc_component_write(codec, TAS2505_PLLJ,
    		rate_divs[match].pll_j);
    	snd_soc_component_write(codec, TAS2505_PLLDMSB,
    		rate_divs[match].pll_d >> 8);
    	snd_soc_component_write(codec, TAS2505_PLLDLSB,
    		rate_divs[match].pll_d & 0xff);
    	snd_soc_component_update_bits(codec, TAS2505_NDAC,
    		TAS2505_PLL_DAC_MASK,
    		rate_divs[match].ndac);
    	snd_soc_component_update_bits(codec, TAS2505_MDAC,
    		TAS2505_PLL_DAC_MASK,
    		rate_divs[match].mdac);
    	snd_soc_component_write(codec, TAS2505_DOSRMSB,
    		rate_divs[match].dosr >> 8);
    	snd_soc_component_write(codec, TAS2505_DOSRLSB,
    		rate_divs[match].dosr & 0xff);
    	snd_soc_component_update_bits(codec, TAS2505_BCLKNDIV,
    		TAS2505_BCLKNDIV_MASK,
    		(rate_divs[match].dosr * rate_divs[match].mdac) /
    		tas2505->frame_size);
    
    	return 0;
    }
    
    static int tas2505_hw_params(struct snd_pcm_substream *substream,
    	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
    {
    	struct snd_soc_component *codec = dai->component;
    	struct tas2505_priv *tas2505 = snd_soc_component_get_drvdata(codec);
    	u8 data = 0;
    
    	switch (params_width(params)) {
    		case 16:
    			break;
    
    		case 20:
    			data = TAS2505_WORD_LEN_20BITS;
    			break;
    
    		case 24:
    			data = TAS2505_WORD_LEN_24BITS;
    			break;
    
    		case 32:
    			data = TAS2505_WORD_LEN_32BITS;
    			break;
    
    		default:
    			dev_err(codec->dev, "Unsupported width %d\n",
    				params_width(params));
    			return -EINVAL;
    	}
    	dev_info(codec->dev, "%s: bit width = %d\n",
    				__func__, params_width(params));
    	data <<= TAS2505_IFACE1_DATALEN_SHIFT;
    
    	snd_soc_component_update_bits(codec, TAS2505_IFACE1,
    		TAS2505_IFACE1_DATALEN_MASK, data);
    	tas2505->rate = params_rate(params);
    	tas2505->frame_size = snd_soc_params_to_frame_size(params);
    
    	return 0;
    }
    
    static int tas2505_dac_mute(struct snd_soc_dai *dai, int mute,
    	int stream)
    {
    	struct snd_soc_component *codec = dai->component;
    
    	dev_info(codec->dev, "%s: mute = %d\n", __func__, mute);
    	if (mute) {
    		snd_soc_component_update_bits(codec, TAS2505_SPKVOL2,
    			TAS2505_SPKVOL2_MSK,
    			TAS2505_SPKVOL2_MUTE << TAS2505_SPKVOL2_6DB_SHIFT);
    		snd_soc_component_update_bits(codec, TAS2505_DACSETUP2,
    			TAS2505_DACSETUP2_MUTE_MASK,
    			TAS2505_DACSETUP2_MUTE_MASK);
    	} else {
    		/*snd_soc_component_update_bits(codec, TAS2505_CLKMUX,
    			TAS2505_CODEC_CLKIN_MSK | TAS2505_PLL_INPUT_CLK_MSK,
    			(tas2505->pll_clkin << TAS2505_PLL_CLKIN_SHIFT) |
    			TAS2505_CODEC_CLKIN_PLL);*/
    		snd_soc_component_update_bits(codec, TAS2505_DACSETUP2,
    			TAS2505_DACSETUP2_MUTE_MASK, 0x0);
    		snd_soc_component_update_bits(codec, TAS2505_SPKVOL2,
    			TAS2505_SPKVOL2_MSK,
    			TAS2505_SPKVOL2_6DB << TAS2505_SPKVOL2_6DB_SHIFT);
    	}
    
    	return 0;
    }
    
    static int tas2505_set_dai_fmt(struct snd_soc_dai *codec_dai,
    	unsigned int fmt)
    {
    	struct snd_soc_component *codec = codec_dai->component;
    	u8 iface_reg1 = 0;
    	u8 iface_reg3 = 0;
    
    	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
    		case SND_SOC_DAIFMT_CBS_CFS:
    			break;
    
    		case SND_SOC_DAIFMT_CBM_CFM:
    			iface_reg1 |= TAS2505_IFACE1_BCLKDIR_MASK;
    			iface_reg1 |= TAS2505_IFACE1_WCLKDIR_MASK;
    			break;
    
    		default:
    			return -EINVAL;
    	}
    
    	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
    		case SND_SOC_DAIFMT_NB_NF:
    			break;
    
    		case SND_SOC_DAIFMT_IB_NF:
    			iface_reg3 |= TAS2505_IFACE3_BCLKINV_MASK;
    			break;
    
    		default:
    			return -EINVAL;
    	}
    
    	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
    		case SND_SOC_DAIFMT_I2S:
    			break;
    
    		case SND_SOC_DAIFMT_DSP_A:
    		case SND_SOC_DAIFMT_DSP_B:
    			iface_reg1 |= (TAS2505_DSP_MODE <<
    				TAS2505_IFACE1_INTERFACE_SHIFT);
    			break;
    
    		case SND_SOC_DAIFMT_RIGHT_J:
    			iface_reg1 |= (TAS2505_RJF_MODE <<
    				TAS2505_IFACE1_INTERFACE_SHIFT);
    			break;
    
    		case SND_SOC_DAIFMT_LEFT_J:
    			iface_reg1 |= (TAS2505_LJF_MODE <<
    				TAS2505_IFACE1_INTERFACE_SHIFT);
    			break;
    
    		default:
    			dev_err(codec->dev, "Invalid DAI interface format\n");
    			return -EINVAL;
    	}
    
    	snd_soc_component_write(codec, TAS2505_IFACE1, iface_reg1);
    	snd_soc_component_update_bits(codec, TAS2505_IFACE3,
    		TAS2505_IFACE3_BCLKINV_MASK | TAS2505_IFACE3_BDIVCLKIN_MASK,
    		iface_reg3);
    
    	return 0;
    }
    
    static int tas2505_set_dai_sysclk(struct snd_soc_dai *codec_dai,
    	int clk_id, unsigned int freq, int dir)
    {
    	struct snd_soc_component *codec = codec_dai->component;
    	struct tas2505_priv *tas2505 = snd_soc_component_get_drvdata(codec);
    	int i, x;
    	int ret = 0;
    
    	dev_info(tas2505->dev, "%s: clk_id: %d, freq: %d\n",
    		__func__, clk_id, freq);
    
    	if (tas2505->clk_src == MCLK) {
    		for (i = 0; i < ARRAY_SIZE(tas2505_divs); i++) {
    			for (x = 1; x < 9; x++) {
    				if ((freq / x) == tas2505_divs[i].mclk_p) {
    					tas2505->p_div = x;
    					break;
    				}
    			}
    		}
    
    		if(i != ARRAY_SIZE(tas2505_divs)) {
    		} else {
    			dev_err(tas2505->dev,
    				"Can't produce required "
    				"PLL_CLKIN frequency\n");
    			ret = -EINVAL;
    		}
    		tas2505->pll_clkin = clk_id;
    	} else {
    		tas2505->pll_clkin = 1;
    		tas2505->p_div = 1;
    	}
    
    	if(!ret) {
    		snd_soc_component_write(codec, TAS2505_CLKMUX,
    			(tas2505->pll_clkin << TAS2505_PLL_CLKIN_SHIFT) |
    			TAS2505_CODEC_CLKIN_PLL);
    
    		tas2505->sysclk = freq;
    	}
    	return ret;
    
    }
    
    static void tas2505_clk_on(struct snd_soc_component *codec)
    {
    	u8 mask = TAS2505_PM_MASK;
    	u8 on = TAS2505_PM_MASK;
    
    	snd_soc_component_update_bits(codec, TAS2505_DACSETUP1,
    			TAS2505_DACSETUP1_PATH_CTRL_MSK,
    			TAS2505_DACSETUP1_PATH_CTRL_LRDIV2
    			<<TAS2505_DACSETUP1_PATH_CTRL_SHIFT);
    	tas2505_setup_pll(codec);
    	snd_soc_component_update_bits(codec, TAS2505_PLLPR, mask, on);
    	mdelay(15);
    	snd_soc_component_update_bits(codec, TAS2505_NDAC, mask, on);
    	snd_soc_component_update_bits(codec, TAS2505_MDAC, mask, on);
    	snd_soc_component_update_bits(codec, TAS2505_BCLKNDIV, mask, on);
    	/*snd_soc_component_update_bits(codec, TAS2505_SPKVOL2,
    		TAS2505_SPKVOL2_MSK,
    		TAS2505_SPKVOL2_6DB << TAS2505_SPKVOL2_6DB_SHIFT);*/
    }
    
    static void tas2505_clk_off(struct snd_soc_component *codec)
    {
    	u8 mask = TAS2505_PM_MASK;
    
    	snd_soc_component_update_bits(codec, TAS2505_BCLKNDIV, mask, 0);
    	snd_soc_component_update_bits(codec, TAS2505_MDAC, mask, 0);
    	snd_soc_component_update_bits(codec, TAS2505_NDAC, mask, 0);
    	snd_soc_component_update_bits(codec, TAS2505_PLLPR, mask, 0);
    }
    
    static void tas2505_power_on(struct snd_soc_component *codec)
    {
    	/*snd_soc_component_write(codec, TAS2505_RESET, 1);
    	usleep_range(500, 1000);*/
    	snd_soc_component_update_bits(codec, TAS2505_LDO_CTRL,
    		TAS2505_LDO_PLL_HP_LVL_MASK, 0);
    	snd_soc_component_update_bits(codec, TAS2505_REF_POR_LDO_BGAP_CTRL,
    		TAS2505_REF_POR_LDO_BGAP_MASTER_REF_MASK,
    		TAS2505_REF_POR_LDO_BGAP_MASTER_REF_MASK);
    }
    
    static void tas2505_power_off(struct snd_soc_component *codec)
    {
    	snd_soc_component_update_bits(codec, TAS2505_REF_POR_LDO_BGAP_CTRL,
    		TAS2505_REF_POR_LDO_BGAP_MASTER_REF_MASK, 0);
    	snd_soc_component_update_bits(codec, TAS2505_LDO_CTRL,
    		TAS2505_LDO_PLL_HP_LVL_MASK,
    		TAS2505_LDO_PLL_HP_LVL_MASK);
    }
    
    static int tas2505_set_bias_level(struct snd_soc_component *codec,
    	enum snd_soc_bias_level level)
    {
    	int current_lvl = snd_soc_component_get_bias_level(codec);
    
    	int rc = 0;
    	switch (level) {
    		case SND_SOC_BIAS_ON:
    			break;
    		case SND_SOC_BIAS_PREPARE:
    			if (current_lvl == SND_SOC_BIAS_STANDBY) {
    				tas2505_clk_on(codec);
    				dev_info(codec->dev, "%s:clk on\n",
    					__func__);
    			} else
    				dev_info(codec->dev,
    					"%s:SND_SOC_BIAS_PREPARE\n", __func__);
    			break;
    		case SND_SOC_BIAS_STANDBY:
    			if (current_lvl == SND_SOC_BIAS_OFF) {
    				tas2505_power_on(codec);
    				dev_info(codec->dev,
    					"%s:power on\n", __func__);
    			}
    			else if (current_lvl == SND_SOC_BIAS_PREPARE) {
    				tas2505_clk_off(codec);
    				dev_info(codec->dev,
    					"%s:clk off\n", __func__);
    			} else
    				dev_info(codec->dev,
    					"%s:SND_SOC_BIAS_STANDBY\n",
    					__func__);
    			break;
    		case SND_SOC_BIAS_OFF:
    			if (current_lvl == SND_SOC_BIAS_STANDBY) {
    				tas2505_power_off(codec);
    				dev_info(codec->dev,
    					"%s:power off\n",
    					__func__);
    			} else
    				dev_info(codec->dev,
    					"%s:SND_SOC_BIAS_OFF\n",
    					__func__);
    			break;
    		default:
    			dev_err(codec->dev, "Invalid bias level\n");
    			rc = -EINVAL;
    	}
    
    	return rc;
    }
    
    static int tas2505_codec_probe(struct snd_soc_component *codec)
    {
    	struct tas2505_priv *tas2505 = snd_soc_component_get_drvdata(codec);
    
    	tas2505->codec = codec;
    	snd_soc_component_write(codec, TAS2505_RESET, 1);
    	return 0;
    }
    
    static struct snd_soc_component_driver soc_codec_driver_tas2505 = {
    	.probe			= tas2505_codec_probe,
    	.set_bias_level		= tas2505_set_bias_level,
    	.suspend_bias_off	= true,
    
    	.controls		= tas2505_snd_controls,
    	.num_controls		= ARRAY_SIZE(tas2505_snd_controls),
    	.dapm_widgets		= tas2505_dapm_widgets,
    	.num_dapm_widgets	= ARRAY_SIZE(tas2505_dapm_widgets),
    	.dapm_routes		= tas2505_audio_map,
    	.num_dapm_routes	= ARRAY_SIZE(tas2505_audio_map),
    };
    
    static const struct snd_soc_dai_ops tas2505_dai_ops = {
    	.hw_params	= tas2505_hw_params,
    	.set_sysclk	= tas2505_set_dai_sysclk,
    	.set_fmt	= tas2505_set_dai_fmt,
    	.mute_stream	= tas2505_dac_mute,
    };
    
    static struct snd_soc_dai_driver tas2505_dai_driver[] = {
    	{
    		.name = "tas2505-hifi",
    		.playback = {
    			.stream_name	= "Playback",
    			.channels_min	= 1,
    			.channels_max	= 2,
    			.rates		= TAS2505_RATES,
    			.formats	= TAS2505_FORMATS,
    		},
    		.ops = &tas2505_dai_ops,
    		.symmetric_rates = 1,
    	},
    };
    
    static int tas2505_i2c_probe(struct i2c_client *i2c,
    	const struct i2c_device_id *id)
    {
    	struct tas2505_priv *tas2505;
    	struct device_node *np = i2c->dev.of_node;
    	const struct regmap_config *regmap_config = &tas2505_i2c_regmap;
    	int ret;
    
    	tas2505 = devm_kzalloc(&i2c->dev, sizeof(*tas2505), GFP_KERNEL);
    	if (tas2505 == NULL)
    		return -ENOMEM;
    
    	tas2505->regmap = devm_regmap_init_i2c(i2c, regmap_config);
    	if (IS_ERR(tas2505->regmap)) {
    		ret = PTR_ERR(tas2505->regmap);
    		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
    			ret);
    		return ret;
    	}
    
    	ret = of_get_named_gpio(np, "gpio-reset", 0);
    	if ((ret > 0) && gpio_is_valid(ret)) {
    		devm_gpio_request_one(&i2c->dev, ret, GPIOF_OUT_INIT_HIGH,
    			"reset");
    	}
    
    	ret = of_property_read_u32(np, "clock-source",
    			&tas2505->clk_src);
    
    	dev_info(&i2c->dev, "%s: clock-source = %s\n", __func__,
    		tas2505->clk_src?"BCLK":"MCLK");
    
    	tas2505->dev = &i2c->dev;
    
    	dev_set_drvdata(tas2505->dev, tas2505);
    
    	return snd_soc_register_component( &i2c->dev,
    		&soc_codec_driver_tas2505, tas2505_dai_driver,
    		ARRAY_SIZE(tas2505_dai_driver));
    }
    
    static int tas2505_i2c_remove(struct i2c_client *i2c)
    {
    
    	snd_soc_unregister_component(&i2c->dev);
    
    	return 0;
    }
    
    static const struct of_device_id tas2505_of_match[] = {
    	{ .compatible = "ti,tas2505" },
    	{},
    };
    MODULE_DEVICE_TABLE(of, tas2505_of_match);
    
    static const struct i2c_device_id tas2505_i2c_id[] = {
    	{ "tas2505", 0 },
    	{}
    };
    MODULE_DEVICE_TABLE(i2ic, tas2505_i2c_id);
    
    static struct i2c_driver tas2505_i2c_driver = {
    	.driver = {
    		.name		= "tas2505-codec",
    		.of_match_table	= of_match_ptr(tas2505_of_match),
    	},
    	.probe		= tas2505_i2c_probe,
    	.remove		= tas2505_i2c_remove,
    	.id_table	= tas2505_i2c_id,
    };
    
    module_i2c_driver(tas2505_i2c_driver);
    
    MODULE_DESCRIPTION("ASoC TAS2505 codec driver");
    MODULE_AUTHOR("Hieu Tran Dang <dangtranhieu2012@gmail.com>");
    MODULE_LICENSE("GPL");
    

    tas2505.h

    /*
     * ALSA SoC TAS2505 codec driver
     *
     * Author: Hieu Tran Dang <dangtranhieu2012@gmail.com>
     *
     * This package is free software; you can redistribute it and/or modify
     * it under the terms of the GNU General Public License version 2 as
     * published by the Free Software Foundation.
     *
     * THIS PACKAGE IS PROVIDED AS IS AND WITHOUT ANY EXPRESS OR
     * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
     * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
     */
    
    #ifndef _TAS2505_H
    #define _TAS2505_H
    
    #define TAS2505_RATES	(SNDRV_PCM_RATE_8000_96000)
    #define TAS2505_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
    	SNDRV_PCM_FMTBIT_S24_LE | \
    	SNDRV_PCM_FMTBIT_S32_LE)
    
    #define TAS2505_REG(page, reg)	((page * 128) + reg)
    
    #define TAS2505_PAGECTL			TAS2505_REG(0, 0)
    #define TAS2505_RESET			TAS2505_REG(0, 1)
    #define TAS2505_CLKMUX			TAS2505_REG(0, 4)
    #define TAS2505_PLLPR			TAS2505_REG(0, 5)
    #define TAS2505_PLLJ			TAS2505_REG(0, 6)
    #define TAS2505_PLLDMSB			TAS2505_REG(0, 7)
    #define TAS2505_PLLDLSB			TAS2505_REG(0, 8)
    #define TAS2505_NDAC			TAS2505_REG(0, 11)
    #define TAS2505_MDAC			TAS2505_REG(0, 12)
    #define TAS2505_DOSRMSB			TAS2505_REG(0, 13)
    #define TAS2505_DOSRLSB			TAS2505_REG(0, 14)
    #define TAS2505_IFACE1			TAS2505_REG(0, 27)
    #define TAS2505_IFACE3			TAS2505_REG(0, 29)
    #define TAS2505_BCLKNDIV		TAS2505_REG(0, 30)
    #define TAS2505_DACFLAG1		TAS2505_REG(0, 37)
    #define TAS2505_DACFLAG2		TAS2505_REG(0, 38)
    #define TAS2505_STICKYFLAG1		TAS2505_REG(0, 42)
    #define TAS2505_INTFLAG1		TAS2505_REG(0, 43)
    #define TAS2505_STICKYFLAG2		TAS2505_REG(0, 44)
    #define TAS2505_INTFLAG2		TAS2505_REG(0, 46)
    #define TAS2505_DACINSTRSET		TAS2505_REG(0, 60)
    #define TAS2505_DACSETUP1		TAS2505_REG(0, 63)
    #define TAS2505_DACSETUP2		TAS2505_REG(0, 64)
    #define TAS2505_DACVOL			TAS2505_REG(0, 65)
    #define TAS2505_REF_POR_LDO_BGAP_CTRL	TAS2505_REG(1, 1)
    #define TAS2505_LDO_CTRL		TAS2505_REG(1, 2)
    #define TAS2505_PLAYBACKCONF1		TAS2505_REG(1, 3)
    #define TAS2505_SPKAMPCTRL1		TAS2505_REG(1, 45)
    #define TAS2505_SPKVOL1			TAS2505_REG(1, 46)
    #define TAS2505_SPKVOL2			TAS2505_REG(1, 48)
    #define TAS2505_DACANLGAINFLAG		TAS2505_REG(1, 63)
    
    #define TAS2505_PLLPR_P_MASK				(0x70)
    #define TAS2505_PLLPR_R_MASK				(0xf)
    #define TAS2505_PLL_DAC_MASK				(0x7f)
    #define TAS2505_BCLKNDIV_MASK				(0x7f)
    #define TAS2505_IFACE1_DATALEN_MASK			(0x30)
    #define TAS2505_IFACE1_WCLKDIR_MASK			(0x4)
    #define TAS2505_IFACE1_BCLKDIR_MASK			(0x8)
    #define TAS2505_IFACE1_INTERFACE_MASK			(0xc0)
    #define TAS2505_IFACE3_BDIVCLKIN_MASK			(0x1)
    #define TAS2505_IFACE3_BCLKINV_MASK			(0x8)
    #define TAS2505_DACSETUP1_PATH_CTRL_MSK			(0x30)
    #define TAS2505_DACSETUP2_MUTE_MASK			(0x8)
    #define TAS2505_PM_MASK					(0x80)
    #define TAS2505_LDO_PLL_HP_LVL_MASK			(0x8)
    #define TAS2505_REF_POR_LDO_BGAP_MASTER_REF_MASK	(0x10)
    #define TAS2505_SPKVOL2_MSK				(0x70)
    #define TAS2505_CODEC_CLKIN_MSK				(0x3)
    #define TAS2505_PLL_INPUT_CLK_MSK			(0xC)
    
    #define TAS2505_PLLPR_P_SHIFT			(4)
    #define TAS2505_PLL_CLKIN_SHIFT			(2)
    #define TAS2505_IFACE1_DATALEN_SHIFT		(4)
    #define TAS2505_IFACE1_INTERFACE_SHIFT		(6)
    #define TAS2505_IFACE3_BCLKINV_SHIFT		(4)
    #define TAS2505_SPKVOL2_6DB_SHIFT		(4)
    #define TAS2505_DACSETUP1_PATH_CTRL_SHIFT	(4)
    
    #define TAS2505_WORD_LEN_20BITS			(1)
    #define TAS2505_WORD_LEN_24BITS			(2)
    #define TAS2505_WORD_LEN_32BITS			(3)
    
    #define TAS2505_DSP_MODE			(1)
    #define TAS2505_RJF_MODE			(2)
    #define TAS2505_LJF_MODE			(3)
    
    #define TAS2505_PLL_CLKIN_MCLK			(0)
    #define TAS2505_PLL_CLKIN_BCLK			(1)
    #define TAS2505_PLL_CLKIN_GPIO			(2)
    #define TAS2505_PLL_CLKIN_DIN			(3)
    #define TAS2505_CODEC_CLKIN_PLL			(3)
    #define TAS2505_SPKVOL2_MUTE			(0)
    #define TAS2505_SPKVOL2_6DB			(1)
    #define TAS2505_DACSETUP1_PATH_CTRL_LRDIV2	(3)
    
    #endif
    

    Best Regards

    Kevin

  • Hi Kevin, 

    Many thanks!!! Your new driver solved our problem. Now it works fine. Grinning

    Just as side note:

    I fixed a bug in the tas2505.c line 39:

    changed it to: snd_soc_component_read(component, TAS2505_SPKVOL1, &val);

    Because it throws an error when compiling. The function snd_soc_component_read() takes two arguments, not three, and the return argument is just the error flag.
    This function changes from linux v5.9

    Best Regards.

  • Hi Federico,

    Thanks for your feedback, and glad our team could make it work for you.

    Best regards,
    -Ivan Salazar
    Applications Engineer