]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - sound/soc/soc-cache.c
asoc: tegra: aic326x: Don't disable clock if call is active
[linux-2.6.git] / sound / soc / soc-cache.c
index 097e33510a7a0120e3b6cc4474a75ac0a5e72766..9d56f0218f41688576fec511db272e6484480bdb 100644 (file)
 #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>
 
-static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec,
-                                    unsigned int reg)
+#include <trace/events/asoc.h>
+
+static bool snd_soc_set_cache_val(void *base, unsigned int idx,
+                                 unsigned int val, unsigned int word_size)
 {
-       u16 *cache = codec->reg_cache;
-       if (reg >= codec->reg_cache_size)
-               return -1;
-       return cache[reg];
+       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 int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
-                            unsigned int value)
+static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx,
+               unsigned int word_size)
 {
-       u16 *cache = codec->reg_cache;
-       u8 data[2];
-       int ret;
-
-       BUG_ON(codec->volatile_register);
-
-       data[0] = (reg << 4) | ((value >> 8) & 0x000f);
-       data[1] = value & 0x00ff;
+       if (!base)
+               return -1;
 
-       if (reg < codec->reg_cache_size)
-               cache[reg] = value;
-       ret = codec->hw_write(codec->control_data, data, 2);
-       if (ret == 2)
-               return 0;
-       if (ret < 0)
-               return ret;
-       else
-               return -EIO;
+       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;
 }
 
-#if defined(CONFIG_SPI_MASTER)
-static int snd_soc_4_12_spi_write(void *control_data, const char *data,
-                                int len)
+static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec)
 {
-       struct spi_device *spi = control_data;
-       struct spi_transfer t;
-       struct spi_message m;
-       u8 msg[2];
-
-       if (len <= 0)
-               return 0;
-
-       msg[0] = data[1];
-       msg[1] = data[0];
-
-       spi_message_init(&m);
-       memset(&t, 0, (sizeof t));
-
-       t.tx_buf = &msg[0];
-       t.len = len;
-
-       spi_message_add_tail(&t, &m);
-       spi_sync(spi, &m);
-
-       return len;
+       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->reg_def_copy)
+                       if (snd_soc_get_cache_val(codec->reg_def_copy,
+                                                 i, codec_drv->reg_word_size) == val)
+                               continue;
+
+               WARN_ON(!snd_soc_codec_writable_register(codec, i));
+
+               ret = snd_soc_write(codec, i, val);
+               if (ret)
+                       return ret;
+               dev_dbg(codec->dev, "Synced register %#x, value = %#x\n",
+                       i, val);
+       }
+       return 0;
 }
-#else
-#define snd_soc_4_12_spi_write NULL
-#endif
 
-static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec,
-                                    unsigned int reg)
+static int snd_soc_flat_cache_write(struct snd_soc_codec *codec,
+                                   unsigned int reg, unsigned int value)
 {
-       u16 *cache = codec->reg_cache;
-       if (reg >= codec->reg_cache_size)
-               return -1;
-       return cache[reg];
+       snd_soc_set_cache_val(codec->reg_cache, reg, value,
+                             codec->driver->reg_word_size);
+       return 0;
 }
 
-static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg,
-                            unsigned int value)
+static int snd_soc_flat_cache_read(struct snd_soc_codec *codec,
+                                  unsigned int reg, unsigned int *value)
 {
-       u16 *cache = codec->reg_cache;
-       u8 data[2];
-       int ret;
-
-       BUG_ON(codec->volatile_register);
-
-       data[0] = (reg << 1) | ((value >> 8) & 0x0001);
-       data[1] = value & 0x00ff;
-
-       if (reg < codec->reg_cache_size)
-               cache[reg] = value;
-       ret = codec->hw_write(codec->control_data, data, 2);
-       if (ret == 2)
-               return 0;
-       if (ret < 0)
-               return ret;
-       else
-               return -EIO;
+       *value = snd_soc_get_cache_val(codec->reg_cache, reg,
+                                      codec->driver->reg_word_size);
+       return 0;
 }
 
-#if defined(CONFIG_SPI_MASTER)
-static int snd_soc_7_9_spi_write(void *control_data, const char *data,
-                                int len)
+static int snd_soc_flat_cache_exit(struct snd_soc_codec *codec)
 {
-       struct spi_device *spi = control_data;
-       struct spi_transfer t;
-       struct spi_message m;
-       u8 msg[2];
-
-       if (len <= 0)
+       if (!codec->reg_cache)
                return 0;
+       kfree(codec->reg_cache);
+       codec->reg_cache = NULL;
+       return 0;
+}
 
