public inbox for u-boot@lists.denx.de
 help / color / mirror / Atom feed
* [PATCH 0/6] Enable audio playback in AM62x SoC
@ 2024-07-08  7:52 Scaria Kochidanadu
  2024-07-08  7:52 ` [PATCH 1/6] sound: ti: Add sound support for am625 board in Uboot Scaria Kochidanadu
                   ` (5 more replies)
  0 siblings, 6 replies; 13+ messages in thread
From: Scaria Kochidanadu @ 2024-07-08  7:52 UTC (permalink / raw)
  To: trini, u-boot
  Cc: sjg, mkorpershoek, abdellatif.elkhlifi, sumit.garg,
	neil.armstrong, caleb.connolly, semen.protsenko,
	marek.vasut+renesas, nathan.morrison, s-kochidanadu, devarsht,
	j-luthra, vigneshr, praneeth, nm, khasim, rogerq,
	francesco.dolcini, afd, jan.kiszka

This series enables audio playback over u-boot for TI AM62x SoC.
It ports MCASP driver which acts as I2S audio signal generator and
TLV320AIC3106 which converts the digital audio to analog.
Lastly, this also adds sound driver to complete the audio graph and enables
audio playback using u-boot command.

Audio can be played over at u-boot prompt using below command :
sound play <duration in ms> <frequency>
This plays a beep sound for the given duration and of the given frequency.

Scaria Kochidanadu (6):
  sound: ti: Add sound support for am625 board in Uboot
  sound: ti: Add TLV320AIC3106 Codec
  sound: ti: Add MCASP driver for transfer of Audio data to sound codec
  arm: dts: k3-am625-sk-u-boot.dtsi: Add sound driver nodes
  configs: am62x_evm_a53_defconfig: Enable sound and I2C commands
  linux: bitmap.h: Add for_each_set_bit_from() function

 MAINTAINERS                          |    5 +
 arch/arm/dts/k3-am625-sk-u-boot.dtsi |   85 ++
 configs/am62x_evm_a53_defconfig      |    9 +
 drivers/sound/Kconfig                |   18 +
 drivers/sound/Makefile               |    3 +
 drivers/sound/davinci-mcasp.h        |  413 ++++++++++
 drivers/sound/mcasp_i2s.c            | 1123 ++++++++++++++++++++++++++
 drivers/sound/ti_sound.c             |  119 +++
 drivers/sound/tlv320aic3106.c        |  758 +++++++++++++++++
 drivers/sound/tlv320aic3106.h        |  336 ++++++++
 include/linux/bitmap.h               |    5 +
 11 files changed, 2874 insertions(+)
 create mode 100644 drivers/sound/davinci-mcasp.h
 create mode 100644 drivers/sound/mcasp_i2s.c
 create mode 100644 drivers/sound/ti_sound.c
 create mode 100644 drivers/sound/tlv320aic3106.c
 create mode 100644 drivers/sound/tlv320aic3106.h

-- 
2.34.1


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 1/6] sound: ti: Add sound support for am625 board in Uboot
  2024-07-08  7:52 [PATCH 0/6] Enable audio playback in AM62x SoC Scaria Kochidanadu
@ 2024-07-08  7:52 ` Scaria Kochidanadu
  2024-07-08 15:46   ` Tom Rini
  2024-07-08  7:52 ` [PATCH 2/6] sound: ti: Add TLV320AIC3106 Codec Scaria Kochidanadu
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 13+ messages in thread
From: Scaria Kochidanadu @ 2024-07-08  7:52 UTC (permalink / raw)
  To: trini, u-boot
  Cc: sjg, mkorpershoek, abdellatif.elkhlifi, sumit.garg,
	neil.armstrong, caleb.connolly, semen.protsenko,
	marek.vasut+renesas, nathan.morrison, s-kochidanadu, devarsht,
	j-luthra, vigneshr, praneeth, nm, khasim, rogerq,
	francesco.dolcini, afd, jan.kiszka

Add a UCLASS_SOUND driver for Texas Instruments SoCs which ties together
the tlv320aic3106 audio codec and MCASP I2S controller. Enable audio
playback functionality by taking a data pointer and data size as the
sound data. The uboot sound play command takes time and frequency as
input and creates the data for a beep sound with the given parameters,
which is then passed to the sound play function.

The code is based on the samsung sound driver[1] in uboot.

Link: https://gitlab.com/u-boot/u-boot/-/blob/master/drivers/sound/samsung_sound.c [1]

Signed-off-by: Scaria Kochidanadu <s-kochidanadu@ti.com>
---
 MAINTAINERS              |   1 +
 drivers/sound/Kconfig    |  10 ++++
 drivers/sound/Makefile   |   1 +
 drivers/sound/ti_sound.c | 119 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 131 insertions(+)
 create mode 100644 drivers/sound/ti_sound.c

diff --git a/MAINTAINERS b/MAINTAINERS
index f8afd7d51e..785afff1a7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -738,6 +738,7 @@ F:	drivers/reset/reset-ti-sci.c
 F:	drivers/rtc/davinci.c
 F:	drivers/serial/serial_omap.c
 F:	drivers/soc/ti/
+F:	drivers/sound/ti_sound.c
 F:	drivers/sysreset/sysreset-ti-sci.c
 F:	drivers/thermal/ti-bandgap.c
 F:	drivers/timer/omap-timer.c
diff --git a/drivers/sound/Kconfig b/drivers/sound/Kconfig
index 0948d8caab..be9f18b6c7 100644
--- a/drivers/sound/Kconfig
+++ b/drivers/sound/Kconfig
@@ -148,4 +148,14 @@ config SOUND_WM8994
 	  audio data and I2C for codec control. At present it only works
 	  with the Samsung I2S driver.
 
+config I2S_TI
+        bool "Enable I2S support for AM62x SoCs"
+        depends on I2S
+        help
+          TI AM62x SoCs support an I2S interface for sending audio
+          data to an audio codec. This option enables support for this,
+          using one of the available audio codec drivers. Enabling this
+          option provides an implementation for sound_init() and
+          sound_play().
+
 endmenu
diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile
index 9b40c8012f..95aaa8521c 100644
--- a/drivers/sound/Makefile
+++ b/drivers/sound/Makefile
@@ -23,3 +23,4 @@ obj-$(CONFIG_SOUND_I8254)	+= i8254_beep.o
 obj-$(CONFIG_SOUND_RT5677)	+= rt5677.o
 obj-$(CONFIG_INTEL_BROADWELL)	+= broadwell_i2s.o broadwell_sound.o
 obj-$(CONFIG_SOUND_IVYBRIDGE)	+= ivybridge_sound.o
+obj-$(CONFIG_I2S_TI)    += ti_sound.o
diff --git a/drivers/sound/ti_sound.c b/drivers/sound/ti_sound.c
new file mode 100644
index 0000000000..a424a99b72
--- /dev/null
+++ b/drivers/sound/ti_sound.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2024 Texas Instruments Incorporated - https://www.ti.com/
+ * Scaria M Kochidanadu, s-kochidanadu@ti.com
+ *
+ * based on the uboot samsung sound driver, which is
+ *
+ * Copyright 2018 Google, LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <asm/u-boot.h> /* boot information for Linux kernel */
+/* Pull in stuff for the build system */
+#ifdef DO_DEPS_ONLY
+#include <env_internal.h>
+#endif
+#include <audio_codec.h>
+#include <dm.h>
+#include <i2s.h>
+#include <log.h>
+#include <sound.h>
+#include <asm/gpio.h>
+
+static int ti_sound_setup(struct udevice *dev)
+{
+	struct sound_uc_priv *uc_priv = dev_get_uclass_priv(dev);
+	struct i2s_uc_priv *i2s_priv = dev_get_uclass_priv(uc_priv->i2s);
+	int ret;
+
+	if (uc_priv->setup_done)
+		return -EALREADY;
+
+	ret = audio_codec_set_params(uc_priv->codec, i2s_priv->id,
+				     i2s_priv->samplingrate,
+				     i2s_priv->samplingrate * i2s_priv->rfs,
+				     i2s_priv->bitspersample,
+				     i2s_priv->channels);
+
+	if (ret) {
+		return ret;
+		printf("failed in set_params\n");
+	}
+	uc_priv->setup_done = true;
+
+	return 0;
+}
+
+static int ti_sound_play(struct udevice *dev, void *data, uint data_size)
+{
+	struct sound_uc_priv *uc_priv = dev_get_uclass_priv(dev);
+
+	return i2s_tx_data(uc_priv->i2s, data, data_size);
+}
+
+static int ti_sound_stop_play(struct udevice *dev)
+{
+	/* This function is necassary to satisfy the function calls
+	 * in the Uboot command: sound play
+	 */
+	return 0;
+}
+
+static int ti_sound_probe(struct udevice *dev)
+{
+	struct sound_uc_priv *uc_priv = dev_get_uclass_priv(dev);
+	struct ofnode_phandle_args args;
+	ofnode node;
+	int ret;
+
+	ret = uclass_get_device_by_phandle(UCLASS_AUDIO_CODEC, dev,
+					   "ti,codec",
+					   &uc_priv->codec);
+	if (ret) {
+		debug("Failed to probe audio codec\n");
+		return ret;
+	}
+
+	node = ofnode_find_subnode(dev_ofnode(dev), "simple-audio-card,cpu");
+	if (!ofnode_valid(node)) {
+		debug("Failed to find /cpu subnode\n");
+		return -EINVAL;
+	}
+
+	ret = ofnode_parse_phandle_with_args(node, "sound-dai",
+					     "#sound-dai-cells", 0, 0, &args);
+	if (ret) {
+		debug("Cannot find phandle: %d\n", ret);
+		return ret;
+	}
+
+	ret = uclass_get_device_by_ofnode(UCLASS_I2S, args.node, &uc_priv->i2s);
+	if (ret) {
+		debug("Cannot find i2s: %d\n", ret);
+		return ret;
+	}
+	debug("Probed sound '%s' with codec '%s' and i2s '%s'\n", dev->name,
+	      uc_priv->codec->name, uc_priv->i2s->name);
+
+	return 0;
+}
+
+static const struct sound_ops ti_sound_ops = {
+	.setup	= ti_sound_setup,
+	.play	= ti_sound_play,
+	.stop_play	= ti_sound_stop_play,
+};
+
+static const struct udevice_id ti_sound_ids[] = {
+	{ .compatible = "simple-audio-card" },
+	{ }
+};
+
+U_BOOT_DRIVER(ti_sound) = {
+	.name		= "ti_sound",
+	.id		= UCLASS_SOUND,
+	.of_match	= ti_sound_ids,
+	.probe		= ti_sound_probe,
+	.ops		= &ti_sound_ops,
+};
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 2/6] sound: ti: Add TLV320AIC3106 Codec
  2024-07-08  7:52 [PATCH 0/6] Enable audio playback in AM62x SoC Scaria Kochidanadu
  2024-07-08  7:52 ` [PATCH 1/6] sound: ti: Add sound support for am625 board in Uboot Scaria Kochidanadu
@ 2024-07-08  7:52 ` Scaria Kochidanadu
  2024-07-08  7:52 ` [PATCH 3/6] sound: ti: Add MCASP driver for transfer of Audio data to sound codec Scaria Kochidanadu
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 13+ messages in thread
From: Scaria Kochidanadu @ 2024-07-08  7:52 UTC (permalink / raw)
  To: trini, u-boot
  Cc: sjg, mkorpershoek, abdellatif.elkhlifi, sumit.garg,
	neil.armstrong, caleb.connolly, semen.protsenko,
	marek.vasut+renesas, nathan.morrison, s-kochidanadu, devarsht,
	j-luthra, vigneshr, praneeth, nm, khasim, rogerq,
	francesco.dolcini, afd, jan.kiszka

This patch adds driver for  TLV320AIC3106 audio Codec.
Initialize and start the codec[1] with appropriate settings and
hardware parameters according to the audio data and the sound card
defined in the devicetree. The codec audio data bus is
programmable for I2S, DSP or TDM modes, and it also has a programmable
PLL for clock generation.
Below default settings are used as u-boot doesn't support file playback:
Codec being the clock and frame master, using the I2S audio format,
sampling rate and width of audio data in one slot.

The data sheet for the codec can be obtained here for further details
about its settings and working-

Link: https://www.ti.com/product/TLV320AIC3106 [1]

The driver is based on the kernel codec driver[2].
Link:
https://gitlab.com/linux-kernel/linux-next/-/blob/master/sound/soc/codecs/tlv320aic3x.c
[2]

Signed-off-by: Scaria Kochidanadu <s-kochidanadu@ti.com>
---
 MAINTAINERS                   |   2 +
 drivers/sound/Kconfig         |   8 +
 drivers/sound/Makefile        |   1 +
 drivers/sound/tlv320aic3106.c | 758 ++++++++++++++++++++++++++++++++++
 drivers/sound/tlv320aic3106.h | 336 +++++++++++++++
 5 files changed, 1105 insertions(+)
 create mode 100644 drivers/sound/tlv320aic3106.c
 create mode 100644 drivers/sound/tlv320aic3106.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 785afff1a7..5d7c260bfc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -739,6 +739,8 @@ F:	drivers/rtc/davinci.c
 F:	drivers/serial/serial_omap.c
 F:	drivers/soc/ti/
 F:	drivers/sound/ti_sound.c
+F:	drivers/sound/tlv320aic3106.c
+F:	drivers/sound/tlv320aic3106.h
 F:	drivers/sysreset/sysreset-ti-sci.c
 F:	drivers/thermal/ti-bandgap.c
 F:	drivers/timer/omap-timer.c
diff --git a/drivers/sound/Kconfig b/drivers/sound/Kconfig
index be9f18b6c7..3f124cfb2b 100644
--- a/drivers/sound/Kconfig
+++ b/drivers/sound/Kconfig
@@ -158,4 +158,12 @@ config I2S_TI
           option provides an implementation for sound_init() and
           sound_play().
 
+config SOUND_TLV320AIC3106
+        bool "Support TLV320AIC3106 audio codec"
+        depends on I2S_TI
+        help
+          Enable the TLV320AIC3106 audio codec. This is connected via I2S for
+          audio data and I2C for codec control. At present it only works
+          with the MCASP driver.
+
 endmenu
diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile
index 95aaa8521c..1e9fbd1000 100644
--- a/drivers/sound/Makefile
+++ b/drivers/sound/Makefile
@@ -24,3 +24,4 @@ obj-$(CONFIG_SOUND_RT5677)	+= rt5677.o
 obj-$(CONFIG_INTEL_BROADWELL)	+= broadwell_i2s.o broadwell_sound.o
 obj-$(CONFIG_SOUND_IVYBRIDGE)	+= ivybridge_sound.o
 obj-$(CONFIG_I2S_TI)    += ti_sound.o
