]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
regmap: sdw-mbq: Add support for further MBQ register sizes
authorCharles Keepax <ckeepax@opensource.cirrus.com>
Tue, 7 Jan 2025 15:44:05 +0000 (15:44 +0000)
committerMark Brown <broonie@kernel.org>
Tue, 7 Jan 2025 20:20:59 +0000 (20:20 +0000)
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>
drivers/base/regmap/regmap-sdw-mbq.c
include/linux/regmap.h

index c99eada83780c997a70a40fb5553c8eccff1f7c4..1bd2773b11a457132e58c0275d19a72a2692ac6e 100644 (file)
@@ -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>
 #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, &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);
 
index fd41baccbf3eb97a36ebe16c40690a1d8c5d2671..dd96a22f56578c561493004bf792d77d06a53706 100644 (file)
@@ -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