-       msg[0] = data[0];
-       msg[1] = data[1];
-
-       spi_message_init(&m);
-       memset(&t, 0, (sizeof t));
-
-       t.tx_buf = &msg[0];
-       t.len = len;
-
-       spi_message_add_tail(&t, &m);
-       spi_sync(spi, &m);
+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(codec->reg_size, GFP_KERNEL);
+       if (!codec->reg_cache)
+               return -ENOMEM;
 
-       return len;
+       return 0;
 }
-#else
-#define snd_soc_7_9_spi_write NULL
-#endif
 
-static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg,
-                            unsigned int value)
+/* 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)
 {
-       u8 *cache = codec->reg_cache;
-       u8 data[2];
+       int i;
 
-       BUG_ON(codec->volatile_register);
+       for (i = 0; i < ARRAY_SIZE(cache_types); ++i)
+               if (cache_types[i].id == codec->compress_type)
+                       break;
 
-       data[0] = reg & 0xff;
-       data[1] = value & 0xff;
+       /* Fall back to flat compression */
+       if (i == ARRAY_SIZE(cache_types)) {
+               dev_warn(codec->dev, "Could not match compress type: %d\n",
+                        codec->compress_type);
+               i = 0;
+       }
 
-       if (reg < codec->reg_cache_size)
-               cache[reg] = value;
+       mutex_init(&codec->cache_rw_mutex);
+       codec->cache_ops = &cache_types[i];
 
-       if (codec->hw_write(codec->control_data, data, 2) == 2)
-               return 0;
-       else
-               return -EIO;
+       if (codec->cache_ops->init) {
+               if (codec->cache_ops->name)
+                       dev_dbg(codec->dev, "Initializing %s cache for %s codec\n",
+                               codec->cache_ops->name, codec->name);
+               return codec->cache_ops->init(codec);
+       }
+       return -ENOSYS;
 }
 
-static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec,
-                                    unsigned int reg)
+/*
+ * NOTE: keep in mind that this function might be called
+ * multiple times.
+ */
+int snd_soc_cache_exit(struct snd_soc_codec *codec)
 {
-       u8 *cache = codec->reg_cache;
-       if (reg >= codec->reg_cache_size)
-               return -1;
-       return cache[reg];
+       if (codec->cache_ops && codec->cache_ops->exit) {
+               if (codec->cache_ops->name)
+                       dev_dbg(codec->dev, "Destroying %s cache for %s codec\n",
+                               codec->cache_ops->name, codec->name);
+               return codec->cache_ops->exit(codec);
+       }
+       return -ENOSYS;
 }
 
-static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
-                             unsigned int value)
+/**
+ * snd_soc_cache_read: Fetch the value of a given register from the cache.
+ *
+ * @codec: CODEC to configure.
+ * @reg: The register index.
+ * @value: The value to be returned.
+ */
+int snd_soc_cache_read(struct snd_soc_codec *codec,
+                      unsigned int reg, unsigned int *value)
 {
-       u16 *reg_cache = codec->reg_cache;
-       u8 data[3];
+       int ret;
 
-       data[0] = reg;
-       data[1] = (value >> 8) & 0xff;
-       data[2] = value & 0xff;
+       mutex_lock(&codec->cache_rw_mutex);
 
-       if (!snd_soc_codec_volatile_register(codec, reg))
-               reg_cache[reg] = value;
+       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;
+       }
 
-       if (codec->hw_write(codec->control_data, data, 3) == 3)
-               return 0;
-       else
-               return -EIO;
+       mutex_unlock(&codec->cache_rw_mutex);
+       return -ENOSYS;
 }
+EXPORT_SYMBOL_GPL(snd_soc_cache_read);
 
-static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec,
-                                     unsigned int reg)
+/**
+ * snd_soc_cache_write: Set the value of a given register in the cache.
+ *
+ * @codec: CODEC to configure.
+ * @reg: The register index.
+ * @value: The new register value.
+ */
+int snd_soc_cache_write(struct snd_soc_codec *codec,
+                       unsigned int reg, unsigned int value)
 {
-       u16 *cache = codec->reg_cache;
+       int ret;
 
-       if (reg >= codec->reg_cache_size ||
-           snd_soc_codec_volatile_register(codec, reg))
-               return codec->hw_read(codec, reg);
-       else
-               return cache[reg];
-}
+       mutex_lock(&codec->cache_rw_mutex);
 
