From 610d8a8b961d97ff850765299b75459f05a9c2cd Mon Sep 17 00:00:00 2001
From: Victor Chen <victor.chen@lightelligence.ai>
Date: Fri, 24 May 2024 19:05:23 +0800
Subject: [PATCH 1/2] Resolve #38-driver-support-for-spi-dac

---
 apps/demo/boards/nucleo_g071rb.conf           |   2 +-
 apps/demo/boards/nucleo_g071rb.overlay        |  20 +-
 core/DAC.cpp                                  |  37 ++--
 drivers/dac/CMakeLists.txt                    |   2 +-
 drivers/dac/Kconfig                           |   4 +-
 drivers/dac/dac_soc_stm32.c                   |   2 +-
 drivers/dac/dac_spi.c                         | 197 ++++++++++++++++++
 drivers/dac/dac_spi_sc.c                      | 171 ---------------
 ...sis,dac-spi-sc.yaml => oasis,dac-spi.yaml} |  20 +-
 include/zoysia/core/DAC.h                     |   6 +-
 include/zoysia/drivers/dac_spi.h              |  33 +++
 include/zoysia/drivers/dac_spi_sc.h           |  40 ----
 12 files changed, 283 insertions(+), 251 deletions(-)
 create mode 100644 drivers/dac/dac_spi.c
 delete mode 100644 drivers/dac/dac_spi_sc.c
 rename dts/bindings/{oasis,dac-spi-sc.yaml => oasis,dac-spi.yaml} (63%)
 create mode 100644 include/zoysia/drivers/dac_spi.h
 delete mode 100644 include/zoysia/drivers/dac_spi_sc.h

diff --git a/apps/demo/boards/nucleo_g071rb.conf b/apps/demo/boards/nucleo_g071rb.conf
index c10f749..4311000 100644
--- a/apps/demo/boards/nucleo_g071rb.conf
+++ b/apps/demo/boards/nucleo_g071rb.conf
@@ -23,7 +23,7 @@ CONFIG_SPI=y
 CONFIG_SPI_NOR=y
 CONFIG_ADC=y
 CONFIG_ADC_STM32=y
-CONFIG_DAC_SPI_SC=y
+CONFIG_DAC_SPI=y
 CONFIG_FISR_TIMER_STM32=y
 
 # Core dump
diff --git a/apps/demo/boards/nucleo_g071rb.overlay b/apps/demo/boards/nucleo_g071rb.overlay
index ad301a9..3ce96da 100644
--- a/apps/demo/boards/nucleo_g071rb.overlay
+++ b/apps/demo/boards/nucleo_g071rb.overlay
@@ -242,32 +242,38 @@
 
 	cs-gpios = <&gpioa 1 GPIO_ACTIVE_LOW>,<&gpioa 4 GPIO_ACTIVE_LOW>,<&gpioa 8 GPIO_ACTIVE_LOW>;
 
