]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - sound/soc/soc-io.c
Merge remote-tracking branches 'asoc/topic/adau' and 'asoc/topic/adsp' into asoc...
[karo-tx-linux.git] / sound / soc / soc-io.c
index 5fb85783b044eb5893c72f8a61e69b7d4c734844..7767fbd73eb7a464434c805a901fa94fe64a5e04 100644 (file)
 #include <linux/export.h>
 #include <sound/soc.h>
 
-#include <trace/events/asoc.h>
+/**
+ * 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)
+{
+       int ret;
 
-unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg)
+       if (component->regmap)
+               ret = regmap_read(component->regmap, reg, val);
+       else if (component->read)
+               ret = component->read(component, reg, val);
+       else
+               ret = -EIO;
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_read);
+
+/**
+ * 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)
 {
-       unsigned int 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);
 
-       ret = codec->read(codec, reg);
-       dev_dbg(codec->dev, "read %x => %x\n", reg, ret);
-       trace_snd_soc_reg_read(codec, reg, ret);
+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;
+
+       if (!component->read || !component->write)
+               return -EIO;
+
+       mutex_lock(&component->io_mutex);
+
+       ret = component->read(component, reg, &old);
+       if (ret < 0)
+               goto out_unlock;
+
+       new = (old & ~mask) | (val & mask);
+       *change = old != new;
+       if (*change)
+               ret = component->write(component, reg, new);
+out_unlock:
+       mutex_unlock(&component->io_mutex);
 
        return ret;
 }
-EXPORT_SYMBOL_GPL(snd_soc_read);
 
-int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg,
-       unsigned int val)
+/**
+ * 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)
 {
-       dev_dbg(codec->dev, "write %x = %x\n", reg, val);
-       trace_snd_soc_reg_write(codec, reg, val);
-       return codec->write(codec, reg, val);
+       bool change;
+       int ret;
+
+       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);
+
+       if (ret < 0)
+               return ret;
+       return change;
 }
-EXPORT_SYMBOL_GPL(snd_soc_write);
+EXPORT_SYMBOL_GPL(snd_soc_component_update_bits);
 
 /**
- * snd_soc_update_bits - update codec register bits
- * @codec: audio codec
- * @reg: codec register
- * @mask: register mask
- * @value: new 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
  *
- * Writes new register value.
+ * 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.
  *
- * Returns 1 for change, 0 for no change, or negative error code.
+ * 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_update_bits(struct snd_soc_codec *codec, unsigned int reg,
-                               unsigned int mask, unsigned int value)
+int snd_soc_component_update_bits_async(struct snd_soc_component *component,
+       unsigned int reg, unsigned int mask, unsigned int val)
 {
        bool change;
-       unsigned int old, new;
        int ret;
 
-       if (codec->using_regmap) {
-               ret = regmap_update_bits_check(codec->control_data, reg,
-                                              mask, value, &change);
-       } else {
-               ret = snd_soc_read(codec, reg);
-               if (ret < 0)
-                       return ret;
-
-               old = ret;
-               new = (old & ~mask) | (value & mask);
-               change = old != new;
-               if (change)
-                       ret = snd_soc_write(codec, reg, new);
-       }
+       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);
 
        if (ret < 0)
                return ret;
-
        return change;
 }
-EXPORT_SYMBOL_GPL(snd_soc_update_bits);
+EXPORT_SYMBOL_GPL(snd_soc_component_update_bits_async);
+
+/**
+ * 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)
+{
+       if (component->regmap)
+               regmap_async_complete(component->regmap);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_async_complete);
+
+/**
+ * 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)
+{
+       unsigned int old, new;
+       int ret;
+
+       ret = snd_soc_component_read(component, reg, &old);
+       if (ret < 0)
+               return ret;
+       new = (old & ~mask) | value;
+       return old != new;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_test_bits);
+
+unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg)
+{
+       unsigned int val;
+       int ret;
+
+       ret = snd_soc_component_read(&codec->component, reg, &val);
+       if (ret < 0)
+               return -1;
+
+       return val;
+}
+EXPORT_SYMBOL_GPL(snd_soc_read);
+
+int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg,
+       unsigned int val)
+{
+       return snd_soc_component_write(&codec->component, reg, val);
+}
+EXPORT_SYMBOL_GPL(snd_soc_write);
 
 /**
- * snd_soc_update_bits_locked - update codec register bits
+ * snd_soc_update_bits - update codec register bits
  * @codec: audio codec
  * @reg: codec register
  * @mask: register mask
  * @value: new value
  *
- * Writes new register value, and takes the codec mutex.
+ * Writes new register value.
  *
- * Returns 1 for change else 0.
+ * Returns 1 for change, 0 for no change, or negative error code.
  */