+obj-$(CONFIG_SOUND_TLV320AIC3106)       += tlv320aic3106.o
diff --git a/drivers/sound/tlv320aic3106.c b/drivers/sound/tlv320aic3106.c
new file mode 100644
index 0000000000..b80bdddd70
--- /dev/null
+++ b/drivers/sound/tlv320aic3106.c
@@ -0,0 +1,758 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2024 Texas Instruments Incorporated - https://www.ti.com/
+ * Scaria M Kochidanadu, s-kochidanadu@ti.com
+ *
+ * based on the linux tlv320aic3x codec driver, which is
+ *
+ * Author:      Vladimir Barinov, <vbarinov@embeddedalley.com>
+ * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.com>
+ */
+
+#include <asm/u-boot.h> /* boot information for Linux kernel */
+/* Pull in stuff for the build system */
+#ifdef DO_DEPS_ONLY
+#include <env_internal.h>
+#endif
+#include <audio_codec.h>
+#include <dm.h>
+#include <div64.h>
+#include <fdtdec.h>
+#include <i2c.h>
+#include <i2s.h>
+#include <log.h>
+#include <sound.h>
+#include <linux/delay.h>
+#include "tlv320aic3106.h"
+
+enum aic3x_micbias_voltage {
+	AIC3X_MICBIAS_OFF = 0,
+	AIC3X_MICBIAS_2_0V = 1,
+	AIC3X_MICBIAS_2_5V = 2,
+	AIC3X_MICBIAS_AVDDV = 3,
+};
+
+enum snd_soc_bias_level {
+	SND_SOC_BIAS_OFF = 0,
+	SND_SOC_BIAS_STANDBY = 1,
+	SND_SOC_BIAS_PREPARE = 2,
+	SND_SOC_BIAS_ON = 3,
+};
+
+struct reg_values {
+	unsigned int reg;
+	unsigned int def;
+};
+
+static const struct reg_values aic3x_default[] = {
+	{   0, 0x00 }, {   1, 0x00 }, {   2, 0x00 }, {   3, 0x10 },
+	{   4, 0x04 }, {   5, 0x00 }, {   6, 0x00 }, {   7, 0x00 },
+	{   8, 0x00 }, {   9, 0x00 }, {  10, 0x00 }, {  11, 0x01 },
+	{  12, 0x00 }, {  13, 0x00 }, {  14, 0x00 }, {  15, 0x80 },
+	{  16, 0x80 }, {  17, 0xff }, {  18, 0xff }, {  19, 0x78 },
+	{  20, 0x78 }, {  21, 0x78 }, {  22, 0x78 }, {  23, 0x78 },
+	{  24, 0x78 }, {  25, 0x00 }, {  26, 0x00 }, {  27, 0xfe },
+	{  28, 0x00 }, {  29, 0x00 }, {  30, 0xfe }, {  31, 0x00 },
+	{  32, 0x18 }, {  33, 0x18 }, {  34, 0x00 }, {  35, 0x00 },
+	{  36, 0x00 }, {  37, 0x00 }, {  38, 0x00 }, {  39, 0x00 },
+	{  40, 0x00 }, {  41, 0x00 }, {  42, 0x00 }, {  43, 0x80 },
+	{  44, 0x80 }, {  45, 0x00 }, {  46, 0x00 }, {  47, 0x00 },
+	{  48, 0x00 }, {  49, 0x00 }, {  50, 0x00 }, {  51, 0x04 },
+	{  52, 0x00 }, {  53, 0x00 }, {  54, 0x00 }, {  55, 0x00 },
+	{  56, 0x00 }, {  57, 0x00 }, {  58, 0x04 }, {  59, 0x00 },
+	{  60, 0x00 }, {  61, 0x00 }, {  62, 0x00 }, {  63, 0x00 },
+	{  64, 0x00 }, {  65, 0x04 }, {  66, 0x00 }, {  67, 0x00 },
+	{  68, 0x00 }, {  69, 0x00 }, {  70, 0x00 }, {  71, 0x00 },
+	{  72, 0x04 }, {  73, 0x00 }, {  74, 0x00 }, {  75, 0x00 },
+	{  76, 0x00 }, {  77, 0x00 }, {  78, 0x00 }, {  79, 0x00 },
+	{  80, 0x00 }, {  81, 0x00 }, {  82, 0x00 }, {  83, 0x00 },
+	{  84, 0x00 }, {  85, 0x00 }, {  86, 0x00 }, {  87, 0x00 },
+	{  88, 0x00 }, {  89, 0x00 }, {  90, 0x00 }, {  91, 0x00 },
+	{  92, 0x00 }, {  93, 0x00 }, {  94, 0x00 }, {  95, 0x00 },
+	{  96, 0x00 }, {  97, 0x00 }, {  98, 0x00 }, {  99, 0x00 },
+	{ 100, 0x00 }, { 101, 0x00 }, { 102, 0x02 }, { 103, 0x00 },
+	{ 104, 0x00 }, { 105, 0x00 }, { 106, 0x00 }, { 107, 0x00 },
+	{ 108, 0x00 }, { 109, 0x00 },
+};
+
+/* codec private data */
+struct tlv320aic3106_priv {
+	struct udevice *dev;
+	enum aic3x_micbias_voltage micbias_vg;
+	unsigned int sysclk;
+	unsigned int dai_fmt;
+	unsigned int tdm_delay;
+	unsigned int slot_width;
+	int master;
+	int power;
+	bool bias_level;
+	/*output common voltage*/
+	u8 ocmv;
+};
+
+static int aic3106_i2c_write(struct tlv320aic3106_priv *priv, unsigned int reg, unsigned char data)
+{
+	unsigned char val[1];
+
+	val[0] = data;
+	debug("Write Addr : 0x%04X, Data :  0x%04X\n", reg, data);
+
+	return dm_i2c_write(priv->dev, reg, val, 1);
+}
+
+static unsigned int aic3106_i2c_read(struct tlv320aic3106_priv *priv, unsigned int reg,
+				     unsigned char *data)
+{
+	unsigned char val[1];
+	int ret;
+
+	ret = dm_i2c_read(priv->dev, reg, val, 1);
+	if (ret != 0) {
+		debug("%s: Error while reading register %#04x\n",
+		      __func__, reg);
+		return -1;
+	}
+	*data = val[0];
+
+	return 0;
+}
+
+static int aic3106_i2c_update_bits(struct tlv320aic3106_priv *priv, unsigned int reg,
+				   unsigned char mask, unsigned char data)
+{
+	unsigned char reg_data;
+
+	int ret = aic3106_i2c_read(priv, reg - 1, &reg_data);
+
+	if (ret < 0) {
+		debug("failed to read ID register\n");
+		return ret;
+	}
+
+	unsigned char final_data = (reg_data & (~mask)) | data;
+
+	ret = aic3106_i2c_write(priv, reg, final_data);
+	if (ret < 0) {
+		debug("failed to write ID register\n");
+		return ret;
+	}
+	return 0;
+}
+
+static int aic3106_set_dai_sysclk(struct tlv320aic3106_priv *priv, int clk_id, unsigned int freq)
+{
+	int ret;
+
+	ret = aic3106_i2c_update_bits(priv, AIC3X_CLKGEN_CTRL_REG, PLLCLK_IN_MASK,
+				      clk_id << PLLCLK_IN_SHIFT);
+	ret |= aic3106_i2c_update_bits(priv, AIC3X_CLKGEN_CTRL_REG, CLKDIV_IN_MASK,
+				       clk_id << CLKDIV_IN_SHIFT);
+
+	priv->sysclk = freq;
+
+	if (ret < 0) {
+		debug("%s sysclk failed", __func__);
+		return ret;
+	}
+	return 0;
+}
+
+static int aic3106_set_dai_fmt(struct tlv320aic3106_priv *priv, unsigned int fmt)
+{
+	int ret;
+	u8 iface_areg, iface_breg;
+
+	ret = aic3106_i2c_read(priv, AIC3X_ASD_INTF_CTRLA - 1, &iface_areg);
+	if (ret < 0)
+		debug("%s error reading reg\n", __func__);
+	iface_areg &= 0x3f;
+
+	ret = aic3106_i2c_read(priv, AIC3X_ASD_INTF_CTRLB - 1, &iface_breg);
+	if (ret < 0)
+		debug("%s error reading reg\n", __func__);
+	iface_breg &= 0x3f;
+
+	switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+	case SND_SOC_DAIFMT_CBP_CFP:
+		priv->master = 1;
+		iface_areg |= BIT_CLK_MASTER | WORD_CLK_MASTER;
+		break;
+	case SND_SOC_DAIFMT_CBC_CFC:
+		priv->master = 0;
+		iface_areg &= ~(BIT_CLK_MASTER | WORD_CLK_MASTER);
+		break;
+	case SND_SOC_DAIFMT_CBP_CFC:
+		priv->master = 1;
+		iface_areg |= BIT_CLK_MASTER;
+		iface_areg &= ~WORD_CLK_MASTER;
+		break;
+	case SND_SOC_DAIFMT_CBC_CFP:
+		priv->master = 1;
+		iface_areg |= WORD_CLK_MASTER;
+		iface_areg &= ~BIT_CLK_MASTER;
+		break;
+	default:
+		return -EINVAL;
+	}
+	/*
+	 * match both interface format and signal polarities since they
+	 * are fixed
+	 */
+	switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK |
+		       SND_SOC_DAIFMT_INV_MASK)) {
+	case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF_UBT):
+		break;
+	case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF):
+	case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF):
+		iface_breg |= (0x01 << 6);
+		break;
+	case (SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_NB_NF_UBT):
+		iface_breg |= (0x02 << 6);
+		break;
+	case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF_UBT):
+		iface_breg |= (0x03 << 6);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	priv->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+	ret = aic3106_i2c_write(priv, AIC3X_ASD_INTF_CTRLA, iface_areg);
+	ret |= aic3106_i2c_write(priv, AIC3X_ASD_INTF_CTRLB, iface_breg);
+
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+static int aic3106_hw_params(struct tlv320aic3106_priv *priv, int rate)
+{
+	int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0;
+	u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
+
+	u16 d, pll_d = 1;
+	int clk;
+	int width = priv->slot_width;
+
+	if (!width)
+		width = 16;
+
+	int ret = aic3106_i2c_read(priv, AIC3X_ASD_INTF_CTRLB - 1, &data);
+
+	if (ret < 0)
+		return ret;
+
+	data &= (~(0x3 << 4));
+
+	switch (width) {
+	case 16:
+		break;
+	case 20:
+		data |= (0x01 << 4);
+		break;
+	case 24:
+		data |= (0x02 << 4);
+		break;
+	case 32:
+		data |= (0x03 << 4);
+		break;
+	}
+
+	aic3106_i2c_write(priv, AIC3X_ASD_INTF_CTRLB, data);
+
+	fsref = (rate % 11025 == 0) ? 44100 : 48000;
+
+	/* Try to find a value for Q which allows us to bypass the PLL and
+	 * generate CODEC_CLK directly.
+	 */
+	for (pll_q = 2; pll_q < 18; pll_q++)
+		if (priv->sysclk / (128 * pll_q) == fsref) {
+			bypass_pll = 1;
+			break;
+		}
+
+	if (bypass_pll) {
+		pll_q &= 0xf;
+		aic3106_i2c_write(priv, AIC3X_PLL_PROGA_REG, pll_q << PLLQ_SHIFT);
+		aic3106_i2c_write(priv, AIC3X_GPIOB_REG, CODEC_CLKIN_CLKDIV);
+		/* disable PLL if it is bypassed */
+		aic3106_i2c_update_bits(priv, AIC3X_PLL_PROGA_REG, PLL_ENABLE, 0);
+
+	} else {
+		aic3106_i2c_write(priv, AIC3X_GPIOB_REG, CODEC_CLKIN_PLLDIV);
+		/* enable PLL when it is used */
+		aic3106_i2c_update_bits(priv, AIC3X_PLL_PROGA_REG,
+					PLL_ENABLE, PLL_ENABLE);
+	}
+
+	/* Route Left DAC to left channel input and
+	 * right DAC to right channel input
+	 */
+	data = (LDAC2LCH | RDAC2RCH);
+	data |= (fsref == 44100) ? FSREF_44100 : FSREF_48000;
+
+	if (rate >= 64000)
+		data |= DUAL_RATE_MODE;
+	aic3106_i2c_write(priv, AIC3X_CODEC_DATAPATH_REG, data);
+
+	/* codec sample rate select */
+	data = (fsref * 20) / rate;
+	if (rate < 64000)
+		data /= 2;
+	data /= 5;
+	data -= 2;
+	data |= (data << 4);
+	aic3106_i2c_write(priv, AIC3X_SAMPLE_RATE_SEL_REG, data);
+
+	if (bypass_pll)
+		return 0;
+
+	/* Use PLL, compute appropriate setup for j, d, r and p, the closest
+	 * one wins the game. Try with d==0 first, next with d!=0.
+	 * Constraints for j are according to the datasheet.
+	 * The sysclk is divided by 1000 to prevent integer overflows.
+	 */
+
+	codec_clk = (2048 * fsref) / (priv->sysclk / 1000);
+
+	for (r = 1; r <= 16; r++)
+		for (p = 1; p <= 8; p++) {
+			for (j = 4; j <= 55; j++) {
+				/* This is actually 1000*((j+(d/10000))*r)/p
+				 * The term had to be converted to get
+				 * rid of the division by 10000; d = 0 here
+				 */
+				int tmp_clk = (1000 * j * r) / p;
+
+				/* Check whether this values get closer than
+				 * the best ones we had before
+				 */
+				if (abs(codec_clk - tmp_clk) <
+					abs(codec_clk - last_clk)) {
+					pll_j = j; pll_d = 0;
+					pll_r = r; pll_p = p;
+					last_clk = tmp_clk;
+				}
+
+				/* Early exit for exact matches */
+				if (tmp_clk == codec_clk)
+					goto found;
+			}
+		}
+
+	/* try with d != 0 */
+	for (p = 1; p <= 8; p++) {
+		j = codec_clk * p / 1000;
+
+		if (j < 4 || j > 11)
+			continue;
+
+		/* do not use codec_clk here since we'd loose precision */
+		d = ((2048 * p * fsref) - j * priv->sysclk)
+			* 100 / (priv->sysclk / 100);
+
+		clk = (10000 * j + d) / (10 * p);
+
+		/* check whether this values get closer than the best
+		 * ones we had before
+		 */
+		if (abs(codec_clk - clk) < abs(codec_clk - last_clk)) {
+			pll_j = j; pll_d = d; pll_r = 1; pll_p = p;
+			last_clk = clk;
+		}
+
+		/* Early exit for exact matches */
+		if (clk == codec_clk)
+			goto found;
+	}
+
+	if (last_clk == 0) {
+		printf("%s(): unable to setup PLL\n", __func__);
+		return -EINVAL;
+	}
+
+found:
+	aic3106_i2c_update_bits(priv, AIC3X_PLL_PROGA_REG, PLLP_MASK, pll_p);
+	aic3106_i2c_write(priv, AIC3X_OVRF_STATUS_AND_PLLR_REG,
+			  pll_r << PLLR_SHIFT);
+	aic3106_i2c_write(priv, AIC3X_PLL_PROGB_REG, pll_j << PLLJ_SHIFT);
+	aic3106_i2c_write(priv, AIC3X_PLL_PROGC_REG,
+			  (pll_d >> 6) << PLLD_MSB_SHIFT);
+	aic3106_i2c_write(priv, AIC3X_PLL_PROGD_REG,
+			  (pll_d & 0x3F) << PLLD_LSB_SHIFT);
+
+	return 0;
+}
+
+static int aic3106_prepare(struct tlv320aic3106_priv *priv)
+{
+	int delay = 0;
+	int width = priv->slot_width;
+
+	if (!width)
+		width = 16;
+
+	/* TDM slot selection only valid in DSP_A/_B mode */
+	if (priv->dai_fmt == SND_SOC_DAIFMT_DSP_A)
+		delay += (priv->tdm_delay * width + 1);
+	else if (priv->dai_fmt == SND_SOC_DAIFMT_DSP_B)
+		delay += priv->tdm_delay * width;
+
+	/* Configure data delay */
+	int ret = aic3106_i2c_write(priv, AIC3X_ASD_INTF_CTRLC, delay);
+
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int aic3106_mute(struct tlv320aic3106_priv *priv, int mute)
+{
+	u8 ldac_reg, rdac_reg;
+	int ret;
+
+	ret = aic3106_i2c_read(priv, LDAC_VOL - 1, &ldac_reg);
+	if (ret < 0)
+		debug("%s error in reading reg\n", __func__);
+	ret = aic3106_i2c_read(priv, RDAC_VOL - 1, &rdac_reg);
+	if (ret < 0)
+		debug("%s error in reading reg\n", __func__);
+
+	ldac_reg &= ~MUTE_ON;
+	rdac_reg &= ~MUTE_ON;
+
+	if (mute) {
+		aic3106_i2c_write(priv, LDAC_VOL, ldac_reg | MUTE_ON);
+		aic3106_i2c_write(priv, RDAC_VOL, rdac_reg | MUTE_ON);
+	} else {
+		aic3106_i2c_write(priv, LDAC_VOL, ldac_reg);
+		aic3106_i2c_write(priv, RDAC_VOL, rdac_reg);
+	}
+
+	return 0;
+}
+
+static int aic3106_set_power(struct tlv320aic3106_priv *priv, bool power)
+{
+	u8 pll_c, pll_d;
+	int ret;
+
+	if (power) {
+		priv->power = 1;
+
+		/* Rewrite paired PLL D registers in case cached sync skipped
+		 * writing one of them and thus caused other one also not
+		 * being written
+		 */
+		ret = aic3106_i2c_read(priv, AIC3X_PLL_PROGC_REG - 1, &pll_c);
+		ret |= aic3106_i2c_read(priv, AIC3X_PLL_PROGD_REG - 1, &pll_d);
+		if (pll_c == aic3x_default[AIC3X_PLL_PROGC_REG].def ||
+		    pll_d == aic3x_default[AIC3X_PLL_PROGD_REG].def) {
+			ret |= aic3106_i2c_write(priv, AIC3X_PLL_PROGC_REG, pll_c);
+			ret |= aic3106_i2c_write(priv, AIC3X_PLL_PROGD_REG, pll_d);
+		}
+
+		if (ret < 0)
+			return ret;
+
+		/*
+		 * Delay is needed to reduce pop-noise after syncing back the
+		 * registers
+		 */
+		udelay(50 * 1000);
+
+	} else {
+		/*
+		 * Do soft reset to this codec instance in order to clear
+		 * possible VDD leakage currents in case the supply regulators
+		 * remain on
+		 */
+		ret = aic3106_i2c_write(priv, AIC3X_RESET, SOFT_RESET);
+		if (ret < 0)
+			return ret;
+
+		priv->power = 0;
+	}
+	return 0;
+}
+
+static int aic3106_set_bias_level(struct tlv320aic3106_priv *priv, bool level)
+{
+	int ret;
+
+	if (level && !priv->bias_level && priv->master) {
+		ret = aic3106_set_power(priv, 1);
+		/*enable PLL */
+		ret |= aic3106_i2c_update_bits(priv, AIC3X_PLL_PROGA_REG, PLL_ENABLE, PLL_ENABLE);
+		if (ret < 0)
+			return ret;
+
+		priv->bias_level = true;
+	}
+
+	if (!level && priv->bias_level && priv->master) {
+		/*disable PLL */
+		ret = aic3106_i2c_update_bits(priv, AIC3X_PLL_PROGA_REG, PLL_ENABLE, 0);
+		priv->bias_level = false;
+		ret |= aic3106_set_power(priv, 0);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static void aic3106_mono_init(struct tlv320aic3106_priv *priv)
+{
+	aic3106_i2c_write(priv, DACL1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
+
+	aic3106_i2c_write(priv, DACR1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
+
+	/* unmute all outputs */
+	aic3106_i2c_update_bits(priv, MONOLOPM_CTRL, UNMUTE, UNMUTE);
+
+	/* PGA to Mono Line Out default volume, disconnect from Output Mixer */
+	aic3106_i2c_write(priv, PGAL_2_MONOLOPM_VOL, DEFAULT_VOL);
+
+	aic3106_i2c_write(priv, PGAR_2_MONOLOPM_VOL, DEFAULT_VOL);
+
+	/* Line2 to Mono Out default volume, disconnect from Output Mixer */
+	aic3106_i2c_write(priv, LINE2L_2_MONOLOPM_VOL, DEFAULT_VOL);
+
+	aic3106_i2c_write(priv, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL);
+}
+
+static int tlv320aic3106_init(struct tlv320aic3106_priv *priv)
+{
+	int ret = aic3106_i2c_write(priv, AIC3X_PAGE_SELECT, PAGE0_SELECT);
+
+	ret |= aic3106_i2c_write(priv, AIC3X_RESET, SOFT_RESET);
+
+	/*Powering up LDAC and RDAC*/
+	ret |= aic3106_i2c_write(priv, 37, 0xc0);
+
+	/* DAC default volume and mute */
+	/* Increasing the Volume since default volume is low */
+	ret |= aic3106_i2c_write(priv, LDAC_VOL, 0x0d | MUTE_ON);
+
+	ret |= aic3106_i2c_write(priv, RDAC_VOL, 0x0d | MUTE_ON);
+
+	/* DAC to HP default volume and route to Output mixer */
+	ret |= aic3106_i2c_write(priv, DACL1_2_HPLOUT_VOL, DEFAULT_VOL | ROUTE_ON);
+
+	ret |= aic3106_i2c_write(priv, DACR1_2_HPROUT_VOL, DEFAULT_VOL | ROUTE_ON);
+
+	ret |= aic3106_i2c_write(priv, DACL1_2_HPLCOM_VOL, DEFAULT_VOL | ROUTE_ON);
+
+	ret |= aic3106_i2c_write(priv, DACR1_2_HPRCOM_VOL, DEFAULT_VOL | ROUTE_ON);
+
+	/* DAC to Line Out default volume and route to Output mixer */
+	ret |= aic3106_i2c_write(priv, DACL1_2_LLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
+
+	ret |= aic3106_i2c_write(priv, DACR1_2_RLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
+
+	/* Powering on the outputs*/
+	ret |= aic3106_i2c_update_bits(priv, HPLOUT_CTRL, HPLOUT_PWR_ON, HPLOUT_PWR_ON);
+
+	ret |= aic3106_i2c_update_bits(priv, HPLCOM_CTRL, HPLCOM_PWR_ON, HPLCOM_PWR_ON);
+
+	ret |= aic3106_i2c_update_bits(priv, HPROUT_CTRL, HPROUT_PWR_ON, HPROUT_PWR_ON);
+
+	ret |= aic3106_i2c_update_bits(priv, HPRCOM_CTRL, HPRCOM_PWR_ON, HPRCOM_PWR_ON);
+
+	/* unmute all outputs */
+	ret |= aic3106_i2c_update_bits(priv, LLOPM_CTRL, UNMUTE, UNMUTE);
+
+	ret |= aic3106_i2c_update_bits(priv, RLOPM_CTRL, UNMUTE, UNMUTE);
+
+	ret |= aic3106_i2c_update_bits(priv, HPLOUT_CTRL, UNMUTE, UNMUTE);
+
+	ret |= aic3106_i2c_update_bits(priv, HPROUT_CTRL, UNMUTE, UNMUTE);
+
+	ret |= aic3106_i2c_update_bits(priv, HPLCOM_CTRL, UNMUTE, UNMUTE);
+
+	ret |= aic3106_i2c_update_bits(priv, HPRCOM_CTRL, UNMUTE, UNMUTE);
+
+	/* ADC default volume and unmute */
+	ret |= aic3106_i2c_write(priv, LADC_VOL, 0x6b);
+
+	ret |= aic3106_i2c_write(priv, RADC_VOL, 0x6b);
+
+	/* Some changes*/
+
+	ret |= aic3106_i2c_write(priv, MIC3LR_2_LADC_CTRL, 0xf0);
+
+	ret |= aic3106_i2c_write(priv, MIC3LR_2_RADC_CTRL, 0xf0);
+
+	/* By default route Line1 to ADC PGA mixer */
+	ret |= aic3106_i2c_write(priv, LINE1L_2_LADC_CTRL, 0x0);
+
+	ret |= aic3106_i2c_write(priv, LINE1R_2_RADC_CTRL, 0x0);
+
+	/* PGA to HP Bypass default volume, disconnect from Output Mixer */
+	ret |= aic3106_i2c_write(priv, PGAL_2_HPLOUT_VOL, DEFAULT_VOL);
+
+	ret |= aic3106_i2c_write(priv, PGAR_2_HPROUT_VOL, DEFAULT_VOL);
+
+	ret |= aic3106_i2c_write(priv, PGAL_2_HPLCOM_VOL, DEFAULT_VOL);
+
+	ret |= aic3106_i2c_write(priv, PGAR_2_HPRCOM_VOL, DEFAULT_VOL);
+	/* PGA to Line Out default volume, disconnect from Output Mixer */
+
+	ret |= aic3106_i2c_write(priv, PGAL_2_LLOPM_VOL, DEFAULT_VOL);
+
+	ret |= aic3106_i2c_write(priv, PGAR_2_RLOPM_VOL, DEFAULT_VOL);
+
+	/* Line2 to HP Bypass default volume, disconnect from Output Mixer */
+	ret |= aic3106_i2c_write(priv, LINE2L_2_HPLOUT_VOL, DEFAULT_VOL);
+
+	ret |= aic3106_i2c_write(priv, LINE2R_2_HPROUT_VOL, DEFAULT_VOL);
+
+	ret |= aic3106_i2c_write(priv, LINE2L_2_HPLCOM_VOL, DEFAULT_VOL);
+
+	ret |= aic3106_i2c_write(priv, LINE2R_2_HPRCOM_VOL, DEFAULT_VOL);
+
+	/* Line2 Line Out default volume, disconnect from Output Mixer */
+	ret |= aic3106_i2c_write(priv, LINE2L_2_LLOPM_VOL, DEFAULT_VOL);
+
+	ret |= aic3106_i2c_write(priv, LINE2R_2_RLOPM_VOL, DEFAULT_VOL);
+
+	aic3106_mono_init(priv);
+
+	ret |= aic3106_i2c_update_bits(priv, HPOUT_SC, HPOUT_SC_OCMV_MASK,
+			    priv->ocmv << HPOUT_SC_OCMV_SHIFT);
+
+	if (ret) {
+		debug("error in init()\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int tlv320aic3106_set_params(struct udevice *dev, int interface, int rate,
+				    int mclk_freq, int bits_per_sample, uint channels)
+{
+	struct tlv320aic3106_priv *priv = dev_get_priv(dev);
+
+	int ret;
+
+	ret = aic3106_set_dai_sysclk(priv, interface, mclk_freq);
+	if (ret) {
+		debug(" %s failure in setting sysclk\n", __func__);
+		return ret;
+	}
+
+	int fmt = SND_SOC_DAIFMT_CBP_CFP | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_DSP_B;
+
+	ret = aic3106_set_dai_fmt(priv, fmt);
+	if (ret) {
+		debug(" %s failure in setting dai format\n", __func__);
+		return ret;
+	}
+
+	ret = aic3106_hw_params(priv, rate);
+	if (ret) {
+		debug(" %s failure in setting hw params\n", __func__);
+		return ret;
+	}
+
+	ret = aic3106_prepare(priv);
+	if (ret) {
+		debug(" %s failure\n", __func__);
+		return ret;
+	}
+
+	ret = aic3106_set_bias_level(priv, 1);
+	if (ret) {
+		debug(" %s failure\n", __func__);
+		return ret;
+	}
+
+	ret = aic3106_mute(priv, 0);
+	if (ret) {
+		debug(" %s failure\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int tlv320aic3106_probe(struct udevice *dev)
+{
+	struct tlv320aic3106_priv *priv = dev_get_priv(dev);
+	int ret, value;
+
+	priv->dev = dev;
+
+	/* Getting Micbias volage and OCMV value from DT */
+	if (!dev_read_u32u(dev, "ai3x-micbias-vg", &value)) {
+		switch (value) {
+		case 1:
+			priv->micbias_vg = AIC3X_MICBIAS_2_0V;
+			break;
+		case 2:
+			priv->micbias_vg = AIC3X_MICBIAS_2_5V;
+			break;
+		case 3:
+			priv->micbias_vg = AIC3X_MICBIAS_AVDDV;
+			break;
+		default:
+			priv->micbias_vg = AIC3X_MICBIAS_OFF;
+			debug("Unsuitable MicBias voltage found in DT\n");
+		}
+	} else {
+		priv->micbias_vg = AIC3X_MICBIAS_OFF;
+	}
+
+	if (!dev_read_u32u(dev, "ai3x-ocmv", &value)) {
+		/* OCMV setting is forced by DT */
+		if (value <= 3)
+			priv->ocmv = value;
+	}
+
+	ret = tlv320aic3106_init(priv);
+	if (ret < 0) {
+		printf("error in init()\n");
+		return ret;
+	}
+
+	switch (priv->micbias_vg) {
+	case AIC3X_MICBIAS_2_0V:
+	case AIC3X_MICBIAS_2_5V:
+	case AIC3X_MICBIAS_AVDDV:
+		aic3106_i2c_update_bits(priv, MICBIAS_CTRL,
+					MICBIAS_LEVEL_MASK,
+					(priv->micbias_vg) << MICBIAS_LEVEL_SHIFT);
+		break;
+	case AIC3X_MICBIAS_OFF:
+		/*
+		 * noting to do. target won't enter here. This is just to avoid
+		 * compile time warning "warning: enumeration value
+		 * 'AIC3X_MICBIAS_OFF' not handled in switch"
+		 */
+		break;
+	}
+
+	return 0;
+}
+
+static const struct audio_codec_ops tlv320aic3106_ops = {
+	.set_params	= tlv320aic3106_set_params,
+};
+
+static const struct udevice_id tlv320aic3106_ids[] = {
+	{ .compatible = "ti,tlv320aic3106" },
+	{ }
+};
+
+U_BOOT_DRIVER(tlv320aic3106) = {
+	.name		= "tlv320aic3106",
+	.id		= UCLASS_AUDIO_CODEC,
+	.of_match	= tlv320aic3106_ids,
+	.probe		= tlv320aic3106_probe,
+	.ops		= &tlv320aic3106_ops,
+	.priv_auto	= sizeof(struct tlv320aic3106_priv),
+};
diff --git a/drivers/sound/tlv320aic3106.h b/drivers/sound/tlv320aic3106.h
new file mode 100644
index 0000000000..55dc497a7e
--- /dev/null
+++ b/drivers/sound/tlv320aic3106.h
@@ -0,0 +1,336 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ALSA SoC TLV320AIC3X codec driver
+ *
+ * Author:      Vladimir Barinov, <vbarinov@embeddedalley.com>
+ * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.com>
+ */
+
+#ifndef _TLV320AIC3106_H
+#define _TLV320AIC3106_H
+
+#define AIC3X_MODEL_3X 0
+#define AIC3X_MODEL_33 1
+#define AIC3X_MODEL_3007 2
+#define AIC3X_MODEL_3104 3
+#define AIC3X_MODEL_3106 4
+
+/* AIC3X register space */
+#define AIC3X_CACHEREGNUM		110
+
+/* Page select register */
+#define AIC3X_PAGE_SELECT		0
+/* Software reset register */
+#define AIC3X_RESET			1
+/* Codec Sample rate select register */
+#define AIC3X_SAMPLE_RATE_SEL_REG	2
+/* PLL progrramming register A */
+#define AIC3X_PLL_PROGA_REG		3
+/* PLL progrramming register B */
+#define AIC3X_PLL_PROGB_REG		4
+/* PLL progrramming register C */
+#define AIC3X_PLL_PROGC_REG		5
+/* PLL progrramming register D */
+#define AIC3X_PLL_PROGD_REG		6
+/* Codec datapath setup register */
+#define AIC3X_CODEC_DATAPATH_REG	7
+/* Audio serial data interface control register A */
+#define AIC3X_ASD_INTF_CTRLA		8
+/* Audio serial data interface control register B */
+#define AIC3X_ASD_INTF_CTRLB		9
+/* Audio serial data interface control register C */
+#define AIC3X_ASD_INTF_CTRLC		10
+/* Audio overflow status and PLL R value programming register */
+#define AIC3X_OVRF_STATUS_AND_PLLR_REG	11
+/* Audio codec digital filter control register */
+#define AIC3X_CODEC_DFILT_CTRL		12
+/* Headset/button press detection register */
+#define AIC3X_HEADSET_DETECT_CTRL_A	13
+#define AIC3X_HEADSET_DETECT_CTRL_B	14
+/* ADC PGA Gain control registers */
+#define LADC_VOL			15
+#define RADC_VOL			16
+/* MIC3 control registers */
+#define MIC3LR_2_LADC_CTRL		17
+#define MIC3LR_2_RADC_CTRL		18
+/* Line1 Input control registers */
+#define LINE1L_2_LADC_CTRL		19
+#define LINE1R_2_LADC_CTRL		21
+#define LINE1R_2_RADC_CTRL		22
+#define LINE1L_2_RADC_CTRL		24
+/* Line2 Input control registers */
+#define LINE2L_2_LADC_CTRL		20
+#define LINE2R_2_RADC_CTRL		23
+/* MICBIAS Control Register */
+#define MICBIAS_CTRL			25
+
+/* AGC Control Registers A, B, C */
+#define LAGC_CTRL_A			26
+#define LAGC_CTRL_B			27
+#define LAGC_CTRL_C			28
+#define RAGC_CTRL_A			29
+#define RAGC_CTRL_B			30
+#define RAGC_CTRL_C			31
+
+/* DAC Power and Left High Power Output control registers */
+#define DAC_PWR				37
+#define HPLCOM_CFG			37
+/* Right High Power Output control registers */
+#define HPRCOM_CFG			38
+/* High Power Output Stage Control Register */
+#define HPOUT_SC			40
+/* DAC Output Switching control registers */
+#define DAC_LINE_MUX			41
+/* High Power Output Driver Pop Reduction registers */
+#define HPOUT_POP_REDUCTION		42
+/* DAC Digital control registers */
+#define LDAC_VOL			43
+#define RDAC_VOL			44
+/* Left High Power Output control registers */
+#define LINE2L_2_HPLOUT_VOL		45
+#define PGAL_2_HPLOUT_VOL		46
+#define DACL1_2_HPLOUT_VOL		47
+#define LINE2R_2_HPLOUT_VOL		48
+#define PGAR_2_HPLOUT_VOL		49
+#define DACR1_2_HPLOUT_VOL		50
+#define HPLOUT_CTRL			51
+/* Left High Power COM control registers */
+#define LINE2L_2_HPLCOM_VOL		52
+#define PGAL_2_HPLCOM_VOL		53
+#define DACL1_2_HPLCOM_VOL		54
+#define LINE2R_2_HPLCOM_VOL		55
+#define PGAR_2_HPLCOM_VOL		56
+#define DACR1_2_HPLCOM_VOL		57
+#define HPLCOM_CTRL			58
+/* Right High Power Output control registers */
+#define LINE2L_2_HPROUT_VOL		59
+#define PGAL_2_HPROUT_VOL		60
+#define DACL1_2_HPROUT_VOL		61
+#define LINE2R_2_HPROUT_VOL		62
+#define PGAR_2_HPROUT_VOL		63
+#define DACR1_2_HPROUT_VOL		64
+#define HPROUT_CTRL			65
+/* Right High Power COM control registers */
+#define LINE2L_2_HPRCOM_VOL		66
+#define PGAL_2_HPRCOM_VOL		67
+#define DACL1_2_HPRCOM_VOL		68
+#define LINE2R_2_HPRCOM_VOL		69
+#define PGAR_2_HPRCOM_VOL		70
+#define DACR1_2_HPRCOM_VOL		71
+#define HPRCOM_CTRL			72
+/* Mono Line Output Plus/Minus control registers */
+#define LINE2L_2_MONOLOPM_VOL		73
+#define PGAL_2_MONOLOPM_VOL		74
+#define DACL1_2_MONOLOPM_VOL		75
+#define LINE2R_2_MONOLOPM_VOL		76
+#define PGAR_2_MONOLOPM_VOL		77
+#define DACR1_2_MONOLOPM_VOL		78
+#define MONOLOPM_CTRL			79
+/* Class-D speaker driver on tlv320aic3007 */
+#define CLASSD_CTRL			73
+/* Left Line Output Plus/Minus control registers */
+#define LINE2L_2_LLOPM_VOL		80
+#define PGAL_2_LLOPM_VOL		81
+#define DACL1_2_LLOPM_VOL		82
+#define LINE2R_2_LLOPM_VOL		83
+#define PGAR_2_LLOPM_VOL		84
+#define DACR1_2_LLOPM_VOL		85
+#define LLOPM_CTRL			86
+/* Right Line Output Plus/Minus control registers */
+#define LINE2L_2_RLOPM_VOL		87
+#define PGAL_2_RLOPM_VOL		88
+#define DACL1_2_RLOPM_VOL		89
+#define LINE2R_2_RLOPM_VOL		90
+#define PGAR_2_RLOPM_VOL		91
+#define DACR1_2_RLOPM_VOL		92
+#define RLOPM_CTRL			93
+/* GPIO/IRQ registers */
+#define AIC3X_STICKY_IRQ_FLAGS_REG	96
+#define AIC3X_RT_IRQ_FLAGS_REG		97
+#define AIC3X_GPIO1_REG			98
+#define AIC3X_GPIO2_REG			99
+#define AIC3X_GPIOA_REG			100
+#define AIC3X_GPIOB_REG			101
+/* Clock generation control register */
+#define AIC3X_CLKGEN_CTRL_REG		102
+/* New AGC registers */
+#define LAGCN_ATTACK			103
+#define LAGCN_DECAY			104
+#define RAGCN_ATTACK			105
+#define RAGCN_DECAY			106
+/* New Programmable ADC Digital Path and I2C Bus Condition Register */
+#define NEW_ADC_DIGITALPATH		107
+/* Passive Analog Signal Bypass Selection During Powerdown Register */
+#define PASSIVE_BYPASS			108
+/* DAC Quiescent Current Adjustment Register */
+#define DAC_ICC_ADJ			109
+
+/* Page select register bits */
+#define PAGE0_SELECT		0
+#define PAGE1_SELECT		1
+
+/* Audio serial data interface control register A bits */
+#define BIT_CLK_MASTER          0x80
+#define WORD_CLK_MASTER         0x40
+#define DOUT_TRISTATE		0x20
+
+/* Codec Datapath setup register 7 */
+#define FSREF_44100		BIT(7)
+#define FSREF_48000		(0 << 7)
+#define DUAL_RATE_MODE		((1 << 5) | (1 << 6))
+#define LDAC2LCH		(0x1 << 3)
+#define RDAC2RCH		(0x1 << 1)
+#define LDAC2RCH		(0x2 << 3)
+#define RDAC2LCH		(0x2 << 1)
+#define LDAC2MONOMIX		(0x3 << 3)
+#define RDAC2MONOMIX		(0x3 << 1)
+
+/* PLL registers bitfields */
+#define PLLP_SHIFT		0
+#define PLLP_MASK		7
+#define PLLQ_SHIFT		3
+#define PLLR_SHIFT		0
+#define PLLJ_SHIFT		2
+#define PLLD_MSB_SHIFT		0
+#define PLLD_LSB_SHIFT		2
+
+/* Clock generation register bits */
+#define CODEC_CLKIN_PLLDIV	0
+#define CODEC_CLKIN_CLKDIV	1
+#define PLL_CLKIN_SHIFT		4
+#define MCLK_SOURCE		0x0
+#define PLL_CLKDIV_SHIFT	0
+#define PLLCLK_IN_MASK		0x30
+#define PLLCLK_IN_SHIFT		4
+#define CLKDIV_IN_MASK		0xc0
+#define CLKDIV_IN_SHIFT		6
+/* clock in source */
+#define CLKIN_MCLK		0
+#define CLKIN_GPIO2		1
+#define CLKIN_BCLK		2
+
+/* Software reset register bits */
+#define SOFT_RESET		0x80
+
+/* PLL progrramming register A bits */
+#define PLL_ENABLE		0x80
+
+/* Route bits */
+#define ROUTE_ON		0x80
+
+/* Mute bits */
+#define UNMUTE			0x08
+#define MUTE_ON			0x80
+
+/* Power bits */
+#define LADC_PWR_ON		0x04
+#define RADC_PWR_ON		0x04
+#define LDAC_PWR_ON		0x80
+#define RDAC_PWR_ON		0x40
+#define HPLOUT_PWR_ON		0x01
+#define HPROUT_PWR_ON		0x01
+#define HPLCOM_PWR_ON		0x01
+#define HPRCOM_PWR_ON		0x01
+#define MONOLOPM_PWR_ON		0x01
+#define LLOPM_PWR_ON		0x01
+#define RLOPM_PWR_ON	0x01
+
+#define INVERT_VOL(val)   (0x7f - (val))
+
+/* Default output volume (inverted) */
+#define DEFAULT_VOL     INVERT_VOL(0x50)
+/* Default input volume */
+#define DEFAULT_GAIN    0x20
+
+/* MICBIAS Control Register */
+#define MICBIAS_LEVEL_SHIFT	(6)
+#define MICBIAS_LEVEL_MASK	(3 << 6)
+
+/* HPOUT_SC */
+#define HPOUT_SC_OCMV_MASK	(3 << 6)
+#define HPOUT_SC_OCMV_SHIFT	(6)
+#define HPOUT_SC_OCMV_1_35V	0
+#define HPOUT_SC_OCMV_1_5V	1
+#define HPOUT_SC_OCMV_1_65V	2
+#define HPOUT_SC_OCMV_1_8V	3
+
+/* headset detection / button API */
+
+/* The AIC3x supports detection of stereo headsets (GND + left + right signal)
+ * and cellular headsets (GND + speaker output + microphone input).
+ * It is recommended to enable MIC bias for this function to work properly.
+ * For more information, please refer to the datasheet.
+ */
+enum {
+	AIC3X_HEADSET_DETECT_OFF	= 0,
+	AIC3X_HEADSET_DETECT_STEREO	= 1,
+	AIC3X_HEADSET_DETECT_CELLULAR   = 2,
+	AIC3X_HEADSET_DETECT_BOTH	= 3
+};
+
+enum {
+	AIC3X_HEADSET_DEBOUNCE_16MS	= 0,
+	AIC3X_HEADSET_DEBOUNCE_32MS	= 1,
+	AIC3X_HEADSET_DEBOUNCE_64MS	= 2,
+	AIC3X_HEADSET_DEBOUNCE_128MS	= 3,
+	AIC3X_HEADSET_DEBOUNCE_256MS	= 4,
+	AIC3X_HEADSET_DEBOUNCE_512MS	= 5
+};
+
+enum {
+	AIC3X_BUTTON_DEBOUNCE_0MS	= 0,
+	AIC3X_BUTTON_DEBOUNCE_8MS	= 1,
+	AIC3X_BUTTON_DEBOUNCE_16MS	= 2,
+	AIC3X_BUTTON_DEBOUNCE_32MS	= 3
+};
+
+#define AIC3X_HEADSET_DETECT_ENABLED	0x80
+#define AIC3X_HEADSET_DETECT_SHIFT	5
+#define AIC3X_HEADSET_DETECT_MASK	3
+#define AIC3X_HEADSET_DEBOUNCE_SHIFT	2
+#define AIC3X_HEADSET_DEBOUNCE_MASK	7
+#define AIC3X_BUTTON_DEBOUNCE_SHIFT	0
+#define AIC3X_BUTTON_DEBOUNCE_MASK	3
+
+/* from soc-dai.h*/
+
+#define SND_SOC_DAIFMT_FORMAT_MASK		0x000f
+#define SND_SOC_DAIFMT_CLOCK_MASK		0x00f0
+#define SND_SOC_DAIFMT_INV_MASK			0x0f00
+#define SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK	0xf000
+
+#define SND_SOC_DAIFMT_CBP_CFP		BIT(12) /* codec clk provider & frame provider */
+#define SND_SOC_DAIFMT_CBC_CFP		(2 << 12) /* codec clk consumer & frame provider */
+#define SND_SOC_DAIFMT_CBP_CFC		(3 << 12) /* codec clk provider & frame consumer */
+#define SND_SOC_DAIFMT_CBC_CFC		(4 << 12) /* codec clk consumer & frame consumer */
+
+/*
+ * DAI hardware signal polarity.
+ *
+ * Specifies whether the DAI can also support inverted clocks for the specified
+ * format.
+ *
+ * BCLK:
+ * - "normal" polarity means signal is available at rising edge of BCLK
+ * - "inverted" polarity means signal is available at falling edge of BCLK
+ *
+ * FSYNC "normal" polarity depends on the frame format:
+ * - I2S: frame consists of left then right channel data. Left channel starts
+ *      with falling FSYNC edge, right channel starts with rising FSYNC edge.
+ * - Left/Right Justified: frame consists of left then right channel data.
+ *      Left channel starts with rising FSYNC edge, right channel starts with
+ *      falling FSYNC edge.
+ * - DSP A/B: Frame starts with rising FSYNC edge.
+ * - AC97: Frame starts with rising FSYNC edge.
+ *
+ * "Negative" FSYNC polarity is the one opposite of "normal" polarity.
+ */
+
+#define SND_SOC_DAIFMT_NB_NF_UBT		(0 << 8) /* normal bit clock + frame */
+#define SND_SOC_DAIFMT_NB_IF		(2 << 8) /* normal BCLK + inv FRM */
+#define SND_SOC_DAIFMT_IB_NF		(3 << 8) /* invert BCLK + nor FRM */
+#define SND_SOC_DAIFMT_IB_IF		(4 << 8) /* invert BCLK + FRM */
+
+#define	EINVAL		22	/* Invalid argument */
+
+#endif /* _AIC3X_H */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 3/6] sound: ti: Add MCASP driver for transfer of Audio data to sound codec
  2024-07-08  7:52 [PATCH 0/6] Enable audio playback in AM62x SoC Scaria Kochidanadu
  2024-07-08  7:52 ` [PATCH 1/6] sound: ti: Add sound support for am625 board in Uboot Scaria Kochidanadu
  2024-07-08  7:52 ` [PATCH 2/6] sound: ti: Add TLV320AIC3106 Codec Scaria Kochidanadu
@ 2024-07-08  7:52 ` Scaria Kochidanadu
  2024-07-08  7:52 ` [PATCH 4/6] arm: dts: k3-am625-sk-u-boot.dtsi: Add sound driver nodes Scaria Kochidanadu
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 13+ messages in thread
From: Scaria Kochidanadu @ 2024-07-08  7:52 UTC (permalink / raw)
  To: trini, u-boot
  Cc: sjg, mkorpershoek, abdellatif.elkhlifi, sumit.garg,
	neil.armstrong, caleb.connolly, semen.protsenko,
	marek.vasut+renesas, nathan.morrison, s-kochidanadu, devarsht,
	j-luthra, vigneshr, praneeth, nm, khasim, rogerq,
	francesco.dolcini, afd, jan.kiszka

This patch adds driver for I2S interface for TI AM62x Soc.
Configure the MCASP [1] component with appropriate settings and
hardware parameters according to the audio data and devicetree.
Set up the MCASP pins and fill the transmit buffers. The driver
uses polling method to transfer data to the codec, since
u-boot does not have DMA driver or interrupt functionality.

The TRM for the AM62X SoC contain the information for the MCASP
component-
Link: https://www.ti.com/lit/ug/spruiv7b/spruiv7b.pdf [1] (Section
12.1.1 : Audio Peripherals-MCASP)

The driver is based on the kernel mcasp driver[2].
Link:
https://gitlab.com/linux-kernel/linux-next/-/blob/master/sound/soc/ti/davinci-mcasp.c
[2]

Signed-off-by: Scaria Kochidanadu <s-kochidanadu@ti.com>
---
 MAINTAINERS                   |    2 +
 drivers/sound/Makefile        |    1 +
 drivers/sound/davinci-mcasp.h |  413 ++++++++++++
 drivers/sound/mcasp_i2s.c     | 1123 +++++++++++++++++++++++++++++++++
 4 files changed, 1539 insertions(+)
 create mode 100644 drivers/sound/davinci-mcasp.h
 create mode 100644 drivers/sound/mcasp_i2s.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 5d7c260bfc..6097b3ba62 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -738,6 +738,8 @@ F:	drivers/reset/reset-ti-sci.c
 F:	drivers/rtc/davinci.c
 F:	drivers/serial/serial_omap.c
 F:	drivers/soc/ti/
+F:	drivers/sound/davinci-mcasp.h
+F:	drivers/sound/mcasp_i2s.c
 F:	drivers/sound/ti_sound.c
 F:	drivers/sound/tlv320aic3106.c
 F:	drivers/sound/tlv320aic3106.h
diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile
index 1e9fbd1000..2068649172 100644
--- a/drivers/sound/Makefile
+++ b/drivers/sound/Makefile
@@ -24,4 +24,5 @@ obj-$(CONFIG_SOUND_RT5677)	+= rt5677.o
 obj-$(CONFIG_INTEL_BROADWELL)	+= broadwell_i2s.o broadwell_sound.o
 obj-$(CONFIG_SOUND_IVYBRIDGE)	+= ivybridge_sound.o
 obj-$(CONFIG_I2S_TI)    += ti_sound.o
+obj-$(CONFIG_I2S_TI)    += mcasp_i2s.o
 obj-$(CONFIG_SOUND_TLV320AIC3106)       += tlv320aic3106.o
diff --git a/drivers/sound/davinci-mcasp.h b/drivers/sound/davinci-mcasp.h
new file mode 100644
index 0000000000..83c77ba26c
--- /dev/null
+++ b/drivers/sound/davinci-mcasp.h
@@ -0,0 +1,413 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ALSA SoC McASP Audio Layer for TI DAVINCI processor
+ *
+ * MCASP related definitions
+ *
+ * Author: Nirmal Pandey <n-pandey@ti.com>,
+ *         Suresh Rajashekara <suresh.r@ti.com>
+ *         Steve Chen <schen@.mvista.com>
+ *
+ * Copyright:   (C) 2009 MontaVista Software, Inc., <source@mvista.com>
+ * Copyright:   (C) 2009  Texas Instruments, India
+ */
+
+#ifndef DAVINCI_MCASP_H
+#define DAVINCI_MCASP_H
+
+/*
+ * McASP register definitions
+ */
+#define DAVINCI_MCASP_PID_REG		0x00
+#define DAVINCI_MCASP_PWREMUMGT_REG	0x04
+
+#define DAVINCI_MCASP_PFUNC_REG		0x10
+#define DAVINCI_MCASP_PDIR_REG		0x14
+#define DAVINCI_MCASP_PDOUT_REG		0x18
+#define DAVINCI_MCASP_PDSET_REG		0x1c
+
+#define DAVINCI_MCASP_PDCLR_REG		0x20
+
+#define DAVINCI_MCASP_TLGC_REG		0x30
+#define DAVINCI_MCASP_TLMR_REG		0x34
+
+#define DAVINCI_MCASP_GBLCTL_REG	0x44
+#define DAVINCI_MCASP_AMUTE_REG		0x48
+#define DAVINCI_MCASP_LBCTL_REG		0x4c
+
+#define DAVINCI_MCASP_TXDITCTL_REG	0x50
+
+#define DAVINCI_MCASP_GBLCTLR_REG	0x60
+#define DAVINCI_MCASP_RXMASK_REG	0x64
+#define DAVINCI_MCASP_RXFMT_REG		0x68
+#define DAVINCI_MCASP_RXFMCTL_REG	0x6c
+
+#define DAVINCI_MCASP_ACLKRCTL_REG	0x70
+#define DAVINCI_MCASP_AHCLKRCTL_REG	0x74
+#define DAVINCI_MCASP_RXTDM_REG		0x78
+#define DAVINCI_MCASP_EVTCTLR_REG	0x7c
+
+#define DAVINCI_MCASP_RXSTAT_REG	0x80
+#define DAVINCI_MCASP_RXTDMSLOT_REG	0x84
+#define DAVINCI_MCASP_RXCLKCHK_REG	0x88
+#define DAVINCI_MCASP_REVTCTL_REG	0x8c
+
+#define DAVINCI_MCASP_GBLCTLX_REG	0xa0
+#define DAVINCI_MCASP_TXMASK_REG	0xa4
+#define DAVINCI_MCASP_TXFMT_REG		0xa8
+#define DAVINCI_MCASP_TXFMCTL_REG	0xac
+
+#define DAVINCI_MCASP_ACLKXCTL_REG	0xb0
+#define DAVINCI_MCASP_AHCLKXCTL_REG	0xb4
+#define DAVINCI_MCASP_TXTDM_REG		0xb8
+#define DAVINCI_MCASP_EVTCTLX_REG	0xbc
+
+#define DAVINCI_MCASP_TXSTAT_REG	0xc0
+#define DAVINCI_MCASP_TXTDMSLOT_REG	0xc4
+#define DAVINCI_MCASP_TXCLKCHK_REG	0xc8
+#define DAVINCI_MCASP_XEVTCTL_REG	0xcc
+
+/* Left(even TDM Slot) Channel Status Register File */
+#define DAVINCI_MCASP_DITCSRA_REG	0x100
+/* Right(odd TDM slot) Channel Status Register File */
+#define DAVINCI_MCASP_DITCSRB_REG	0x118
+/* Left(even TDM slot) User Data Register File */
+#define DAVINCI_MCASP_DITUDRA_REG	0x130
+/* Right(odd TDM Slot) User Data Register File */
+#define DAVINCI_MCASP_DITUDRB_REG	0x148
+
+/* Serializer n Control Register */
+#define DAVINCI_MCASP_XRSRCTL_BASE_REG	0x180
+#define DAVINCI_MCASP_XRSRCTL_REG(n)	(DAVINCI_MCASP_XRSRCTL_BASE_REG + \
+						((n) << 2))
+
+/* Transmit Buffer for Serializer n */
+#define DAVINCI_MCASP_TXBUF_REG(n)	(0x200 + ((n) << 2))
+/* Receive Buffer for Serializer n */
+#define DAVINCI_MCASP_RXBUF_REG(n)	(0x280 + ((n) << 2))
+
+/* McASP FIFO Registers */
+#define DAVINCI_MCASP_V2_AFIFO_BASE	(0x1010)
+#define DAVINCI_MCASP_V3_AFIFO_BASE	(0x1000)
+
+/* FIFO register offsets from AFIFO base */
+#define MCASP_WFIFOCTL_OFFSET		(0x0)
+#define MCASP_WFIFOSTS_OFFSET		(0x4)
+#define MCASP_RFIFOCTL_OFFSET		(0x8)
+#define MCASP_RFIFOSTS_OFFSET		(0xc)
+
+/*
+ * DAVINCI_MCASP_PWREMUMGT_REG - Power Down and Emulation Management
+ *     Register Bits
+ */
+#define MCASP_FREE	BIT(0)
+#define MCASP_SOFT	BIT(1)
+
+/*
+ * DAVINCI_MCASP_PFUNC_REG - Pin Function / GPIO Enable Register Bits
+ * DAVINCI_MCASP_PDIR_REG - Pin Direction Register Bits
+ * DAVINCI_MCASP_PDOUT_REG - Pin output in GPIO mode
+ * DAVINCI_MCASP_PDSET_REG - Pin input in GPIO mode
+ */
+#define PIN_BIT_AXR(n)	(n)
+#define PIN_BIT_AMUTE	25
+#define PIN_BIT_ACLKX	26
+#define PIN_BIT_AHCLKX	27
+#define PIN_BIT_AFSX	28
+#define PIN_BIT_ACLKR	29
+#define PIN_BIT_AHCLKR	30
+#define PIN_BIT_AFSR	31
+
+/*
+ * DAVINCI_MCASP_TXDITCTL_REG - Transmit DIT Control Register Bits
+ */
+#define DITEN	BIT(0)	/* Transmit DIT mode enable/disable */
+#define VA	BIT(2)
+#define VB	BIT(3)
+
+/*
+ * DAVINCI_MCASP_TXFMT_REG - Transmit Bitstream Format Register Bits
+ */
+#define TXROT(val)	(val)
+#define TXSEL		BIT(3)
+#define TXSSZ(val)	((val) << 4)
+#define TXPBIT(val)	((val) << 8)
+#define TXPAD(val)	((val) << 13)
+#define TXORD		BIT(15)
+#define FSXDLY(val)	((val) << 16)
+
+/*
+ * DAVINCI_MCASP_RXFMT_REG - Receive Bitstream Format Register Bits
+ */
+#define RXROT(val)	(val)
+#define RXSEL		BIT(3)
+#define RXSSZ(val)	((val) << 4)
+#define RXPBIT(val)	((val) << 8)
+#define RXPAD(val)	((val) << 13)
+#define RXORD		BIT(15)
+#define FSRDLY(val)	((val) << 16)
+
+/*
+ * DAVINCI_MCASP_TXFMCTL_REG -  Transmit Frame Control Register Bits
+ */
+#define FSXPOL		BIT(0)
+#define AFSXE		BIT(1)
+#define FSXDUR		BIT(4)
+#define FSXMOD(val)	((val) << 7)
+
+/*
+ * DAVINCI_MCASP_RXFMCTL_REG - Receive Frame Control Register Bits
+ */
+#define FSRPOL		BIT(0)
+#define AFSRE		BIT(1)
+#define FSRDUR		BIT(4)
+#define FSRMOD(val)	((val) << 7)
+
+/*
+ * DAVINCI_MCASP_ACLKXCTL_REG - Transmit Clock Control Register Bits
+ */
+#define ACLKXDIV(val)	(val)
+#define ACLKXE		BIT(5)
+#define TX_ASYNC	BIT(6)
+#define ACLKXPOL	BIT(7)
+#define ACLKXDIV_MASK	0x1f
+
+/*
+ * DAVINCI_MCASP_ACLKRCTL_REG Receive Clock Control Register Bits
+ */
+#define ACLKRDIV(val)	(val)
+#define ACLKRE		BIT(5)
+#define RX_ASYNC	BIT(6)
+#define ACLKRPOL	BIT(7)
+#define ACLKRDIV_MASK	0x1f
+
+/*
+ * DAVINCI_MCASP_AHCLKXCTL_REG - High Frequency Transmit Clock Control
+ *     Register Bits
+ */
+#define AHCLKXDIV(val)	(val)
+#define AHCLKXPOL	BIT(14)
+#define AHCLKXE		BIT(15)
+#define AHCLKXDIV_MASK	0xfff
+
+/*
+ * DAVINCI_MCASP_AHCLKRCTL_REG - High Frequency Receive Clock Control
+ *     Register Bits
+ */
+#define AHCLKRDIV(val)	(val)
+#define AHCLKRPOL	BIT(14)
+#define AHCLKRE		BIT(15)
+#define AHCLKRDIV_MASK	0xfff
+
+/*
+ * DAVINCI_MCASP_XRSRCTL_BASE_REG -  Serializer Control Register Bits
+ */
+#define MODE(val)	(val)
+#define DISMOD_3STATE	(0x0)
+#define DISMOD_LOW	(0x2 << 2)
+#define DISMOD_HIGH	(0x3 << 2)
+#define DISMOD_VAL(x)	((x) << 2)
+#define DISMOD_MASK	DISMOD_HIGH
+#define TXSTATE		BIT(4)
+#define RXSTATE		BIT(5)
+#define SRMOD_MASK	3
+#define SRMOD_INACTIVE	0
+
+/*
+ * DAVINCI_MCASP_LBCTL_REG - Loop Back Control Register Bits
+ */
+#define LBEN		BIT(0)
+#define LBORD		BIT(1)
+#define LBGENMODE(val)	((val) << 2)
+
+/*
+ * DAVINCI_MCASP_TXTDMSLOT_REG - Transmit TDM Slot Register configuration
+ */
+#define TXTDMS(n)	(1 << (n))
+
+/*
+ * DAVINCI_MCASP_RXTDMSLOT_REG - Receive TDM Slot Register configuration
+ */
+#define RXTDMS(n)	(1 << (n))
+
+/*
+ * DAVINCI_MCASP_GBLCTL_REG -  Global Control Register Bits
+ */
+#define RXCLKRST	BIT(0)	/* Receiver Clock Divider Reset */
+#define RXHCLKRST	BIT(1)	/* Receiver High Frequency Clock Divider */
+#define RXSERCLR	BIT(2)	/* Receiver Serializer Clear */
+#define RXSMRST		BIT(3)	/* Receiver State Machine Reset */
+#define RXFSRST		BIT(4)	/* Frame Sync Generator Reset */
+#define TXCLKRST	BIT(8)	/* Transmitter Clock Divider Reset */
+#define TXHCLKRST	BIT(9)	/* Transmitter High Frequency Clock Divider*/
+#define TXSERCLR	BIT(10)	/* Transmit Serializer Clear */
+#define TXSMRST		BIT(11)	/* Transmitter State Machine Reset */
+#define TXFSRST		BIT(12)	/* Frame Sync Generator Reset */
+
+/*
+ * DAVINCI_MCASP_TXSTAT_REG - Transmitter Status Register Bits
+ * DAVINCI_MCASP_RXSTAT_REG - Receiver Status Register Bits
+ */
+#define XRERR		BIT(8) /* Transmit/Receive error */
+#define XRDATA		BIT(5) /* Transmit/Receive data ready */
+
+/*
+ * DAVINCI_MCASP_AMUTE_REG -  Mute Control Register Bits
+ */
+#define MUTENA(val)	(val)
+#define MUTEINPOL	BIT(2)
+#define MUTEINENA	BIT(3)
+#define MUTEIN		BIT(4)
+#define MUTER		BIT(5)
+#define MUTEX		BIT(6)
+#define MUTEFSR		BIT(7)
+#define MUTEFSX		BIT(8)
+#define MUTEBADCLKR	BIT(9)
+#define MUTEBADCLKX	BIT(10)
+#define MUTERXDMAERR	BIT(11)
+#define MUTETXDMAERR	BIT(12)
+
+/*
+ * DAVINCI_MCASP_REVTCTL_REG - Receiver DMA Event Control Register bits
+ */
+#define RXDATADMADIS	BIT(0)
+
+/*
+ * DAVINCI_MCASP_XEVTCTL_REG - Transmitter DMA Event Control Register bits
+ */
+#define TXDATADMADIS	BIT(0)
+
+/*
+ * DAVINCI_MCASP_EVTCTLR_REG - Receiver Interrupt Control Register Bits
+ */
+#define ROVRN		BIT(0)
+
+/*
+ * DAVINCI_MCASP_EVTCTLX_REG - Transmitter Interrupt Control Register Bits
+ */
+#define XUNDRN		BIT(0)
+
+/*
+ * DAVINCI_MCASP_W[R]FIFOCTL - Write/Read FIFO Control Register bits
+ */
+#define FIFO_ENABLE	BIT(16)
+#define NUMEVT_MASK	(0xFF << 8)
+#define NUMEVT(x)	(((x) & 0xFF) << 8)
+#define NUMDMA_MASK	(0xFF)
+
+/* Source of High-frequency transmit/receive clock */
+#define MCASP_CLK_HCLK_AHCLK		0 /* AHCLKX/R */
+#define MCASP_CLK_HCLK_AUXCLK		1 /* Internal functional clock */
+
+/* clock divider IDs */
+#define MCASP_CLKDIV_AUXCLK		0 /* HCLK divider from AUXCLK */
+#define MCASP_CLKDIV_BCLK		1 /* BCLK divider from HCLK */
+#define MCASP_CLKDIV_BCLK_FS_RATIO	2 /* to set BCLK FS ration */
+
+#define INACTIVE_MODE	0
+#define TX_MODE		1
+#define RX_MODE		2
+
+#define DAVINCI_MCASP_IIS_MODE	0
+#define DAVINCI_MCASP_DIT_MODE	1
+
+enum {
+	SNDRV_PCM_STREAM_PLAYBACK = 0,
+	SNDRV_PCM_STREAM_CAPTURE,
+	SNDRV_PCM_STREAM_LAST = SNDRV_PCM_STREAM_CAPTURE,
+};
+
+enum {
+	MCASP_VERSION_1 = 0,	/* DM646x */
+	MCASP_VERSION_2,	/* DA8xx/OMAPL1x */
+	MCASP_VERSION_3,        /* TI81xx/AM33xx */
+	MCASP_VERSION_4,	/* DRA7xxx */
+	MCASP_VERSION_OMAP,	/* OMAP4/5 */
+};
+
+/* Some SND defaults */
+
+#define SND_SOC_DAIFMT_FORMAT_MASK		0x000f
+#define SND_SOC_DAIFMT_CLOCK_MASK		0x00f0
+#define SND_SOC_DAIFMT_INV_MASK			0x0f00
+#define SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK	0xf000
+
+#define SND_SOC_DAIFMT_CBP_CFP		BIT(12) /* codec clk provider & frame provider */
+#define SND_SOC_DAIFMT_CBC_CFP		(2 << 12) /* codec clk consumer & frame provider */
+#define SND_SOC_DAIFMT_CBP_CFC		(3 << 12) /* codec clk provider & frame consumer */
+#define SND_SOC_DAIFMT_CBC_CFC		(4 << 12) /* codec clk consumer & frame consumer */
+
+/* when passed to set_fmt directly indicate if the device is provider or consumer */
+#define SND_SOC_DAIFMT_BP_FP		SND_SOC_DAIFMT_CBP_CFP
+#define SND_SOC_DAIFMT_BC_FP		SND_SOC_DAIFMT_CBC_CFP
+#define SND_SOC_DAIFMT_BP_FC		SND_SOC_DAIFMT_CBP_CFC
+#define SND_SOC_DAIFMT_BC_FC		SND_SOC_DAIFMT_CBC_CFC
+
+#define SND_SOC_DAIFMT_NB_NF_UBT		(0 << 8) /* normal bit clock + frame */
+#define SND_SOC_DAIFMT_NB_IF		(2 << 8) /* normal BCLK + inv FRM */
+#define SND_SOC_DAIFMT_IB_NF		(3 << 8) /* invert BCLK + nor FRM */
+#define SND_SOC_DAIFMT_IB_IF		(4 << 8) /* invert BCLK + FRM */
+
+/*
+ * Master Clock Directions
+ */
+#define SND_SOC_CLOCK_IN		0
+#define SND_SOC_CLOCK_OUT		1
+
+typedef int __bitwise snd_pcm_format_t;
+#define	SNDRV_PCM_FORMAT_S8	((snd_pcm_format_t)0)
+#define	SNDRV_PCM_FORMAT_U8	((snd_pcm_format_t)1)
+#define	SNDRV_PCM_FORMAT_S16_LE	((snd_pcm_format_t)2)
+#define	SNDRV_PCM_FORMAT_S16_BE	((snd_pcm_format_t)3)
+#define	SNDRV_PCM_FORMAT_U16_LE	((snd_pcm_format_t)4)
+#define	SNDRV_PCM_FORMAT_U16_BE	((snd_pcm_format_t)5)
+#define	SNDRV_PCM_FORMAT_S24_LE	((snd_pcm_format_t)6) /* low three bytes */
+#define	SNDRV_PCM_FORMAT_S24_BE	((snd_pcm_format_t)7) /* low three bytes */
+#define	SNDRV_PCM_FORMAT_U24_LE	((snd_pcm_format_t)8) /* low three bytes */
+#define	SNDRV_PCM_FORMAT_U24_BE	((snd_pcm_format_t)9) /* low three bytes */
+#define	SNDRV_PCM_FORMAT_S32_LE	((snd_pcm_format_t)10)
+#define	SNDRV_PCM_FORMAT_S32_BE	((snd_pcm_format_t)11)
+#define	SNDRV_PCM_FORMAT_U32_LE	((snd_pcm_format_t)12)
+#define	SNDRV_PCM_FORMAT_U32_BE	((snd_pcm_format_t)13)
+#define	SNDRV_PCM_FORMAT_FLOAT_LE	((snd_pcm_format_t)14)
+#define	SNDRV_PCM_FORMAT_FLOAT_BE	((snd_pcm_format_t)15)
+#define	SNDRV_PCM_FORMAT_FLOAT64_LE	((snd_pcm_format_t)16)
+#define	SNDRV_PCM_FORMAT_FLOAT64_BE	((snd_pcm_format_t)17)
+#define	SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE ((snd_pcm_format_t)18)
+#define	SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE ((snd_pcm_format_t)19)
+#define	SNDRV_PCM_FORMAT_MU_LAW		((snd_pcm_format_t)20)
+#define	SNDRV_PCM_FORMAT_A_LAW		((snd_pcm_format_t)21)
+#define	SNDRV_PCM_FORMAT_IMA_ADPCM	((snd_pcm_format_t)22)
+#define	SNDRV_PCM_FORMAT_MPEG		((snd_pcm_format_t)23)
+#define	SNDRV_PCM_FORMAT_GSM		((snd_pcm_format_t)24)
+#define	SNDRV_PCM_FORMAT_S20_LE	((snd_pcm_format_t)25) /* in four bytes, LSB justified */
+#define	SNDRV_PCM_FORMAT_S20_BE	((snd_pcm_format_t)26) /* in four bytes, LSB justified */
+#define	SNDRV_PCM_FORMAT_U20_LE	((snd_pcm_format_t)27) /* in four bytes, LSB justified */
+#define	SNDRV_PCM_FORMAT_U20_BE	((snd_pcm_format_t)28) /* in four bytes, LSB justified */
+/* gap in the numbering for a future standard linear format */
+#define	SNDRV_PCM_FORMAT_SPECIAL	((snd_pcm_format_t)31)
+#define	SNDRV_PCM_FORMAT_S24_3LE	((snd_pcm_format_t)32)	/* in three bytes */
+#define	SNDRV_PCM_FORMAT_S24_3BE	((snd_pcm_format_t)33)	/* in three bytes */
+#define	SNDRV_PCM_FORMAT_U24_3LE	((snd_pcm_format_t)34)	/* in three bytes */
+#define	SNDRV_PCM_FORMAT_U24_3BE	((snd_pcm_format_t)35)	/* in three bytes */
+#define	SNDRV_PCM_FORMAT_S20_3LE	((snd_pcm_format_t)36)	/* in three bytes */
+#define	SNDRV_PCM_FORMAT_S20_3BE	((snd_pcm_format_t)37)	/* in three bytes */
+#define	SNDRV_PCM_FORMAT_U20_3LE	((snd_pcm_format_t)38)	/* in three bytes */
+#define	SNDRV_PCM_FORMAT_U20_3BE	((snd_pcm_format_t)39)	/* in three bytes */
+#define	SNDRV_PCM_FORMAT_S18_3LE	((snd_pcm_format_t)40)	/* in three bytes */
+#define	SNDRV_PCM_FORMAT_S18_3BE	((snd_pcm_format_t)41)	/* in three bytes */
+#define	SNDRV_PCM_FORMAT_U18_3LE	((snd_pcm_format_t)42)	/* in three bytes */
+#define	SNDRV_PCM_FORMAT_U18_3BE	((snd_pcm_format_t)43)	/* in three bytes */
+#define	SNDRV_PCM_FORMAT_G723_24	((snd_pcm_format_t)44) /* 8 samples in 3 bytes */
+#define	SNDRV_PCM_FORMAT_G723_24_1B	((snd_pcm_format_t)45) /* 1 sample in 1 byte */
+#define	SNDRV_PCM_FORMAT_G723_40	((snd_pcm_format_t)46) /* 8 Samples in 5 bytes */
+#define	SNDRV_PCM_FORMAT_G723_40_1B	((snd_pcm_format_t)47) /* 1 sample in 1 byte */
+#define	SNDRV_PCM_FORMAT_DSD_U8		((snd_pcm_format_t)48) /* DSD, 1-byte samples DSD (x8) */
+#define	SNDRV_PCM_FORMAT_DSD_U16_LE	((snd_pcm_format_t)49)
+#define	SNDRV_PCM_FORMAT_DSD_U32_LE	((snd_pcm_format_t)50)
+#define	SNDRV_PCM_FORMAT_DSD_U16_BE	((snd_pcm_format_t)51)
+#define	SNDRV_PCM_FORMAT_DSD_U32_BE	((snd_pcm_format_t)52)
+#define	SNDRV_PCM_FORMAT_LAST		SNDRV_PCM_FORMAT_DSD_U32_BE
+#define	SNDRV_PCM_FORMAT_FIRST		SNDRV_PCM_FORMAT_S8
+
+#endif	/* DAVINCI_MCASP_H */
diff --git a/drivers/sound/mcasp_i2s.c b/drivers/sound/mcasp_i2s.c
new file mode 100644
index 0000000000..6da4badd9c
--- /dev/null
+++ b/drivers/sound/mcasp_i2s.c
@@ -0,0 +1,1123 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2024 Texas Instruments Incorporated - https://www.ti.com/
+ * Scaria M Kochidanadu, s-kochidanadu@ti.com
+ *
+ * based on the linux MCASP driver, which is
+ *
+ * Author: Nirmal Pandey <n-pandey@ti.com>,
+ *         Suresh Rajashekara <suresh.r@ti.com>
+ *         Steve Chen <schen@.mvista.com>
+ *
+ * Copyright:   (C) 2009 MontaVista Software, Inc., <source@mvista.com>
+ * Copyright:   (C) 2009  Texas Instruments, India
+ */
+
+#include <asm/u-boot.h> /* boot information for Linux kernel */
+/* Pull in stuff for the build system */
+#ifdef DO_DEPS_ONLY
+#include <env_internal.h>
+#endif
+#include <dm.h>
+#include <i2s.h>
+#include <log.h>
+#include <sound.h>
+#include <asm/io.h>
+#include <linux/bitmap.h>
+#include <linux/math64.h>
+#include <linux/ioport.h>
+#include "davinci-mcasp.h"
+
+#define MCASP_MAX_AFIFO_DEPTH  64
+
+struct mcasp_priv {
+	void __iomem *base;
+	u32 fifo_base;
+	struct i2s_uc_priv *pdata;
+	struct udevice *dev;
+	unsigned int dai_fmt;
+	unsigned long dat_port_addr;
+
+	/* McASP specific data */
+	int	tdm_slots;
+	u32	tdm_mask[2];
+	int	slot_width;
+	u8	op_mode;
+	u8	dismod;
+	u8	num_serializer;
+	u32	*serial_dir;
+	u8	version;
+	u8	bclk_div;
+	int	streams;
+
+	int	sysclk_freq;
+	bool	bclk_master;
+
+	unsigned long pdir; /* Pin direction bitfield */
+
+	/* McASP FIFO related */
+	u8	txnumevt;
+	u8	rxnumevt;
+
+	bool	dat_port;
+
+	/* Used for comstraint setting on the second stream */
+	u32	channels;
+	int	max_format_width;
+	u8	active_serializers[2];
+
+	bool missing_audio_param;
+
+};
+
+/* Register Data manipulation Functions */
+
+static inline void set_bit_pdir(int nbit, unsigned long *pdir)
+{
+	*pdir = *pdir | BIT(nbit);
+}
+
+static inline void clear_bit_pdir(int nbit, unsigned long *pdir)
+{
+	*pdir = *pdir & (~BIT(nbit));
+}
+
+static inline void mcasp_set_bits(struct mcasp_priv *mcasp, u32 offset, u32 val)
+{
+	void __iomem *reg = mcasp->base + offset;
+
+	__raw_writel(__raw_readl(reg) | val, reg);
+}
+
+static inline void mcasp_clr_bits(struct mcasp_priv *mcasp, u32 offset,
+				  u32 val)
+{
+	void __iomem *reg = mcasp->base + offset;
+
+	__raw_writel((__raw_readl(reg) & ~(val)), reg);
+}
+
+static inline void mcasp_mod_bits(struct mcasp_priv *mcasp, u32 offset,
+				  u32 val, u32 mask)
+{
+	void __iomem *reg = mcasp->base + offset;
+
+	__raw_writel((__raw_readl(reg) & ~mask) | val, reg);
+}
+
+static inline void mcasp_set_reg(struct mcasp_priv *mcasp, u32 offset,
+				 u32 val)
+{
+	__raw_writel(val, mcasp->base + offset);
+}
+
+static inline u32 mcasp_get_reg(struct mcasp_priv *mcasp, u32 offset)
+{
+	return (u32)__raw_readl(mcasp->base + offset);
+}
+
+static void mcasp_set_ctl_reg(struct mcasp_priv *mcasp, u32 ctl_reg, u32 val)
+{
+	int i = 0;
+
+	mcasp_set_bits(mcasp, ctl_reg, val);
+
+	/* programming GBLCTL needs to read back from GBLCTL and verfiy */
+	/* loop count is to avoid the lock-up */
+	for (i = 0; i < 1000; i++) {
+		if ((mcasp_get_reg(mcasp, ctl_reg) & val) == val)
+			break;
+	}
+
+	if (i == 1000 && ((mcasp_get_reg(mcasp, ctl_reg) & val) != val))
+		printf("GBLCTL write error\n");
+}
+
+/********************************************************************/
+static bool mcasp_is_synchronous(struct mcasp_priv *mcasp)
+{
+	u32 rxfmctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG);
+	u32 aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG);
+
+	return !(aclkxctl & TX_ASYNC) && rxfmctl & AFSRE;
+}
+
+static inline void mcasp_set_clk_pdir(struct mcasp_priv *mcasp, bool enable)
+{
+	u32 bit = PIN_BIT_AMUTE;
+
+	for_each_set_bit_from(bit, &mcasp->pdir, PIN_BIT_AFSR + 1) {
+		if (enable)
+			mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit));
+		else
+			mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit));
+	}
+}
+
+static inline void mcasp_set_axr_pdir(struct mcasp_priv *mcasp, bool enable)
+{
+	u32 bit;
+
+	for_each_set_bit(bit, &mcasp->pdir, PIN_BIT_AMUTE) {
+		if (enable)
+			mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit));
+		else
+			mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit));
+	}
+}
+
+static int davinci_mcasp_get_config(struct mcasp_priv *mcasp)
+{
+	u32 val;
+
+	if (!dev_read_u32u(mcasp->dev, "op-mode", &val)) {
+		mcasp->op_mode = val;
+	} else {
+		mcasp->missing_audio_param = true;
+		goto out;
+	}
+
+	if (!dev_read_u32u(mcasp->dev, "tdm-slots", &val)) {
+		if (val < 2 || val > 32)
+			debug("tdm-slots must be in range[2-32]\n");
+
+		mcasp->tdm_slots = val;
+	} else if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) {
+		mcasp->missing_audio_param = true;
+		goto out;
+	}
+
+	/* In u-boot we do not have DMA => We do not use DAT_port for transfer*/
+	mcasp->dat_port = false;
+	mcasp->dat_port_addr = 0x02b18000;
+
+	/* serial-dir */
+	u32 *of_serial_dir32;
+
+	if (!dev_read_u32_array(mcasp->dev, "serial-dir", of_serial_dir32, 16)) {
+		mcasp->serial_dir = of_serial_dir32;
+		mcasp->num_serializer = 16;
+	} else {
+		printf("error in reading serial-dir from DT\n");
+	}
+
+	if (!dev_read_u32u(mcasp->dev, "tx-num-evt", &val))
+		mcasp->txnumevt = val;
+	else
+		printf("error in reading tx-num-evt\n");
+
+	if (!dev_read_u32u(mcasp->dev, "rx-num-evt", &val))
+		mcasp->rxnumevt = val;
+	else
+		printf("error in reading tx-num-evt\n");
+
+	if (!dev_read_u32u(mcasp->dev, "dismod", &val)) {
+		if (val == 0 || val == 2 || val == 3) {
+			mcasp->dismod = DISMOD_VAL(val);
+		} else {
+			printf("Invalid dismod value: %u\n", val);
+			mcasp->dismod = DISMOD_LOW;
+		}
+	} else {
+		mcasp->dismod = DISMOD_LOW;
+	}
+
+out:
+	if (mcasp->missing_audio_param) {
+		printf("Insufficient DT parameter(s)\n");
+		return -ENODEV;
+	}
+	/* sanity check for tdm slots parameter */
+	if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) {
+		if (mcasp->tdm_slots < 2) {
+			printf("invalid tdm slots: %d\n",
+			       mcasp->tdm_slots);
+			mcasp->tdm_slots = 2;
+		} else if (mcasp->tdm_slots > 32) {
+			printf("invalid tdm slots: %d\n",
+			       mcasp->tdm_slots);
+			mcasp->tdm_slots = 32;
+		}
+	} else {
+		mcasp->tdm_slots = 32;
+	}
+	return 0;
+}
+
+static int mcasp_i2s_of_to_plat(struct udevice *dev)
+{
+	struct i2s_uc_priv *priv = dev_get_uclass_priv(dev);
+	ulong base;
+
+	base = dev_read_addr(dev);
+	if (base == FDT_ADDR_T_NONE) {
+		debug("%s: Missing  i2s base\n", __func__);
+		return -EINVAL;
+	}
+	priv->base_address = base;
+
+	if (dev_read_u32u(dev, "ti,i2s-epll-clock-frequency",
+			  &priv->audio_pll_clk))
+		goto err;
+	debug("audio_pll_clk = %d\n", priv->audio_pll_clk);
+	if (dev_read_u32u(dev, "ti,i2s-sampling-rate",
+			  &priv->samplingrate))
+		goto err;
+	debug("samplingrate = %d\n", priv->samplingrate);
+	if (dev_read_u32u(dev, "ti,i2s-bits-per-sample",
+			  &priv->bitspersample))
+		goto err;
+	debug("bitspersample = %d\n", priv->bitspersample);
+	if (dev_read_u32u(dev, "ti,i2s-channels", &priv->channels))
+		goto err;
+	debug("channels = %d\n", priv->channels);
+	if (dev_read_u32u(dev, "ti,i2s-lr-clk-framesize", &priv->rfs))
+		goto err;
+	debug("rfs = %d\n", priv->rfs);
+	if (dev_read_u32u(dev, "ti,i2s-bit-clk-framesize", &priv->bfs))
+		goto err;
+	debug("bfs = %d\n", priv->bfs);
+
+	if (dev_read_u32u(dev, "ti,i2s-id", &priv->id))
+		goto err;
+	debug("id = %d\n", priv->id);
+	return 0;
+
+err:
+	debug("fail to get sound i2s node properties\n");
+	return -EINVAL;
+}
+
+static int davinci_mcasp_set_dai_fmt(struct mcasp_priv *mcasp,
+				     unsigned int fmt)
+{
+	int ret = 0;
+	u32 data_delay;
+	bool fs_pol_rising;
+	bool inv_fs = false;
+
+	if (!fmt)
+		return 0;
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_DSP_A:
+		mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
+		mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
+		/* 1st data bit occur one ACLK cycle after the frame sync */
+		data_delay = 1;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+	case SND_SOC_DAIFMT_AC97:
+		mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
+		mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
+		/* No delay after FS */
+		data_delay = 0;
+		break;
+	case SND_SOC_DAIFMT_I2S:
+		/* configure a full-word SYNC pulse (LRCLK) */
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
+		/* 1st data bit occur one ACLK cycle after the frame sync */
+		data_delay = 1;
+		/* FS need to be inverted */
+		inv_fs = true;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+	case SND_SOC_DAIFMT_LEFT_J:
+		/* configure a full-word SYNC pulse (LRCLK) */
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
+		/* No delay after FS */
+		data_delay = 0;
+		break;
+	default:
+		ret = -EINVAL;
+		goto out;
+	}
+
+	mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, FSXDLY(data_delay),
+		       FSXDLY(3));
+	mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, FSRDLY(data_delay),
+		       FSRDLY(3));
+
+	switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+	case SND_SOC_DAIFMT_BP_FP:
+		/* codec is clock and frame slave */
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
+
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
+
+		/* BCLK */
+
+		set_bit_pdir(PIN_BIT_ACLKX, &mcasp->pdir);
+		set_bit_pdir(PIN_BIT_ACLKR, &mcasp->pdir);
+		/* Frame Sync */
+		set_bit_pdir(PIN_BIT_AFSX, &mcasp->pdir);
+		set_bit_pdir(PIN_BIT_AFSR, &mcasp->pdir);
+
+		mcasp->bclk_master = 1;
+		break;
+	case SND_SOC_DAIFMT_BP_FC:
+		/* codec is clock slave and frame master */
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+		mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
+
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+		mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
+
+		/* BCLK */
+		set_bit_pdir(PIN_BIT_ACLKX, &mcasp->pdir);
+		set_bit_pdir(PIN_BIT_ACLKR, &mcasp->pdir);
+		/* Frame Sync */
+		clear_bit_pdir(PIN_BIT_AFSX, &mcasp->pdir);
+		clear_bit_pdir(PIN_BIT_AFSR, &mcasp->pdir);
+
+		mcasp->bclk_master = 1;
+		break;
+	case SND_SOC_DAIFMT_BC_FP:
+		/* codec is clock master and frame slave */
+		mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
+
+		mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
+
+		/* BCLK */
+		clear_bit_pdir(PIN_BIT_ACLKX, &mcasp->pdir);
+		clear_bit_pdir(PIN_BIT_ACLKR, &mcasp->pdir);
+		/* Frame Sync */
+		set_bit_pdir(PIN_BIT_AFSX, &mcasp->pdir);
+		set_bit_pdir(PIN_BIT_AFSR, &mcasp->pdir);
+
+		mcasp->bclk_master = 0;
+		break;
+	case SND_SOC_DAIFMT_BC_FC:
+		/* codec is clock and frame master */
+		mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+		mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
+
+		mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+		mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
+
+		/* BCLK */
+		clear_bit_pdir(PIN_BIT_ACLKX, &mcasp->pdir);
+		clear_bit_pdir(PIN_BIT_ACLKR, &mcasp->pdir);
+		/* Frame Sync */
+		clear_bit_pdir(PIN_BIT_AFSX, &mcasp->pdir);
+		clear_bit_pdir(PIN_BIT_AFSR, &mcasp->pdir);
+
+		mcasp->bclk_master = 0;
+		break;
+	default:
+		ret = -EINVAL;
+		goto out;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_IB_NF:
+		mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+		mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+		fs_pol_rising = true;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+		fs_pol_rising = false;
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+		mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+		fs_pol_rising = false;
+		break;
+	case SND_SOC_DAIFMT_NB_NF_UBT:
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+		fs_pol_rising = true;
+		break;
+	default:
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (inv_fs)
+		fs_pol_rising = !fs_pol_rising;
+
+	if (fs_pol_rising) {
+		mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
+		mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+	} else {
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+	}
+
+	mcasp->dai_fmt = fmt;
+out:
+	return ret;
+}
+
+static int mcasp_common_hw_param(struct mcasp_priv *mcasp, int stream,
+				 int period_words, int channels)
+{
+	int i;
+	u8 tx_ser = 0;
+	u8 rx_ser = 0;
+	u8 slots = mcasp->tdm_slots;
+	u8 max_active_serializers, max_rx_serializers, max_tx_serializers;
+	int active_serializers, numevt;
+	u32 reg;
+	/* In DIT mode we only allow maximum of one serializers for now */
+	if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
+		max_active_serializers = 1;
+	else
+		max_active_serializers = (channels + slots - 1) / slots;
+
+	/* Default configuration */
+	if (mcasp->version < MCASP_VERSION_3)
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_PWREMUMGT_REG, MCASP_SOFT);
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS);
+		max_tx_serializers = max_active_serializers;
+		max_rx_serializers =
+		mcasp->active_serializers[SNDRV_PCM_STREAM_CAPTURE];
+	} else {
+		mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
+		mcasp_clr_bits(mcasp, DAVINCI_MCASP_REVTCTL_REG, RXDATADMADIS);
+		max_tx_serializers =
+			mcasp->active_serializers[SNDRV_PCM_STREAM_PLAYBACK];
+		max_rx_serializers = max_active_serializers;
+	}
+
+	for (i = 0; i < mcasp->num_serializer; i++) {
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i),
+			       mcasp->serial_dir[i]);
+		if (mcasp->serial_dir[i] == TX_MODE && tx_ser < max_tx_serializers) {
+			mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i),
+				       mcasp->dismod, DISMOD_MASK);
+			set_bit_pdir(PIN_BIT_AXR(i), &mcasp->pdir);
+			tx_ser++;
+		} else if (mcasp->serial_dir[i] == RX_MODE &&
+					rx_ser < max_rx_serializers) {
+			clear_bit_pdir(PIN_BIT_AXR(i), &mcasp->pdir);
+			rx_ser++;
+		} else {
+			/* Inactive or unused pin, set it to inactive */
+			mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i),
+				       SRMOD_INACTIVE, SRMOD_MASK);
+			/* If unused, set DISMOD for the pin */
+			if (mcasp->serial_dir[i] != INACTIVE_MODE)
+				mcasp_mod_bits(mcasp,
+					       DAVINCI_MCASP_XRSRCTL_REG(i),
+					       mcasp->dismod, DISMOD_MASK);
+			clear_bit_pdir(PIN_BIT_AXR(i), &mcasp->pdir);
+		}
+	}
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		active_serializers = tx_ser;
+		numevt = mcasp->txnumevt;
+		reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+	} else {
+		active_serializers = rx_ser;
+		numevt = mcasp->rxnumevt;
+		reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+	}
+
+	if (active_serializers < max_active_serializers) {
+		debug("stream has more channels (%d) than are enabled in mcasp (%d)\n", channels,
+		      active_serializers * slots);
+		return -EINVAL;
+	}
+
+	if (period_words % active_serializers) {
+		debug("Invalid combination of period words and active serializers: %d, %d\n",
+		      period_words, active_serializers);
+		return -EINVAL;
+	}
+
+	/*
+	 * Calculate the optimal AFIFO depth for platform side:
+	 * The number of words for numevt need to be in steps of active
+	 * serializers.
+	 */
+	numevt = (numevt / active_serializers) * active_serializers;
+
+	while (period_words % numevt && numevt > 0)
+		numevt -= active_serializers;
+	if (numevt <= 0)
+		numevt = active_serializers;
+
+	mcasp_mod_bits(mcasp, reg, active_serializers, NUMDMA_MASK);
+	mcasp_mod_bits(mcasp, reg, NUMEVT(numevt), NUMEVT_MASK);
+
+	mcasp->active_serializers[stream] = active_serializers;
+
+	return 0;
+}
+
+static int mcasp_i2s_hw_param(struct mcasp_priv *mcasp, int stream,
+			      int channels)
+{
+	int i, active_slots;
+	int total_slots;
+	int active_serializers;
+	u32 mask = 0;
+	u32 busel = 0;
+
+	total_slots = mcasp->tdm_slots;
+
+	/*
+	 * If more than one serializer is needed, then use them with
+	 * all the specified tdm_slots. Otherwise, one serializer can
+	 * cope with the transaction using just as many slots as there
+	 * are channels in the stream.
+	 */
+	if (mcasp->tdm_mask[stream]) {
+		active_slots = hweight32(mcasp->tdm_mask[stream]);
+		active_serializers = (channels + active_slots - 1) /
+			active_slots;
+		if (active_serializers == 1)
+			active_slots = channels;
+		for (i = 0; i < total_slots; i++) {
+			if ((1 << i) & mcasp->tdm_mask[stream]) {
+				mask |= (1 << i);
+				if (--active_slots <= 0)
+					break;
+			}
+		}
+	} else {
+		active_serializers = (channels + total_slots - 1) / total_slots;
+		if (active_serializers == 1)
+			active_slots = channels;
+		else
+			active_slots = total_slots;
+
+		for (i = 0; i < active_slots; i++)
+			mask |= (1 << i);
+	}
+
+	mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
+
+	if (!mcasp->dat_port)
+		busel = TXSEL;
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask);
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD);
+		mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
+			       FSXMOD(total_slots), FSXMOD(0x1FF));
+	} else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+		mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask);
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD);
+		mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG,
+			       FSRMOD(total_slots), FSRMOD(0x1FF));
+		/*
+		 * If McASP is set to be TX/RX synchronous and the playback is
+		 * not running already we need to configure the TX slots in
+		 * order to have correct FSX on the bus
+		 */
+		if (mcasp_is_synchronous(mcasp) && !mcasp->channels)
+			mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
+				       FSXMOD(total_slots), FSXMOD(0x1FF));
+	}
+
+	return 0;
+}
+
+static int davinci_config_channel_size(struct mcasp_priv *mcasp,
+				       int sample_width)
+{
+	u32 fmt;
+	u32 tx_rotate, rx_rotate, slot_width;
+	u32 mask = (1ULL << sample_width) - 1;
+
+	if (mcasp->slot_width)
+		slot_width = mcasp->slot_width;
+	else if (mcasp->max_format_width)
+		slot_width = mcasp->max_format_width;
+	else
+		slot_width = sample_width;
+	/*
+	 * TX rotation:
+	 * right aligned formats: rotate w/ slot_width
+	 * left aligned formats: rotate w/ sample_width
+	 *
+	 * RX rotation:
+	 * right aligned formats: no rotation needed
+	 * left aligned formats: rotate w/ (slot_width - sample_width)
+	 */
+	if ((mcasp->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) ==
+	    SND_SOC_DAIFMT_RIGHT_J) {
+		tx_rotate = (slot_width / 4) & 0x7;
+		rx_rotate = 0;
+	} else {
+		tx_rotate = (sample_width / 4) & 0x7;
+		rx_rotate = (slot_width - sample_width) / 4;
+	}
+
+	/* mapping of the XSSZ bit-field as described in the datasheet */
+	fmt = (slot_width >> 1) - 1;
+
+	if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) {
+		mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt),
+			       RXSSZ(0x0F));
+		mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXSSZ(fmt),
+			       TXSSZ(0x0F));
+		mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(tx_rotate),
+			       TXROT(7));
+		mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXROT(rx_rotate),
+			       RXROT(7));
+		mcasp_set_reg(mcasp, DAVINCI_MCASP_RXMASK_REG, mask);
+	} else {
+		/*
+		 * according to the TRM it should be TXROT=0, this one works:
+		 * 16 bit to 23-8 (TXROT=6, rotate 24 bits)
+		 * 24 bit to 23-0 (TXROT=0, rotate 0 bits)
+		 *
+		 * TXROT = 0 only works with 24bit samples
+		 */
+		tx_rotate = (sample_width / 4 + 2) & 0x7;
+
+		mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(tx_rotate),
+			       TXROT(7));
+		mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXSSZ(15),
+			       TXSSZ(0x0F));
+	}
+
+	mcasp_set_reg(mcasp, DAVINCI_MCASP_TXMASK_REG, mask);
+
+	return 0;
+}
+
+static int __davinci_mcasp_set_clkdiv(struct mcasp_priv *mcasp, int div_id,
+				      int div, bool explicit)
+{
+	switch (div_id) {
+	case MCASP_CLKDIV_AUXCLK:			/* MCLK divider */
+		mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
+			       AHCLKXDIV(div - 1), AHCLKXDIV_MASK);
+		mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
+			       AHCLKRDIV(div - 1), AHCLKRDIV_MASK);
+		break;
+
+	case MCASP_CLKDIV_BCLK:			/* BCLK divider */
+		mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG,
+			       ACLKXDIV(div - 1), ACLKXDIV_MASK);
+		mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG,
+			       ACLKRDIV(div - 1), ACLKRDIV_MASK);
+		if (explicit)
+			mcasp->bclk_div = div;
+		break;
+
+	case MCASP_CLKDIV_BCLK_FS_RATIO:
+		/*
+		 * BCLK/LRCLK ratio descries how many bit-clock cycles
+		 * fit into one frame. The clock ratio is given for a
+		 * full period of data (for I2S format both left and
+		 * right channels), so it has to be divided by number
+		 * of tdm-slots (for I2S - divided by 2).
+		 * Instead of storing this ratio, we calculate a new
+		 * tdm_slot width by dividing the ratio by the
+		 * number of configured tdm slots.
+		 */
+		mcasp->slot_width = div / mcasp->tdm_slots;
+		if (div % mcasp->tdm_slots)
+			debug("%s(): BCLK/LRCLK %d is not divisible by %d tdm slots",
+			      __func__, div, mcasp->tdm_slots);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int davinci_mcasp_calc_clk_div(struct mcasp_priv *mcasp,
+				      unsigned int sysclk_freq,
+				      unsigned int bclk_freq, bool set)
+{
+	u32 reg = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG);
+	int div = sysclk_freq / bclk_freq;
+	int rem = sysclk_freq % bclk_freq;
+	int error_ppm;
+	int aux_div = 1;
+
+	if (div > (ACLKXDIV_MASK + 1)) {
+		if (reg & AHCLKXE) {
+			aux_div = div / (ACLKXDIV_MASK + 1);
+			if (div % (ACLKXDIV_MASK + 1))
+				aux_div++;
+
+			sysclk_freq /= aux_div;
+			div = sysclk_freq / bclk_freq;
+			rem = sysclk_freq % bclk_freq;
+		} else if (set) {
+			debug("Too fast reference clock (%u)\n",
+			      sysclk_freq);
+		}
+	}
+
+	if (rem != 0) {
+		if (div == 0 ||
+		    ((sysclk_freq / div) - bclk_freq) >
+		    (bclk_freq - (sysclk_freq / (div + 1)))) {
+			div++;
+			rem = rem - bclk_freq;
+		}
+	}
+	error_ppm = (div * 1000000 + (int)div64_long(1000000LL * rem,
+		     (int)bclk_freq)) / div - 1000000;
+
+	if (set) {
+		if (error_ppm)
+			debug("Sample-rate is off by %d PPM\n",
+			      error_ppm);
+
+		__davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_BCLK, div, 0);
+		if (reg & AHCLKXE)
+			__davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_AUXCLK,
+						   aux_div, 0);
+	}
+
+	return error_ppm;
+}
+
+static int davinci_mcasp_hw_params(struct mcasp_priv *mcasp)
+{
+	int word_length;
+	int channels = mcasp->pdata->channels;
+	int period_size = 1000;
+	int ret;
+	int params_format = 2;
+
+	switch (params_format) {
+	case SNDRV_PCM_FORMAT_U8:
+	case SNDRV_PCM_FORMAT_S8:
+		word_length = 8;
+		break;
+
+	case SNDRV_PCM_FORMAT_U16_LE:
+	case SNDRV_PCM_FORMAT_S16_LE:
+		word_length = 16;
+		break;
+
+	case SNDRV_PCM_FORMAT_U24_3LE:
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		word_length = 24;
+		break;
+
+	case SNDRV_PCM_FORMAT_U24_LE:
+	case SNDRV_PCM_FORMAT_S24_LE:
+		word_length = 24;
+		break;
+
+	case SNDRV_PCM_FORMAT_U32_LE:
+	case SNDRV_PCM_FORMAT_S32_LE:
+		word_length = 32;
+		break;
+
+	default:
+		debug("davinci-mcasp: unsupported PCM format\n");
+		return -EINVAL;
+	}
+
+	ret = davinci_mcasp_set_dai_fmt(mcasp, mcasp->dai_fmt);
+	if (ret)
+		return ret;
+
+	/*
+	 * If mcasp is BCLK master, and a BCLK divider was not provided by
+	 * the machine driver, we need to calculate the ratio.
+	 */
+	if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) {
+		int slots = mcasp->tdm_slots;
+		// int rate = params_rate(params);
+		// int sbits = params_width(params);
+		int rate = mcasp->pdata->samplingrate;
+		int sbits = mcasp->pdata->bitspersample;
+		unsigned int bclk_target;
+
+		if (mcasp->slot_width)
+			sbits = mcasp->slot_width;
+
+		if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE)
+			bclk_target = rate * sbits * slots;
+		else
+			bclk_target = rate * 128;
+
+		davinci_mcasp_calc_clk_div(mcasp, mcasp->sysclk_freq,
+					   bclk_target, true);
+	}
+
+	/* 0 is put in place of stream, because we only support transmission */
+	ret = mcasp_common_hw_param(mcasp, 0,
+				    period_size * channels, channels);
+	if (ret)
+		return ret;
+
+	ret = mcasp_i2s_hw_param(mcasp, 0,
+				 channels);
+
+	if (ret)
+		return ret;
+
+	davinci_config_channel_size(mcasp, word_length);
+
+	if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) {
+		mcasp->channels = channels;
+		if (!mcasp->max_format_width)
+			mcasp->max_format_width = word_length;
+	}
+
+	return 0;
+}
+
+static int davinci_mcasp_set_sysclk(struct mcasp_priv *mcasp, int clk_id,
+				    unsigned int freq, int dir)
+{
+	if (dir == SND_SOC_CLOCK_IN) {
+		switch (clk_id) {
+		case MCASP_CLK_HCLK_AHCLK:
+			mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
+				       AHCLKXE);
+			mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
+				       AHCLKRE);
+			clear_bit_pdir(PIN_BIT_AHCLKX, &mcasp->pdir);
+			break;
+		case MCASP_CLK_HCLK_AUXCLK:
+			mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
+				       AHCLKXE);
+			mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
+				       AHCLKRE);
+			set_bit_pdir(PIN_BIT_AHCLKX, &mcasp->pdir);
+			break;
+		default:
+			debug("Invalid clk id: %d\n", clk_id);
+			goto out;
+		}
+	} else {
+		/* Select AUXCLK as HCLK */
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE);
+		mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE);
+		set_bit_pdir(PIN_BIT_AHCLKX, &mcasp->pdir);
+	}
+	/*
+	 * When AHCLK X/R is selected to be output it means that the HCLK is
+	 * the same clock - coming via AUXCLK.
+	 */
+	mcasp->sysclk_freq = freq;
+out:
+	return 0;
+}
+
+static void mcasp_start_tx(struct mcasp_priv *mcasp)
+{
+	if (mcasp->txnumevt) {	/* enable FIFO */
+		u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+
+		mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+		mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
+	}
+
+	/* Start clocks */
+	mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
+	mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
+	mcasp_set_clk_pdir(mcasp, true);
+
+	// Disabling DMA req
+	mcasp_set_bits(mcasp, DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS);
+}
+
+static void mcasp_stop_tx(struct mcasp_priv *mcasp)
+{
+	u32 val = 0;
+	/*
+	 * In synchronous mode keep TX clocks running if the capture stream is
+	 * still running.
+	 */
+	if (mcasp_is_synchronous(mcasp) && mcasp->streams)
+		val =  TXHCLKRST | TXCLKRST | TXFSRST;
+	else
+		mcasp_set_clk_pdir(mcasp, false);
+
+	mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, val);
+	mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
+
+	if (mcasp->txnumevt) {	/* disable FIFO */
+		u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+
+		mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+	}
+
+	mcasp_set_axr_pdir(mcasp, false);
+}
+
+int i2s_transfer_data(struct mcasp_priv *mcasp, void *data, uint data_size)
+{
+	u32 *ptr;
+
+	ptr = data;
+
+	if (data_size < MCASP_MAX_AFIFO_DEPTH) {
+		debug("%s : Invalid data size\n", __func__);
+		return -ENODATA;
+	}
+
+	/* Activate serializer(s) */
+	mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFF);
+
+	int error_cnt = 0;
+stage_A:
+	mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR);
+
+	//Are serializers active
+	if (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG) & BIT(10))) {
+		debug("%s : serializer not active\n", __func__);
+		return -EINVAL;
+	}
+
+	//is transmit slot 0 active in polling mode
+	if (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_TXTDMSLOT_REG) & BIT(0)))
+		printf("not active in polling mode\n");
+
+	//is XDATA set
+	if (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) & XRDATA)) {
+		printf("XDATA is not 1\n");
+		error_cnt++;
+		if (error_cnt > 10) {
+			debug("%s : XDATA is not set\n", __func__);
+			return -EINVAL;
+		}
+		goto stage_A;
+	}
+
+	//getting the data
+	mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG(0), *ptr++);
+	data_size -= sizeof(*ptr);
+
+	/* wait for XDATA to be cleared */
+	int cnt = 0;
+
+	while ((mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) & XRDATA) &&
+	       (cnt < 100000))
+		cnt++;
+
+	mcasp_set_axr_pdir(mcasp, true);
+
+	/* Release TX state machine */
+	mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTL_REG, TXSMRST);
+	/* Release Frame Sync generator */
+	mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTL_REG, TXFSRST);
+
+	while (data_size > 0) {
+		/*wait for XDATA to be set*/
+		int cnt = 0;
+
+		while (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) & XRDATA) && cnt < 1000000)
+			cnt++;
+
+		for (int i = 0, j = 0;
+		     i < mcasp->num_serializer && j < mcasp->active_serializers[0]; i++) {
+			if ((mcasp_get_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i)) & BIT(4)) &&
+			    (mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) & BIT(5))) {
+				mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG(i), *ptr++);
+				data_size -= sizeof(*ptr);
+				j++;
+			}
+		}
+
+		if ((mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) & BIT(0))) {
+			debug("Underrun fault has occurred\n");
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int mcasp_i2s_tx_data(struct udevice *dev, void *data, uint data_size)
+{
+	struct mcasp_priv *mcasp = dev_get_priv(dev);
+
+	mcasp_start_tx(mcasp);
+
+	/*sound play*/
+
+	int ret = i2s_transfer_data(mcasp, data, data_size);
+
+	if (ret)
+		printf("no audio playback");
+
+	mcasp_stop_tx(mcasp);
+
+	return 0;
+}
+
+static int mcasp_i2s_probe(struct udevice *dev)
+{
+	struct i2s_uc_priv *priv = dev_get_uclass_priv(dev);
+	struct mcasp_priv *mcasp = dev_get_priv(dev);
+	int ret;
+
+	mcasp->dev = dev;
+	mcasp->pdata = priv;
+	mcasp->base = dev_remap_addr_name(dev, "mpu");
+	mcasp->version = MCASP_VERSION_3;
+	ret = davinci_mcasp_get_config(mcasp);
+	if (ret) {
+		printf("error in get_config\n");
+		goto err;
+	}
+	/* All pins as McASP*/
+	mcasp_set_reg(mcasp, DAVINCI_MCASP_PFUNC_REG, 0x00000000);
+
+	//FIFO base address
+	mcasp->fifo_base = DAVINCI_MCASP_V3_AFIFO_BASE;
+
+	ret = davinci_mcasp_set_sysclk(mcasp, priv->id, priv->audio_pll_clk, SND_SOC_CLOCK_IN);
+	if (ret) {
+		printf("error in set_sysclk\n");
+		goto err;
+	}
+	int fmt = SND_SOC_DAIFMT_BC_FC | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_DSP_B;
+
+	ret = davinci_mcasp_set_dai_fmt(mcasp, fmt);
+	if  (ret) {
+		printf("error in set_dai_fmt\n");
+		goto err;
+	}
+
+	ret = davinci_mcasp_hw_params(mcasp);
+	if (ret) {
+		printf("error in hw_params\n");
+		goto err;
+	}
+
+	return 0;
+
+err:
+	printf("error in probe\n");
+	return ret;
+}
+
+static const struct i2s_ops mcasp_i2s_ops = {
+	.tx_data	= mcasp_i2s_tx_data,
+};
+
+static const struct udevice_id mcasp_i2s_ids[] = {
+	{ .compatible = "ti,am33xx-mcasp-audio" },
+	{ }
+};
+
+U_BOOT_DRIVER(mcasp_i2s) = {
+	.name		= "mcasp_i2s",
+	.id		= UCLASS_I2S,
+	.of_match	= mcasp_i2s_ids,
+	.probe		= mcasp_i2s_probe,
+	.of_to_plat	= mcasp_i2s_of_to_plat,
+	.ops		= &mcasp_i2s_ops,
+	.priv_auto	= sizeof(struct mcasp_priv)
+};
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 4/6] arm: dts: k3-am625-sk-u-boot.dtsi: Add sound driver nodes
  2024-07-08  7:52 [PATCH 0/6] Enable audio playback in AM62x SoC Scaria Kochidanadu
                   ` (2 preceding siblings ...)
  2024-07-08  7:52 ` [PATCH 3/6] sound: ti: Add MCASP driver for transfer of Audio data to sound codec Scaria Kochidanadu