-	dac_spi_sc_1: dac_spi_sc@0 {
-		compatible = "oasis,dac-spi-sc";
+	dac_spi_1: dac_spi@0 {
+		compatible = "oasis,dac-spi";
 		reg = <0>;
 		spi-max-frequency = <10000000>;
 		status = "okay";
 		cs = <0>;
 		nbits = <12>;
-		shift = <2>;
+		command-nbits = <2>;
+		chan-commands = <0>;
+		frame-shift = <2>;
 	};
 
-	dac_spi_sc_2: dac_spi_sc@1 {
-		compatible = "oasis,dac-spi-sc";
+	dac_spi_2: dac_spi@1 {
+		compatible = "oasis,dac-spi";
 		reg = <1>;
 		spi-max-frequency = <10000000>;
 		status = "okay";
 		cs = <1>;
 		nbits = <14>;
+		command-nbits = <2>;
+		chan-commands = <0>;
 	};
 
-	dac_spi_sc_3: dac_spi_sc@2 {
-		compatible = "oasis,dac-spi-sc";
+	dac_spi_3: dac_spi@2 {
+		compatible = "oasis,dac-spi";
 		reg = <2>;
 		spi-max-frequency = <10000000>;
 		status = "okay";
 		cs = <2>;
 		nbits = <16>;
+		command-nbits = <2>;
+		chan-commands = <0>;
 		twos-complement;
 	};
 };
diff --git a/core/DAC.cpp b/core/DAC.cpp
index e3f1a20..0082c45 100644
--- a/core/DAC.cpp
+++ b/core/DAC.cpp
@@ -78,6 +78,11 @@ int DAC_::ChannelIndex(int chan) const
 	return 0;
 }
 
+/*virtual*/ int DAC_::RefVoltagemV() const
+{
+	return 0;
+}
+
 /*virtual*/ int DAC_::ReadValue(int idx) const
 {
 	return INT_MIN;
@@ -119,8 +124,8 @@ int DAC_::ChannelIndex(int chan) const
 #ifdef CONFIG_DAC_PWM
 	DAC_PWM::Init();
 #endif
-#ifdef CONFIG_DAC_SPI_SC
-	DAC_SPI_SC::Init();
+#ifdef CONFIG_DAC_SPI
+	DAC_SPI::Init();
 #endif
 
 	for(const InitEntry *e = ms_initList; e != 0; e = e->next)
@@ -285,32 +290,32 @@ int DAC_::ChannelIndex(int chan) const
 
 #endif
 
-#ifdef CONFIG_DAC_SPI_SC
+#ifdef CONFIG_DAC_SPI
 
-#include "zoysia/drivers/dac_spi_sc.h"
+#include "zoysia/drivers/dac_spi.h"
 
-/*static*/ void DAC_SPI_SC::Init(void)
+/*static*/ void DAC_SPI::Init(void)
 {
-	for (const struct device **pdev = devlist_dac_spi_sc; *pdev != 0; pdev++)
+	for (const struct device **pdev = devlist_dac_spi; *pdev != 0; pdev++)
 	{
-		const struct dac_spi_sc_config *config = reinterpret_cast<const struct dac_spi_sc_config*>((*pdev)->config);
-		const int dac_spi_sc_cs = config->cs;
-		const int dac_spi_sc_nbits = config->nbits;
-		const int dac_spi_sc_nchan = config->nchan;
-		if (dac_spi_sc_cs < CONFIG_DAC_MAX && dac_spi_sc_nchan > 0)
-			new DAC_SPI_SC(dac_spi_sc_cs, dac_spi_sc_nbits, dac_spi_sc_nchan, *pdev);
+		const struct dac_spi_config *config = reinterpret_cast<const struct dac_spi_config*>((*pdev)->config);
+		const int dac_spi_cs = config->cs;
+		const int dac_spi_nbits = config->nbits;
+		const int dac_spi_nchan = config->nchan;
+		if (dac_spi_cs < CONFIG_DAC_MAX && dac_spi_nchan > 0)
+			new DAC_SPI(dac_spi_cs, dac_spi_nbits, dac_spi_nchan, *pdev);
 	}
 }
 
-/*virtual*/ int DAC_SPI_SC::Command(int cmd, int value)
+/*virtual*/ int DAC_SPI::Command(int cmd, int value)
 {
-	return dac_spi_sc_command(reinterpret_cast<const struct device*>(m_dev), cmd, value);
+	return dac_spi_command(reinterpret_cast<const struct device*>(m_dev), cmd, value);
 }
 
-/*virtual*/ bool DAC_SPI_SC::IsTwosComplement()
+/*virtual*/ bool DAC_SPI::IsTwosComplement()
 {
 	const struct device *dev = reinterpret_cast<const struct device*>(m_dev);
-	const struct dac_spi_sc_config *config = reinterpret_cast<const struct dac_spi_sc_config*>(dev->config);
+	const struct dac_spi_config *config = reinterpret_cast<const struct dac_spi_config*>(dev->config);
 	return config->twos_complement != 0;
 }
 
diff --git a/drivers/dac/CMakeLists.txt b/drivers/dac/CMakeLists.txt
index 77d61c2..8d61892 100644
--- a/drivers/dac/CMakeLists.txt
+++ b/drivers/dac/CMakeLists.txt
@@ -5,4 +5,4 @@ zephyr_library_sources_ifdef(CONFIG_DAC_SOC dac_soc.c)
 zephyr_library_sources_ifdef(CONFIG_DAC_SOC_STM32 dac_soc_stm32.c)
 zephyr_library_sources_ifdef(CONFIG_DAC_PWM dac_pwm.c)
 zephyr_library_sources_ifdef(CONFIG_DAC_PWM_STM32 dac_pwm_stm32.c)
-zephyr_library_sources_ifdef(CONFIG_DAC_SPI_SC dac_spi_sc.c)
+zephyr_library_sources_ifdef(CONFIG_DAC_SPI dac_spi.c)
diff --git a/drivers/dac/Kconfig b/drivers/dac/Kconfig
index 63c3e4d..68d0d30 100644
--- a/drivers/dac/Kconfig
+++ b/drivers/dac/Kconfig
@@ -50,8 +50,8 @@ config DAC_SPI_INIT_PRIORITY
 	help
 	  SPI DAC driver device initialization priority.
 
-config DAC_SPI_SC
-	bool "Single channel SPI DAC driver"
+config DAC_SPI
+	bool "SPI DAC driver"
 	select SPI
 	select DAC
 	help
diff --git a/drivers/dac/dac_soc_stm32.c b/drivers/dac/dac_soc_stm32.c
index 3eda9f0..2e9a2a8 100644
--- a/drivers/dac/dac_soc_stm32.c
+++ b/drivers/dac/dac_soc_stm32.c
@@ -165,7 +165,7 @@ int dac_soc_ref_internal(const struct device *dev)
 	static const int dac_stm32_chan_##inst[] = { \
 		LISTIFY(DT_INST_PROP_LEN(inst, channels), DT_INST_DEV_CONFIG_GET, (,), inst) \
 	}; \
-	static const int dac_stm32_nchan_##inst = sizeof(dac_stm32_chan_##inst) / sizeof(uint32_t); \
+	static const int dac_stm32_nchan_##inst = sizeof(dac_stm32_chan_##inst) / sizeof(int); \
 	static const struct dac_stm32_cfg dac_stm32_cfg_##inst = { \
 		.config = { \
 			.cs = DT_INST_PROP(inst, cs), \
diff --git a/drivers/dac/dac_spi.c b/drivers/dac/dac_spi.c
new file mode 100644
index 0000000..66ec7eb
--- /dev/null
+++ b/drivers/dac/dac_spi.c
@@ -0,0 +1,197 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT oasis_dac_spi
+
+#include <zephyr/drivers/dac.h>
+#include <zephyr/drivers/spi.h>
+#include <zephyr/device.h>
+#include <zephyr/kernel.h>
+#include <zephyr/init.h>
+
+#include "zoysia/drivers/dac_spi.h"
+
+struct dac_spi_cfg {
+	const struct dac_spi_config config;
+	const struct spi_dt_spec spi;
+};
+
+struct dac_spi_data {
+	struct k_sem sem;
+	int command;
+	int value;
+};
+
+static void acquire_device(const struct device *dev)
+{
+	if (IS_ENABLED(CONFIG_MULTITHREADING))
+	{
+		struct dac_spi_data *const data = dev->data;
+		k_sem_take(&data->sem, K_FOREVER);
+	}
+}
+
+static void release_device(const struct device *dev)
+{
+	if (IS_ENABLED(CONFIG_MULTITHREADING))
+	{
+		struct dac_spi_data *const data = dev->data;
+		k_sem_give(&data->sem);
+	}
+}
+
+static int dac_spi_output(const struct device *dev)
+{
+	const struct dac_spi_cfg *cfg = dev->config;
+	struct dac_spi_data *const data = dev->data;
+
+	const int nbits = cfg->config.nbits;
+	const int frame_shift = cfg->config.frame_shift;
+	const int command_nbits = cfg->config.command_nbits;
+	const int total_nbits = nbits + frame_shift + command_nbits;
+
+	int output = ((data->command << nbits) | data->value) << frame_shift;
+	int length = 3;
+
+	if (total_nbits <= 16)
+	{
+		output <<= 8;
+		length = 2;
+	}
+
+	uint8_t buffer[3] = {
+		(output >> 16) & 0xFF,
+		(output >> 8) & 0xFF,
+		(output & 0xFF)
+	};
+
+	struct spi_buf spi_buf[1] = {
+		{
+			.buf = buffer,
+			.len = length
+		}
+	};
+	const struct spi_buf_set tx_set = {
+		.buffers = spi_buf,
+		.count = 1,
+	};
+
+	int rc = spi_write_dt(&cfg->spi, &tx_set);
+
+	return rc;
+}
+
+static int dac_spi_write_value(const struct device *dev, uint8_t channel, uint32_t value)
+{
+	const struct dac_spi_cfg *cfg = dev->config;
+	struct dac_spi_data *const data = dev->data;
+
+	int rc = -EIO;
+
+	if (channel > 0 && channel <= cfg->config.nchan)
+	{
+		const int nbits = cfg->config.nbits;
+		acquire_device(dev);
+		data->command = cfg->config.chan_commands[channel-1];
+		data->value = (int)(value & ((1 << nbits) - 1));
+		rc = dac_spi_output(dev);
+		release_device(dev);
+	}
+
+	return rc;
+}
+
+static int dac_spi_init(const struct device *dev)
+{
+	const struct dac_spi_cfg *cfg = dev->config;
+	struct dac_spi_data *const data = dev->data;
+
+	data->command = 0;
+	data->value = 0;
+	if (IS_ENABLED(CONFIG_MULTITHREADING))
+		k_sem_init(&data->sem, 1, K_SEM_MAX_LIMIT);
+
+	if (!spi_is_ready_dt(&cfg->spi))
+		return -ENODEV;
+
+	int pkt;
+	const int command_mask = (1 << cfg->config.command_nbits) - 1;
+	const int value_mask = (1 << cfg->config.nbits) - 1;
+	const int *cmd = cfg->config.init_commands;
+	while ((pkt = *cmd++) != 0)
+	{
+		data->command = (pkt >> (cfg->config.frame_shift + cfg->config.nbits)) & command_mask;
+		data->value = (pkt >> cfg->config.frame_shift) & value_mask;
+		int rc = dac_spi_output(dev);
+		if (rc != 0)
+			return rc;
+	}
+
+	return 0;
+}
+
+static const struct dac_driver_api dac_spi_driver_api = {
+	.write_value = dac_spi_write_value,
+};
+
+int dac_spi_command(const struct device *dev, int cmd, int value)
+{
+	const struct dac_spi_cfg *cfg = dev->config;
+	struct dac_spi_data *const data = dev->data;
+
+	acquire_device(dev);
+
+	int rc = EINVAL;
+	const int command_mask = (1 << cfg->config.command_nbits) - 1;
+	if ((cmd & ~command_mask) == 0)
+	{
+		data->command = (int)(cmd & command_mask);
+		const int value_mask = (1 << cfg->config.nbits) - 1;
+		if ((value & ~value_mask) == 0)
+			data->value = (int)(value & value_mask);
+		rc = dac_spi_output(dev);
+	}
+
+	release_device(dev);
+
+	return rc;
+}
+
+#define DT_INST_DEV_CONFIG_INIT_CMD_GET(idx, inst) DT_INST_PROP_BY_IDX(inst, init_commands, idx)
+#define DT_INST_DEV_CONFIG_CHAN_CMD_GET(idx, inst) DT_INST_PROP_BY_IDX(inst, chan_commands, idx)
+
+#define DAC_DEVICE_ITEM(inst) DEVICE_DT_GET(DT_DRV_INST(inst)),
+#define DAC_DEVICE_INIT(inst) \
+	static const int dac_spi_init_cmd_##inst[] = { \
+		LISTIFY(DT_INST_PROP_LEN(inst, init_commands), DT_INST_DEV_CONFIG_INIT_CMD_GET, (,), inst) \
+	}; \
+	static const int dac_spi_chan_cmd_##inst[] = { \
+		LISTIFY(DT_INST_PROP_LEN(inst, chan_commands), DT_INST_DEV_CONFIG_CHAN_CMD_GET, (,), inst) \
+	}; \
+	static const int dac_spi_nchan_cmd_##inst = sizeof(dac_spi_chan_cmd_##inst) / sizeof(int); \
+	static const struct dac_spi_cfg dac_spi_cfg_##inst = { \
+		.config = { \
+			.cs = DT_INST_PROP(inst, cs), \
+			.nbits = DT_INST_PROP(inst, nbits), \
+			.nchan = dac_spi_nchan_cmd_##inst, \
+			.init_commands = dac_spi_init_cmd_##inst, \
+			.chan_commands = dac_spi_chan_cmd_##inst, \
+			.command_nbits = DT_INST_PROP(inst, command_nbits), \
+			.frame_shift = DT_INST_PROP(inst, frame_shift), \
+			.twos_complement = DT_INST_PROP_OR(inst, twos_complement, 0), \
+		}, \
+		.spi = SPI_DT_SPEC_INST_GET(inst, SPI_MODE_CPHA | SPI_WORD_SET(8), 0), \
+	}; \
+	static struct dac_spi_data dac_spi_data_##inst; \
+	DEVICE_DT_INST_DEFINE(inst, \
+		&dac_spi_init, \
+		NULL, \
+		&dac_spi_data_##inst, \
+		&dac_spi_cfg_##inst, POST_KERNEL, \
+		CONFIG_DAC_SPI_INIT_PRIORITY, \
+		&dac_spi_driver_api); \
+
+DT_INST_FOREACH_STATUS_OKAY(DAC_DEVICE_INIT)
+
+const struct device* devlist_dac_spi[] = { DT_INST_FOREACH_STATUS_OKAY(DAC_DEVICE_ITEM) (0) };
diff --git a/drivers/dac/dac_spi_sc.c b/drivers/dac/dac_spi_sc.c
deleted file mode 100644
index 8b585b9..0000000
--- a/drivers/dac/dac_spi_sc.c
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * SPDX-License-Identifier: Apache-2.0
- */
-
-#define DT_DRV_COMPAT oasis_dac_spi_sc
-
-#include <zephyr/drivers/dac.h>
-#include <zephyr/drivers/spi.h>
-#include <zephyr/device.h>
-#include <zephyr/kernel.h>
-#include <zephyr/init.h>
-
-#include "zoysia/drivers/dac_spi_sc.h"
-
-struct dac_spi_sc_cfg {
-	const struct dac_spi_sc_config config;
-	const struct spi_dt_spec spi;
-};
-
-struct dac_spi_sc_data {
-	struct k_sem sem;
-	int command;
-	int value;
-};
-
-static void acquire_device(const struct device *dev)
-{
-	if (IS_ENABLED(CONFIG_MULTITHREADING))
-	{
-		struct dac_spi_sc_data *const data = dev->data;
-		k_sem_take(&data->sem, K_FOREVER);
-	}
-}
-
-static void release_device(const struct device *dev)
-{
-	if (IS_ENABLED(CONFIG_MULTITHREADING))
-	{
-		struct dac_spi_sc_data *const data = dev->data;
-		k_sem_give(&data->sem);
-	}
-}
-
-static int dac_spi_sc_output(const struct device *dev)
-{
-	const struct dac_spi_sc_cfg *cfg = dev->config;
-	struct dac_spi_sc_data *const data = dev->data;
-
-	const int nbits = cfg->config.nbits;
-	const int shift = cfg->config.shift;
-	const int command_nbits = cfg->config.command_nbits;
-	const int total_nbits = nbits + shift + command_nbits;
-
-	int output = ((data->command << nbits) | data->value) << shift;
-	int length = 3;
-
-	if (total_nbits <= 16)
-	{
-		output <<= 8;
-		length = 2;
-	}
-
-	uint8_t buffer[3] = {
-		(output >> 16) & 0xFF,
-		(output >> 8) & 0xFF,
-		(output & 0xFF)
-	};
-
-	struct spi_buf spi_buf[1] = {
-		{
-			.buf = buffer,
-			.len = length
-		}
-	};
-	const struct spi_buf_set tx_set = {
-		.buffers = spi_buf,
-		.count = 1,
-	};
-
-	int rc = spi_write_dt(&cfg->spi, &tx_set);
-
-	return rc;
-}
-
-static int dac_spi_sc_write_value(const struct device *dev, uint8_t channel, uint32_t value)
-{
-	const struct dac_spi_sc_cfg *cfg = dev->config;
-	struct dac_spi_sc_data *const data = dev->data;
-
-	const int nbits = cfg->config.nbits;
-
-	acquire_device(dev);
-
-	data->command = cfg->config.write_command;
-	data->value = (int)(value & ((1 << nbits) - 1));
-	int rc = dac_spi_sc_output(dev);
-
-	release_device(dev);
-
-	return rc;
-}
-
-static int dac_spi_sc_init(const struct device *dev)
-{
-	const struct dac_spi_sc_cfg *cfg = dev->config;
-	struct dac_spi_sc_data *const data = dev->data;
-
-	data->command = 0;
-	data->value = 0;
-	if (IS_ENABLED(CONFIG_MULTITHREADING))
-		k_sem_init(&data->sem, 1, K_SEM_MAX_LIMIT);
-
-	if (!spi_is_ready_dt(&cfg->spi))
-		return -ENODEV;
-
-	return 0;
-}
-
-static const struct dac_driver_api dac_spi_sc_driver_api = {
-	.write_value = dac_spi_sc_write_value,
-};
-
-int dac_spi_sc_command(const struct device *dev, int cmd, int value)
-{
-	const struct dac_spi_sc_cfg *cfg = dev->config;
-	struct dac_spi_sc_data *const data = dev->data;
-
-	acquire_device(dev);
-
-	int rc = EINVAL;
-	int command_mask = (1 << cfg->config.command_nbits) - 1;
-	if ((cmd & ~command_mask) == 0)
-	{
-		data->command = (int)(cmd & command_mask);
-		int value_mask = (1 << cfg->config.nbits) - 1;
-		if ((value & ~value_mask) == 0)
-			data->value = (int)(value & value_mask);
-		rc = dac_spi_sc_output(dev);
-	}
-
-	release_device(dev);
-
-	return rc;
-}
-
-#define DAC_DEVICE_ITEM(inst) DEVICE_DT_GET(DT_DRV_INST(inst)),
-#define DAC_DEVICE_INIT(inst) \
-	static const struct dac_spi_sc_cfg dac_spi_sc_cfg_##inst = { \
-		.config = { \
-			.cs = DT_INST_PROP(inst, cs), \
-			.nbits = DT_INST_PROP(inst, nbits), \
-			.nchan = 1, \
-			.shift = DT_INST_PROP(inst, shift), \
-			.command_nbits = DT_INST_PROP(inst, command_nbits), \
-			.write_command = DT_INST_PROP(inst, write_command), \
-			.twos_complement = DT_INST_PROP_OR(inst, twos_complement, 0), \
-		}, \
-		.spi = SPI_DT_SPEC_INST_GET(inst, SPI_MODE_CPHA | SPI_WORD_SET(8), 0), \
-	}; \
-	static struct dac_spi_sc_data dac_spi_sc_data_##inst; \
-	DEVICE_DT_INST_DEFINE(inst, \
-		&dac_spi_sc_init, \
-		NULL, \
-		&dac_spi_sc_data_##inst, \
-		&dac_spi_sc_cfg_##inst, POST_KERNEL, \
-		CONFIG_DAC_SPI_INIT_PRIORITY, \
-		&dac_spi_sc_driver_api); \
-
-DT_INST_FOREACH_STATUS_OKAY(DAC_DEVICE_INIT)
-
-const struct device* devlist_dac_spi_sc[] = { DT_INST_FOREACH_STATUS_OKAY(DAC_DEVICE_ITEM) (0) };
diff --git a/dts/bindings/oasis,dac-spi-sc.yaml b/dts/bindings/oasis,dac-spi.yaml
similarity index 63%
rename from dts/bindings/oasis,dac-spi-sc.yaml
rename to dts/bindings/oasis,dac-spi.yaml
index 23a32fb..adf173d 100644
--- a/dts/bindings/oasis,dac-spi-sc.yaml
+++ b/dts/bindings/oasis,dac-spi.yaml
@@ -1,8 +1,8 @@
 # SPDX-License-Identifier: Apache-2.0
 
