diff options
Diffstat (limited to 'sound/soc/soc-cache.c')
| -rw-r--r-- | sound/soc/soc-cache.c | 797 |
1 files changed, 137 insertions, 660 deletions
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index d214f02cbb6..00e70b6c7da 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -11,716 +11,193 @@ * option) any later version. */ -#include <linux/i2c.h> -#include <linux/spi/spi.h> #include <sound/soc.h> +#include <linux/export.h> +#include <linux/slab.h> -static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - u16 *cache = codec->reg_cache; - - if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { - if (codec->cache_only) - return -1; - - return codec->hw_read(codec, reg); - } - - return cache[reg]; -} - -static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u16 *cache = codec->reg_cache; - u8 data[2]; - int ret; - - data[0] = (reg << 4) | ((value >> 8) & 0x000f); - data[1] = value & 0x00ff; - - if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) - cache[reg] = value; - - if (codec->cache_only) { - codec->cache_sync = 1; - return 0; - } - - dev_dbg(codec->dev, "0x%x = 0x%x\n", reg, value); - - ret = codec->hw_write(codec->control_data, data, 2); - if (ret == 2) - return 0; - if (ret < 0) - return ret; - else - return -EIO; -} - -#if defined(CONFIG_SPI_MASTER) -static int snd_soc_4_12_spi_write(void *control_data, const char *data, - int len) -{ - 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; -} -#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) -{ - u16 *cache = codec->reg_cache; - - if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { - if (codec->cache_only) - return -1; - - return codec->hw_read(codec, reg); - } - - return cache[reg]; -} - -static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u16 *cache = codec->reg_cache; - u8 data[2]; - int ret; - - data[0] = (reg << 1) | ((value >> 8) & 0x0001); - data[1] = value & 0x00ff; - - if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) - cache[reg] = value; - - if (codec->cache_only) { - codec->cache_sync = 1; - return 0; - } - - dev_dbg(codec->dev, "0x%x = 0x%x\n", reg, value); - - ret = codec->hw_write(codec->control_data, data, 2); - if (ret == 2) - return 0; - if (ret < 0) - return ret; - else - return -EIO; -} - -#if defined(CONFIG_SPI_MASTER) -static int snd_soc_7_9_spi_write(void *control_data, const char *data, - int len) -{ - 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[0]; - msg[1] = data[1]; +#include <trace/events/asoc.h> - 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; -} -#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) +static bool snd_soc_set_cache_val(void *base, unsigned int idx, + unsigned int val, unsigned int word_size) { - u8 *cache = codec->reg_cache; - u8 data[2]; - - reg &= 0xff; - data[0] = reg; - data[1] = value & 0xff; - - if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) - cache[reg] = value; - - if (codec->cache_only) { - codec->cache_sync = 1; - return 0; + switch (word_size) { + case 1: { + u8 *cache = base; + if (cache[idx] == val) + return true; + cache[idx] = val; + break; } - - dev_dbg(codec->dev, "0x%x = 0x%x\n", reg, value); - - if (codec->hw_write(codec->control_data, data, 2) == 2) - return 0; - else - return -EIO; -} - -static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - u8 *cache = codec->reg_cache; - - reg &= 0xff; - if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { - if (codec->cache_only) - return -1; - - return codec->hw_read(codec, reg); + case 2: { + u16 *cache = base; + if (cache[idx] == val) + return true; + cache[idx] = val; + break; } - - return cache[reg]; -} - -#if defined(CONFIG_SPI_MASTER) -static int snd_soc_8_8_spi_write(void *control_data, const char *data, - int len) -{ - 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[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); - - return len; -} -#else -#define snd_soc_8_8_spi_write NULL -#endif - -static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u16 *reg_cache = codec->reg_cache; - u8 data[3]; - - data[0] = reg; - data[1] = (value >> 8) & 0xff; - data[2] = value & 0xff; - - if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) - reg_cache[reg] = value; - - if (codec->cache_only) { - codec->cache_sync = 1; - return 0; + default: + WARN(1, "Invalid word_size %d\n", word_size); + break; } - - dev_dbg(codec->dev, "0x%x = 0x%x\n", reg, value); - - if (codec->hw_write(codec->control_data, data, 3) == 3) - return 0; - else - return -EIO; + return false; } -static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec, - unsigned int reg) +static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx, + unsigned int word_size) { - u16 *cache = codec->reg_cache; + if (!base) + return -1; - if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { - if (codec->cache_only) - return -1; - - return codec->hw_read(codec, reg); - } else { - return cache[reg]; + switch (word_size) { + case 1: { + const u8 *cache = base; + return cache[idx]; } -} - -#if defined(CONFIG_SPI_MASTER) -static int snd_soc_8_16_spi_write(void *control_data, const char *data, - int len) -{ - struct spi_device *spi = control_data; - struct spi_transfer t; - struct spi_message m; - u8 msg[3]; - - if (len <= 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; -} -#else -#define snd_soc_8_16_spi_write NULL -#endif - -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) -static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec, - unsigned int r) -{ - struct i2c_msg xfer[2]; - u8 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 = 1; - xfer[0].buf = ® - - /* 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); - return 0; + case 2: { + const u16 *cache = base; + return cache[idx]; } - - return data; -} -#else -#define snd_soc_8_8_read_i2c NULL -#endif - -#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 = ® - - /* 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; + default: + WARN(1, "Invalid word_size %d\n", word_size); + break; } - - return (data >> 8) | ((data & 0xff) << 8); + /* unreachable */ + return -1; } -#else -#define snd_soc_8_16_read_i2c NULL -#endif -#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) +int snd_soc_cache_init(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 *)® + const struct snd_soc_codec_driver *codec_drv = codec->driver; + size_t reg_size; - /* Read data */ - xfer[1].addr = client->addr; - xfer[1].flags = I2C_M_RD; - xfer[1].len = 1; - xfer[1].buf = &data; + reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; - ret = i2c_transfer(client->adapter, xfer, 2); - if (ret != 2) { - dev_err(&client->dev, "i2c_transfer() returned %d\n", ret); + if (!reg_size) return 0; - } - - return data; -} -#else -#define snd_soc_16_8_read_i2c NULL -#endif - -static unsigned int snd_soc_16_8_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - u8 *cache = codec->reg_cache; - - reg &= 0xff; - if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { - if (codec->cache_only) - return -1; - return codec->hw_read(codec, reg); - } - - return cache[reg]; -} - -static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u8 *cache = codec->reg_cache; - u8 data[3]; - int ret; - - data[0] = (reg >> 8) & 0xff; - data[1] = reg & 0xff; - data[2] = value; + mutex_init(&codec->cache_rw_mutex); - reg &= 0xff; - if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) - cache[reg] = value; + dev_dbg(codec->dev, "ASoC: Initializing cache for %s codec\n", + codec->name); - if (codec->cache_only) { - codec->cache_sync = 1; - return 0; - } - - dev_dbg(codec->dev, "0x%x = 0x%x\n", reg, value); - - ret = codec->hw_write(codec->control_data, data, 3); - if (ret == 3) - return 0; - if (ret < 0) - return ret; + if (codec_drv->reg_cache_default) + codec->reg_cache = kmemdup(codec_drv->reg_cache_default, + reg_size, GFP_KERNEL); else - return -EIO; + codec->reg_cache = kzalloc(reg_size, GFP_KERNEL); + if (!codec->reg_cache) + return -ENOMEM; + + return 0; } -#if defined(CONFIG_SPI_MASTER) -static int snd_soc_16_8_spi_write(void *control_data, const char *data, - int len) +/* + * NOTE: keep in mind that this function might be called + * multiple times. + */ +int snd_soc_cache_exit(struct snd_soc_codec *codec) { - struct spi_device *spi = control_data; - struct spi_transfer t; - struct spi_message m; - u8 msg[3]; - - if (len <= 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; + dev_dbg(codec->dev, "ASoC: Destroying cache for %s codec\n", + codec->name); - spi_message_add_tail(&t, &m); - spi_sync(spi, &m); - - return len; + kfree(codec->reg_cache); + codec->reg_cache = NULL; + return 0; } -#else -#define snd_soc_16_8_spi_write NULL -#endif -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) -static unsigned int snd_soc_16_16_read_i2c(struct snd_soc_codec *codec, - unsigned int r) +/** + * 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) { - struct i2c_msg xfer[2]; - u16 reg = cpu_to_be16(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 = 2; - xfer[0].buf = (u8 *)® + if (!value) + return -EINVAL; - /* Read data */ - xfer[1].addr = client->addr; - xfer[1].flags = I2C_M_RD; - xfer[1].len = 2; - xfer[1].buf = (u8 *)&data; + mutex_lock(&codec->cache_rw_mutex); + if (!ZERO_OR_NULL_PTR(codec->reg_cache)) + *value = snd_soc_get_cache_val(codec->reg_cache, reg, + codec->driver->reg_word_size); + mutex_unlock(&codec->cache_rw_mutex); - ret = i2c_transfer(client->adapter, xfer, 2); - if (ret != 2) { - dev_err(&client->dev, "i2c_transfer() returned %d\n", ret); - return 0; - } - - return be16_to_cpu(data); + return 0; } -#else -#define snd_soc_16_16_read_i2c NULL -#endif +EXPORT_SYMBOL_GPL(snd_soc_cache_read); -static unsigned int snd_soc_16_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; - - if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { - if (codec->cache_only) - return -1; - - return codec->hw_read(codec, reg); - } + mutex_lock(&codec->cache_rw_mutex); + if (!ZERO_OR_NULL_PTR(codec->reg_cache)) + snd_soc_set_cache_val(codec->reg_cache, reg, value, + codec->driver->reg_word_size); + mutex_unlock(&codec->cache_rw_mutex); - return cache[reg]; + return 0; } +EXPORT_SYMBOL_GPL(snd_soc_cache_write); -static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) +static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) { - u16 *cache = codec->reg_cache; - u8 data[4]; + int i; int ret; - - data[0] = (reg >> 8) & 0xff; - data[1] = reg & 0xff; - data[2] = (value >> 8) & 0xff; - data[3] = value & 0xff; - - if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) - cache[reg] = value; - - if (codec->cache_only) { - codec->cache_sync = 1; - return 0; + 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; + + ret = snd_soc_write(codec, i, val); + if (ret) + return ret; + dev_dbg(codec->dev, "ASoC: Synced register %#x, value = %#x\n", + i, val); } - - dev_dbg(codec->dev, "0x%x = 0x%x\n", reg, value); - - ret = codec->hw_write(codec->control_data, data, 4); - if (ret == 4) - return 0; - if (ret < 0) - return ret; - else - return -EIO; -} - -#if defined(CONFIG_SPI_MASTER) -static int snd_soc_16_16_spi_write(void *control_data, const char *data, - int len) -{ - struct spi_device *spi = control_data; - struct spi_transfer t; - struct spi_message m; - u8 msg[4]; - - if (len <= 0) - return 0; - - msg[0] = data[0]; - msg[1] = data[1]; - msg[2] = data[2]; - msg[3] = data[3]; - - 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 0; } -#else -#define snd_soc_16_16_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, - .i2c_read = snd_soc_8_8_read_i2c, - .spi_write = snd_soc_8_8_spi_write, - }, - { - .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, - .spi_write = snd_soc_8_16_spi_write, - }, - { - .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, - }, - { - .addr_bits = 16, .data_bits = 16, - .write = snd_soc_16_16_write, .read = snd_soc_16_16_read, - .i2c_read = snd_soc_16_16_read_i2c, - .spi_write = snd_soc_16_16_spi_write, - }, -}; /** - * snd_soc_codec_set_cache_io: Set up standard I/O functions. + * snd_soc_cache_sync: Sync the register cache with the hardware. * * @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. + * 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_codec_set_cache_io(struct snd_soc_codec *codec, - int addr_bits, int data_bits, - enum snd_soc_control_type control) +int snd_soc_cache_sync(struct snd_soc_codec *codec) { - int i; - - 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->driver->write = io_types[i].write; - codec->driver->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; - - codec->control_data = container_of(codec->dev, - struct i2c_client, - dev); - break; - - case SND_SOC_SPI: - if (io_types[i].spi_write) - codec->hw_write = io_types[i].spi_write; + const char *name = "flat"; + int ret; - codec->control_data = container_of(codec->dev, - struct spi_device, - dev); - break; - } + if (!codec->cache_sync) + return 0; - return 0; + dev_dbg(codec->dev, "ASoC: Syncing cache for %s codec\n", + codec->name); + trace_snd_soc_cache_sync(codec, name, "start"); + ret = snd_soc_flat_cache_sync(codec); + if (!ret) + codec->cache_sync = 0; + trace_snd_soc_cache_sync(codec, name, "end"); + return ret; } -EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io); +EXPORT_SYMBOL_GPL(snd_soc_cache_sync); |
