Skip to content

Commit dc0dc90

Browse files
committed
drivers: dac: Add drivers for TI DAC family X311 on SPI bus
Add shared code in 'dac_tx311.c' and configuration files. Support power mode bits via configuration. Signed-off-by: Andreas Wolf <[email protected]>
1 parent ff3284f commit dc0dc90

File tree

10 files changed

+331
-0
lines changed

10 files changed

+331
-0
lines changed

drivers/dac/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,5 @@ zephyr_library_sources_ifdef(CONFIG_DAC_SAMD5X dac_samd5x.c)
3434
zephyr_library_sources_ifdef(CONFIG_DAC_SILABS_VDAC dac_silabs_vdac.c)
3535
zephyr_library_sources_ifdef(CONFIG_DAC_STM32 dac_stm32.c)
3636
zephyr_library_sources_ifdef(CONFIG_DAC_TEST dac_test.c)
37+
zephyr_library_sources_ifdef(CONFIG_DAC_TX311 dac_tx311.c)
3738
# zephyr-keep-sorted-stop

drivers/dac/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ source "drivers/dac/Kconfig.samd5x"
5252
source "drivers/dac/Kconfig.silabs"
5353
source "drivers/dac/Kconfig.stm32"
5454
source "drivers/dac/Kconfig.test"
55+
source "drivers/dac/Kconfig.tx311"
5556
# zephyr-keep-sorted-stop
5657

5758
endif # DAC

drivers/dac/Kconfig.tx311

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# DAC configuration options
2+
#
3+
# Copyright (c) 2025 Andreas Wolf <[email protected]>
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
config DAC_TX311
8+
bool "TI DACx311 DAC driver"
9+
default y
10+
depends on DT_HAS_TI_DAC8311_ENABLED || DT_HAS_TI_DAC7311_ENABLED || \
11+
DT_HAS_TI_DAC6311_ENABLED || DT_HAS_TI_DAC5311_ENABLED
12+
select SPI
13+
help
14+
Enable the driver for TI DACx311 chip.