-description: Single channel SPI DAC node
+description: SPI DAC node
 
-compatible: "oasis,dac-spi-sc"
+compatible: "oasis,dac-spi"
 
 include: [spi-device.yaml, pinctrl-device.yaml]
 
@@ -15,21 +15,23 @@ properties:
       required: true
       type: int
 
-    shift:
+    init-commands:
       required: false
-      type: int
-      default: 0
+      type: array
+      default: [0]
+
+    chan-commands:
+      required: true
+      type: array
 
     command-nbits:
-      required: false
+      required: true
       type: int
-      default: 2
 
-    write-command:
+    frame-shift:
       required: false
       type: int
       default: 0
 
     twos-complement:
-      required: false
       type: boolean
diff --git a/include/zoysia/core/DAC.h b/include/zoysia/core/DAC.h
index ce6e403..bde0d07 100644
--- a/include/zoysia/core/DAC.h
+++ b/include/zoysia/core/DAC.h
@@ -103,15 +103,15 @@ private:
 };
 #endif
 
-#ifdef CONFIG_DAC_SPI_SC
-class DAC_SPI_SC : public DAC_
+#ifdef CONFIG_DAC_SPI
+class DAC_SPI : public DAC_
 {
 public:
     static void Init(void);
 	virtual int Command(int cmd, int value);
 	virtual bool IsTwosComplement();
 private:
-	DAC_SPI_SC(int cs, int nbits, int nchan, const void *dev) :
+	DAC_SPI(int cs, int nbits, int nchan, const void *dev) :
 		DAC_(cs, nbits, nchan, 0, dev) { }
 };
 #endif
diff --git a/include/zoysia/drivers/dac_spi.h b/include/zoysia/drivers/dac_spi.h
new file mode 100644
index 0000000..eff399b
--- /dev/null
+++ b/include/zoysia/drivers/dac_spi.h
@@ -0,0 +1,33 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef ZOYSIA_DRIVERS_DAC_SPI_H
+#define ZOYSIA_DRIVERS_DAC_SPI_H 1
+
+#include <zephyr/device.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct dac_spi_config {
+	const int cs;
+	const int nbits;
+    const int nchan;
+	const int command_nbits;
+	const int *init_commands;
+	const int *chan_commands;
+	const int frame_shift;
+	const int twos_complement;
+};
+
+int dac_spi_command(const struct device*, int, int);
+
+extern const struct device* devlist_dac_spi[];
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/zoysia/drivers/dac_spi_sc.h b/include/zoysia/drivers/dac_spi_sc.h
deleted file mode 100644
index b3882e9..0000000
--- a/include/zoysia/drivers/dac_spi_sc.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * SPDX-License-Identifier: Apache-2.0
- */
-
-#ifndef ZOYSIA_DRIVERS_DAC_SPI_SC_H
-#define ZOYSIA_DRIVERS_DAC_SPI_SC_H 1
-
-#include <zephyr/device.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct dac_spi_sc_config {
-	const int cs;
-	const int nbits;
-    const int nchan;
-	const int shift;
-	const int command_nbits;
-	const int write_command;
-	const int twos_complement;
-};
-
-enum
-{
-	DAC_SPI_SC_NORMAL = 0,
-	DAC_SPI_SC_PD1K   = 1,
-	DAC_SPI_SC_PD100K = 2,
-	DAC_SPI_SC_PDHIZ  = 3,	
-};
-
-int dac_spi_sc_command(const struct device*, int, int);
-
-extern const struct device* devlist_dac_spi_sc[];
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-- 
GitLab


From cbc4b4633a02e043c5adade9f967c8625d4bc4d7 Mon Sep 17 00:00:00 2001
From: Victor Chen <victor.chen@lightelligence.ai>
Date: Mon, 27 May 2024 00:38:41 +0800
Subject: [PATCH 2/2] Resolve #38-driver-support-for-spi-dac

---
 apps/demo/boards/nucleo_g071rb.overlay |   2 +-
 core/DAC.cpp                           | 206 +++++++++--------
 drivers/dac/dac_pwm_stm32.c            |   3 +
 drivers/dac/dac_soc.c                  |   7 +-
 drivers/dac/dac_soc_stm32.c            |   2 +-
 drivers/dac/dac_spi.c                  | 301 +++++++++++++++++--------
 dts/bindings/oasis,dac-pwm-stm32.yaml  |   4 +-
 dts/bindings/oasis,dac-spi.yaml        |  37 ++-
 include/zoysia/core/DAC.h              |  60 ++---
 include/zoysia/drivers/dac_mmap.h      |  26 ---
 include/zoysia/drivers/dac_pwm.h       |   1 +
 include/zoysia/drivers/dac_soc.h       |   2 +-
 include/zoysia/drivers/dac_spi.h       |  17 +-
 13 files changed, 419 insertions(+), 249 deletions(-)
 delete mode 100644 include/zoysia/drivers/dac_mmap.h

