All of lore.kernel.org
 help / color / mirror / Atom feed
From: Frank Li <Frank.li@oss.nxp.com>
To: "Nuno Sá" <nuno.sa@analog.com>
Cc: linux-kernel@vger.kernel.org, dmaengine@vger.kernel.org,
	linux-arm-msm@vger.kernel.org,
	linux-stm32@st-md-mailman.stormreply.com,
	linux-arm-kernel@lists.infradead.org, linux-iio@vger.kernel.org,
	linux-sound@vger.kernel.org, linux-spi@vger.kernel.org,
	"Vinod Koul" <vkoul@kernel.org>, "Frank Li" <Frank.Li@kernel.org>,
	"Lars-Peter Clausen" <lars@metafoo.de>,
	"Eugeniy Paltsev" <Eugeniy.Paltsev@synopsys.com>,
	"Amélie Delaunay" <amelie.delaunay@foss.st.com>,
	"Maxime Coquelin" <mcoquelin.stm32@gmail.com>,
	"Alexandre Torgue" <alexandre.torgue@foss.st.com>,
	"Jonathan Cameron" <jic23@kernel.org>,
	"David Lechner" <dlechner@baylibre.com>,
	"Andy Shevchenko" <andy@kernel.org>,
	"Jaroslav Kysela" <perex@perex.cz>,
	"Takashi Iwai" <tiwai@suse.com>,
	"Mark Brown" <broonie@kernel.org>
Subject: Re: [PATCH v6 1/9] dmaengine: Support bus widths of 32 bytes and above
Date: Wed, 14 Oct 2026 10:28:00 -0500	[thread overview]
Message-ID: <as-fgDViIz4oSap7@SMW015318> (raw)
In-Reply-To: <20261014-dmaengine-support-wider-dma-masks-v6-1-5b3ffdef8191@analog.com>

On Wed, Oct 14, 2026 at 03:19:11PM +0100, Nuno Sá wrote:
> The src_addr_widths and dst_addr_widths capability masks encode each
> supported width as a bit whose position equals the corresponding
> enum dma_slave_buswidth value (e.g. DMA_SLAVE_BUSWIDTH_4_BYTES sets bit
> 4). As these masks are plain u32, widths of 32 bytes and above
> (DMA_SLAVE_BUSWIDTH_32/64/128_BYTES map to bits 32, 64 and 128) cannot
> be represented at all.
>
> Introduce bitmap-based bus width capabilities that span the full enum
> range, through a new dma_buswidth_mask_t type modeled after
> dma_cap_mask_t. To allow DMA controller drivers to be converted
> incrementally, the legacy dma_device u32 fields are kept alongside the
> new masks and the core folds a legacy-only driver's u32 into the mask
> when the device is registered, so consumers only ever have to look at
> the mask.
>
> The fold is bidirectional while both representations coexist. A driver
> that only fills in the legacy u32 gets its mask derived from it, so the
> consumers already converted see it. A driver that only fills in the mask
> gets its legacy u32 derived from the mask, so the consumers not
> converted yet, which read the legacy dma_slave_caps fields, keep working.
>
> On top of that, dma_get_slave_caps() derives the legacy dma_slave_caps
> masks from the new ones when a device_caps() callback adjusted them, so
> that a converted controller narrowing its per-channel capabilities is
> still seen by the consumers not converted yet, while a driver adjusting
> the legacy masks directly keeps working.
>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
>
> ---

Reviewed-by: Frank Li <Frank.Li@nxp.com>