-#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec,
-                                         unsigned int r)
-{
-       struct i2c_msg xfer[2];
-       u8 reg = r;
-       u16 data;
-       int ret;
-       struct i2c_client *client = codec->control_data;
-
-       /* Write register */
-       xfer[0].addr = client->addr;
-       xfer[0].flags = 0;
-       xfer[0].len = 1;
-       xfer[0].buf = &reg;
-
-       /* Read data */
-       xfer[1].addr = client->addr;
-       xfer[1].flags = I2C_M_RD;
-       xfer[1].len = 2;
-       xfer[1].buf = (u8 *)&data;
-
-       ret = i2c_transfer(client->adapter, xfer, 2);
-       if (ret != 2) {
-               dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
-               return 0;
+       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 (data >> 8) | ((data & 0xff) << 8);
+       mutex_unlock(&codec->cache_rw_mutex);
+       return -ENOSYS;
 }
-#else
-#define snd_soc_8_16_read_i2c NULL
-#endif
+EXPORT_SYMBOL_GPL(snd_soc_cache_write);
 
-#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec,
-                                         unsigned int r)
+/**
+ * snd_soc_cache_sync: Sync the register cache with the hardware.
+ *
+ * @codec: CODEC to configure.
+ *
+ * Any registers that should not be synced should be marked as
+ * volatile.  In general drivers can choose not to use the provided
+ * syncing functionality if they so require.
+ */
+int snd_soc_cache_sync(struct snd_soc_codec *codec)
 {
-       struct i2c_msg xfer[2];
-       u16 reg = r;
-       u8 data;
        int ret;
-       struct i2c_client *client = codec->control_data;
-
-       /* Write register */
-       xfer[0].addr = client->addr;
-       xfer[0].flags = 0;
-       xfer[0].len = 2;
-       xfer[0].buf = (u8 *)&reg;
-
-       /* Read data */
-       xfer[1].addr = client->addr;
-       xfer[1].flags = I2C_M_RD;
-       xfer[1].len = 1;
-       xfer[1].buf = &data;
-
-       ret = i2c_transfer(client->adapter, xfer, 2);
-       if (ret != 2) {
-               dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
+       const char *name;
+
+       if (!codec->cache_sync) {
                return 0;
        }
 
-       return data;
+       if (!codec->cache_ops || !codec->cache_ops->sync)
+               return -ENOSYS;
+
+       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;
 }
-#else
-#define snd_soc_16_8_read_i2c NULL
-#endif
+EXPORT_SYMBOL_GPL(snd_soc_cache_sync);
 
-static unsigned int snd_soc_16_8_read(struct snd_soc_codec *codec,
-                                    unsigned int reg)
+static int snd_soc_get_reg_access_index(struct snd_soc_codec *codec,
+                                       unsigned int reg)
 {
-       u16 *cache = codec->reg_cache;
-
-       reg &= 0xff;
-       if (reg >= codec->reg_cache_size)
-               return -1;
-       return cache[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;
 }
 
-static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg,
-                            unsigned int value)
+int snd_soc_default_volatile_register(struct snd_soc_codec *codec,
+                                     unsigned int reg)
 {
-       u16 *cache = codec->reg_cache;
-       u8 data[3];
-       int ret;
-
-       BUG_ON(codec->volatile_register);
+       int index;
 
-       data[0] = (reg >> 8) & 0xff;
-       data[1] = reg & 0xff;
-       data[2] = value;
-
-       reg &= 0xff;
-       if (reg < codec->reg_cache_size)
-               cache[reg] = value;
-       ret = codec->hw_write(codec->control_data, data, 3);
-       if (ret == 3)
+       if (reg >= codec->driver->reg_cache_size)
+               return 1;
+       index = snd_soc_get_reg_access_index(codec, reg);
+       if (index < 0)
                return 0;
-       if (ret < 0)
-               return ret;
-       else
-               return -EIO;
+       return codec->driver->reg_access_default[index].vol;
 }
+EXPORT_SYMBOL_GPL(snd_soc_default_volatile_register);
 
-#if defined(CONFIG_SPI_MASTER)
-static int snd_soc_16_8_spi_write(void *control_data, const char *data,
-                                int len)
+int snd_soc_default_readable_register(struct snd_soc_codec *codec,
+                                     unsigned int reg)
 {
-       struct spi_device *spi = control_data;
-       struct spi_transfer t;
-       struct spi_message m;
-       u8 msg[3];
+       int index;
 
-       if (len <= 0)
+       if (reg >= codec->driver->reg_cache_size)
+               return 1;
+       index = snd_soc_get_reg_access_index(codec, reg);
+       if (index < 0)
                return 0;
-
-       msg[0] = data[0];
-       msg[1] = data[1];
-       msg[2] = data[2];
-
-       spi_message_init(&m);
-       memset(&t, 0, (sizeof t));
-
-       t.tx_buf = &msg[0];
-       t.len = len;
-
-       spi_message_add_tail(&t, &m);
-       spi_sync(spi, &m);
-
-       return len;
+       return codec->driver->reg_access_default[index].read;
 }
-#else
-#define snd_soc_16_8_spi_write NULL
-#endif
-
-
-static struct {
-       int addr_bits;
-       int data_bits;
-       int (*write)(struct snd_soc_codec *codec, unsigned int, unsigned int);
-       int (*spi_write)(void *, const char *, int);
-       unsigned int (*read)(struct snd_soc_codec *, unsigned int);
-       unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int);
-} io_types[] = {
-       {
-               .addr_bits = 4, .data_bits = 12,
-               .write = snd_soc_4_12_write, .read = snd_soc_4_12_read,
-               .spi_write = snd_soc_4_12_spi_write,
-       },
-       {
-               .addr_bits = 7, .data_bits = 9,
-               .write = snd_soc_7_9_write, .read = snd_soc_7_9_read,
-               .spi_write = snd_soc_7_9_spi_write,
-       },
-       {
-               .addr_bits = 8, .data_bits = 8,
-               .write = snd_soc_8_8_write, .read = snd_soc_8_8_read,
-       },
-       {
-               .addr_bits = 8, .data_bits = 16,
-               .write = snd_soc_8_16_write, .read = snd_soc_8_16_read,
-               .i2c_read = snd_soc_8_16_read_i2c,
-       },
-       {
-               .addr_bits = 16, .data_bits = 8,
-               .write = snd_soc_16_8_write, .read = snd_soc_16_8_read,
-               .i2c_read = snd_soc_16_8_read_i2c,
-               .spi_write = snd_soc_16_8_spi_write,
-       },
-};
+EXPORT_SYMBOL_GPL(snd_soc_default_readable_register);
 