diff --git a/apps/demo/boards/nucleo_g071rb.overlay b/apps/demo/boards/nucleo_g071rb.overlay
index 3ce96da..95e1f5b 100644
--- a/apps/demo/boards/nucleo_g071rb.overlay
+++ b/apps/demo/boards/nucleo_g071rb.overlay
@@ -248,10 +248,10 @@
 		spi-max-frequency = <10000000>;
 		status = "okay";
 		cs = <0>;
+		shift = <2>;
 		nbits = <12>;
 		command-nbits = <2>;
 		chan-commands = <0>;
-		frame-shift = <2>;
 	};
 
 	dac_spi_2: dac_spi@1 {
diff --git a/core/DAC.cpp b/core/DAC.cpp
index 0082c45..883b33a 100644
--- a/core/DAC.cpp
+++ b/core/DAC.cpp
@@ -18,6 +18,7 @@ DAC_::DAC_(int cs, int nbits, int nchan, const int *chanlist, const void *dev) :
 	m_nbits(nbits),
 	m_nchan(nchan),
 	m_chanlist(chanlist),
+	m_nchain(1),
 	m_cache(0),
 	m_dev(dev)
 {
@@ -78,17 +79,12 @@ int DAC_::ChannelIndex(int chan) const
 	return 0;
 }
 
-/*virtual*/ int DAC_::RefVoltagemV() const
+/*virtual*/ int DAC_::ReadValue(int idx, int chain) const
 {
 	return 0;
 }
 
-/*virtual*/ int DAC_::ReadValue(int idx) const
-{
-	return INT_MIN;
-}
-
-/*virtual*/ int DAC_::WriteValue(int idx, int count)
+/*virtual*/ int DAC_::WriteValue(int idx, int value)
 {
 	return 0;
 }
@@ -106,20 +102,7 @@ int DAC_::ChannelIndex(int chan) const
 /*static*/ int* DAC_::Init(int *persist)
 {
 #ifdef CONFIG_DAC_SOC
-	for (const struct device **pdev = devlist_dac_soc; *pdev != 0; pdev++)
-	{
-		const struct dac_soc_config *config = reinterpret_cast<const struct dac_soc_config*>((*pdev)->config);
-		const int dac_cs = config->cs;
-		const int dac_nbits = config->nbits;
-		const int dac_nchan = config->nchan;
-		const int *dac_chanlist = config->channels;
-		if (dac_cs < CONFIG_DAC_MAX && dac_nchan > 0)
-			new DAC_(dac_cs, dac_nbits, dac_nchan, dac_chanlist, *pdev);
-	}
-#endif
-
-#ifdef CONFIG_DAC_MMAP
-	DAC_MMAP::Init();
+	DAC_SOC::Init();
 #endif
 #ifdef CONFIG_DAC_PWM
 	DAC_PWM::Init();
@@ -132,66 +115,53 @@ int DAC_::ChannelIndex(int chan) const
 		(*e->init)();
 
 	for (int cs = 0; cs < CONFIG_DAC_MAX; cs++)
-	{
 		if (ms_inst[cs] != 0 && persist != 0)
-		{
-			ms_inst[cs]->AssignCache(persist);
-			const int nchan = ms_inst[cs]->nChan();
-			persist += nchan;
-		}
-	}
+			persist = ms_inst[cs]->AssignCache(persist);
 
 	return persist;
 }
 
-/*virtual*/ int DAC_::RefVoltagemV() const
+/*virtual*/ int DAC_::ReadValue(int idx, int chain) const
 {
-#ifdef CONFIG_DAC_SOC
-	return dac_soc_ref_internal(reinterpret_cast<const struct device*>(m_dev));
-#else
-	return 0;
-#endif
+	return (m_cache != 0) ? m_cache[chain * m_nchan + idx] : 0;
 }
 
-/*virtual*/ int DAC_::ReadValue(int idx) const
+/*virtual*/ int DAC_::WriteValue(int idx, int value)
 {
-#ifdef CONFIG_DAC_SOC
-	int output = dac_soc_value(reinterpret_cast<const struct device*>(m_dev), m_chanlist[idx]);
-#else
-	int output = INT_MIN;
-#endif
-	if (output == INT_MIN && m_cache != 0)
-		output = m_cache[idx];
-	return output;
-}
 
-/*virtual*/ int DAC_::WriteValue(int idx, int count)
-{
 	if (m_cache != 0)
-		m_cache[idx] = count;
+		for (int chain = 0; chain < m_nchain; chain++)
+			m_cache[chain * m_nchan + idx] = value;
 	const int channel = (m_chanlist != 0) ? m_chanlist[idx] : idx;
-	return dac_write_value(reinterpret_cast<const struct device*>(m_dev), channel, count);
+	return dac_write_value(reinterpret_cast<const struct device*>(m_dev), channel, value);
 }
 
-#ifdef CONFIG_DAC_MMAP
-
-#include "zoysia/drivers/dac_mmap.h"
+#ifdef CONFIG_DAC_SOC
 
-/*static*/ void DAC_MMAP::Init(void)
+/*static*/ void DAC_SOC::Init(void)
 {
-	for (const struct device **pdev = devlist_dac_mmap; *pdev != 0; pdev++)
+	for (const struct device **pdev = devlist_dac_soc; *pdev != 0; pdev++)
 	{
-		const struct dac_mmap_config *config = reinterpret_cast<const struct dac_mmap_config*>((*pdev)->config);
-		const int dac_mmap_cs = config->cs;
-		const int dac_mmap_nbits = config->nbits;
-		const int dac_mmap_nchan = config->nchan;
-		const int dac_mmap_width = config->width;
-		const int dac_mmap_addr = config->addr;
-		if (dac_mmap_cs < CONFIG_DAC_MAX && dac_mmap_nchan > 0)
-			new DAC_MMAP(dac_mmap_cs, dac_mmap_nbits, dac_mmap_nchan, dac_mmap_addr);
+		const struct dac_soc_config *config = reinterpret_cast<const struct dac_soc_config*>((*pdev)->config);
+		const int dac_cs = config->cs;
+		const int dac_nbits = config->nbits;
+		const int dac_nchan = config->nchan;
+		const int *dac_chanlist = config->channels;
+		if (dac_cs < CONFIG_DAC_MAX && dac_nchan > 0)
+			new DAC_SOC(dac_cs, dac_nbits, dac_nchan, dac_chanlist, *pdev);
 	}
 }
 
+/*virtual*/ int DAC_SOC::Reference(int idx) const
+{
+	return dac_soc_ref_internal(reinterpret_cast<const struct device*>(m_dev));
+}
+
+/*virtual*/ int DAC_SOC::ReadValue(int idx, int) const
+{
+	return dac_soc_value(reinterpret_cast<const struct device*>(m_dev), m_chanlist[idx]);
+}
+
 #endif
 
 #ifdef CONFIG_DAC_PWM
@@ -231,11 +201,11 @@ int DAC_::ChannelIndex(int chan) const
 	}
 }
 
-/*virtual*/ int DAC_PWM::WriteValue(int idx, int count)
+/*virtual*/ int DAC_PWM::WriteValue(int idx, int value)
 {
 	if (m_fsadj != 0)
 		*m_fsadj = 0;
-	return dac_write_value(reinterpret_cast<const struct device*>(m_dev), m_chanlist[idx], count);
+	return dac_write_value(reinterpret_cast<const struct device*>(m_dev), m_chanlist[idx], value);
 }
 
 /*virtual*/ int DAC_PWM::WriteFraction(int idx, const Fraction& value)
@@ -254,7 +224,7 @@ int DAC_::ChannelIndex(int chan) const
 	return DAC_::WriteFraction(idx, value);
 }
 
-/*virtual*/ int DAC_PWM::ReadValue(int idx) const
+/*virtual*/ int DAC_PWM::ReadValue(int idx, int) const
 {
 	return dac_pwm_cycles(reinterpret_cast<const struct device*>(m_dev), m_chanlist[idx]);
 }
@@ -273,19 +243,14 @@ int DAC_::ChannelIndex(int chan) const
 	return float(count) / float(period);
 }
 
-/*virtual*/ void DAC_PWM::Lock()
+/*virtual*/ int DAC_PWM::LDACTrigger()
 {
-	dac_pwm_lock(reinterpret_cast<const struct device*>(m_dev));
-}
-
-/*virtual*/ void DAC_PWM::UnLock()
-{
-	dac_pwm_unlock(reinterpret_cast<const struct device*>(m_dev));
-}
-
-/*virtual*/ bool DAC_PWM::IsLocked() const
-{
-	return dac_pwm_islocked(reinterpret_cast<const struct device*>(m_dev));
+	if (dac_pwm_islocked(reinterpret_cast<const struct device*>(m_dev)))
+	{
+		dac_pwm_unlock(reinterpret_cast<const struct device*>(m_dev));
+		dac_pwm_lock(reinterpret_cast<const struct device*>(m_dev));
+	}
+	return 0;
 }
 
 #endif
@@ -302,16 +267,44 @@ int DAC_::ChannelIndex(int chan) const
 		const int dac_spi_cs = config->cs;
 		const int dac_spi_nbits = config->nbits;
 		const int dac_spi_nchan = config->nchan;
+		const int dac_spi_nchain = config->nchain;
 		if (dac_spi_cs < CONFIG_DAC_MAX && dac_spi_nchan > 0)
