diff options
Diffstat (limited to 'sound/soc/soc-cache.c')
-rw-r--r-- | sound/soc/soc-cache.c | 269 |
1 files changed, 204 insertions, 65 deletions
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 375dc6d..e72f554 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -11,9 +11,12 @@ * option) any later version. */ +#include <linux/i2c.h> +#include <linux/spi/spi.h> #include <sound/soc.h> +#include <linux/bitmap.h> +#include <linux/rbtree.h> #include <linux/export.h> -#include <linux/slab.h> #include <trace/events/asoc.h> @@ -36,8 +39,7 @@ static bool snd_soc_set_cache_val(void *base, unsigned int idx, break; } default: - WARN(1, "Invalid word_size %d\n", word_size); - break; + BUG(); } return false; } @@ -58,49 +60,132 @@ static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx, return cache[idx]; } default: - WARN(1, "Invalid word_size %d\n", word_size); - break; + BUG(); } /* unreachable */ return -1; } -int snd_soc_cache_init(struct snd_soc_codec *codec) +static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) { - const struct snd_soc_codec_driver *codec_drv = codec->driver; - size_t reg_size; + int i; + int ret; + const struct snd_soc_codec_driver *codec_drv; + unsigned int val; - reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; + codec_drv = codec->driver; + for (i = 0; i < codec_drv->reg_cache_size; ++i) { + ret = snd_soc_cache_read(codec, i, &val); + if (ret) + return ret; + if (codec->reg_def_copy) + if (snd_soc_get_cache_val(codec->reg_def_copy, + i, codec_drv->reg_word_size) == val) + continue; - mutex_init(&codec->cache_rw_mutex); + WARN_ON(!snd_soc_codec_writable_register(codec, i)); + + ret = snd_soc_write(codec, i, val); + if (ret) + return ret; + dev_dbg(codec->dev, "ASoC: Synced register %#x, value = %#x\n", + i, val); + } + return 0; +} + +static int snd_soc_flat_cache_write(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + 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) +{ + *value = snd_soc_get_cache_val(codec->reg_cache, reg, + codec->driver->reg_word_size); + return 0; +} - dev_dbg(codec->dev, "ASoC: Initializing cache for %s codec\n", - codec->name); +static int snd_soc_flat_cache_exit(struct snd_soc_codec *codec) +{ + if (!codec->reg_cache) + return 0; + kfree(codec->reg_cache); + codec->reg_cache = NULL; + return 0; +} - if (codec_drv->reg_cache_default) - codec->reg_cache = kmemdup(codec_drv->reg_cache_default, - reg_size, GFP_KERNEL); +static int snd_soc_flat_cache_init(struct snd_soc_codec *codec) +{ + 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; return 0; } +/* an array of all supported compression types */ +static const struct snd_soc_cache_ops cache_types[] = { + /* Flat *must* be the first entry for fallback */ + { + .id = SND_SOC_FLAT_COMPRESSION, + .name = "flat", + .init = snd_soc_flat_cache_init, + .exit = snd_soc_flat_cache_exit, + .read = snd_soc_flat_cache_read, + .write = snd_soc_flat_cache_write, + .sync = snd_soc_flat_cache_sync + }, +}; + +int snd_soc_cache_init(struct snd_soc_codec *codec) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cache_types); ++i) + if (cache_types[i].id == codec->compress_type) + break; + + /* Fall back to flat compression */ + if (i == ARRAY_SIZE(cache_types)) { + dev_warn(codec->dev, "ASoC: Could not match compress type: %d\n", + codec->compress_type); + i = 0; + } + + mutex_init(&codec->cache_rw_mutex); + codec->cache_ops = &cache_types[i]; + + if (codec->cache_ops->init) { + if (codec->cache_ops->name) + dev_dbg(codec->dev, "ASoC: Initializing %s cache for %s codec\n", + codec->cache_ops->name, codec->name); + return codec->cache_ops->init(codec); + } + return -ENOSYS; +} + /* * NOTE: keep in mind that this function might be called * multiple times. */ int snd_soc_cache_exit(struct snd_soc_codec *codec) { - dev_dbg(codec->dev, "ASoC: Destroying cache for %s codec\n", - codec->name); - if (!codec->reg_cache) - return 0; - kfree(codec->reg_cache); - codec->reg_cache = NULL; - return 0; + if (codec->cache_ops && codec->cache_ops->exit) { + if (codec->cache_ops->name) + dev_dbg(codec->dev, "ASoC: Destroying %s cache for %s codec\n", + codec->cache_ops->name, codec->name); + return codec->cache_ops->exit(codec); + } + return -ENOSYS; } /** @@ -113,15 +198,18 @@ int snd_soc_cache_exit(struct snd_soc_codec *codec) int snd_soc_cache_read(struct snd_soc_codec *codec, unsigned int reg, unsigned int *value) { - if (!value) - return -EINVAL; + int ret; mutex_lock(&codec->cache_rw_mutex); - *value = snd_soc_get_cache_val(codec->reg_cache, reg, - codec->driver->reg_word_size); - mutex_unlock(&codec->cache_rw_mutex); - return 0; + if (value && codec->cache_ops && codec->cache_ops->read) { + ret = codec->cache_ops->read(codec, reg, value); + mutex_unlock(&codec->cache_rw_mutex); + return ret; + } + + mutex_unlock(&codec->cache_rw_mutex); + return -ENOSYS; } EXPORT_SYMBOL_GPL(snd_soc_cache_read); @@ -135,42 +223,20 @@ EXPORT_SYMBOL_GPL(snd_soc_cache_read); int snd_soc_cache_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { - mutex_lock(&codec->cache_rw_mutex); - snd_soc_set_cache_val(codec->reg_cache, reg, value, - codec->driver->reg_word_size); - mutex_unlock(&codec->cache_rw_mutex); - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_cache_write); - -static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) -{ - int i; int ret; - const struct snd_soc_codec_driver *codec_drv; - unsigned int val; - - codec_drv = codec->driver; - for (i = 0; i < codec_drv->reg_cache_size; ++i) { - ret = snd_soc_cache_read(codec, i, &val); - if (ret) - return ret; - if (codec_drv->reg_cache_default) - if (snd_soc_get_cache_val(codec_drv->reg_cache_default, - i, codec_drv->reg_word_size) == val) - continue; - WARN_ON(!snd_soc_codec_writable_register(codec, i)); + mutex_lock(&codec->cache_rw_mutex); - ret = snd_soc_write(codec, i, val); - if (ret) - return ret; - dev_dbg(codec->dev, "ASoC: Synced register %#x, value = %#x\n", - i, val); + if (codec->cache_ops && codec->cache_ops->write) { + ret = codec->cache_ops->write(codec, reg, value); + mutex_unlock(&codec->cache_rw_mutex); + return ret; } - return 0; + + mutex_unlock(&codec->cache_rw_mutex); + return -ENOSYS; } +EXPORT_SYMBOL_GPL(snd_soc_cache_write); /** * snd_soc_cache_sync: Sync the register cache with the hardware. @@ -183,19 +249,92 @@ static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) */ int snd_soc_cache_sync(struct snd_soc_codec *codec) { - const char *name = "flat"; int ret; + const char *name; - if (!codec->cache_sync) + if (!codec->cache_sync) { return 0; + } + + if (!codec->cache_ops || !codec->cache_ops->sync) + return -ENOSYS; - dev_dbg(codec->dev, "ASoC: Syncing cache for %s codec\n", - codec->name); + if (codec->cache_ops->name) + name = codec->cache_ops->name; + else + name = "unknown"; + + if (codec->cache_ops->name) + dev_dbg(codec->dev, "ASoC: Syncing %s cache for %s codec\n", + codec->cache_ops->name, codec->name); trace_snd_soc_cache_sync(codec, name, "start"); - ret = snd_soc_flat_cache_sync(codec); + 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); + +int snd_soc_default_writable_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].write; +} +EXPORT_SYMBOL_GPL(snd_soc_default_writable_register); |