aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/soc-cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/soc-cache.c')
-rw-r--r--sound/soc/soc-cache.c386
1 files changed, 188 insertions, 198 deletions
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c
index 8c2a21a..5d76da4 100644
--- a/sound/soc/soc-cache.c
+++ b/sound/soc/soc-cache.c
@@ -18,6 +18,8 @@
#include <linux/bitmap.h>
#include <linux/rbtree.h>
+#include <trace/events/asoc.h>
+
static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec,
unsigned int reg)
{
@@ -25,7 +27,8 @@ static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec,
unsigned int val;
if (reg >= codec->driver->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg)) {
+ snd_soc_codec_volatile_register(codec, reg) ||
+ codec->cache_bypass) {
if (codec->cache_only)
return -1;
@@ -49,7 +52,8 @@ static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
data[1] = value & 0x00ff;
if (!snd_soc_codec_volatile_register(codec, reg) &&
- reg < codec->driver->reg_cache_size) {
+ reg < codec->driver->reg_cache_size &&
+ !codec->cache_bypass) {
ret = snd_soc_cache_write(codec, reg, value);
if (ret < 0)
return -1;
@@ -106,7 +110,8 @@ static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec,
unsigned int val;
if (reg >= codec->driver->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg)) {
+ snd_soc_codec_volatile_register(codec, reg) ||
+ codec->cache_bypass) {
if (codec->cache_only)
return -1;
@@ -130,7 +135,8 @@ static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg,
data[1] = value & 0x00ff;
if (!snd_soc_codec_volatile_register(codec, reg) &&
- reg < codec->driver->reg_cache_size) {
+ reg < codec->driver->reg_cache_size &&
+ !codec->cache_bypass) {
ret = snd_soc_cache_write(codec, reg, value);
if (ret < 0)
return -1;
@@ -191,7 +197,8 @@ static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg,
data[1] = value & 0xff;
if (!snd_soc_codec_volatile_register(codec, reg) &&
- reg < codec->driver->reg_cache_size) {
+ reg < codec->driver->reg_cache_size &&
+ !codec->cache_bypass) {
ret = snd_soc_cache_write(codec, reg, value);
if (ret < 0)
return -1;
@@ -216,7 +223,8 @@ static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec,
reg &= 0xff;
if (reg >= codec->driver->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg)) {
+ snd_soc_codec_volatile_register(codec, reg) ||
+ codec->cache_bypass) {
if (codec->cache_only)
return -1;
@@ -271,7 +279,8 @@ static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
data[2] = value & 0xff;
if (!snd_soc_codec_volatile_register(codec, reg) &&
- reg < codec->driver->reg_cache_size) {
+ reg < codec->driver->reg_cache_size &&
+ !codec->cache_bypass) {
ret = snd_soc_cache_write(codec, reg, value);
if (ret < 0)
return -1;
@@ -295,7 +304,8 @@ static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec,
unsigned int val;
if (reg >= codec->driver->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg)) {
+ snd_soc_codec_volatile_register(codec, reg) ||
+ codec->cache_bypass) {
if (codec->cache_only)
return -1;
@@ -450,7 +460,8 @@ static unsigned int snd_soc_16_8_read(struct snd_soc_codec *codec,
reg &= 0xff;
if (reg >= codec->driver->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg)) {
+ snd_soc_codec_volatile_register(codec, reg) ||
+ codec->cache_bypass) {
if (codec->cache_only)
return -1;
@@ -476,7 +487,8 @@ static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg,
reg &= 0xff;
if (!snd_soc_codec_volatile_register(codec, reg) &&
- reg < codec->driver->reg_cache_size) {
+ reg < codec->driver->reg_cache_size &&
+ !codec->cache_bypass) {
ret = snd_soc_cache_write(codec, reg, value);
if (ret < 0)
return -1;
@@ -568,7 +580,8 @@ static unsigned int snd_soc_16_16_read(struct snd_soc_codec *codec,
unsigned int val;
if (reg >= codec->driver->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg)) {
+ snd_soc_codec_volatile_register(codec, reg) ||
+ codec->cache_bypass) {
if (codec->cache_only)
return -1;
@@ -595,7 +608,8 @@ static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg,
data[3] = value & 0xff;
if (!snd_soc_codec_volatile_register(codec, reg) &&
- reg < codec->driver->reg_cache_size) {
+ reg < codec->driver->reg_cache_size &&
+ !codec->cache_bypass) {
ret = snd_soc_cache_write(codec, reg, value);
if (ret < 0)
return -1;
@@ -761,6 +775,49 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
}
EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);
+static bool snd_soc_set_cache_val(void *base, unsigned int idx,
+ unsigned int val, unsigned int word_size)
+{
+ switch (word_size) {
+ case 1: {
+ u8 *cache = base;
+ if (cache[idx] == val)
+ return true;
+ cache[idx] = val;
+ break;
+ }
+ case 2: {
+ u16 *cache = base;
+ if (cache[idx] == val)
+ return true;
+ cache[idx] = val;
+ break;
+ }
+ default:
+ BUG();
+ }
+ return false;
+}
+
+static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx,
+ unsigned int word_size)
+{
+ switch (word_size) {
+ case 1: {
+ const u8 *cache = base;
+ return cache[idx];
+ }
+ case 2: {
+ const u16 *cache = base;
+ return cache[idx];
+ }
+ default:
+ BUG();
+ }
+ /* unreachable */
+ return -1;
+}
+
struct snd_soc_rbtree_node {
struct rb_node node;
unsigned int reg;
@@ -835,7 +892,9 @@ static int snd_soc_rbtree_cache_sync(struct snd_soc_codec *codec)
ret = snd_soc_cache_read(codec, rbnode->reg, &val);
if (ret)
return ret;
+ codec->cache_bypass = 1;
ret = snd_soc_write(codec, rbnode->reg, val);
+ codec->cache_bypass = 0;
if (ret)
return ret;
dev_dbg(codec->dev, "Synced register %#x, value = %#x\n",
@@ -924,7 +983,12 @@ static int snd_soc_rbtree_cache_exit(struct snd_soc_codec *codec)
static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec)
{
+ struct snd_soc_rbtree_node *rbtree_node;
struct snd_soc_rbtree_ctx *rbtree_ctx;
+ unsigned int val;
+ unsigned int word_size;
+ int i;
+ int ret;
codec->reg_cache = kmalloc(sizeof *rbtree_ctx, GFP_KERNEL);
if (!codec->reg_cache)
@@ -936,53 +1000,25 @@ static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec)
if (!codec->reg_def_copy)
return 0;
-/*
- * populate the rbtree with the initialized registers. All other
- * registers will be inserted into the tree when they are first written.
- *
- * The reasoning behind this, is that we need to step through and
- * dereference the cache in u8/u16 increments without sacrificing
- * portability. This could also be done using memcpy() but that would
- * be slightly more cryptic.
- */
-#define snd_soc_rbtree_populate(cache) \
-({ \
- int ret, i; \
- struct snd_soc_rbtree_node *rbtree_node; \
- \
- ret = 0; \
- cache = codec->reg_def_copy; \
- for (i = 0; i < codec->driver->reg_cache_size; ++i) { \
- if (!cache[i]) \
- continue; \
- rbtree_node = kzalloc(sizeof *rbtree_node, GFP_KERNEL); \
- if (!rbtree_node) { \
- ret = -ENOMEM; \
- snd_soc_cache_exit(codec); \
- break; \
- } \
- rbtree_node->reg = i; \
- rbtree_node->value = cache[i]; \
- rbtree_node->defval = cache[i]; \
- snd_soc_rbtree_insert(&rbtree_ctx->root, \
- rbtree_node); \
- } \
- ret; \
-})
-
- switch (codec->driver->reg_word_size) {
- case 1: {
- const u8 *cache;
-
- return snd_soc_rbtree_populate(cache);
- }
- case 2: {
- const u16 *cache;
-
- return snd_soc_rbtree_populate(cache);
- }
- default:
- BUG();
+ /*
+ * populate the rbtree with the initialized registers. All other
+ * registers will be inserted when they are first modified.
+ */
+ word_size = codec->driver->reg_word_size;
+ for (i = 0; i < codec->driver->reg_cache_size; ++i) {
+ val = snd_soc_get_cache_val(codec->reg_def_copy, i, word_size);
+ if (!val)
+ continue;
+ rbtree_node = kzalloc(sizeof *rbtree_node, GFP_KERNEL);
+ if (!rbtree_node) {
+ ret = -ENOMEM;
+ snd_soc_cache_exit(codec);
+ break;
+ }
+ rbtree_node->reg = i;
+ rbtree_node->value = val;
+ rbtree_node->defval = val;
+ snd_soc_rbtree_insert(&rbtree_ctx->root, rbtree_node);
}
return 0;
@@ -1080,34 +1116,28 @@ static inline int snd_soc_lzo_get_blkindex(struct snd_soc_codec *codec,
unsigned int reg)
{
const struct snd_soc_codec_driver *codec_drv;
- size_t reg_size;
codec_drv = codec->driver;
- reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
return (reg * codec_drv->reg_word_size) /
- DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count());
+ DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count());
}
static inline int snd_soc_lzo_get_blkpos(struct snd_soc_codec *codec,
unsigned int reg)
{
const struct snd_soc_codec_driver *codec_drv;
- size_t reg_size;
codec_drv = codec->driver;
- reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
- return reg % (DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count()) /
+ return reg % (DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count()) /
codec_drv->reg_word_size);
}
static inline int snd_soc_lzo_get_blksize(struct snd_soc_codec *codec)
{
const struct snd_soc_codec_driver *codec_drv;
- size_t reg_size;
codec_drv = codec->driver;
- reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
- return DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count());
+ return DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count());
}
static int snd_soc_lzo_cache_sync(struct snd_soc_codec *codec)
@@ -1122,7 +1152,9 @@ static int snd_soc_lzo_cache_sync(struct snd_soc_codec *codec)
ret = snd_soc_cache_read(codec, i, &val);
if (ret)
return ret;
+ codec->cache_bypass = 1;
ret = snd_soc_write(codec, i, val);
+ codec->cache_bypass = 0;
if (ret)
return ret;
dev_dbg(codec->dev, "Synced register %#x, value = %#x\n",
@@ -1165,29 +1197,10 @@ static int snd_soc_lzo_cache_write(struct snd_soc_codec *codec,
}
/* write the new value to the cache */
- switch (codec->driver->reg_word_size) {
- case 1: {
- u8 *cache;
- cache = lzo_block->dst;
- if (cache[blkpos] == value) {
- kfree(lzo_block->dst);
- goto out;
- }
- cache[blkpos] = value;
- }
- break;
- case 2: {
- u16 *cache;
- cache = lzo_block->dst;
- if (cache[blkpos] == value) {
- kfree(lzo_block->dst);
- goto out;
- }
- cache[blkpos] = value;
- }
- break;
- default:
- BUG();
+ if (snd_soc_set_cache_val(lzo_block->dst, blkpos, value,
+ codec->driver->reg_word_size)) {
+ kfree(lzo_block->dst);
+ goto out;
}
/* prepare the source to be the decompressed block */
@@ -1241,25 +1254,10 @@ static int snd_soc_lzo_cache_read(struct snd_soc_codec *codec,
/* decompress the block */
ret = snd_soc_lzo_decompress_cache_block(codec, lzo_block);
- if (ret >= 0) {
+ if (ret >= 0)
/* fetch the value from the cache */
- switch (codec->driver->reg_word_size) {
- case 1: {
- u8 *cache;
- cache = lzo_block->dst;
- *value = cache[blkpos];
- }
- break;
- case 2: {
- u16 *cache;
- cache = lzo_block->dst;
- *value = cache[blkpos];
- }
- break;
- default:
- BUG();
- }
- }
+ *value = snd_soc_get_cache_val(lzo_block->dst, blkpos,
+ codec->driver->reg_word_size);
kfree(lzo_block->dst);
/* restore the pointer and length of the compressed block */
@@ -1301,7 +1299,7 @@ static int snd_soc_lzo_cache_exit(struct snd_soc_codec *codec)
static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec)
{
struct snd_soc_lzo_ctx **lzo_blocks;
- size_t reg_size, bmp_size;
+ size_t bmp_size;
const struct snd_soc_codec_driver *codec_drv;
int ret, tofree, i, blksize, blkcount;
const char *p, *end;
@@ -1309,7 +1307,6 @@ static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec)
ret = 0;
codec_drv = codec->driver;
- reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
/*
* If we have not been given a default register cache
@@ -1321,8 +1318,7 @@ static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec)
tofree = 1;
if (!codec->reg_def_copy) {
- codec->reg_def_copy = kzalloc(reg_size,
- GFP_KERNEL);
+ codec->reg_def_copy = kzalloc(codec->reg_size, GFP_KERNEL);
if (!codec->reg_def_copy)
return -ENOMEM;
}
@@ -1370,7 +1366,7 @@ static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec)
blksize = snd_soc_lzo_get_blksize(codec);
p = codec->reg_def_copy;
- end = codec->reg_def_copy + reg_size;
+ end = codec->reg_def_copy + codec->reg_size;
/* compress the register map and fill the lzo blocks */
for (i = 0; i < blkcount; ++i, p += blksize) {
lzo_blocks[i]->src = p;
@@ -1414,28 +1410,10 @@ static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec)
ret = snd_soc_cache_read(codec, i, &val);
if (ret)
return ret;
- if (codec_drv->reg_cache_default) {
- switch (codec_drv->reg_word_size) {
- case 1: {
- const u8 *cache;
-
- cache = codec_drv->reg_cache_default;
- if (cache[i] == val)
- continue;
- }
- break;
- case 2: {
- const u16 *cache;
-
- cache = codec_drv->reg_cache_default;
- if (cache[i] == val)
- continue;
- }
- break;
- default:
- BUG();
- }
- }
+ if (codec->reg_def_copy)
+ if (snd_soc_get_cache_val(codec->reg_def_copy,
+ i, codec_drv->reg_word_size) == val)
+ continue;
ret = snd_soc_write(codec, i, val);
if (ret)
return ret;
@@ -1448,50 +1426,16 @@ static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec)
static int snd_soc_flat_cache_write(struct snd_soc_codec *codec,
unsigned int reg, unsigned int value)
{
- switch (codec->driver->reg_word_size) {
- case 1: {
- u8 *cache;
-
- cache = codec->reg_cache;
- cache[reg] = value;
- }
- break;
- case 2: {
- u16 *cache;
-
- cache = codec->reg_cache;
- cache[reg] = value;
- }
- break;
- default:
- BUG();
- }
-
+ snd_soc_set_cache_val(codec->reg_cache, reg, value,
+ codec->driver->reg_word_size);
return 0;
}
static int snd_soc_flat_cache_read(struct snd_soc_codec *codec,
unsigned int reg, unsigned int *value)
{
- switch (codec->driver->reg_word_size) {
- case 1: {
- u8 *cache;
-
- cache = codec->reg_cache;
- *value = cache[reg];
- }
- break;
- case 2: {
- u16 *cache;
-
- cache = codec->reg_cache;
- *value = cache[reg];
- }
- break;
- default:
- BUG();
- }
-
+ *value = snd_soc_get_cache_val(codec->reg_cache, reg,
+ codec->driver->reg_word_size);
return 0;
}
@@ -1507,24 +1451,14 @@ static int snd_soc_flat_cache_exit(struct snd_soc_codec *codec)
static int snd_soc_flat_cache_init(struct snd_soc_codec *codec)
{
const struct snd_soc_codec_driver *codec_drv;
- size_t reg_size;
codec_drv = codec->driver;
- reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
- /*
- * for flat compression, we don't need to keep a copy of the
- * original defaults register cache as it will definitely not
- * be marked as __devinitconst
- */
- kfree(codec->reg_def_copy);
- codec->reg_def_copy = NULL;
-
- if (codec_drv->reg_cache_default)
- codec->reg_cache = kmemdup(codec_drv->reg_cache_default,
- reg_size, GFP_KERNEL);
+ if (codec->reg_def_copy)
+ codec->reg_cache = kmemdup(codec->reg_def_copy,
+ codec->reg_size, GFP_KERNEL);
else
- codec->reg_cache = kzalloc(reg_size, GFP_KERNEL);
+ codec->reg_cache = kzalloc(codec->reg_size, GFP_KERNEL);
if (!codec->reg_cache)
return -ENOMEM;
@@ -1669,21 +1603,77 @@ EXPORT_SYMBOL_GPL(snd_soc_cache_write);
int snd_soc_cache_sync(struct snd_soc_codec *codec)
{
int ret;
+ const char *name;
if (!codec->cache_sync) {
return 0;
}
- if (codec->cache_ops && codec->cache_ops->sync) {
- if (codec->cache_ops->name)
- dev_dbg(codec->dev, "Syncing %s cache for %s codec\n",
- codec->cache_ops->name, codec->name);
- ret = codec->cache_ops->sync(codec);
- if (!ret)
- codec->cache_sync = 0;
- return ret;
- }
+ if (!codec->cache_ops || !codec->cache_ops->sync)
+ return -EINVAL;
- return -EINVAL;
+ if (codec->cache_ops->name)
+ name = codec->cache_ops->name;
+ else
+ name = "unknown";
+
+ if (codec->cache_ops->name)
+ dev_dbg(codec->dev, "Syncing %s cache for %s codec\n",
+ codec->cache_ops->name, codec->name);
+ trace_snd_soc_cache_sync(codec, name, "start");
+ ret = codec->cache_ops->sync(codec);
+ if (!ret)
+ codec->cache_sync = 0;
+ trace_snd_soc_cache_sync(codec, name, "end");
+ return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_cache_sync);
+
+static int snd_soc_get_reg_access_index(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ const struct snd_soc_codec_driver *codec_drv;
+ unsigned int min, max, index;
+
+ codec_drv = codec->driver;
+ min = 0;
+ max = codec_drv->reg_access_size - 1;
+ do {
+ index = (min + max) / 2;
+ if (codec_drv->reg_access_default[index].reg == reg)
+ return index;
+ if (codec_drv->reg_access_default[index].reg < reg)
+ min = index + 1;
+ else
+ max = index;
+ } while (min <= max);
+ return -1;
+}
+
+int snd_soc_default_volatile_register(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ int index;
+
+ if (reg >= codec->driver->reg_cache_size)
+ return 1;
+ index = snd_soc_get_reg_access_index(codec, reg);
+ if (index < 0)
+ return 0;
+ return codec->driver->reg_access_default[index].vol;
+}
+EXPORT_SYMBOL_GPL(snd_soc_default_volatile_register);
+
+int snd_soc_default_readable_register(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ int index;
+
+ if (reg >= codec->driver->reg_cache_size)
+ return 1;
+ index = snd_soc_get_reg_access_index(codec, reg);
+ if (index < 0)
+ return 0;
+ return codec->driver->reg_access_default[index].read;
+}
+EXPORT_SYMBOL_GPL(snd_soc_default_readable_register);