[alsa-devel] [PATCH 0/4] add regmap_filelds and use it on Renesas Sound driver
Hi Mark
These patches add new regmap_filelds API on kernel. ~ and use it on Renesas sound driver instead of original method. It can care about multi port register offset via regmap.
0xAAAA + 0x40 -- port 0 -- regX regY regZ 0xAAAA + 0x80 -- port 1 -- regX regY regZ
This case, current API needs 2 (= port) x 3 (= regX/Y/Z) regmap_fileld, but this new API can care about all port via 3 regmap_filelds with port ID.
I'm not sure that regmap_filelds is good naming or not. Please let me know if you have good naming idea.
These are based on below branchs regmap/for-next + asoc/for-next
Kuninori Morimoto (4): regmap: add regmap_field_update_bits() regmap: Add regmap_fields APIs ASoC: rsnd: gen: rsnd_gen_ops cares .probe and .remove ASoC: rsnd: use regmap instead of original register mapping method
drivers/base/regmap/internal.h | 3 + drivers/base/regmap/regmap.c | 117 +++++++++++++++++ include/linux/regmap.h | 14 +++ sound/soc/sh/rcar/core.c | 45 ------- sound/soc/sh/rcar/gen.c | 270 +++++++++++++++++++++++++--------------- 5 files changed, 307 insertions(+), 142 deletions(-)
Best regards --- Kuninori Morimoto
Current regmap_field is supporting read/write functions. This patch adds new update_bits function for it.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- drivers/base/regmap/regmap.c | 20 ++++++++++++++++++++ include/linux/regmap.h | 2 ++ 2 files changed, 22 insertions(+)
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index e0d0c7d..7ae90d8 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1360,6 +1360,26 @@ int regmap_field_write(struct regmap_field *field, unsigned int val) } EXPORT_SYMBOL_GPL(regmap_field_write);
+/** + * regmap_field_update_bits(): Perform a read/modify/write cycle + * on the register field + * + * @field: Register field to write to + * @mask: Bitmask to change + * @val: Value to be written + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_field_update_bits(struct regmap_field *field, unsigned int mask, unsigned int val) +{ + mask = (mask << field->shift) & field->mask; + + return regmap_update_bits(field->regmap, field->reg, + mask, val << field->shift); +} +EXPORT_SYMBOL_GPL(regmap_field_write); + /* * regmap_bulk_write(): Write multiple registers to the device * diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 75981d0..1425c91 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -446,6 +446,8 @@ void devm_regmap_field_free(struct device *dev, struct regmap_field *field);
int regmap_field_read(struct regmap_field *field, unsigned int *val); int regmap_field_write(struct regmap_field *field, unsigned int val); +int regmap_field_update_bits(struct regmap_field *field, + unsigned int mask, unsigned int val);
/** * Description of an IRQ for the generic regmap irq_chip.
Current Linux kernel is supporting regmap_field method and it is very useful feature. It needs one regmap_filed for one register access.
OTOH, there is multi port device which has many same registers in the market. The difference for each port register access is only its address offset.
Current API needs many regmap_field for such device, but it is not good. This patch adds new regmap_fileds API which can care about multi port/offset access via regmap.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- drivers/base/regmap/internal.h | 3 ++ drivers/base/regmap/regmap.c | 97 ++++++++++++++++++++++++++++++++++++++++ include/linux/regmap.h | 12 +++++ 3 files changed, 112 insertions(+)
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 29c8316..ba22cc7 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -182,6 +182,9 @@ struct regmap_field { /* lsb */ unsigned int shift; unsigned int reg; + + unsigned int id_size; + unsigned int id_offset; };
#ifdef CONFIG_DEBUG_FS diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7ae90d8..677e559 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -815,6 +815,8 @@ static void regmap_field_init(struct regmap_field *rm_field, rm_field->reg = reg_field.reg; rm_field->shift = reg_field.lsb; rm_field->mask = ((BIT(field_bits) - 1) << reg_field.lsb); + rm_field->id_size = reg_field.id_size; + rm_field->id_offset = reg_field.id_offset; }
/** @@ -1380,6 +1382,68 @@ int regmap_field_update_bits(struct regmap_field *field, unsigned int mask, unsi } EXPORT_SYMBOL_GPL(regmap_field_write);
+/** + * regmap_fields_write(): Write a value to a single register field with port ID + * + * @field: Register field to write to + * @id: port ID + * @val: Value to be written + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_fields_write(struct regmap_field *field, unsigned int id, + unsigned int val) +{ + if (id >= field->id_size) + return -EINVAL; + + return regmap_update_bits(field->regmap, + field->reg + (field->id_offset * id), + field->mask, val << field->shift); +} +EXPORT_SYMBOL_GPL(regmap_field_write); + +/** + * regmap_fields_update_bits(): Perform a read/modify/write cycle + * on the register field + * + * @field: Register field to write to + * @id: port ID + * @mask: Bitmask to change + * @val: Value to be written + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_fields_update_bits(struct regmap_field *field, unsigned int id, + unsigned int mask, unsigned int val) +{ + if (id >= field->id_size) + return -EINVAL; + + mask = (mask << field->shift) & field->mask; + + return regmap_update_bits(field->regmap, + field->reg + (field->id_offset * id), + mask, val << field->shift); +} +EXPORT_SYMBOL_GPL(regmap_field_write); + +/** + * regmap_fields_enable(): query fields access + * + * @field: Query Register field + * + * A non-zero will be returned when fields access enable, + * a zero will be returned in single field. + */ +int regmap_fields_enable(struct regmap_field *field) +{ + return field->id_size && field->id_offset; +} +EXPORT_SYMBOL_GPL(regmap_fields_enable); + /* * regmap_bulk_write(): Write multiple registers to the device * @@ -1688,6 +1752,39 @@ int regmap_field_read(struct regmap_field *field, unsigned int *val) EXPORT_SYMBOL_GPL(regmap_field_read);
/** + * regmap_fields_read(): Read a value to a single register field with port ID + * + * @field: Register field to read from + * @id: port ID + * @val: Pointer to store read value + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_fields_read(struct regmap_field *field, unsigned int id, + unsigned int *val) +{ + int ret; + unsigned int reg_val; + + if (id >= field->id_size) + return -EINVAL; + + ret = regmap_read(field->regmap, + field->reg + (field->id_offset * id), + ®_val); + if (ret != 0) + return ret; + + reg_val &= field->mask; + reg_val >>= field->shift; + *val = reg_val; + + return ret; +} +EXPORT_SYMBOL_GPL(regmap_field_read); + +/** * regmap_bulk_read(): Read multiple registers from the device * * @map: Register map to write to diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 1425c91..d329f50 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -423,11 +423,15 @@ bool regmap_reg_in_ranges(unsigned int reg, * @reg: Offset of the register within the regmap bank * @lsb: lsb of the register field. * @reg: msb of the register field. + * @id_size: port size if it has some ports + * @id_offset: address offset for each ports */ struct reg_field { unsigned int reg; unsigned int lsb; unsigned int msb; + unsigned int id_size; + unsigned int id_offset; };
#define REG_FIELD(_reg, _lsb, _msb) { \ @@ -449,6 +453,14 @@ int regmap_field_write(struct regmap_field *field, unsigned int val); int regmap_field_update_bits(struct regmap_field *field, unsigned int mask, unsigned int val);
+int regmap_fields_write(struct regmap_field *field, unsigned int id, + unsigned int val); +int regmap_fields_read(struct regmap_field *field, unsigned int id, + unsigned int *val); +int regmap_fields_enable(struct regmap_field *field); +int regmap_fields_update_bits(struct regmap_field *field, unsigned int id, + unsigned int mask, unsigned int val); + /** * Description of an IRQ for the generic regmap irq_chip. *
Hi
Current Linux kernel is supporting regmap_field method and it is very useful feature. It needs one regmap_filed for one register access.
OTOH, there is multi port device which has many same registers in the market. The difference for each port register access is only its address offset.
Current API needs many regmap_field for such device, but it is not good. This patch adds new regmap_fileds API which can care about multi port/offset access via regmap.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
I noticed that this series didin't care EXPORT_SYMBOL_GPL() name (many same names) Please give me v2 chance
Best regards --- Kuninori Morimoto
Current rsnd_gen_ops didn't care about .probe and .remove functions, but it was not good sense. This patch tidyup it
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/gen.c | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-)
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index babb203..331fc55 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -11,6 +11,11 @@ #include "rsnd.h"
struct rsnd_gen_ops { + int (*probe)(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv); + void (*remove)(struct platform_device *pdev, + struct rsnd_priv *priv); int (*path_init)(struct rsnd_priv *priv, struct rsnd_dai *rdai, struct rsnd_dai_stream *io); @@ -98,11 +103,6 @@ static int rsnd_gen1_path_exit(struct rsnd_priv *priv, return ret; }
-static struct rsnd_gen_ops rsnd_gen1_ops = { - .path_init = rsnd_gen1_path_init, - .path_exit = rsnd_gen1_path_exit, -}; - #define RSND_GEN1_REG_MAP(g, s, i, oi, oa) \ do { \ (g)->reg_map[RSND_REG_##i].index = RSND_GEN1_##s; \ @@ -163,7 +163,6 @@ static int rsnd_gen1_probe(struct platform_device *pdev, IS_ERR(gen->base[RSND_GEN1_SSI])) return -ENODEV;
- gen->ops = &rsnd_gen1_ops; rsnd_gen1_reg_map_init(gen);
dev_dbg(dev, "Gen1 device probed\n"); @@ -183,6 +182,13 @@ static void rsnd_gen1_remove(struct platform_device *pdev, { }
+static struct rsnd_gen_ops rsnd_gen1_ops = { + .probe = rsnd_gen1_probe, + .remove = rsnd_gen1_remove, + .path_init = rsnd_gen1_path_init, + .path_exit = rsnd_gen1_path_exit, +}; + /* * Gen */ @@ -251,6 +257,14 @@ int rsnd_gen_probe(struct platform_device *pdev, return -ENOMEM; }
+ if (rsnd_is_gen1(priv)) + gen->ops = &rsnd_gen1_ops; + + if (!gen->ops) { + dev_err(dev, "unknown generation R-Car sound device\n"); + return -ENODEV; + } + priv->gen = gen;
/* @@ -261,20 +275,13 @@ int rsnd_gen_probe(struct platform_device *pdev, for (i = 0; i < RSND_REG_MAX; i++) gen->reg_map[i].index = -1;
- /* - * init each module - */ - if (rsnd_is_gen1(priv)) - return rsnd_gen1_probe(pdev, info, priv); - - dev_err(dev, "unknown generation R-Car sound device\n"); - - return -ENODEV; + return gen->ops->probe(pdev, info, priv); }
void rsnd_gen_remove(struct platform_device *pdev, struct rsnd_priv *priv) { - if (rsnd_is_gen1(priv)) - rsnd_gen1_remove(pdev, priv); + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + gen->ops->remove(pdev, priv); }
Current Linux kernel is supporting regmap/regmap_field, and, it is good match for Renesas Sound Gen1/Gen2 register mapping. This patch uses regmap instead of original method for register access
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/core.c | 45 --------- sound/soc/sh/rcar/gen.c | 233 ++++++++++++++++++++++++++++++---------------- 2 files changed, 151 insertions(+), 127 deletions(-)
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index a357060..fc83f0f 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -106,51 +106,6 @@ (!(priv->info->func) ? -ENODEV : \ priv->info->func(param))
- -/* - * basic function - */ -u32 rsnd_read(struct rsnd_priv *priv, - struct rsnd_mod *mod, enum rsnd_reg reg) -{ - void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); - - BUG_ON(!base); - - return ioread32(base); -} - -void rsnd_write(struct rsnd_priv *priv, - struct rsnd_mod *mod, - enum rsnd_reg reg, u32 data) -{ - void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); - struct device *dev = rsnd_priv_to_dev(priv); - - BUG_ON(!base); - - dev_dbg(dev, "w %p : %08x\n", base, data); - - iowrite32(data, base); -} - -void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, - enum rsnd_reg reg, u32 mask, u32 data) -{ - void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); - struct device *dev = rsnd_priv_to_dev(priv); - u32 val; - - BUG_ON(!base); - - val = ioread32(base); - val &= ~mask; - val |= data & mask; - iowrite32(val, base); - - dev_dbg(dev, "s %p : %08x\n", base, val); -} - /* * rsnd_mod functions */ diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 331fc55..b245584 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -24,21 +24,112 @@ struct rsnd_gen_ops { struct rsnd_dai_stream *io); };
-struct rsnd_gen_reg_map { - int index; /* -1 : not supported */ - u32 offset_id; /* offset of ssi0, ssi1, ssi2... */ - u32 offset_adr; /* offset of SSICR, SSISR, ... */ -}; - struct rsnd_gen { void __iomem *base[RSND_BASE_MAX];
- struct rsnd_gen_reg_map reg_map[RSND_REG_MAX]; struct rsnd_gen_ops *ops; + + struct regmap *regmap; + struct regmap_field *regs[RSND_REG_MAX]; };
#define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen)
+#define RSND_REG_SET(gen, id, reg_id, offset, _id_offset, _id_size) \ + [id] = { \ + .reg = (unsigned int)gen->base[reg_id] + offset, \ + .lsb = 0, \ + .msb = 31, \ + .id_size = _id_size, \ + .id_offset = _id_offset, \ + } + +#define RSND_SINGLE_REG(gen, reg, id, offset) \ + RSND_REG_SET(gen, RSND_REG_##id, RSND_GEN1_##reg, offset, 0, 0) + +#define RSND_MULTI_REG(gen, reg, id, offset, _id_offset) \ + RSND_REG_SET(gen, RSND_REG_##id, RSND_GEN1_##reg, offset, _id_offset, 9) + +/* + * basic function + */ +static int rsnd_regmap_write32(void *context, const void *_data, size_t count) +{ + struct rsnd_priv *priv = context; + struct device *dev = rsnd_priv_to_dev(priv); + u32 *data = (u32 *)_data; + u32 val = data[1]; + void __iomem *reg = (void *)data[0]; + + iowrite32(val, reg); + + dev_dbg(dev, "w %p : %08x\n", reg, val); + + return 0; +} + +static int rsnd_regmap_read32(void *context, + const void *_data, size_t reg_size, + void *_val, size_t val_size) +{ + struct rsnd_priv *priv = context; + struct device *dev = rsnd_priv_to_dev(priv); + u32 *data = (u32 *)_data; + u32 *val = (u32 *)_val; + void __iomem *reg = (void *)data[0]; + + *val = ioread32(reg); + + dev_dbg(dev, "r %p : %08x\n", reg, *val); + + return 0; +} + +static struct regmap_bus rsnd_regmap_bus = { + .write = rsnd_regmap_write32, + .read = rsnd_regmap_read32, + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, +}; + +u32 rsnd_read(struct rsnd_priv *priv, + struct rsnd_mod *mod, enum rsnd_reg reg) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + u32 val; + + if (regmap_fields_enable(gen->regs[reg])) + regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val); + else + regmap_field_read(gen->regs[reg], &val); + + return val; +} + +void rsnd_write(struct rsnd_priv *priv, + struct rsnd_mod *mod, + enum rsnd_reg reg, u32 data) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + if (regmap_fields_enable(gen->regs[reg])) + regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data); + else + regmap_field_write(gen->regs[reg], data); +} + +void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, + enum rsnd_reg reg, u32 mask, u32 data) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + if (regmap_fields_enable(gen->regs[reg])) + regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod), + mask, data); + else + regmap_field_update_bits(gen->regs[reg], mask, data); +} + /* * Gen2 * will be filled in the future @@ -103,39 +194,56 @@ static int rsnd_gen1_path_exit(struct rsnd_priv *priv, return ret; }
-#define RSND_GEN1_REG_MAP(g, s, i, oi, oa) \ - do { \ - (g)->reg_map[RSND_REG_##i].index = RSND_GEN1_##s; \ - (g)->reg_map[RSND_REG_##i].offset_id = oi; \ - (g)->reg_map[RSND_REG_##i].offset_adr = oa; \ - } while (0) - -static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen) +static int rsnd_gen1_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen) { - RSND_GEN1_REG_MAP(gen, SRU, SRC_ROUTE_SEL, 0x0, 0x00); - RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL0, 0x0, 0x08); - RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL1, 0x0, 0x0c); - RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL2, 0x0, 0x10); - RSND_GEN1_REG_MAP(gen, SRU, SRC_CTRL, 0x0, 0xc0); - RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE0, 0x0, 0xD0); - RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE1, 0x0, 0xD4); - RSND_GEN1_REG_MAP(gen, SRU, BUSIF_MODE, 0x4, 0x20); - RSND_GEN1_REG_MAP(gen, SRU, BUSIF_ADINR, 0x40, 0x214); - - RSND_GEN1_REG_MAP(gen, ADG, BRRA, 0x0, 0x00); - RSND_GEN1_REG_MAP(gen, ADG, BRRB, 0x0, 0x04); - RSND_GEN1_REG_MAP(gen, ADG, SSICKR, 0x0, 0x08); - RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL0, 0x0, 0x0c); - RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL1, 0x0, 0x10); - RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL3, 0x0, 0x18); - RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL4, 0x0, 0x1c); - RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL5, 0x0, 0x20); - - RSND_GEN1_REG_MAP(gen, SSI, SSICR, 0x40, 0x00); - RSND_GEN1_REG_MAP(gen, SSI, SSISR, 0x40, 0x04); - RSND_GEN1_REG_MAP(gen, SSI, SSITDR, 0x40, 0x08); - RSND_GEN1_REG_MAP(gen, SSI, SSIRDR, 0x40, 0x0c); - RSND_GEN1_REG_MAP(gen, SSI, SSIWSR, 0x40, 0x20); + int i; + struct device *dev = rsnd_priv_to_dev(priv); + struct regmap_config regc; + struct reg_field regf[RSND_REG_MAX] = { + RSND_SINGLE_REG(gen, SRU, SRC_ROUTE_SEL, 0x00), + RSND_SINGLE_REG(gen, SRU, SRC_TMG_SEL0, 0x08), + RSND_SINGLE_REG(gen, SRU, SRC_TMG_SEL1, 0x0c), + RSND_SINGLE_REG(gen, SRU, SRC_TMG_SEL2, 0x10), + RSND_SINGLE_REG(gen, SRU, SRC_CTRL, 0xc0), + RSND_SINGLE_REG(gen, SRU, SSI_MODE0, 0xD0), + RSND_SINGLE_REG(gen, SRU, SSI_MODE1, 0xD4), + RSND_MULTI_REG(gen, SRU, BUSIF_MODE, 0x20, 0x4), + RSND_MULTI_REG(gen, SRU, BUSIF_ADINR, 0x214, 0x40), + + RSND_SINGLE_REG(gen, ADG, BRRA, 0x00), + RSND_SINGLE_REG(gen, ADG, BRRB, 0x04), + RSND_SINGLE_REG(gen, ADG, SSICKR, 0x08), + RSND_SINGLE_REG(gen, ADG, AUDIO_CLK_SEL0, 0x0c), + RSND_SINGLE_REG(gen, ADG, AUDIO_CLK_SEL1, 0x10), + RSND_SINGLE_REG(gen, ADG, AUDIO_CLK_SEL3, 0x18), + RSND_SINGLE_REG(gen, ADG, AUDIO_CLK_SEL4, 0x1c), + RSND_SINGLE_REG(gen, ADG, AUDIO_CLK_SEL5, 0x20), + + RSND_MULTI_REG(gen, SSI, SSICR, 0x00, 0x40), + RSND_MULTI_REG(gen, SSI, SSISR, 0x04, 0x40), + RSND_MULTI_REG(gen, SSI, SSITDR, 0x08, 0x40), + RSND_MULTI_REG(gen, SSI, SSIRDR, 0x0c, 0x40), + RSND_MULTI_REG(gen, SSI, SSIWSR, 0x20, 0x40), + }; + + memset(®c, 0, sizeof(regc)); + regc.reg_bits = 32; + regc.val_bits = 32; + + gen->regmap = devm_regmap_init(dev, &rsnd_regmap_bus, priv, ®c); + if (IS_ERR(gen->regmap)) { + dev_err(dev, "regmap error %ld\n", PTR_ERR(gen->regmap)); + return PTR_ERR(gen->regmap); + } + + for (i = 0; i < RSND_REG_MAX; i++) { + gen->regs[i] = devm_regmap_field_alloc(dev, gen->regmap, regf[i]); + if (IS_ERR(gen->regs[i])) + return PTR_ERR(gen->regs[i]); + + } + + return 0; }
static int rsnd_gen1_probe(struct platform_device *pdev, @@ -147,6 +255,7 @@ static int rsnd_gen1_probe(struct platform_device *pdev, struct resource *sru_res; struct resource *adg_res; struct resource *ssi_res; + int ret;
/* * map address @@ -163,7 +272,9 @@ static int rsnd_gen1_probe(struct platform_device *pdev, IS_ERR(gen->base[RSND_GEN1_SSI])) return -ENODEV;
- rsnd_gen1_reg_map_init(gen); + ret = rsnd_gen1_regmap_init(priv, gen); + if (ret < 0) + return ret;
dev_dbg(dev, "Gen1 device probed\n"); dev_dbg(dev, "SRU : %08x => %p\n", sru_res->start, @@ -210,46 +321,12 @@ int rsnd_gen_path_exit(struct rsnd_priv *priv, return gen->ops->path_exit(priv, rdai, io); }
-void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, - struct rsnd_mod *mod, - enum rsnd_reg reg) -{ - struct rsnd_gen *gen = rsnd_priv_to_gen(priv); - struct device *dev = rsnd_priv_to_dev(priv); - int index; - u32 offset_id, offset_adr; - - if (reg >= RSND_REG_MAX) { - dev_err(dev, "rsnd_reg reg error\n"); - return NULL; - } - - index = gen->reg_map[reg].index; - offset_id = gen->reg_map[reg].offset_id; - offset_adr = gen->reg_map[reg].offset_adr; - - if (index < 0) { - dev_err(dev, "unsupported reg access %d\n", reg); - return NULL; - } - - if (offset_id && mod) - offset_id *= rsnd_mod_id(mod); - - /* - * index/offset were set on gen1/gen2 - */ - - return gen->base[index] + offset_id + offset_adr; -} - int rsnd_gen_probe(struct platform_device *pdev, struct rcar_snd_info *info, struct rsnd_priv *priv) { struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_gen *gen; - int i;
gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL); if (!gen) { @@ -267,14 +344,6 @@ int rsnd_gen_probe(struct platform_device *pdev,
priv->gen = gen;
- /* - * see - * rsnd_reg_get() - * rsnd_gen_probe() - */ - for (i = 0; i < RSND_REG_MAX; i++) - gen->reg_map[i].index = -1; - return gen->ops->probe(pdev, info, priv); }
Hi Mark
These patches are v2 of new regmap_filelds API on kernel. ~ It can care about multi port register offset via regmap.
0xAAAA + 0x40 -- port 0 -- regX regY regZ 0xAAAA + 0x80 -- port 1 -- regX regY regZ
This case, current API needs 2 (= port) x 3 (= regX/Y/Z) regmap_fileld, but this new API can care about all port via 3 regmap_filelds with port ID.
I'm not sure that regmap_filelds is good naming or not. Please let me know if you have good naming idea.
The difference between v1 <-> v2 is EXPORT_SYMBOL_GPL() naming on regmap_filelds
these are based on below branchs regmap/for-next + asoc/for-next
Kuninori Morimoto (4): regmap: add regmap_field_update_bits() regmap: Add regmap_fields APIs ASoC: rsnd: gen: rsnd_gen_ops cares .probe and .remove ASoC: rsnd: use regmap instead of original register mapping method
drivers/base/regmap/internal.h | 3 + drivers/base/regmap/regmap.c | 117 +++++++++++++++++ include/linux/regmap.h | 14 +++ sound/soc/sh/rcar/core.c | 45 ------- sound/soc/sh/rcar/gen.c | 270 +++++++++++++++++++++++++--------------- 5 files changed, 307 insertions(+), 142 deletions(-)
Current regmap_field is supporting read/write functions. This patch adds new update_bits function for it.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- v1 -> v2
- fixup EXPORT_SYMBOL_GPL() naming
drivers/base/regmap/regmap.c | 20 ++++++++++++++++++++ include/linux/regmap.h | 2 ++ 2 files changed, 22 insertions(+)
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7d689a1..285afa7 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1369,6 +1369,26 @@ int regmap_field_write(struct regmap_field *field, unsigned int val) } EXPORT_SYMBOL_GPL(regmap_field_write);
+/** + * regmap_field_update_bits(): Perform a read/modify/write cycle + * on the register field + * + * @field: Register field to write to + * @mask: Bitmask to change + * @val: Value to be written + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_field_update_bits(struct regmap_field *field, unsigned int mask, unsigned int val) +{ + mask = (mask << field->shift) & field->mask; + + return regmap_update_bits(field->regmap, field->reg, + mask, val << field->shift); +} +EXPORT_SYMBOL_GPL(regmap_field_update_bits); + /* * regmap_bulk_write(): Write multiple registers to the device * diff --git a/include/linux/regmap.h b/include/linux/regmap.h index a10380b..4c8c20a 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -448,6 +448,8 @@ void devm_regmap_field_free(struct device *dev, struct regmap_field *field);
int regmap_field_read(struct regmap_field *field, unsigned int *val); int regmap_field_write(struct regmap_field *field, unsigned int val); +int regmap_field_update_bits(struct regmap_field *field, + unsigned int mask, unsigned int val);
/** * Description of an IRQ for the generic regmap irq_chip.
On Sun, Sep 01, 2013 at 08:24:50PM -0700, Kuninori Morimoto wrote:
Current regmap_field is supporting read/write functions. This patch adds new update_bits function for it.
Applied, thanks.
Current Linux kernel is supporting regmap_field method and it is very useful feature. It needs one regmap_filed for one register access.
OTOH, there is multi port device which has many same registers in the market. The difference for each register access is only its address offset.
Current API needs many regmap_field for such device, but it is not good. This patch adds new regmap_fileds API which can care about multi port/offset access via regmap.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- v1 -> v2
- fixup EXPORT_SYMBOL_GPL() naming
drivers/base/regmap/internal.h | 3 ++ drivers/base/regmap/regmap.c | 97 ++++++++++++++++++++++++++++++++++++++++ include/linux/regmap.h | 12 +++++ 3 files changed, 112 insertions(+)
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 57f7778..9010614 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -179,6 +179,9 @@ struct regmap_field { /* lsb */ unsigned int shift; unsigned int reg; + + unsigned int id_size; + unsigned int id_offset; };
#ifdef CONFIG_DEBUG_FS diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 285afa7..7cb2e9f 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -821,6 +821,8 @@ static void regmap_field_init(struct regmap_field *rm_field, rm_field->reg = reg_field.reg; rm_field->shift = reg_field.lsb; rm_field->mask = ((BIT(field_bits) - 1) << reg_field.lsb); + rm_field->id_size = reg_field.id_size; + rm_field->id_offset = reg_field.id_offset; }
/** @@ -1389,6 +1391,68 @@ int regmap_field_update_bits(struct regmap_field *field, unsigned int mask, unsi } EXPORT_SYMBOL_GPL(regmap_field_update_bits);
+/** + * regmap_fields_write(): Write a value to a single register field with port ID + * + * @field: Register field to write to + * @id: port ID + * @val: Value to be written + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_fields_write(struct regmap_field *field, unsigned int id, + unsigned int val) +{ + if (id >= field->id_size) + return -EINVAL; + + return regmap_update_bits(field->regmap, + field->reg + (field->id_offset * id), + field->mask, val << field->shift); +} +EXPORT_SYMBOL_GPL(regmap_fields_write); + +/** + * regmap_fields_update_bits(): Perform a read/modify/write cycle + * on the register field + * + * @field: Register field to write to + * @id: port ID + * @mask: Bitmask to change + * @val: Value to be written + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_fields_update_bits(struct regmap_field *field, unsigned int id, + unsigned int mask, unsigned int val) +{ + if (id >= field->id_size) + return -EINVAL; + + mask = (mask << field->shift) & field->mask; + + return regmap_update_bits(field->regmap, + field->reg + (field->id_offset * id), + mask, val << field->shift); +} +EXPORT_SYMBOL_GPL(regmap_fields_update_bits); + +/** + * regmap_fields_enable(): query fields access + * + * @field: Query Register field + * + * A non-zero will be returned when fields access enable, + * a zero will be returned in single field. + */ +int regmap_fields_enable(struct regmap_field *field) +{ + return field->id_size && field->id_offset; +} +EXPORT_SYMBOL_GPL(regmap_fields_enable); + /* * regmap_bulk_write(): Write multiple registers to the device * @@ -1697,6 +1761,39 @@ int regmap_field_read(struct regmap_field *field, unsigned int *val) EXPORT_SYMBOL_GPL(regmap_field_read);
/** + * regmap_fields_read(): Read a value to a single register field with port ID + * + * @field: Register field to read from + * @id: port ID + * @val: Pointer to store read value + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_fields_read(struct regmap_field *field, unsigned int id, + unsigned int *val) +{ + int ret; + unsigned int reg_val; + + if (id >= field->id_size) + return -EINVAL; + + ret = regmap_read(field->regmap, + field->reg + (field->id_offset * id), + ®_val); + if (ret != 0) + return ret; + + reg_val &= field->mask; + reg_val >>= field->shift; + *val = reg_val; + + return ret; +} +EXPORT_SYMBOL_GPL(regmap_fields_read); + +/** * regmap_bulk_read(): Read multiple registers from the device * * @map: Register map to write to diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 4c8c20a..b43d2e3 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -425,11 +425,15 @@ bool regmap_reg_in_ranges(unsigned int reg, * @reg: Offset of the register within the regmap bank * @lsb: lsb of the register field. * @reg: msb of the register field. + * @id_size: port size if it has some ports + * @id_offset: address offset for each ports */ struct reg_field { unsigned int reg; unsigned int lsb; unsigned int msb; + unsigned int id_size; + unsigned int id_offset; };
#define REG_FIELD(_reg, _lsb, _msb) { \ @@ -451,6 +455,14 @@ int regmap_field_write(struct regmap_field *field, unsigned int val); int regmap_field_update_bits(struct regmap_field *field, unsigned int mask, unsigned int val);
+int regmap_fields_write(struct regmap_field *field, unsigned int id, + unsigned int val); +int regmap_fields_read(struct regmap_field *field, unsigned int id, + unsigned int *val); +int regmap_fields_enable(struct regmap_field *field); +int regmap_fields_update_bits(struct regmap_field *field, unsigned int id, + unsigned int mask, unsigned int val); + /** * Description of an IRQ for the generic regmap irq_chip. *
On Sun, Sep 01, 2013 at 08:30:50PM -0700, Kuninori Morimoto wrote:
Current Linux kernel is supporting regmap_field method and it is very useful feature. It needs one regmap_filed for one register access.
Applied, thanks. I did make one update:
- A non-zero will be returned when fields access enable,
- a zero will be returned in single field.
- */
+int regmap_fields_enable(struct regmap_field *field) +{
- return field->id_size && field->id_offset;
+} +EXPORT_SYMBOL_GPL(regmap_fields_enable);
This seems like it should be something like _is_present() - the name makes me think it will turn something on, not check that something is there. I removed it since it's not used by the following patches and I didn't notice earlier when I initially looked at the patches during the merge window.
Current rsnd_gen_ops didn't care about .probe and .remove functions, but it was not good sense. This patch tidyup it
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- v1 -> v2
- no change
sound/soc/sh/rcar/gen.c | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-)
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index babb203..331fc55 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -11,6 +11,11 @@ #include "rsnd.h"
struct rsnd_gen_ops { + int (*probe)(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv); + void (*remove)(struct platform_device *pdev, + struct rsnd_priv *priv); int (*path_init)(struct rsnd_priv *priv, struct rsnd_dai *rdai, struct rsnd_dai_stream *io); @@ -98,11 +103,6 @@ static int rsnd_gen1_path_exit(struct rsnd_priv *priv, return ret; }
-static struct rsnd_gen_ops rsnd_gen1_ops = { - .path_init = rsnd_gen1_path_init, - .path_exit = rsnd_gen1_path_exit, -}; - #define RSND_GEN1_REG_MAP(g, s, i, oi, oa) \ do { \ (g)->reg_map[RSND_REG_##i].index = RSND_GEN1_##s; \ @@ -163,7 +163,6 @@ static int rsnd_gen1_probe(struct platform_device *pdev, IS_ERR(gen->base[RSND_GEN1_SSI])) return -ENODEV;
- gen->ops = &rsnd_gen1_ops; rsnd_gen1_reg_map_init(gen);
dev_dbg(dev, "Gen1 device probed\n"); @@ -183,6 +182,13 @@ static void rsnd_gen1_remove(struct platform_device *pdev, { }
+static struct rsnd_gen_ops rsnd_gen1_ops = { + .probe = rsnd_gen1_probe, + .remove = rsnd_gen1_remove, + .path_init = rsnd_gen1_path_init, + .path_exit = rsnd_gen1_path_exit, +}; + /* * Gen */ @@ -251,6 +257,14 @@ int rsnd_gen_probe(struct platform_device *pdev, return -ENOMEM; }
+ if (rsnd_is_gen1(priv)) + gen->ops = &rsnd_gen1_ops; + + if (!gen->ops) { + dev_err(dev, "unknown generation R-Car sound device\n"); + return -ENODEV; + } + priv->gen = gen;
/* @@ -261,20 +275,13 @@ int rsnd_gen_probe(struct platform_device *pdev, for (i = 0; i < RSND_REG_MAX; i++) gen->reg_map[i].index = -1;
- /* - * init each module - */ - if (rsnd_is_gen1(priv)) - return rsnd_gen1_probe(pdev, info, priv); - - dev_err(dev, "unknown generation R-Car sound device\n"); - - return -ENODEV; + return gen->ops->probe(pdev, info, priv); }
void rsnd_gen_remove(struct platform_device *pdev, struct rsnd_priv *priv) { - if (rsnd_is_gen1(priv)) - rsnd_gen1_remove(pdev, priv); + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + gen->ops->remove(pdev, priv); }
On Sun, Sep 01, 2013 at 08:31:16PM -0700, Kuninori Morimoto wrote:
Current rsnd_gen_ops didn't care about .probe and .remove functions, but it was not good sense. This patch tidyup it
Applied, thanks.
Current Linux kernel is supporting regmap/regmap_field, and, it is good match for Renesas Sound Gen1/Gen2 register mapping. This patch uses regmap instead of original method for register access
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- v1 -> v2
- exchange macro naming (RSND_GEN1_S_REG/RSND_GEN1_M_REG)
sound/soc/sh/rcar/core.c | 45 --------- sound/soc/sh/rcar/gen.c | 231 ++++++++++++++++++++++++++++++---------------- 2 files changed, 150 insertions(+), 126 deletions(-)
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index a357060..fc83f0f 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -106,51 +106,6 @@ (!(priv->info->func) ? -ENODEV : \ priv->info->func(param))
- -/* - * basic function - */ -u32 rsnd_read(struct rsnd_priv *priv, - struct rsnd_mod *mod, enum rsnd_reg reg) -{ - void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); - - BUG_ON(!base); - - return ioread32(base); -} - -void rsnd_write(struct rsnd_priv *priv, - struct rsnd_mod *mod, - enum rsnd_reg reg, u32 data) -{ - void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); - struct device *dev = rsnd_priv_to_dev(priv); - - BUG_ON(!base); - - dev_dbg(dev, "w %p : %08x\n", base, data); - - iowrite32(data, base); -} - -void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, - enum rsnd_reg reg, u32 mask, u32 data) -{ - void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); - struct device *dev = rsnd_priv_to_dev(priv); - u32 val; - - BUG_ON(!base); - - val = ioread32(base); - val &= ~mask; - val |= data & mask; - iowrite32(val, base); - - dev_dbg(dev, "s %p : %08x\n", base, val); -} - /* * rsnd_mod functions */ diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 331fc55..9a55fdf 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -24,21 +24,106 @@ struct rsnd_gen_ops { struct rsnd_dai_stream *io); };
-struct rsnd_gen_reg_map { - int index; /* -1 : not supported */ - u32 offset_id; /* offset of ssi0, ssi1, ssi2... */ - u32 offset_adr; /* offset of SSICR, SSISR, ... */ -}; - struct rsnd_gen { void __iomem *base[RSND_BASE_MAX];
- struct rsnd_gen_reg_map reg_map[RSND_REG_MAX]; struct rsnd_gen_ops *ops; + + struct regmap *regmap; + struct regmap_field *regs[RSND_REG_MAX]; };
#define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen)
+#define RSND_REG_SET(gen, id, reg_id, offset, _id_offset, _id_size) \ + [id] = { \ + .reg = (unsigned int)gen->base[reg_id] + offset, \ + .lsb = 0, \ + .msb = 31, \ + .id_size = _id_size, \ + .id_offset = _id_offset, \ + } + +/* + * basic function + */ +static int rsnd_regmap_write32(void *context, const void *_data, size_t count) +{ + struct rsnd_priv *priv = context; + struct device *dev = rsnd_priv_to_dev(priv); + u32 *data = (u32 *)_data; + u32 val = data[1]; + void __iomem *reg = (void *)data[0]; + + iowrite32(val, reg); + + dev_dbg(dev, "w %p : %08x\n", reg, val); + + return 0; +} + +static int rsnd_regmap_read32(void *context, + const void *_data, size_t reg_size, + void *_val, size_t val_size) +{ + struct rsnd_priv *priv = context; + struct device *dev = rsnd_priv_to_dev(priv); + u32 *data = (u32 *)_data; + u32 *val = (u32 *)_val; + void __iomem *reg = (void *)data[0]; + + *val = ioread32(reg); + + dev_dbg(dev, "r %p : %08x\n", reg, *val); + + return 0; +} + +static struct regmap_bus rsnd_regmap_bus = { + .write = rsnd_regmap_write32, + .read = rsnd_regmap_read32, + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, +}; + +u32 rsnd_read(struct rsnd_priv *priv, + struct rsnd_mod *mod, enum rsnd_reg reg) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + u32 val; + + if (regmap_fields_enable(gen->regs[reg])) + regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val); + else + regmap_field_read(gen->regs[reg], &val); + + return val; +} + +void rsnd_write(struct rsnd_priv *priv, + struct rsnd_mod *mod, + enum rsnd_reg reg, u32 data) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + if (regmap_fields_enable(gen->regs[reg])) + regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data); + else + regmap_field_write(gen->regs[reg], data); +} + +void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, + enum rsnd_reg reg, u32 mask, u32 data) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + if (regmap_fields_enable(gen->regs[reg])) + regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod), + mask, data); + else + regmap_field_update_bits(gen->regs[reg], mask, data); +} + /* * Gen2 * will be filled in the future @@ -103,39 +188,62 @@ static int rsnd_gen1_path_exit(struct rsnd_priv *priv, return ret; }
-#define RSND_GEN1_REG_MAP(g, s, i, oi, oa) \ - do { \ - (g)->reg_map[RSND_REG_##i].index = RSND_GEN1_##s; \ - (g)->reg_map[RSND_REG_##i].offset_id = oi; \ - (g)->reg_map[RSND_REG_##i].offset_adr = oa; \ - } while (0) +#define RSND_GEN1_S_REG(gen, reg, id, offset) \ + RSND_REG_SET(gen, RSND_REG_##id, RSND_GEN1_##reg, offset, 0, 0)
-static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen) +#define RSND_GEN1_M_REG(gen, reg, id, offset, _id_offset) \ + RSND_REG_SET(gen, RSND_REG_##id, RSND_GEN1_##reg, offset, _id_offset, 9) + +static int rsnd_gen1_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen) { - RSND_GEN1_REG_MAP(gen, SRU, SRC_ROUTE_SEL, 0x0, 0x00); - RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL0, 0x0, 0x08); - RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL1, 0x0, 0x0c); - RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL2, 0x0, 0x10); - RSND_GEN1_REG_MAP(gen, SRU, SRC_CTRL, 0x0, 0xc0); - RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE0, 0x0, 0xD0); - RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE1, 0x0, 0xD4); - RSND_GEN1_REG_MAP(gen, SRU, BUSIF_MODE, 0x4, 0x20); - RSND_GEN1_REG_MAP(gen, SRU, BUSIF_ADINR, 0x40, 0x214); - - RSND_GEN1_REG_MAP(gen, ADG, BRRA, 0x0, 0x00); - RSND_GEN1_REG_MAP(gen, ADG, BRRB, 0x0, 0x04); - RSND_GEN1_REG_MAP(gen, ADG, SSICKR, 0x0, 0x08); - RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL0, 0x0, 0x0c); - RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL1, 0x0, 0x10); - RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL3, 0x0, 0x18); - RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL4, 0x0, 0x1c); - RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL5, 0x0, 0x20); - - RSND_GEN1_REG_MAP(gen, SSI, SSICR, 0x40, 0x00); - RSND_GEN1_REG_MAP(gen, SSI, SSISR, 0x40, 0x04); - RSND_GEN1_REG_MAP(gen, SSI, SSITDR, 0x40, 0x08); - RSND_GEN1_REG_MAP(gen, SSI, SSIRDR, 0x40, 0x0c); - RSND_GEN1_REG_MAP(gen, SSI, SSIWSR, 0x40, 0x20); + int i; + struct device *dev = rsnd_priv_to_dev(priv); + struct regmap_config regc; + struct reg_field regf[RSND_REG_MAX] = { + RSND_GEN1_S_REG(gen, SRU, SRC_ROUTE_SEL, 0x00), + RSND_GEN1_S_REG(gen, SRU, SRC_TMG_SEL0, 0x08), + RSND_GEN1_S_REG(gen, SRU, SRC_TMG_SEL1, 0x0c), + RSND_GEN1_S_REG(gen, SRU, SRC_TMG_SEL2, 0x10), + RSND_GEN1_S_REG(gen, SRU, SRC_CTRL, 0xc0), + RSND_GEN1_S_REG(gen, SRU, SSI_MODE0, 0xD0), + RSND_GEN1_S_REG(gen, SRU, SSI_MODE1, 0xD4), + RSND_GEN1_M_REG(gen, SRU, BUSIF_MODE, 0x20, 0x4), + RSND_GEN1_M_REG(gen, SRU, BUSIF_ADINR, 0x214, 0x40), + + RSND_GEN1_S_REG(gen, ADG, BRRA, 0x00), + RSND_GEN1_S_REG(gen, ADG, BRRB, 0x04), + RSND_GEN1_S_REG(gen, ADG, SSICKR, 0x08), + RSND_GEN1_S_REG(gen, ADG, AUDIO_CLK_SEL0, 0x0c), + RSND_GEN1_S_REG(gen, ADG, AUDIO_CLK_SEL1, 0x10), + RSND_GEN1_S_REG(gen, ADG, AUDIO_CLK_SEL3, 0x18), + RSND_GEN1_S_REG(gen, ADG, AUDIO_CLK_SEL4, 0x1c), + RSND_GEN1_S_REG(gen, ADG, AUDIO_CLK_SEL5, 0x20), + + RSND_GEN1_M_REG(gen, SSI, SSICR, 0x00, 0x40), + RSND_GEN1_M_REG(gen, SSI, SSISR, 0x04, 0x40), + RSND_GEN1_M_REG(gen, SSI, SSITDR, 0x08, 0x40), + RSND_GEN1_M_REG(gen, SSI, SSIRDR, 0x0c, 0x40), + RSND_GEN1_M_REG(gen, SSI, SSIWSR, 0x20, 0x40), + }; + + memset(®c, 0, sizeof(regc)); + regc.reg_bits = 32; + regc.val_bits = 32; + + gen->regmap = devm_regmap_init(dev, &rsnd_regmap_bus, priv, ®c); + if (IS_ERR(gen->regmap)) { + dev_err(dev, "regmap error %ld\n", PTR_ERR(gen->regmap)); + return PTR_ERR(gen->regmap); + } + + for (i = 0; i < RSND_REG_MAX; i++) { + gen->regs[i] = devm_regmap_field_alloc(dev, gen->regmap, regf[i]); + if (IS_ERR(gen->regs[i])) + return PTR_ERR(gen->regs[i]); + + } + + return 0; }
static int rsnd_gen1_probe(struct platform_device *pdev, @@ -147,6 +255,7 @@ static int rsnd_gen1_probe(struct platform_device *pdev, struct resource *sru_res; struct resource *adg_res; struct resource *ssi_res; + int ret;
/* * map address @@ -163,7 +272,9 @@ static int rsnd_gen1_probe(struct platform_device *pdev, IS_ERR(gen->base[RSND_GEN1_SSI])) return -ENODEV;
- rsnd_gen1_reg_map_init(gen); + ret = rsnd_gen1_regmap_init(priv, gen); + if (ret < 0) + return ret;
dev_dbg(dev, "Gen1 device probed\n"); dev_dbg(dev, "SRU : %08x => %p\n", sru_res->start, @@ -210,46 +321,12 @@ int rsnd_gen_path_exit(struct rsnd_priv *priv, return gen->ops->path_exit(priv, rdai, io); }
-void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, - struct rsnd_mod *mod, - enum rsnd_reg reg) -{ - struct rsnd_gen *gen = rsnd_priv_to_gen(priv); - struct device *dev = rsnd_priv_to_dev(priv); - int index; - u32 offset_id, offset_adr; - - if (reg >= RSND_REG_MAX) { - dev_err(dev, "rsnd_reg reg error\n"); - return NULL; - } - - index = gen->reg_map[reg].index; - offset_id = gen->reg_map[reg].offset_id; - offset_adr = gen->reg_map[reg].offset_adr; - - if (index < 0) { - dev_err(dev, "unsupported reg access %d\n", reg); - return NULL; - } - - if (offset_id && mod) - offset_id *= rsnd_mod_id(mod); - - /* - * index/offset were set on gen1/gen2 - */ - - return gen->base[index] + offset_id + offset_adr; -} - int rsnd_gen_probe(struct platform_device *pdev, struct rcar_snd_info *info, struct rsnd_priv *priv) { struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_gen *gen; - int i;
gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL); if (!gen) { @@ -267,14 +344,6 @@ int rsnd_gen_probe(struct platform_device *pdev,
priv->gen = gen;
- /* - * see - * rsnd_reg_get() - * rsnd_gen_probe() - */ - for (i = 0; i < RSND_REG_MAX; i++) - gen->reg_map[i].index = -1; - return gen->ops->probe(pdev, info, priv); }
On Sun, Sep 01, 2013 at 08:32:45PM -0700, Kuninori Morimoto wrote:
+void rsnd_write(struct rsnd_priv *priv,
struct rsnd_mod *mod,
enum rsnd_reg reg, u32 data)
+{
- struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
- if (regmap_fields_enable(gen->regs[reg]))
regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data);
- else
regmap_field_write(gen->regs[reg], data);
+}
Ah, sorry - I see you are actually using fields_enable(). I must've searched for the wrong thing. However looking at this I'm not sure that this is a good use anyway, it seems like the caller should know if the register it is writing to is in one of the repeated blocks. Can you provide a bit more detail as to what's going on here?
participants (2)
-
Kuninori Morimoto
-
Mark Brown