|
@@ -19,24 +19,205 @@
|
|
|
|
|
|
#include <trace/events/asoc.h>
|
|
|
|
|
|
-unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg)
|
|
|
+/**
|
|
|
+ * 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;
|
|
|
+
|
|
|
+ if (component->regmap)
|
|
|
+ ret = regmap_read(component->regmap, reg, val);
|
|
|
+ else if (component->read)
|
|
|
+ ret = component->read(component, reg, val);
|
|
|
+ else
|
|
|
+ ret = -EIO;
|
|
|
+
|
|
|
+ dev_dbg(component->dev, "read %x => %x\n", reg, *val);
|
|
|
+
|
|
|
+ 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;
|
|
|
+ dev_dbg(component->dev, "write %x = %x\n", reg, val);
|
|
|
|
|
|
- ret = codec->read(codec, reg);
|
|
|
- dev_dbg(codec->dev, "read %x => %x\n", reg, ret);
|
|
|
- trace_snd_soc_reg_read(codec, reg, 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 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;
|
|
|
}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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)
|
|
|
+{
|
|
|
+ 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_component_update_bits);
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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)
|
|
|
+{
|
|
|
+ bool change;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ 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_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;
|
|
|
+ trace_snd_soc_reg_read(codec, reg, val);
|
|
|
+
|
|
|
+ return val;
|
|
|
+}
|
|
|
EXPORT_SYMBOL_GPL(snd_soc_read);
|
|
|
|
|
|
int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg,
|
|
|
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);
|
|
|
+ return snd_soc_component_write(&codec->component, reg, val);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(snd_soc_write);
|
|
|
|
|
@@ -54,29 +235,8 @@ EXPORT_SYMBOL_GPL(snd_soc_write);
|
|
|
int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned int reg,
|
|
|
unsigned int mask, unsigned int value)
|
|
|
{
|
|
|
- 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 (ret < 0)
|
|
|
- return ret;
|
|
|
-
|
|
|
- return change;
|
|
|
+ return snd_soc_component_update_bits(&codec->component, reg, mask,
|
|
|
+ value);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(snd_soc_update_bits);
|
|
|
|
|
@@ -95,13 +255,8 @@ int snd_soc_update_bits_locked(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);
|
|
|
|
|
@@ -120,115 +275,58 @@ 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);
|
|
|
+ trace_snd_soc_preg_read(platform, reg, val);
|
|
|
|
|
|
- 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;
|
|
|
|
|
|
if (!regmap)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- /* Device has made its own regmap arrangements */
|
|
|
- codec->control_data = regmap;
|
|
|
-
|
|
|
- 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);
|