drivers/dac/dac_tx311.c

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
/*
2+
* Copyright (c) 2025 Andreas Wolf <[email protected]>
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/**
8+
* @file dac_tx311.c
9+
* @brief Driver for the TI x311 single channel DAC chips.
10+
*
11+
* This driver supports multiple variants of the Texas Instrument DAC chip.
12+
*
13+
* DAC5311 : 8-bit resolution
14+
* DAC6311 : 10-bit resolution
15+
* DAC7311 : 12-bit resolution
16+
* DAC8311 : 14-bit resolution
17+
*
18+
*/
19+
20+
#include <zephyr/kernel.h>
21+
#include <zephyr/drivers/spi.h>
22+
#include <zephyr/drivers/dac.h>
23+
#include <zephyr/sys/byteorder.h>
24+
#include <zephyr/logging/log.h>
25+
26+
LOG_MODULE_REGISTER(dac_tx311, CONFIG_DAC_LOG_LEVEL);
27+
28+
#define DACX311_MIN_RESOLUTION 8U
29+
#define DACX311_MAX_RESOLUTION 14U
30+
31+
#define DAC8311_RESOLUTION 14U
32+
#define DAC7311_RESOLUTION 12U
33+
#define DAC6311_RESOLUTION 10U
34+
#define DAC5311_RESOLUTION 8U
35+
36+
#define DACX311_MAX_CHANNEL 1U
37+
38+
#define DACX311_SPI_HZ_MAX (5*1000*1000)
39+
40+
struct dacx311_config {
41+
struct spi_dt_spec bus;
42+
uint8_t resolution;
43+
uint8_t power_down_mode;
44+
};
45+
46+
struct dacx311_data {
47+
uint8_t resolution;
48+
uint16_t power_down_mode;
49+
uint8_t configured;
50+
};
51+
52+
static int dacx311_reg_write(const struct device *dev, uint16_t val)
53+
{
54+
const struct dacx311_config *cfg = dev->config;
55+
uint8_t tx_bytes[2];
56+
57+
/* Construct write buffer for SPI API */
58+
const struct spi_buf tx_buf[1] = {
59+
{
60+
.buf = tx_bytes,
61+
.len = sizeof(tx_bytes)
62+
}
63+
};
64+
const struct spi_buf_set tx = {
65+
.buffers = tx_buf,
66+
.count = ARRAY_SIZE(tx_buf)
67+
};
68+
69+
/* Set register bits */
70+
tx_bytes[0] = val >> 8;
71+
tx_bytes[1] = val & 0xFF;
72+
73+
/* Write to bus */
74+
return spi_write_dt(&cfg->bus, &tx);
75+
}
76+
77+
static int dacx311_channel_setup(const struct device *dev,
78+
const struct dac_channel_cfg *channel_cfg)
79+
{
80+
struct dacx311_data *data = dev->data;
81+
82+
/* Validate configuration */
83+
if (channel_cfg->channel_id > (DACX311_MAX_CHANNEL - 1)) {
84+
LOG_ERR("Unsupported channel %d", channel_cfg->channel_id);
85+
return -ENOTSUP;
86+
}
87+
88+
if (channel_cfg->internal) {
89+
LOG_ERR("Internal channels not supported");
90+
return -ENOTSUP;
91+
}
92+
93+
if (data->configured & BIT(channel_cfg->channel_id)) {
94+
LOG_DBG("Channel %d already configured", channel_cfg->channel_id);
95+
return 0;
96+
}
97+
98+
/* Mark channel as configured */
99+
data->configured |= BIT(channel_cfg->channel_id);
100+
101+
LOG_DBG("Channel %d initialized", channel_cfg->channel_id);
102+
103+
return 0;
104+
}
105+
106+
static int dacx311_write_value(const struct device *dev, uint8_t channel,
107+
uint32_t value)
108+
{
109+
struct dacx311_data *data = dev->data;
110+
uint16_t regval;
111+
uint8_t shift;
112+
int ret;
113+
114+
const bool brdcast = (channel == DAC_CHANNEL_BROADCAST) ? 1 : 0;
115+
116+
if (!brdcast && (channel > (DACX311_MAX_CHANNEL - 1))) {
117+
LOG_ERR("Unsupported channel %d", channel);
118+
return -ENOTSUP;
119+
}
120+
121+
/*
122+
* Check if channel is initialized
123+
* If broadcast channel is used, check if any channel is initialized
124+
*/
125+
if ((brdcast && !data->configured) ||
126+
(channel < DACX311_MAX_CHANNEL && !(data->configured & BIT(channel)))) {
127+
LOG_ERR("Channel %d not initialized", channel);
128+
return -EINVAL;
129+
}
130+
131+
if (value >= (1 << (data->resolution))) {
132+
LOG_ERR("Value %d out of range", value);
133+
return -EINVAL;
134+
}
135+
136+
/*
137+
* (See https://www.ti.com/document-viewer/dac6311/datasheet)
138+
*
139+
* Shift given value to align MSB bit position to register bit 13.
140+
*
141+
* DAC output register format:
142+
*
143+
* | 15 14 | 13 12 11 10 9 8 7 6 5 4 3 2 1 0 |
144+
* |-------|---------------------------------------------------|
145+
* | Mode | 8311[13:0] / 7311[13:2] / 6311[13:4] / 5311[13:6] |
146+
*/
147+
shift = DACX311_MAX_RESOLUTION - data->resolution;
148+
regval = value << shift;
149+
150+
/*
151+
* Set mode bits to value taken from configuration.
152+
*
153+
* MODE = 0 0 -> Normal Operation
154+
* 0 1 -> Output 1 kΩ to GND
155+
* 1 0 -> Output 100 kΩ to GND
156+
* 1 1 -> High-Z
157+
*/
158+
regval &= 0x3FFF;
159+
regval |= data->power_down_mode;
160+
161+
/* Write to output */
162+
ret = dacx311_reg_write(dev, regval);
163+
if (ret) {
164+
LOG_ERR("Unable to set value %d on channel %d", value, channel);
165+
return -EIO;
166+
}
167+
168+
return 0;
169+
}
170+
171+
static int dacx311_init(const struct device *dev)
172+
{
173+
const struct dacx311_config *config = dev->config;
174+
struct dacx311_data *data = dev->data;
175+
176+
/* Set bit resolution for this chip variant */
177+
data->resolution = config->resolution;
178+
179+
/* Set the power mode (shifted to upper bits) */
180+
data->power_down_mode = FIELD_PREP(BIT_MASK(2) << DACX311_MAX_RESOLUTION,
181+
config->power_down_mode);
182+
183+
return 0;
184+
}
185+
186+
static DEVICE_API(dac, dacx311_driver_api) = {
187+
.channel_setup = dacx311_channel_setup,
188+
.write_value = dacx311_write_value
189+
};
190+
191+
#define INST_DT_DACX311(inst, t) DT_INST(inst, ti_dac##t)
192+
193+
#define DACX311_DEVICE(t, n, res) \
194+
BUILD_ASSERT(DT_INST_ENUM_IDX(n, power_down_mode) <= 3, \
195+
"Invalid power down mode");\
196+
BUILD_ASSERT(DT_PROP(INST_DT_DACX311(n, t), spi_max_frequency) \
197+
<= DACX311_SPI_HZ_MAX, "Invalid SPI frequency");\
198+
static struct dacx311_data dac##t##_data_##n; \
199+
static const struct dacx311_config dac##t##_config_##n = { \
200+
.bus = SPI_DT_SPEC_GET(INST_DT_DACX311(n, t), \
201+
SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | \
202+
SPI_MODE_CPHA | \
203+
SPI_WORD_SET(16), 0), \
204+
.resolution = res, \
205+
.power_down_mode = DT_INST_ENUM_IDX(n, power_down_mode), \
206+
}; \
207+
DEVICE_DT_DEFINE(INST_DT_DACX311(n, t), \
208+
dacx311_init, NULL, \
209+
&dac##t##_data_##n, \
210+
&dac##t##_config_##n, POST_KERNEL, \
211+
CONFIG_DAC_INIT_PRIORITY, \
212+
&dacx311_driver_api)
213+
214+
/*
215+
* DAC8311: 14-bit
216+
*/
217+
#define DAC8311_DEVICE(n) DACX311_DEVICE(8311, n, DAC8311_RESOLUTION)
218+
/*
219+
* DAC7311: 12-bit
220+
*/
221+
#define DAC7311_DEVICE(n) DACX311_DEVICE(7311, n, DAC7311_RESOLUTION)
222+
/*
223+
* DAC6311: 10-bit
224+
*/
225+
#define DAC6311_DEVICE(n) DACX311_DEVICE(6311, n, DAC6311_RESOLUTION)
226+
/*
227+
* DAC5311: 8-bit
228+
*/
229+
#define DAC5311_DEVICE(n) DACX311_DEVICE(5311, n, DAC5311_RESOLUTION)
230+
231+
#define CALL_WITH_ARG(arg, expr) expr(arg)
232+
233+
#define INST_DT_DACX311_FOREACH(t, inst_expr) \
234+
LISTIFY(DT_NUM_INST_STATUS_OKAY(ti_dac##t), \
235+
CALL_WITH_ARG, (), inst_expr)
236+
237+
#define DT_DRV_COMPAT ti_dac8311
238+
INST_DT_DACX311_FOREACH(8311, DAC8311_DEVICE);
239+
#undef DT_DRV_COMPAT
240+
#define DT_DRV_COMPAT ti_dac7311
241+
INST_DT_DACX311_FOREACH(7311, DAC7311_DEVICE);
242+
#undef DT_DRV_COMPAT
243+
#define DT_DRV_COMPAT ti_dac6311
244+
INST_DT_DACX311_FOREACH(6311, DAC6311_DEVICE);
245+
#undef DT_DRV_COMPAT
246+
#define DT_DRV_COMPAT ti_dac5311
247+
INST_DT_DACX311_FOREACH(5311, DAC5311_DEVICE);
248+
#undef DT_DRV_COMPAT

dts/bindings/dac/ti,dac5311.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright (c) 2025 Andreas Wolf <[email protected]>
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: Texas Instrument 8-Bit DAC 5311
5+
6+
compatible: "ti,dac5311"
7+
8+
include: ti,dacx311-base.yaml

dts/bindings/dac/ti,dac6311.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright (c) 2025 Andreas Wolf <[email protected]>
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: Texas Instrument 10-Bit DAC 6311
5+
6+
compatible: "ti,dac6311"
7+
8+
include: ti,dacx311-base.yaml

dts/bindings/dac/ti,dac7311.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright (c) 2025 Andreas Wolf <[email protected]>
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: Texas Instrument 12-Bit DAC 7311
5+
6+
compatible: "ti,dac7311"
7+
8+
include: ti,dacx311-base.yaml

dts/bindings/dac/ti,dac8311.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright (c) 2025 Andreas Wolf <[email protected]>
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: Texas Instrument 14-Bit DAC 8311
5+
6+
compatible: "ti,dac8311"
7+
8+
include: ti,dacx311-base.yaml
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Copyright (c) 2025 Andreas Wolf <[email protected]>
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
include: [dac-controller.yaml, spi-device.yaml]
5+
6+
properties:
7+
"#io-channel-cells":
8+
const: 1
9+
10+
power-down-mode:
11+
type: string
12+
default: "normal"
13+
enum:
14+
- "normal"
15+
- "power-down-1k"
16+
- "power-down-100k"
17+
- "power-down-3-state"
18+
description: |
19+
Power-down mode select.
20+
- Normal mode (reg: 0).
21+
- 1 kOhm output impedance (reg: 1).
22+
- 100 kOhm output impedance (reg: 2).
23+
- Three-state output impedance (reg: 3).
24+
The default corresponds to the reset value of the register field.
25+
26+
io-channel-cells:
27+
- output

tests/drivers/build_all/dac/app.overlay

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@
125125
<&test_gpio 0 0>,
126126
<&test_gpio 0 0>,
127127
<&test_gpio 0 0>,
128+
<&test_gpio 0 0>,
128129
<&test_gpio 0 0>;
129130

130131
test_spi_dac60508: dac60508@0 {
@@ -331,6 +332,13 @@
331332
spi-max-frequency = <0>;
332333
#io-channel-cells = <1>;
333334
};
335+
336+
test_spi_dac6311: dac6311@16 {
337+
compatible = "ti,dac6311";
338+
reg = <0x16>;
339+
spi-max-frequency = <0>;
340+
#io-channel-cells = <1>;
341+
};
334342
};
335343
};
336344
};

0 commit comments

Comments
 (0)