@@ -147,6 +147,27 @@ config AD9739A
To compile this driver as a module, choose M here: the module will be
called ad9739a.
+config ADI_AXI_DAC
+ tristate "Analog Devices Generic AXI DAC IP core driver"
+ select IIO_BUFFER
+ select IIO_BUFFER_DMAENGINE
+ select REGMAP_MMIO
+ select IIO_BACKEND
+ help
+ Say yes here to build support for Analog Devices Generic
+ AXI DAC IP core. The IP core is used for interfacing with
+ digital-to-analog (DAC) converters that require either a high-speed
+ serial interface (JESD204B/C) or a source synchronous parallel
+ interface (LVDS/CMOS).
+ Typically (for such devices) SPI will be used for configuration only,
+ while this IP core handles the streaming of data into memory via DMA.
+
+ Link: https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
+ If unsure, say N (but it's safe to say "Y").
+
+ To compile this driver as a module, choose M here: the
+ module will be called adi-axi-dac.
+
config LTC2688
tristate "Analog Devices LTC2688 DAC spi driver"
depends on SPI
@@ -30,6 +30,7 @@ obj-$(CONFIG_AD7293) += ad7293.o
obj-$(CONFIG_AD7303) += ad7303.o
obj-$(CONFIG_AD8801) += ad8801.o
obj-$(CONFIG_AD9739A) += ad9739a.o
+obj-$(CONFIG_ADI_AXI_DAC) += adi-axi-dac.o
obj-$(CONFIG_CIO_DAC) += cio-dac.o
obj-$(CONFIG_DPOT_DAC) += dpot-dac.o
obj-$(CONFIG_DS4424) += ds4424.o
new file mode 100644
@@ -0,0 +1,510 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices Generic AXI DAC IP core
+ * Link: https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
+ *
+ * Copyright 2016-2024 Analog Devices Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/limits.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/units.h>
+
+#include <linux/fpga/adi-axi-common.h>
+
+#include <linux/iio/backend.h>
+#include <linux/iio/buffer-dmaengine.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+
+/*
+ * Register definitions:
+ * https://wiki.analog.com/resources/fpga/docs/axi_dac_ip#register_map
+ */
+
+/* Base controls */
+#define AXI_DAC_REG_CONFIG 0x0C
+#define AXI_DAC_DISABLE BIT(6)
+
+ /* DAC controls */
+#define AXI_DAC_REG_RSTN 0x0040
+#define AXI_DAC_RSTN_CE_N BIT(2)
+#define AXI_DAC_RSTN_MMCM_RSTN BIT(1)
+#define AXI_DAC_RSTN_RSTN BIT(0)
+#define AXI_DAC_REG_CNTRL_1 0x0044
+#define AXI_DAC_SYNC BIT(0)
+#define AXI_DAC_REG_RATECNTRL 0x004C
+#define AXI_DAC_DRP_STATUS 0x0074
+#define AXI_DAC_DRP_LOCKED BIT(17)
+
+/* DAC Channel controls */
+#define AXI_DAC_REG_CHAN_CNTRL_1(c) (0x0400 + (c) * 0x40)
+#define AXI_DAC_REG_CHAN_CNTRL_3(c) (0x0400 + (c) * 0x40)
+#define AXI_DAC_SCALE_SIGN BIT(15)
+#define AXI_DAC_SCALE_INT BIT(14)
+#define AXI_DAC_SCALE_INT_NEG GENMASK(15, 14)
+#define AXI_DAC_SCALE_FRAC GENMASK(13, 0)
+#define AXI_DAC_REG_CHAN_CNTRL_2(c) (0x0404 + (c) * 0x40)
+#define AXI_DAC_REG_CHAN_CNTRL_4(c) (0x040c + (c) * 0x40)
+#define AXI_DAC_PHASE GENMASK(31, 16)
+#define AXI_DAC_FREQUENCY GENMASK(15, 0)
+#define AXI_DAC_REG_CHAN_CNTRL_7(c) (0x0418 + (c) * 0x40)
+#define AXI_DAC_DATA_SEL GENMASK(3, 0)
+
+/* 360 degrees in rad */
+#define AXI_DAC_2_PI_MEGA 6283190
+enum {
+ AXI_DAC_DATA_INTERNAL_TONE,
+ AXI_DAC_DATA_DMA = 2,
+};
+
+struct axi_dac_state {
+ struct regmap *regmap;
+ struct device *dev;
+ /* Lock for synchronizing device register access. */
+ struct mutex lock;
+ u64 dac_clk;
+ u32 reg_config;
+};
+
+static int axi_dac_frequency_read(struct iio_backend *back,
+ const struct iio_chan_spec *chan, int *val,
+ int *val2, unsigned int tone_idx,
+ unsigned long long sample_freq)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+ u32 reg, __val;
+ u16 freq;
+ int ret;
+
+ if (tone_idx > 1) {
+ dev_err(st->dev, "Not a valid tone: %u\n", tone_idx);
+ return -EINVAL;
+ }
+
+ if (tone_idx)
+ reg = AXI_DAC_REG_CHAN_CNTRL_4(chan->channel);
+ else
+ reg = AXI_DAC_REG_CHAN_CNTRL_2(chan->channel);
+
+ ret = regmap_read(st->regmap, reg, &__val);
+ if (ret)
+ return ret;
+
+ freq = FIELD_GET(AXI_DAC_FREQUENCY, __val);
+ *val = DIV_ROUND_CLOSEST_ULL(__val * sample_freq, BIT(16));
+
+ return 0;
+}
+
+static int axi_dac_scale_read(struct iio_backend *back,
+ const struct iio_chan_spec *chan, int *val,
+ int *val2, unsigned int tone_idx)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+ u8 sign, integer;
+ u32 reg, __val;
+ u16 frac;
+ int ret;
+
+ if (tone_idx > 1) {
+ dev_err(st->dev, "Not a valid tone: %u\n", tone_idx);
+ return -EINVAL;
+ }
+
+ if (tone_idx)
+ reg = AXI_DAC_REG_CHAN_CNTRL_3(chan->channel);
+ else
+ reg = AXI_DAC_REG_CHAN_CNTRL_1(chan->channel);
+
+ ret = regmap_read(st->regmap, reg, &__val);
+ if (ret)
+ return ret;
+
+ frac = FIELD_GET(AXI_DAC_SCALE_FRAC, __val);
+ sign = FIELD_GET(AXI_DAC_SCALE_SIGN, __val);
+ integer = FIELD_GET(AXI_DAC_SCALE_INT, __val);
+
+ *val2 = DIV_ROUND_CLOSEST_ULL((u64)frac * MICRO, AXI_DAC_SCALE_INT);
+
+ if (integer && sign)
+ *val = -1;
+ else if (integer)
+ *val = 1;
+ else if (sign)
+ *val2 *= -1;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int axi_dac_phase_read(struct iio_backend *back,
+ const struct iio_chan_spec *chan, int *val,
+ int *val2, unsigned int tone_idx)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+ unsigned int tmp;
+ u32 reg, __val;
+ u16 phase;
+ int ret;
+
+ if (tone_idx > 1) {
+ dev_err(st->dev, "Not a valid tone: %u\n", tone_idx);
+ return -EINVAL;
+ }
+
+ if (tone_idx)
+ reg = AXI_DAC_REG_CHAN_CNTRL_4(chan->channel);
+ else
+ reg = AXI_DAC_REG_CHAN_CNTRL_2(chan->channel);
+
+ ret = regmap_read(st->regmap, reg, &__val);
+ if (ret)
+ return ret;
+
+ phase = FIELD_GET(AXI_DAC_PHASE, __val);
+ tmp = DIV_ROUND_CLOSEST_ULL((u64)phase * AXI_DAC_2_PI_MEGA, U16_MAX);
+ *val = tmp / MICRO;
+ *val2 = tmp % MICRO;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int axi_dac_frequency_write(struct iio_backend *back,
+ const struct iio_chan_spec *chan, int val,
+ int val2, unsigned int tone_idx,
+ unsigned long long sample_freq)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+ u16 freq;
+ u32 reg;
+ int ret;
+
+ if (val < 0 || !sample_freq || val > sample_freq / 2) {
+ dev_err(st->dev, "Invalid frequency: %d, sampling freq: %llu\n",
+ val, sample_freq);
+ return -EINVAL;
+ }
+
+ if (tone_idx > 1) {
+ dev_err(st->dev, "Not a valid tone: %u\n", tone_idx);
+ return -EINVAL;
+ }
+
+ if (tone_idx)
+ reg = AXI_DAC_REG_CHAN_CNTRL_4(chan->channel);
+ else
+ reg = AXI_DAC_REG_CHAN_CNTRL_2(chan->channel);
+
+ freq = DIV64_U64_ROUND_CLOSEST((u64)val * BIT(16), sample_freq);
+
+ guard(mutex)(&st->lock);
+ ret = regmap_update_bits(st->regmap, reg, AXI_DAC_FREQUENCY, freq);
+ if (ret)
+ return ret;
+
+ /* synchronize channels */
+ return regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
+}
+
+static int axi_dac_scale_write(struct iio_backend *back,
+ const struct iio_chan_spec *chan, int val,
+ int val2, unsigned int tone_idx)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+ u32 scale = 0, tmp, reg;
+ int ret;
+
+ if (tone_idx > 1) {
+ dev_err(st->dev, "Not a valid tone: %u\n", tone_idx);
+ return -EINVAL;
+ }
+
+ /* format is 1.1.14 (sign, integer and fractional bits) */
+ switch (val) {
+ case 1:
+ scale = FIELD_PREP(AXI_DAC_SCALE_INT, 1);
+ break;
+ case -1:
+ scale = FIELD_PREP(AXI_DAC_SCALE_INT_NEG, 3);
+ break;
+ case 0:
+ if (val2 < 0) {
+ scale = FIELD_PREP(AXI_DAC_SCALE_SIGN, 1);
+ val2 *= -1;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tmp = DIV_ROUND_CLOSEST_ULL((u64)val2 * AXI_DAC_SCALE_INT, MICRO);
+ scale |= FIELD_PREP(AXI_DAC_SCALE_FRAC, tmp);
+
+ if (tone_idx)
+ reg = AXI_DAC_REG_CHAN_CNTRL_3(chan->channel);
+ else
+ reg = AXI_DAC_REG_CHAN_CNTRL_1(chan->channel);
+
+ guard(mutex)(&st->lock);
+ ret = regmap_write(st->regmap, reg, scale);
+ if (ret)
+ return ret;
+
+ /* synchronize channels */
+ return regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
+}
+
+static int axi_dac_phase_write(struct iio_backend *back,
+ const struct iio_chan_spec *chan, int val,
+ int val2, unsigned int tone_idx)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+ u32 phase, reg;
+ int ret;
+
+ if (tone_idx > 1) {
+ dev_err(st->dev, "Not a valid tone: %u\n", tone_idx);
+ return -EINVAL;
+ }
+
+ phase = val * MILLI + val2;
+
+ phase = DIV_ROUND_CLOSEST_ULL((u64)phase * U16_MAX, AXI_DAC_2_PI_MEGA);
+
+ if (tone_idx)
+ reg = AXI_DAC_REG_CHAN_CNTRL_4(chan->channel);
+ else
+ reg = AXI_DAC_REG_CHAN_CNTRL_2(chan->channel);
+
+ guard(mutex)(&st->lock);
+ ret = regmap_update_bits(st->regmap, reg, AXI_DAC_PHASE,
+ FIELD_PREP(AXI_DAC_PHASE, phase));
+ if (ret)
+ return ret;
+
+ /* synchronize channels */
+ return regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
+}
+
+static int axi_dac_data_source_set(struct iio_backend *back, unsigned int chan,
+ enum iio_backend_data_source data)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+
+ switch (data) {
+ case IIO_BACKEND_INTERNAL_CW:
+ return regmap_update_bits(st->regmap,
+ AXI_DAC_REG_CHAN_CNTRL_7(chan),
+ AXI_DAC_DATA_SEL,
+ AXI_DAC_DATA_INTERNAL_TONE);
+ case IIO_BACKEND_EXTERNAL:
+ return regmap_update_bits(st->regmap,
+ AXI_DAC_REG_CHAN_CNTRL_7(chan),
+ AXI_DAC_DATA_SEL, AXI_DAC_DATA_DMA);
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct iio_buffer *axi_dac_request_buffer(struct iio_backend *back,
+ struct iio_dev *indio_dev)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+ struct iio_buffer *buffer;
+ const char *dma_name;
+ int ret;
+
+ if (device_property_read_string(st->dev, "dma-names", &dma_name))
+ dma_name = "tx";
+
+ buffer = iio_dmaengine_buffer_alloc(st->dev, dma_name);
+ if (IS_ERR(buffer)) {
+ dev_err(st->dev, "Could not get DMA buffer, %ld\n",
+ PTR_ERR(buffer));
+ return ERR_CAST(buffer);
+ }
+
+ indio_dev->modes |= INDIO_BUFFER_HARDWARE;
+ iio_buffer_set_dir(buffer, IIO_BUFFER_DIRECTION_OUT);
+
+ ret = iio_device_attach_buffer(indio_dev, buffer);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return buffer;
+}
+
+static void axi_dac_free_buffer(struct iio_backend *back,
+ struct iio_buffer *buffer)
+{
+ iio_dmaengine_buffer_free(buffer);
+}
+
+static int axi_dac_enable(struct iio_backend *back)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+ unsigned int __val;
+ int ret;
+
+ guard(mutex)(&st->lock);
+ ret = regmap_set_bits(st->regmap, AXI_DAC_REG_RSTN,
+ AXI_DAC_RSTN_MMCM_RSTN);
+ if (ret)
+ return ret;
+ /*
+ * Make sure the DRP (Dynamic Reconfiguration Port) is locked. Not all
+ * designs really use it but if they don't we still get the lock bit
+ * set. So let's do it all the time so the code is generic.
+ */
+ ret = regmap_read_poll_timeout(st->regmap, AXI_DAC_DRP_STATUS, __val,
+ __val & AXI_DAC_DRP_LOCKED, 100, 1000);
+ if (ret)
+ return ret;
+
+ return regmap_set_bits(st->regmap, AXI_DAC_REG_RSTN,
+ AXI_DAC_RSTN_RSTN | AXI_DAC_RSTN_MMCM_RSTN);
+}
+
+static void axi_dac_disable(struct iio_backend *back)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+
+ guard(mutex)(&st->lock);
+ regmap_write(st->regmap, AXI_DAC_REG_RSTN, 0);
+}
+
+static const struct iio_backend_ops axi_dac_generic = {
+ .enable = axi_dac_enable,
+ .disable = axi_dac_disable,
+ .request_buffer = axi_dac_request_buffer,
+ .free_buffer = axi_dac_free_buffer,
+ .read_frequency = axi_dac_frequency_read,
+ .write_frequency = axi_dac_frequency_write,
+ .read_scale = axi_dac_scale_read,
+ .write_scale = axi_dac_scale_write,
+ .read_phase = axi_dac_phase_read,
+ .write_phase = axi_dac_phase_write,
+ .data_source_set = axi_dac_data_source_set,
+};
+
+static const struct regmap_config axi_dac_regmap_config = {
+ .val_bits = 32,
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x0800,
+};
+
+static int axi_dac_probe(struct platform_device *pdev)
+{
+ const unsigned int *expected_ver;
+ struct axi_dac_state *st;
+ void __iomem *base;
+ unsigned int ver;
+ struct clk *clk;
+ int ret;
+
+ st = devm_kzalloc(&pdev->dev, sizeof(*st), GFP_KERNEL);
+ if (!st)
+ return -ENOMEM;
+
+ expected_ver = device_get_match_data(&pdev->dev);
+ if (!expected_ver)
+ return -ENODEV;
+
+ clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ st->dev = &pdev->dev;
+ st->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &axi_dac_regmap_config);
+ if (IS_ERR(st->regmap))
+ return PTR_ERR(st->regmap);
+
+ /*
+ * Force disable the core. Up to the frontend to enable us. And we can
+ * still read/write registers...
+ */
+ ret = regmap_write(st->regmap, AXI_DAC_REG_RSTN, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(st->regmap, ADI_AXI_REG_VERSION, &ver);
+ if (ret)
+ return ret;
+
+ if (ADI_AXI_PCORE_VER_MAJOR(ver) != ADI_AXI_PCORE_VER_MAJOR(*expected_ver)) {
+ dev_err(&pdev->dev,
+ "Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
+ ADI_AXI_PCORE_VER_MAJOR(*expected_ver),
+ ADI_AXI_PCORE_VER_MINOR(*expected_ver),
+ ADI_AXI_PCORE_VER_PATCH(*expected_ver),
+ ADI_AXI_PCORE_VER_MAJOR(ver),
+ ADI_AXI_PCORE_VER_MINOR(ver),
+ ADI_AXI_PCORE_VER_PATCH(ver));
+ return -ENODEV;
+ }
+
+ /* Let's get the core read only configuration */
+ ret = regmap_read(st->regmap, AXI_DAC_REG_CONFIG, &st->reg_config);
+ if (ret)
+ return ret;
+
+ /*
+ * Default rate to 1 so the sampling frequency will be the same as the
+ * digital interface clock which should be the same as the frontend
+ * sampling clock/frequency. When this is not true, the frontend will
+ * have to explicitly set a new frequency and thus rate.
+ */
+ ret = regmap_write(st->regmap, AXI_DAC_REG_RATECNTRL, 1);
+ if (ret)
+ return ret;
+
+ mutex_init(&st->lock);
+ ret = devm_iio_backend_register(&pdev->dev, &axi_dac_generic, st);
+ if (ret)
+ return ret;
+
+ dev_info(&pdev->dev, "AXI DAC IP core (%d.%.2d.%c) probed\n",
+ ADI_AXI_PCORE_VER_MAJOR(ver),
+ ADI_AXI_PCORE_VER_MINOR(ver),
+ ADI_AXI_PCORE_VER_PATCH(ver));
+
+ return 0;
+}
+
+static unsigned int axi_dac_9_1_b_info = ADI_AXI_PCORE_VER(9, 1, 'b');
+
+static const struct of_device_id axi_dac_of_match[] = {
+ { .compatible = "adi,axi-dac-9.1.b", .data = &axi_dac_9_1_b_info },
+ {}
+};
+MODULE_DEVICE_TABLE(of, axi_dac_of_match);
+
+static struct platform_driver axi_dac_driver = {
+ .driver = {
+ .name = "adi-axi-dac",
+ .of_match_table = axi_dac_of_match,
+ },
+ .probe = axi_dac_probe,
+};
+module_platform_driver(axi_dac_driver);
+
+MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices Generic AXI DAC IP core driver");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_DMAENGINE_BUFFER);
+MODULE_IMPORT_NS(IIO_BACKEND);