Unverified Commit fdd9ef3d authored by Charles Keepax's avatar Charles Keepax Committed by Mark Brown
Browse files

regmap: sdw-mbq: Add support for further MBQ register sizes



SoundWire MBQ register maps typically contain a variety of register
sizes, which doesn't map ideally to the regmap abstraction which
expects register maps to have a consistent size. Currently the MBQ
register map only allows 16-bit registers to be defined, however
this leads to complex CODEC driver implementations with an 8-bit
register map and a 16-bit MBQ, every control will then have a custom
get and put handler that allows them to access different register
maps. Further more 32-bit MBQ quantities are not currently supported.

Add support for additional MBQ sizes and to avoid the complexity
of multiple register maps treat the val_size as a maximum size for
the register map. Within the regmap use an ancillary callback to
determine how many bytes to actually read/write to the hardware for
a specific register. In the case that no callback is defined the
behaviour defaults back to the existing behaviour of a fixed size
register map.

Signed-off-by: default avatarCharles Keepax <ckeepax@opensource.cirrus.com>
Link: https://patch.msgid.link/20250107154408.814455-4-ckeepax@opensource.cirrus.com


Reviewed-by: default avatarPierre-Louis Bossart <pierre-louis.bossart@linux.dev>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent b21468e8
Loading
Loading
Loading
Loading
+94 −20
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
// Copyright(c) 2020 Intel Corporation.

#include <linux/bits.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/module.h>
@@ -9,35 +10,77 @@
#include <linux/soundwire/sdw_registers.h>
#include "internal.h"

struct regmap_mbq_context {
	struct device *dev;

	struct regmap_sdw_mbq_cfg cfg;

	int val_size;
};

static int regmap_sdw_mbq_size(struct regmap_mbq_context *ctx, unsigned int reg)
{
	int size = ctx->val_size;

	if (ctx->cfg.mbq_size) {
		size = ctx->cfg.mbq_size(ctx->dev, reg);
		if (!size || size > ctx->val_size)
			return -EINVAL;
	}

	return size;
}

static int regmap_sdw_mbq_write(void *context, unsigned int reg, unsigned int val)
{
	struct device *dev = context;
	struct regmap_mbq_context *ctx = context;
	struct device *dev = ctx->dev;
	struct sdw_slave *slave = dev_to_sdw_dev(dev);
	int mbq_size = regmap_sdw_mbq_size(ctx, reg);
	int shift = mbq_size * BITS_PER_BYTE;
	int ret;

	ret = sdw_write_no_pm(slave, SDW_SDCA_MBQ_CTL(reg), (val >> 8) & 0xff);
	if (mbq_size < 0)
		return mbq_size;

	while (--mbq_size > 0) {
		shift -= BITS_PER_BYTE;

		ret = sdw_write_no_pm(slave, SDW_SDCA_MBQ_CTL(reg),
				      (val >> shift) & 0xff);
		if (ret < 0)
			return ret;
	}

	return sdw_write_no_pm(slave, reg, val & 0xff);
}