-int snd_soc_update_bits_locked(struct snd_soc_codec *codec,
-                              unsigned int reg, unsigned int mask,
-                              unsigned int value)
+int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned int reg,
+                               unsigned int mask, unsigned int value)
 {
-       int change;
-
-       mutex_lock(&codec->mutex);
-       change = snd_soc_update_bits(codec, reg, mask, value);
-       mutex_unlock(&codec->mutex);
-
-       return change;
+       return snd_soc_component_update_bits(&codec->component, reg, mask,
+               value);
 }
-EXPORT_SYMBOL_GPL(snd_soc_update_bits_locked);
+EXPORT_SYMBOL_GPL(snd_soc_update_bits);
 
 /**
  * snd_soc_test_bits - test register for change
@@ -120,118 +247,55 @@ EXPORT_SYMBOL_GPL(snd_soc_update_bits_locked);
 int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned int reg,
                                unsigned int mask, unsigned int value)
 {
-       int change;
-       unsigned int old, new;
-
-       old = snd_soc_read(codec, reg);
-       new = (old & ~mask) | value;
-       change = old != new;
-
-       return change;
+       return snd_soc_component_test_bits(&codec->component, reg, mask, value);
 }
 EXPORT_SYMBOL_GPL(snd_soc_test_bits);
 
 int snd_soc_platform_read(struct snd_soc_platform *platform,
                                        unsigned int reg)
 {
-       unsigned int ret;
+       unsigned int val;
+       int ret;
 
-       if (!platform->driver->read) {
-               dev_err(platform->dev, "ASoC: platform has no read back\n");
+       ret = snd_soc_component_read(&platform->component, reg, &val);
+       if (ret < 0)
                return -1;
-       }
 
-       ret = platform->driver->read(platform, reg);
-       dev_dbg(platform->dev, "read %x => %x\n", reg, ret);
-       trace_snd_soc_preg_read(platform, reg, ret);
-
-       return ret;
+       return val;
 }
 EXPORT_SYMBOL_GPL(snd_soc_platform_read);
 
 int snd_soc_platform_write(struct snd_soc_platform *platform,
                                         unsigned int reg, unsigned int val)
 {
-       if (!platform->driver->write) {
-               dev_err(platform->dev, "ASoC: platform has no write back\n");
-               return -1;
-       }
-
-       dev_dbg(platform->dev, "write %x = %x\n", reg, val);
-       trace_snd_soc_preg_write(platform, reg, val);
-       return platform->driver->write(platform, reg, val);
+       return snd_soc_component_write(&platform->component, reg, val);
 }
 EXPORT_SYMBOL_GPL(snd_soc_platform_write);
 
-#ifdef CONFIG_REGMAP
-static int hw_write(struct snd_soc_codec *codec, unsigned int reg,
-                   unsigned int value)
-{
-       return regmap_write(codec->control_data, reg, value);
-}
-
-static unsigned int hw_read(struct snd_soc_codec *codec, unsigned int reg)
-{
-       int ret;
-       unsigned int val;
-
-       ret = regmap_read(codec->control_data, reg, &val);
-       if (ret == 0)
-               return val;
-       else
-               return -1;
-}
-
 /**
- * snd_soc_codec_set_cache_io: Set up standard I/O functions.
- *
- * @codec: CODEC to configure.
- * @map: Register map to write to
+ * snd_soc_component_init_io() - Initialize regmap IO
  *
- * 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.
+ * @component: component to initialize
+ * @regmap: regmap instance to use for IO operations
  *
- * 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.
+ * Return: 0 on success, a negative error code otherwise
  */
-int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
-                              struct regmap *regmap)
+int snd_soc_component_init_io(struct snd_soc_component *component,
+       struct regmap *regmap)
 {
        int ret;
 
-       /* Device has made its own regmap arrangements */
        if (!regmap)
-               codec->control_data = dev_get_regmap(codec->dev, NULL);
-       else
-               codec->control_data = regmap;
+               return -EINVAL;
 
-       if (IS_ERR(codec->control_data))
-               return PTR_ERR(codec->control_data);
-
-       codec->write = hw_write;
-       codec->read = hw_read;
-
-       ret = regmap_get_val_bytes(codec->control_data);
+       ret = regmap_get_val_bytes(regmap);
        /* Errors are legitimate for non-integer byte
         * multiples */
        if (ret > 0)
-               codec->val_bytes = ret;
+               component->val_bytes = ret;
 
-       codec->using_regmap = true;
+       component->regmap = regmap;
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);
-#else
-int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
-                              struct regmap *regmap)
-{
-       return -ENOTSUPP;
-}
-EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);
-#endif
+EXPORT_SYMBOL_GPL(snd_soc_component_init_io);