@ 2024-07-08  7:52 ` Scaria Kochidanadu
  2024-07-08 15:48   ` Tom Rini
  2024-07-08  7:52 ` [PATCH 5/6] configs: am62x_evm_a53_defconfig: Enable sound and I2C commands Scaria Kochidanadu
  2024-07-08  7:52 ` [PATCH 6/6] linux: bitmap.h: Add for_each_set_bit_from() function Scaria Kochidanadu
  5 siblings, 1 reply; 13+ messages in thread
From: Scaria Kochidanadu @ 2024-07-08  7:52 UTC (permalink / raw)
  To: trini, u-boot
  Cc: sjg, mkorpershoek, abdellatif.elkhlifi, sumit.garg,
	neil.armstrong, caleb.connolly, semen.protsenko,
	marek.vasut+renesas, nathan.morrison, s-kochidanadu, devarsht,
	j-luthra, vigneshr, praneeth, nm, khasim, rogerq,
	francesco.dolcini, afd, jan.kiszka

Update the am625-sk-u-boot device tree to incorporate the sound card and
sound drivers: MCASP and TLV320AIC3106 Codec

Signed-off-by: Scaria Kochidanadu <s-kochidanadu@ti.com>
---
 arch/arm/dts/k3-am625-sk-u-boot.dtsi | 85 ++++++++++++++++++++++++++++
 1 file changed, 85 insertions(+)