-/**
- * snd_soc_codec_set_cache_io: Set up standard I/O functions.
- *
- * @codec: CODEC to configure.
- * @type: Type of cache.
- * @addr_bits: Number of bits of register address data.
- * @data_bits: Number of bits of data per register.
- * @control: Control bus used.
- *
- * Register formats are frequently shared between many I2C and SPI
- * devices.  In order to promote code reuse the ASoC core provides
- * some standard implementations of CODEC read and write operations
- * which can be set up using this function.
- *
- * The caller is responsible for allocating and initialising the
- * actual cache.
- *
- * Note that at present this code cannot be used by CODECs with
- * volatile registers.
- */
-int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
-                              int addr_bits, int data_bits,
-                              enum snd_soc_control_type control)
+int snd_soc_default_writable_register(struct snd_soc_codec *codec,
+                                     unsigned int reg)
 {
-       int i;
+       int index;
 
-       for (i = 0; i < ARRAY_SIZE(io_types); i++)
-               if (io_types[i].addr_bits == addr_bits &&
-                   io_types[i].data_bits == data_bits)
-                       break;
-       if (i == ARRAY_SIZE(io_types)) {
-               printk(KERN_ERR
-                      "No I/O functions for %d bit address %d bit data\n",
-                      addr_bits, data_bits);
-               return -EINVAL;
-       }
-
-       codec->write = io_types[i].write;
-       codec->read = io_types[i].read;
-
-       switch (control) {
-       case SND_SOC_CUSTOM:
-               break;
-
-       case SND_SOC_I2C:
-#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-               codec->hw_write = (hw_write_t)i2c_master_send;
-#endif
-               if (io_types[i].i2c_read)
-                       codec->hw_read = io_types[i].i2c_read;
-               break;
-
-       case SND_SOC_SPI:
-               if (io_types[i].spi_write)
-                       codec->hw_write = io_types[i].spi_write;
-               break;
-       }
-
-       return 0;
+       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_codec_set_cache_io);
+EXPORT_SYMBOL_GPL(snd_soc_default_writable_register);