-			new DAC_SPI(dac_spi_cs, dac_spi_nbits, dac_spi_nchan, *pdev);
+			new DAC_SPI(dac_spi_cs, dac_spi_nbits, dac_spi_nchan, dac_spi_nchain, *pdev);
 	}
 }
 
+/*virtual*/ int DAC_SPI::Reference(int idx) const
+{
+	return dac_spi_ref_internal(reinterpret_cast<const struct device*>(m_dev), idx);
+}
+
+/*virtual*/ int DAC_SPI::WriteChain(int idx, int *values)
+{
+	if (values == 0)
+		return -EINVAL;
+
+	if (m_cache != 0)
+		for (int chain = 0; chain < m_nchain; chain++)
+			m_cache[chain * m_nchan + idx] = values[chain];
+
+	return dac_spi_write_chain(reinterpret_cast<const struct device*>(m_dev), idx, values);
+}
+
 /*virtual*/ int DAC_SPI::Command(int cmd, int value)
 {
 	return dac_spi_command(reinterpret_cast<const struct device*>(m_dev), cmd, value);
 }
 
+/*virtual*/ int DAC_SPI::Reset()
+{
+	return dac_spi_reset(reinterpret_cast<const struct device*>(m_dev));
+}
+
+/*virtual*/ int DAC_SPI::LDACTrigger()
+{
+	return dac_spi_ldac_trigger(reinterpret_cast<const struct device*>(m_dev));
+}
+
 /*virtual*/ bool DAC_SPI::IsTwosComplement()
 {
 	const struct device *dev = reinterpret_cast<const struct device*>(m_dev);
@@ -391,20 +384,21 @@ private:
 				}
 			}
 
-			if (cmdproc->ArgParseKeyword("LOCK", CommandProc::optional) == ArgParseResult(PARSE_OK) && cmdproc->check_excess())
+			if (cmdproc->ArgParseKeyword("LDAC", CommandProc::optional) == ArgParseResult(PARSE_OK) && cmdproc->check_excess())
 			{
-				dac->Lock();
+				dac->LDACTrigger();
 				return;
 			}
 
-			if (cmdproc->ArgParseKeyword("UNLOCK", CommandProc::optional) == ArgParseResult(PARSE_OK) && cmdproc->check_excess())
+			if (cmdproc->ArgParseKeyword("RESET", CommandProc::optional) == ArgParseResult(PARSE_OK) && cmdproc->check_excess())
 			{
-				dac->UnLock();
+				dac->Reset();
 				return;
 			}
 
 			int setmode = 0;
-			int value;
+			int nvalues = 0;
+			int *values = new int[dac->nChain()];
 			float fvalue;
 			int value_min = 0;
 			int value_max = (1 << dac->nBits()) - 1;
@@ -413,18 +407,33 @@ private:
 				value_min -= 1 << (dac->nBits() - 1);
 				value_max -= 1 << (dac->nBits() - 1);
 			}
-			result = cmdproc->ArgParseInt(value, value_min, value_max, CommandProc::optional, 0);
+			result = cmdproc->ArgParseInt(values[nvalues++], value_min, value_max, CommandProc::optional, 0);
 			if (result == ArgParseResult(PARSE_ERROR))
 			{
 				return;
 			}
 			else if (result == ArgParseResult(PARSE_OK))
 			{
+				while (true)
+				{
+					int val;
+					if (cmdproc->ArgParseInt(val, value_min, value_max, CommandProc::optional, 0) == ArgParseResult(PARSE_OK))
+					{
+						if (nvalues == dac->nChain())
+						{
+							cmdproc->error("?Too many DAC values\n");
+							return;
+						}
+						values[nvalues++] = val;
+						continue;
+					}
+					break;
+				}
 				setmode = 1;
 			}
 			else
 			{
-				result = cmdproc->ArgParseFloat(fvalue, 0.0f, float(dac->RefVoltagemV())/1000.0f, 4, CommandProc::optional);
+				result = cmdproc->ArgParseFloat(fvalue, CommandProc::optional);
 				if (result == ArgParseResult(PARSE_ERROR))
 				{
 					return;
@@ -443,23 +452,38 @@ private:
 				{
 					if (setmode == 1)
 					{
-						int ret = dac->WriteValue(i, value);
+						int ret = (nvalues > 1) ? dac->WriteChain(i, values) : dac->WriteValue(i, values[0]);
 						if (ret != 0)
 							cmdproc->GetComm().cprintf("DAC 0x%02X 0x%02X: FAILED(%d)\n", cs, dac->Channel(i), ret);
 					}
 					else if (setmode == 2)
 					{
-						int ret = dac->WriteVoltage(i, fvalue);
+						int ret = dac->WriteVoltageCurrent(i, fvalue);
 						if (ret != 0)
 							cmdproc->GetComm().cprintf("DAC 0x%02X 0x%02X: FAILED(%d)\n", cs, dac->Channel(i), ret);
 					}
 					else
 					{
-						const int value = dac->ReadValue(i);
-						cmdproc->GetComm().cprintf("DAC 0x%02X 0x%02X: %d(%.3fmv)\n", cs, dac->Channel(i), value, dac->CalcVoltagemV(i, value));
+						const int nchain = dac->nChain();
+						const int val_buf_size = nchain * 16;
+						float *val_arr = new float[nchain];
+						char *val_buf = new char[val_buf_size];
+						int val, n = 0;
+						for (int chain = 0; chain < nchain; chain++)
+						{
+							val = dac->ReadValue(i, chain);
+							val_arr[chain] = dac->CalcVoltageCurrent(i, val);
+							n += snprintf(val_buf + n, val_buf_size - n, (chain == nchain - 1) ? "%d (" : "%d ", val);
+						}
+						for (int chain = 0; chain < nchain; chain++)
+							n += snprintf(val_buf + n, val_buf_size - n, (chain == nchain - 1) ? "%.3f)" : "%.3f ", val_arr[chain]);
+						cmdproc->GetComm().cprintf("DAC 0x%02X 0x%02X: %s %d\n", cs, dac->Channel(i), val_buf, dac->Reference(i));
+						delete[] val_buf;
+						delete[] val_arr;
 					}
 				}
 			}
+			delete[] values;
 		}
 		else if (result == ArgParseResult(PARSE_NONE))
 		{
@@ -472,7 +496,7 @@ private:
 				{
 					int n = 0;
 					int s = 32 * dac->nChan();
-					char *buf = new char[s + 1];
+					char *buf = new char[s];
 					for (int i = 0; i < dac->nChan(); i++)
 					{
 						char vbuf[32] = "";
@@ -485,9 +509,9 @@ private:
 						}
 						else
 							snprintf(vbuf, sizeof(vbuf), "%d", val);
-						n += snprintf(buf + n, s + 1 - n, "%s%d:%s%s", i == 0 ? "(" : ",", dac->Channel(i), vbuf, i < dac->nChan() - 1 ? "" : ")");
+						n += snprintf(buf + n, s - n, "%s%d:%s%s", i == 0 ? "(" : ",", dac->Channel(i), vbuf, i < dac->nChan() - 1 ? "" : ")");
 					}
-					cmdproc->GetComm().cprintf("%d/%db/%dmv,%s%s\n", cs + 1, dac->nBits(), dac->RefVoltagemV(), buf, dac->IsLocked() ? ",locked" : "");
+					cmdproc->GetComm().cprintf("%d/%db,%s\n", cs + 1, dac->nBits(), buf);
 					delete[] buf;
 				}
 			}
diff --git a/drivers/dac/dac_pwm_stm32.c b/drivers/dac/dac_pwm_stm32.c
index d907bc1..d816af7 100644
--- a/drivers/dac/dac_pwm_stm32.c
+++ b/drivers/dac/dac_pwm_stm32.c
@@ -147,6 +147,8 @@ static int dac_pwm_stm32_init(const struct device *dev)
 				dac_pwm_stm32_channel_setup(dev, &chan_config);
 				dac_pwm_stm32_write_value(dev, cfg->dac_config.channels[chan], cfg->dac_config.init_values[chan]);
 			}
+		if (cfg->dac_config.enable_ldac)
+			dac_pwm_lock(dev);
 	}
 	const uint32_t period_cycles = LL_TIM_GetAutoReload(cfg->timer_config.timer);
 	data->period_cycles_adjust = (int)(1 << cfg->dac_config.nbits) - (int)(period_cycles) - 1;
@@ -215,6 +217,7 @@ int dac_pwm_cycles(const struct device *dev, int chan)
 			.nchan = dac_stm32_timer_nchan_##inst, \
 			.channels = dac_stm32_timer_chan_##inst, \
 			.init_values = dac_stm32_timer_init_val_##inst, \
+			.enable_ldac = DT_INST_PROP_OR(inst, enable_ldac, 0), \
 			.auto_period = (dac_stm32_timer_nchan_##inst != 1) ? 0 : DT_INST_PROP_OR(inst, auto_period, 0), \
 		}, \
 		.pinctrl_config = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
diff --git a/drivers/dac/dac_soc.c b/drivers/dac/dac_soc.c
index 1e0ef47..f590287 100644
--- a/drivers/dac/dac_soc.c
+++ b/drivers/dac/dac_soc.c
@@ -7,7 +7,12 @@
 
 __attribute__((weak)) const struct device* devlist_dac_soc[] = {0};
 
-__attribute__((weak)) int dac_soc_value(const struct device*, uint8_t)
+__attribute__((weak)) int dac_soc_value(const struct device*, int)
 {
     return INT_MIN;
 }
