From 17a52fd60a0a0e617ed94aadb1b19751a8fa219e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 5 Jul 2009 17:24:50 +0100 Subject: ASoC: Begin to factor out register cache I/O functions A lot of CODECs share the same register data formats and therefore replicate the code to manage access to and caching of the register map. In order to reduce code duplication centralised versions of this code will be introduced with drivers able to configure the use of the common code by calling the new snd_soc_codec_set_cache_io() API call during startup. As an initial user the 7 bit address/9 bit data format used by many Wolfson devices is supported for write only CODECs and the drivers with straightforward register cache implementations are converted to use it. Signed-off-by: Mark Brown --- include/sound/soc.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/sound') diff --git a/include/sound/soc.h b/include/sound/soc.h index 94fcc65609b6..27409dd41ae9 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -192,6 +192,8 @@ void snd_soc_unregister_platform(struct snd_soc_platform *platform); int snd_soc_register_codec(struct snd_soc_codec *codec); void snd_soc_unregister_codec(struct snd_soc_codec *codec); int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg); +int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, + int addr_bits, int data_bits); #ifdef CONFIG_PM int snd_soc_suspend_device(struct device *dev); -- cgit v1.2.3 From 7084a42b965d972079201414d19a399e65b26099 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 10 Jul 2009 22:24:27 +0100 Subject: ASoC: Add I/O control bus information to factored out cache setup While writes tend to be able to use a fairly bus independant format to do the writes reads are all bus specific. To allow us to factor out this code include the bus type as a parameter when setting up the cache. Initially just use this to factor out hw_write_t for I2C. Signed-off-by: Mark Brown --- include/sound/soc.h | 9 ++++++++- sound/soc/codecs/wm8510.c | 10 +++++----- sound/soc/codecs/wm8580.c | 8 ++++---- sound/soc/codecs/wm8728.c | 10 +++++----- sound/soc/codecs/wm8731.c | 10 +++++----- sound/soc/codecs/wm8750.c | 10 +++++----- sound/soc/codecs/wm8960.c | 8 ++++---- sound/soc/codecs/wm8971.c | 8 ++++---- sound/soc/codecs/wm8988.c | 10 +++++----- sound/soc/soc-cache.c | 19 ++++++++++++++++++- 10 files changed, 63 insertions(+), 39 deletions(-) (limited to 'include/sound') diff --git a/include/sound/soc.h b/include/sound/soc.h index 27409dd41ae9..d0b29a509bdd 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -187,13 +187,20 @@ typedef int (*hw_read_t)(void *,char* ,int); extern struct snd_ac97_bus_ops soc_ac97_ops; +enum snd_soc_control_type { + SND_SOC_CUSTOM, + SND_SOC_I2C, + SND_SOC_SPI, +}; + int snd_soc_register_platform(struct snd_soc_platform *platform); void snd_soc_unregister_platform(struct snd_soc_platform *platform); int snd_soc_register_codec(struct snd_soc_codec *codec); void snd_soc_unregister_codec(struct snd_soc_codec *codec); int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg); int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, - int addr_bits, int data_bits); + int addr_bits, int data_bits, + enum snd_soc_control_type control); #ifdef CONFIG_PM int snd_soc_suspend_device(struct device *dev); diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index 7a169bff86f9..4ca724ff4c6e 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -564,7 +564,8 @@ static int wm8510_resume(struct platform_device *pdev) * initialise the WM8510 driver * register the mixer and dsp interfaces with the kernel */ -static int wm8510_init(struct snd_soc_device *socdev) +static int wm8510_init(struct snd_soc_device *socdev, + enum snd_soc_control_type control) { struct snd_soc_codec *codec = socdev->card->codec; int ret = 0; @@ -580,7 +581,7 @@ static int wm8510_init(struct snd_soc_device *socdev) if (codec->reg_cache == NULL) return -ENOMEM; - ret = snd_soc_codec_set_cache_io(codec, 7, 9); + ret = snd_soc_codec_set_cache_io(codec, 7, 9, control); if (ret < 0) { printk(KERN_ERR "wm8510: failed to set cache I/O: %d\n", ret); @@ -635,7 +636,7 @@ static int wm8510_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, codec); codec->control_data = i2c; - ret = wm8510_init(socdev); + ret = wm8510_init(socdev, SND_SOC_I2C); if (ret < 0) pr_err("failed to initialise WM8510\n"); @@ -715,7 +716,7 @@ static int __devinit wm8510_spi_probe(struct spi_device *spi) codec->control_data = spi; - ret = wm8510_init(socdev); + ret = wm8510_init(socdev, SND_SOC_SPI); if (ret < 0) dev_err(&spi->dev, "failed to initialise WM8510\n"); @@ -784,7 +785,6 @@ static int wm8510_probe(struct platform_device *pdev) wm8510_socdev = socdev; #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) if (setup->i2c_address) { - codec->hw_write = (hw_write_t)i2c_master_send; ret = wm8510_add_i2c_device(pdev, setup); } #endif diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index 2250adea749f..d5473473a1e3 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -832,7 +832,8 @@ struct snd_soc_codec_device soc_codec_dev_wm8580 = { }; EXPORT_SYMBOL_GPL(soc_codec_dev_wm8580); -static int wm8580_register(struct wm8580_priv *wm8580) +static int wm8580_register(struct wm8580_priv *wm8580, + enum snd_soc_control_type control) { int ret, i; struct snd_soc_codec *codec = &wm8580->codec; @@ -859,7 +860,7 @@ static int wm8580_register(struct wm8580_priv *wm8580) memcpy(codec->reg_cache, wm8580_reg, sizeof(wm8580_reg)); - ret = snd_soc_codec_set_cache_io(codec, 7, 9); + ret = snd_soc_codec_set_cache_io(codec, 7, 9, control); if (ret < 0) { dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); goto err; @@ -944,14 +945,13 @@ static int wm8580_i2c_probe(struct i2c_client *i2c, return -ENOMEM; codec = &wm8580->codec; - codec->hw_write = (hw_write_t)i2c_master_send; i2c_set_clientdata(i2c, wm8580); codec->control_data = i2c; codec->dev = &i2c->dev; - return wm8580_register(wm8580); + return wm8580_register(wm8580, SND_SOC_I2C); } static int wm8580_i2c_remove(struct i2c_client *client) diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c index 66da44b08d35..a28630de9c91 100644 --- a/sound/soc/codecs/wm8728.c +++ b/sound/soc/codecs/wm8728.c @@ -248,7 +248,8 @@ static int wm8728_resume(struct platform_device *pdev) * initialise the WM8728 driver * register the mixer and dsp interfaces with the kernel */ -static int wm8728_init(struct snd_soc_device *socdev) +static int wm8728_init(struct snd_soc_device *socdev, + enum snd_soc_control_type control) { struct snd_soc_codec *codec = socdev->card->codec; int ret = 0; @@ -266,7 +267,7 @@ static int wm8728_init(struct snd_soc_device *socdev) if (codec->reg_cache == NULL) return -ENOMEM; - ret = snd_soc_codec_set_cache_io(codec, 7, 9); + ret = snd_soc_codec_set_cache_io(codec, 7, 9, control); if (ret < 0) { printk(KERN_ERR "wm8728: failed to configure cache I/O: %d\n", ret); @@ -323,7 +324,7 @@ static int wm8728_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, codec); codec->control_data = i2c; - ret = wm8728_init(socdev); + ret = wm8728_init(socdev, SND_SOC_I2C); if (ret < 0) pr_err("failed to initialise WM8728\n"); @@ -403,7 +404,7 @@ static int __devinit wm8728_spi_probe(struct spi_device *spi) codec->control_data = spi; - ret = wm8728_init(socdev); + ret = wm8728_init(socdev, SND_SOC_SPI); if (ret < 0) dev_err(&spi->dev, "failed to initialise WM8728\n"); @@ -472,7 +473,6 @@ static int wm8728_probe(struct platform_device *pdev) #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) if (setup->i2c_address) { - codec->hw_write = (hw_write_t)i2c_master_send; ret = wm8728_add_i2c_device(pdev, setup); } #endif diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 4eb84ff691b3..27fc942a5ced 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -504,7 +504,8 @@ struct snd_soc_codec_device soc_codec_dev_wm8731 = { }; EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731); -static int wm8731_register(struct wm8731_priv *wm8731) +static int wm8731_register(struct wm8731_priv *wm8731, + enum snd_soc_control_type control) { int ret; struct snd_soc_codec *codec = &wm8731->codec; @@ -531,7 +532,7 @@ static int wm8731_register(struct wm8731_priv *wm8731) memcpy(codec->reg_cache, wm8731_reg, sizeof(wm8731_reg)); - ret = snd_soc_codec_set_cache_io(codec, 7, 9); + ret = snd_soc_codec_set_cache_io(codec, 7, 9, control); if (ret < 0) { dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); goto err; @@ -630,7 +631,7 @@ static int __devinit wm8731_spi_probe(struct spi_device *spi) dev_set_drvdata(&spi->dev, wm8731); - return wm8731_register(wm8731); + return wm8731_register(wm8731, SND_SOC_SPI); } static int __devexit wm8731_spi_remove(struct spi_device *spi) @@ -682,14 +683,13 @@ static __devinit int wm8731_i2c_probe(struct i2c_client *i2c, return -ENOMEM; codec = &wm8731->codec; - codec->hw_write = (hw_write_t)i2c_master_send; i2c_set_clientdata(i2c, wm8731); codec->control_data = i2c; codec->dev = &i2c->dev; - return wm8731_register(wm8731); + return wm8731_register(wm8731, SND_SOC_I2C); } static __devexit int wm8731_i2c_remove(struct i2c_client *client) diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index ed09043181e1..e6422b161678 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -711,7 +711,8 @@ static int wm8750_resume(struct platform_device *pdev) * initialise the WM8750 driver * register the mixer and dsp interfaces with the kernel */ -static int wm8750_init(struct snd_soc_device *socdev) +static int wm8750_init(struct snd_soc_device *socdev, + enum snd_soc_control_type control) { struct snd_soc_codec *codec = socdev->card->codec; int reg, ret = 0; @@ -726,7 +727,7 @@ static int wm8750_init(struct snd_soc_device *socdev) if (codec->reg_cache == NULL) return -ENOMEM; - ret = snd_soc_codec_set_cache_io(codec, 7, 9); + ret = snd_soc_codec_set_cache_io(codec, 7, 9, control); if (ret < 0) { printk(KERN_ERR "wm8750: failed to set cache I/O: %d\n", ret); goto err; @@ -809,7 +810,7 @@ static int wm8750_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, codec); codec->control_data = i2c; - ret = wm8750_init(socdev); + ret = wm8750_init(socdev, SND_SOC_I2C); if (ret < 0) pr_err("failed to initialise WM8750\n"); @@ -889,7 +890,7 @@ static int __devinit wm8750_spi_probe(struct spi_device *spi) codec->control_data = spi; - ret = wm8750_init(socdev); + ret = wm8750_init(socdev, SND_SOC_SPI); if (ret < 0) dev_err(&spi->dev, "failed to initialise WM8750\n"); @@ -967,7 +968,6 @@ static int wm8750_probe(struct platform_device *pdev) #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) if (setup->i2c_address) { - codec->hw_write = (hw_write_t)i2c_master_send; ret = wm8750_add_i2c_device(pdev, setup); } #endif diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index c529ffcfe5d8..f59703be61c8 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -747,7 +747,8 @@ struct snd_soc_codec_device soc_codec_dev_wm8960 = { }; EXPORT_SYMBOL_GPL(soc_codec_dev_wm8960); -static int wm8960_register(struct wm8960_priv *wm8960) +static int wm8960_register(struct wm8960_priv *wm8960, + enum snd_soc_control_type control) { struct wm8960_data *pdata = wm8960->codec.dev->platform_data; struct snd_soc_codec *codec = &wm8960->codec; @@ -785,7 +786,7 @@ static int wm8960_register(struct wm8960_priv *wm8960) memcpy(codec->reg_cache, wm8960_reg, sizeof(wm8960_reg)); - ret = snd_soc_codec_set_cache_io(codec, 7, 9); + ret = snd_soc_codec_set_cache_io(codec, 7, 9, control); if (ret < 0) { dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); goto err; @@ -866,14 +867,13 @@ static __devinit int wm8960_i2c_probe(struct i2c_client *i2c, return -ENOMEM; codec = &wm8960->codec; - codec->hw_write = (hw_write_t)i2c_master_send; i2c_set_clientdata(i2c, wm8960); codec->control_data = i2c; codec->dev = &i2c->dev; - return wm8960_register(wm8960); + return wm8960_register(wm8960, SND_SOC_I2C); } static __devexit int wm8960_i2c_remove(struct i2c_client *client) diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index 53f3bc90ea0c..d66efb0546ea 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c @@ -640,7 +640,8 @@ static int wm8971_resume(struct platform_device *pdev) return 0; } -static int wm8971_init(struct snd_soc_device *socdev) +static int wm8971_init(struct snd_soc_device *socdev, + enum snd_soc_control_type control) { struct snd_soc_codec *codec = socdev->card->codec; int reg, ret = 0; @@ -656,7 +657,7 @@ static int wm8971_init(struct snd_soc_device *socdev) if (codec->reg_cache == NULL) return -ENOMEM; - ret = snd_soc_codec_set_cache_io(codec, 7, 9); + ret = snd_soc_codec_set_cache_io(codec, 7, 9, control); if (ret < 0) { printk(KERN_ERR "wm8971: failed to set cache I/O: %d\n", ret); goto err; @@ -734,7 +735,7 @@ static int wm8971_i2c_probe(struct i2c_client *i2c, codec->control_data = i2c; - ret = wm8971_init(socdev); + ret = wm8971_init(socdev, SND_SOC_I2C); if (ret < 0) pr_err("failed to initialise WM8971\n"); @@ -844,7 +845,6 @@ static int wm8971_probe(struct platform_device *pdev) #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) if (setup->i2c_address) { - codec->hw_write = (hw_write_t)i2c_master_send; ret = wm8971_add_i2c_device(pdev, setup); } #endif diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index 7d5b807bdb48..7486d3ec787e 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -825,7 +825,8 @@ struct snd_soc_codec_device soc_codec_dev_wm8988 = { }; EXPORT_SYMBOL_GPL(soc_codec_dev_wm8988); -static int wm8988_register(struct wm8988_priv *wm8988) +static int wm8988_register(struct wm8988_priv *wm8988, + enum snd_soc_control_type control) { struct snd_soc_codec *codec = &wm8988->codec; int ret; @@ -854,7 +855,7 @@ static int wm8988_register(struct wm8988_priv *wm8988) memcpy(codec->reg_cache, wm8988_reg, sizeof(wm8988_reg)); - ret = snd_soc_codec_set_cache_io(codec, 7, 9); + ret = snd_soc_codec_set_cache_io(codec, 7, 9, control); if (ret < 0) { dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); goto err; @@ -927,14 +928,13 @@ static int wm8988_i2c_probe(struct i2c_client *i2c, return -ENOMEM; codec = &wm8988->codec; - codec->hw_write = (hw_write_t)i2c_master_send; i2c_set_clientdata(i2c, wm8988); codec->control_data = i2c; codec->dev = &i2c->dev; - return wm8988_register(wm8988); + return wm8988_register(wm8988, SND_SOC_I2C); } static int wm8988_i2c_remove(struct i2c_client *client) @@ -1019,7 +1019,7 @@ static int __devinit wm8988_spi_probe(struct spi_device *spi) spi->dev.driver_data = wm8988; - return wm8988_register(wm8988); + return wm8988_register(wm8988, SND_SOC_SPI); } static int __devexit wm8988_spi_remove(struct spi_device *spi) diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 4eb4333a0efb..8b126682c843 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -11,6 +11,7 @@ * option) any later version. */ +#include #include static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec, @@ -62,6 +63,7 @@ static struct { * @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 @@ -75,7 +77,8 @@ static struct { * volatile registers. */ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, - int addr_bits, int data_bits) + int addr_bits, int data_bits, + enum snd_soc_control_type control) { int i; @@ -100,6 +103,20 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, codec->write = io_types[i].write; codec->read = io_types[i].read; + switch (control) { + case SND_SOC_CUSTOM: + break; + + case SND_SOC_I2C: +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + codec->hw_write = (hw_write_t)i2c_master_send; +#endif + break; + + case SND_SOC_SPI: + break; + } + return 0; } EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io); -- cgit v1.2.3 From afa2f1066e7288a9e4f8e3fda277da245219dffc Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 10 Jul 2009 23:11:24 +0100 Subject: ASoC: Factor out I2C 8 bit address 16 bit data I/O As part of this refactoring the type of the CODEC hw_read operation is changed to match the regular read operation. Signed-off-by: Mark Brown --- include/sound/soc.h | 2 +- sound/soc/soc-cache.c | 79 +++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 72 insertions(+), 9 deletions(-) (limited to 'include/sound') diff --git a/include/sound/soc.h b/include/sound/soc.h index d0b29a509bdd..4a5846e72473 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -373,7 +373,7 @@ struct snd_soc_codec { size_t, unsigned int); int (*volatile_register)(unsigned int); hw_write_t hw_write; - hw_read_t hw_read; + unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int); void *reg_cache; short reg_cache_size; short reg_cache_step; diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 8b126682c843..ab058b811633 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -46,14 +46,82 @@ static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg, return -EIO; } +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_cache[reg] = value; + + if (codec->hw_write(codec->control_data, data, 3) == 3) + return 0; + else + return -EIO; +} + +static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + u16 *cache = codec->reg_cache; + + if (reg >= codec->reg_cache_size || + snd_soc_codec_volatile_register(codec, reg)) + return codec->hw_read(codec, reg); + else + return cache[reg]; +} + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_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; + } + + return (data >> 8) | ((data & 0xff) << 8); +} +#else +#define snd_soc_8_16_read_i2c NULL +#endif static struct { int addr_bits; int data_bits; - int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); + 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[] = { { 7, 9, snd_soc_7_9_write, snd_soc_7_9_read }, + { 8, 16, + snd_soc_8_16_write, snd_soc_8_16_read, + snd_soc_8_16_read_i2c }, }; /** @@ -82,13 +150,6 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, { int i; - /* We don't support volatile registers yet - refactoring of - * the hw_read operation will be required to do so. */ - if (codec->volatile_register) { - printk(KERN_ERR "Volatile registers not yet supported\n"); - return -EINVAL; - } - for (i = 0; i < ARRAY_SIZE(io_types); i++) if (io_types[i].addr_bits == addr_bits && io_types[i].data_bits == data_bits) @@ -111,6 +172,8 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, #if defined(CONFIG_I2C) || defined(CONFIG_I2C_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; break; case SND_SOC_SPI: -- cgit v1.2.3