diff --git a/arch/arm/dts/k3-am625-sk-u-boot.dtsi b/arch/arm/dts/k3-am625-sk-u-boot.dtsi
index fa778b0ff4..74c273d940 100644
--- a/arch/arm/dts/k3-am625-sk-u-boot.dtsi
+++ b/arch/arm/dts/k3-am625-sk-u-boot.dtsi
@@ -10,6 +10,91 @@
 	chosen {
 		tick-timer = &main_timer0;
 	};
+
+	tlv320_mclk: clk-0 {
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <12288000>;
+	};
+
+	codec_audio: sound {
+		compatible = "simple-audio-card";
+		ti,codec = <&tlv320aic3106>;
+		simple-audio-card,name = "AM62x-SKEVM";
+		simple-audio-card,widgets =
+		"Headphone",    "Headphone Jack",
+		"Line",         "Line In",
+		"Microphone",   "Microphone Jack";
+		simple-audio-card,routing =
+		"Headphone Jack",       "HPLOUT",
+		"Headphone Jack",       "HPROUT",
+		"LINE1L",               "Line In",
+		"LINE1R",               "Line In",
+		"MIC3R",                "Microphone Jack",
+		"Microphone Jack",      "Mic Bias";
+		simple-audio-card,format = "dsp_b";
+		simple-audio-card,bitclock-master = <&sound_master>;
+		simple-audio-card,frame-master = <&sound_master>;
+		simple-audio-card,bitclock-inversion;
+
+		simple-audio-card,cpu {
+		sound-dai = <&mcasp1>;
+		};
+
+		sound_master: simple-audio-card,codec {
+		sound-dai = <&tlv320aic3106>;
+		clocks = <&tlv320_mclk>;
+		};
+	};
+};
+
+&mcasp1 {
+	status = "okay";
+	#sound-dai-cells = <0>;
+
+	pinctrl-names = "default";
+	pinctrl-0 = <&main_mcasp1_pins_default>;
+
+	op-mode = <0>;          /* MCASP_IIS_MODE */
+	tdm-slots = <2>;
+
+	ti,i2s-epll-clock-frequency = <96000000>;
+	ti,i2s-sampling-rate = <48000>;
+	ti,i2s-bits-per-sample = <16>;
+	ti,i2s-channels = <2>;
+	ti,i2s-lr-clk-framesize = <256>;
+	ti,i2s-bit-clk-framesize = <32>;
+	ti,i2s-id = <0>;
+
+	serial-dir = <
+	       1 0 2 0
+	       0 0 0 0
+	       0 0 0 0
+	       0 0 0 0
+	>;              /* 0: INACTIVE, 1: TX, 2: RX */
+	tx-num-evt = <0>;
+	rx-num-evt = <0>;
+};
+
+&main_i2c1 {
+	status = "okay";
+	pinctrl-names = "default";
+	pinctrl-0 = <&main_i2c1_pins_default>;
+	clock-frequency = <100000>;
+
+	tlv320aic3106: audio-codec@1b {
+		#sound-dai-cells = <0>;
+		compatible = "ti,tlv320aic3106";
+		reg = <0x1b>;
+		ai3x-micbias-vg = <1>;  /* 2.0V */
+		ai3x-ocmv = <1>;
+
+		/* Regulators */
+		AVDD-supply = <&vcc_3v3_sys>;
+		IOVDD-supply = <&vcc_3v3_sys>;
+		DRVDD-supply = <&vcc_3v3_sys>;
+		DVDD-supply = <&vcc_1v8>;
+	};
 };
 
 &main_timer0 {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 5/6] configs: am62x_evm_a53_defconfig: Enable sound and I2C commands
  2024-07-08  7:52 [PATCH 0/6] Enable audio playback in AM62x SoC Scaria Kochidanadu
                   ` (3 preceding siblings ...)
  2024-07-08  7:52 ` [PATCH 4/6] arm: dts: k3-am625-sk-u-boot.dtsi: Add sound driver nodes Scaria Kochidanadu