+
+__attribute__((weak)) int dac_soc_ref_internal(const struct device*)
+{
+    return 0;
+}
diff --git a/drivers/dac/dac_soc_stm32.c b/drivers/dac/dac_soc_stm32.c
index 2e9a2a8..47384e3 100644
--- a/drivers/dac/dac_soc_stm32.c
+++ b/drivers/dac/dac_soc_stm32.c
@@ -127,7 +127,7 @@ static const struct dac_driver_api dac_stm32_driver_api = {
 	.write_value = dac_stm32_write_value
 };
 
-int dac_soc_value(const struct device *dev, uint8_t channel)
+int dac_soc_value(const struct device *dev, int channel)
 {
 	const struct dac_stm32_cfg *cfg = dev->config;
 
diff --git a/drivers/dac/dac_spi.c b/drivers/dac/dac_spi.c
index 66ec7eb..3cb7394 100644
--- a/drivers/dac/dac_spi.c
+++ b/drivers/dac/dac_spi.c
@@ -4,6 +4,8 @@
 
 #define DT_DRV_COMPAT oasis_dac_spi
 
+#include <stdlib.h>
+
 #include <zephyr/drivers/dac.h>
 #include <zephyr/drivers/spi.h>
 #include <zephyr/device.h>
@@ -17,149 +19,254 @@ struct dac_spi_cfg {
 	const struct spi_dt_spec spi;
 };
 
-struct dac_spi_data {
-	struct k_sem sem;
-	int command;
-	int value;
-};
-
-static void acquire_device(const struct device *dev)
+static int dac_spi_output(const struct device *dev, int output)
 {
-	if (IS_ENABLED(CONFIG_MULTITHREADING))
+	const struct dac_spi_cfg *cfg = dev->config;
+
+	int rc = -EINVAL;
+
+	const int nbits = cfg->config.nbits;
+	const int shift = cfg->config.shift;
+	const int command_nbits = cfg->config.command_nbits;
+	const int total_nbits = nbits + shift + command_nbits;
+	const int bytes = (total_nbits <= 16) ? 2 : 3;
+
+#if (CONFIG_HEAP_MEM_POOL_SIZE > 0)
+	uint8_t *buffer = k_malloc(bytes * cfg->config.nchain);
+#elif defined(CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE)
+	uint8_t *buffer = malloc(bytes * cfg->config.nchain);
+#else
+	uint8_t *buffer = 0;
+#endif
+	if (buffer)
 	{
-		struct dac_spi_data *const data = dev->data;
-		k_sem_take(&data->sem, K_FOREVER);
+		for (int chain = 0; chain < cfg->config.nchain; chain++)
+		{
+			if (total_nbits <= 16)
+			{
+				buffer[2*chain+0] = (output >> 8) & 0xFF;
+				buffer[2*chain+1] = (output >> 0) & 0xFF;
+			}
+			else
+			{
+				buffer[3*chain+0] = (output >> 16) & 0xFF;
+				buffer[3*chain+1] = (output >> 8) & 0xFF;
+				buffer[3*chain+2] = (output >> 0) & 0xFF;
+			}
+		}
+		struct spi_buf spi_buf[1] = {
+			{
+				.buf = buffer,
+				.len = bytes * cfg->config.nchain,
+			}
+		};
+		const struct spi_buf_set tx_set = {
+			.buffers = spi_buf,
+			.count = 1,
+		};
+		rc = spi_write_dt(&cfg->spi, &tx_set);
+		free(buffer);
 	}
+
+	return rc;
 }
 
-static void release_device(const struct device *dev)
+static int dac_spi_write_value(const struct device *dev, uint8_t idx, uint32_t value)
 {
-	if (IS_ENABLED(CONFIG_MULTITHREADING))
+	const struct dac_spi_cfg *cfg = dev->config;
+
+	int rc = -EIO;
+
+	if (idx >= 0 && idx < cfg->config.nchan)
 	{
-		struct dac_spi_data *const data = dev->data;
-		k_sem_give(&data->sem);
+		const int command = (cfg->config.deferred_chan_commands[idx] != 0) ?
+			cfg->config.deferred_chan_commands[idx] : cfg->config.chan_commands[idx];
+		const int val = (int)((value & ((1 << cfg->config.nbits) - 1)) << cfg->config.shift);
+		rc = dac_spi_output(dev, (command << (cfg->config.nbits + cfg->config.shift)) | val);
 	}
+
+	return rc;
 }
 
-static int dac_spi_output(const struct device *dev)
+static const struct dac_driver_api dac_spi_driver_api = {
+	.write_value = dac_spi_write_value,
+};
+
+int dac_spi_ref_internal(const struct device *dev, int idx)
 {
 	const struct dac_spi_cfg *cfg = dev->config;
-	struct dac_spi_data *const data = dev->data;
 
-	const int nbits = cfg->config.nbits;
-	const int frame_shift = cfg->config.frame_shift;
-	const int command_nbits = cfg->config.command_nbits;
-	const int total_nbits = nbits + frame_shift + command_nbits;
+	int value = 0;
 
-	int output = ((data->command << nbits) | data->value) << frame_shift;
-	int length = 3;
+	if (idx >= 0 && idx < cfg->config.nchan)
+		value = cfg->config.references[idx];
 
-	if (total_nbits <= 16)
-	{
-		output <<= 8;
-		length = 2;
-	}
+	return value;
+}
 
-	uint8_t buffer[3] = {
-		(output >> 16) & 0xFF,
-		(output >> 8) & 0xFF,
-		(output & 0xFF)
-	};
+int dac_spi_ldac_trigger(const struct device *dev)
+{
+	const struct dac_spi_cfg *cfg = dev->config;
 
-	struct spi_buf spi_buf[1] = {
-		{
-			.buf = buffer,
-			.len = length
-		}
-	};
-	const struct spi_buf_set tx_set = {
-		.buffers = spi_buf,
-		.count = 1,
-	};
+	int rc;
 
-	int rc = spi_write_dt(&cfg->spi, &tx_set);
+	if (cfg->config.ldac_command != 0)
+	{
+		if ((rc = dac_spi_output(dev, cfg->config.ldac_command)) < 0)
+			return rc;
+	}
+	else if (cfg->config.ldac_gpio.port != 0 && device_is_ready(cfg->config.ldac_gpio.port))
+	{
+		if ((rc = gpio_pin_set_dt(&cfg->config.ldac_gpio, 1)) < 0)
+			return rc;
+		if ((rc = gpio_pin_set_dt(&cfg->config.ldac_gpio, 0)) < 0)
+			return rc;
+	}
 
-	return rc;
+	return 0;
 }
 
-static int dac_spi_write_value(const struct device *dev, uint8_t channel, uint32_t value)
+int dac_spi_write_chain(const struct device *dev, int idx, int *values)
 {
 	const struct dac_spi_cfg *cfg = dev->config;
-	struct dac_spi_data *const data = dev->data;
 
 	int rc = -EIO;
 
-	if (channel > 0 && channel <= cfg->config.nchan)
+	if (idx >= 0 && idx < cfg->config.nchan)
 	{
 		const int nbits = cfg->config.nbits;
-		acquire_device(dev);
-		data->command = cfg->config.chan_commands[channel-1];
-		data->value = (int)(value & ((1 << nbits) - 1));
-		rc = dac_spi_output(dev);
-		release_device(dev);
+		const int shift = cfg->config.shift;
+		const int command_nbits = cfg->config.command_nbits;
+		const int total_nbits = nbits + shift + command_nbits;
+
+		const int command = (cfg->config.deferred_chan_commands[idx] != 0) ?
+			cfg->config.deferred_chan_commands[idx] : cfg->config.chan_commands[idx];
+		const int bytes = (total_nbits <= 16) ? 2 : 3;
+		
+#if (CONFIG_HEAP_MEM_POOL_SIZE > 0)
+		uint8_t *buffer = k_malloc(bytes * cfg->config.nchain);
+#elif defined(CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE)
+		uint8_t *buffer = malloc(bytes * cfg->config.nchain);
+#else
+		uint8_t *buffer = 0;
+#endif
+		if (buffer)
+		{
+			for (int chain = 0; chain < cfg->config.nchain; chain++)
+			{
+				const int value = (int)((values[cfg->config.nchain - chain - 1] & ((1 << nbits) - 1)) << shift);
+				const int output = (command << (nbits + shift)) | value;
+				if (total_nbits <= 16)
+				{
+					buffer[2*chain+0] = (output >> 8) & 0xFF;
+					buffer[2*chain+1] = (output >> 0) & 0xFF;
+				}
+				else
+				{
+					buffer[3*chain+0] = (output >> 16) & 0xFF;
+					buffer[3*chain+1] = (output >> 8) & 0xFF;
+					buffer[3*chain+2] = (output >> 0) & 0xFF;
+				}
+			}
+			struct spi_buf spi_buf[1] = {
+				{
+					.buf = buffer,
+					.len = bytes * cfg->config.nchain,
+				}
+			};
+			const struct spi_buf_set tx_set = {
+				.buffers = spi_buf,
+				.count = 1,
+			};
+			rc = spi_write_dt(&cfg->spi, &tx_set);
+			free(buffer);
+		}
 	}
 
 	return rc;
 }
 
-static int dac_spi_init(const struct device *dev)
+int dac_spi_command(const struct device *dev, int cmd, int value)
 {
 	const struct dac_spi_cfg *cfg = dev->config;
-	struct dac_spi_data *const data = dev->data;
-
-	data->command = 0;
-	data->value = 0;
-	if (IS_ENABLED(CONFIG_MULTITHREADING))
-		k_sem_init(&data->sem, 1, K_SEM_MAX_LIMIT);
 
-	if (!spi_is_ready_dt(&cfg->spi))
-		return -ENODEV;
+	int rc = -EINVAL;
 
-	int pkt;
 	const int command_mask = (1 << cfg->config.command_nbits) - 1;
-	const int value_mask = (1 << cfg->config.nbits) - 1;
+	if ((cmd & ~command_mask) == 0)
+	{
+		const int command = (int)(cmd & command_mask);
+		value &= (1 << (cfg->config.nbits + cfg->config.shift)) - 1;
+		rc = dac_spi_output(dev, (command << (cfg->config.nbits + cfg->config.shift)) | value);
+	}
+
+	return rc;
+}
+
+static int dac_spi_configure(const struct device *dev)
+{
+	const struct dac_spi_cfg *cfg = dev->config;
+
+	// Initial commands
+	int output;
 	const int *cmd = cfg->config.init_commands;
-	while ((pkt = *cmd++) != 0)
+	while ((output = *cmd++) != 0)
 	{
-		data->command = (pkt >> (cfg->config.frame_shift + cfg->config.nbits)) & command_mask;
-		data->value = (pkt >> cfg->config.frame_shift) & value_mask;
-		int rc = dac_spi_output(dev);
+		int rc = dac_spi_output(dev, output);
 		if (rc != 0)
 			return rc;
 	}
+	// Initial DAC values
+	for (int i = 0; i < cfg->config.nchan; i++)
+	{
+		if (cfg->config.init_values[i] != 0)
+		{
+			int rc = dac_spi_write_value(dev, i, cfg->config.init_values[i]);
+			if (rc != 0)
+				return rc;
+		}
+	}
 
 	return 0;
 }
 
-static const struct dac_driver_api dac_spi_driver_api = {
-	.write_value = dac_spi_write_value,
-};
+static int dac_spi_init(const struct device *dev)
+{
+	const struct dac_spi_cfg *cfg = dev->config;
 
-int dac_spi_command(const struct device *dev, int cmd, int value)
+	if (!spi_is_ready_dt(&cfg->spi))
+		return -ENODEV;
+
+	const int rc = dac_spi_configure(dev);
+	if (rc != 0)
+		return rc;
+
+	return 0;
+}
+
+int dac_spi_reset(const struct device *dev)
 {
 	const struct dac_spi_cfg *cfg = dev->config;
-	struct dac_spi_data *const data = dev->data;
 
-	acquire_device(dev);
+	int rc;
 
-	int rc = EINVAL;
-	const int command_mask = (1 << cfg->config.command_nbits) - 1;
-	if ((cmd & ~command_mask) == 0)
+	if (cfg->config.reset_command != 0)
 	{
-		data->command = (int)(cmd & command_mask);
-		const int value_mask = (1 << cfg->config.nbits) - 1;
-		if ((value & ~value_mask) == 0)
-			data->value = (int)(value & value_mask);
-		rc = dac_spi_output(dev);
+		if ((rc = dac_spi_output(dev, cfg->config.reset_command)) != 0)
+			return rc;
+		k_busy_wait(cfg->config.reset_wait_us);
+		if ((rc = dac_spi_configure(dev)) != 0)
+			return rc;
 	}
 
-	release_device(dev);
-
-	return rc;
+	return 0;
 }
 
 #define DT_INST_DEV_CONFIG_INIT_CMD_GET(idx, inst) DT_INST_PROP_BY_IDX(inst, init_commands, idx)
 #define DT_INST_DEV_CONFIG_CHAN_CMD_GET(idx, inst) DT_INST_PROP_BY_IDX(inst, chan_commands, idx)
+#define DT_INST_DEV_CONFIG_DEFERRED_CHAN_CMD_GET(idx, inst) COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, deferred_chan_commands), (DT_INST_PROP_BY_IDX(inst, deferred_chan_commands, idx)), (0))
+#define DT_INST_DEV_CONFIG_INIT_VAL_GET(idx, inst) COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, init_values), (DT_INST_PROP_BY_IDX(inst, init_values, idx)), (0))
+#define DT_INST_DEV_CONFIG_REF_GET(idx, inst) COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, references), (DT_INST_PROP_BY_IDX(inst, references, idx)), (0))
 
 #define DAC_DEVICE_ITEM(inst) DEVICE_DT_GET(DT_DRV_INST(inst)),
 #define DAC_DEVICE_INIT(inst) \
@@ -169,25 +276,41 @@ int dac_spi_command(const struct device *dev, int cmd, int value)
 	static const int dac_spi_chan_cmd_##inst[] = { \
 		LISTIFY(DT_INST_PROP_LEN(inst, chan_commands), DT_INST_DEV_CONFIG_CHAN_CMD_GET, (,), inst) \
 	}; \
