From 54e4e2811b92efd45cbe9f58bb852fc25e9075df Mon Sep 17 00:00:00 2001 From: sarpadi Date: Fri, 3 Oct 2025 10:28:31 +0300 Subject: [PATCH 01/11] ad9740: Add dts --- .../dts/xilinx/zynq-zed-adv7511-ad9740.dts | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts new file mode 100644 index 00000000000000..8c047738cbe737 --- /dev/null +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2025 Analog Devices Inc. */ + +/dts-v1/; + +#include "zynq-zed.dtsi" +#include "zynq-zed-adv7511.dtsi" + +/ { + + vcc: regulator-vref { + compatible = "regulator-fixed"; + regulator-name = "vcc"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + clocks { + adf4351_clkin: clock@1 { + #clock-cells = <0x0>; + compatible = "fixed-clock"; + clock-frequency = <10000000>; + clock-output-names = "refclk"; + }; + }; +}; + + &spi0 { + status="okay"; + + adf4351: adf4351@0 { + compatible = "adi,adf4351"; + reg = <0>; + + spi-max-frequency = <10000000>; + + clocks = <&adf4351_clkin>; + clock-names = "clkin"; + + adi,channel-spacing = <10000>; + adi,power-up-frequency = <210000000>; + adi,phase-detector-polarity-positive-enable; + adi,charge-pump-current = <2500>; + adi,output-power = <3>; + adi,mute-till-lock-enable; + adi,muxout-select = <6>; + }; + }; From ca742216b2bc04e49514bf878e5689b9ddca8dd3 Mon Sep 17 00:00:00 2001 From: sarpadi Date: Tue, 7 Oct 2025 11:01:01 +0300 Subject: [PATCH 02/11] ad9740: enable aux output; set f=128MHz --- arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts index 8c047738cbe737..8b64e754af3269 100644 --- a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts @@ -39,11 +39,13 @@ clock-names = "clkin"; adi,channel-spacing = <10000>; - adi,power-up-frequency = <210000000>; + adi,power-up-frequency = <128000000>; adi,phase-detector-polarity-positive-enable; adi,charge-pump-current = <2500>; adi,output-power = <3>; adi,mute-till-lock-enable; + adi,aux-output-enable; + adi,aux-output-power = <3>; adi,muxout-select = <6>; }; }; From da812e1424776f771a83c45b015d1edd952c28f5 Mon Sep 17 00:00:00 2001 From: sarpadi Date: Tue, 21 Oct 2025 15:05:07 +0300 Subject: [PATCH 03/11] ad9740: Fix adf dts --- .../dts/xilinx/zynq-zed-adv7511-ad9740.dts | 84 ++++- drivers/iio/dac/Kconfig | 9 + drivers/iio/dac/Makefile | 1 + drivers/iio/dac/ad9740.c | 315 ++++++++++++++++++ 4 files changed, 398 insertions(+), 11 deletions(-) create mode 100644 drivers/iio/dac/ad9740.c diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts index 8b64e754af3269..3cd811367e2e66 100644 --- a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts @@ -8,25 +8,24 @@ / { - vcc: regulator-vref { - compatible = "regulator-fixed"; - regulator-name = "vcc"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-always-on; - }; - clocks { adf4351_clkin: clock@1 { - #clock-cells = <0x0>; + #clock-cells = <0>; compatible = "fixed-clock"; clock-frequency = <10000000>; clock-output-names = "refclk"; }; + + adf4351_clkout: clock@2 { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <128000000>; + clock-output-names = "adf4351_clkout"; + }; }; }; - &spi0 { +&spi0 { status="okay"; adf4351: adf4351@0 { @@ -43,9 +42,72 @@ adi,phase-detector-polarity-positive-enable; adi,charge-pump-current = <2500>; adi,output-power = <3>; - adi,mute-till-lock-enable; adi,aux-output-enable; adi,aux-output-power = <3>; adi,muxout-select = <6>; }; }; + +&fpga_axi { + + dac_tx_dma: dma-controller@44a30000 { + compatible = "adi,axi-dmac-1.00.a"; + reg = <0x44a40000 0x10000>; + #dma-cells = <1>; + interrupt-parent = <&intc>; + interrupts = <0 57 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clkc 15>; + + adi,channels { + #size-cells = <0>; + #address-cells = <1>; + + dma-channel@0 { + reg = <0>; + adi,source-bus-width = <32>; + adi,source-bus-type = <0>; + adi,destination-bus-width = <32>; + adi,destination-bus-type = <1>; + }; + }; + }; + + axi_dac: spi@44a70000 { + compatible = "adi,axi-ad3552r"; + reg = <0x44a70000 0x1000>; + dmas = <&dac_tx_dma 0>; + dma-names = "tx"; + #io-backend-cells = <0>; + clocks = <&clkc 15>, <&adf4351_clkout>; + clock-names = "s_axi_aclk", "dac_clk"; + + #address-cells = <1>; + #size-cells = <0>; + + dac@0 { + reg = <0>; + compatible = "adi,ad3552r"; + reset-gpios = <&gpio0 92 GPIO_ACTIVE_LOW>; + io-backends = <&axi_dac>; + + clocks = <&adf4351_clkout>; + + spi-3-wire; + spi-tx-bus-width = <4>; + spi-rx-bus-width = <4>; + + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + adi,output-range-microvolt = <0 5000000>; + }; + + channel@1 { + reg = <1>; + adi,output-range-microvolt = <0 5000000>; + }; + }; + }; +}; diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 6b97fefd805784..3b031af1462656 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -6,6 +6,15 @@ menu "Digital to analog converters" +config AD9740 + tristate "Analog Devices AD9740 and Similar DACs driver" + help + Say yes here to build support for Analog Devices AD9740 + Digital to Analog Converter. + + To compile this driver as a module, choose M here: the + module will be called ad9740. + config AD3530R tristate "Analog Devices AD3530R and Similar DACs driver" depends on SPI diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 4b0a840d8610b7..7490d3f951f45a 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -4,6 +4,7 @@ # # When adding new entries keep the list in alphabetical order +obj-$(CONFIG_AD3530R) += ad9740.o obj-$(CONFIG_AD3530R) += ad3530r.o obj-$(CONFIG_AD3552R_HS) += ad3552r-hs.o obj-$(CONFIG_AD3552R_LIB) += ad3552r-common.o diff --git a/drivers/iio/dac/ad9740.c b/drivers/iio/dac/ad9740.c new file mode 100644 index 00000000000000..94a020b89ff11f --- /dev/null +++ b/drivers/iio/dac/ad9740.c @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices AD3552R + * Digital to Analog converter driver, High Speed version + * + * Copyright 2024 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ad3552r.h" +#include "ad3552r-hs.h" + +/* + * Important notes for register map access: + * ======================================== + * + * Register address space is divided in 2 regions, primary (config) and + * secondary (DAC). Primary region can only be accessed in simple SPI mode, + * with exception for ad355x models where setting QSPI pin high allows QSPI + * access to both the regions. + * + * Due to the fact that ad3541/2r do not implement QSPI, for proper device + * detection, HDL keeps "QSPI" pin level low at boot (see ad3552r manual, rev B + * table 7, pin 31, digital input). For this reason, actually the working mode + * between SPI, DSPI and QSPI must be set via software, configuring the target + * DAC appropriately, together with the backend API to configure the bus mode + * accordingly. + * + * Also, important to note that none of the three modes allow to read in DDR. + * + * In non-buffering operations, mode is set to simple SPI SDR for all primary + * and secondary region r/w accesses, to avoid to switch the mode each time DAC + * register is accessed (raw accesses, r/w), and to be able to dump registers + * content (possible as non DDR only). + * In buffering mode, driver sets best possible mode, D/QSPI and DDR. + */ + +struct ad3552r_hs_state { + const struct ad3552r_model_data *model_data; + struct gpio_desc *reset_gpio; + struct device *dev; + struct iio_backend *back; + bool single_channel; + struct ad3552r_ch_data ch_data[AD3552R_MAX_CH]; + struct ad3552r_hs_platform_data *data; + /* INTERFACE_CONFIG_D register cache, in DDR we cannot read values. */ + u32 config_d; + /* Protects backend I/O operations from concurrent accesses. */ + struct mutex lock; +}; + +enum ad3552r_sources { + AD3552R_SRC_NORMAL, + AD3552R_SRC_RAMP_16BIT, +}; + +static const char * const dbgfs_attr_source[] = { + [AD3552R_SRC_NORMAL] = "normal", + [AD3552R_SRC_RAMP_16BIT] = "ramp-16bit", +}; + +static int ad3552r_hs_set_data_source(struct ad3552r_hs_state *st, + enum iio_backend_data_source type) +{ + int ret; + + ret = iio_backend_data_source_set(st->back, 0, type); + + return 0; +} + + +static int ad3552r_hs_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + return 0; +} + +static int ad3552r_hs_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + + return 0; +} + +static int ad3552r_hs_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad3552r_hs_state *st = iio_priv(indio_dev); + struct iio_backend_data_fmt fmt = { + .type = IIO_BACKEND_DATA_UNSIGNED + }; + int loop_len, val, ret; + + ret = iio_backend_data_transfer_addr(st->back, val); + + ret = iio_backend_data_format_set(st->back, 0, &fmt); + + ret = iio_backend_data_stream_enable(st->back); + + return 0; +} + +static int ad3552r_hs_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad3552r_hs_state *st = iio_priv(indio_dev); + int ret; + + ret = iio_backend_data_stream_disable(st->back); + if (ret) + return ret; + + return 0; +} + + + + +static ssize_t ad3552r_hs_show_data_source(struct file *f, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct ad3552r_hs_state *st = file_inode(f)->i_private; + enum iio_backend_data_source type; + int idx, ret; + + ret = iio_backend_data_source_get(st->back, 0, &type); + if (ret) + return ret; + +// return simple_read_from_buffer(userbuf, count, ppos, +// dbgfs_attr_source[idx], +// strlen(dbgfs_attr_source[idx])); + return 0; +} + +static ssize_t ad3552r_hs_write_data_source(struct file *f, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct ad3552r_hs_state *st = file_inode(f)->i_private; + char buf[64]; + int ret, source; + + ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, userbuf, + count); + if (ret < 0) + return ret; + + buf[count] = '\0'; + + return 0; +} + +static ssize_t ad3552r_hs_show_data_source_avail(struct file *f, + char __user *userbuf, + size_t count, loff_t *ppos) +{ + ssize_t len = 0; + char buf[128]; + int i; + + for (i = 0; i < ARRAY_SIZE(dbgfs_attr_source); i++) { + len += scnprintf(buf + len, PAGE_SIZE - len, "%s ", + dbgfs_attr_source[i]); + } + buf[len - 1] = '\n'; + + return simple_read_from_buffer(userbuf, count, ppos, buf, len); +} + +static const struct file_operations ad3552r_hs_data_source_fops = { + .owner = THIS_MODULE, + .write = ad3552r_hs_write_data_source, + .read = ad3552r_hs_show_data_source, +}; + +static const struct file_operations ad3552r_hs_data_source_avail_fops = { + .owner = THIS_MODULE, + .read = ad3552r_hs_show_data_source_avail, +}; + +static int ad3552r_hs_setup(struct ad3552r_hs_state *st) +{ + u16 id; + u16 gain = 0, offset = 0; + u32 ch, val, range; + int ret; + + ret = ad3552r_hs_set_data_source(st, IIO_BACKEND_EXTERNAL); + if (ret) + return ret; + + val = ret; + + return 0; +} + +static const struct iio_buffer_setup_ops ad3552r_hs_buffer_setup_ops = { + .postenable = ad3552r_hs_buffer_postenable, + .predisable = ad3552r_hs_buffer_predisable, +}; + +#define AD3552R_CHANNEL(ch) { \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .output = 1, \ + .indexed = 1, \ + .channel = (ch), \ + .scan_index = (ch), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + } \ +} + +static const struct iio_chan_spec ad3552r_hs_channels[] = { + AD3552R_CHANNEL(0), +}; + +static const struct iio_info ad3552r_hs_info = { + .read_raw = &ad3552r_hs_read_raw, + .write_raw = &ad3552r_hs_write_raw, +}; + + +static int ad3552r_hs_probe(struct platform_device *pdev) +{ + struct ad3552r_hs_state *st; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->dev = &pdev->dev; + + st->data = dev_get_platdata(st->dev); + if (!st->data) + return dev_err_probe(st->dev, -ENODEV, "No platform data !"); + + st->back = devm_iio_backend_get(&pdev->dev, NULL); + if (IS_ERR(st->back)) + return PTR_ERR(st->back); + + ret = devm_iio_backend_enable(&pdev->dev, st->back); + if (ret) + return ret; + + st->model_data = device_get_match_data(&pdev->dev); + if (!st->model_data) + return -ENODEV; + + indio_dev->name = "ad3552r"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->setup_ops = &ad3552r_hs_buffer_setup_ops; + indio_dev->channels = ad3552r_hs_channels; + indio_dev->num_channels = ARRAY_SIZE(ad3552r_hs_channels); + indio_dev->info = &ad3552r_hs_info; + + ret = devm_iio_backend_request_buffer(&pdev->dev, st->back, indio_dev); + if (ret) + return ret; + + ret = ad3552r_hs_setup(st); + if (ret) + return ret; + + ret = devm_iio_device_register(&pdev->dev, indio_dev); + if (ret) + return ret; + + return ret; +} + +static const struct of_device_id ad3552r_hs_of_id[] = { + { .compatible = "adi,ad3541r", .data = &ad3541r_model_data }, + { .compatible = "adi,ad3542r", .data = &ad3542r_model_data }, + { .compatible = "adi,ad3551r", .data = &ad3551r_model_data }, + { .compatible = "adi,ad3552r", .data = &ad3552r_model_data }, + { } +}; +MODULE_DEVICE_TABLE(of, ad3552r_hs_of_id); + +static struct platform_driver ad3552r_hs_driver = { + .driver = { + .name = "ad3552r-hs", + .of_match_table = ad3552r_hs_of_id, + }, + .probe = ad3552r_hs_probe, +}; +module_platform_driver(ad3552r_hs_driver); + +MODULE_AUTHOR("Dragos Bogdan "); +MODULE_AUTHOR("Angelo Dureghello "); +MODULE_DESCRIPTION("AD3552R Driver - High Speed version"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_BACKEND); +MODULE_IMPORT_NS(IIO_AD3552R); From 1fff25ac7494d59d8ab19e7c386f91acd49d2af6 Mon Sep 17 00:00:00 2001 From: sarpadi Date: Tue, 21 Oct 2025 22:47:56 +0300 Subject: [PATCH 04/11] ad9740: Cleanup --- .../dts/xilinx/zynq-zed-adv7511-ad9740.dts | 29 +- drivers/iio/dac/Kconfig | 8 +- drivers/iio/dac/Makefile | 2 +- drivers/iio/dac/ad9740.c | 541 ++++++++++++------ drivers/iio/dac/adi-axi-dac.c | 9 + 5 files changed, 398 insertions(+), 191 deletions(-) diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts index 3cd811367e2e66..2fa603c299834f 100644 --- a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts @@ -50,7 +50,7 @@ &fpga_axi { - dac_tx_dma: dma-controller@44a30000 { + dac_tx_dma: dma-controller@44a40000 { compatible = "adi,axi-dmac-1.00.a"; reg = <0x44a40000 0x10000>; #dma-cells = <1>; @@ -72,8 +72,8 @@ }; }; - axi_dac: spi@44a70000 { - compatible = "adi,axi-ad3552r"; + axi_dac: axi-ad9740@44a70000 { + compatible = "adi,axi-ad9740"; reg = <0x44a70000 0x1000>; dmas = <&dac_tx_dma 0>; dma-names = "tx"; @@ -86,28 +86,11 @@ dac@0 { reg = <0>; - compatible = "adi,ad3552r"; + compatible = "adi,ad9740"; reset-gpios = <&gpio0 92 GPIO_ACTIVE_LOW>; io-backends = <&axi_dac>; - - clocks = <&adf4351_clkout>; - - spi-3-wire; - spi-tx-bus-width = <4>; - spi-rx-bus-width = <4>; - - #address-cells = <1>; - #size-cells = <0>; - - channel@0 { - reg = <0>; - adi,output-range-microvolt = <0 5000000>; - }; - - channel@1 { - reg = <1>; - adi,output-range-microvolt = <0 5000000>; - }; + /* Data format: use offset binary (false) or 2's complement (true) */ + /* adi,twos-complement; */ }; }; }; diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 3b031af1462656..c7e6204a569eec 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -7,10 +7,14 @@ menu "Digital to analog converters" config AD9740 - tristate "Analog Devices AD9740 and Similar DACs driver" + tristate "Analog Devices AD9740 DAC driver" + select IIO_BACKEND help Say yes here to build support for Analog Devices AD9740 - Digital to Analog Converter. + 10-Bit, 210 MSPS Digital-to-Analog Converter. + + The driver requires the assistance of an FPGA IP core (AXI DAC) + to operate, since data is streamed into the device via DMA. To compile this driver as a module, choose M here: the module will be called ad9740. diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 7490d3f951f45a..cf8a7c5f2e6ed2 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -4,7 +4,7 @@ # # When adding new entries keep the list in alphabetical order -obj-$(CONFIG_AD3530R) += ad9740.o +obj-$(CONFIG_AD9740) += ad9740.o obj-$(CONFIG_AD3530R) += ad3530r.o obj-$(CONFIG_AD3552R_HS) += ad3552r-hs.o obj-$(CONFIG_AD3552R_LIB) += ad3552r-common.o diff --git a/drivers/iio/dac/ad9740.c b/drivers/iio/dac/ad9740.c index 94a020b89ff11f..47a325fb543ad6 100644 --- a/drivers/iio/dac/ad9740.c +++ b/drivers/iio/dac/ad9740.c @@ -1,13 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Analog Devices AD3552R - * Digital to Analog converter driver, High Speed version + * Analog Devices AD9740 + * 10-Bit, 210 MSPS Digital-to-Analog Converter * - * Copyright 2024 Analog Devices Inc. + * Copyright 2025 Analog Devices Inc. */ #include -#include #include #include #include @@ -15,301 +14,513 @@ #include #include #include -#include -#include "ad3552r.h" -#include "ad3552r-hs.h" +#include /* - * Important notes for register map access: - * ======================================== - * - * Register address space is divided in 2 regions, primary (config) and - * secondary (DAC). Primary region can only be accessed in simple SPI mode, - * with exception for ad355x models where setting QSPI pin high allows QSPI - * access to both the regions. - * - * Due to the fact that ad3541/2r do not implement QSPI, for proper device - * detection, HDL keeps "QSPI" pin level low at boot (see ad3552r manual, rev B - * table 7, pin 31, digital input). For this reason, actually the working mode - * between SPI, DSPI and QSPI must be set via software, configuring the target - * DAC appropriately, together with the backend API to configure the bus mode - * accordingly. - * - * Also, important to note that none of the three modes allow to read in DDR. - * - * In non-buffering operations, mode is set to simple SPI SDR for all primary - * and secondary region r/w accesses, to avoid to switch the mode each time DAC - * register is accessed (raw accesses, r/w), and to be able to dump registers - * content (possible as non DDR only). - * In buffering mode, driver sets best possible mode, D/QSPI and DDR. + * AD9740 Register Map */ +#define AD9740_REG_FSC_MSB 0x00 +#define AD9740_REG_FSC_LSB 0x01 +#define AD9740_REG_MODE 0x02 +#define AD9740_REG_POWER 0x03 -struct ad3552r_hs_state { - const struct ad3552r_model_data *model_data; - struct gpio_desc *reset_gpio; +/* MODE register bits */ +#define AD9740_MODE_MIX_MODE BIT(7) +#define AD9740_MODE_2S_COMPLEMENT BIT(6) + +/* POWER register bits */ +#define AD9740_POWER_DOWN BIT(0) + +struct ad9740_state { struct device *dev; struct iio_backend *back; - bool single_channel; - struct ad3552r_ch_data ch_data[AD3552R_MAX_CH]; - struct ad3552r_hs_platform_data *data; - /* INTERFACE_CONFIG_D register cache, in DDR we cannot read values. */ - u32 config_d; + struct spi_device *spi; + struct gpio_desc *reset_gpio; + /* Full-scale current setting (8-20 mA) */ + unsigned int fsc_ua; + /* Data format: true = 2's complement, false = offset binary */ + bool twos_complement; /* Protects backend I/O operations from concurrent accesses. */ struct mutex lock; }; -enum ad3552r_sources { - AD3552R_SRC_NORMAL, - AD3552R_SRC_RAMP_16BIT, +enum ad9740_sources { + AD9740_SRC_NORMAL, + AD9740_SRC_DDS, + AD9740_SRC_RAMP, }; static const char * const dbgfs_attr_source[] = { - [AD3552R_SRC_NORMAL] = "normal", - [AD3552R_SRC_RAMP_16BIT] = "ramp-16bit", + [AD9740_SRC_NORMAL] = "normal", + [AD9740_SRC_DDS] = "dds", + [AD9740_SRC_RAMP] = "ramp", }; -static int ad3552r_hs_set_data_source(struct ad3552r_hs_state *st, - enum iio_backend_data_source type) +static int ad9740_set_data_source(struct ad9740_state *st, + enum iio_backend_data_source type) { int ret; + const char *type_str; + + switch (type) { + case IIO_BACKEND_INTERNAL_CONTINUOUS_WAVE: + type_str = "DDS"; + break; + case IIO_BACKEND_INTERNAL_RAMP_16BIT: + type_str = "RAMP"; + break; + case IIO_BACKEND_EXTERNAL: + type_str = "DMA"; + break; + default: + type_str = "UNKNOWN"; + break; + } + + dev_info(st->dev, "Setting data source to %s (type=%d)\n", type_str, type); ret = iio_backend_data_source_set(st->back, 0, type); + if (ret) + dev_err(st->dev, "Failed to set data source to %s: %d\n", type_str, ret); + else + dev_info(st->dev, "Data source set to %s successfully\n", type_str); - return 0; + return ret; } - -static int ad3552r_hs_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, long mask) +static int ad9740_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { - return 0; + return -EINVAL; } -static int ad3552r_hs_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) +static int ad9740_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) { - - return 0; + return -EINVAL; } -static int ad3552r_hs_buffer_postenable(struct iio_dev *indio_dev) +static int ad9740_buffer_postenable(struct iio_dev *indio_dev) { - struct ad3552r_hs_state *st = iio_priv(indio_dev); + struct ad9740_state *st = iio_priv(indio_dev); struct iio_backend_data_fmt fmt = { - .type = IIO_BACKEND_DATA_UNSIGNED + .sign_extend = false, + .enable = true, }; - int loop_len, val, ret; + int ret; + + dev_info(st->dev, "========================================\n"); + dev_info(st->dev, "Buffer enable requested - starting DMA streaming\n"); + dev_info(st->dev, "========================================\n"); - ret = iio_backend_data_transfer_addr(st->back, val); + mutex_lock(&st->lock); + + /* Configure data format based on DT setting */ + if (st->twos_complement) { + fmt.type = IIO_BACKEND_TWOS_COMPLEMENT; + dev_info(st->dev, "Configuring data format: 2's complement\n"); + } else { + fmt.type = IIO_BACKEND_OFFSET_BINARY; + dev_info(st->dev, "Configuring data format: offset binary\n"); + } + dev_info(st->dev, "Setting backend data format (10-bit in 16-bit container)\n"); + /* Configure data format: 10-bit in 16-bit container */ ret = iio_backend_data_format_set(st->back, 0, &fmt); + if (ret) { + dev_err(st->dev, "Failed to set data format: %d\n", ret); + goto err_unlock; + } + dev_info(st->dev, "Data format configured successfully\n"); + /* Enable data streaming from DMA to DAC core */ + dev_info(st->dev, "Enabling backend data stream\n"); ret = iio_backend_data_stream_enable(st->back); + if (ret) { + dev_err(st->dev, "Failed to enable data stream: %d\n", ret); + goto err_unlock; + } + mutex_unlock(&st->lock); + dev_info(st->dev, "========================================\n"); + dev_info(st->dev, "Buffer enabled - DMA streaming ACTIVE\n"); + dev_info(st->dev, "========================================\n"); return 0; + +err_unlock: + mutex_unlock(&st->lock); + dev_err(st->dev, "Buffer enable FAILED\n"); + return ret; } -static int ad3552r_hs_buffer_predisable(struct iio_dev *indio_dev) +static int ad9740_buffer_predisable(struct iio_dev *indio_dev) { - struct ad3552r_hs_state *st = iio_priv(indio_dev); + struct ad9740_state *st = iio_priv(indio_dev); int ret; + dev_info(st->dev, "========================================\n"); + dev_info(st->dev, "Buffer disable requested - stopping DMA streaming\n"); + dev_info(st->dev, "========================================\n"); + + mutex_lock(&st->lock); + + /* Disable data streaming */ + dev_info(st->dev, "Disabling backend data stream\n"); ret = iio_backend_data_stream_disable(st->back); - if (ret) + if (ret) { + dev_err(st->dev, "Failed to disable data stream: %d\n", ret); + mutex_unlock(&st->lock); + dev_err(st->dev, "Buffer disable FAILED\n"); return ret; + } + mutex_unlock(&st->lock); + dev_info(st->dev, "========================================\n"); + dev_info(st->dev, "Buffer disabled - DMA streaming STOPPED\n"); + dev_info(st->dev, "========================================\n"); return 0; } - - - -static ssize_t ad3552r_hs_show_data_source(struct file *f, char __user *userbuf, - size_t count, loff_t *ppos) +/* IIO extended info for data source control */ +static ssize_t ad9740_ext_info_get_data_source(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) { - struct ad3552r_hs_state *st = file_inode(f)->i_private; + struct ad9740_state *st = iio_priv(indio_dev); enum iio_backend_data_source type; - int idx, ret; + const char *src_str; + int ret; + + dev_dbg(st->dev, "Reading data_source attribute\n"); ret = iio_backend_data_source_get(st->back, 0, &type); - if (ret) + if (ret) { + dev_err(st->dev, "Failed to get data source: %d\n", ret); return ret; + } -// return simple_read_from_buffer(userbuf, count, ppos, -// dbgfs_attr_source[idx], -// strlen(dbgfs_attr_source[idx])); - return 0; + switch (type) { + case IIO_BACKEND_INTERNAL_CONTINUOUS_WAVE: + src_str = dbgfs_attr_source[AD9740_SRC_DDS]; + break; + case IIO_BACKEND_INTERNAL_RAMP_16BIT: + src_str = dbgfs_attr_source[AD9740_SRC_RAMP]; + break; + case IIO_BACKEND_EXTERNAL: + default: + src_str = dbgfs_attr_source[AD9740_SRC_NORMAL]; + break; + } + + dev_dbg(st->dev, "Current data source: %s (type=%d)\n", src_str, type); + + return sysfs_emit(buf, "%s\n", src_str); } -static ssize_t ad3552r_hs_write_data_source(struct file *f, - const char __user *userbuf, - size_t count, loff_t *ppos) +static ssize_t ad9740_ext_info_set_data_source(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) { - struct ad3552r_hs_state *st = file_inode(f)->i_private; - char buf[64]; - int ret, source; + struct ad9740_state *st = iio_priv(indio_dev); + enum iio_backend_data_source type; + int ret; - ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, userbuf, - count); - if (ret < 0) - return ret; + dev_info(st->dev, "User requesting data source change to: '%s'\n", buf); + + if (sysfs_streq(buf, "normal")) + type = IIO_BACKEND_EXTERNAL; + else if (sysfs_streq(buf, "dds")) + type = IIO_BACKEND_INTERNAL_CONTINUOUS_WAVE; + else if (sysfs_streq(buf, "ramp")) + type = IIO_BACKEND_INTERNAL_RAMP_16BIT; + else { + dev_err(st->dev, "Invalid data source '%s'. Valid: normal, dds, ramp\n", buf); + return -EINVAL; + } - buf[count] = '\0'; + ret = ad9740_set_data_source(st, type); + if (ret) + return ret; - return 0; + return len; } -static ssize_t ad3552r_hs_show_data_source_avail(struct file *f, - char __user *userbuf, - size_t count, loff_t *ppos) +static ssize_t ad9740_ext_info_get_data_source_available(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) { ssize_t len = 0; - char buf[128]; int i; - for (i = 0; i < ARRAY_SIZE(dbgfs_attr_source); i++) { - len += scnprintf(buf + len, PAGE_SIZE - len, "%s ", - dbgfs_attr_source[i]); - } + for (i = 0; i < ARRAY_SIZE(dbgfs_attr_source); i++) + len += sysfs_emit_at(buf, len, "%s ", dbgfs_attr_source[i]); + buf[len - 1] = '\n'; - return simple_read_from_buffer(userbuf, count, ppos, buf, len); + return len; } -static const struct file_operations ad3552r_hs_data_source_fops = { - .owner = THIS_MODULE, - .write = ad3552r_hs_write_data_source, - .read = ad3552r_hs_show_data_source, -}; - -static const struct file_operations ad3552r_hs_data_source_avail_fops = { - .owner = THIS_MODULE, - .read = ad3552r_hs_show_data_source_avail, +static const struct iio_chan_spec_ext_info ad9740_ext_info[] = { + { + .name = "data_source", + .read = ad9740_ext_info_get_data_source, + .write = ad9740_ext_info_set_data_source, + .shared = IIO_SEPARATE, + }, + { + .name = "data_source_available", + .read = ad9740_ext_info_get_data_source_available, + .shared = IIO_SHARED_BY_ALL, + }, + { } }; -static int ad3552r_hs_setup(struct ad3552r_hs_state *st) +static int ad9740_setup(struct ad9740_state *st) { - u16 id; - u16 gain = 0, offset = 0; - u32 ch, val, range; int ret; - ret = ad3552r_hs_set_data_source(st, IIO_BACKEND_EXTERNAL); - if (ret) + dev_info(st->dev, "Starting AD9740 setup\n"); + + /* Set data source to external (from DMA) */ + dev_info(st->dev, "Initializing data source to DMA mode\n"); + ret = ad9740_set_data_source(st, IIO_BACKEND_EXTERNAL); + if (ret) { + dev_err(st->dev, "Failed to set initial data source: %d\n", ret); return ret; + } - val = ret; + /* TODO: Configure AD9740 registers via SPI: + * - Full-scale current + * - Mix mode + * - Data format (2's complement) + */ + dev_info(st->dev, "AD9740 setup completed successfully\n"); return 0; } -static const struct iio_buffer_setup_ops ad3552r_hs_buffer_setup_ops = { - .postenable = ad3552r_hs_buffer_postenable, - .predisable = ad3552r_hs_buffer_predisable, +static const struct iio_buffer_setup_ops ad9740_buffer_setup_ops = { + .postenable = ad9740_buffer_postenable, + .predisable = ad9740_buffer_predisable, }; -#define AD3552R_CHANNEL(ch) { \ - .type = IIO_VOLTAGE, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ - BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ - BIT(IIO_CHAN_INFO_SCALE) | \ - BIT(IIO_CHAN_INFO_OFFSET), \ +#define AD9740_CHANNEL(ch) { \ + .type = IIO_ALTVOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .output = 1, \ .indexed = 1, \ .channel = (ch), \ .scan_index = (ch), \ .scan_type = { \ .sign = 'u', \ - .realbits = 16, \ + .realbits = 10, \ .storagebits = 16, \ .endianness = IIO_BE, \ } \ } -static const struct iio_chan_spec ad3552r_hs_channels[] = { - AD3552R_CHANNEL(0), +static const struct iio_chan_spec ad9740_channels[] = { + AD9740_CHANNEL(0), }; -static const struct iio_info ad3552r_hs_info = { - .read_raw = &ad3552r_hs_read_raw, - .write_raw = &ad3552r_hs_write_raw, +static const struct iio_info ad9740_info = { + .read_raw = &ad9740_read_raw, + .write_raw = &ad9740_write_raw, }; - -static int ad3552r_hs_probe(struct platform_device *pdev) +static int ad9740_probe(struct platform_device *pdev) { - struct ad3552r_hs_state *st; + struct ad9740_state *st; struct iio_dev *indio_dev; int ret; + dev_info(&pdev->dev, "AD9740 probe starting\n"); + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); - if (!indio_dev) + if (!indio_dev) { + dev_err(&pdev->dev, "Failed to allocate IIO device\n"); return -ENOMEM; + } st = iio_priv(indio_dev); st->dev = &pdev->dev; - st->data = dev_get_platdata(st->dev); - if (!st->data) - return dev_err_probe(st->dev, -ENODEV, "No platform data !"); + mutex_init(&st->lock); + dev_dbg(&pdev->dev, "Mutex initialized\n"); + + /* Parse device tree properties */ + st->twos_complement = device_property_read_bool(&pdev->dev, + "adi,twos-complement"); + dev_info(&pdev->dev, "Data format: %s\n", + st->twos_complement ? "2's complement" : "offset binary"); + /* Get IIO backend (AXI_AD974X DAC core) */ + dev_info(&pdev->dev, "Getting IIO backend (AXI_AD974X)\n"); st->back = devm_iio_backend_get(&pdev->dev, NULL); if (IS_ERR(st->back)) - return PTR_ERR(st->back); + return dev_err_probe(&pdev->dev, PTR_ERR(st->back), + "Failed to get backend\n"); + dev_info(&pdev->dev, "IIO backend acquired successfully\n"); + dev_info(&pdev->dev, "Enabling IIO backend\n"); ret = devm_iio_backend_enable(&pdev->dev, st->back); if (ret) - return ret; + return dev_err_probe(&pdev->dev, ret, + "Failed to enable backend\n"); + dev_info(&pdev->dev, "IIO backend enabled successfully\n"); + + /* Get reset GPIO if present */ + dev_dbg(&pdev->dev, "Checking for reset GPIO\n"); + st->reset_gpio = devm_gpiod_get_optional(&pdev->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(st->reset_gpio)) { + dev_err(&pdev->dev, "Failed to get reset GPIO: %ld\n", + PTR_ERR(st->reset_gpio)); + return PTR_ERR(st->reset_gpio); + } - st->model_data = device_get_match_data(&pdev->dev); - if (!st->model_data) - return -ENODEV; + if (st->reset_gpio) { + dev_info(&pdev->dev, "Reset GPIO found, performing hardware reset\n"); + /* Reset the device */ + gpiod_set_value_cansleep(st->reset_gpio, 1); + msleep(10); + gpiod_set_value_cansleep(st->reset_gpio, 0); + msleep(10); + dev_info(&pdev->dev, "Hardware reset completed\n"); + } else { + dev_info(&pdev->dev, "No reset GPIO specified\n"); + } - indio_dev->name = "ad3552r"; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->setup_ops = &ad3552r_hs_buffer_setup_ops; - indio_dev->channels = ad3552r_hs_channels; - indio_dev->num_channels = ARRAY_SIZE(ad3552r_hs_channels); - indio_dev->info = &ad3552r_hs_info; + /* Make a modifiable copy of the channel spec for backend extension */ + struct iio_chan_spec *channels; + const struct iio_chan_spec_ext_info *backend_ext_info; + struct iio_chan_spec_ext_info *merged_ext_info; + int num_our_ext_info, num_backend_ext_info, i, j; + + dev_info(&pdev->dev, "Creating modifiable channel spec\n"); + channels = devm_kmemdup(&pdev->dev, ad9740_channels, + sizeof(ad9740_channels), GFP_KERNEL); + if (!channels) { + dev_err(&pdev->dev, "Failed to allocate channel spec\n"); + return -ENOMEM; + } - ret = devm_iio_backend_request_buffer(&pdev->dev, st->back, indio_dev); - if (ret) + /* Extend channel with DDS controls from backend */ + dev_info(&pdev->dev, "Extending channel spec with backend DDS controls\n"); + ret = iio_backend_extend_chan_spec(st->back, &channels[0]); + if (ret) { + dev_err(&pdev->dev, "Failed to extend channel spec: %d\n", ret); return ret; + } + + /* Merge our ext_info (data_source) with backend's ext_info (DDS) */ + backend_ext_info = channels[0].ext_info; + + /* Count entries in both arrays */ + num_our_ext_info = ARRAY_SIZE(ad9740_ext_info) - 1; /* exclude terminator */ + num_backend_ext_info = 0; + if (backend_ext_info) { + while (backend_ext_info[num_backend_ext_info].name) + num_backend_ext_info++; + } + + dev_info(&pdev->dev, "Merging ext_info: %d our attrs + %d backend attrs\n", + num_our_ext_info, num_backend_ext_info); - ret = ad3552r_hs_setup(st); + /* Allocate merged array: our entries + backend entries + terminator */ + merged_ext_info = devm_kcalloc(&pdev->dev, + num_our_ext_info + num_backend_ext_info + 1, + sizeof(*merged_ext_info), GFP_KERNEL); + if (!merged_ext_info) { + dev_err(&pdev->dev, "Failed to allocate merged ext_info\n"); + return -ENOMEM; + } + + /* Copy our ext_info first */ + for (i = 0; i < num_our_ext_info; i++) + merged_ext_info[i] = ad9740_ext_info[i]; + + /* Then append backend ext_info */ + for (j = 0; j < num_backend_ext_info; j++) + merged_ext_info[i + j] = backend_ext_info[j]; + + /* Terminator is already zero from kcalloc */ + channels[0].ext_info = merged_ext_info; + dev_info(&pdev->dev, "Extended attributes merged successfully\n"); + + dev_info(&pdev->dev, "Configuring IIO device structure\n"); + indio_dev->name = "ad9740"; + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_HARDWARE; + indio_dev->setup_ops = &ad9740_buffer_setup_ops; + indio_dev->channels = channels; + indio_dev->num_channels = ARRAY_SIZE(ad9740_channels); + indio_dev->info = &ad9740_info; + dev_info(&pdev->dev, "IIO device configured: %d channel(s), modes=0x%x\n", + indio_dev->num_channels, indio_dev->modes); + + /* Request DMA buffer from backend */ + dev_info(&pdev->dev, "Requesting DMA buffer from backend\n"); + ret = devm_iio_backend_request_buffer(&pdev->dev, st->back, indio_dev); if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to request backend buffer\n"); + dev_info(&pdev->dev, "DMA buffer allocated successfully\n"); + + /* Setup DAC and backend */ + ret = ad9740_setup(st); + if (ret) { + dev_err(&pdev->dev, "AD9740 setup failed: %d\n", ret); return ret; + } + /* Register IIO device */ + dev_info(&pdev->dev, "Registering IIO device\n"); ret = devm_iio_device_register(&pdev->dev, indio_dev); - if (ret) + if (ret) { + dev_err(&pdev->dev, "Failed to register IIO device: %d\n", ret); return ret; + } - return ret; + dev_info(&pdev->dev, "========================================\n"); + dev_info(&pdev->dev, "AD9740 DAC registered successfully!\n"); + dev_info(&pdev->dev, "========================================\n"); + dev_info(&pdev->dev, "Features:\n"); + dev_info(&pdev->dev, " - DDS dual-tone generator\n"); + dev_info(&pdev->dev, " - DMA streaming mode\n"); + dev_info(&pdev->dev, " - Internal ramp pattern\n"); + dev_info(&pdev->dev, "Data source control:\n"); + dev_info(&pdev->dev, " - out_voltage0_data_source (normal/dds/ramp)\n"); + dev_info(&pdev->dev, " - out_voltage0_data_source_available\n"); + dev_info(&pdev->dev, "DDS controls:\n"); + dev_info(&pdev->dev, " - out_voltage0_frequency0/1 (tone frequencies)\n"); + dev_info(&pdev->dev, " - out_voltage0_scale0/1 (tone amplitudes)\n"); + dev_info(&pdev->dev, " - out_voltage0_phase0/1 (tone phases)\n"); + dev_info(&pdev->dev, "========================================\n"); + + return 0; } -static const struct of_device_id ad3552r_hs_of_id[] = { - { .compatible = "adi,ad3541r", .data = &ad3541r_model_data }, - { .compatible = "adi,ad3542r", .data = &ad3542r_model_data }, - { .compatible = "adi,ad3551r", .data = &ad3551r_model_data }, - { .compatible = "adi,ad3552r", .data = &ad3552r_model_data }, +static const struct of_device_id ad9740_of_id[] = { + { .compatible = "adi,ad9740" }, { } }; -MODULE_DEVICE_TABLE(of, ad3552r_hs_of_id); +MODULE_DEVICE_TABLE(of, ad9740_of_id); -static struct platform_driver ad3552r_hs_driver = { +static struct platform_driver ad9740_driver = { .driver = { - .name = "ad3552r-hs", - .of_match_table = ad3552r_hs_of_id, + .name = "ad9740", + .of_match_table = ad9740_of_id, }, - .probe = ad3552r_hs_probe, + .probe = ad9740_probe, }; -module_platform_driver(ad3552r_hs_driver); +module_platform_driver(ad9740_driver); -MODULE_AUTHOR("Dragos Bogdan "); -MODULE_AUTHOR("Angelo Dureghello "); -MODULE_DESCRIPTION("AD3552R Driver - High Speed version"); +MODULE_AUTHOR("Analog Devices Inc."); +MODULE_DESCRIPTION("Analog Devices AD9740 DAC Driver"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS(IIO_BACKEND); -MODULE_IMPORT_NS(IIO_AD3552R); diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c index 695839f688a1cb..e9353e4764eac9 100644 --- a/drivers/iio/dac/adi-axi-dac.c +++ b/drivers/iio/dac/adi-axi-dac.c @@ -834,6 +834,7 @@ static const struct iio_backend_ops axi_dac_generic_ops = { .ext_info_set = axi_dac_ext_info_set, .ext_info_get = axi_dac_ext_info_get, .data_source_set = axi_dac_data_source_set, + .data_source_get = axi_dac_data_source_get, .set_sample_rate = axi_dac_set_sample_rate, .debugfs_reg_access = iio_backend_debugfs_ptr(axi_dac_reg_access), }; @@ -1009,9 +1010,17 @@ static const struct axi_dac_info dac_ad3552r = { .has_child_nodes = true, }; +static const struct axi_dac_info dac_ad9740 = { + .version = ADI_AXI_PCORE_VER(9, 1, 'b'), + .backend_info = &axi_dac_generic, + .has_dac_clk = true, + .has_child_nodes = true, +}; + static const struct of_device_id axi_dac_of_match[] = { { .compatible = "adi,axi-dac-9.1.b", .data = &dac_generic }, { .compatible = "adi,axi-ad3552r", .data = &dac_ad3552r }, + { .compatible = "adi,axi-ad9740", .data = &dac_ad9740 }, {} }; MODULE_DEVICE_TABLE(of, axi_dac_of_match); From f8886161640fa10c49a940e2f5725dfa8cf80c76 Mon Sep 17 00:00:00 2001 From: sarpadi Date: Wed, 22 Oct 2025 21:46:51 +0300 Subject: [PATCH 05/11] ad9740: DDS and Ramp mode working DDS is generating an apparently correct sine wave; in ramp mode a periodic signal is generated more similar to an asymetric PWM than to a ramp --- .../dts/xilinx/zynq-zed-adv7511-ad9740.dts | 4 +-- drivers/iio/dac/ad9740.c | 27 +++++-------------- drivers/iio/dac/adi-axi-dac.c | 8 +++++- 3 files changed, 15 insertions(+), 24 deletions(-) diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts index 2fa603c299834f..004cae26153f7c 100644 --- a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts @@ -41,9 +41,9 @@ adi,power-up-frequency = <128000000>; adi,phase-detector-polarity-positive-enable; adi,charge-pump-current = <2500>; - adi,output-power = <3>; + adi,output-power = <0>; adi,aux-output-enable; - adi,aux-output-power = <3>; + adi,aux-output-power = <0>; adi,muxout-select = <6>; }; }; diff --git a/drivers/iio/dac/ad9740.c b/drivers/iio/dac/ad9740.c index 47a325fb543ad6..80c72ea882bd11 100644 --- a/drivers/iio/dac/ad9740.c +++ b/drivers/iio/dac/ad9740.c @@ -6,7 +6,6 @@ * Copyright 2025 Analog Devices Inc. */ -#include #include #include #include @@ -14,30 +13,16 @@ #include #include #include -#include /* - * AD9740 Register Map + * AD9740 has no configuration registers - it's a simple parallel DAC. + * All configuration is done via the AXI DAC IP core in the FPGA. */ -#define AD9740_REG_FSC_MSB 0x00 -#define AD9740_REG_FSC_LSB 0x01 -#define AD9740_REG_MODE 0x02 -#define AD9740_REG_POWER 0x03 - -/* MODE register bits */ -#define AD9740_MODE_MIX_MODE BIT(7) -#define AD9740_MODE_2S_COMPLEMENT BIT(6) - -/* POWER register bits */ -#define AD9740_POWER_DOWN BIT(0) struct ad9740_state { struct device *dev; struct iio_backend *back; - struct spi_device *spi; struct gpio_desc *reset_gpio; - /* Full-scale current setting (8-20 mA) */ - unsigned int fsc_ua; /* Data format: true = 2's complement, false = offset binary */ bool twos_complement; /* Protects backend I/O operations from concurrent accesses. */ @@ -294,10 +279,10 @@ static int ad9740_setup(struct ad9740_state *st) return ret; } - /* TODO: Configure AD9740 registers via SPI: - * - Full-scale current - * - Mix mode - * - Data format (2's complement) + /* + * AD9740 has no software-configurable registers. + * Hardware configuration (full-scale current, references, etc.) + * is done via external analog components on the board. */ dev_info(st->dev, "AD9740 setup completed successfully\n"); diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c index e9353e4764eac9..bdab3468e8c3fe 100644 --- a/drivers/iio/dac/adi-axi-dac.c +++ b/drivers/iio/dac/adi-axi-dac.c @@ -897,14 +897,20 @@ static int axi_dac_probe(struct platform_device *pdev) if (st->info->has_dac_clk) { struct clk *dac_clk; + unsigned long rate; dac_clk = devm_clk_get_enabled(&pdev->dev, "dac_clk"); if (IS_ERR(dac_clk)) return dev_err_probe(&pdev->dev, PTR_ERR(dac_clk), "failed to get dac_clk clock\n"); + rate = clk_get_rate(dac_clk); /* We only care about the streaming mode rate */ - st->dac_clk_rate = clk_get_rate(dac_clk) / 2; + st->dac_clk_rate = rate / 2; + /* Initialize DDS sample rate for frequency calculations */ + st->dac_clk = rate; + dev_info(&pdev->dev, "DAC clock: %lu Hz, DDS sample rate: %llu Hz\n", + rate, st->dac_clk); } base = devm_platform_ioremap_resource(pdev, 0); From 64739bdfeb72dd8104d16c2be7e923b004d6d645 Mon Sep 17 00:00:00 2001 From: sarpadi Date: Thu, 23 Oct 2025 14:21:00 +0300 Subject: [PATCH 06/11] ad9740: DMA mode working but not in cyclic mode --- drivers/iio/dac/ad9740.c | 8 ++++---- drivers/iio/dac/adi-axi-dac.c | 8 ++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/iio/dac/ad9740.c b/drivers/iio/dac/ad9740.c index 80c72ea882bd11..8d96c44580fddf 100644 --- a/drivers/iio/dac/ad9740.c +++ b/drivers/iio/dac/ad9740.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Analog Devices AD9740 - * 10-Bit, 210 MSPS Digital-to-Analog Converter + * 14-Bit, 210 MSPS Digital-to-Analog Converter * * Copyright 2025 Analog Devices Inc. */ @@ -111,8 +111,8 @@ static int ad9740_buffer_postenable(struct iio_dev *indio_dev) dev_info(st->dev, "Configuring data format: offset binary\n"); } - dev_info(st->dev, "Setting backend data format (10-bit in 16-bit container)\n"); - /* Configure data format: 10-bit in 16-bit container */ + dev_info(st->dev, "Setting backend data format (14-bit in 16-bit container)\n"); + /* Configure data format: 14-bit in 16-bit container */ ret = iio_backend_data_format_set(st->back, 0, &fmt); if (ret) { dev_err(st->dev, "Failed to set data format: %d\n", ret); @@ -303,7 +303,7 @@ static const struct iio_buffer_setup_ops ad9740_buffer_setup_ops = { .scan_index = (ch), \ .scan_type = { \ .sign = 'u', \ - .realbits = 10, \ + .realbits = 14, \ .storagebits = 16, \ .endianness = IIO_BE, \ } \ diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c index bdab3468e8c3fe..9f51ac410af6cc 100644 --- a/drivers/iio/dac/adi-axi-dac.c +++ b/drivers/iio/dac/adi-axi-dac.c @@ -682,7 +682,12 @@ static int axi_dac_data_format_set(struct iio_backend *back, unsigned int ch, struct axi_dac_state *st = iio_backend_get_priv(back); switch (data->type) { + case IIO_BACKEND_OFFSET_BINARY: case IIO_BACKEND_DATA_UNSIGNED: + /* Offset binary and unsigned are the same for DACs */ + return regmap_set_bits(st->regmap, AXI_DAC_CNTRL_2_REG, + AXI_DAC_CNTRL_2_UNSIGNED_DATA); + case IIO_BACKEND_TWOS_COMPLEMENT: return regmap_clear_bits(st->regmap, AXI_DAC_CNTRL_2_REG, AXI_DAC_CNTRL_2_UNSIGNED_DATA); default: @@ -835,6 +840,9 @@ static const struct iio_backend_ops axi_dac_generic_ops = { .ext_info_get = axi_dac_ext_info_get, .data_source_set = axi_dac_data_source_set, .data_source_get = axi_dac_data_source_get, + .data_format_set = axi_dac_data_format_set, + .data_stream_enable = axi_dac_data_stream_enable, + .data_stream_disable = axi_dac_data_stream_disable, .set_sample_rate = axi_dac_set_sample_rate, .debugfs_reg_access = iio_backend_debugfs_ptr(axi_dac_reg_access), }; From 0cc359626441295126d908723572a686438aef65 Mon Sep 17 00:00:00 2001 From: sarpadi Date: Thu, 23 Oct 2025 16:12:43 +0300 Subject: [PATCH 07/11] ad9740: DMA mode working at 120MHz --- .../boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts | 4 ++-- drivers/dma/dma-axi-dmac.c | 14 +++++++++++++- drivers/iio/buffer/industrialio-buffer-dma.c | 5 +++++ drivers/iio/buffer/industrialio-buffer-dmaengine.c | 12 ++++++++++-- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts index 004cae26153f7c..505f0d78d58cf4 100644 --- a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts @@ -19,7 +19,7 @@ adf4351_clkout: clock@2 { #clock-cells = <0>; compatible = "fixed-clock"; - clock-frequency = <128000000>; + clock-frequency = <120000000>; clock-output-names = "adf4351_clkout"; }; }; @@ -38,7 +38,7 @@ clock-names = "clkin"; adi,channel-spacing = <10000>; - adi,power-up-frequency = <128000000>; + adi,power-up-frequency = <120000000>; adi,phase-detector-polarity-positive-enable; adi,charge-pump-current = <2500>; adi,output-power = <0>; diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c index 78ce6504fffd25..00a23fba0a5ed8 100644 --- a/drivers/dma/dma-axi-dmac.c +++ b/drivers/dma/dma-axi-dmac.c @@ -291,10 +291,17 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan) * call, enable hw cyclic mode to avoid unnecessary interrupts. */ if (chan->hw_cyclic && desc->cyclic && !desc->vdesc.tx.callback) { + pr_info("axi_dmac_start_transfer: Cyclic transfer, hw_sg=%d, num_sgs=%u\n", + chan->hw_sg, desc->num_sgs); if (chan->hw_sg) desc->sg[desc->num_sgs - 1].hw->flags &= ~AXI_DMAC_HW_FLAG_IRQ; - else if (desc->num_sgs == 1) + else if (desc->num_sgs == 1) { + pr_info("axi_dmac_start_transfer: Setting HW_CYCLIC flag (num_sgs==1)\n"); flags |= AXI_DMAC_FLAG_CYCLIC; + } else { + pr_warn("axi_dmac_start_transfer: NOT setting HW_CYCLIC flag (num_sgs=%u > 1)\n", + desc->num_sgs); + } } if (chan->hw_partial_xfer) @@ -746,6 +753,11 @@ static struct dma_async_tx_descriptor *axi_dmac_prep_dma_cyclic( num_segments = DIV_ROUND_UP(period_len, chan->max_length); num_sgs = num_periods * num_segments; + pr_info("axi_dmac_prep_dma_cyclic: buf_len=%zu, period_len=%zu, max_length=%u\n", + buf_len, period_len, chan->max_length); + pr_info("axi_dmac_prep_dma_cyclic: num_periods=%u, num_segments=%u, num_sgs=%u\n", + num_periods, num_segments, num_sgs); + desc = axi_dmac_alloc_desc(chan, num_sgs); if (!desc) return NULL; diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c index dc27ea1600b29a..0a7d67c38f0a7a 100644 --- a/drivers/iio/buffer/industrialio-buffer-dma.c +++ b/drivers/iio/buffer/industrialio-buffer-dma.c @@ -938,6 +938,11 @@ int iio_dma_buffer_enqueue_block(struct iio_buffer *buffer, dma_block->block.bytes_used = block->bytes_used; dma_block->block.flags = block->flags; + /* Debug: Print block enqueue info */ + pr_info("iio_dma_buffer_enqueue_block: block_id=%u, bytes_used=%u, flags=0x%x (cyclic=%d)\n", + block->id, block->bytes_used, block->flags, + !!(block->flags & IIO_BUFFER_BLOCK_FLAG_CYCLIC)); + switch (dma_block->state) { case IIO_BLOCK_STATE_DONE: list_del_init(&dma_block->head); diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index 41649606040c3c..a97eea9d5c70b8 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -98,17 +98,25 @@ int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, } if (block->block.flags & IIO_BUFFER_BLOCK_FLAG_CYCLIC) { + pr_info("iio_dmaengine_buffer_submit_block: Using CYCLIC mode, bytes=%u\n", + block->block.bytes_used); desc = dmaengine_prep_dma_cyclic(dmaengine_buffer->chan, block->phys_addr, block->block.bytes_used, block->block.bytes_used, dma_dir, 0); - if (!desc) + if (!desc) { + pr_err("iio_dmaengine_buffer_submit_block: dmaengine_prep_dma_cyclic failed\n"); return -ENOMEM; + } } else { + pr_info("iio_dmaengine_buffer_submit_block: Using SINGLE mode, bytes=%u\n", + block->block.bytes_used); desc = dmaengine_prep_slave_single(dmaengine_buffer->chan, block->phys_addr, block->block.bytes_used, dma_dir, DMA_PREP_INTERRUPT); - if (!desc) + if (!desc) { + pr_err("iio_dmaengine_buffer_submit_block: dmaengine_prep_slave_single failed\n"); return -ENOMEM; + } desc->callback_result = iio_dmaengine_buffer_block_done; desc->callback_param = block; From 73b298b71777efd7e1d2d84e4bf5fc2196798c54 Mon Sep 17 00:00:00 2001 From: sarpadi Date: Fri, 24 Oct 2025 15:59:38 +0300 Subject: [PATCH 08/11] ad9740: DTS: Update ADF clk to 210MHz --- arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts index 505f0d78d58cf4..53d0690938f1d0 100644 --- a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts @@ -19,7 +19,7 @@ adf4351_clkout: clock@2 { #clock-cells = <0>; compatible = "fixed-clock"; - clock-frequency = <120000000>; + clock-frequency = <210000000>; clock-output-names = "adf4351_clkout"; }; }; @@ -38,7 +38,7 @@ clock-names = "clkin"; adi,channel-spacing = <10000>; - adi,power-up-frequency = <120000000>; + adi,power-up-frequency = <210000000>; adi,phase-detector-polarity-positive-enable; adi,charge-pump-current = <2500>; adi,output-power = <0>; From b7d39cd1d8066709d03fa3e6a89291fa602c752f Mon Sep 17 00:00:00 2001 From: sarpadi Date: Tue, 28 Oct 2025 10:57:18 +0200 Subject: [PATCH 09/11] AD9740: Add support for all 4 devices --- .../dts/xilinx/zynq-zed-adv7511-ad9740.dts | 2 +- .../dts/xilinx/zynq-zed-adv7511-ad9742.dts | 96 ++++++++++++++++ .../dts/xilinx/zynq-zed-adv7511-ad9744.dts | 96 ++++++++++++++++ .../dts/xilinx/zynq-zed-adv7511-ad9748.dts | 96 ++++++++++++++++ drivers/iio/dac/ad9740.c | 103 ++++++++++++++---- 5 files changed, 372 insertions(+), 21 deletions(-) create mode 100644 arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9742.dts create mode 100644 arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9744.dts create mode 100644 arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9748.dts diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts index 53d0690938f1d0..f6bacbad25f5a0 100644 --- a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9740.dts @@ -93,4 +93,4 @@ /* adi,twos-complement; */ }; }; -}; +}; \ No newline at end of file diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9742.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9742.dts new file mode 100644 index 00000000000000..a6abe99a459e77 --- /dev/null +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9742.dts @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2025 Analog Devices Inc. */ + +/dts-v1/; + +#include "zynq-zed.dtsi" +#include "zynq-zed-adv7511.dtsi" + +/ { + + clocks { + adf4351_clkin: clock@1 { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <10000000>; + clock-output-names = "refclk"; + }; + + adf4351_clkout: clock@2 { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <210000000>; + clock-output-names = "adf4351_clkout"; + }; + }; +}; + +&spi0 { + status="okay"; + + adf4351: adf4351@0 { + compatible = "adi,adf4351"; + reg = <0>; + + spi-max-frequency = <10000000>; + + clocks = <&adf4351_clkin>; + clock-names = "clkin"; + + adi,channel-spacing = <10000>; + adi,power-up-frequency = <210000000>; + adi,phase-detector-polarity-positive-enable; + adi,charge-pump-current = <2500>; + adi,output-power = <0>; + adi,aux-output-enable; + adi,aux-output-power = <0>; + adi,muxout-select = <6>; + }; + }; + +&fpga_axi { + + dac_tx_dma: dma-controller@44a40000 { + compatible = "adi,axi-dmac-1.00.a"; + reg = <0x44a40000 0x10000>; + #dma-cells = <1>; + interrupt-parent = <&intc>; + interrupts = <0 57 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clkc 15>; + + adi,channels { + #size-cells = <0>; + #address-cells = <1>; + + dma-channel@0 { + reg = <0>; + adi,source-bus-width = <32>; + adi,source-bus-type = <0>; + adi,destination-bus-width = <32>; + adi,destination-bus-type = <1>; + }; + }; + }; + + axi_dac: axi-ad9740@44a70000 { + compatible = "adi,axi-ad9740"; + reg = <0x44a70000 0x1000>; + dmas = <&dac_tx_dma 0>; + dma-names = "tx"; + #io-backend-cells = <0>; + clocks = <&clkc 15>, <&adf4351_clkout>; + clock-names = "s_axi_aclk", "dac_clk"; + + #address-cells = <1>; + #size-cells = <0>; + + dac@0 { + reg = <0>; + compatible = "adi,ad9742"; + reset-gpios = <&gpio0 92 GPIO_ACTIVE_LOW>; + io-backends = <&axi_dac>; + /* Data format: use offset binary (false) or 2's complement (true) */ + /* adi,twos-complement; */ + }; + }; +}; \ No newline at end of file diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9744.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9744.dts new file mode 100644 index 00000000000000..a2d4738f7ec8eb --- /dev/null +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9744.dts @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2025 Analog Devices Inc. */ + +/dts-v1/; + +#include "zynq-zed.dtsi" +#include "zynq-zed-adv7511.dtsi" + +/ { + + clocks { + adf4351_clkin: clock@1 { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <10000000>; + clock-output-names = "refclk"; + }; + + adf4351_clkout: clock@2 { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <210000000>; + clock-output-names = "adf4351_clkout"; + }; + }; +}; + +&spi0 { + status="okay"; + + adf4351: adf4351@0 { + compatible = "adi,adf4351"; + reg = <0>; + + spi-max-frequency = <10000000>; + + clocks = <&adf4351_clkin>; + clock-names = "clkin"; + + adi,channel-spacing = <10000>; + adi,power-up-frequency = <210000000>; + adi,phase-detector-polarity-positive-enable; + adi,charge-pump-current = <2500>; + adi,output-power = <0>; + adi,aux-output-enable; + adi,aux-output-power = <0>; + adi,muxout-select = <6>; + }; + }; + +&fpga_axi { + + dac_tx_dma: dma-controller@44a40000 { + compatible = "adi,axi-dmac-1.00.a"; + reg = <0x44a40000 0x10000>; + #dma-cells = <1>; + interrupt-parent = <&intc>; + interrupts = <0 57 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clkc 15>; + + adi,channels { + #size-cells = <0>; + #address-cells = <1>; + + dma-channel@0 { + reg = <0>; + adi,source-bus-width = <32>; + adi,source-bus-type = <0>; + adi,destination-bus-width = <32>; + adi,destination-bus-type = <1>; + }; + }; + }; + + axi_dac: axi-ad9740@44a70000 { + compatible = "adi,axi-ad9740"; + reg = <0x44a70000 0x1000>; + dmas = <&dac_tx_dma 0>; + dma-names = "tx"; + #io-backend-cells = <0>; + clocks = <&clkc 15>, <&adf4351_clkout>; + clock-names = "s_axi_aclk", "dac_clk"; + + #address-cells = <1>; + #size-cells = <0>; + + dac@0 { + reg = <0>; + compatible = "adi,ad9744"; + reset-gpios = <&gpio0 92 GPIO_ACTIVE_LOW>; + io-backends = <&axi_dac>; + /* Data format: use offset binary (false) or 2's complement (true) */ + /* adi,twos-complement; */ + }; + }; +}; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9748.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9748.dts new file mode 100644 index 00000000000000..fea7be89ed4d56 --- /dev/null +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad9748.dts @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2025 Analog Devices Inc. */ + +/dts-v1/; + +#include "zynq-zed.dtsi" +#include "zynq-zed-adv7511.dtsi" + +/ { + + clocks { + adf4351_clkin: clock@1 { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <10000000>; + clock-output-names = "refclk"; + }; + + adf4351_clkout: clock@2 { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <210000000>; + clock-output-names = "adf4351_clkout"; + }; + }; +}; + +&spi0 { + status="okay"; + + adf4351: adf4351@0 { + compatible = "adi,adf4351"; + reg = <0>; + + spi-max-frequency = <10000000>; + + clocks = <&adf4351_clkin>; + clock-names = "clkin"; + + adi,channel-spacing = <10000>; + adi,power-up-frequency = <210000000>; + adi,phase-detector-polarity-positive-enable; + adi,charge-pump-current = <2500>; + adi,output-power = <0>; + adi,aux-output-enable; + adi,aux-output-power = <0>; + adi,muxout-select = <6>; + }; + }; + +&fpga_axi { + + dac_tx_dma: dma-controller@44a40000 { + compatible = "adi,axi-dmac-1.00.a"; + reg = <0x44a40000 0x10000>; + #dma-cells = <1>; + interrupt-parent = <&intc>; + interrupts = <0 57 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clkc 15>; + + adi,channels { + #size-cells = <0>; + #address-cells = <1>; + + dma-channel@0 { + reg = <0>; + adi,source-bus-width = <32>; + adi,source-bus-type = <0>; + adi,destination-bus-width = <32>; + adi,destination-bus-type = <1>; + }; + }; + }; + + axi_dac: axi-ad9740@44a70000 { + compatible = "adi,axi-ad9740"; + reg = <0x44a70000 0x1000>; + dmas = <&dac_tx_dma 0>; + dma-names = "tx"; + #io-backend-cells = <0>; + clocks = <&clkc 15>, <&adf4351_clkout>; + clock-names = "s_axi_aclk", "dac_clk"; + + #address-cells = <1>; + #size-cells = <0>; + + dac@0 { + reg = <0>; + compatible = "adi,ad9748"; + reset-gpios = <&gpio0 92 GPIO_ACTIVE_LOW>; + io-backends = <&axi_dac>; + /* Data format: use offset binary (false) or 2's complement (true) */ + /* adi,twos-complement; */ + }; + }; +}; \ No newline at end of file diff --git a/drivers/iio/dac/ad9740.c b/drivers/iio/dac/ad9740.c index 8d96c44580fddf..d703e5ae7eee4e 100644 --- a/drivers/iio/dac/ad9740.c +++ b/drivers/iio/dac/ad9740.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Analog Devices AD9740 - * 14-Bit, 210 MSPS Digital-to-Analog Converter + * Analog Devices AD9740/AD9742/AD9744/AD9748 + * 8/10/12/14-Bit, 210 MSPS Digital-to-Analog Converters * * Copyright 2025 Analog Devices Inc. */ @@ -15,14 +15,22 @@ #include /* - * AD9740 has no configuration registers - it's a simple parallel DAC. + * AD974x family has no configuration registers - they are simple parallel DACs. * All configuration is done via the AXI DAC IP core in the FPGA. */ +struct ad9740_chip_info { + const char *name; + unsigned int resolution; /* DAC resolution in bits */ + const struct iio_chan_spec *channels; + int num_channels; +}; + struct ad9740_state { struct device *dev; struct iio_backend *back; struct gpio_desc *reset_gpio; + const struct ad9740_chip_info *chip_info; /* Data format: true = 2's complement, false = offset binary */ bool twos_complement; /* Protects backend I/O operations from concurrent accesses. */ @@ -111,8 +119,9 @@ static int ad9740_buffer_postenable(struct iio_dev *indio_dev) dev_info(st->dev, "Configuring data format: offset binary\n"); } - dev_info(st->dev, "Setting backend data format (14-bit in 16-bit container)\n"); - /* Configure data format: 14-bit in 16-bit container */ + dev_info(st->dev, "Setting backend data format (%u-bit in 16-bit container)\n", + st->chip_info->resolution); + /* Configure data format: N-bit in 16-bit container */ ret = iio_backend_data_format_set(st->back, 0, &fmt); if (ret) { dev_err(st->dev, "Failed to set data format: %d\n", ret); @@ -269,7 +278,7 @@ static int ad9740_setup(struct ad9740_state *st) { int ret; - dev_info(st->dev, "Starting AD9740 setup\n"); + dev_info(st->dev, "Starting %s setup\n", st->chip_info->name); /* Set data source to external (from DMA) */ dev_info(st->dev, "Initializing data source to DMA mode\n"); @@ -280,12 +289,12 @@ static int ad9740_setup(struct ad9740_state *st) } /* - * AD9740 has no software-configurable registers. + * AD974x family has no software-configurable registers. * Hardware configuration (full-scale current, references, etc.) * is done via external analog components on the board. */ - dev_info(st->dev, "AD9740 setup completed successfully\n"); + dev_info(st->dev, "%s setup completed successfully\n", st->chip_info->name); return 0; } @@ -294,7 +303,7 @@ static const struct iio_buffer_setup_ops ad9740_buffer_setup_ops = { .predisable = ad9740_buffer_predisable, }; -#define AD9740_CHANNEL(ch) { \ +#define AD974X_CHANNEL(ch, bits) { \ .type = IIO_ALTVOLTAGE, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .output = 1, \ @@ -303,14 +312,26 @@ static const struct iio_buffer_setup_ops ad9740_buffer_setup_ops = { .scan_index = (ch), \ .scan_type = { \ .sign = 'u', \ - .realbits = 14, \ + .realbits = (bits), \ .storagebits = 16, \ .endianness = IIO_BE, \ } \ } +static const struct iio_chan_spec ad9748_channels[] = { + AD974X_CHANNEL(0, 8), +}; + static const struct iio_chan_spec ad9740_channels[] = { - AD9740_CHANNEL(0), + AD974X_CHANNEL(0, 10), +}; + +static const struct iio_chan_spec ad9742_channels[] = { + AD974X_CHANNEL(0, 12), +}; + +static const struct iio_chan_spec ad9744_channels[] = { + AD974X_CHANNEL(0, 14), }; static const struct iio_info ad9740_info = { @@ -318,14 +339,40 @@ static const struct iio_info ad9740_info = { .write_raw = &ad9740_write_raw, }; +static const struct ad9740_chip_info ad9748_chip_info = { + .name = "ad9748", + .resolution = 8, + .channels = ad9748_channels, + .num_channels = ARRAY_SIZE(ad9748_channels), +}; + +static const struct ad9740_chip_info ad9740_chip_info = { + .name = "ad9740", + .resolution = 10, + .channels = ad9740_channels, + .num_channels = ARRAY_SIZE(ad9740_channels), +}; + +static const struct ad9740_chip_info ad9742_chip_info = { + .name = "ad9742", + .resolution = 12, + .channels = ad9742_channels, + .num_channels = ARRAY_SIZE(ad9742_channels), +}; + +static const struct ad9740_chip_info ad9744_chip_info = { + .name = "ad9744", + .resolution = 14, + .channels = ad9744_channels, + .num_channels = ARRAY_SIZE(ad9744_channels), +}; + static int ad9740_probe(struct platform_device *pdev) { struct ad9740_state *st; struct iio_dev *indio_dev; int ret; - dev_info(&pdev->dev, "AD9740 probe starting\n"); - indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); if (!indio_dev) { dev_err(&pdev->dev, "Failed to allocate IIO device\n"); @@ -335,6 +382,16 @@ static int ad9740_probe(struct platform_device *pdev) st = iio_priv(indio_dev); st->dev = &pdev->dev; + /* Get chip info from device match data */ + st->chip_info = device_get_match_data(&pdev->dev); + if (!st->chip_info) { + dev_err(&pdev->dev, "Failed to get chip info\n"); + return -ENODEV; + } + + dev_info(&pdev->dev, "%s probe starting (%u-bit DAC)\n", + st->chip_info->name, st->chip_info->resolution); + mutex_init(&st->lock); dev_dbg(&pdev->dev, "Mutex initialized\n"); @@ -388,8 +445,9 @@ static int ad9740_probe(struct platform_device *pdev) int num_our_ext_info, num_backend_ext_info, i, j; dev_info(&pdev->dev, "Creating modifiable channel spec\n"); - channels = devm_kmemdup(&pdev->dev, ad9740_channels, - sizeof(ad9740_channels), GFP_KERNEL); + channels = devm_kmemdup(&pdev->dev, st->chip_info->channels, + sizeof(struct iio_chan_spec) * st->chip_info->num_channels, + GFP_KERNEL); if (!channels) { dev_err(&pdev->dev, "Failed to allocate channel spec\n"); return -ENOMEM; @@ -439,11 +497,11 @@ static int ad9740_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Extended attributes merged successfully\n"); dev_info(&pdev->dev, "Configuring IIO device structure\n"); - indio_dev->name = "ad9740"; + indio_dev->name = st->chip_info->name; indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_HARDWARE; indio_dev->setup_ops = &ad9740_buffer_setup_ops; indio_dev->channels = channels; - indio_dev->num_channels = ARRAY_SIZE(ad9740_channels); + indio_dev->num_channels = st->chip_info->num_channels; indio_dev->info = &ad9740_info; dev_info(&pdev->dev, "IIO device configured: %d channel(s), modes=0x%x\n", indio_dev->num_channels, indio_dev->modes); @@ -472,9 +530,11 @@ static int ad9740_probe(struct platform_device *pdev) } dev_info(&pdev->dev, "========================================\n"); - dev_info(&pdev->dev, "AD9740 DAC registered successfully!\n"); + dev_info(&pdev->dev, "%s %u-bit DAC registered successfully!\n", + st->chip_info->name, st->chip_info->resolution); dev_info(&pdev->dev, "========================================\n"); dev_info(&pdev->dev, "Features:\n"); + dev_info(&pdev->dev, " - Resolution: %u-bit\n", st->chip_info->resolution); dev_info(&pdev->dev, " - DDS dual-tone generator\n"); dev_info(&pdev->dev, " - DMA streaming mode\n"); dev_info(&pdev->dev, " - Internal ramp pattern\n"); @@ -491,7 +551,10 @@ static int ad9740_probe(struct platform_device *pdev) } static const struct of_device_id ad9740_of_id[] = { - { .compatible = "adi,ad9740" }, + { .compatible = "adi,ad9748", .data = &ad9748_chip_info }, + { .compatible = "adi,ad9740", .data = &ad9740_chip_info }, + { .compatible = "adi,ad9742", .data = &ad9742_chip_info }, + { .compatible = "adi,ad9744", .data = &ad9744_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ad9740_of_id); @@ -506,6 +569,6 @@ static struct platform_driver ad9740_driver = { module_platform_driver(ad9740_driver); MODULE_AUTHOR("Analog Devices Inc."); -MODULE_DESCRIPTION("Analog Devices AD9740 DAC Driver"); +MODULE_DESCRIPTION("Analog Devices AD9740/AD9742/AD9744/AD9748 DAC Driver"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS(IIO_BACKEND); From a699234c2ca230320017ffb0e7360f53b3bc73c5 Mon Sep 17 00:00:00 2001 From: sarpadi Date: Fri, 28 Nov 2025 16:30:40 +0200 Subject: [PATCH 10/11] ad9740: Fix MSB alignment --- drivers/iio/dac/ad9740.c | 70 ++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 9 deletions(-) diff --git a/drivers/iio/dac/ad9740.c b/drivers/iio/dac/ad9740.c index d703e5ae7eee4e..298ab631cae11f 100644 --- a/drivers/iio/dac/ad9740.c +++ b/drivers/iio/dac/ad9740.c @@ -4,6 +4,21 @@ * 8/10/12/14-Bit, 210 MSPS Digital-to-Analog Converters * * Copyright 2025 Analog Devices Inc. + * + * MSB Alignment Requirements: + * The AD974x family DACs expect MSB-aligned data in their internal 14-bit bus. + * This driver handles the alignment by configuring the IIO scan_type.shift field + * to indicate how many bits the data should be shifted left. + * + * Data Format per Device: + * - AD9748 (8-bit): Bits [15:8] of 16-bit word → DAC bits [13:6], shift=8 + * - AD9740 (10-bit): Bits [15:6] of 16-bit word → DAC bits [13:4], shift=6 + * - AD9742 (12-bit): Bits [15:4] of 16-bit word → DAC bits [13:2], shift=4 + * - AD9744 (14-bit): Bits [13:0] of 16-bit word → DAC bits [13:0], shift=0 + * + * Note: AD9744 extracts bits[13:0] directly, not bits[15:2] as the formula + * (16 - resolution) would suggest. Userspace should provide data in the + * lower 14 bits for AD9744, and MSB-aligned for other variants. */ #include @@ -119,15 +134,26 @@ static int ad9740_buffer_postenable(struct iio_dev *indio_dev) dev_info(st->dev, "Configuring data format: offset binary\n"); } - dev_info(st->dev, "Setting backend data format (%u-bit in 16-bit container)\n", + /* + * AD974x DACs expect data aligned in a 14-bit internal bus: + * - AD9748 (8-bit): Data from bits [15:8] → DAC [13:6], shift=8 + * - AD9740 (10-bit): Data from bits [15:6] → DAC [13:4], shift=6 + * - AD9742 (12-bit): Data from bits [15:4] → DAC [13:2], shift=4 + * - AD9744 (14-bit): Data from bits [13:0] → DAC [13:0], shift=0 + * + * The IIO framework handles this via the .shift field in scan_type. + * AD9744 is special: it extracts lower 14 bits directly (no MSB alignment). + */ + dev_info(st->dev, "Setting backend data format (%u-bit MSB-aligned in 16-bit container)\n", st->chip_info->resolution); - /* Configure data format: N-bit in 16-bit container */ + + /* Configure data format: N-bit MSB-aligned in 16-bit container */ ret = iio_backend_data_format_set(st->back, 0, &fmt); if (ret) { dev_err(st->dev, "Failed to set data format: %d\n", ret); goto err_unlock; } - dev_info(st->dev, "Data format configured successfully\n"); + dev_info(st->dev, "Data format configured successfully (MSB-aligned)\n"); /* Enable data streaming from DMA to DAC core */ dev_info(st->dev, "Enabling backend data stream\n"); @@ -276,10 +302,35 @@ static const struct iio_chan_spec_ext_info ad9740_ext_info[] = { static int ad9740_setup(struct ad9740_state *st) { + struct iio_backend_data_fmt fmt = { + .sign_extend = false, + .enable = true, + }; int ret; dev_info(st->dev, "Starting %s setup\n", st->chip_info->name); + /* + * Configure data format based on DT setting. + * This must be done at probe time so both DMA and DDS modes work. + * The DDS (CORDIC) always outputs signed (2's complement) data, + * so the backend needs to know whether to convert it to unsigned + * (offset binary) for the DAC. + */ + if (st->twos_complement) { + fmt.type = IIO_BACKEND_TWOS_COMPLEMENT; + dev_info(st->dev, "Configuring data format: 2's complement (signed DAC)\n"); + } else { + fmt.type = IIO_BACKEND_OFFSET_BINARY; + dev_info(st->dev, "Configuring data format: offset binary (unsigned DAC)\n"); + } + + ret = iio_backend_data_format_set(st->back, 0, &fmt); + if (ret) { + dev_err(st->dev, "Failed to set data format: %d\n", ret); + return ret; + } + /* Set data source to external (from DMA) */ dev_info(st->dev, "Initializing data source to DMA mode\n"); ret = ad9740_set_data_source(st, IIO_BACKEND_EXTERNAL); @@ -303,7 +354,7 @@ static const struct iio_buffer_setup_ops ad9740_buffer_setup_ops = { .predisable = ad9740_buffer_predisable, }; -#define AD974X_CHANNEL(ch, bits) { \ +#define AD974X_CHANNEL(ch, bits, shft) { \ .type = IIO_ALTVOLTAGE, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .output = 1, \ @@ -314,24 +365,25 @@ static const struct iio_buffer_setup_ops ad9740_buffer_setup_ops = { .sign = 'u', \ .realbits = (bits), \ .storagebits = 16, \ + .shift = (shft), \ .endianness = IIO_BE, \ } \ } static const struct iio_chan_spec ad9748_channels[] = { - AD974X_CHANNEL(0, 8), + AD974X_CHANNEL(0, 8, 8), /* 8-bit: shift=8, data in bits[15:8] */ }; static const struct iio_chan_spec ad9740_channels[] = { - AD974X_CHANNEL(0, 10), + AD974X_CHANNEL(0, 10, 6), /* 10-bit: shift=6, data in bits[15:6] */ }; static const struct iio_chan_spec ad9742_channels[] = { - AD974X_CHANNEL(0, 12), + AD974X_CHANNEL(0, 12, 4), /* 12-bit: shift=4, data in bits[15:4] */ }; static const struct iio_chan_spec ad9744_channels[] = { - AD974X_CHANNEL(0, 14), + AD974X_CHANNEL(0, 14, 0), /* 14-bit: shift=0, data in bits[13:0] */ }; static const struct iio_info ad9740_info = { @@ -569,6 +621,6 @@ static struct platform_driver ad9740_driver = { module_platform_driver(ad9740_driver); MODULE_AUTHOR("Analog Devices Inc."); -MODULE_DESCRIPTION("Analog Devices AD9740/AD9742/AD9744/AD9748 DAC Driver"); +MODULE_DESCRIPTION("Analog Devices AD9740/AD9742/AD9744/AD9748 DAC Driver with MSB Alignment"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS(IIO_BACKEND); From 318468aecd9841c25d20a88bc78c4ab1771dfeca Mon Sep 17 00:00:00 2001 From: sarpadi Date: Wed, 3 Dec 2025 13:28:14 +0200 Subject: [PATCH 11/11] ad9740: Clean up ad974x references --- drivers/iio/dac/ad9740.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/iio/dac/ad9740.c b/drivers/iio/dac/ad9740.c index 298ab631cae11f..240f8c6e1aa78d 100644 --- a/drivers/iio/dac/ad9740.c +++ b/drivers/iio/dac/ad9740.c @@ -6,7 +6,7 @@ * Copyright 2025 Analog Devices Inc. * * MSB Alignment Requirements: - * The AD974x family DACs expect MSB-aligned data in their internal 14-bit bus. + * The AD9740 family DACs expect MSB-aligned data in their internal 14-bit bus. * This driver handles the alignment by configuring the IIO scan_type.shift field * to indicate how many bits the data should be shifted left. * @@ -30,7 +30,7 @@ #include /* - * AD974x family has no configuration registers - they are simple parallel DACs. + * AD9740 family has no configuration registers - they are simple parallel DACs. * All configuration is done via the AXI DAC IP core in the FPGA. */ @@ -135,7 +135,7 @@ static int ad9740_buffer_postenable(struct iio_dev *indio_dev) } /* - * AD974x DACs expect data aligned in a 14-bit internal bus: + * AD9740 DACs expect data aligned in a 14-bit internal bus: * - AD9748 (8-bit): Data from bits [15:8] → DAC [13:6], shift=8 * - AD9740 (10-bit): Data from bits [15:6] → DAC [13:4], shift=6 * - AD9742 (12-bit): Data from bits [15:4] → DAC [13:2], shift=4 @@ -340,7 +340,7 @@ static int ad9740_setup(struct ad9740_state *st) } /* - * AD974x family has no software-configurable registers. + * AD9740 family has no software-configurable registers. * Hardware configuration (full-scale current, references, etc.) * is done via external analog components on the board. */ @@ -354,7 +354,7 @@ static const struct iio_buffer_setup_ops ad9740_buffer_setup_ops = { .predisable = ad9740_buffer_predisable, }; -#define AD974X_CHANNEL(ch, bits, shft) { \ +#define AD9740_CHANNEL(ch, bits, shft) { \ .type = IIO_ALTVOLTAGE, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .output = 1, \ @@ -371,19 +371,19 @@ static const struct iio_buffer_setup_ops ad9740_buffer_setup_ops = { } static const struct iio_chan_spec ad9748_channels[] = { - AD974X_CHANNEL(0, 8, 8), /* 8-bit: shift=8, data in bits[15:8] */ + AD9740_CHANNEL(0, 8, 8), /* 8-bit: shift=8, data in bits[15:8] */ }; static const struct iio_chan_spec ad9740_channels[] = { - AD974X_CHANNEL(0, 10, 6), /* 10-bit: shift=6, data in bits[15:6] */ + AD9740_CHANNEL(0, 10, 6), /* 10-bit: shift=6, data in bits[15:6] */ }; static const struct iio_chan_spec ad9742_channels[] = { - AD974X_CHANNEL(0, 12, 4), /* 12-bit: shift=4, data in bits[15:4] */ + AD9740_CHANNEL(0, 12, 4), /* 12-bit: shift=4, data in bits[15:4] */ }; static const struct iio_chan_spec ad9744_channels[] = { - AD974X_CHANNEL(0, 14, 0), /* 14-bit: shift=0, data in bits[13:0] */ + AD9740_CHANNEL(0, 14, 0), /* 14-bit: shift=0, data in bits[13:0] */ }; static const struct iio_info ad9740_info = { @@ -453,8 +453,8 @@ static int ad9740_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Data format: %s\n", st->twos_complement ? "2's complement" : "offset binary"); - /* Get IIO backend (AXI_AD974X DAC core) */ - dev_info(&pdev->dev, "Getting IIO backend (AXI_AD974X)\n"); + /* Get IIO backend (AXI_AD9740 DAC core) */ + dev_info(&pdev->dev, "Getting IIO backend (AXI_AD9740)\n"); st->back = devm_iio_backend_get(&pdev->dev, NULL); if (IS_ERR(st->back)) return dev_err_probe(&pdev->dev, PTR_ERR(st->back),