@ 2024-07-08  7:52 ` Scaria Kochidanadu
  2024-07-08  7:52 ` [PATCH 6/6] linux: bitmap.h: Add for_each_set_bit_from() function Scaria Kochidanadu
  5 siblings, 0 replies; 13+ messages in thread
From: Scaria Kochidanadu @ 2024-07-08  7:52 UTC (permalink / raw)
  To: trini, u-boot
  Cc: sjg, mkorpershoek, abdellatif.elkhlifi, sumit.garg,
	neil.armstrong, caleb.connolly, semen.protsenko,
	marek.vasut+renesas, nathan.morrison, s-kochidanadu, devarsht,
	j-luthra, vigneshr, praneeth, nm, khasim, rogerq,
	francesco.dolcini, afd, jan.kiszka

Add the tlv320aic3106 codec and sound driver in the defconfig, and
enable the sound commands in Uboot. Also enable the I2C communication
for transfer to and from the codec.

Signed-off-by: Scaria Kochidanadu <s-kochidanadu@ti.com>
---
 configs/am62x_evm_a53_defconfig | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/configs/am62x_evm_a53_defconfig b/configs/am62x_evm_a53_defconfig
index 6c708dcb05..858946d7fc 100644
--- a/configs/am62x_evm_a53_defconfig
+++ b/configs/am62x_evm_a53_defconfig
@@ -51,8 +51,10 @@ CONFIG_SYS_SPI_U_BOOT_OFFS=0x280000
 CONFIG_SPL_YMODEM_SUPPORT=y
 CONFIG_CMD_BOOTEFI_SELFTEST=y
 CONFIG_CMD_NVEDIT_EFI=y