+	static const int dac_spi_deferred_chan_cmd_##inst[] = { \
+		LISTIFY(DT_INST_PROP_LEN(inst, chan_commands), DT_INST_DEV_CONFIG_DEFERRED_CHAN_CMD_GET, (,), inst) \
+	}; \
+	static const int dac_spi_init_val_##inst[] = { \
+		LISTIFY(DT_INST_PROP_LEN(inst, chan_commands), DT_INST_DEV_CONFIG_INIT_VAL_GET, (,), inst) \
+	}; \
+	static const int dac_spi_chan_ref_##inst[] = { \
+		LISTIFY(DT_INST_PROP_LEN(inst, chan_commands), DT_INST_DEV_CONFIG_REF_GET, (,), inst) \
+	}; \
 	static const int dac_spi_nchan_cmd_##inst = sizeof(dac_spi_chan_cmd_##inst) / sizeof(int); \
 	static const struct dac_spi_cfg dac_spi_cfg_##inst = { \
 		.config = { \
 			.cs = DT_INST_PROP(inst, cs), \
+			.shift = DT_INST_PROP_OR(inst, shift, 0), \
+			.nchain = DT_INST_PROP_OR(inst, nchain, 1), \
 			.nbits = DT_INST_PROP(inst, nbits), \
 			.nchan = dac_spi_nchan_cmd_##inst, \
-			.init_commands = dac_spi_init_cmd_##inst, \
-			.chan_commands = dac_spi_chan_cmd_##inst, \
 			.command_nbits = DT_INST_PROP(inst, command_nbits), \
-			.frame_shift = DT_INST_PROP(inst, frame_shift), \
 			.twos_complement = DT_INST_PROP_OR(inst, twos_complement, 0), \
+			.init_commands = dac_spi_init_cmd_##inst, \
+			.chan_commands = dac_spi_chan_cmd_##inst, \
+			.deferred_chan_commands = dac_spi_deferred_chan_cmd_##inst, \
+			.init_values = dac_spi_init_val_##inst, \
+			.references = dac_spi_chan_ref_##inst, \
+			.reset_wait_us = DT_INST_PROP_OR(inst, reset_wait_us, 1000), \
+			.reset_command = DT_INST_PROP_OR(inst, reset_command, 0), \
+			.ldac_command = DT_INST_PROP_OR(inst, ldac_command, 0), \
+			.ldac_gpio = GPIO_DT_SPEC_GET_OR(DT_INST_PHANDLE(inst, ldac_gpio), gpios, {}), \
 		}, \
 		.spi = SPI_DT_SPEC_INST_GET(inst, SPI_MODE_CPHA | SPI_WORD_SET(8), 0), \
 	}; \
-	static struct dac_spi_data dac_spi_data_##inst; \
 	DEVICE_DT_INST_DEFINE(inst, \
 		&dac_spi_init, \
 		NULL, \
-		&dac_spi_data_##inst, \
+		NULL, \
 		&dac_spi_cfg_##inst, POST_KERNEL, \
 		CONFIG_DAC_SPI_INIT_PRIORITY, \
 		&dac_spi_driver_api); \
diff --git a/dts/bindings/oasis,dac-pwm-stm32.yaml b/dts/bindings/oasis,dac-pwm-stm32.yaml
index f018d04..0980e79 100644
--- a/dts/bindings/oasis,dac-pwm-stm32.yaml
+++ b/dts/bindings/oasis,dac-pwm-stm32.yaml
@@ -30,12 +30,14 @@ properties:
       type: array
 
     auto-period:
-      required: false
       type: boolean
 
     init-values:
       type: array
 
+    enable-ldac:
+      type: boolean
+
     "#dac-pwm-cells":
       type: int
       required: true
diff --git a/dts/bindings/oasis,dac-spi.yaml b/dts/bindings/oasis,dac-spi.yaml
index adf173d..dabb4ef 100644
--- a/dts/bindings/oasis,dac-spi.yaml
+++ b/dts/bindings/oasis,dac-spi.yaml
@@ -11,12 +11,24 @@ properties:
       required: true
       type: int
 
+    shift:
+      type: int
+
+    nchain:
+      type: int
+
     nbits:
       required: true
       type: int
 
+    command-nbits:
+      required: true
+      type: int
+
+    twos-complement:
+      type: boolean
+
     init-commands:
-      required: false
       type: array
       default: [0]
 
@@ -24,14 +36,23 @@ properties:
       required: true
       type: array
 
-    command-nbits:
-      required: true
+    deferred-chan-commands:
+      type: array
+
+    init-values:
+      type: array
+
+    references:
+      type: array
+
+    reset-wait-us:
       type: int
 
-    frame-shift:
-      required: false
+    reset-command:
       type: int
-      default: 0
 
-    twos-complement:
-      type: boolean
+    ldac-command:
+      type: int
+
+    ldac_gpio:
+      type: phandle
diff --git a/include/zoysia/core/DAC.h b/include/zoysia/core/DAC.h
index bde0d07..373f228 100644
--- a/include/zoysia/core/DAC.h
+++ b/include/zoysia/core/DAC.h
@@ -16,27 +16,27 @@ public:
     static DAC_* Instance(int cs) { return (cs >= 0 && cs < CONFIG_DAC_MAX) ? ms_inst[cs] : 0; }
 	static void RegisterInit(void (*init)(void));
 	virtual bool IsTwosComplement() const { return false; }
