[PATCH v4 5/6] ASoC: sophgo: add CV1800B internal DAC codec driver

Anton D. Stavinskii posted 6 patches 2 weeks, 5 days ago
[PATCH v4 5/6] ASoC: sophgo: add CV1800B internal DAC codec driver
Posted by Anton D. Stavinskii 2 weeks, 5 days ago
Codec DAI endpoint for TXDAC. The codec does only a few things
- set up decimation
- enable codec and I2S output
- ensures the driver doesn't have dac overwrite enabled. (unmute the
  output)

Signed-off-by: Anton D. Stavinskii <stavinsky@gmail.com>
---
 sound/soc/sophgo/Kconfig             |  11 +-
 sound/soc/sophgo/Makefile            |   1 +
 sound/soc/sophgo/cv1800b-sound-dac.c | 204 +++++++++++++++++++++++++++++++++++
 3 files changed, 215 insertions(+), 1 deletion(-)

diff --git a/sound/soc/sophgo/Kconfig b/sound/soc/sophgo/Kconfig
index 12d1a57ea308..e4786f087589 100644
--- a/sound/soc/sophgo/Kconfig
+++ b/sound/soc/sophgo/Kconfig
@@ -28,10 +28,19 @@ config SND_SOC_CV1800B_ADC_CODEC
 	help
 	  This driver provides an ASoC codec DAI for capture and basic
 	  control of the RXADC registers.
-
 	  Say Y or M to build support for the Sophgo CV1800B
 	  internal analog ADC codec block (RXADC).
 	  The module will be called cv1800b-sound-adc
 
+config SND_SOC_CV1800B_DAC_CODEC
+	tristate "Sophgo CV1800B/SG2002 internal DAC codec"
+	depends on SND_SOC
+	help
+	  This driver provides an ASoC codec DAI for playback and basic
+	  control of the TXDAC registers.
+
+	  Say Y or M to build support for the Sophgo CV1800B
+	  internal analog DAC codec block (TXDAC).
+	  The module will be called cv1800b-sound-dac
 
 endmenu
diff --git a/sound/soc/sophgo/Makefile b/sound/soc/sophgo/Makefile
index c654d6059cbd..ec8dd31efddd 100644
--- a/sound/soc/sophgo/Makefile
+++ b/sound/soc/sophgo/Makefile
@@ -2,3 +2,4 @@
 # Sophgo Platform Support
 obj-$(CONFIG_SND_SOC_CV1800B_TDM) += cv1800b-tdm.o
 obj-$(CONFIG_SND_SOC_CV1800B_ADC_CODEC) += cv1800b-sound-adc.o