+CONFIG_CMD_I2C=y
 CONFIG_CMD_MMC=y
 CONFIG_CMD_EFIDEBUG=y
+CONFIG_CMD_SOUND=y
 CONFIG_MMC_SPEED_MODE_SET=y
 CONFIG_OF_CONTROL=y
 CONFIG_SPL_OF_CONTROL=y
@@ -71,6 +73,8 @@ CONFIG_CLK_TI_SCI=y
 CONFIG_DMA_CHANNELS=y
 CONFIG_TI_K3_NAVSS_UDMA=y
 CONFIG_TI_SCI_PROTOCOL=y
+CONFIG_DM_I2C=y
+CONFIG_SYS_I2C_OMAP24XX=y
 CONFIG_DM_MAILBOX=y
 CONFIG_K3_SEC_PROXY=y
 CONFIG_SUPPORT_EMMC_BOOT=y
@@ -100,6 +104,11 @@ CONFIG_RESET_TI_SCI=y
 CONFIG_DM_RTC=y
 CONFIG_RTC_EMULATION=y
 CONFIG_DM_SERIAL=y
+CONFIG_SOUND=y
+CONFIG_I2S=y
+CONFIG_SOUND_RT5677=y
+CONFIG_I2S_TI=y
+CONFIG_SOUND_TLV320AIC3106=y
 CONFIG_SOC_DEVICE=y
 CONFIG_SOC_DEVICE_TI_K3=y
 CONFIG_SOC_TI=y
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 6/6] linux: bitmap.h: Add for_each_set_bit_from() function
  2024-07-08  7:52 [PATCH 0/6] Enable audio playback in AM62x SoC Scaria Kochidanadu
                   ` (4 preceding siblings ...)
  2024-07-08  7:52 ` [PATCH 5/6] configs: am62x_evm_a53_defconfig: Enable sound and I2C commands Scaria Kochidanadu
@ 2024-07-08  7:52 ` Scaria Kochidanadu
  2024-07-08 15:49   ` Tom Rini
  5 siblings, 1 reply; 13+ messages in thread
From: Scaria Kochidanadu @ 2024-07-08  7:52 UTC (permalink / raw)
  To: trini, u-boot
  Cc: sjg, mkorpershoek, abdellatif.elkhlifi, sumit.garg,
	neil.armstrong, caleb.connolly, semen.protsenko,
	marek.vasut+renesas, nathan.morrison, s-kochidanadu, devarsht,
	j-luthra, vigneshr, praneeth, nm, khasim, rogerq,
	francesco.dolcini, afd, jan.kiszka

Add function macro for_each_set_bit_from(), an extension to the already
existing function macro for_each_set_bit() to iterate through set bits
of a variable from a given bit index as required by the MCASP Driver.

Signed-off-by: Scaria Kochidanadu <s-kochidanadu@ti.com>
---
 include/linux/bitmap.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h
index 0a8503af9f..da3aa428b6 100644
--- a/include/linux/bitmap.h
+++ b/include/linux/bitmap.h
@@ -159,6 +159,11 @@ static inline unsigned long find_first_bit(const unsigned long *addr, unsigned l
 	     (bit) < (size);					\
 	     (bit) = find_next_bit((addr), (size), (bit) + 1))
 