>
> Andy, did not take your tag given that you're the one asking for the
> split and since we ended up not doing it, not sure if the tag is
> to be kept :)
> ---
>  drivers/dma/dmaengine.c   |  50 +++++++++++-
>  include/linux/dmaengine.h | 197 +++++++++++++++++++++++++++++++++++++++++++---
>  2 files changed, 234 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
> index c5fef57b5adf..b47008595332 100644
> --- a/drivers/dma/dmaengine.c
> +++ b/drivers/dma/dmaengine.c
> @@ -33,6 +33,7 @@
>
>  #include <linux/acpi.h>
>  #include <linux/acpi_dma.h>
> +#include <linux/bitmap.h>
>  #include <linux/device.h>
>  #include <linux/dma-mapping.h>
>  #include <linux/dmaengine.h>
> @@ -592,6 +593,8 @@ int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps)
>  	if (!device->directions)
>  		return -ENXIO;
>
> +	dmaengine_bus_width_copy(&caps->src_bus_widths, &device->src_bus_widths);
> +	dmaengine_bus_width_copy(&caps->dst_bus_widths, &device->dst_bus_widths);
>  	caps->src_addr_widths = device->src_addr_widths;
>  	caps->dst_addr_widths = device->dst_addr_widths;
>  	caps->directions = device->directions;
> @@ -611,9 +614,31 @@ int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps)
>  	 * callback to override the generic capabilities with
>  	 * channel-specific ones.
>  	 */
> -	if (device->device_caps)
> +	if (device->device_caps) {
>  		device->device_caps(chan, caps);
>
> +		/*
> +		 * A driver already converted to the bus width interface
> +		 * adjusts the masks, so derive the legacy capabilities from
> +		 * them for the consumers not converted yet. Drivers not
> +		 * converted adjust the legacy capabilities directly, in which
> +		 * case there is nothing to do.
> +		 *
> +		 * Goes away with the legacy dma_slave_caps fields.
> +		 */
> +		if (!bitmap_equal(caps->src_bus_widths.bits,
> +				  device->src_bus_widths.bits,
> +				  DMA_SLAVE_BUSWIDTH_MAX))
> +			caps->src_addr_widths = bitmap_read(caps->src_bus_widths.bits,
> +							    0, 32);
> +
> +		if (!bitmap_equal(caps->dst_bus_widths.bits,
> +				  device->dst_bus_widths.bits,
> +				  DMA_SLAVE_BUSWIDTH_MAX))
> +			caps->dst_addr_widths = bitmap_read(caps->dst_bus_widths.bits,
> +							    0, 32);
> +	}
> +
>  	return 0;
>  }
>  EXPORT_SYMBOL_GPL(dma_get_slave_caps);
> @@ -1173,6 +1198,27 @@ void dma_async_device_channel_unregister(struct dma_device *device,
>  }
>  EXPORT_SYMBOL_GPL(dma_async_device_channel_unregister);
>
> +/*
> + * DMA controller drivers not converted to the bus width helpers only fill in
> + * the legacy u32 masks, which cannot hold widths of 32 bytes and above. Fold
> + * them into the mask so that consumers only ever have to look at the mask.
> + *
> + * Goes away with the legacy dma_device fields.
> + */
> +static void dma_device_fold_legacy_bus_widths(struct dma_device *device)
> +{
> +	if (bitmap_empty(device->src_bus_widths.bits, DMA_SLAVE_BUSWIDTH_MAX))
> +		bitmap_from_arr32(device->src_bus_widths.bits, &device->src_addr_widths, 32);
> +	/* consumers not converted yet still read the legacy caps */
> +	else if (!device->src_addr_widths)
> +		device->src_addr_widths = bitmap_read(device->src_bus_widths.bits, 0, 32);
> +
> +	if (bitmap_empty(device->dst_bus_widths.bits, DMA_SLAVE_BUSWIDTH_MAX))
> +		bitmap_from_arr32(device->dst_bus_widths.bits, &device->dst_addr_widths, 32);
> +	else if (!device->dst_addr_widths)
> +		device->dst_addr_widths = bitmap_read(device->dst_bus_widths.bits, 0, 32);
> +}
> +
>  /**
>   * dma_async_device_register - registers DMA devices found
>   * @device:	pointer to &struct dma_device
> @@ -1234,6 +1280,8 @@ int dma_async_device_register(struct dma_device *device)
>  		dev_dbg(device->dev,
>  			 "WARN: Device release is not defined so it is not safe to unbind this driver while in use\n");
>
> +	dma_device_fold_legacy_bus_widths(device);
> +
>  	kref_init(&device->ref);
>
>  	/* note: this only matters in the
> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
> index 71cde09ff240..b190bfb42df3 100644
> --- a/include/linux/dmaengine.h
> +++ b/include/linux/dmaengine.h
> @@ -427,8 +427,168 @@ enum dma_slave_buswidth {
>  	DMA_SLAVE_BUSWIDTH_32_BYTES = 32,
>  	DMA_SLAVE_BUSWIDTH_64_BYTES = 64,
>  	DMA_SLAVE_BUSWIDTH_128_BYTES = 128,
> +	DMA_SLAVE_BUSWIDTH_MAX
>  };
>
> +/**
> + * typedef dma_buswidth_mask_t - bus width capabilities bitmap modeled after
> + * dma_cap_mask_t.
> + *
> + * Each supported bus width is represented by the bit whose position equals the
> + * corresponding enum dma_slave_buswidth value, e.g. a device supporting a bus
> + * width of 4 bytes has bit 4 set.
> + */
> +typedef struct {
> +	DECLARE_BITMAP(bits, DMA_SLAVE_BUSWIDTH_MAX);
> +} dma_buswidth_mask_t;
> +
> +/**
> + * dmaengine_bus_width_valid - test if a bus width is a valid one
> + * @width: bus width to validate
> + *
> + * Return: true if @width is a valid &enum dma_slave_buswidth, false otherwise.
> + */
> +static inline bool dmaengine_bus_width_valid(enum dma_slave_buswidth width)
> +{
> +	switch (width) {
> +	case DMA_SLAVE_BUSWIDTH_UNDEFINED:
> +	case DMA_SLAVE_BUSWIDTH_1_BYTE:
> +	case DMA_SLAVE_BUSWIDTH_2_BYTES:
> +	case DMA_SLAVE_BUSWIDTH_3_BYTES:
> +	case DMA_SLAVE_BUSWIDTH_4_BYTES:
> +	case DMA_SLAVE_BUSWIDTH_8_BYTES:
> +	case DMA_SLAVE_BUSWIDTH_16_BYTES:
> +	case DMA_SLAVE_BUSWIDTH_32_BYTES:
> +	case DMA_SLAVE_BUSWIDTH_64_BYTES:
> +	case DMA_SLAVE_BUSWIDTH_128_BYTES:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +/**
> + * dmaengine_bus_width_set_many - set the supported bus widths
> + * @mask: bus width mask
> + * @widths: array of supported bus widths
> + * @n_widths: number of entries in @widths
> + *
> + * Like __set_bit(), the widths are added to @mask and the bits already set in it
> + * are left untouched, so @mask must be initialized by the caller.
> + *
> + * Return: 0 on success, -EINVAL if @widths contains an invalid bus width. Note
> + * that the bus widths validated before the failing one are still set.
> + */
> +static inline int dmaengine_bus_width_set_many(dma_buswidth_mask_t *mask,
> +					       const enum dma_slave_buswidth *widths,
> +					       unsigned int n_widths)
> +{
> +	for (unsigned int i = 0; i < n_widths; i++) {
> +		if (!dmaengine_bus_width_valid(widths[i]))
> +			return -EINVAL;
> +
> +		__set_bit(widths[i], mask->bits);
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * dmaengine_bus_width_set - set a single supported bus width
> + * @mask: bus width mask
> + * @width: supported bus width
> + *
> + * Like __set_bit(), @width is added to @mask and the bits already set in it are
> + * left untouched, so @mask must be initialized by the caller.
> + *
> + * Return: 0 on success, -EINVAL if @width is invalid.
> + */
> +static inline int dmaengine_bus_width_set(dma_buswidth_mask_t *mask,
> +					  enum dma_slave_buswidth width)
> +{
> +	return dmaengine_bus_width_set_many(mask, &width, 1);
> +}
> +
> +/**
> + * dmaengine_bus_width_clear - remove a bus width from a bus width mask
> + * @mask: bus width mask
> + * @width: bus width to clear
> + *
> + * Return: 0 on success, -EINVAL if @width is invalid.
> + */
> +static inline int dmaengine_bus_width_clear(dma_buswidth_mask_t *mask,
> +					    enum dma_slave_buswidth width)
> +{
> +	if (!dmaengine_bus_width_valid(width))
> +		return -EINVAL;
> +
> +	__clear_bit(width, mask->bits);
> +
> +	return 0;
> +}
> +
> +/**
> + * dmaengine_bus_width_test - test if a bus width is part of a bus width mask
> + * @mask: bus width mask
> + * @width: bus width to test
> + *
> + * Return: true if @width is set in @mask, false otherwise.
> + */
> +static inline bool dmaengine_bus_width_test(const dma_buswidth_mask_t *mask,
> +					    enum dma_slave_buswidth width)
> +{
> +	if (!dmaengine_bus_width_valid(width))
> +		return false;
> +
> +	return test_bit(width, mask->bits);
> +}
> +
> +/**
> + * dmaengine_bus_width_min - get the smallest bus width of a bus width mask
> + * @mask: bus width mask
> + *
> + * Return: the smallest bus width set in @mask, or
> + * %DMA_SLAVE_BUSWIDTH_UNDEFINED if @mask is empty.
> + */
> +static inline enum dma_slave_buswidth
> +dmaengine_bus_width_min(const dma_buswidth_mask_t *mask)
> +{
> +	enum dma_slave_buswidth width;
> +
> +	width = find_first_bit(mask->bits, DMA_SLAVE_BUSWIDTH_MAX);
> +	if (width == DMA_SLAVE_BUSWIDTH_MAX)
> +		return DMA_SLAVE_BUSWIDTH_UNDEFINED;
> +
> +	return width;
> +}
> +
> +/**
> + * dmaengine_bus_width_copy - copy a bus width mask
> + * @dst: bus width mask to copy to
> + * @src: bus width mask to copy from
> + */
> +static inline void dmaengine_bus_width_copy(dma_buswidth_mask_t *dst,
> +					    const dma_buswidth_mask_t *src)
> +{
> +	bitmap_copy(dst->bits, src->bits, DMA_SLAVE_BUSWIDTH_MAX);
> +}
> +
> +/**
> + * dmaengine_bus_width_and - intersect two bus width masks
> + * @dst: bus width mask to store the result in
> + * @src1: first bus width mask
> + * @src2: second bus width mask
> + *
> + * Return: true if @dst has at least one bus width set, false otherwise.
> + */
> +static inline bool dmaengine_bus_width_and(dma_buswidth_mask_t *dst,
> +					   const dma_buswidth_mask_t *src1,
> +					   const dma_buswidth_mask_t *src2)
> +{
> +	return bitmap_and(dst->bits, src1->bits, src2->bits,
> +			  DMA_SLAVE_BUSWIDTH_MAX);
> +}
> +
>  /**
>   * struct dma_slave_config - dma slave channel runtime config
>   * @direction: whether the data shall go in or out on this slave
> @@ -523,10 +683,11 @@ enum dma_residue_granularity {
>
>  /**
>   * struct dma_slave_caps - expose capabilities of a slave channel only
> - * @src_addr_widths: bit mask of src addr widths the channel supports.
> - *	Width is specified in bytes, e.g. for a channel supporting
> - *	a width of 4 the mask should have BIT(4) set.
> - * @dst_addr_widths: bit mask of dst addr widths the channel supports
> + * @src_bus_widths: mask of source bus widths the channel supports.
> + * @src_addr_widths: legacy bit mask of source bus widths the channel supports.
> + * @dst_bus_widths: mask of destination bus widths the channel supports.
> + * @dst_addr_widths: legacy bit mask of destination bus widths the channel
> + *	supports.
>   * @directions: bit mask of slave directions the channel supports.
>   *	Since the enum dma_transfer_direction is not defined as bit flag for
>   *	each type, the dma controller should set BIT(<TYPE>) and same
> @@ -545,8 +706,14 @@ enum dma_residue_granularity {
>   * resubmitted multiple times
>   */
>  struct dma_slave_caps {
> -	u32 src_addr_widths;
> -	u32 dst_addr_widths;
> +	struct {
> +		dma_buswidth_mask_t src_bus_widths;
> +		u32 src_addr_widths;
> +	};
> +	struct {
> +		dma_buswidth_mask_t dst_bus_widths;
> +		u32 dst_addr_widths;
> +	};
>  	u32 directions;
>  	u32 min_burst;
>  	u32 max_burst;
> @@ -839,10 +1006,10 @@ struct dma_filter {
>   * @dev: struct device reference for dma mapping api
>   * @owner: owner module (automatically set based on the provided dev)
>   * @chan_ida: unique channel ID
> - * @src_addr_widths: bit mask of src addr widths the device supports
> - *	Width is specified in bytes, e.g. for a device supporting
> - *	a width of 4 the mask should have BIT(4) set.
> - * @dst_addr_widths: bit mask of dst addr widths the device supports
> + * @src_bus_widths: mask of source bus widths the device supports.
> + * @src_addr_widths: legacy bit mask of source bus widths the device supports.
> + * @dst_bus_widths: mask of destination bus widths the device supports.
> + * @dst_addr_widths: legacy bit mask of destination bus widths the device supports.
>   * @directions: bit mask of slave directions the device supports.
>   *	Since the enum dma_transfer_direction is not defined as bit flag for
>   *	each type, the dma controller should set BIT(<TYPE>) and same
> @@ -924,8 +1091,14 @@ struct dma_device {
>  	struct module *owner;
>  	struct ida chan_ida;
>
> -	u32 src_addr_widths;
> -	u32 dst_addr_widths;
> +	struct {
> +		dma_buswidth_mask_t src_bus_widths;
> +		u32 src_addr_widths;
> +	};
> +	struct {
> +		dma_buswidth_mask_t dst_bus_widths;
> +		u32 dst_addr_widths;
> +	};
>  	u32 directions;
>  	u32 min_burst;
>  	u32 max_burst;
>
> --
> 2.56.0
>

  reply	other threads:[~2026-10-14 15:28 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-14 14:19 [PATCH v6 0/9] dmaengine: Support bus widths of 32 bytes and above Nuno Sá
2026-10-14 14:19 ` [PATCH v6 1/9] " Nuno Sá
2026-10-14 15:28   ` Frank Li [this message]
2026-10-14 14:19 ` [PATCH v6 2/9] dmaengine: dma-axi-dmac: Use bus width capability helpers Nuno Sá
2026-10-14 14:33   ` sashiko-bot
2026-10-14 14:19 ` [PATCH v6 3/9] dmaengine: dw-axi-dmac: " Nuno Sá
2026-10-14 14:19 ` [PATCH v6 4/9] dmaengine: qcom: gpi: " Nuno Sá
2026-10-14 14:19 ` [PATCH v6 5/9] dmaengine: stm32-dma3: " Nuno Sá
2026-10-14 14:19 ` [PATCH v6 6/9] iio: buffer-dmaengine: Use dma_slave_caps bus width accessors Nuno Sá
2026-10-14 14:19 ` [PATCH v6 7/9] ALSA: pcm_dmaengine: Use dma_slave_caps bus width helpers Nuno Sá
2026-10-14 14:19 ` [PATCH v6 8/9] spi: dw: " Nuno Sá
2026-10-14 14:19 ` [PATCH v6 9/9] dmaengine: Drop legacy bus width fields from dma_slave_caps Nuno Sá

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=as-fgDViIz4oSap7@SMW015318 \
    --to=frank.li@oss.nxp.com \
    --cc=Eugeniy.Paltsev@synopsys.com \
    --cc=Frank.Li@kernel.org \
    --cc=alexandre.torgue@foss.st.com \
    --cc=amelie.delaunay@foss.st.com \
    --cc=andy@kernel.org \
    --cc=broonie@kernel.org \
    --cc=dlechner@baylibre.com \
    --cc=dmaengine@vger.kernel.org \
    --cc=jic23@kernel.org \
    --cc=lars@metafoo.de \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=linux-iio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-sound@vger.kernel.org \
    --cc=linux-spi@vger.kernel.org \
    --cc=linux-stm32@st-md-mailman.stormreply.com \
    --cc=mcoquelin.stm32@gmail.com \
    --cc=nuno.sa@analog.com \
    --cc=perex@perex.cz \
    --cc=tiwai@suse.com \
    --cc=vkoul@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.