+obj-$(CONFIG_SND_SOC_CV1800B_DAC_CODEC) += cv1800b-sound-dac.o
diff --git a/sound/soc/sophgo/cv1800b-sound-dac.c b/sound/soc/sophgo/cv1800b-sound-dac.c
new file mode 100644
index 000000000000..ccf386174639
--- /dev/null
+++ b/sound/soc/sophgo/cv1800b-sound-dac.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Internal DAC codec for cv1800b based CPUs
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <sound/soc.h>
+#include <linux/io.h>
+
+#define CV1800B_TXDAC_CTRL0   0x00
+#define CV1800B_TXDAC_CTRL1   0x04
+#define CV1800B_TXDAC_STATUS  0x08
+#define CV1800B_TXDAC_AFE0    0x0c
+#define CV1800B_TXDAC_AFE1    0x10
+#define CV1800B_TXDAC_ANA0    0x20
+#define CV1800B_TXDAC_ANA1    0x24
+#define CV1800B_TXDAC_ANA2    0x28
+
+/* cv1800b_TXDAC_CTRL0 */
+#define REG_TXDAC_EN GENMASK(0, 0)
+#define REG_I2S_RX_EN GENMASK(1, 1)
+
+/* cv1800b_TXDAC_CTRL1 */
+#define REG_TXDAC_CIC_OPT GENMASK(1, 0)
+
+/* cv1800b_TXDAC_AFE0 */
+#define REG_TXDAC_INIT_DLY_CNT GENMASK(5, 0)
+
+/* cv1800b_TXDAC_ANA2 */
+#define TXDAC_OW_VAL_L_MASK GENMASK(7, 0)
+#define TXDAC_OW_VAL_R_MASK GENMASK(15, 8)
+#define TXDAC_OW_EN_L_MASK GENMASK(16, 16)
+#define TXDAC_OW_EN_R_MASK GENMASK(17, 17)
+
+struct cv1800b_priv {
+	void __iomem *regs;
+	struct device *dev;
+};
+
+enum decimation_values {
+	DECIMATION_64 = 0,
+	DECIMATION_128,
+	DECIMATION_256,
+	DECIMATION_512,
+};
+
+static void cv1800b_dac_enable(struct cv1800b_priv *priv, bool enable)
+{
+	u32 val;
+
+	val = readl(priv->regs + CV1800B_TXDAC_CTRL0);
+	val = u32_replace_bits(val, enable, REG_TXDAC_EN);
+	val = u32_replace_bits(val, enable, REG_I2S_RX_EN);
+	writel(val, priv->regs + CV1800B_TXDAC_CTRL0);
+}
+
+static void cv1800b_dac_mute(struct cv1800b_priv *priv, bool enable)
+{
+	u32 val;
+
+	val = readl(priv->regs + CV1800B_TXDAC_ANA2);
+	val = u32_replace_bits(val, enable, TXDAC_OW_EN_L_MASK);
+	val = u32_replace_bits(val, enable, TXDAC_OW_EN_R_MASK);
+	writel(val, priv->regs + CV1800B_TXDAC_ANA2);
+}
+
+static int cv1800b_dac_decimation(struct cv1800b_priv *priv, u8 dec)
+{
+	u32 val;
+
+	if (dec > 3)
+		return -EINVAL;
+
+	val = readl(priv->regs + CV1800B_TXDAC_CTRL1);
+	val = u32_replace_bits(val, dec, REG_TXDAC_CIC_OPT);
+	writel(val, priv->regs + CV1800B_TXDAC_CTRL1);
+	return 0;
+}
+
+static int cv1800b_dac_dly(struct cv1800b_priv *priv, u32 dly)
+{
+	u32 val;
+
+	if (dly > 63)
+		return -EINVAL;
+
+	val = readl(priv->regs + CV1800B_TXDAC_AFE0);
+	val = u32_replace_bits(val, dly, REG_TXDAC_INIT_DLY_CNT);
+	writel(val, priv->regs + CV1800B_TXDAC_AFE0);
+	return 0;
+}
+
+static int cv1800b_dac_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *params,
+				 struct snd_soc_dai *dai)
+{
+	struct cv1800b_priv *priv = snd_soc_dai_get_drvdata(dai);
+	int ret;
+	unsigned int rate = params_rate(params);
+
+	if (rate != 48000) {
+		dev_err(priv->dev, "rate %u is not supported\n", rate);
+		return -EINVAL;
+	}
+
+	cv1800b_dac_mute(priv, false);
+	/* minimal decimation for 48kHz is 64*/
+	ret = cv1800b_dac_decimation(priv, DECIMATION_64);
+	if (ret)
+		return ret;
+
+	/* value is taken from vendors driver 48kHz
+	 * tested on sg2000 and sg2002.
+	 */
+	ret = cv1800b_dac_dly(priv, 0x19);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int cv1800b_dac_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+				   struct snd_soc_dai *dai)
+{
+	struct cv1800b_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		cv1800b_dac_enable(priv, true);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		cv1800b_dac_enable(priv, false);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops cv1800b_dac_dai_ops = {
+	.hw_params = cv1800b_dac_hw_params,
+	.trigger = cv1800b_dac_dai_trigger,
+};
+
+static struct snd_soc_dai_driver cv1800b_dac_dai = {
+	.name = "dac-hifi",
+	.playback = { .stream_name = "DAC Playback",
+		      .channels_min = 2,
+		      .channels_max = 2,
+		      .rates = SNDRV_PCM_RATE_48000,
+		      .formats = SNDRV_PCM_FMTBIT_S16_LE },
+	.ops = &cv1800b_dac_dai_ops,
+};
+
+static const struct snd_soc_component_driver cv1800b_dac_component = {
+	.name = "cv1800b-dac-codec",
+};
+
+static int cv1800b_dac_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct cv1800b_priv *priv;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dev = dev;
+	priv->regs = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(priv->regs))
+		return PTR_ERR(priv->regs);
+
+	platform_set_drvdata(pdev, priv);
+	return devm_snd_soc_register_component(&pdev->dev,
+					       &cv1800b_dac_component,
+					       &cv1800b_dac_dai, 1);
+}
+
+static const struct of_device_id cv1800b_dac_of_match[] = {
+	{ .compatible = "sophgo,cv1800b-sound-dac" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, cv1800b_dac_of_match);
+
+static struct platform_driver cv1800b_dac_driver = {
+	.probe = cv1800b_dac_probe,
+	.driver = {
+		.name = "cv1800b-dac-codec",
+		.of_match_table = cv1800b_dac_of_match,
+	},
+};
+module_platform_driver(cv1800b_dac_driver);
+
+MODULE_DESCRIPTION("DAC codec for CV1800B");
+MODULE_AUTHOR("Anton D. Stavinskii <stavinsky@gmail.com>");
+MODULE_LICENSE("GPL");

-- 
2.43.0
Re: [PATCH v4 5/6] ASoC: sophgo: add CV1800B internal DAC codec driver
Posted by Mark Brown 1 week, 6 days ago
On Tue, Jan 20, 2026 at 11:06:07PM +0400, Anton D. Stavinskii wrote:

>  	help
>  	  This driver provides an ASoC codec DAI for capture and basic
>  	  control of the RXADC registers.
> -
>  	  Say Y or M to build support for the Sophgo CV1800B
>  	  internal analog ADC codec block (RXADC).
>  	  The module will be called cv1800b-sound-adc

Extra change here.

> +static int cv1800b_dac_hw_params(struct snd_pcm_substream *substream,
> +				 struct snd_pcm_hw_params *params,
> +				 struct snd_soc_dai *dai)
> +{
> +	struct cv1800b_priv *priv = snd_soc_dai_get_drvdata(dai);
> +	int ret;
> +	unsigned int rate = params_rate(params);

> +	cv1800b_dac_mute(priv, false);
> +	/* minimal decimation for 48kHz is 64*/

Nothing ever mutes the DAC so this is a bit redundant.  The mute should
probably be a mute_stream() operation.
Re: [PATCH v4 5/6] ASoC: sophgo: add CV1800B internal DAC codec driver
Posted by Anton D. Stavinskii 1 week, 6 days ago
On Tue, Jan 27, 2026 at 12:49:52PM +0400, Mark Brown wrote:
> On Tue, Jan 20, 2026 at 11:06:07PM +0400, Anton D. Stavinskii wrote:
> 
> >  	help
> >  	  This driver provides an ASoC codec DAI for capture and basic
> >  	  control of the RXADC registers.
> > -
> >  	  Say Y or M to build support for the Sophgo CV1800B
> >  	  internal analog ADC codec block (RXADC).
> >  	  The module will be called cv1800b-sound-adc
> 
> Extra change here.
Will fix, thanks. 
> 
> > +static int cv1800b_dac_hw_params(struct snd_pcm_substream *substream,
> > +				 struct snd_pcm_hw_params *params,
> > +				 struct snd_soc_dai *dai)
> > +{
> > +	struct cv1800b_priv *priv = snd_soc_dai_get_drvdata(dai);
> > +	int ret;
> > +	unsigned int rate = params_rate(params);
> 
> > +	cv1800b_dac_mute(priv, false);
> > +	/* minimal decimation for 48kHz is 64*/
> 
> Nothing ever mutes the DAC so this is a bit redundant.  The mute should
> probably be a mute_stream() operation.

I'm not sure here. DAC mute feature was not implemented because I don't know how
exactly do that. The public documentation is very weak for my taste. 
This call added here to be sure that override flag is
not set (override feature replaces the output by setting it to constant 
value and ignoring the input, so it is some kind of mute from my understanding. 
So ensuring this off is needed be sure that DAC will output our I2S data). 
What do you think will be better to do here? 
I'm sure that is needed, but may be better to move 
it to some early stages, like probe function.
Re: [PATCH v4 5/6] ASoC: sophgo: add CV1800B internal DAC codec driver
Posted by Mark Brown 1 week, 6 days ago
On Tue, Jan 27, 2026 at 07:11:24PM +0400, Anton D. Stavinskii wrote:
> On Tue, Jan 27, 2026 at 12:49:52PM +0400, Mark Brown wrote:

> > Nothing ever mutes the DAC so this is a bit redundant.  The mute should
> > probably be a mute_stream() operation.

> I'm not sure here. DAC mute feature was not implemented because I don't know how
> exactly do that. The public documentation is very weak for my taste. 
> This call added here to be sure that override flag is
> not set (override feature replaces the output by setting it to constant 
> value and ignoring the input, so it is some kind of mute from my understanding. 
> So ensuring this off is needed be sure that DAC will output our I2S data). 
> What do you think will be better to do here? 
> I'm sure that is needed, but may be better to move 
> it to some early stages, like probe function. 

It's probably fine to leave the functional code as is but you should add
some comments to the mute function explaining waht's going on here.
Re: [PATCH v4 5/6] ASoC: sophgo: add CV1800B internal DAC codec driver
Posted by Anton D. Stavinskii 1 week, 6 days ago
On Tue, Jan 27, 2026 at 03:13:59PM +0400, Mark Brown wrote:
> On Tue, Jan 27, 2026 at 07:11:24PM +0400, Anton D. Stavinskii wrote:
> > On Tue, Jan 27, 2026 at 12:49:52PM +0400, Mark Brown wrote:
> 
> > > Nothing ever mutes the DAC so this is a bit redundant.  The mute should
> > > probably be a mute_stream() operation.
> 
> > I'm not sure here. DAC mute feature was not implemented because I don't know how
> > exactly do that. The public documentation is very weak for my taste. 
> > This call added here to be sure that override flag is
> > not set (override feature replaces the output by setting it to constant 
> > value and ignoring the input, so it is some kind of mute from my understanding. 
> > So ensuring this off is needed be sure that DAC will output our I2S data). 
> > What do you think will be better to do here? 
> > I'm sure that is needed, but may be better to move 
> > it to some early stages, like probe function. 
> 
> It's probably fine to leave the functional code as is but you should add
> some comments to the mute function explaining waht's going on here.

Sounds good. Will add comments and prepare v5. 
Thanks a lot!


> _______________________________________________
> linux-riscv mailing list
> linux-riscv@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-riscv
Re: [PATCH v4 5/6] ASoC: sophgo: add CV1800B internal DAC codec driver
Posted by Mark Brown 1 week, 6 days ago
On Tue, Jan 27, 2026 at 07:30:27PM +0400, Anton D. Stavinskii wrote:
> On Tue, Jan 27, 2026 at 03:13:59PM +0400, Mark Brown wrote:

> > It's probably fine to leave the functional code as is but you should add
> > some comments to the mute function explaining waht's going on here.

> Sounds good. Will add comments and prepare v5. 

Like I said in reply to the cover letter please send an incremental
change.