+#define for_each_set_bit_from(_bit, _addr, _size) \
+	for ((_bit) = find_next_bit((_addr), (_size), (_bit));		\
+	    (_bit) < (_size);					\
+	    (_bit) = find_next_bit((_addr), (_size), (_bit) + 1))
+
 static inline unsigned long
 bitmap_find_next_zero_area(unsigned long *map,
 			   unsigned long size,
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/6] sound: ti: Add sound support for am625 board in Uboot
  2024-07-08  7:52 ` [PATCH 1/6] sound: ti: Add sound support for am625 board in Uboot Scaria Kochidanadu
@ 2024-07-08 15:46   ` Tom Rini
  2024-07-09 11:26     ` [EXTERNAL] " Scaria Kochidanadu
  0 siblings, 1 reply; 13+ messages in thread
From: Tom Rini @ 2024-07-08 15:46 UTC (permalink / raw)
  To: Scaria Kochidanadu
  Cc: u-boot, sjg, mkorpershoek, abdellatif.elkhlifi, sumit.garg,
	neil.armstrong, caleb.connolly, semen.protsenko,
	marek.vasut+renesas, nathan.morrison, devarsht, j-luthra,
	vigneshr, praneeth, nm, khasim, rogerq, francesco.dolcini, afd,
	jan.kiszka

[-- Attachment #1: Type: text/plain, Size: 1823 bytes --]

On Mon, Jul 08, 2024 at 01:22:02PM +0530, Scaria Kochidanadu wrote:

> Add a UCLASS_SOUND driver for Texas Instruments SoCs which ties together
> the tlv320aic3106 audio codec and MCASP I2S controller. Enable audio
> playback functionality by taking a data pointer and data size as the
> sound data. The uboot sound play command takes time and frequency as
> input and creates the data for a beep sound with the given parameters,
> which is then passed to the sound play function.
[snip]
> +++ b/drivers/sound/ti_sound.c
> @@ -0,0 +1,119 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * (C) Copyright 2024 Texas Instruments Incorporated - https://www.ti.com/
> + * Scaria M Kochidanadu, s-kochidanadu@ti.com
> + *
> + * based on the uboot samsung sound driver, which is
> + *
> + * Copyright 2018 Google, LLC
> + * Written by Simon Glass <sjg@chromium.org>
> + */
> +
> +#include <asm/u-boot.h> /* boot information for Linux kernel */
> +/* Pull in stuff for the build system */
> +#ifdef DO_DEPS_ONLY
> +#include <env_internal.h>
> +#endif
> +#include <audio_codec.h>
> +#include <dm.h>
> +#include <i2s.h>
> +#include <log.h>
> +#include <sound.h>
> +#include <asm/gpio.h>

Please audit this list. I really am confused by <env_internal.h>.

> +static int ti_sound_stop_play(struct udevice *dev)
> +{
> +	/* This function is necassary to satisfy the function calls
> +	 * in the Uboot command: sound play
> +	 */
> +	return 0;

/*
 * Like this...
 */

[snip]
> +static const struct udevice_id ti_sound_ids[] = {
> +	{ .compatible = "simple-audio-card" },
> +	{ }
> +};

Adding an actual simple-audio-card framework for U-Boot would be great.
This driver implies (and is?) TI-centric, instead. So lets rework this
to be split appropriately? Thanks.

-- 
Tom

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 659 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 4/6] arm: dts: k3-am625-sk-u-boot.dtsi: Add sound driver nodes
  2024-07-08  7:52 ` [PATCH 4/6] arm: dts: k3-am625-sk-u-boot.dtsi: Add sound driver nodes Scaria Kochidanadu
@ 2024-07-08 15:48   ` Tom Rini
  0 siblings, 0 replies; 13+ messages in thread
From: Tom Rini @ 2024-07-08 15:48 UTC (permalink / raw)
  To: Scaria Kochidanadu
  Cc: u-boot, sjg, mkorpershoek, abdellatif.elkhlifi, sumit.garg,
	neil.armstrong, caleb.connolly, semen.protsenko,
	marek.vasut+renesas, nathan.morrison, devarsht, j-luthra,
	vigneshr, praneeth, nm, khasim, rogerq, francesco.dolcini, afd,
	jan.kiszka

[-- Attachment #1: Type: text/plain, Size: 434 bytes --]

On Mon, Jul 08, 2024 at 01:22:05PM +0530, Scaria Kochidanadu wrote:

> Update the am625-sk-u-boot device tree to incorporate the sound card and
> sound drivers: MCASP and TLV320AIC3106 Codec
> 
> Signed-off-by: Scaria Kochidanadu <s-kochidanadu@ti.com>
> ---
>  arch/arm/dts/k3-am625-sk-u-boot.dtsi | 85 ++++++++++++++++++++++++++++
>  1 file changed, 85 insertions(+)

Where's all of this in the upstream dts?

-- 
Tom

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 659 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 6/6] linux: bitmap.h: Add for_each_set_bit_from() function
  2024-07-08  7:52 ` [PATCH 6/6] linux: bitmap.h: Add for_each_set_bit_from() function Scaria Kochidanadu
@ 2024-07-08 15:49   ` Tom Rini
  0 siblings, 0 replies; 13+ messages in thread
From: Tom Rini @ 2024-07-08 15:49 UTC (permalink / raw)
  To: Scaria Kochidanadu
  Cc: u-boot, sjg, mkorpershoek, abdellatif.elkhlifi, sumit.garg,
	neil.armstrong, caleb.connolly, semen.protsenko,
	marek.vasut+renesas, nathan.morrison, devarsht, j-luthra,
	vigneshr, praneeth, nm, khasim, rogerq, francesco.dolcini, afd,
	jan.kiszka

[-- Attachment #1: Type: text/plain, Size: 603 bytes --]

On Mon, Jul 08, 2024 at 01:22:07PM +0530, Scaria Kochidanadu wrote:

> Add function macro for_each_set_bit_from(), an extension to the already
> existing function macro for_each_set_bit() to iterate through set bits
> of a variable from a given bit index as required by the MCASP Driver.
> 
> Signed-off-by: Scaria Kochidanadu <s-kochidanadu@ti.com>
> ---
>  include/linux/bitmap.h | 5 +++++
>  1 file changed, 5 insertions(+)

Please note the Linux Kernel commit (or tag, really) you're backporting
this function from and emphasize that, not what you're using it for,
thanks.

-- 
Tom

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 659 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [EXTERNAL] Re: [PATCH 1/6] sound: ti: Add sound support for am625 board in Uboot
  2024-07-08 15:46   ` Tom Rini
@ 2024-07-09 11:26     ` Scaria Kochidanadu
  2024-07-09 15:04       ` Tom Rini
  2024-07-17 11:47       ` Nishanth Menon
  0 siblings, 2 replies; 13+ messages in thread
From: Scaria Kochidanadu @ 2024-07-09 11:26 UTC (permalink / raw)
  To: Tom Rini
  Cc: u-boot, sjg, mkorpershoek, abdellatif.elkhlifi, sumit.garg,
	neil.armstrong, caleb.connolly, semen.protsenko,
	marek.vasut+renesas, nathan.morrison, devarsht, j-luthra,
	vigneshr, praneeth, nm, khasim, rogerq, francesco.dolcini, afd,
	jan.kiszka



On 08/07/24 21:16, Tom Rini wrote:
> On Mon, Jul 08, 2024 at 01:22:02PM +0530, Scaria Kochidanadu wrote:
> 
>> Add a UCLASS_SOUND driver for Texas Instruments SoCs which ties together
>> the tlv320aic3106 audio codec and MCASP I2S controller. Enable audio
>> playback functionality by taking a data pointer and data size as the
>> sound data. The uboot sound play command takes time and frequency as
>> input and creates the data for a beep sound with the given parameters,
>> which is then passed to the sound play function.
> [snip]
>> +++ b/drivers/sound/ti_sound.c
>> @@ -0,0 +1,119 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * (C) Copyright 2024 Texas Instruments Incorporated - https://www.ti.com/
>> + * Scaria M Kochidanadu, s-kochidanadu@ti.com
>> + *
>> + * based on the uboot samsung sound driver, which is
>> + *
>> + * Copyright 2018 Google, LLC
>> + * Written by Simon Glass <sjg@chromium.org>
>> + */
>> +
>> +#include <asm/u-boot.h> /* boot information for Linux kernel */
>> +/* Pull in stuff for the build system */
>> +#ifdef DO_DEPS_ONLY
>> +#include <env_internal.h>
>> +#endif
>> +#include <audio_codec.h>
>> +#include <dm.h>
>> +#include <i2s.h>
>> +#include <log.h>
>> +#include <sound.h>
>> +#include <asm/gpio.h>
> 
> Please audit this list. I really am confused by <env_internal.h>.
> 

I will be removing this in v2 as it is not required.

>> +static int ti_sound_stop_play(struct udevice *dev)
>> +{
>> +	/* This function is necassary to satisfy the function calls
>> +	 * in the Uboot command: sound play
>> +	 */
>> +	return 0;
> 
> /*
>  * Like this...
>  */
> 
> [snip]
>> +static const struct udevice_id ti_sound_ids[] = {
>> +	{ .compatible = "simple-audio-card" },
>> +	{ }
>> +};
> 
> Adding an actual simple-audio-card framework for U-Boot would be great.
> This driver implies (and is?) TI-centric, instead. So lets rework this
> to be split appropriately? Thanks.
> 

I wanted to reuse the sound card DT node in the upstream kernel
device-tree. The U-boot upstrem repo does not have the node as it is
using an older version of DT.
But if using the simple-audio-card node is not preferred, then can we
create TI specific compatible and a separate DT node?

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [EXTERNAL] Re: [PATCH 1/6] sound: ti: Add sound support for am625 board in Uboot
  2024-07-09 11:26     ` [EXTERNAL] " Scaria Kochidanadu
@ 2024-07-09 15:04       ` Tom Rini
  2024-07-17 11:47       ` Nishanth Menon
  1 sibling, 0 replies; 13+ messages in thread
From: Tom Rini @ 2024-07-09 15:04 UTC (permalink / raw)
  To: Scaria Kochidanadu
  Cc: u-boot, sjg, mkorpershoek, abdellatif.elkhlifi, sumit.garg,
	neil.armstrong, caleb.connolly, semen.protsenko,
	marek.vasut+renesas, nathan.morrison, devarsht, j-luthra,
	vigneshr, praneeth, nm, khasim, rogerq, francesco.dolcini, afd,
	jan.kiszka

[-- Attachment #1: Type: text/plain, Size: 2744 bytes --]

On Tue, Jul 09, 2024 at 04:56:45PM +0530, Scaria Kochidanadu wrote:
> 
> 
> On 08/07/24 21:16, Tom Rini wrote:
> > On Mon, Jul 08, 2024 at 01:22:02PM +0530, Scaria Kochidanadu wrote:
> > 
> >> Add a UCLASS_SOUND driver for Texas Instruments SoCs which ties together
> >> the tlv320aic3106 audio codec and MCASP I2S controller. Enable audio
> >> playback functionality by taking a data pointer and data size as the
> >> sound data. The uboot sound play command takes time and frequency as
> >> input and creates the data for a beep sound with the given parameters,
> >> which is then passed to the sound play function.
> > [snip]
> >> +++ b/drivers/sound/ti_sound.c
> >> @@ -0,0 +1,119 @@
> >> +// SPDX-License-Identifier: GPL-2.0+
> >> +/*
> >> + * (C) Copyright 2024 Texas Instruments Incorporated - https://www.ti.com/
> >> + * Scaria M Kochidanadu, s-kochidanadu@ti.com
> >> + *
> >> + * based on the uboot samsung sound driver, which is
> >> + *
> >> + * Copyright 2018 Google, LLC
> >> + * Written by Simon Glass <sjg@chromium.org>
> >> + */
> >> +
> >> +#include <asm/u-boot.h> /* boot information for Linux kernel */
> >> +/* Pull in stuff for the build system */
> >> +#ifdef DO_DEPS_ONLY
> >> +#include <env_internal.h>
> >> +#endif
> >> +#include <audio_codec.h>
> >> +#include <dm.h>
> >> +#include <i2s.h>
> >> +#include <log.h>
> >> +#include <sound.h>
> >> +#include <asm/gpio.h>
> > 
> > Please audit this list. I really am confused by <env_internal.h>.
> > 
> 
> I will be removing this in v2 as it is not required.
> 
> >> +static int ti_sound_stop_play(struct udevice *dev)
> >> +{
> >> +	/* This function is necassary to satisfy the function calls
> >> +	 * in the Uboot command: sound play
> >> +	 */
> >> +	return 0;
> > 
> > /*
> >  * Like this...
> >  */
> > 
> > [snip]
> >> +static const struct udevice_id ti_sound_ids[] = {
> >> +	{ .compatible = "simple-audio-card" },
> >> +	{ }
> >> +};
> > 
> > Adding an actual simple-audio-card framework for U-Boot would be great.
> > This driver implies (and is?) TI-centric, instead. So lets rework this
> > to be split appropriately? Thanks.
> > 
> 
> I wanted to reuse the sound card DT node in the upstream kernel
> device-tree. The U-boot upstrem repo does not have the node as it is
> using an older version of DT.

When was the node upstreamed? Use the cherry-pick option to 
dts/update-dts-subtree.sh if needed.

> But if using the simple-audio-card node is not preferred, then can we
> create TI specific compatible and a separate DT node?

No, I would like to see the start of simple-audio-card support be worked
on, so that other platforms can leverage on top of this. Thanks.

-- 
Tom

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 659 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [EXTERNAL] Re: [PATCH 1/6] sound: ti: Add sound support for am625 board in Uboot
  2024-07-09 11:26     ` [EXTERNAL] " Scaria Kochidanadu
  2024-07-09 15:04       ` Tom Rini
@ 2024-07-17 11:47       ` Nishanth Menon
  1 sibling, 0 replies; 13+ messages in thread
From: Nishanth Menon @ 2024-07-17 11:47 UTC (permalink / raw)
  To: Scaria Kochidanadu
  Cc: Tom Rini, u-boot, sjg, mkorpershoek, abdellatif.elkhlifi,
	sumit.garg, neil.armstrong, caleb.connolly, semen.protsenko,
	marek.vasut+renesas, nathan.morrison, devarsht, j-luthra,
	vigneshr, praneeth, khasim, rogerq, francesco.dolcini, afd,
	jan.kiszka

On 16:56-20240709, Scaria Kochidanadu wrote:
> I wanted to reuse the sound card DT node in the upstream kernel
> device-tree. The U-boot upstrem repo does not have the node as it is
> using an older version of DT.

Please DONOT do this. migrate to OF_UPSTREAM is it is not done (and as I
recollect I have now spend close to an year getting u-boot TI dts to get
to kernel.org dts and OF_UPSTREAM should now be enabled on master for
am625), so what are you talking about? be specific what node is missing
- contribute upstream kernel to enable the node and then do the changes.


> But if using the simple-audio-card node is not preferred, then can we
> create TI specific compatible and a separate DT node?

-- 
Regards,
Nishanth Menon
Key (0xDDB5849D1736249D) / Fingerprint: F8A2 8693 54EB 8232 17A3  1A34 DDB5 849D 1736 249D

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2024-07-17 11:47 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-07-08  7:52 [PATCH 0/6] Enable audio playback in AM62x SoC Scaria Kochidanadu
2024-07-08  7:52 ` [PATCH 1/6] sound: ti: Add sound support for am625 board in Uboot Scaria Kochidanadu
2024-07-08 15:46   ` Tom Rini
2024-07-09 11:26     ` [EXTERNAL] " Scaria Kochidanadu
2024-07-09 15:04       ` Tom Rini
2024-07-17 11:47       ` Nishanth Menon
2024-07-08  7:52 ` [PATCH 2/6] sound: ti: Add TLV320AIC3106 Codec Scaria Kochidanadu
2024-07-08  7:52 ` [PATCH 3/6] sound: ti: Add MCASP driver for transfer of Audio data to sound codec Scaria Kochidanadu
2024-07-08  7:52 ` [PATCH 4/6] arm: dts: k3-am625-sk-u-boot.dtsi: Add sound driver nodes Scaria Kochidanadu
2024-07-08 15:48   ` Tom Rini
2024-07-08  7:52 ` [PATCH 5/6] configs: am62x_evm_a53_defconfig: Enable sound and I2C commands Scaria Kochidanadu
2024-07-08  7:52 ` [PATCH 6/6] linux: bitmap.h: Add for_each_set_bit_from() function Scaria Kochidanadu
2024-07-08 15:49   ` Tom Rini

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox