//
// tegra210_amx.c - Tegra210 AMX driver
+#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/io.h>
static void tegra210_amx_write_map_ram(struct tegra210_amx *amx)
{
+ const unsigned int bits_per_mask = BITS_PER_TYPE(*amx->byte_mask);
int i;
+ memset(amx->byte_mask, 0,
+ amx->soc_data->byte_mask_size * sizeof(*amx->byte_mask));
+
regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL + amx->soc_data->reg_offset,
TEGRA210_AMX_CFG_RAM_CTRL_SEQ_ACCESS_EN |
TEGRA210_AMX_CFG_RAM_CTRL_ADDR_INIT_EN |
TEGRA210_AMX_CFG_RAM_CTRL_RW_WRITE);
- for (i = 0; i < amx->soc_data->ram_depth; i++)
+ for (i = 0; i < amx->soc_data->ram_depth; i++) {
+ u32 word = 0;
+ int b;
+
+ for (b = 0; b < TEGRA_AMX_SLOTS_PER_WORD; b++) {
+ unsigned int slot = i * TEGRA_AMX_SLOTS_PER_WORD + b;
+ u16 val = amx->map[slot];
+
+ if (val >= 256)
+ continue;
+
+ word |= (u32)val << (b * BITS_PER_BYTE);
+ amx->byte_mask[slot / bits_per_mask] |=
+ 1U << (slot % bits_per_mask);
+ }
regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_DATA + amx->soc_data->reg_offset,
- amx->map[i]);
+ word);
+ }
for (i = 0; i < amx->soc_data->byte_mask_size; i++)
regmap_write(amx->regmap,
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct tegra210_amx *amx = snd_soc_component_get_drvdata(cmpnt);
- unsigned char *bytes_map = (unsigned char *)amx->map;
- int reg = mc->reg;
- int enabled;
-
- enabled = amx->byte_mask[reg / 32] & (1 << (reg % 32));
- /*
- * TODO: Simplify this logic to just return from bytes_map[]
- *
- * Presently below is required since bytes_map[] is
- * tightly packed and cannot store the control value of 256.
- * Byte mask state is used to know if 256 needs to be returned.
- * Note that for control value of 256, the put() call stores 0
- * in the bytes_map[] and disables the corresponding bit in
- * byte_mask[].
- */
- if (enabled)
- ucontrol->value.integer.value[0] = bytes_map[reg];
- else
- ucontrol->value.integer.value[0] = 256;
+ ucontrol->value.integer.value[0] = amx->map[mc->reg];
return 0;
}
(struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
struct tegra210_amx *amx = snd_soc_component_get_drvdata(cmpnt);
- unsigned char *bytes_map = (unsigned char *)amx->map;
- int reg = mc->reg;
- int value = ucontrol->value.integer.value[0];
- unsigned int mask_val = amx->byte_mask[reg / 32];
+ unsigned int value = ucontrol->value.integer.value[0];
- if (value >= 0 && value <= 255)
- mask_val |= (1 << (reg % 32));
- else
- mask_val &= ~(1 << (reg % 32));
+ /*
+ * Match the previous behaviour: any value outside [0, 255] is
+ * treated as the "disabled" sentinel (256). Negative values from
+ * userspace fold in through the unsigned cast and are caught here.
+ */
+ if (value > 255)
+ value = 256;
- if (mask_val == amx->byte_mask[reg / 32])
+ if (amx->map[mc->reg] == value)
return 0;
- /* Update byte map and slot */
- bytes_map[reg] = value % 256;
- amx->byte_mask[reg / 32] = mask_val;
+ amx->map[mc->reg] = value;
return 1;
}
struct device *dev = &pdev->dev;
struct tegra210_amx *amx;
void __iomem *regs;
- int err;
+ int err, i;
amx = devm_kzalloc(dev, sizeof(*amx), GFP_KERNEL);
if (!amx)
regcache_cache_only(amx->regmap, true);
- amx->map = devm_kzalloc(dev, amx->soc_data->ram_depth * sizeof(*amx->map),
- GFP_KERNEL);
+ amx->map = devm_kcalloc(dev,
+ amx->soc_data->ram_depth * TEGRA_AMX_SLOTS_PER_WORD,
+ sizeof(*amx->map), GFP_KERNEL);
if (!amx->map)
return -ENOMEM;
- amx->byte_mask = devm_kzalloc(dev,
- amx->soc_data->byte_mask_size * sizeof(*amx->byte_mask),
- GFP_KERNEL);
+ amx->byte_mask = devm_kcalloc(dev, amx->soc_data->byte_mask_size,
+ sizeof(*amx->byte_mask), GFP_KERNEL);
if (!amx->byte_mask)
return -ENOMEM;
+ /* Initialise all byte map slots as disabled (value 256). */
+ for (i = 0; i < amx->soc_data->ram_depth * TEGRA_AMX_SLOTS_PER_WORD; i++)
+ amx->map[i] = 256;
+
tegra210_amx_dais[TEGRA_AMX_OUT_DAI_ID].capture.channels_max =
amx->soc_data->max_ch;