From: Jonathan Cameron <jic23@kernel.org>
To: Nuno Sa via B4 Relay <devnull+nuno.sa.analog.com@kernel.org>
Cc: nuno.sa@analog.com, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org,
Dragos Bogdan <dragos.bogdan@analog.com>,
Lars-Peter Clausen <lars@metafoo.de>,
Michael Hennerich <Michael.Hennerich@analog.com>,
Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzysztof.kozlowski+dt@linaro.org>,
Conor Dooley <conor+dt@kernel.org>,
Olivier Moysan <olivier.moysan@foss.st.com>
Subject: Re: [PATCH 09/10] iio: dac: add support for AXI DAC IP core
Date: Thu, 28 Mar 2024 15:35:42 +0000 [thread overview]
Message-ID: <20240328153542.7ddb897c@jic23-huawei> (raw)
In-Reply-To: <20240328-iio-backend-axi-dac-v1-9-afc808b3fde3@analog.com>
On Thu, 28 Mar 2024 14:22:33 +0100
Nuno Sa via B4 Relay <devnull+nuno.sa.analog.com@kernel.org> wrote:
> From: Nuno Sa <nuno.sa@analog.com>
>
> Support the 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.
>
> Signed-off-by: Nuno Sa <nuno.sa@analog.com>
A few minor things inline, but mostly seems fine to me.
Jonathan
...
> diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c
> new file mode 100644
> index 000000000000..0022ecb4e4bb
> --- /dev/null
> +++ b/drivers/iio/dac/adi-axi-dac.c
> +
> +enum {
> + AXI_DAC_FREQ_TONE_1,
> + AXI_DAC_FREQ_TONE_2,
> + AXI_DAC_SCALE_TONE_1,
> + AXI_DAC_SCALE_TONE_2,
> + AXI_DAC_PHASE_TONE_1,
> + AXI_DAC_PHASE_TONE_2,
> +};
> +
> +static int __axi_dac_frequency_get(struct axi_dac_state *st, unsigned int chan,
> + unsigned int tone, unsigned int *freq)
> +{
> + u32 reg, raw;
> + int ret;
> +
> + if (!st->dac_clk) {
> + dev_err(st->dev, "Sampling rate is 0...\n");
> + return -EINVAL;
> + }
> +
> + if (tone == AXI_DAC_FREQ_TONE_1)
Given this is matching 2 out of enum with other values, it would be more
locally readable as a switch statement with an error returning default.
Then we wouldn't need to look at the caller.
Or at the caller convert from the enum to 0,1 for all these functions.
> + reg = AXI_DAC_REG_CHAN_CNTRL_2(chan);
> + else
> + reg = AXI_DAC_REG_CHAN_CNTRL_4(chan);
> +
> + ret = regmap_read(st->regmap, reg, &raw);
> + if (ret)
> + return ret;
> +
> + raw = FIELD_GET(AXI_DAC_FREQUENCY, raw);
> + *freq = DIV_ROUND_CLOSEST_ULL(raw * st->dac_clk, BIT(16));
> +
> + return 0;
> +}
...
> +static int axi_dac_scale_set(struct axi_dac_state *st,
> + const struct iio_chan_spec *chan,
> + const char *buf, size_t len, unsigned int tone)
> +{
> + int integer, frac, scale;
> + u32 raw = 0, reg;
> + int ret;
> +
> + ret = iio_str_to_fixpoint(buf, 100000, &integer, &frac);
> + if (ret)
> + return ret;
> +
> + scale = integer * MEGA + frac;
> + if (scale <= -2 * (int)MEGA || scale >= 2 * (int)MEGA)
> + return -EINVAL;
> +
> + /* format is 1.1.14 (sign, integer and fractional bits) */
> + if (scale < 0) {
> + raw = FIELD_PREP(AXI_DAC_SCALE_SIGN, 1);
> + scale *= -1;
> + }
> +
> + raw |= div_u64((u64)scale * AXI_DAC_SCALE_INT, MEGA);
> +
> + if (tone == AXI_DAC_SCALE_TONE_1)
> + reg = AXI_DAC_REG_CHAN_CNTRL_1(chan->channel);
> + else
> + reg = AXI_DAC_REG_CHAN_CNTRL_3(chan->channel);
> +
> + guard(mutex)(&st->lock);
> + ret = regmap_write(st->regmap, reg, raw);
> + if (ret)
> + return ret;
> +
> + /* synchronize channels */
> + ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
> + if (ret)
> + return ret;
> +
> + return len;
> +}
> +
> +static int axi_dac_phase_set(struct axi_dac_state *st,
> + const struct iio_chan_spec *chan,
> + const char *buf, size_t len, unsigned int tone)
> +{
> + int integer, frac, phase;
> + u32 raw, reg;
> + int ret;
> +
> + ret = iio_str_to_fixpoint(buf, 100000, &integer, &frac);
> + if (ret)
> + return ret;
> +
> + phase = integer * MEGA + frac;
> + if (phase < 0 || phase > AXI_DAC_2_PI_MEGA)
> + return -EINVAL;
> +
> + raw = DIV_ROUND_CLOSEST_ULL((u64)phase * U16_MAX, AXI_DAC_2_PI_MEGA);
> +
> + if (tone == AXI_DAC_PHASE_TONE_1)
Preference for a switch so it's clear there are only 2 choices.
> + reg = AXI_DAC_REG_CHAN_CNTRL_2(chan->channel);
> + else
> + reg = AXI_DAC_REG_CHAN_CNTRL_4(chan->channel);
> +
> + guard(mutex)(&st->lock);
> + ret = regmap_update_bits(st->regmap, reg, AXI_DAC_PHASE,
> + FIELD_PREP(AXI_DAC_PHASE, raw));
> + if (ret)
> + return ret;
> +
> + /* synchronize channels */
> + ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
> + if (ret)
> + return ret;
> +
> + return len;
> +}
> +
> +static int axi_dac_ext_info_set(struct iio_backend *back, uintptr_t private,
> + const struct iio_chan_spec *chan,
> + const char *buf, size_t len)
> +{
> + struct axi_dac_state *st = iio_backend_get_priv(back);
> +
> + switch (private) {
> + case AXI_DAC_FREQ_TONE_1:
> + case AXI_DAC_FREQ_TONE_2:
Same as the get path - convert to which tone here so that the enum becomes
a tone index for the functions called and the mapping to that single enum
is kept clear of the lower level code.
> + return axi_dac_frequency_set(st, chan, buf, len, private);
> + case AXI_DAC_SCALE_TONE_1:
> + case AXI_DAC_SCALE_TONE_2:
> + return axi_dac_scale_set(st, chan, buf, len, private);
> + case AXI_DAC_PHASE_TONE_1:
> + case AXI_DAC_PHASE_TONE_2:
> + return axi_dac_phase_set(st, chan, buf, len, private);
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +static int axi_dac_ext_info_get(struct iio_backend *back, uintptr_t private,
> + const struct iio_chan_spec *chan, char *buf)
> +{
> + struct axi_dac_state *st = iio_backend_get_priv(back);
> +
> + switch (private) {
> + case AXI_DAC_FREQ_TONE_1:
> + case AXI_DAC_FREQ_TONE_2:
> + return axi_dac_frequency_get(st, chan, buf, private);
I'd break out private as an unsigned int here and then - AXI_DAC_FREQ_TONE_1
so that it is just which tone for all the calls made from here.
Similar for the following ones.
> + case AXI_DAC_SCALE_TONE_1:
> + case AXI_DAC_SCALE_TONE_2:
> + return axi_dac_scale_get(st, chan, buf, private);
> + case AXI_DAC_PHASE_TONE_1:
> + case AXI_DAC_PHASE_TONE_2:
> + return axi_dac_phase_get(st, chan, buf, private);
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +static const struct iio_chan_spec_ext_info axi_dac_ext_info[] = {
> + IIO_BACKEND_EX_INFO("frequency0", IIO_SEPARATE, AXI_DAC_FREQ_TONE_1),
> + IIO_BACKEND_EX_INFO("frequency1", IIO_SEPARATE, AXI_DAC_FREQ_TONE_2),
> + IIO_BACKEND_EX_INFO("scale0", IIO_SEPARATE, AXI_DAC_SCALE_TONE_1),
> + IIO_BACKEND_EX_INFO("scale1", IIO_SEPARATE, AXI_DAC_SCALE_TONE_2),
> + IIO_BACKEND_EX_INFO("phase0", IIO_SEPARATE, AXI_DAC_PHASE_TONE_1),
> + IIO_BACKEND_EX_INFO("phase1", IIO_SEPARATE, AXI_DAC_PHASE_TONE_2),
> + {}
> +};
> +
> +static int axi_dac_extend_chan(struct iio_backend *back,
> + struct iio_chan_spec *chan)
> +{
> + struct axi_dac_state *st = iio_backend_get_priv(back);
> +
> + if (chan->type != IIO_ALTVOLTAGE)
> + return -EINVAL;
> + if (st->reg_config & AXI_DDS_DISABLE)
> + /* nothing to extend */
> + return 0;
> +
> + chan->ext_info = axi_dac_ext_info;
> +
> + return 0;
> +}
> +static int axi_dac_set_sample_rate(struct iio_backend *back, unsigned int chan,
> + u64 sample_rate)
> +{
> + struct axi_dac_state *st = iio_backend_get_priv(back);
> + unsigned int freq;
> + int ret, tone;
> +
> + if (!sample_rate)
> + return -EINVAL;
> + if (st->reg_config & AXI_DDS_DISABLE)
> + /* nothing to care if DDS is disabled */
Rephrase this. Is the point that the sample rate has no meaning without DDS or
that it has meaning but nothing to do here?
> + return 0;
> +
> + guard(mutex)(&st->lock);
> + /*
> + * If dac_clk is 0 then this must be the first time we're being notified
> + * about the interface sample rate. Hence, just update our internal
> + * variable and bail... If it's not 0, then we get the current DDS
> + * frequency (for the old rate) and update the registers for the new
> + * sample rate.
> + */
> + if (!st->dac_clk) {
> + st->dac_clk = sample_rate;
> + return 0;
> + }
> +
> + for (tone = 0; tone <= AXI_DAC_FREQ_TONE_2; tone++) {
> + ret = __axi_dac_frequency_get(st, chan, tone, &freq);
> + if (ret)
> + return ret;
> +
> + ret = __axi_dac_frequency_set(st, chan, sample_rate, tone, freq);
> + if (ret)
> + return ret;
> + }
> +
> + st->dac_clk = sample_rate;
> +
> + return 0;
> +}
next prev parent reply other threads:[~2024-03-28 15:35 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-03-28 13:22 [PATCH 00/10] iio: dac: support IIO backends on the output direction Nuno Sa via B4 Relay
2024-03-28 13:22 ` [PATCH 01/10] iio: buffer: add helper for setting direction Nuno Sa via B4 Relay
2024-03-28 14:36 ` Jonathan Cameron
2024-03-28 15:18 ` Nuno Sá
2024-03-28 15:54 ` Jonathan Cameron
2024-03-28 13:22 ` [PATCH 02/10] iio: buffer-dma: Rename iio_dma_buffer_data_available() Nuno Sa via B4 Relay
2024-03-28 13:22 ` [PATCH 03/10] iio: buffer-dma: Enable buffer write support Nuno Sa via B4 Relay
2024-03-28 13:22 ` [PATCH 04/10] iio: buffer-dmaengine: Support specifying buffer direction Nuno Sa via B4 Relay
2024-03-28 13:22 ` [PATCH 05/10] iio: buffer-dmaengine: Enable write support Nuno Sa via B4 Relay
2024-03-28 13:22 ` [PATCH 06/10] dt-bindings: iio: dac: add bindings doc for AXI DAC driver Nuno Sa via B4 Relay
2024-03-29 18:46 ` David Lechner
2024-04-02 7:51 ` Nuno Sá
2024-04-01 13:59 ` Rob Herring
2024-04-04 10:03 ` Nuno Sá
2024-03-28 13:22 ` [PATCH 07/10] dt-bindings: iio: dac: add bindings doc for AD9739A Nuno Sa via B4 Relay
2024-03-29 19:06 ` David Lechner
2024-03-30 18:27 ` Krzysztof Kozlowski
2024-04-02 7:49 ` Nuno Sá
2024-04-02 7:50 ` Nuno Sá
2024-04-01 14:02 ` Rob Herring
2024-03-28 13:22 ` [PATCH 08/10] iio: backend: add new functionality Nuno Sa via B4 Relay
2024-03-28 15:16 ` Jonathan Cameron
2024-03-28 15:42 ` Nuno Sá
2024-03-28 15:59 ` Jonathan Cameron
2024-03-28 16:54 ` Nuno Sá
2024-03-28 13:22 ` [PATCH 09/10] iio: dac: add support for AXI DAC IP core Nuno Sa via B4 Relay
2024-03-28 15:35 ` Jonathan Cameron [this message]
2024-03-28 16:43 ` Nuno Sá
2024-03-28 13:22 ` [PATCH 10/10] iio: dac: support the ad9739a RF DAC Nuno Sa via B4 Relay
2024-03-28 15:51 ` Jonathan Cameron
2024-03-28 16:37 ` Nuno Sá
2024-03-28 16:52 ` Jonathan Cameron
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240328153542.7ddb897c@jic23-huawei \
--to=jic23@kernel.org \
--cc=Michael.Hennerich@analog.com \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=devnull+nuno.sa.analog.com@kernel.org \
--cc=dragos.bogdan@analog.com \
--cc=krzysztof.kozlowski+dt@linaro.org \
--cc=lars@metafoo.de \
--cc=linux-iio@vger.kernel.org \
--cc=nuno.sa@analog.com \
--cc=olivier.moysan@foss.st.com \
--cc=robh@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox