diff options
Diffstat (limited to 'sound/soc/soc-io.c')
| -rw-r--r-- | sound/soc/soc-io.c | 517 |
1 files changed, 211 insertions, 306 deletions
diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c index cca490c8058..7767fbd73eb 100644 --- a/sound/soc/soc-io.c +++ b/sound/soc/soc-io.c @@ -13,384 +13,289 @@ #include <linux/i2c.h> #include <linux/spi/spi.h> +#include <linux/regmap.h> +#include <linux/export.h> #include <sound/soc.h> -#include <trace/events/asoc.h> - -#ifdef CONFIG_SPI_MASTER -static int do_spi_write(void *control, const char *data, int len) +/** + * snd_soc_component_read() - Read register value + * @component: Component to read from + * @reg: Register to read + * @val: Pointer to where the read value is stored + * + * Return: 0 on success, a negative error code otherwise. + */ +int snd_soc_component_read(struct snd_soc_component *component, + unsigned int reg, unsigned int *val) { - struct spi_device *spi = control; int ret; - ret = spi_write(spi, data, len); - if (ret < 0) - return ret; + if (component->regmap) + ret = regmap_read(component->regmap, reg, val); + else if (component->read) + ret = component->read(component, reg, val); + else + ret = -EIO; - return len; + return ret; } -#endif +EXPORT_SYMBOL_GPL(snd_soc_component_read); -static int do_hw_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value, const void *data, int len) +/** + * snd_soc_component_write() - Write register value + * @component: Component to write to + * @reg: Register to write + * @val: Value to write to the register + * + * Return: 0 on success, a negative error code otherwise. + */ +int snd_soc_component_write(struct snd_soc_component *component, + unsigned int reg, unsigned int val) { - int ret; - - if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size && - !codec->cache_bypass) { - ret = snd_soc_cache_write(codec, reg, value); - if (ret < 0) - return -1; - } - - if (codec->cache_only) { - codec->cache_sync = 1; - return 0; - } - - ret = codec->hw_write(codec->control_data, data, len); - if (ret == len) - return 0; - if (ret < 0) - return ret; + if (component->regmap) + return regmap_write(component->regmap, reg, val); + else if (component->write) + return component->write(component, reg, val); else return -EIO; } +EXPORT_SYMBOL_GPL(snd_soc_component_write); -static unsigned int hw_read(struct snd_soc_codec *codec, unsigned int reg) +static int snd_soc_component_update_bits_legacy( + struct snd_soc_component *component, unsigned int reg, + unsigned int mask, unsigned int val, bool *change) { + unsigned int old, new; int ret; - unsigned int val; - if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg) || - codec->cache_bypass) { - if (codec->cache_only) - return -1; + if (!component->read || !component->write) + return -EIO; - BUG_ON(!codec->hw_read); - return codec->hw_read(codec, reg); - } + mutex_lock(&component->io_mutex); - ret = snd_soc_cache_read(codec, reg, &val); + ret = component->read(component, reg, &old); if (ret < 0) - return -1; - return val; -} - -static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u16 data; + goto out_unlock; - data = cpu_to_be16((reg << 12) | (value & 0xffffff)); + new = (old & ~mask) | (val & mask); + *change = old != new; + if (*change) + ret = component->write(component, reg, new); +out_unlock: + mutex_unlock(&component->io_mutex); - return do_hw_write(codec, reg, value, &data, 2); + return ret; } -static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) +/** + * snd_soc_component_update_bits() - Perform read/modify/write cycle + * @component: Component to update + * @reg: Register to update + * @mask: Mask that specifies which bits to update + * @val: New value for the bits specified by mask + * + * Return: 1 if the operation was successful and the value of the register + * changed, 0 if the operation was successful, but the value did not change. + * Returns a negative error code otherwise. + */ +int snd_soc_component_update_bits(struct snd_soc_component *component, + unsigned int reg, unsigned int mask, unsigned int val) { - u16 data; + bool change; + int ret; - data = cpu_to_be16((reg << 9) | (value & 0x1ff)); + if (component->regmap) + ret = regmap_update_bits_check(component->regmap, reg, mask, + val, &change); + else + ret = snd_soc_component_update_bits_legacy(component, reg, + mask, val, &change); - return do_hw_write(codec, reg, value, &data, 2); + if (ret < 0) + return ret; + return change; } +EXPORT_SYMBOL_GPL(snd_soc_component_update_bits); -static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) +/** + * snd_soc_component_update_bits_async() - Perform asynchronous + * read/modify/write cycle + * @component: Component to update + * @reg: Register to update + * @mask: Mask that specifies which bits to update + * @val: New value for the bits specified by mask + * + * This function is similar to snd_soc_component_update_bits(), but the update + * operation is scheduled asynchronously. This means it may not be completed + * when the function returns. To make sure that all scheduled updates have been + * completed snd_soc_component_async_complete() must be called. + * + * Return: 1 if the operation was successful and the value of the register + * changed, 0 if the operation was successful, but the value did not change. + * Returns a negative error code otherwise. + */ +int snd_soc_component_update_bits_async(struct snd_soc_component *component, + unsigned int reg, unsigned int mask, unsigned int val) { - u8 data[2]; + bool change; + int ret; - reg &= 0xff; - data[0] = reg; - data[1] = value & 0xff; + if (component->regmap) + ret = regmap_update_bits_check_async(component->regmap, reg, + mask, val, &change); + else + ret = snd_soc_component_update_bits_legacy(component, reg, + mask, val, &change); - return do_hw_write(codec, reg, value, data, 2); + if (ret < 0) + return ret; + return change; } +EXPORT_SYMBOL_GPL(snd_soc_component_update_bits_async); -static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) +/** + * snd_soc_component_async_complete() - Ensure asynchronous I/O has completed + * @component: Component for which to wait + * + * This function blocks until all asynchronous I/O which has previously been + * scheduled using snd_soc_component_update_bits_async() has completed. + */ +void snd_soc_component_async_complete(struct snd_soc_component *component) { - u8 data[3]; - u16 val = cpu_to_be16(value); - - data[0] = reg; - memcpy(&data[1], &val, sizeof(val)); - - return do_hw_write(codec, reg, value, data, 3); + if (component->regmap) + regmap_async_complete(component->regmap); } +EXPORT_SYMBOL_GPL(snd_soc_component_async_complete); -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) -static unsigned int do_i2c_read(struct snd_soc_codec *codec, - void *reg, int reglen, - void *data, int datalen) +/** + * snd_soc_component_test_bits - Test register for change + * @component: component + * @reg: Register to test + * @mask: Mask that specifies which bits to test + * @value: Value to test against + * + * Tests a register with a new value and checks if the new value is + * different from the old value. + * + * Return: 1 for change, otherwise 0. + */ +int snd_soc_component_test_bits(struct snd_soc_component *component, + unsigned int reg, unsigned int mask, unsigned int value) { - struct i2c_msg xfer[2]; + unsigned int old, new; int ret; - struct i2c_client *client = codec->control_data; - - /* Write register */ - xfer[0].addr = client->addr; - xfer[0].flags = 0; - xfer[0].len = reglen; - xfer[0].buf = reg; - - /* Read data */ - xfer[1].addr = client->addr; - xfer[1].flags = I2C_M_RD; - xfer[1].len = datalen; - xfer[1].buf = data; - - ret = i2c_transfer(client->adapter, xfer, 2); - if (ret == 2) - return 0; - else if (ret < 0) + + ret = snd_soc_component_read(component, reg, &old); + if (ret < 0) return ret; - else - return -EIO; + new = (old & ~mask) | value; + return old != new; } -#endif +EXPORT_SYMBOL_GPL(snd_soc_component_test_bits); -#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) +unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg) { - u8 reg = r; - u8 data; + unsigned int val; int ret; - ret = do_i2c_read(codec, ®, 1, &data, 1); + ret = snd_soc_component_read(&codec->component, reg, &val); if (ret < 0) - return 0; - return data; + return -1; + + return val; } -#else -#define snd_soc_8_8_read_i2c NULL -#endif +EXPORT_SYMBOL_GPL(snd_soc_read); -#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) +int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int val) { - u8 reg = r; - u16 data; - int ret; - - ret = do_i2c_read(codec, ®, 1, &data, 2); - if (ret < 0) - return 0; - return (data >> 8) | ((data & 0xff) << 8); + return snd_soc_component_write(&codec->component, reg, val); } -#else -#define snd_soc_8_16_read_i2c NULL -#endif +EXPORT_SYMBOL_GPL(snd_soc_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_update_bits - update codec register bits + * @codec: audio codec + * @reg: codec register + * @mask: register mask + * @value: new value + * + * Writes new register value. + * + * Returns 1 for change, 0 for no change, or negative error code. + */ +int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned int reg, + unsigned int mask, unsigned int value) { - u16 reg = r; - u8 data; - int ret; - - ret = do_i2c_read(codec, ®, 2, &data, 1); - if (ret < 0) - return 0; - return data; + return snd_soc_component_update_bits(&codec->component, reg, mask, + value); } -#else -#define snd_soc_16_8_read_i2c NULL -#endif +EXPORT_SYMBOL_GPL(snd_soc_update_bits); -static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) +/** + * snd_soc_test_bits - test register for change + * @codec: audio codec + * @reg: codec register + * @mask: register mask + * @value: new value + * + * Tests a register with a new value and checks if the new value is + * different from the old value. + * + * Returns 1 for change else 0. + */ +int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned int reg, + unsigned int mask, unsigned int value) { - u8 data[3]; - u16 rval = cpu_to_be16(reg); - - memcpy(data, &rval, sizeof(rval)); - data[2] = value; - - return do_hw_write(codec, reg, value, data, 3); + return snd_soc_component_test_bits(&codec->component, reg, mask, value); } +EXPORT_SYMBOL_GPL(snd_soc_test_bits); -#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) +int snd_soc_platform_read(struct snd_soc_platform *platform, + unsigned int reg) { - u16 reg = cpu_to_be16(r); - u16 data; + unsigned int val; int ret; - ret = do_i2c_read(codec, ®, 2, &data, 2); + ret = snd_soc_component_read(&platform->component, reg, &val); if (ret < 0) - return 0; - return be16_to_cpu(data); -} -#else -#define snd_soc_16_16_read_i2c NULL -#endif - -static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u16 data[2]; - - data[0] = cpu_to_be16(reg); - data[1] = cpu_to_be16(value); + return -1; - return do_hw_write(codec, reg, value, data, sizeof(data)); + return val; } +EXPORT_SYMBOL_GPL(snd_soc_platform_read); -/* Primitive bulk write support for soc-cache. The data pointed to by - * `data' needs to already be in the form the hardware expects - * including any leading register specific data. Any data written - * through this function will not go through the cache as it only - * handles writing to volatile or out of bounds registers. - */ -static int snd_soc_hw_bulk_write_raw(struct snd_soc_codec *codec, unsigned int reg, - const void *data, size_t len) +int snd_soc_platform_write(struct snd_soc_platform *platform, + unsigned int reg, unsigned int val) { - int ret; - - /* To ensure that we don't get out of sync with the cache, check - * whether the base register is volatile or if we've directly asked - * to bypass the cache. Out of bounds registers are considered - * volatile. - */ - if (!codec->cache_bypass - && !snd_soc_codec_volatile_register(codec, reg) - && reg < codec->driver->reg_cache_size) - return -EINVAL; - - switch (codec->control_type) { -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) - case SND_SOC_I2C: - ret = i2c_master_send(to_i2c_client(codec->dev), data, len); - break; -#endif -#if defined(CONFIG_SPI_MASTER) - case SND_SOC_SPI: - ret = spi_write(to_spi_device(codec->dev), data, len); - break; -#endif - default: - BUG(); - } - - if (ret == len) - return 0; - if (ret < 0) - return ret; - else - return -EIO; + return snd_soc_component_write(&platform->component, reg, val); } - -static struct { - int addr_bits; - int data_bits; - int (*write)(struct snd_soc_codec *codec, unsigned int, unsigned 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, - }, - { - .addr_bits = 7, .data_bits = 9, - .write = snd_soc_7_9_write, - }, - { - .addr_bits = 8, .data_bits = 8, - .write = snd_soc_8_8_write, - .i2c_read = snd_soc_8_8_read_i2c, - }, - { - .addr_bits = 8, .data_bits = 16, - .write = snd_soc_8_16_write, - .i2c_read = snd_soc_8_16_read_i2c, - }, - { - .addr_bits = 16, .data_bits = 8, - .write = snd_soc_16_8_write, - .i2c_read = snd_soc_16_8_read_i2c, - }, - { - .addr_bits = 16, .data_bits = 16, - .write = snd_soc_16_16_write, - .i2c_read = snd_soc_16_16_read_i2c, - }, -}; +EXPORT_SYMBOL_GPL(snd_soc_platform_write); /** - * snd_soc_codec_set_cache_io: Set up standard I/O functions. - * - * @codec: CODEC to configure. - * @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. + * snd_soc_component_init_io() - Initialize regmap IO * - * The caller is responsible for allocating and initialising the - * actual cache. + * @component: component to initialize + * @regmap: regmap instance to use for IO operations * - * Note that at present this code cannot be used by CODECs with - * volatile registers. + * Return: 0 on success, a negative error code otherwise */ -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_component_init_io(struct snd_soc_component *component, + struct regmap *regmap) { - 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); + int ret; + + if (!regmap) return -EINVAL; - } - - codec->write = io_types[i].write; - codec->read = hw_read; - codec->bulk_write_raw = snd_soc_hw_bulk_write_raw; - - switch (control) { - 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: -#ifdef CONFIG_SPI_MASTER - codec->hw_write = do_spi_write; -#endif - - codec->control_data = container_of(codec->dev, - struct spi_device, - dev); - break; - } + + ret = regmap_get_val_bytes(regmap); + /* Errors are legitimate for non-integer byte + * multiples */ + if (ret > 0) + component->val_bytes = ret; + + component->regmap = regmap; return 0; } -EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io); - +EXPORT_SYMBOL_GPL(snd_soc_component_init_io); |
