mirror of
https://github.com/torvalds/linux.git
synced 2026-01-25 15:03:52 +08:00
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: Charles Keepax <ckeepax@opensource.cirrus.com> Link: https://patch.msgid.link/20250107154408.814455-4-ckeepax@opensource.cirrus.com Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.dev> Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
committed by
Mark Brown
parent
b21468e83b
commit
fdd9ef3dce
@@ -1,6 +1,7 @@
|
||||
// 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 (ret < 0)
|
||||
return ret;
|
||||
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, ®map_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, ®map_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, ®map_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, ®map_sdw_mbq, ctx,
|
||||
config, lock_key, lock_name);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__devm_regmap_init_sdw_mbq);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user