static int regmap_sdw_mbq_read(void *context, unsigned int reg, unsigned int *val)
{
	struct device *dev = context;
	struct regmap_mbq_context *ctx = context;
	struct device *dev = ctx->dev;
	struct sdw_slave *slave = dev_to_sdw_dev(dev);
	int read0;
	int read1;
	int mbq_size = regmap_sdw_mbq_size(ctx, reg);
	int shift = BITS_PER_BYTE;
	int read;

	read0 = sdw_read_no_pm(slave, reg);
	if (read0 < 0)
		return read0;
	if (mbq_size < 0)
		return mbq_size;

	read1 = sdw_read_no_pm(slave, SDW_SDCA_MBQ_CTL(reg));
	if (read1 < 0)
		return read1;
	read = sdw_read_no_pm(slave, reg);
	if (read < 0)
		return read;

	*val = (read1 << 8) | read0;
	*val = read;

	while (--mbq_size > 0) {
		read = sdw_read_no_pm(slave, SDW_SDCA_MBQ_CTL(reg));
		if (read < 0)
			return read;

		*val |= read << shift;
		shift += BITS_PER_BYTE;
	}

	return 0;
}
@@ -51,8 +94,7 @@ static const struct regmap_bus regmap_sdw_mbq = {

static int regmap_sdw_mbq_config_check(const struct regmap_config *config)
{
	/* MBQ-based controls are only 16-bits for now */
	if (config->val_bits != 16)
	if (config->val_bits > (sizeof(unsigned int) * BITS_PER_BYTE))
		return -ENOTSUPP;

	/* Registers are 32 bits wide */
@@ -65,35 +107,67 @@ static int regmap_sdw_mbq_config_check(const struct regmap_config *config)
	return 0;
}

static struct regmap_mbq_context *
regmap_sdw_mbq_gen_context(struct device *dev,
			   const struct regmap_config *config,
			   const struct regmap_sdw_mbq_cfg *mbq_config)
{
	struct regmap_mbq_context *ctx;

	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
	if (!ctx)
		return ERR_PTR(-ENOMEM);

	ctx->dev = dev;
	ctx->val_size = config->val_bits / BITS_PER_BYTE;

	if (mbq_config)
		ctx->cfg = *mbq_config;

	return ctx;
}

struct regmap *__regmap_init_sdw_mbq(struct sdw_slave *sdw,
				     const struct regmap_config *config,
				     const struct regmap_sdw_mbq_cfg *mbq_config,
				     struct lock_class_key *lock_key,
				     const char *lock_name)
{
	struct regmap_mbq_context *ctx;
	int ret;

	ret = regmap_sdw_mbq_config_check(config);
	if (ret)
		return ERR_PTR(ret);

	return __regmap_init(&sdw->dev, &regmap_sdw_mbq,
			&sdw->dev, config, lock_key, lock_name);
	ctx = regmap_sdw_mbq_gen_context(&sdw->dev, config, mbq_config);
	if (IS_ERR(ctx))
		return ERR_CAST(ctx);

	return __regmap_init(&sdw->dev, &regmap_sdw_mbq, ctx,
			     config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__regmap_init_sdw_mbq);

struct regmap *__devm_regmap_init_sdw_mbq(struct sdw_slave *sdw,
					  const struct regmap_config *config,
					  const struct regmap_sdw_mbq_cfg *mbq_config,
					  struct lock_class_key *lock_key,
					  const char *lock_name)
{
	struct regmap_mbq_context *ctx;
	int ret;

	ret = regmap_sdw_mbq_config_check(config);
	if (ret)
		return ERR_PTR(ret);

	return __devm_regmap_init(&sdw->dev, &regmap_sdw_mbq,
			&sdw->dev, config, lock_key, lock_name);
	ctx = regmap_sdw_mbq_gen_context(&sdw->dev, config, mbq_config);
	if (IS_ERR(ctx))
		return ERR_CAST(ctx);

	return __devm_regmap_init(&sdw->dev, &regmap_sdw_mbq, ctx,
				  config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_sdw_mbq);

+45 −2
Original line number Diff line number Diff line
@@ -506,6 +506,17 @@ struct regmap_range_cfg {
	unsigned int window_len;
};

/**
 * struct regmap_sdw_mbq_cfg - Configuration for Multi-Byte Quantities
 *
 * @mbq_size: Callback returning the actual size of the given register.
 *
 * Provides additional configuration required for SoundWire MBQ register maps.
 */
struct regmap_sdw_mbq_cfg {
	int (*mbq_size)(struct device *dev, unsigned int reg);
};

struct regmap_async;

typedef int (*regmap_hw_write)(void *context, const void *data,
@@ -652,6 +663,7 @@ struct regmap *__regmap_init_sdw(struct sdw_slave *sdw,
				 const char *lock_name);
struct regmap *__regmap_init_sdw_mbq(struct sdw_slave *sdw,
				     const struct regmap_config *config,
				     const struct regmap_sdw_mbq_cfg *mbq_config,
				     struct lock_class_key *lock_key,
				     const char *lock_name);
struct regmap *__regmap_init_spi_avmm(struct spi_device *spi,
@@ -713,6 +725,7 @@ struct regmap *__devm_regmap_init_sdw(struct sdw_slave *sdw,
				 const char *lock_name);
struct regmap *__devm_regmap_init_sdw_mbq(struct sdw_slave *sdw,
					  const struct regmap_config *config,
					  const struct regmap_sdw_mbq_cfg *mbq_config,
					  struct lock_class_key *lock_key,
					  const char *lock_name);
struct regmap *__devm_regmap_init_slimbus(struct slim_device *slimbus,
@@ -942,7 +955,22 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
 */
#define regmap_init_sdw_mbq(sdw, config)					\
	__regmap_lockdep_wrapper(__regmap_init_sdw_mbq, #config,		\
				sdw, config)
				sdw, config, NULL)

/**
 * regmap_init_sdw_mbq_cfg() - Initialise MBQ SDW register map with config
 *
 * @sdw: Device that will be interacted with
 * @config: Configuration for register map
 * @mbq_config: Properties for the MBQ registers
 *
 * The return value will be an ERR_PTR() on error or a valid pointer
 * to a struct regmap. The regmap will be automatically freed by the
 * device management code.
 */
#define regmap_init_sdw_mbq_cfg(sdw, config, mbq_config)		\
	__regmap_lockdep_wrapper(__regmap_init_sdw_mbq, #config,	\
				sdw, config, mbq_config)

/**
 * regmap_init_spi_avmm() - Initialize register map for Intel SPI Slave
@@ -1155,7 +1183,22 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
 */
#define devm_regmap_init_sdw_mbq(sdw, config)			\
	__regmap_lockdep_wrapper(__devm_regmap_init_sdw_mbq, #config,   \
				sdw, config)
				sdw, config, NULL)

/**
 * devm_regmap_init_sdw_mbq_cfg() - Initialise managed MBQ SDW register map with config
 *
 * @sdw: Device that will be interacted with
 * @config: Configuration for register map
 * @mbq_config: Properties for the MBQ registers
 *
 * The return value will be an ERR_PTR() on error or a valid pointer
 * to a struct regmap. The regmap will be automatically freed by the
 * device management code.
 */
#define devm_regmap_init_sdw_mbq_cfg(sdw, config, mbq_config)	\
	__regmap_lockdep_wrapper(__devm_regmap_init_sdw_mbq,	\
				#config, sdw, config, mbq_config)

/**
 * devm_regmap_init_slimbus() - Initialise managed register map