-	virtual int RefVoltagemV() const;
-	virtual int WriteValue(int idx, int count);
-	virtual int ReadValue(int idx) const;
+	virtual int Reference(int idx) const { return 0; }
+	virtual int WriteChain(int idx, int *values) { return 0; }
+	virtual int WriteValue(int idx, int value);
+	virtual int ReadValue(int idx, int chain = 0) const;
 	virtual int WriteFraction(int idx, const Fraction& value) { int raw = value.toInt(m_nbits); return WriteValue(idx, raw > 0 ? raw : 0); }
 	virtual Fraction ReadFraction(int idx) const { return Fraction(ReadValue(idx), m_nbits); }
 	virtual float ReadFloat(int idx) const { return float(ReadValue(idx)) / float(1 << m_nbits); }
 	virtual int WriteFloat(int idx, float value) { return WriteFraction(idx, Fraction(value)); }
 	virtual int Command(int cmd, int value) { return 0; }
-	virtual void Lock() { }
-	virtual void UnLock() { }
-	virtual bool IsLocked() const { return false; }
+	virtual int Reset() { return 0; }
+	virtual int LDACTrigger() { return 0; }
 	virtual int ReadFullScaleAdjust() const { return 0; }
-	float CalcVoltagemV(int idx, int value) const { return float(value) / float(1 << m_nbits) * float(RefVoltagemV()); }
-	float ReadVoltagemV(int idx) const { return CalcVoltagemV(idx, ReadValue(idx)); }
-	float ReadVoltage(int idx) const { return ReadVoltagemV(idx) / 1000.0f; }
-	int WriteVoltage(int idx, float voltage) { return WriteFloat(idx, voltage * 1000.0f / float(RefVoltagemV())); }
+	float CalcVoltageCurrent(int idx, int value) const { return float(value) / float(1 << m_nbits) * float(Reference(idx)); }
+	float ReadVoltageCurrent(int idx) const { return CalcVoltageCurrent(idx, ReadValue(idx)); }
+	int WriteVoltageCurrent(int idx, float voltage) { return WriteFloat(idx, voltage / float(Reference(idx))); }
 	int Channel(int idx) const;
 	int ChannelIndex(int chan) const;
+	int nChain() const { return m_nchain; }
 	int nChan() const { return m_nchan; }
 	int nBits() const { return m_nbits; }
-	int* AssignCache(int *cache) { m_cache = cache; return cache != 0 ? cache + m_nchan : 0; }
+	int* AssignCache(int *cache) { m_cache = cache; return cache != 0 ? cache + m_nchan * m_nchain + 1 : 0; }
 	static int ScaleFloat (float value, int steps);
 
 protected:
@@ -45,6 +45,7 @@ protected:
 	const int m_nbits;
 	const int m_nchan;
 	const int *m_chanlist;
+	int m_nchain;
 	int *m_cache;
 	const void *m_dev;
 
@@ -58,27 +59,28 @@ class DAChan
 {
 public:
 	DAChan(DAC_ &dac, int idx) : m_dac(dac), m_idx(idx) { }
-	int ReadValue() const { return m_dac.ReadValue(m_idx); }
-	int WriteValue(int count) { return m_dac.WriteValue(m_idx, count); }
+	int ReadValue(int chain = 0) const { return m_dac.ReadValue(m_idx, chain); }
+	int WriteValue(int value) { return m_dac.WriteValue(m_idx, value); }
 	Fraction readFraction() const { return m_dac.ReadFraction(m_idx); }
 	int WriteFraction(const Fraction& value) { return m_dac.WriteFraction(m_idx, value); }
-	float ReadVoltagemV() const { return m_dac.ReadVoltagemV(m_idx); }
-	float ReadVoltage() const { return m_dac.ReadVoltage(m_idx); }
-	int WriteVoltage(float value) { return m_dac.WriteVoltage(m_idx, value); }
+	float ReadVoltageCurrent() const { return m_dac.ReadVoltageCurrent(m_idx); }
+	int WriteVoltageCurrent(float value) { return m_dac.WriteVoltageCurrent(m_idx, value); }
 
 private:
 	DAC_ &m_dac;
 	const int m_idx;
 };
 
-#ifdef CONFIG_DAC_MMAP
-class DAC_MMAP : public DAC_
+#ifdef CONFIG_DAC_SOC
+class DAC_SOC : public DAC_
 {
 public:
-    static void Init(void);
+	static void Init(void);
+	virtual int Reference(int idx) const;
+	virtual int ReadValue(int idx, int) const;
 private:
-	DAC_MMAP(int cs, int nbits, int nchan, const void *dev) :
-		DAC_(cs, nbits, nchan, 0, dev) { }
+	DAC_SOC(int cs, int nbits, int nchan, const int *chanlist, const void *dev) :
+		DAC_(cs, nbits, nchan, chanlist, dev) { }
 };
 #endif
 
@@ -87,14 +89,12 @@ class DAC_PWM : public DAC_
 {
 public:
     static void Init(void);
-	virtual int WriteValue(int idx, int count);
+	virtual int WriteValue(int idx, int value);
 	virtual int WriteFraction(int idx, const Fraction& value);
-	virtual int ReadValue(int idx) const;
+	virtual int ReadValue(int idx, int) const;
 	virtual Fraction ReadFraction(int idx) const;
 	virtual float ReadFloat(int idx) const;
-	virtual void Lock();
-	virtual void UnLock();
-	virtual bool IsLocked() const;
+	virtual int LDACTrigger();
 	virtual int ReadFullScaleAdjust() const { return (m_fsadj != 0) ? *m_fsadj : 0; }
 private:
 	DAC_PWM(int auto_period, int cs, int nbits, int nchan, int *fsadj, const int *chanlist, const void *dev) :
@@ -108,11 +108,15 @@ class DAC_SPI : public DAC_
 {
 public:
     static void Init(void);
+	virtual int Reference(int idx) const;
+	virtual int WriteChain(int idx, int *values);
 	virtual int Command(int cmd, int value);
+	virtual int Reset();
+	virtual int LDACTrigger();
 	virtual bool IsTwosComplement();
 private:
-	DAC_SPI(int cs, int nbits, int nchan, const void *dev) :
-		DAC_(cs, nbits, nchan, 0, dev) { }
+	DAC_SPI(int cs, int nbits, int nchan, int nchain, const void *dev) :
+		DAC_(cs, nbits, nchan, 0, dev) { m_nchain = nchain; }
 };
 #endif
 
diff --git a/include/zoysia/drivers/dac_mmap.h b/include/zoysia/drivers/dac_mmap.h
deleted file mode 100644
index 0a7c1fc..0000000
--- a/include/zoysia/drivers/dac_mmap.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * SPDX-License-Identifier: Apache-2.0
- */
-
-#ifndef ZOYSIA_DRIVERS_DAC_MMAP_H
-#define ZOYSIA_DRIVERS_DAC_MMAP_H 1
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct dac_mmap_config {
-	const int cs;
-	const int nbits;
-    const int nchan;
-	const int width;
-	const int addr;
-};
-
-extern const struct device* devlist_dac_mmap[];
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/include/zoysia/drivers/dac_pwm.h b/include/zoysia/drivers/dac_pwm.h
index 96be288..f257394 100644
--- a/include/zoysia/drivers/dac_pwm.h
+++ b/include/zoysia/drivers/dac_pwm.h
@@ -17,6 +17,7 @@ struct dac_pwm_config {
     const int nchan;
 	const int *channels;
 	const int *init_values;
+	const int enable_ldac;
 	const int auto_period;
 };
 
diff --git a/include/zoysia/drivers/dac_soc.h b/include/zoysia/drivers/dac_soc.h
index 8ae9378..92060ca 100644
--- a/include/zoysia/drivers/dac_soc.h
+++ b/include/zoysia/drivers/dac_soc.h
@@ -18,7 +18,7 @@ struct dac_soc_config {
 	const int *channels;
 };
 
-int dac_soc_value(const struct device*, uint8_t);
+int dac_soc_value(const struct device*, int);
 int dac_soc_ref_internal(const struct device*);
 
 extern const struct device* devlist_dac_soc[];
diff --git a/include/zoysia/drivers/dac_spi.h b/include/zoysia/drivers/dac_spi.h
index eff399b..17008d3 100644
--- a/include/zoysia/drivers/dac_spi.h
+++ b/include/zoysia/drivers/dac_spi.h
@@ -6,6 +6,7 @@
 #define ZOYSIA_DRIVERS_DAC_SPI_H 1
 
 #include <zephyr/device.h>
+#include <zephyr/drivers/gpio.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -13,16 +14,28 @@ extern "C" {
 
 struct dac_spi_config {
 	const int cs;
+	const int shift;
 	const int nbits;
     const int nchan;
+	const int nchain;
 	const int command_nbits;
+	const int twos_complement;
 	const int *init_commands;
 	const int *chan_commands;
-	const int frame_shift;
-	const int twos_complement;
+	const int *deferred_chan_commands;
+	const int *init_values;
+	const int *references;
+	const int reset_wait_us;
+	const int reset_command;
+	const int ldac_command;
+	struct gpio_dt_spec ldac_gpio;
 };
 
+int dac_spi_ref_internal(const struct device*, int);
+int dac_spi_ldac_trigger(const struct device*);
+int dac_spi_write_chain(const struct device *dev, int, int*);
 int dac_spi_command(const struct device*, int, int);
+int dac_spi_reset(const struct device*);
 
 extern const struct device* devlist_dac_spi[];
 
-- 
GitLab