summaryrefslogtreecommitdiffstats
path: root/sound/soc/soc-cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/soc-cache.c')
-rw-r--r--sound/soc/soc-cache.c257
1 files changed, 252 insertions, 5 deletions
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c
index c8ceddc2a26c..5869dc3be781 100644
--- a/sound/soc/soc-cache.c
+++ b/sound/soc/soc-cache.c
@@ -15,6 +15,74 @@
#include <linux/spi/spi.h>
#include <sound/soc.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->reg_cache_size)
+ return -1;
+ 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;
+
+ BUG_ON(codec->volatile_register);
+
+ data[0] = (reg << 4) | ((value >> 8) & 0x000f);
+ data[1] = value & 0x00ff;
+
+ if (reg < codec->reg_cache_size)
+ cache[reg] = value;
+
+ if (codec->cache_only) {
+ codec->cache_sync = 1;
+ return 0;
+ }
+
+ 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)
{
@@ -38,6 +106,12 @@ static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg,
if (reg < codec->reg_cache_size)
cache[reg] = value;
+
+ if (codec->cache_only) {
+ codec->cache_sync = 1;
+ return 0;
+ }
+
ret = codec->hw_write(codec->control_data, data, 2);
if (ret == 2)
return 0;
@@ -77,6 +151,40 @@ static int snd_soc_7_9_spi_write(void *control_data, const char *data,
#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)
+{
+ u8 *cache = codec->reg_cache;
+ u8 data[2];
+
+ BUG_ON(codec->volatile_register);
+
+ data[0] = reg & 0xff;
+ data[1] = value & 0xff;
+
+ if (reg < codec->reg_cache_size)
+ cache[reg] = value;
+
+ if (codec->cache_only) {
+ codec->cache_sync = 1;
+ return 0;
+ }
+
+ 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;
+ if (reg >= codec->reg_cache_size)
+ return -1;
+ return cache[reg];
+}
+
static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
@@ -90,6 +198,11 @@ static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
if (!snd_soc_codec_volatile_register(codec, reg))
reg_cache[reg] = value;
+ if (codec->cache_only) {
+ codec->cache_sync = 1;
+ return 0;
+ }
+
if (codec->hw_write(codec->control_data, data, 3) == 3)
return 0;
else
@@ -102,10 +215,14 @@ static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec,
u16 *cache = codec->reg_cache;
if (reg >= codec->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg))
+ snd_soc_codec_volatile_register(codec, reg)) {
+ if (codec->cache_only)
+ return -EINVAL;
+
return codec->hw_read(codec, reg);
- else
+ } else {
return cache[reg];
+ }
}
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
@@ -142,6 +259,114 @@ static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec,
#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)
+{
+ 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);
+ 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)
+{
+ u16 *cache = codec->reg_cache;
+
+ reg &= 0xff;
+ if (reg >= codec->reg_cache_size)
+ return -1;
+ return cache[reg];
+}
+
+static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+ u8 data[3];
+ int ret;
+
+ BUG_ON(codec->volatile_register);
+
+ data[0] = (reg >> 8) & 0xff;
+ data[1] = reg & 0xff;
+ data[2] = value;
+
+ reg &= 0xff;
+ if (reg < codec->reg_cache_size)
+ cache[reg] = value;
+
+ if (codec->cache_only) {
+ codec->cache_sync = 1;
+ return 0;
+ }
+
+ ret = codec->hw_write(codec->control_data, data, 3);
+ if (ret == 3)
+ return 0;
+ if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+#if defined(CONFIG_SPI_MASTER)
+static int snd_soc_16_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[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_16_8_spi_write NULL
+#endif
+
+
static struct {
int addr_bits;
int data_bits;
@@ -150,9 +375,31 @@ static struct {
unsigned int (*read)(struct snd_soc_codec *, unsigned int);
unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int);
} io_types[] = {
- { 7, 9, snd_soc_7_9_write, snd_soc_7_9_spi_write, snd_soc_7_9_read },
- { 8, 16, snd_soc_8_16_write, NULL, snd_soc_8_16_read,
- snd_soc_8_16_read_i2c },
+ {
+ .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,
+ },
};
/**