From 175ee39e8f4053f95e1948afd75c74552b3a175c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 31 Aug 2013 20:31:11 +0200 Subject: ASoC: Remove support for reg_access_defaults No users of reg_access_defaults are left and new drivers are going to use regmap for this, so support for it can be removed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 24 ------------------------ 1 file changed, 24 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index d22cb0a06feb..447278a3b3e6 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -403,12 +403,6 @@ int snd_soc_cache_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value); int snd_soc_cache_read(struct snd_soc_codec *codec, unsigned int reg, unsigned int *value); -int snd_soc_default_volatile_register(struct snd_soc_codec *codec, - unsigned int reg); -int snd_soc_default_readable_register(struct snd_soc_codec *codec, - unsigned int reg); -int snd_soc_default_writable_register(struct snd_soc_codec *codec, - unsigned int reg); int snd_soc_platform_read(struct snd_soc_platform *platform, unsigned int reg); int snd_soc_platform_write(struct snd_soc_platform *platform, @@ -541,22 +535,6 @@ int snd_soc_get_strobe(struct snd_kcontrol *kcontrol, int snd_soc_put_strobe(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); -/** - * struct snd_soc_reg_access - Describes whether a given register is - * readable, writable or volatile. - * - * @reg: the register number - * @read: whether this register is readable - * @write: whether this register is writable - * @vol: whether this register is volatile - */ -struct snd_soc_reg_access { - u16 reg; - u16 read; - u16 write; - u16 vol; -}; - /** * struct snd_soc_jack_pin - Describes a pin to update based on jack detection * @@ -760,8 +738,6 @@ struct snd_soc_codec_driver { short reg_cache_step; short reg_word_size; const void *reg_cache_default; - short reg_access_size; - const struct snd_soc_reg_access *reg_access_default; enum snd_soc_compress_type compress_type; /* codec bias level */ -- cgit v1.2.3 From 2a1212a8342c469cee240cf69fe3001b898cda8e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 31 Aug 2013 20:31:12 +0200 Subject: ASoC: Remove snd_soc_bulk_write_raw() No users of snd_soc_bulk_write_raw() are left and new drivers are going to use regmap directly for this, so the function can be removed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 3 --- sound/soc/soc-core.c | 7 ------- sound/soc/soc-io.c | 26 -------------------------- 3 files changed, 36 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 447278a3b3e6..3f7de6f992c0 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -686,7 +686,6 @@ struct snd_soc_codec { unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int); unsigned int (*read)(struct snd_soc_codec *, unsigned int); int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); - int (*bulk_write_raw)(struct snd_soc_codec *, unsigned int, const void *, size_t); void *reg_cache; const void *reg_def_copy; const struct snd_soc_cache_ops *cache_ops; @@ -1097,8 +1096,6 @@ struct soc_enum { unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg); unsigned int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val); -unsigned int snd_soc_bulk_write_raw(struct snd_soc_codec *codec, - unsigned int reg, const void *data, size_t len); /* device driver data */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index f5ec301603d8..4ce02e6777e5 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2298,13 +2298,6 @@ unsigned int snd_soc_write(struct snd_soc_codec *codec, } EXPORT_SYMBOL_GPL(snd_soc_write); -unsigned int snd_soc_bulk_write_raw(struct snd_soc_codec *codec, - unsigned int reg, const void *data, size_t len) -{ - return codec->bulk_write_raw(codec, reg, data, len); -} -EXPORT_SYMBOL_GPL(snd_soc_bulk_write_raw); - /** * snd_soc_update_bits - update codec register bits * @codec: audio codec diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c index 122c0c18b9dd..4f11d23f2062 100644 --- a/sound/soc/soc-io.c +++ b/sound/soc/soc-io.c @@ -65,31 +65,6 @@ static unsigned int hw_read(struct snd_soc_codec *codec, unsigned int reg) return val; } -/* Primitive bulk write support for soc-cache. The data pointed to by - * `data' needs to already be in the form the hardware expects. Any - * data written through this function will not go through the cache as - * it only handles writing to volatile or out of bounds registers. - * - * This is currently only supported for devices using the regmap API - * wrappers. - */ -static int snd_soc_hw_bulk_write_raw(struct snd_soc_codec *codec, - unsigned int reg, - const void *data, size_t len) -{ - /* 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; - - return regmap_raw_write(codec->control_data, reg, data, len); -} - /** * snd_soc_codec_set_cache_io: Set up standard I/O functions. * @@ -119,7 +94,6 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, memset(&config, 0, sizeof(config)); codec->write = hw_write; codec->read = hw_read; - codec->bulk_write_raw = snd_soc_hw_bulk_write_raw; config.reg_bits = addr_bits; config.val_bits = data_bits; -- cgit v1.2.3 From b012aa619e50d22df0835b64a5dcebc221fb8053 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 31 Aug 2013 20:31:13 +0200 Subject: ASoC: Remove reg_def_copy reg_def_copy was introduced in commit 3335ddca ("ASoC: soc-cache: Use reg_def_copy instead of reg_cache_default") to keep a copy of the register defaults around in case the register defaults where placed in the __devinitdata section. With the __devinitdata section gone we effectivly keep the same data around twice. This patch removes reg_def_copy and uses reg_cache_default directly instead. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 1 - sound/soc/soc-cache.c | 10 ++++++---- sound/soc/soc-core.c | 15 --------------- 3 files changed, 6 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 3f7de6f992c0..62f320f56644 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -687,7 +687,6 @@ struct snd_soc_codec { unsigned int (*read)(struct snd_soc_codec *, unsigned int); int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); void *reg_cache; - const void *reg_def_copy; const struct snd_soc_cache_ops *cache_ops; struct mutex cache_rw_mutex; int val_bytes; diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index eaa898f8d808..a7f83c0c62ce 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -78,8 +78,8 @@ static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) ret = snd_soc_cache_read(codec, i, &val); if (ret) return ret; - if (codec->reg_def_copy) - if (snd_soc_get_cache_val(codec->reg_def_copy, + if (codec_drv->reg_cache_default) + if (snd_soc_get_cache_val(codec_drv->reg_cache_default, i, codec_drv->reg_word_size) == val) continue; @@ -121,8 +121,10 @@ static int snd_soc_flat_cache_exit(struct snd_soc_codec *codec) static int snd_soc_flat_cache_init(struct snd_soc_codec *codec) { - if (codec->reg_def_copy) - codec->reg_cache = kmemdup(codec->reg_def_copy, + const struct snd_soc_codec_driver *codec_drv = codec->driver; + + if (codec_drv->reg_cache_default) + codec->reg_cache = kmemdup(codec_drv->reg_cache_default, codec->reg_size, GFP_KERNEL); else codec->reg_cache = kzalloc(codec->reg_size, GFP_KERNEL); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4ce02e6777e5..bbe833ab657e 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4201,20 +4201,6 @@ int snd_soc_register_codec(struct device *dev, if (codec_drv->reg_cache_size && codec_drv->reg_word_size) { reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; codec->reg_size = reg_size; - /* it is necessary to make a copy of the default register cache - * because in the case of using a compression type that requires - * the default register cache to be marked as the - * kernel might have freed the array by the time we initialize - * the cache. - */ - if (codec_drv->reg_cache_default) { - codec->reg_def_copy = kmemdup(codec_drv->reg_cache_default, - reg_size, GFP_KERNEL); - if (!codec->reg_def_copy) { - ret = -ENOMEM; - goto fail_codec_name; - } - } } for (i = 0; i < num_dai; i++) { @@ -4273,7 +4259,6 @@ found: dev_dbg(codec->dev, "ASoC: Unregistered codec '%s'\n", codec->name); snd_soc_cache_exit(codec); - kfree(codec->reg_def_copy); kfree(codec->name); kfree(codec); } -- cgit v1.2.3 From a94ed23436fb28bdcdd66e7fcf68ca5f7967e456 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 31 Aug 2013 20:31:14 +0200 Subject: ASoC: Remove 'reg_size' field from snd_soc_codec struct The reg_size field is calculated in snd_soc_register_codec() and then used exactly once in snd_soc_flat_cache_init(). Since it is calculated based on other fields from the codec struct just move the calculation to snd_soc_flat_cache_init() and remove the 'reg_size' field from the codec struct. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 1 - sound/soc/soc-cache.c | 7 +++++-- sound/soc/soc-core.c | 7 ------- 3 files changed, 5 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 62f320f56644..577212629d0f 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -662,7 +662,6 @@ struct snd_soc_codec { struct list_head card_list; int num_dai; enum snd_soc_compress_type compress_type; - size_t reg_size; /* reg_cache_size * reg_word_size */ int (*volatile_register)(struct snd_soc_codec *, unsigned int); int (*readable_register)(struct snd_soc_codec *, unsigned int); int (*writable_register)(struct snd_soc_codec *, unsigned int); diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index a7f83c0c62ce..9542c83d2295 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -122,12 +122,15 @@ static int snd_soc_flat_cache_exit(struct snd_soc_codec *codec) static int snd_soc_flat_cache_init(struct snd_soc_codec *codec) { const struct snd_soc_codec_driver *codec_drv = codec->driver; + size_t reg_size; + + reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; if (codec_drv->reg_cache_default) codec->reg_cache = kmemdup(codec_drv->reg_cache_default, - codec->reg_size, GFP_KERNEL); + reg_size, GFP_KERNEL); else - codec->reg_cache = kzalloc(codec->reg_size, GFP_KERNEL); + codec->reg_cache = kzalloc(reg_size, GFP_KERNEL); if (!codec->reg_cache) return -ENOMEM; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index bbe833ab657e..af9648426f4f 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4159,7 +4159,6 @@ int snd_soc_register_codec(struct device *dev, struct snd_soc_dai_driver *dai_drv, int num_dai) { - size_t reg_size; struct snd_soc_codec *codec; int ret, i; @@ -4197,12 +4196,6 @@ int snd_soc_register_codec(struct device *dev, codec->num_dai = num_dai; mutex_init(&codec->mutex); - /* allocate CODEC register cache */ - if (codec_drv->reg_cache_size && codec_drv->reg_word_size) { - reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; - codec->reg_size = reg_size; - } - for (i = 0; i < num_dai; i++) { fixup_codec_formats(&dai_drv[i].playback); fixup_codec_formats(&dai_drv[i].capture); -- cgit v1.2.3 From f90fb3f778042b0b9f9aa1fd48cb76047a25eac0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 31 Aug 2013 20:31:15 +0200 Subject: ASoC: Remove infrastructure for supporting multiple cache types The only cache type left is the flat cache and new other cache types won't be added since new drivers are supposed to use regmap directly for IO and caching. This patch removes the snd_soc_cache_ops indirection that was added to support multiple cache types and modifies the code to always use the flat cache directly. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 27 ------- sound/soc/soc-cache.c | 195 +++++++++++++++----------------------------------- sound/soc/soc-core.c | 27 +------ 3 files changed, 58 insertions(+), 191 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 577212629d0f..a72af6327987 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -330,7 +330,6 @@ struct soc_enum; struct snd_soc_jack; struct snd_soc_jack_zone; struct snd_soc_jack_pin; -struct snd_soc_cache_ops; #include #include @@ -348,10 +347,6 @@ enum snd_soc_control_type { SND_SOC_REGMAP, }; -enum snd_soc_compress_type { - SND_SOC_FLAT_COMPRESSION = 1, -}; - enum snd_soc_pcm_subclass { SND_SOC_PCM_CLASS_PCM = 0, SND_SOC_PCM_CLASS_BE = 1, @@ -635,19 +630,6 @@ struct snd_soc_compr_ops { int (*trigger)(struct snd_compr_stream *); }; -/* SoC cache ops */ -struct snd_soc_cache_ops { - const char *name; - enum snd_soc_compress_type id; - int (*init)(struct snd_soc_codec *codec); - int (*exit)(struct snd_soc_codec *codec); - int (*read)(struct snd_soc_codec *codec, unsigned int reg, - unsigned int *value); - int (*write)(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value); - int (*sync)(struct snd_soc_codec *codec); -}; - /* SoC Audio Codec device */ struct snd_soc_codec { const char *name; @@ -661,7 +643,6 @@ struct snd_soc_codec { struct list_head list; struct list_head card_list; int num_dai; - enum snd_soc_compress_type compress_type; int (*volatile_register)(struct snd_soc_codec *, unsigned int); int (*readable_register)(struct snd_soc_codec *, unsigned int); int (*writable_register)(struct snd_soc_codec *, unsigned int); @@ -686,7 +667,6 @@ struct snd_soc_codec { unsigned int (*read)(struct snd_soc_codec *, unsigned int); int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); void *reg_cache; - const struct snd_soc_cache_ops *cache_ops; struct mutex cache_rw_mutex; int val_bytes; @@ -735,7 +715,6 @@ struct snd_soc_codec_driver { short reg_cache_step; short reg_word_size; const void *reg_cache_default; - enum snd_soc_compress_type compress_type; /* codec bias level */ int (*set_bias_level)(struct snd_soc_codec *, @@ -917,12 +896,6 @@ struct snd_soc_codec_conf { * associated per device */ const char *name_prefix; - - /* - * set this to the desired compression type if you want to - * override the one supplied in codec->driver->compress_type - */ - enum snd_soc_compress_type compress_type; }; struct snd_soc_aux_dev { diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 9542c83d2295..1b6663f45b34 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -11,12 +11,9 @@ * option) any later version. */ -#include -#include #include -#include -#include #include +#include #include @@ -66,66 +63,18 @@ static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx, return -1; } -static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) -{ - int i; - int ret; - const struct snd_soc_codec_driver *codec_drv; - unsigned int val; - - codec_drv = codec->driver; - for (i = 0; i < codec_drv->reg_cache_size; ++i) { - ret = snd_soc_cache_read(codec, i, &val); - if (ret) - return ret; - if (codec_drv->reg_cache_default) - if (snd_soc_get_cache_val(codec_drv->reg_cache_default, - i, codec_drv->reg_word_size) == val) - continue; - - WARN_ON(!snd_soc_codec_writable_register(codec, i)); - - ret = snd_soc_write(codec, i, val); - if (ret) - return ret; - dev_dbg(codec->dev, "ASoC: Synced register %#x, value = %#x\n", - i, val); - } - return 0; -} - -static int snd_soc_flat_cache_write(struct snd_soc_codec *codec, - unsigned int reg, unsigned int value) -{ - snd_soc_set_cache_val(codec->reg_cache, reg, value, - codec->driver->reg_word_size); - return 0; -} - -static int snd_soc_flat_cache_read(struct snd_soc_codec *codec, - unsigned int reg, unsigned int *value) -{ - *value = snd_soc_get_cache_val(codec->reg_cache, reg, - codec->driver->reg_word_size); - return 0; -} - -static int snd_soc_flat_cache_exit(struct snd_soc_codec *codec) -{ - if (!codec->reg_cache) - return 0; - kfree(codec->reg_cache); - codec->reg_cache = NULL; - return 0; -} - -static int snd_soc_flat_cache_init(struct snd_soc_codec *codec) +int snd_soc_cache_init(struct snd_soc_codec *codec) { const struct snd_soc_codec_driver *codec_drv = codec->driver; size_t reg_size; reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; + mutex_init(&codec->cache_rw_mutex); + + dev_dbg(codec->dev, "ASoC: Initializing cache for %s codec\n", + codec->name); + if (codec_drv->reg_cache_default) codec->reg_cache = kmemdup(codec_drv->reg_cache_default, reg_size, GFP_KERNEL); @@ -137,60 +86,19 @@ static int snd_soc_flat_cache_init(struct snd_soc_codec *codec) return 0; } -/* an array of all supported compression types */ -static const struct snd_soc_cache_ops cache_types[] = { - /* Flat *must* be the first entry for fallback */ - { - .id = SND_SOC_FLAT_COMPRESSION, - .name = "flat", - .init = snd_soc_flat_cache_init, - .exit = snd_soc_flat_cache_exit, - .read = snd_soc_flat_cache_read, - .write = snd_soc_flat_cache_write, - .sync = snd_soc_flat_cache_sync - }, -}; - -int snd_soc_cache_init(struct snd_soc_codec *codec) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(cache_types); ++i) - if (cache_types[i].id == codec->compress_type) - break; - - /* Fall back to flat compression */ - if (i == ARRAY_SIZE(cache_types)) { - dev_warn(codec->dev, "ASoC: Could not match compress type: %d\n", - codec->compress_type); - i = 0; - } - - mutex_init(&codec->cache_rw_mutex); - codec->cache_ops = &cache_types[i]; - - if (codec->cache_ops->init) { - if (codec->cache_ops->name) - dev_dbg(codec->dev, "ASoC: Initializing %s cache for %s codec\n", - codec->cache_ops->name, codec->name); - return codec->cache_ops->init(codec); - } - return -ENOSYS; -} - /* * NOTE: keep in mind that this function might be called * multiple times. */ int snd_soc_cache_exit(struct snd_soc_codec *codec) { - if (codec->cache_ops && codec->cache_ops->exit) { - if (codec->cache_ops->name) - dev_dbg(codec->dev, "ASoC: Destroying %s cache for %s codec\n", - codec->cache_ops->name, codec->name); - return codec->cache_ops->exit(codec); - } - return -ENOSYS; + dev_dbg(codec->dev, "ASoC: Destroying cache for %s codec\n", + codec->name); + if (!codec->reg_cache) + return 0; + kfree(codec->reg_cache); + codec->reg_cache = NULL; + return 0; } /** @@ -203,18 +111,15 @@ int snd_soc_cache_exit(struct snd_soc_codec *codec) int snd_soc_cache_read(struct snd_soc_codec *codec, unsigned int reg, unsigned int *value) { - int ret; + if (!value) + return -EINVAL; mutex_lock(&codec->cache_rw_mutex); - - if (value && codec->cache_ops && codec->cache_ops->read) { - ret = codec->cache_ops->read(codec, reg, value); - mutex_unlock(&codec->cache_rw_mutex); - return ret; - } - + *value = snd_soc_get_cache_val(codec->reg_cache, reg, + codec->driver->reg_word_size); mutex_unlock(&codec->cache_rw_mutex); - return -ENOSYS; + + return 0; } EXPORT_SYMBOL_GPL(snd_soc_cache_read); @@ -228,20 +133,42 @@ EXPORT_SYMBOL_GPL(snd_soc_cache_read); int snd_soc_cache_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { + mutex_lock(&codec->cache_rw_mutex); + snd_soc_set_cache_val(codec->reg_cache, reg, value, + codec->driver->reg_word_size); + mutex_unlock(&codec->cache_rw_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_cache_write); + +static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) +{ + int i; int ret; + const struct snd_soc_codec_driver *codec_drv; + unsigned int val; - mutex_lock(&codec->cache_rw_mutex); + codec_drv = codec->driver; + for (i = 0; i < codec_drv->reg_cache_size; ++i) { + ret = snd_soc_cache_read(codec, i, &val); + if (ret) + return ret; + if (codec_drv->reg_cache_default) + if (snd_soc_get_cache_val(codec_drv->reg_cache_default, + i, codec_drv->reg_word_size) == val) + continue; - if (codec->cache_ops && codec->cache_ops->write) { - ret = codec->cache_ops->write(codec, reg, value); - mutex_unlock(&codec->cache_rw_mutex); - return ret; - } + WARN_ON(!snd_soc_codec_writable_register(codec, i)); - mutex_unlock(&codec->cache_rw_mutex); - return -ENOSYS; + ret = snd_soc_write(codec, i, val); + if (ret) + return ret; + dev_dbg(codec->dev, "ASoC: Synced register %#x, value = %#x\n", + i, val); + } + return 0; } -EXPORT_SYMBOL_GPL(snd_soc_cache_write); /** * snd_soc_cache_sync: Sync the register cache with the hardware. @@ -254,26 +181,16 @@ EXPORT_SYMBOL_GPL(snd_soc_cache_write); */ int snd_soc_cache_sync(struct snd_soc_codec *codec) { + const char *name = "flat"; int ret; - const char *name; - if (!codec->cache_sync) { + if (!codec->cache_sync) return 0; - } - - if (!codec->cache_ops || !codec->cache_ops->sync) - return -ENOSYS; - - if (codec->cache_ops->name) - name = codec->cache_ops->name; - else - name = "unknown"; - if (codec->cache_ops->name) - dev_dbg(codec->dev, "ASoC: Syncing %s cache for %s codec\n", - codec->cache_ops->name, codec->name); + dev_dbg(codec->dev, "ASoC: Syncing cache for %s codec\n", + codec->name); trace_snd_soc_cache_sync(codec, name, "start"); - ret = codec->cache_ops->sync(codec); + ret = snd_soc_flat_cache_sync(codec); if (!ret) codec->cache_sync = 0; trace_snd_soc_cache_sync(codec, name, "end"); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index af9648426f4f..16a3930c6375 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1590,17 +1590,13 @@ static void soc_remove_aux_dev(struct snd_soc_card *card, int num) soc_remove_codec(codec); } -static int snd_soc_init_codec_cache(struct snd_soc_codec *codec, - enum snd_soc_compress_type compress_type) +static int snd_soc_init_codec_cache(struct snd_soc_codec *codec) { int ret; if (codec->cache_init) return 0; - /* override the compress_type if necessary */ - if (compress_type && codec->compress_type != compress_type) - codec->compress_type = compress_type; ret = snd_soc_cache_init(codec); if (ret < 0) { dev_err(codec->dev, @@ -1615,8 +1611,6 @@ static int snd_soc_init_codec_cache(struct snd_soc_codec *codec, static int snd_soc_instantiate_card(struct snd_soc_card *card) { struct snd_soc_codec *codec; - struct snd_soc_codec_conf *codec_conf; - enum snd_soc_compress_type compress_type; struct snd_soc_dai_link *dai_link; int ret, i, order, dai_fmt; @@ -1640,19 +1634,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) list_for_each_entry(codec, &codec_list, list) { if (codec->cache_init) continue; - /* by default we don't override the compress_type */ - compress_type = 0; - /* check to see if we need to override the compress_type */ - for (i = 0; i < card->num_configs; ++i) { - codec_conf = &card->codec_conf[i]; - if (!strcmp(codec->name, codec_conf->dev_name)) { - compress_type = codec_conf->compress_type; - if (compress_type && compress_type - != codec->compress_type) - break; - } - } - ret = snd_soc_init_codec_cache(codec, compress_type); + ret = snd_soc_init_codec_cache(codec); if (ret < 0) goto base_error; } @@ -4175,11 +4157,6 @@ int snd_soc_register_codec(struct device *dev, goto fail_codec; } - if (codec_drv->compress_type) - codec->compress_type = codec_drv->compress_type; - else - codec->compress_type = SND_SOC_FLAT_COMPRESSION; - codec->write = codec_drv->write; codec->read = codec_drv->read; codec->volatile_register = codec_drv->volatile_register; -- cgit v1.2.3 From 56fb7421d23c63cf22ac885d2db2302cefc9e1f1 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 13 Sep 2013 18:09:46 +0100 Subject: ASoC: trace: Make sure trace header doesnt depend on any headers Fix build so that asoc trace event header doesn't depend on other headers. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown --- include/trace/events/asoc.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/trace/events/asoc.h b/include/trace/events/asoc.h index 5fc2dcdd21cd..03996b2bb04f 100644 --- a/include/trace/events/asoc.h +++ b/include/trace/events/asoc.h @@ -14,6 +14,7 @@ struct snd_soc_codec; struct snd_soc_platform; struct snd_soc_card; struct snd_soc_dapm_widget; +struct snd_soc_dapm_path; /* * Log register events -- cgit v1.2.3 From a0b03a616b08cf9d709812ff5cf7e9c0958d6807 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 4 Sep 2013 20:37:34 +0100 Subject: ASoC: core: Implement devm_snd_soc_register_component() Since with the wider use of devres many drivers are now only calling snd_soc_unregister_component() in their remove functions providing a managed version will save a reasonable amount of code. Signed-off-by: Mark Brown --- include/sound/soc.h | 3 +++ sound/soc/Makefile | 2 +- sound/soc/soc-devres.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 sound/soc/soc-devres.c (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index d22cb0a06feb..b970f019b452 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -386,6 +386,9 @@ void snd_soc_unregister_codec(struct device *dev); int snd_soc_register_component(struct device *dev, const struct snd_soc_component_driver *cmpnt_drv, struct snd_soc_dai_driver *dai_drv, int num_dai); +int devm_snd_soc_register_component(struct device *dev, + const struct snd_soc_component_driver *cmpnt_drv, + struct snd_soc_dai_driver *dai_drv, int num_dai); void snd_soc_unregister_component(struct device *dev); int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, unsigned int reg); diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 61a64d281905..8b9e70105dd2 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,5 +1,5 @@ snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o -snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o +snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),) snd-soc-core-objs += soc-generic-dmaengine-pcm.o diff --git a/sound/soc/soc-devres.c b/sound/soc/soc-devres.c new file mode 100644 index 000000000000..13fe86f7c9a8 --- /dev/null +++ b/sound/soc/soc-devres.c @@ -0,0 +1,52 @@ +/* + * soc-devres.c -- ALSA SoC Audio Layer devres functions + * + * Copyright (C) 2013 Linaro Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include + +static void devm_component_release(struct device *dev, void *res) +{ + snd_soc_unregister_component(*(struct device **)res); +} + +/** + * devm_snd_soc_register_component - resource managed component registration + * @dev: Device used to manage component + * @cmpnt_drv: Component driver + * @dai_drv: DAI driver + * @num_dai: Number of DAIs to register + * + * Register a component with automatic unregistration when the device is + * unregistered. + */ +int devm_snd_soc_register_component(struct device *dev, + const struct snd_soc_component_driver *cmpnt_drv, + struct snd_soc_dai_driver *dai_drv, int num_dai) +{ + struct device **ptr; + int ret; + + ptr = devres_alloc(devm_component_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = snd_soc_register_component(dev, cmpnt_drv, dai_drv, num_dai); + if (ret == 0) { + *ptr = dev; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return ret; +} +EXPORT_SYMBOL_GPL(devm_snd_soc_register_component); -- cgit v1.2.3 From 0e4ff5c806263bf40ee5409ac283b776f0c11e41 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 16 Sep 2013 18:02:05 +0100 Subject: ASoC: core: Add devm_snd_soc_register_card() Simplify error handling and remove repetitive (and rarely executed) code for unregistration by providing a devm_snd_soc_register() card. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 1 + sound/soc/soc-devres.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index b970f019b452..d44728ab2be0 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -369,6 +369,7 @@ int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source, int snd_soc_register_card(struct snd_soc_card *card); int snd_soc_unregister_card(struct snd_soc_card *card); +int devm_snd_soc_register_card(struct device *dev, struct snd_soc_card *card); int snd_soc_suspend(struct device *dev); int snd_soc_resume(struct device *dev); int snd_soc_poweroff(struct device *dev); diff --git a/sound/soc/soc-devres.c b/sound/soc/soc-devres.c index 13fe86f7c9a8..b1d732255c02 100644 --- a/sound/soc/soc-devres.c +++ b/sound/soc/soc-devres.c @@ -50,3 +50,37 @@ int devm_snd_soc_register_component(struct device *dev, return ret; } EXPORT_SYMBOL_GPL(devm_snd_soc_register_component); + +static void devm_card_release(struct device *dev, void *res) +{ + snd_soc_unregister_card(*(struct snd_soc_card **)res); +} + +/** + * devm_snd_soc_register_card - resource managed card registration + * @dev: Device used to manage card + * @card: Card to register + * + * Register a card with automatic unregistration when the device is + * unregistered. + */ +int devm_snd_soc_register_card(struct device *dev, struct snd_soc_card *card) +{ + struct device **ptr; + int ret; + + ptr = devres_alloc(devm_card_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = snd_soc_register_card(card); + if (ret == 0) { + *ptr = dev; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return ret; +} +EXPORT_SYMBOL_GPL(devm_snd_soc_register_card); -- cgit v1.2.3 From e54cf76ba2c9ec071a68e98f2830226c0cac8086 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 16 Sep 2013 13:01:46 +0100 Subject: ASoC: core: Add API for configuration of DAI BCLK ratio Some codec drivers when running in slave mode require that BCLK to sample rate ratio is explicitly set by the machine driver as it may not be exactly rate * frame size. Extend the DAI API by adding :- int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio); Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 3 +++ sound/soc/soc-core.c | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) (limited to 'include') diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index ae9a227d35d3..d8acf0ca77be 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -105,6 +105,8 @@ int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai, int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, unsigned int freq_in, unsigned int freq_out); +int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio); + /* Digital Audio interface formatting */ int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt); @@ -131,6 +133,7 @@ struct snd_soc_dai_ops { int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source, unsigned int freq_in, unsigned int freq_out); int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div); + int (*set_bclk_ratio)(struct snd_soc_dai *dai, unsigned int ratio); /* * DAI format configuration diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4d0561312f3b..31adad04222d 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3576,6 +3576,22 @@ int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source, } EXPORT_SYMBOL_GPL(snd_soc_codec_set_pll); +/** + * snd_soc_dai_set_bclk_ratio - configure BCLK to sample rate ratio. + * @dai: DAI + * @ratio Ratio of BCLK to Sample rate. + * + * Configures the DAI for a preset BCLK to sample rate ratio. + */ +int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + if (dai->driver && dai->driver->ops->set_bclk_ratio) + return dai->driver->ops->set_bclk_ratio(dai, ratio); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_ratio); + /** * snd_soc_dai_set_fmt - configure DAI hardware audio format. * @dai: DAI -- cgit v1.2.3 From d191bd8de8c61619563f2b19f1fdcc0944ff1a72 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 4 Sep 2013 19:39:03 -0700 Subject: ASoC: snd_soc_codec includes snd_soc_component Codec includes component by this patch, and component moved to upside of codec to avoid extra declaration. Codec dai will be registered via component by this patch. Current component register function is used for cpu, and it is using dai/dais functions properly to keep existing cpu dai name. And now, it will be used from codec also. But codec driver had been used dais function only even though it was single dai. This patch adds new flag which can selects dai/dais function on component register function to keep existing codec dai name. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/soc.h | 33 +++++---- sound/soc/soc-core.c | 202 ++++++++++++++++++++++++++++----------------------- 2 files changed, 131 insertions(+), 104 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index d22cb0a06feb..9a81e2e7d661 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -670,6 +670,21 @@ struct snd_soc_cache_ops { int (*sync)(struct snd_soc_codec *codec); }; +/* component interface */ +struct snd_soc_component_driver { + const char *name; +}; + +struct snd_soc_component { + const char *name; + int id; + int num_dai; + struct device *dev; + struct list_head list; + + const struct snd_soc_component_driver *driver; +}; + /* SoC Audio Codec device */ struct snd_soc_codec { const char *name; @@ -715,6 +730,9 @@ struct snd_soc_codec { struct mutex cache_rw_mutex; int val_bytes; + /* component */ + struct snd_soc_component component; + /* dapm */ struct snd_soc_dapm_context dapm; unsigned int ignore_pmdown_time:1; /* pmdown_time is ignored at stop */ @@ -733,6 +751,7 @@ struct snd_soc_codec_driver { int (*remove)(struct snd_soc_codec *); int (*suspend)(struct snd_soc_codec *); int (*resume)(struct snd_soc_codec *); + struct snd_soc_component_driver component_driver; /* Default control and setup, added after probe() is run */ const struct snd_kcontrol_new *controls; @@ -849,20 +868,6 @@ struct snd_soc_platform { #endif }; -struct snd_soc_component_driver { - const char *name; -}; - -struct snd_soc_component { - const char *name; - int id; - int num_dai; - struct device *dev; - struct list_head list; - - const struct snd_soc_component_driver *driver; -}; - struct snd_soc_dai_link { /* config - must be set by machine driver */ const char *name; /* Codec name */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4d0561312f3b..014ac10267da 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4020,6 +4020,112 @@ static void snd_soc_unregister_dais(struct device *dev, size_t count) snd_soc_unregister_dai(dev); } +/** + * snd_soc_register_component - Register a component with the ASoC core + * + */ +static int +__snd_soc_register_component(struct device *dev, + struct snd_soc_component *cmpnt, + const struct snd_soc_component_driver *cmpnt_drv, + struct snd_soc_dai_driver *dai_drv, + int num_dai, bool allow_single_dai) +{ + int ret; + + dev_dbg(dev, "component register %s\n", dev_name(dev)); + + if (!cmpnt) { + dev_err(dev, "ASoC: Failed to connecting component\n"); + return -ENOMEM; + } + + cmpnt->name = fmt_single_name(dev, &cmpnt->id); + if (!cmpnt->name) { + dev_err(dev, "ASoC: Failed to simplifying name\n"); + return -ENOMEM; + } + + cmpnt->dev = dev; + cmpnt->driver = cmpnt_drv; + cmpnt->num_dai = num_dai; + + /* + * snd_soc_register_dai() uses fmt_single_name(), and + * snd_soc_register_dais() uses fmt_multiple_name() + * for dai->name which is used for name based matching + * + * this function is used from cpu/codec. + * allow_single_dai flag can ignore "codec" driver reworking + * since it had been used snd_soc_register_dais(), + */ + if ((1 == num_dai) && allow_single_dai) + ret = snd_soc_register_dai(dev, dai_drv); + else + ret = snd_soc_register_dais(dev, dai_drv, num_dai); + if (ret < 0) { + dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret); + goto error_component_name; + } + + mutex_lock(&client_mutex); + list_add(&cmpnt->list, &component_list); + mutex_unlock(&client_mutex); + + dev_dbg(cmpnt->dev, "ASoC: Registered component '%s'\n", cmpnt->name); + + return ret; + +error_component_name: + kfree(cmpnt->name); + + return ret; +} + +int snd_soc_register_component(struct device *dev, + const struct snd_soc_component_driver *cmpnt_drv, + struct snd_soc_dai_driver *dai_drv, + int num_dai) +{ + struct snd_soc_component *cmpnt; + + cmpnt = devm_kzalloc(dev, sizeof(*cmpnt), GFP_KERNEL); + if (!cmpnt) { + dev_err(dev, "ASoC: Failed to allocate memory\n"); + return -ENOMEM; + } + + return __snd_soc_register_component(dev, cmpnt, cmpnt_drv, + dai_drv, num_dai, true); +} +EXPORT_SYMBOL_GPL(snd_soc_register_component); + +/** + * snd_soc_unregister_component - Unregister a component from the ASoC core + * + */ +void snd_soc_unregister_component(struct device *dev) +{ + struct snd_soc_component *cmpnt; + + list_for_each_entry(cmpnt, &component_list, list) { + if (dev == cmpnt->dev) + goto found; + } + return; + +found: + snd_soc_unregister_dais(dev, cmpnt->num_dai); + + mutex_lock(&client_mutex); + list_del(&cmpnt->list); + mutex_unlock(&client_mutex); + + dev_dbg(dev, "ASoC: Unregistered component '%s'\n", cmpnt->name); + kfree(cmpnt->name); +} +EXPORT_SYMBOL_GPL(snd_soc_unregister_component); + /** * snd_soc_add_platform - Add a platform to the ASoC core * @dev: The parent device for the platform @@ -4242,10 +4348,12 @@ int snd_soc_register_codec(struct device *dev, list_add(&codec->list, &codec_list); mutex_unlock(&client_mutex); - /* register any DAIs */ - ret = snd_soc_register_dais(dev, dai_drv, num_dai); + /* register component */ + ret = __snd_soc_register_component(dev, &codec->component, + &codec_drv->component_driver, + dai_drv, num_dai, false); if (ret < 0) { - dev_err(codec->dev, "ASoC: Failed to regster DAIs: %d\n", ret); + dev_err(codec->dev, "ASoC: Failed to regster component: %d\n", ret); goto fail_codec_name; } @@ -4280,7 +4388,7 @@ void snd_soc_unregister_codec(struct device *dev) return; found: - snd_soc_unregister_dais(dev, codec->num_dai); + snd_soc_unregister_component(dev); mutex_lock(&client_mutex); list_del(&codec->list); @@ -4295,92 +4403,6 @@ found: } EXPORT_SYMBOL_GPL(snd_soc_unregister_codec); - -/** - * snd_soc_register_component - Register a component with the ASoC core - * - */ -int snd_soc_register_component(struct device *dev, - const struct snd_soc_component_driver *cmpnt_drv, - struct snd_soc_dai_driver *dai_drv, - int num_dai) -{ - struct snd_soc_component *cmpnt; - int ret; - - dev_dbg(dev, "component register %s\n", dev_name(dev)); - - cmpnt = devm_kzalloc(dev, sizeof(*cmpnt), GFP_KERNEL); - if (!cmpnt) { - dev_err(dev, "ASoC: Failed to allocate memory\n"); - return -ENOMEM; - } - - cmpnt->name = fmt_single_name(dev, &cmpnt->id); - if (!cmpnt->name) { - dev_err(dev, "ASoC: Failed to simplifying name\n"); - return -ENOMEM; - } - - cmpnt->dev = dev; - cmpnt->driver = cmpnt_drv; - cmpnt->num_dai = num_dai; - - /* - * snd_soc_register_dai() uses fmt_single_name(), and - * snd_soc_register_dais() uses fmt_multiple_name() - * for dai->name which is used for name based matching - */ - if (1 == num_dai) - ret = snd_soc_register_dai(dev, dai_drv); - else - ret = snd_soc_register_dais(dev, dai_drv, num_dai); - if (ret < 0) { - dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret); - goto error_component_name; - } - - mutex_lock(&client_mutex); - list_add(&cmpnt->list, &component_list); - mutex_unlock(&client_mutex); - - dev_dbg(cmpnt->dev, "ASoC: Registered component '%s'\n", cmpnt->name); - - return ret; - -error_component_name: - kfree(cmpnt->name); - - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_register_component); - -/** - * snd_soc_unregister_component - Unregister a component from the ASoC core - * - */ -void snd_soc_unregister_component(struct device *dev) -{ - struct snd_soc_component *cmpnt; - - list_for_each_entry(cmpnt, &component_list, list) { - if (dev == cmpnt->dev) - goto found; - } - return; - -found: - snd_soc_unregister_dais(dev, cmpnt->num_dai); - - mutex_lock(&client_mutex); - list_del(&cmpnt->list); - mutex_unlock(&client_mutex); - - dev_dbg(dev, "ASoC: Unregistered component '%s'\n", cmpnt->name); - kfree(cmpnt->name); -} -EXPORT_SYMBOL_GPL(snd_soc_unregister_component); - /* Retrieve a card's name from device tree */ int snd_soc_of_parse_card_name(struct snd_soc_card *card, const char *propname) -- cgit v1.2.3 From cb470087669a3fab1958fec79dd7db280b33f178 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 10 Sep 2013 17:39:56 -0700 Subject: ASoC: add .of_xlate_dai_name on snd_soc_component_driver ASoC sound driver requires CPU/CODEC drivers for probing, and each CPU/CODEC has some DAI on it. Then, "dai name matching" have been used to identify CPU-CODEC DAI pair on ASoC. But, the "dai port number matching" is now required from DeviceTree. The solution of this issue is to replace the dai port number into dai name. Now, CPU/CODEC are based on struct snd_soc_component, and it can care above as common issue. This patch adds .of_xlate_dai_name callback interface on struct snd_soc_component_driver, and snd_soc_of_get_dai_name() which is using .of_xlate_dai_name. Then, #sound-dai-cells which enables DAI specifier is required on CPU/CODEC device tree properties. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/soc.h | 8 ++++++++ sound/soc/soc-core.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 9a81e2e7d661..1dd7dc5f7d52 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -13,6 +13,7 @@ #ifndef __LINUX_SND_SOC_H #define __LINUX_SND_SOC_H +#include #include #include #include @@ -673,6 +674,11 @@ struct snd_soc_cache_ops { /* component interface */ struct snd_soc_component_driver { const char *name; + + /* DT */ + int (*of_xlate_dai_name)(struct snd_soc_component *component, + struct of_phandle_args *args, + const char **dai_name); }; struct snd_soc_component { @@ -1206,6 +1212,8 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname); unsigned int snd_soc_of_parse_daifmt(struct device_node *np, const char *prefix); +int snd_soc_of_get_dai_name(struct device_node *of_node, + const char **dai_name); #include diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 014ac10267da..711bd362028d 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4590,6 +4590,41 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt); +int snd_soc_of_get_dai_name(struct device_node *of_node, + const char **dai_name) +{ + struct snd_soc_component *pos; + struct of_phandle_args args; + int ret; + + ret = of_parse_phandle_with_args(of_node, "sound-dai", + "#sound-dai-cells", 0, &args); + if (ret) + return ret; + + ret = -EPROBE_DEFER; + + mutex_lock(&client_mutex); + list_for_each_entry(pos, &component_list, list) { + if (pos->dev->of_node != args.np) + continue; + + if (!pos->driver->of_xlate_dai_name) { + ret = -ENOSYS; + break; + } + + ret = pos->driver->of_xlate_dai_name(pos, &args, dai_name); + break; + } + mutex_unlock(&client_mutex); + + of_node_put(args.np); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_name); + static int __init snd_soc_init(void) { #ifdef CONFIG_DEBUG_FS -- cgit v1.2.3 From fd792f8fbcfa95674b6c417429f576ad1d808086 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 23 Sep 2013 19:14:32 +0100 Subject: mfd: mc13xxx: Move SPI erratum workaround into SPI I/O function Move the workaround for double sending AUDIO_CODEC and AUDIO_DAC writes into the SPI core, aiding refactoring to eliminate the ASoC custom I/O functions and avoiding the extra writes for I2C. Signed-off-by: Mark Brown Signed-off-by: Lee Jones --- drivers/mfd/mc13xxx-spi.c | 5 +++++ include/linux/mfd/mc13xxx.h | 7 +++++++ sound/soc/codecs/mc13783.c | 4 ---- 3 files changed, 12 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/mfd/mc13xxx-spi.c b/drivers/mfd/mc13xxx-spi.c index 77189daadf1e..5f14ef6693c2 100644 --- a/drivers/mfd/mc13xxx-spi.c +++ b/drivers/mfd/mc13xxx-spi.c @@ -94,10 +94,15 @@ static int mc13xxx_spi_write(void *context, const void *data, size_t count) { struct device *dev = context; struct spi_device *spi = to_spi_device(dev); + const char *reg = data; if (count != 4) return -ENOTSUPP; + /* include errata fix for spi audio problems */ + if (*reg == MC13783_AUDIO_CODEC || *reg == MC13783_AUDIO_DAC) + spi_write(spi, data, count); + return spi_write(spi, data, count); } diff --git a/include/linux/mfd/mc13xxx.h b/include/linux/mfd/mc13xxx.h index 41ed59276c00..67c17b5a6f44 100644 --- a/include/linux/mfd/mc13xxx.h +++ b/include/linux/mfd/mc13xxx.h @@ -41,6 +41,13 @@ int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode, unsigned int channel, u8 ato, bool atox, unsigned int *sample); +#define MC13783_AUDIO_RX0 36 +#define MC13783_AUDIO_RX1 37 +#define MC13783_AUDIO_TX 38 +#define MC13783_SSI_NETWORK 39 +#define MC13783_AUDIO_CODEC 40 +#define MC13783_AUDIO_DAC 41 + #define MC13XXX_IRQ_ADCDONE 0 #define MC13XXX_IRQ_ADCBISDONE 1 #define MC13XXX_IRQ_TS 2 diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c index ea141e1d6f28..4d3c8fd8c5db 100644 --- a/sound/soc/codecs/mc13783.c +++ b/sound/soc/codecs/mc13783.c @@ -125,10 +125,6 @@ static int mc13783_write(struct snd_soc_codec *codec, ret = mc13xxx_reg_write(priv->mc13xxx, reg, value); - /* include errata fix for spi audio problems */ - if (reg == MC13783_AUDIO_CODEC || reg == MC13783_AUDIO_DAC) - ret = mc13xxx_reg_write(priv->mc13xxx, reg, value); - mc13xxx_unlock(priv->mc13xxx); return ret; -- cgit v1.2.3 From 249ce1387b7739dbea2ac1a697e4bf1e37ec06b7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 6 Oct 2013 13:43:49 +0200 Subject: ASoC: dapm: Add support for virtual mixer controls This patch adds support for virtual DAPM mixer controls. They are similar to virtual DAPM enums. There is no hardware register backing the control, so changing the control's value wont have any direct effect on the hardware. But it still influences the DAPM graph by causing the path it sits on to be connected or disconnected. This in turn can cause power changes for some of the widgets on the DAPM graph, which will then modify the hardware state. Signed-off-by: Lars-Peter Clausen Tested-by: Peter Ujfalusi Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 4 ++++ include/sound/soc.h | 3 ++- sound/soc/soc-dapm.c | 45 +++++++++++++++++++++++++++------------------ 3 files changed, 33 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 27a72d5d4b00..2037c45adfe6 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -286,6 +286,8 @@ struct device; .info = snd_soc_info_volsw, \ .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 1) } +#define SOC_DAPM_SINGLE_VIRT(xname, max) \ + SOC_DAPM_SINGLE(xname, SND_SOC_NOPM, 0, max, 0) #define SOC_DAPM_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, \ @@ -300,6 +302,8 @@ struct device; .tlv.p = (tlv_array), \ .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } +#define SOC_DAPM_SINGLE_TLV_VIRT(xname, max, tlv_array) \ + SOC_DAPM_SINGLE(xname, SND_SOC_NOPM, 0, max, 0, tlv_array) #define SOC_DAPM_ENUM(xname, xenum) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_enum_double, \ diff --git a/include/sound/soc.h b/include/sound/soc.h index d22cb0a06feb..b429dba57bf6 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1088,7 +1088,8 @@ struct snd_soc_pcm_runtime { /* mixer control */ struct soc_mixer_control { int min, max, platform_max; - unsigned int reg, rreg, shift, rshift; + int reg, rreg; + unsigned int shift, rshift; unsigned int invert:1; unsigned int autodisable:1; }; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 177f8a1938da..9273216f22fc 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -499,18 +499,22 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, int val; struct soc_mixer_control *mc = (struct soc_mixer_control *) w->kcontrol_news[i].private_value; - unsigned int reg = mc->reg; + int reg = mc->reg; unsigned int shift = mc->shift; int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; - val = soc_widget_read(w, reg); - val = (val >> shift) & mask; - if (invert) - val = max - val; + if (reg != SND_SOC_NOPM) { + val = soc_widget_read(w, reg); + val = (val >> shift) & mask; + if (invert) + val = max - val; + p->connect = !!val; + } else { + p->connect = 0; + } - p->connect = !!val; } break; case snd_soc_dapm_mux: { @@ -2792,7 +2796,7 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, struct snd_soc_card *card = codec->card; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; + int reg = mc->reg; unsigned int shift = mc->shift; int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; @@ -2805,7 +2809,7 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, kcontrol->id.name); mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - if (dapm_kcontrol_is_powered(kcontrol)) + if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) val = (snd_soc_read(codec, reg) >> shift) & mask; else val = dapm_kcontrol_get_value(kcontrol); @@ -2836,7 +2840,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, struct snd_soc_card *card = codec->card; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; + int reg = mc->reg; unsigned int shift = mc->shift; int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; @@ -2858,19 +2862,24 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - dapm_kcontrol_set_value(kcontrol, val); + change = dapm_kcontrol_set_value(kcontrol, val); - mask = mask << shift; - val = val << shift; + if (reg != SND_SOC_NOPM) { + mask = mask << shift; + val = val << shift; + + change = snd_soc_test_bits(codec, reg, mask, val); + } - change = snd_soc_test_bits(codec, reg, mask, val); if (change) { - update.kcontrol = kcontrol; - update.reg = reg; - update.mask = mask; - update.val = val; + if (reg != SND_SOC_NOPM) { + update.kcontrol = kcontrol; + update.reg = reg; + update.mask = mask; + update.val = val; - card->update = &update; + card->update = &update; + } soc_dapm_mixer_update_power(card, kcontrol, connect); -- cgit v1.2.3 From 9f1614aae59033b79941220f65ad36f5fe54a579 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Fri, 11 Oct 2013 12:11:02 +0200 Subject: ASoC: snd_soc_dai_ops trigger function description Add a comment to the trigger function in snd_soc_dai_ops struct about possible command sequences. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index ae9a227d35d3..0f2e5daa1451 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -166,6 +166,13 @@ struct snd_soc_dai_ops { struct snd_soc_dai *); int (*prepare)(struct snd_pcm_substream *, struct snd_soc_dai *); + /* + * NOTE: Commands passed to the trigger function are not necessarily + * compatible with the current state of the dai. For example this + * sequence of commands is possible: START STOP STOP. + * So do not unconditionally use refcounting functions in the trigger + * function, e.g. clk_enable/disable. + */ int (*trigger)(struct snd_pcm_substream *, int, struct snd_soc_dai *); int (*bespoke_trigger)(struct snd_pcm_substream *, int, -- cgit v1.2.3 From 420f9739a62cdb027f5580d25c813501ff93aa6f Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 16 Oct 2013 23:10:31 +0200 Subject: thinkpad-acpi: Add mute and mic-mute LED functionality The LEDs are currently not visible to userspace, for security reasons. They are exported through thinkpad_acpi.h for use by the snd-hda-intel driver. Thanks to Alex Hung and Takashi Iwai for writing parts of this patch. Signed-off-by: David Henningsson Acked-by: Henrique de Moraes Holschuh Signed-off-by: Takashi Iwai --- Documentation/laptops/thinkpad-acpi.txt | 7 ++- drivers/platform/x86/thinkpad_acpi.c | 92 ++++++++++++++++++++++++++++++++- include/linux/thinkpad_acpi.h | 15 ++++++ 3 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 include/linux/thinkpad_acpi.h (limited to 'include') diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index 86c52360ffe7..fc04c14de4bb 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt @@ -1,7 +1,7 @@ ThinkPad ACPI Extras Driver - Version 0.24 - December 11th, 2009 + Version 0.25 + October 16th, 2013 Borislav Deianov Henrique de Moraes Holschuh @@ -741,6 +741,9 @@ compiled with the CONFIG_THINKPAD_ACPI_UNSAFE_LEDS option enabled. Distributions must never enable this option. Individual users that are aware of the consequences are welcome to enabling it. +Audio mute and microphone mute LEDs are supported, but currently not +visible to userspace. They are used by the snd-hda-intel audio driver. + procfs notes: The available commands are: diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 03ca6c139f1a..0b7efb269cf1 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -23,7 +23,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define TPACPI_VERSION "0.24" +#define TPACPI_VERSION "0.25" #define TPACPI_SYSFS_VERSION 0x020700 /* @@ -88,6 +88,7 @@ #include +#include /* ThinkPad CMOS commands */ #define TP_CMOS_VOLUME_DOWN 0 @@ -8350,6 +8351,91 @@ static struct ibm_struct fan_driver_data = { .resume = fan_resume, }; +/************************************************************************* + * Mute LED subdriver + */ + + +struct tp_led_table { + acpi_string name; + int on_value; + int off_value; + int state; +}; + +static struct tp_led_table led_tables[] = { + [TPACPI_LED_MUTE] = { + .name = "SSMS", + .on_value = 1, + .off_value = 0, + }, + [TPACPI_LED_MICMUTE] = { + .name = "MMTS", + .on_value = 2, + .off_value = 0, + }, +}; + +static int mute_led_on_off(struct tp_led_table *t, bool state) +{ + acpi_handle temp; + int output; + + if (!ACPI_SUCCESS(acpi_get_handle(hkey_handle, t->name, &temp))) { + pr_warn("Thinkpad ACPI has no %s interface.\n", t->name); + return -EIO; + } + + if (!acpi_evalf(hkey_handle, &output, t->name, "dd", + state ? t->on_value : t->off_value)) + return -EIO; + + t->state = state; + return state; +} + +int tpacpi_led_set(int whichled, bool on) +{ + struct tp_led_table *t; + + if (whichled < 0 || whichled >= TPACPI_LED_MAX) + return -EINVAL; + + t = &led_tables[whichled]; + if (t->state < 0 || t->state == on) + return t->state; + return mute_led_on_off(t, on); +} +EXPORT_SYMBOL_GPL(tpacpi_led_set); + +static int mute_led_init(struct ibm_init_struct *iibm) +{ + acpi_handle temp; + int i; + + for (i = 0; i < TPACPI_LED_MAX; i++) { + struct tp_led_table *t = &led_tables[i]; + if (ACPI_SUCCESS(acpi_get_handle(hkey_handle, t->name, &temp))) + mute_led_on_off(t, false); + else + t->state = -ENODEV; + } + return 0; +} + +static void mute_led_exit(void) +{ + int i; + + for (i = 0; i < TPACPI_LED_MAX; i++) + tpacpi_led_set(i, false); +} + +static struct ibm_struct mute_led_driver_data = { + .name = "mute_led", + .exit = mute_led_exit, +}; + /**************************************************************************** **************************************************************************** * @@ -8768,6 +8854,10 @@ static struct ibm_init_struct ibms_init[] __initdata = { .init = fan_init, .data = &fan_driver_data, }, + { + .init = mute_led_init, + .data = &mute_led_driver_data, + }, }; static int __init set_ibm_param(const char *val, struct kernel_param *kp) diff --git a/include/linux/thinkpad_acpi.h b/include/linux/thinkpad_acpi.h new file mode 100644 index 000000000000..361de59a2285 --- /dev/null +++ b/include/linux/thinkpad_acpi.h @@ -0,0 +1,15 @@ +#ifndef __THINKPAD_ACPI_H__ +#define __THINKPAD_ACPI_H__ + +/* These two functions return 0 if success, or negative error code + (e g -ENODEV if no led present) */ + +enum { + TPACPI_LED_MUTE, + TPACPI_LED_MICMUTE, + TPACPI_LED_MAX, +}; + +int tpacpi_led_set(int whichled, bool on); + +#endif -- cgit v1.2.3 From 82fbb4f7b47683077e0716474d4f1ce65a2146cb Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 4 Sep 2011 22:04:49 +0200 Subject: ALSA: add DICE driver As a start point for further development, this is an incomplete driver for DICE devices: - only playback (so no clock source except the bus clock) - only 44.1 kHz - no MIDI - recovery after bus reset is slow - hwdep device is created, but not actually implemented Contains compilation fixes by Stefan Richter. Signed-off-by: Clemens Ladisch --- Documentation/ioctl/ioctl-number.txt | 1 + include/uapi/sound/Kbuild | 1 + include/uapi/sound/asound.h | 3 +- include/uapi/sound/firewire.h | 51 ++ sound/firewire/Kconfig | 13 + sound/firewire/Makefile | 2 + sound/firewire/dice.c | 1008 ++++++++++++++++++++++++++++++++++ 7 files changed, 1078 insertions(+), 1 deletion(-) create mode 100644 include/uapi/sound/firewire.h create mode 100644 sound/firewire/dice.c (limited to 'include') diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 2a5f0e14efa3..7cbfa3c4fc3d 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -138,6 +138,7 @@ Code Seq#(hex) Include File Comments 'H' C0-DF net/bluetooth/cmtp/cmtp.h conflict! 'H' C0-DF net/bluetooth/bnep/bnep.h conflict! 'H' F1 linux/hid-roccat.h +'H' F8-FA sound/firewire.h 'I' all linux/isdn.h conflict! 'I' 00-0F drivers/isdn/divert/isdn_divert.h conflict! 'I' 40-4F linux/mISDNif.h conflict! diff --git a/include/uapi/sound/Kbuild b/include/uapi/sound/Kbuild index 0f7d279ebde3..a7f27704f980 100644 --- a/include/uapi/sound/Kbuild +++ b/include/uapi/sound/Kbuild @@ -5,6 +5,7 @@ header-y += asound_fm.h header-y += compress_offload.h header-y += compress_params.h header-y += emu10k1.h +header-y += firewire.h header-y += hdsp.h header-y += hdspm.h header-y += sb16_csp.h diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index 041203f20f6d..9fc6219d3848 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -93,9 +93,10 @@ enum { SNDRV_HWDEP_IFACE_SB_RC, /* SB Extigy/Audigy2NX remote control */ SNDRV_HWDEP_IFACE_HDA, /* HD-audio */ SNDRV_HWDEP_IFACE_USB_STREAM, /* direct access to usb stream */ + SNDRV_HWDEP_IFACE_FW_DICE, /* TC DICE FireWire device */ /* Don't forget to change the following: */ - SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_USB_STREAM + SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_DICE }; struct snd_hwdep_info { diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h new file mode 100644 index 000000000000..e86131ca49e5 --- /dev/null +++ b/include/uapi/sound/firewire.h @@ -0,0 +1,51 @@ +#ifndef UAPI_SOUND_FIREWIRE_H_INCLUDED +#define UAPI_SOUND_FIREWIRE_H_INCLUDED + +#include + +/* events can be read() from the hwdep device */ + +#define SNDRV_FIREWIRE_EVENT_LOCK_STATUS 0x000010cc +#define SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION 0xd1ce004e + +struct snd_firewire_event_common { + unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */ +}; + +struct snd_firewire_event_lock_status { + unsigned int type; + unsigned int status; /* 0/1 = unlocked/locked */ +}; + +struct snd_firewire_event_dice_notification { + unsigned int type; + unsigned int notification; /* DICE-specific bits */ +}; + +union snd_firewire_event { + struct snd_firewire_event_common common; + struct snd_firewire_event_lock_status lock_status; + struct snd_firewire_event_dice_notification dice_notification; +}; + + +#define SNDRV_FIREWIRE_IOCTL_GET_INFO _IOR('H', 0xf8, struct snd_firewire_get_info) +#define SNDRV_FIREWIRE_IOCTL_LOCK _IO('H', 0xf9) +#define SNDRV_FIREWIRE_IOCTL_UNLOCK _IO('H', 0xfa) + +#define SNDRV_FIREWIRE_TYPE_DICE 1 +/* Fireworks, AV/C, RME, MOTU, ... */ + +struct snd_firewire_get_info { + unsigned int type; /* SNDRV_FIREWIRE_TYPE_xxx */ + unsigned int card; /* same as fw_cdev_get_info.card */ + unsigned char guid[8]; + char device_name[16]; /* device node in /dev */ +}; + +/* + * SNDRV_FIREWIRE_IOCTL_LOCK prevents the driver from streaming. + * Returns -EBUSY if the driver is already streaming. + */ + +#endif diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index ea063e1f8722..915330989412 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -11,6 +11,19 @@ config SND_FIREWIRE_LIB tristate depends on SND_PCM +config SND_DICE + tristate "DICE devices (EXPERIMENTAL)" + select SND_HWDEP + select SND_PCM + select SND_FIREWIRE_LIB + help + Say Y here to include support for many FireWire audio interfaces + based on the DICE chip family (DICE-II/Jr/Mini) from TC Applied + Technologies. + + To compile this driver as a module, choose M here: the module + will be called snd-dice. + config SND_FIREWIRE_SPEAKERS tristate "FireWire speakers" select SND_PCM diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile index 460179df5bb5..509955061d30 100644 --- a/sound/firewire/Makefile +++ b/sound/firewire/Makefile @@ -1,10 +1,12 @@ snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \ fcp.o cmp.o amdtp.o +snd-dice-objs := dice.o snd-firewire-speakers-objs := speakers.o snd-isight-objs := isight.o snd-scs1x-objs := scs1x.o obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o +obj-$(CONFIG_SND_DICE) += snd-dice.o obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o obj-$(CONFIG_SND_ISIGHT) += snd-isight.o obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c new file mode 100644 index 000000000000..ac71b2b94eec --- /dev/null +++ b/sound/firewire/dice.c @@ -0,0 +1,1008 @@ +/* + * TC Applied Technologies Digital Interface Communications Engine driver + * + * Copyright (c) Clemens Ladisch + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "amdtp.h" +#include "iso-resources.h" +#include "lib.h" + +#define DICE_PRIVATE_SPACE 0xffffe0000000uLL + +/* offset from DICE_PRIVATE_SPACE; offsets and sizes in quadlets */ +#define DICE_GLOBAL_OFFSET 0x00 +#define DICE_GLOBAL_SIZE 0x04 +#define DICE_TX_OFFSET 0x08 +#define DICE_TX_SIZE 0x0c +#define DICE_RX_OFFSET 0x10 +#define DICE_RX_SIZE 0x14 + +/* pointed to by DICE_GLOBAL_OFFSET */ +#define GLOBAL_OWNER 0x000 +#define OWNER_NO_OWNER 0xffff000000000000uLL +#define OWNER_NODE_SHIFT 48 +#define GLOBAL_NOTIFICATION 0x008 +#define NOTIFY_RX_CFG_CHG 0x00000001 +#define NOTIFY_TX_CFG_CHG 0x00000002 +#define NOTIFY_DUP_ISOC 0x00000004 +#define NOTIFY_BW_ERR 0x00000008 +#define NOTIFY_LOCK_CHG 0x00000010 +#define NOTIFY_CLOCK_ACCEPTED 0x00000020 +#define NOTIFY_INTERFACE_CHG 0x00000040 +#define NOTIFY_MESSAGE 0x00100000 +#define GLOBAL_NICK_NAME 0x00c +#define NICK_NAME_SIZE 64 +#define GLOBAL_CLOCK_SELECT 0x04c +#define CLOCK_SOURCE_MASK 0x000000ff +#define CLOCK_SOURCE_AES1 0x00000000 +#define CLOCK_SOURCE_AES2 0x00000001 +#define CLOCK_SOURCE_AES3 0x00000002 +#define CLOCK_SOURCE_AES4 0x00000003 +#define CLOCK_SOURCE_AES_ANY 0x00000004 +#define CLOCK_SOURCE_ADAT 0x00000005 +#define CLOCK_SOURCE_TDIF 0x00000006 +#define CLOCK_SOURCE_WC 0x00000007 +#define CLOCK_SOURCE_ARX1 0x00000008 +#define CLOCK_SOURCE_ARX2 0x00000009 +#define CLOCK_SOURCE_ARX3 0x0000000a +#define CLOCK_SOURCE_ARX4 0x0000000b +#define CLOCK_SOURCE_INTERNAL 0x0000000c +#define CLOCK_RATE_MASK 0x0000ff00 +#define CLOCK_RATE_32000 0x00000000 +#define CLOCK_RATE_44100 0x00000100 +#define CLOCK_RATE_48000 0x00000200 +#define CLOCK_RATE_88200 0x00000300 +#define CLOCK_RATE_96000 0x00000400 +#define CLOCK_RATE_176400 0x00000500 +#define CLOCK_RATE_192000 0x00000600 +#define CLOCK_RATE_ANY_LOW 0x00000700 +#define CLOCK_RATE_ANY_MID 0x00000800 +#define CLOCK_RATE_ANY_HIGH 0x00000900 +#define CLOCK_RATE_NONE 0x00000a00 +#define GLOBAL_ENABLE 0x050 +#define ENABLE 0x00000001 +#define GLOBAL_STATUS 0x054 +#define STATUS_SOURCE_LOCKED 0x00000001 +#define STATUS_RATE_CONFLICT 0x00000002 +#define STATUS_NOMINAL_RATE_MASK 0x0000ff00 +#define GLOBAL_EXTENDED_STATUS 0x058 +#define EXT_STATUS_AES1_LOCKED 0x00000001 +#define EXT_STATUS_AES2_LOCKED 0x00000002 +#define EXT_STATUS_AES3_LOCKED 0x00000004 +#define EXT_STATUS_AES4_LOCKED 0x00000008 +#define EXT_STATUS_ADAT_LOCKED 0x00000010 +#define EXT_STATUS_TDIF_LOCKED 0x00000020 +#define EXT_STATUS_ARX1_LOCKED 0x00000040 +#define EXT_STATUS_ARX2_LOCKED 0x00000080 +#define EXT_STATUS_ARX3_LOCKED 0x00000100 +#define EXT_STATUS_ARX4_LOCKED 0x00000200 +#define EXT_STATUS_WC_LOCKED 0x00000400 +#define EXT_STATUS_AES1_SLIP 0x00010000 +#define EXT_STATUS_AES2_SLIP 0x00020000 +#define EXT_STATUS_AES3_SLIP 0x00040000 +#define EXT_STATUS_AES4_SLIP 0x00080000 +#define EXT_STATUS_ADAT_SLIP 0x00100000 +#define EXT_STATUS_TDIF_SLIP 0x00200000 +#define EXT_STATUS_ARX1_SLIP 0x00400000 +#define EXT_STATUS_ARX2_SLIP 0x00800000 +#define EXT_STATUS_ARX3_SLIP 0x01000000 +#define EXT_STATUS_ARX4_SLIP 0x02000000 +#define EXT_STATUS_WC_SLIP 0x04000000 +#define GLOBAL_SAMPLE_RATE 0x05c +#define GLOBAL_VERSION 0x060 +#define GLOBAL_CLOCK_CAPABILITIES 0x064 +#define CLOCK_CAP_RATE_32000 0x00000001 +#define CLOCK_CAP_RATE_44100 0x00000002 +#define CLOCK_CAP_RATE_48000 0x00000004 +#define CLOCK_CAP_RATE_88200 0x00000008 +#define CLOCK_CAP_RATE_96000 0x00000010 +#define CLOCK_CAP_RATE_176400 0x00000020 +#define CLOCK_CAP_RATE_192000 0x00000040 +#define CLOCK_CAP_SOURCE_AES1 0x00010000 +#define CLOCK_CAP_SOURCE_AES2 0x00020000 +#define CLOCK_CAP_SOURCE_AES3 0x00040000 +#define CLOCK_CAP_SOURCE_AES4 0x00080000 +#define CLOCK_CAP_SOURCE_AES_ANY 0x00100000 +#define CLOCK_CAP_SOURCE_ADAT 0x00200000 +#define CLOCK_CAP_SOURCE_TDIF 0x00400000 +#define CLOCK_CAP_SOURCE_WC 0x00800000 +#define CLOCK_CAP_SOURCE_ARX1 0x01000000 +#define CLOCK_CAP_SOURCE_ARX2 0x02000000 +#define CLOCK_CAP_SOURCE_ARX3 0x04000000 +#define CLOCK_CAP_SOURCE_ARX4 0x08000000 +#define CLOCK_CAP_SOURCE_INTERNAL 0x10000000 +#define GLOBAL_CLOCK_SOURCE_NAMES 0x068 +#define CLOCK_SOURCE_NAMES_SIZE 256 + +/* pointed to by DICE_TX_OFFSET */ +#define TX_NUMBER 0x000 +#define TX_SIZE 0x004 +/* repeated TX_NUMBER times, offset by TX_SIZE quadlets */ +#define TX_ISOCHRONOUS 0x008 +#define TX_NUMBER_AUDIO 0x00c +#define TX_NUMBER_MIDI 0x010 +#define TX_SPEED 0x014 +#define TX_NAMES 0x018 +#define TX_NAMES_SIZE 256 +#define TX_AC3_CAPABILITIES 0x118 +#define TX_AC3_ENABLE 0x11c + +/* pointed to by DICE_RX_OFFSET */ +#define RX_NUMBER 0x000 +#define RX_SIZE 0x004 +/* repeated RX_NUMBER times, offset by RX_SIZE quadlets */ +#define RX_ISOCHRONOUS 0x008 +#define RX_SEQ_START 0x00c +#define RX_NUMBER_AUDIO 0x010 +#define RX_NUMBER_MIDI 0x014 +#define RX_NAMES 0x018 +#define RX_NAMES_SIZE 256 +#define RX_AC3_CAPABILITIES 0x118 +#define RX_AC3_ENABLE 0x11c + + +#define FIRMWARE_LOAD_SPACE 0xffffe0100000uLL + +/* offset from FIRMWARE_LOAD_SPACE */ +#define FIRMWARE_VERSION 0x000 +#define FIRMWARE_OPCODE 0x004 +#define OPCODE_MASK 0x00000fff +#define OPCODE_GET_IMAGE_DESC 0x00000000 +#define OPCODE_DELETE_IMAGE 0x00000001 +#define OPCODE_CREATE_IMAGE 0x00000002 +#define OPCODE_UPLOAD 0x00000003 +#define OPCODE_UPLOAD_STAT 0x00000004 +#define OPCODE_RESET_IMAGE 0x00000005 +#define OPCODE_TEST_ACTION 0x00000006 +#define OPCODE_GET_RUNNING_IMAGE_VINFO 0x0000000a +#define OPCODE_EXECUTE 0x80000000 +#define FIRMWARE_RETURN_STATUS 0x008 +#define FIRMWARE_PROGRESS 0x00c +#define PROGRESS_CURR_MASK 0x00000fff +#define PROGRESS_MAX_MASK 0x00fff000 +#define PROGRESS_TOUT_MASK 0x0f000000 +#define PROGRESS_FLAG 0x80000000 +#define FIRMWARE_CAPABILITIES 0x010 +#define FL_CAP_AUTOERASE 0x00000001 +#define FL_CAP_PROGRESS 0x00000002 +#define FIRMWARE_DATA 0x02c +#define TEST_CMD_POKE 0x00000001 +#define TEST_CMD_PEEK 0x00000002 +#define CMD_GET_AVS_CNT 0x00000003 +#define CMD_CLR_AVS_CNT 0x00000004 +#define CMD_SET_MODE 0x00000005 +#define CMD_SET_MIDIBP 0x00000006 +#define CMD_GET_AVSPHASE 0x00000007 +#define CMD_ENABLE_BNC_SYNC 0x00000008 +#define CMD_PULSE_BNC_SYNC 0x00000009 +#define CMD_EMUL_SLOW_CMD 0x0000000a +#define FIRMWARE_TEST_DELAY 0xfd8 +#define FIRMWARE_TEST_BUF 0xfdc + + +/* EAP */ +#define EAP_PRIVATE_SPACE 0xffffe0200000uLL + +#define EAP_CAPABILITY_OFFSET 0x000 +#define EAP_CAPABILITY_SIZE 0x004 +/* ... */ + +#define EAP_ROUTER_CAPS 0x000 +#define ROUTER_EXPOSED 0x00000001 +#define ROUTER_READ_ONLY 0x00000002 +#define ROUTER_FLASH 0x00000004 +#define MAX_ROUTES_MASK 0xffff0000 +#define EAP_MIXER_CAPS 0x004 +#define MIXER_EXPOSED 0x00000001 +#define MIXER_READ_ONLY 0x00000002 +#define MIXER_FLASH 0x00000004 +#define MIXER_IN_DEV_MASK 0x000000f0 +#define MIXER_OUT_DEV_MASK 0x00000f00 +#define MIXER_INPUTS_MASK 0x00ff0000 +#define MIXER_OUTPUTS_MASK 0xff000000 +#define EAP_GENERAL_CAPS 0x008 +#define GENERAL_STREAM_CONFIG 0x00000001 +#define GENERAL_FLASH 0x00000002 +#define GENERAL_PEAK 0x00000004 +#define GENERAL_MAX_TX_STREAMS_MASK 0x000000f0 +#define GENERAL_MAX_RX_STREAMS_MASK 0x00000f00 +#define GENERAL_STREAM_CONFIG_FLASH 0x00001000 +#define GENERAL_CHIP_MASK 0x00ff0000 +#define GENERAL_CHIP_DICE_II 0x00000000 +#define GENERAL_CHIP_DICE_MINI 0x00010000 +#define GENERAL_CHIP_DICE_JR 0x00020000 + + +struct dice { + struct snd_card *card; + struct fw_unit *unit; + struct mutex mutex; + unsigned int global_offset; + unsigned int rx_offset; + struct fw_address_handler notification_handler; + int owner_generation; + bool global_enabled; + bool stream_running; + struct snd_pcm_substream *pcm; + struct fw_iso_resources resources; + struct amdtp_out_stream stream; +}; + +MODULE_DESCRIPTION("DICE driver"); +MODULE_AUTHOR("Clemens Ladisch "); +MODULE_LICENSE("GPL v2"); + +static inline u64 global_address(struct dice *dice, unsigned int offset) +{ + return DICE_PRIVATE_SPACE + dice->global_offset + offset; +} + +// TODO: rx index +static inline u64 rx_address(struct dice *dice, unsigned int offset) +{ + return DICE_PRIVATE_SPACE + dice->rx_offset + offset; +} + +static int dice_owner_set(struct dice *dice) +{ + struct fw_device *device = fw_parent_device(dice->unit); + __be64 *buffer; + int rcode, err, errors = 0; + + buffer = kmalloc(2 * 8, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + for (;;) { + buffer[0] = cpu_to_be64(OWNER_NO_OWNER); + buffer[1] = cpu_to_be64( + ((u64)device->card->node_id << OWNER_NODE_SHIFT) | + dice->notification_handler.offset); + + dice->owner_generation = device->generation; + smp_rmb(); /* node_id vs. generation */ + rcode = fw_run_transaction(device->card, + TCODE_LOCK_COMPARE_SWAP, + device->node_id, + dice->owner_generation, + device->max_speed, + global_address(dice, GLOBAL_OWNER), + buffer, 2 * 8); + + if (rcode == RCODE_COMPLETE) { + if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER)) { + err = 0; + } else { + dev_err(&dice->unit->device, + "device is already in use\n"); + err = -EBUSY; + } + break; + } + if (rcode_is_permanent_error(rcode) || ++errors >= 3) { + dev_err(&dice->unit->device, + "setting device owner failed: %s\n", + fw_rcode_string(rcode)); + err = -EIO; + break; + } + msleep(20); + } + + kfree(buffer); + + return err; +} + +static int dice_owner_update(struct dice *dice) +{ + struct fw_device *device = fw_parent_device(dice->unit); + __be64 *buffer; + int rcode, err, errors = 0; + + if (dice->owner_generation == -1) + return 0; + + buffer = kmalloc(2 * 8, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + for (;;) { + buffer[0] = cpu_to_be64(OWNER_NO_OWNER); + buffer[1] = cpu_to_be64( + ((u64)device->card->node_id << OWNER_NODE_SHIFT) | + dice->notification_handler.offset); + + dice->owner_generation = device->generation; + smp_rmb(); /* node_id vs. generation */ + rcode = fw_run_transaction(device->card, + TCODE_LOCK_COMPARE_SWAP, + device->node_id, + dice->owner_generation, + device->max_speed, + global_address(dice, GLOBAL_OWNER), + buffer, 2 * 8); + + if (rcode == RCODE_COMPLETE) { + if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER)) { + err = 0; + } else { + dev_err(&dice->unit->device, + "device is already in use\n"); + err = -EBUSY; + } + break; + } + if (rcode == RCODE_GENERATION) { + err = 0; /* try again later */ + break; + } + if (rcode_is_permanent_error(rcode) || ++errors >= 3) { + dev_err(&dice->unit->device, + "setting device owner failed: %s\n", + fw_rcode_string(rcode)); + err = -EIO; + break; + } + msleep(20); + } + + kfree(buffer); + + if (err < 0) + dice->owner_generation = -1; + + return err; +} + +static void dice_owner_clear(struct dice *dice) +{ + struct fw_device *device = fw_parent_device(dice->unit); + __be64 *buffer; + int rcode, errors = 0; + + buffer = kmalloc(2 * 8, GFP_KERNEL); + if (!buffer) + return; + + for (;;) { + buffer[0] = cpu_to_be64( + ((u64)device->card->node_id << OWNER_NODE_SHIFT) | + dice->notification_handler.offset); + buffer[1] = cpu_to_be64(OWNER_NO_OWNER); + + rcode = fw_run_transaction(device->card, + TCODE_LOCK_COMPARE_SWAP, + device->node_id, + dice->owner_generation, + device->max_speed, + global_address(dice, GLOBAL_OWNER), + buffer, 2 * 8); + + if (rcode == RCODE_COMPLETE) + break; + if (rcode == RCODE_GENERATION) + break; + if (rcode_is_permanent_error(rcode) || ++errors >= 3) { + dev_err(&dice->unit->device, + "clearing device owner failed: %s\n", + fw_rcode_string(rcode)); + break; + } + msleep(20); + } + + kfree(buffer); + + dice->owner_generation = -1; +} + +static int dice_enable_set(struct dice *dice) +{ + struct fw_device *device = fw_parent_device(dice->unit); + __be32 value; + int rcode, err, errors = 0; + + value = cpu_to_be32(ENABLE); + for (;;) { + rcode = fw_run_transaction(device->card, + TCODE_WRITE_QUADLET_REQUEST, + device->node_id, + dice->owner_generation, + device->max_speed, + global_address(dice, GLOBAL_ENABLE), + &value, 4); + if (rcode == RCODE_COMPLETE) { + dice->global_enabled = true; + err = 0; + break; + } + if (rcode == RCODE_GENERATION) { + err = -EAGAIN; + break; + } + if (rcode_is_permanent_error(rcode) || ++errors >= 3) { + dev_err(&dice->unit->device, + "device enabling failed: %s\n", + fw_rcode_string(rcode)); + err = -EIO; + break; + } + msleep(20); + } + + return err; +} + +static void dice_enable_clear(struct dice *dice) +{ + struct fw_device *device = fw_parent_device(dice->unit); + __be32 value; + int rcode, errors = 0; + + value = 0; + for (;;) { + rcode = fw_run_transaction(device->card, + TCODE_WRITE_QUADLET_REQUEST, + device->node_id, + dice->owner_generation, + device->max_speed, + global_address(dice, GLOBAL_ENABLE), + &value, 4); + if (rcode == RCODE_COMPLETE || + rcode == RCODE_GENERATION) + break; + if (rcode_is_permanent_error(rcode) || ++errors >= 3) { + dev_err(&dice->unit->device, + "device disabling failed: %s\n", + fw_rcode_string(rcode)); + break; + } + msleep(20); + } + dice->global_enabled = false; +} + +static void dice_notification(struct fw_card *card, struct fw_request *request, + int tcode, int destination, int source, + int generation, unsigned long long offset, + void *data, size_t length, void *callback_data) +{ + struct dice *dice = callback_data; + + if (tcode != TCODE_WRITE_QUADLET_REQUEST) { + fw_send_response(card, request, RCODE_TYPE_ERROR); + return; + } + if ((offset & 3) != 0) { + fw_send_response(card, request, RCODE_ADDRESS_ERROR); + return; + } + dev_info(&dice->unit->device, + "notification: %08x\n", be32_to_cpup(data)); + fw_send_response(card, request, RCODE_COMPLETE); +} + +static int dice_open(struct snd_pcm_substream *substream) +{ + static const struct snd_pcm_hardware hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + .formats = AMDTP_OUT_PCM_FORMAT_BITS, + .rates = SNDRV_PCM_RATE_44100, + .rate_min = 44100, + .rate_max = 44100, + .buffer_bytes_max = 16 * 1024 * 1024, + .period_bytes_min = 1, + .period_bytes_max = UINT_MAX, + .periods_min = 1, + .periods_max = UINT_MAX, + }; + struct dice *dice = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + __be32 number_audio, number_midi; + int err; + + err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, + rx_address(dice, RX_NUMBER_AUDIO), + &number_audio, 4); + if (err < 0) + return err; + err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, + rx_address(dice, RX_NUMBER_MIDI), + &number_midi, 4); + if (err < 0) + return err; + + runtime->hw = hardware; + runtime->hw.channels_min = be32_to_cpu(number_audio); + runtime->hw.channels_max = be32_to_cpu(number_audio); + + amdtp_out_stream_set_rate(&dice->stream, 44100); + amdtp_out_stream_set_pcm(&dice->stream, be32_to_cpu(number_audio)); + amdtp_out_stream_set_midi(&dice->stream, be32_to_cpu(number_midi)); + + err = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_PERIOD_TIME, + 5000, 8192000); + if (err < 0) + return err; + + err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + if (err < 0) + return err; + + return 0; +} + +static int dice_close(struct snd_pcm_substream *substream) +{ + return 0; +} + +static void dice_stop_stream(struct dice *dice) +{ + __be32 channel; + + if (dice->stream_running) { + dice_enable_clear(dice); + + amdtp_out_stream_stop(&dice->stream); + + channel = cpu_to_be32((u32)-1); + snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + rx_address(dice, RX_ISOCHRONOUS), + &channel, 4); + + fw_iso_resources_free(&dice->resources); + + dice->stream_running = false; + } +} + +static int dice_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct dice *dice = substream->private_data; + int err; + + mutex_lock(&dice->mutex); + dice_stop_stream(dice); + mutex_unlock(&dice->mutex); + + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + goto error; + + amdtp_out_stream_set_pcm_format(&dice->stream, + params_format(hw_params)); + + return 0; + +error: + return err; +} + +static int dice_hw_free(struct snd_pcm_substream *substream) +{ + struct dice *dice = substream->private_data; + + mutex_lock(&dice->mutex); + dice_stop_stream(dice); + mutex_unlock(&dice->mutex); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int dice_prepare(struct snd_pcm_substream *substream) +{ + struct dice *dice = substream->private_data; + struct fw_device *device = fw_parent_device(dice->unit); + __be32 channel; + int err; + + mutex_lock(&dice->mutex); + + if (amdtp_out_streaming_error(&dice->stream)) + dice_stop_stream(dice); + + if (!dice->stream_running) { + err = fw_iso_resources_allocate(&dice->resources, + amdtp_out_stream_get_max_payload(&dice->stream), + device->max_speed); + if (err < 0) + goto error; + + //TODO: RX_SEQ_START + channel = cpu_to_be32(dice->resources.channel); + err = snd_fw_transaction(dice->unit, + TCODE_WRITE_QUADLET_REQUEST, + rx_address(dice, RX_ISOCHRONOUS), + &channel, 4); + if (err < 0) + goto err_resources; + + err = amdtp_out_stream_start(&dice->stream, + dice->resources.channel, + device->max_speed); + if (err < 0) + goto err_resources; + + err = dice_enable_set(dice); + if (err < 0) + goto err_stream; + + dice->stream_running = true; + } + + mutex_unlock(&dice->mutex); + + amdtp_out_stream_pcm_prepare(&dice->stream); + + return 0; + +err_stream: + amdtp_out_stream_stop(&dice->stream); +err_resources: + fw_iso_resources_free(&dice->resources); +error: + mutex_unlock(&dice->mutex); + + return err; +} + +static int dice_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct dice *dice = substream->private_data; + struct snd_pcm_substream *pcm; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + pcm = substream; + break; + case SNDRV_PCM_TRIGGER_STOP: + pcm = NULL; + break; + default: + return -EINVAL; + } + amdtp_out_stream_pcm_trigger(&dice->stream, pcm); + + return 0; +} + +static snd_pcm_uframes_t dice_pointer(struct snd_pcm_substream *substream) +{ + struct dice *dice = substream->private_data; + + return amdtp_out_stream_pcm_pointer(&dice->stream); +} + +static int dice_create_pcm(struct dice *dice) +{ + static struct snd_pcm_ops ops = { + .open = dice_open, + .close = dice_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = dice_hw_params, + .hw_free = dice_hw_free, + .prepare = dice_prepare, + .trigger = dice_trigger, + .pointer = dice_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; + __be32 clock; + struct snd_pcm *pcm; + int err; + + clock = cpu_to_be32(CLOCK_SOURCE_ARX1 | CLOCK_RATE_44100); + err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + global_address(dice, GLOBAL_CLOCK_SELECT), + &clock, 4); + if (err < 0) + return err; + + err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm); + if (err < 0) + return err; + pcm->private_data = dice; + strcpy(pcm->name, dice->card->shortname); + dice->pcm = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + dice->pcm->ops = &ops; + + return 0; +} + +// TODO: implement these + +static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, + long count, loff_t *offset) +{ + return -EIO; +} + +static int dice_hwdep_open(struct snd_hwdep *hwdep, struct file *file) +{ + return -EIO; +} + +static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file) +{ + return 0; +} + +static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, + poll_table *wait) +{ + return POLLERR | POLLHUP; +} + +static int dice_hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return -EIO; +} + +static int dice_create_hwdep(struct dice *dice) +{ + static const struct snd_hwdep_ops ops = { + .read = dice_hwdep_read, + .open = dice_hwdep_open, + .release = dice_hwdep_release, + .poll = dice_hwdep_poll, + .ioctl = dice_hwdep_ioctl, + .ioctl_compat = dice_hwdep_ioctl, + }; + struct snd_hwdep *hwdep; + int err; + + err = snd_hwdep_new(dice->card, "DICE", 0, &hwdep); + if (err < 0) + return err; + strcpy(hwdep->name, "DICE"); + hwdep->iface = SNDRV_HWDEP_IFACE_FW_DICE; + hwdep->ops = ops; + hwdep->private_data = dice; + hwdep->exclusive = true; + + return 0; +} + +static void dice_card_free(struct snd_card *card) +{ + struct dice *dice = card->private_data; + + amdtp_out_stream_destroy(&dice->stream); + fw_core_remove_address_handler(&dice->notification_handler); + mutex_destroy(&dice->mutex); +} + +static int dice_init_offsets(struct dice *dice) +{ + __be32 pointers[6]; + unsigned int global_size, rx_size; + int err; + + err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, + DICE_PRIVATE_SPACE, &pointers, 6 * 4); + if (err < 0) + return err; + + dice->global_offset = be32_to_cpu(pointers[0]) * 4; + global_size = be32_to_cpu(pointers[1]); + dice->rx_offset = be32_to_cpu(pointers[4]) * 4; + rx_size = be32_to_cpu(pointers[5]); + + /* some sanity checks to ensure that we actually have a DICE */ + if (dice->global_offset < 10 * 4 || global_size < 0x168 / 4 || + dice->rx_offset < 10 * 4 || rx_size < 0x120 / 4) { + dev_err(&dice->unit->device, "invalid register pointers\n"); + return -ENXIO; + } + + return 0; +} + +static void dice_card_strings(struct dice *dice) +{ + struct snd_card *card = dice->card; + struct fw_device *dev = fw_parent_device(dice->unit); + char vendor[32], model[32]; + unsigned int i; + int err; + + strcpy(card->driver, "DICE"); + + strcpy(card->shortname, "DICE"); + BUILD_BUG_ON(NICK_NAME_SIZE < sizeof(card->shortname)); + err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, + global_address(dice, GLOBAL_NICK_NAME), + card->shortname, sizeof(card->shortname)); + if (err >= 0) { + /* DICE strings are returned in "always-wrong" endianness */ + BUILD_BUG_ON(sizeof(card->shortname) % 4 != 0); + for (i = 0; i < sizeof(card->shortname); i += 4) + swab32s((u32 *)&card->shortname[i]); + card->shortname[sizeof(card->shortname) - 1] = '\0'; + } + + strcpy(vendor, "?"); + fw_csr_string(dev->config_rom + 5, CSR_VENDOR, vendor, sizeof(vendor)); + strcpy(model, "?"); + fw_csr_string(dice->unit->directory, CSR_MODEL, model, sizeof(model)); + snprintf(card->longname, sizeof(card->longname), + "%s %s, GUID %08x%08x at %s, S%d", + vendor, model, dev->config_rom[3], dev->config_rom[4], + dev_name(&dice->unit->device), 100 << dev->max_speed); + + strcpy(card->mixername, "DICE"); +} + +static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) +{ + struct snd_card *card; + struct dice *dice; + int err; + + err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*dice), &card); + if (err < 0) + return err; + snd_card_set_dev(card, &unit->device); + + dice = card->private_data; + dice->card = card; + mutex_init(&dice->mutex); + dice->unit = unit; + + err = dice_init_offsets(dice); + if (err < 0) + goto err_mutex; + + dice->notification_handler.length = 4; + dice->notification_handler.address_callback = dice_notification; + dice->notification_handler.callback_data = dice; + err = fw_core_add_address_handler(&dice->notification_handler, + &fw_high_memory_region); + if (err < 0) + goto err_mutex; + + err = fw_iso_resources_init(&dice->resources, unit); + if (err < 0) + goto err_notification_handler; + dice->resources.channels_mask = 0x00000000ffffffffuLL; + + err = amdtp_out_stream_init(&dice->stream, unit, CIP_NONBLOCKING); + if (err < 0) + goto err_resources; + + err = dice_owner_set(dice); + if (err < 0) + goto err_stream; + + card->private_free = dice_card_free; + + dice_card_strings(dice); + + err = dice_create_pcm(dice); + if (err < 0) + goto error; + + err = dice_create_hwdep(dice); + if (err < 0) + goto error; + + err = snd_card_register(card); + if (err < 0) + goto error; + + dev_set_drvdata(&unit->device, dice); + + return 0; + +err_stream: + amdtp_out_stream_destroy(&dice->stream); +err_resources: + fw_iso_resources_destroy(&dice->resources); +err_notification_handler: + fw_core_remove_address_handler(&dice->notification_handler); +err_mutex: + mutex_destroy(&dice->mutex); +error: + snd_card_free(card); + return err; +} + +static void dice_remove(struct fw_unit *unit) +{ + struct dice *dice = dev_get_drvdata(&unit->device); + + snd_card_disconnect(dice->card); + + mutex_lock(&dice->mutex); + amdtp_out_stream_pcm_abort(&dice->stream); + dice_stop_stream(dice); + dice_owner_clear(dice); + mutex_unlock(&dice->mutex); + + snd_card_free_when_closed(dice->card); +} + +static void dice_bus_reset(struct fw_unit *unit) +{ + struct dice *dice = dev_get_drvdata(&unit->device); + + mutex_lock(&dice->mutex); + /* + * XXX is the following true? + * On a bus reset, the DICE firmware disables streaming and then goes + * off contemplating its own navel for hundreds of milliseconds before + * it can react to any of our attempts to reenable streaming. This + * means that we lose synchronization anyway, so we force our streams + * to stop so that the application can restart them in an orderly + * manner. + */ + dice_owner_update(dice); + amdtp_out_stream_pcm_abort(&dice->stream); + dice_stop_stream(dice); + mutex_unlock(&dice->mutex); +} + +#define TC_OUI 0x000166 +#define DICE_INTERFACE 0x000001 + +static const struct ieee1394_device_id dice_id_table[] = { + { + .match_flags = IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION, + .specifier_id = TC_OUI, + .version = DICE_INTERFACE, + }, + { } +}; +MODULE_DEVICE_TABLE(ieee1394, dice_id_table); + +static struct fw_driver dice_driver = { + .driver = { + .owner = THIS_MODULE, + .name = KBUILD_MODNAME, + .bus = &fw_bus_type, + }, + .probe = dice_probe, + .update = dice_bus_reset, + .remove = dice_remove, + .id_table = dice_id_table, +}; + +static int __init alsa_dice_init(void) +{ + return driver_register(&dice_driver.driver); +} + +static void __exit alsa_dice_exit(void) +{ + driver_unregister(&dice_driver.driver); +} + +module_init(alsa_dice_init); +module_exit(alsa_dice_exit); -- cgit v1.2.3 From 3d8c8bc0250f7cb11f887691b7473b51adcd2bcb Mon Sep 17 00:00:00 2001 From: Brian Austin Date: Thu, 17 Oct 2013 11:03:33 -0500 Subject: ASoC: cs42l73: Add platform data support for cs42l73 codec Add support for RST GPIO and Charge Pump Freq in platform data Signed-off-by: Brian Austin Signed-off-by: Mark Brown --- include/sound/cs42l73.h | 22 ++++++++++++++++++++ sound/soc/codecs/cs42l73.c | 51 ++++++++++++++++++++++++++++++---------------- sound/soc/codecs/cs42l73.h | 1 + 3 files changed, 56 insertions(+), 18 deletions(-) create mode 100644 include/sound/cs42l73.h (limited to 'include') diff --git a/include/sound/cs42l73.h b/include/sound/cs42l73.h new file mode 100644 index 000000000000..f354be4cdc9e --- /dev/null +++ b/include/sound/cs42l73.h @@ -0,0 +1,22 @@ +/* + * linux/sound/cs42l73.h -- Platform data for CS42L73 + * + * Copyright (c) 2012 Cirrus Logic Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __CS42L73_H +#define __CS42L73_H + +struct cs42l73_platform_data { + /* RST GPIO */ + unsigned int reset_gpio; + unsigned int chgfreq; + int jack_detection; + unsigned int mclk_freq; +}; + +#endif /* __CS42L73_H */ diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c index 3b20c86cdb01..db9d39604d68 100644 --- a/sound/soc/codecs/cs42l73.c +++ b/sound/soc/codecs/cs42l73.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,7 @@ #include #include #include +#include #include "cs42l73.h" struct sp_config { @@ -35,6 +37,7 @@ struct sp_config { u32 srate; }; struct cs42l73_private { + struct cs42l73_platform_data pdata; struct sp_config config[3]; struct regmap *regmap; u32 sysclk; @@ -310,15 +313,6 @@ static const struct soc_enum ng_delay_enum = SOC_ENUM_SINGLE(CS42L73_NGCAB, 0, ARRAY_SIZE(cs42l73_ng_delay_text), cs42l73_ng_delay_text); -static const char * const charge_pump_freq_text[] = { - "0", "1", "2", "3", "4", - "5", "6", "7", "8", "9", - "10", "11", "12", "13", "14", "15" }; - -static const struct soc_enum charge_pump_enum = - SOC_ENUM_SINGLE(CS42L73_CPFCHC, 4, - ARRAY_SIZE(charge_pump_freq_text), charge_pump_freq_text); - static const char * const cs42l73_mono_mix_texts[] = { "Left", "Right", "Mono Mix"}; @@ -511,8 +505,6 @@ static const struct snd_kcontrol_new cs42l73_snd_controls[] = { SOC_SINGLE("NG Threshold", CS42L73_NGCAB, 2, 7, 0), SOC_ENUM("NG Delay", ng_delay_enum), - SOC_ENUM("Charge Pump Frequency", charge_pump_enum), - SOC_DOUBLE_R_TLV("XSP-IP Volume", CS42L73_XSPAIPAA, CS42L73_XSPBIPBA, 0, 0x3F, 1, attn_tlv), @@ -1367,11 +1359,16 @@ static int cs42l73_probe(struct snd_soc_codec *codec) return ret; } - regcache_cache_only(cs42l73->regmap, true); - cs42l73_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - cs42l73->mclksel = CS42L73_CLKID_MCLK1; /* MCLK1 as master clk */ + /* Set Charge Pump Frequency */ + if (cs42l73->pdata.chgfreq) + snd_soc_update_bits(codec, CS42L73_CPFCHC, + CS42L73_CHARGEPUMP_MASK, + cs42l73->pdata.chgfreq << 4); + + /* MCLK1 as master clk */ + cs42l73->mclksel = CS42L73_CLKID_MCLK1; cs42l73->mclk = 0; return ret; @@ -1415,6 +1412,7 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client, const struct i2c_device_id *id) { struct cs42l73_private *cs42l73; + struct cs42l73_platform_data *pdata = dev_get_platdata(&i2c_client->dev); int ret; unsigned int devid = 0; unsigned int reg; @@ -1426,14 +1424,32 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client, return -ENOMEM; } - i2c_set_clientdata(i2c_client, cs42l73); - cs42l73->regmap = devm_regmap_init_i2c(i2c_client, &cs42l73_regmap); if (IS_ERR(cs42l73->regmap)) { ret = PTR_ERR(cs42l73->regmap); dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); return ret; } + + if (pdata) + cs42l73->pdata = *pdata; + + i2c_set_clientdata(i2c_client, cs42l73); + + if (cs42l73->pdata.reset_gpio) { + ret = gpio_request_one(cs42l73->pdata.reset_gpio, + GPIOF_OUT_INIT_HIGH, "CS42L73 /RST"); + if (ret < 0) { + dev_err(&i2c_client->dev, "Failed to request /RST %d: %d\n", + cs42l73->pdata.reset_gpio, ret); + return ret; + } + gpio_set_value_cansleep(cs42l73->pdata.reset_gpio, 0); + gpio_set_value_cansleep(cs42l73->pdata.reset_gpio, 1); + } + + regcache_cache_bypass(cs42l73->regmap, true); + /* initialize codec */ ret = regmap_read(cs42l73->regmap, CS42L73_DEVID_AB, ®); devid = (reg & 0xFF) << 12; @@ -1444,7 +1460,6 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client, ret = regmap_read(cs42l73->regmap, CS42L73_DEVID_E, ®); devid |= (reg & 0xF0) >> 4; - if (devid != CS42L73_DEVID) { ret = -ENODEV; dev_err(&i2c_client->dev, @@ -1462,7 +1477,7 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client, dev_info(&i2c_client->dev, "Cirrus Logic CS42L73, Revision: %02X\n", reg & 0xFF); - regcache_cache_only(cs42l73->regmap, true); + regcache_cache_bypass(cs42l73->regmap, false); ret = snd_soc_register_codec(&i2c_client->dev, &soc_codec_dev_cs42l73, cs42l73_dai, diff --git a/sound/soc/codecs/cs42l73.h b/sound/soc/codecs/cs42l73.h index f30a4c4d62e6..4f83d39496a8 100644 --- a/sound/soc/codecs/cs42l73.h +++ b/sound/soc/codecs/cs42l73.h @@ -159,6 +159,7 @@ #define THMOVLD_115C 2 #define THMOVLD_098C 3 +#define CS42L73_CHARGEPUMP_MASK (0xF0) /* CS42L73_ASPC, CS42L73_XSPC, CS42L73_VSPC */ #define SP_3ST (1 << 7) -- cgit v1.2.3 From 6833c452c2fb47353566aa705d68541c6045c796 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 16 Oct 2013 22:05:26 -0700 Subject: ASoC: add snd_soc_of_get_dai_name() default of_xlate Current snd_soc_of_get_dai_name() needs .of_xlate_dai_name() callback on each component drivers. But required behavior on almost all these drivers is just returns its indexed driver's name. This patch adds this feature as default behavior. .of_xlate_dai_name() can overwrite it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/soc.h | 4 +++- sound/soc/soc-core.c | 28 ++++++++++++++++++++++++---- 2 files changed, 27 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index b13eecbaea78..6ed3dc0773cc 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -644,10 +644,12 @@ struct snd_soc_component_driver { struct snd_soc_component { const char *name; int id; - int num_dai; struct device *dev; struct list_head list; + struct snd_soc_dai_driver *dai_drv; + int num_dai; + const struct snd_soc_component_driver *driver; }; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 67cfb5f5ca96..0860a7f11299 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4023,6 +4023,7 @@ __snd_soc_register_component(struct device *dev, cmpnt->dev = dev; cmpnt->driver = cmpnt_drv; + cmpnt->dai_drv = dai_drv; cmpnt->num_dai = num_dai; /* @@ -4548,12 +4549,31 @@ int snd_soc_of_get_dai_name(struct device_node *of_node, if (pos->dev->of_node != args.np) continue; - if (!pos->driver->of_xlate_dai_name) { - ret = -ENOSYS; - break; + if (pos->driver->of_xlate_dai_name) { + ret = pos->driver->of_xlate_dai_name(pos, &args, dai_name); + } else { + int id = -1; + + switch (args.args_count) { + case 0: + id = 0; /* same as dai_drv[0] */ + break; + case 1: + id = args.args[0]; + break; + default: + /* not supported */ + break; + } + + if (id < 0 || id >= pos->num_dai) { + ret = -EINVAL; + } else { + *dai_name = pos->dai_drv[id].name; + ret = 0; + } } - ret = pos->driver->of_xlate_dai_name(pos, &args, dai_name); break; } mutex_unlock(&client_mutex); -- cgit v1.2.3 From c0de42bf595238e9dd593405ebc2992cc8470732 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 8 Oct 2013 15:07:59 +0200 Subject: ASoC: dmaengine-pcm: Add support for querying DMA capabilities Currently each platform making use the the generic dmaengine PCM driver still needs to provide a custom snd_pcm_hardware struct which specifies the capabilities of the DMA controller, e.g. the maximum period size that can be supported. This patch adds code which uses the newly introduced dma_get_slave_caps() API to query this information from the dmaengine driver. The new code path will only be taken if the 'pcm_hardware' field of the snd_dmaengine_pcm_config struct is NULL. The patch also introduces a new 'fifo_size' field to the snd_dmaengine_dai_dma_data struct which is used to initialize the snd_pcm_hardware 'fifo_size' field and needs to be set by the DAI driver. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/dmaengine_pcm.h | 2 ++ sound/soc/soc-generic-dmaengine-pcm.c | 55 ++++++++++++++++++++++++++++------- 2 files changed, 47 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index f11c35cd5532..83b2c3e95a1a 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -61,6 +61,7 @@ struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream) * @slave_id: Slave requester id for the DMA channel. * @filter_data: Custom DMA channel filter data, this will usually be used when * requesting the DMA channel. + * @fifo_size: FIFO size of the DAI controller in bytes */ struct snd_dmaengine_dai_dma_data { dma_addr_t addr; @@ -68,6 +69,7 @@ struct snd_dmaengine_dai_dma_data { u32 maxburst; unsigned int slave_id; void *filter_data; + unsigned int fifo_size; }; void snd_dmaengine_pcm_set_config_from_dai_data( diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index e29ec3cd84b1..c39e19e84c8a 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -36,6 +36,15 @@ static struct dmaengine_pcm *soc_platform_to_pcm(struct snd_soc_platform *p) return container_of(p, struct dmaengine_pcm, platform); } +static struct device *dmaengine_dma_dev(struct dmaengine_pcm *pcm, + struct snd_pcm_substream *substream) +{ + if (!pcm->chan[substream->stream]) + return NULL; + + return pcm->chan[substream->stream]->device->dev; +} + /** * snd_dmaengine_pcm_prepare_slave_config() - Generic prepare_slave_config callback * @substream: PCM substream @@ -92,28 +101,54 @@ static int dmaengine_pcm_hw_params(struct snd_pcm_substream *substream, return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); } -static int dmaengine_pcm_open(struct snd_pcm_substream *substream) +static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform); + struct device *dma_dev = dmaengine_dma_dev(pcm, substream); struct dma_chan *chan = pcm->chan[substream->stream]; + struct snd_dmaengine_dai_dma_data *dma_data; + struct dma_slave_caps dma_caps; + struct snd_pcm_hardware hw; int ret; - ret = snd_soc_set_runtime_hwparams(substream, + if (pcm->config->pcm_hardware) + return snd_soc_set_runtime_hwparams(substream, pcm->config->pcm_hardware); - if (ret) - return ret; - return snd_dmaengine_pcm_open(substream, chan); + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + + memset(&hw, 0, sizeof(hw)); + hw.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED; + hw.periods_min = 2; + hw.periods_max = UINT_MAX; + hw.period_bytes_min = 256; + hw.period_bytes_max = dma_get_max_seg_size(dma_dev); + hw.buffer_bytes_max = SIZE_MAX; + hw.fifo_size = dma_data->fifo_size; + + ret = dma_get_slave_caps(chan, &dma_caps); + if (ret == 0) { + if (dma_caps.cmd_pause) + hw.info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME; + } + + return snd_soc_set_runtime_hwparams(substream, &hw); } -static struct device *dmaengine_dma_dev(struct dmaengine_pcm *pcm, - struct snd_pcm_substream *substream) +static int dmaengine_pcm_open(struct snd_pcm_substream *substream) { - if (!pcm->chan[substream->stream]) - return NULL; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform); + struct dma_chan *chan = pcm->chan[substream->stream]; + int ret; - return pcm->chan[substream->stream]->device->dev; + ret = dmaengine_pcm_set_runtime_hwparams(substream); + if (ret) + return ret; + + return snd_dmaengine_pcm_open(substream, chan); } static void dmaengine_pcm_free(struct snd_pcm *pcm) -- cgit v1.2.3 From ecfc0c04f236f1e2a95094792ec10cf27be39f7c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 17 Oct 2013 21:13:19 +0100 Subject: ASoC: dai: Provide interface for setting DMA data at probe time Allow DMA data to be set at probe time for devices that can do that, avoiding the need to do it every time we start a stream and supporting non-DT dmaengine users using the helpers. Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index ae9a227d35d3..97943fd397cc 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -276,6 +276,13 @@ static inline void snd_soc_dai_set_dma_data(struct snd_soc_dai *dai, dai->capture_dma_data = data; } +static inline void snd_soc_dai_init_dma_data(struct snd_soc_dai *dai, + void *playback, void *capture) +{ + dai->playback_dma_data = playback; + dai->capture_dma_data = capture; +} + static inline void snd_soc_dai_set_drvdata(struct snd_soc_dai *dai, void *data) { -- cgit v1.2.3 From cdcfcac968a1ec648434892b6addd80e66a5a892 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 17 Oct 2013 22:50:59 -0700 Subject: ASoC: rcar: add rsnd_scu_hpbif_is_enable() Current SSI needs RSND_SSI_DEPENDENT flag to decide dependent/independent mode. And SCU needs RSND_SCU_USE_HPBIF flag to decide HPBIF is enable/disable. But these 2 means same things. This patch adds new rsnd_scu_hpbif_is_enable() function, and merges above methods. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 1 - sound/soc/sh/rcar/rsnd.h | 1 + sound/soc/sh/rcar/scu.c | 12 +++++++++--- sound/soc/sh/rcar/ssi.c | 8 +++++--- 4 files changed, 15 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index fe66533e9b7a..6b4211c256b3 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -36,7 +36,6 @@ #define RSND_SSI_CLK_PIN_SHARE (1 << 31) #define RSND_SSI_CLK_FROM_ADG (1 << 30) /* clock parent is master */ #define RSND_SSI_SYNC (1 << 29) /* SSI34_sync etc */ -#define RSND_SSI_DEPENDENT (1 << 28) /* SSI needs SRU/SCU */ #define RSND_SSI_PLAY (1 << 24) diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 3868aaf41cc4..5feb67ca2d24 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -281,6 +281,7 @@ int rsnd_scu_probe(struct platform_device *pdev, void rsnd_scu_remove(struct platform_device *pdev, struct rsnd_priv *priv); struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id); +bool rsnd_scu_hpbif_is_enable(struct rsnd_mod *mod); #define rsnd_scu_nr(priv) ((priv)->scu_nr) /* diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c index 2df2e9150b89..1ab1bce6be7f 100644 --- a/sound/soc/sh/rcar/scu.c +++ b/sound/soc/sh/rcar/scu.c @@ -146,20 +146,26 @@ static int rsnd_scu_set_hpbif(struct rsnd_priv *priv, return 0; } +bool rsnd_scu_hpbif_is_enable(struct rsnd_mod *mod) +{ + struct rsnd_scu *scu = rsnd_mod_to_scu(mod); + u32 flags = rsnd_scu_mode_flags(scu); + + return !!(flags & RSND_SCU_USE_HPBIF); +} + static int rsnd_scu_start(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_scu *scu = rsnd_mod_to_scu(mod); struct device *dev = rsnd_priv_to_dev(priv); - u32 flags = rsnd_scu_mode_flags(scu); int ret; /* * SCU will be used if it has RSND_SCU_USE_HPBIF flags */ - if (!(flags & RSND_SCU_USE_HPBIF)) { + if (!rsnd_scu_hpbif_is_enable(mod)) { /* it use PIO transter */ dev_dbg(dev, "%s%d is not used\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index fae26d3f79d2..7613256c9840 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -106,6 +106,7 @@ static void rsnd_ssi_mode_init(struct rsnd_priv *priv, { struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_ssi *ssi; + struct rsnd_mod *scu; u32 flags; u32 val; int i; @@ -116,13 +117,14 @@ static void rsnd_ssi_mode_init(struct rsnd_priv *priv, ssiu->ssi_mode0 = 0; for_each_rsnd_ssi(ssi, priv, i) { flags = rsnd_ssi_mode_flags(ssi); + scu = rsnd_scu_mod_get(priv, rsnd_mod_id(&ssi->mod)); /* see also BUSIF_MODE */ - if (!(flags & RSND_SSI_DEPENDENT)) { + if (rsnd_scu_hpbif_is_enable(scu)) { + dev_dbg(dev, "SSI%d uses DEPENDENT mode\n", i); + } else { ssiu->ssi_mode0 |= (1 << i); dev_dbg(dev, "SSI%d uses INDEPENDENT mode\n", i); - } else { - dev_dbg(dev, "SSI%d uses DEPENDENT mode\n", i); } } -- cgit v1.2.3 From 4023fe6ff2192d6050647571ea54f5497b2ec8f6 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Fri, 18 Oct 2013 18:37:43 +0300 Subject: ASoC: davinci-mcasp: Extract DMA channels directly from DT Extract DMA channels directly from DT as they can not be found from platform resources anymore. This is a work-around until davinci audio driver is updated to use dmaengine. Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown --- .../bindings/sound/davinci-mcasp-audio.txt | 5 +++ include/linux/platform_data/davinci_asp.h | 2 + sound/soc/davinci/davinci-mcasp.c | 45 +++++++++++++++------- 3 files changed, 38 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt index c2ab8697e24a..c3ccde71f97a 100644 --- a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt +++ b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt @@ -18,6 +18,11 @@ Required properties: - serial-dir : A list of serializer pin mode. The list number should be equal to "num-serializer" parameter. Each entry is a number indication serializer pin direction. (0 - INACTIVE, 1 - TX, 2 - RX) +- dmas: two element list of DMA controller phandles and DMA request line + ordered pairs. +- dma-names: identifier string for each DMA request line in the dmas property. + These strings correspond 1:1 with the ordered pairs in dmas. The dma + identifiers must be "rx" and "tx". Optional properties: diff --git a/include/linux/platform_data/davinci_asp.h b/include/linux/platform_data/davinci_asp.h index 8db5ae03b6e3..689a856b86f9 100644 --- a/include/linux/platform_data/davinci_asp.h +++ b/include/linux/platform_data/davinci_asp.h @@ -84,6 +84,8 @@ struct snd_platform_data { u8 version; u8 txnumevt; u8 rxnumevt; + int tx_dma_channel; + int rx_dma_channel; }; enum { diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 806bec34e4d9..4c207508348f 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -1047,6 +1047,7 @@ static struct snd_platform_data *davinci_mcasp_set_pdata_from_of( struct snd_platform_data *pdata = NULL; const struct of_device_id *match = of_match_device(mcasp_dt_ids, &pdev->dev); + struct of_phandle_args dma_spec; const u32 *of_serial_dir32; u8 *of_serial_dir; @@ -1109,6 +1110,28 @@ static struct snd_platform_data *davinci_mcasp_set_pdata_from_of( pdata->serial_dir = of_serial_dir; } + ret = of_property_match_string(np, "dma-names", "tx"); + if (ret < 0) + goto nodata; + + ret = of_parse_phandle_with_args(np, "dmas", "#dma-cells", ret, + &dma_spec); + if (ret < 0) + goto nodata; + + pdata->tx_dma_channel = dma_spec.args[0]; + + ret = of_property_match_string(np, "dma-names", "rx"); + if (ret < 0) + goto nodata; + + ret = of_parse_phandle_with_args(np, "dmas", "#dma-cells", ret, + &dma_spec); + if (ret < 0) + goto nodata; + + pdata->rx_dma_channel = dma_spec.args[0]; + ret = of_property_read_u32(np, "tx-num-evt", &val); if (ret >= 0) pdata->txnumevt = val; @@ -1213,15 +1236,11 @@ static int davinci_mcasp_probe(struct platform_device *pdev) dma_data->sram_size = pdata->sram_size_playback; dma_data->dma_addr = dat->start + pdata->tx_dma_offset; - /* first TX, then RX */ res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!res) { - dev_err(&pdev->dev, "no DMA resource\n"); - ret = -ENODEV; - goto err_release_clk; - } - - dma_data->channel = res->start; + if (res) + dma_data->channel = res->start; + else + dma_data->channel = pdata->tx_dma_channel; dma_data = &dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]; dma_data->asp_chan_q = pdata->asp_chan_q; @@ -1231,13 +1250,11 @@ static int davinci_mcasp_probe(struct platform_device *pdev) dma_data->dma_addr = dat->start + pdata->rx_dma_offset; res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (!res) { - dev_err(&pdev->dev, "no DMA resource\n"); - ret = -ENODEV; - goto err_release_clk; - } + if (res) + dma_data->channel = res->start; + else + dma_data->channel = pdata->rx_dma_channel; - dma_data->channel = res->start; dev_set_drvdata(&pdev->dev, dev); ret = snd_soc_register_component(&pdev->dev, &davinci_mcasp_component, &davinci_mcasp_dai[pdata->op_mode], 1); -- cgit v1.2.3 From 055032142c42d2821c4aa617915292d6a08d56fc Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Wed, 23 Oct 2013 11:47:43 +0800 Subject: ALSA: Add SoC on-chip internal ram support for DMA buffer allocation Now it's quite common that an SoC contains its on-chip internal RAM. By using this RAM space for DMA buffer during audio playback/record, we can shutdown the voltage for external RAM to save power. So add new DEV type with iram malloc()/free() and accordingly modify current default mmap() for the iram circumstance. Signed-off-by: Nicolin Chen Reviewed-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai --- include/sound/memalloc.h | 1 + sound/core/memalloc.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ sound/core/pcm_native.c | 6 ++++++ 3 files changed, 59 insertions(+) (limited to 'include') diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h index cf15b8213df7..510aec437f72 100644 --- a/include/sound/memalloc.h +++ b/include/sound/memalloc.h @@ -52,6 +52,7 @@ struct snd_dma_device { #else #define SNDRV_DMA_TYPE_DEV_SG SNDRV_DMA_TYPE_DEV /* no SG-buf support */ #endif +#define SNDRV_DMA_TYPE_DEV_IRAM 4 /* generic device iram-buffer */ /* * info for buffer allocation diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index bdf826f4fe0c..18c1d4759ccb 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -157,6 +158,46 @@ static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr, dec_snd_pages(pg); dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma); } + +/** + * snd_malloc_dev_iram - allocate memory from on-chip internal ram + * @dmab: buffer allocation record to store the allocated data + * @size: number of bytes to allocate from the iram + * + * This function requires iram phandle provided via of_node + */ +void snd_malloc_dev_iram(struct snd_dma_buffer *dmab, size_t size) +{ + struct device *dev = dmab->dev.dev; + struct gen_pool *pool = NULL; + + if (dev->of_node) + pool = of_get_named_gen_pool(dev->of_node, "iram", 0); + + if (!pool) + return; + + /* Assign the pool into private_data field */ + dmab->private_data = pool; + + dmab->area = (void *)gen_pool_alloc(pool, size); + if (!dmab->area) + return; + + dmab->addr = gen_pool_virt_to_phys(pool, (unsigned long)dmab->area); +} + +/** + * snd_free_dev_iram - free allocated specific memory from on-chip internal ram + * @dmab: buffer allocation record to store the allocated data + */ +void snd_free_dev_iram(struct snd_dma_buffer *dmab) +{ + struct gen_pool *pool = dmab->private_data; + + if (pool && dmab->area) + gen_pool_free(pool, (unsigned long)dmab->area, dmab->bytes); +} #endif /* CONFIG_HAS_DMA */ /* @@ -197,6 +238,14 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, dmab->addr = 0; break; #ifdef CONFIG_HAS_DMA + case SNDRV_DMA_TYPE_DEV_IRAM: + snd_malloc_dev_iram(dmab, size); + if (dmab->area) + break; + /* Internal memory might have limited size and no enough space, + * so if we fail to malloc, try to fetch memory traditionally. + */ + dmab->dev.type = SNDRV_DMA_TYPE_DEV; case SNDRV_DMA_TYPE_DEV: dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr); break; @@ -269,6 +318,9 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab) snd_free_pages(dmab->area, dmab->bytes); break; #ifdef CONFIG_HAS_DMA + case SNDRV_DMA_TYPE_DEV_IRAM: + snd_free_dev_iram(dmab); + break; case SNDRV_DMA_TYPE_DEV: snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr); break; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index a68d4c6d702c..513f0954d92e 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3199,6 +3199,12 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *area) { area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; + if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_IRAM) { + area->vm_page_prot = pgprot_writecombine(area->vm_page_prot); + return remap_pfn_range(area, area->vm_start, + substream->dma_buffer.addr >> PAGE_SHIFT, + area->vm_end - area->vm_start, area->vm_page_prot); + } #ifdef ARCH_HAS_DMA_MMAP_COHERENT if (!substream->ops->page && substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) -- cgit v1.2.3 From ea73b7ddf13548afd666373dc5e26ee7c812a3fe Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 19 Oct 2013 17:43:51 +0100 Subject: ASoC: dmaengine: Support custom channel names Some devices have more than just simple TX and RX DMA channels, for example modern Samsung I2S IPs support a secondary transmit DMA stream which is mixed into the primary stream during playback. Allow such devices to specify the names of the channels to be requested in their dma_data. Signed-off-by: Mark Brown Acked-by: Lars-Peter Clausen --- include/sound/dmaengine_pcm.h | 6 ++++++ sound/soc/soc-generic-dmaengine-pcm.c | 13 ++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index 83b2c3e95a1a..15017311f2e9 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -61,6 +61,7 @@ struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream) * @slave_id: Slave requester id for the DMA channel. * @filter_data: Custom DMA channel filter data, this will usually be used when * requesting the DMA channel. + * @chan_name: Custom channel name to use when requesting DMA channel. * @fifo_size: FIFO size of the DAI controller in bytes */ struct snd_dmaengine_dai_dma_data { @@ -69,6 +70,7 @@ struct snd_dmaengine_dai_dma_data { u32 maxburst; unsigned int slave_id; void *filter_data; + const char *chan_name; unsigned int fifo_size; }; @@ -98,6 +100,10 @@ void snd_dmaengine_pcm_set_config_from_dai_data( * playback. */ #define SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX BIT(3) +/* + * The PCM streams have custom channel names specified. + */ +#define SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME BIT(4) /** * struct snd_dmaengine_pcm_config - Configuration data for dmaengine based PCM diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 99f9495c1c40..793cd6c246f6 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -183,6 +183,8 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) { struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform); const struct snd_dmaengine_pcm_config *config = pcm->config; + struct device *dev = rtd->platform->dev; + struct snd_dmaengine_dai_dma_data *dma_data; struct snd_pcm_substream *substream; size_t prealloc_buffer_size; size_t max_buffer_size; @@ -203,6 +205,13 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) if (!substream) continue; + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + + if (!pcm->chan[i] && + (pcm->flags & SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME)) + pcm->chan[i] = dma_request_slave_channel(dev, + dma_data->chan_name); + if (!pcm->chan[i] && (pcm->flags & SND_DMAENGINE_PCM_FLAG_COMPAT)) { pcm->chan[i] = dmaengine_pcm_compat_request_channel(rtd, substream); @@ -275,7 +284,9 @@ static void dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, { unsigned int i; - if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_DT) || !dev->of_node) + if ((pcm->flags & (SND_DMAENGINE_PCM_FLAG_NO_DT | + SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME)) || + !dev->of_node) return; if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) { -- cgit v1.2.3 From a5606f85611267047206d8ba055bc0e4ba166ad3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 24 Oct 2013 14:25:32 +0200 Subject: ALSA: Add ifdef CONFIG_GENERIC_ALLOCATOR for SNDRV_DMA_TYPE_IRAM code It turned out that we can't use gen_pool_*() functions on archs without CONFIG_GENERIC_ALLOCATOR (resulting in missing symbols), since linux/genalloc.h doesn't provide dummy functions for all. We'd be able to fix linux/genalloc.h size, but I take an easier path for now... Reported-by: Fengguang Wu Signed-off-by: Takashi Iwai --- include/sound/memalloc.h | 4 ++++ sound/core/memalloc.c | 4 ++++ sound/core/pcm_native.c | 2 ++ 3 files changed, 10 insertions(+) (limited to 'include') diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h index 510aec437f72..af9983970417 100644 --- a/include/sound/memalloc.h +++ b/include/sound/memalloc.h @@ -52,7 +52,11 @@ struct snd_dma_device { #else #define SNDRV_DMA_TYPE_DEV_SG SNDRV_DMA_TYPE_DEV /* no SG-buf support */ #endif +#ifdef CONFIG_GENERIC_ALLOCATOR #define SNDRV_DMA_TYPE_DEV_IRAM 4 /* generic device iram-buffer */ +#else +#define SNDRV_DMA_TYPE_DEV_IRAM SNDRV_DMA_TYPE_DEV +#endif /* * info for buffer allocation diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index 18c1d4759ccb..51a79218815b 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -238,6 +238,7 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, dmab->addr = 0; break; #ifdef CONFIG_HAS_DMA +#ifdef CONFIG_GENERIC_ALLOCATOR case SNDRV_DMA_TYPE_DEV_IRAM: snd_malloc_dev_iram(dmab, size); if (dmab->area) @@ -246,6 +247,7 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, * so if we fail to malloc, try to fetch memory traditionally. */ dmab->dev.type = SNDRV_DMA_TYPE_DEV; +#endif /* CONFIG_GENERIC_ALLOCATOR */ case SNDRV_DMA_TYPE_DEV: dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr); break; @@ -318,9 +320,11 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab) snd_free_pages(dmab->area, dmab->bytes); break; #ifdef CONFIG_HAS_DMA +#ifdef CONFIG_GENERIC_ALLOCATOR case SNDRV_DMA_TYPE_DEV_IRAM: snd_free_dev_iram(dmab); break; +#endif /* CONFIG_GENERIC_ALLOCATOR */ case SNDRV_DMA_TYPE_DEV: snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr); break; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 513f0954d92e..b71be579c6ec 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3199,12 +3199,14 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *area) { area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; +#ifdef CONFIG_GENERIC_ALLOCATOR if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_IRAM) { area->vm_page_prot = pgprot_writecombine(area->vm_page_prot); return remap_pfn_range(area, area->vm_start, substream->dma_buffer.addr >> PAGE_SHIFT, area->vm_end - area->vm_start, area->vm_page_prot); } +#endif /* CONFIG_GENERIC_ALLOCATOR */ #ifdef ARCH_HAS_DMA_MMAP_COHERENT if (!substream->ops->page && substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) -- cgit v1.2.3 From 917f4b5cba78980a527098a910d94139d3e82c8d Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 24 Oct 2013 16:37:31 +0530 Subject: ALSA: compress: fix drain calls blocking other compress functions The drain and drain_notify callback were blocked by low level driver untill the draining was complete. Due to this being invoked with big fat mutex held, others ops like reading timestamp, calling pause, drop were blocked. So to fix this we add a new snd_compr_drain_notify() API. This would be required to be invoked by low level driver when drain or partial drain has been completed by the DSP. Thus we make the drain and partial_drain callback as non blocking and driver returns immediately after notifying DSP. The waiting is done while relasing the lock so that other ops can go ahead. Signed-off-by: Vinod Koul CC: stable@vger.kernel.org Signed-off-by: Takashi Iwai --- include/sound/compress_driver.h | 12 ++++++++++++ sound/core/compress_offload.c | 41 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h index 9031a26249b5..175ab3237b58 100644 --- a/include/sound/compress_driver.h +++ b/include/sound/compress_driver.h @@ -48,6 +48,8 @@ struct snd_compr_ops; * the ring buffer * @total_bytes_transferred: cumulative bytes transferred by offload DSP * @sleep: poll sleep + * @wait: drain wait queue + * @drain_wake: condition for drain wake */ struct snd_compr_runtime { snd_pcm_state_t state; @@ -59,6 +61,8 @@ struct snd_compr_runtime { u64 total_bytes_available; u64 total_bytes_transferred; wait_queue_head_t sleep; + wait_queue_head_t wait; + unsigned int drain_wake; void *private_data; }; @@ -171,4 +175,12 @@ static inline void snd_compr_fragment_elapsed(struct snd_compr_stream *stream) wake_up(&stream->runtime->sleep); } +static inline void snd_compr_drain_notify(struct snd_compr_stream *stream) +{ + snd_BUG_ON(!stream); + + stream->runtime->drain_wake = 1; + wake_up(&stream->runtime->wait); +} + #endif diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index bea523a5d852..3eb47d0006a7 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -123,6 +123,7 @@ static int snd_compr_open(struct inode *inode, struct file *f) } runtime->state = SNDRV_PCM_STATE_OPEN; init_waitqueue_head(&runtime->sleep); + init_waitqueue_head(&runtime->wait); data->stream.runtime = runtime; f->private_data = (void *)data; mutex_lock(&compr->lock); @@ -682,12 +683,34 @@ static int snd_compr_stop(struct snd_compr_stream *stream) if (!retval) { stream->runtime->state = SNDRV_PCM_STATE_SETUP; wake_up(&stream->runtime->sleep); + snd_compr_drain_notify(stream); stream->runtime->total_bytes_available = 0; stream->runtime->total_bytes_transferred = 0; } return retval; } +static int snd_compress_wait_for_drain(struct snd_compr_stream *stream) +{ + /* + * We are called with lock held. So drop the lock while we wait for + * drain complete notfication from the driver + * + * It is expected that driver will notify the drain completion and then + * stream will be moved to SETUP state, even if draining resulted in an + * error. We can trigger next track after this. + */ + stream->runtime->state = SNDRV_PCM_STATE_DRAINING; + mutex_unlock(&stream->device->lock); + + wait_event(stream->runtime->wait, stream->runtime->drain_wake); + + wake_up(&stream->runtime->sleep); + mutex_lock(&stream->device->lock); + + return 0; +} + static int snd_compr_drain(struct snd_compr_stream *stream) { int retval; @@ -695,11 +718,17 @@ static int snd_compr_drain(struct snd_compr_stream *stream) if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || stream->runtime->state == SNDRV_PCM_STATE_SETUP) return -EPERM; + + stream->runtime->drain_wake = 0; retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN); - if (!retval) { - stream->runtime->state = SNDRV_PCM_STATE_DRAINING; + if (retval) { + pr_err("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval); wake_up(&stream->runtime->sleep); + return retval; } + + retval = snd_compress_wait_for_drain(stream); + stream->runtime->state = SNDRV_PCM_STATE_SETUP; return retval; } @@ -735,10 +764,16 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream) if (stream->next_track == false) return -EPERM; + stream->runtime->drain_wake = 0; retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN); + if (retval) { + pr_err("Partial drain returned failure\n"); + wake_up(&stream->runtime->sleep); + return retval; + } stream->next_track = false; - return retval; + return snd_compress_wait_for_drain(stream); } static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) -- cgit v1.2.3 From 6dd17757927ba9d23c604fee6fe72b4755c7ea7f Mon Sep 17 00:00:00 2001 From: Brian Austin Date: Fri, 25 Oct 2013 10:01:14 -0500 Subject: ASoC: cs42l52: Add platform data for reset gpio This patch adds platform data support for a reset GPIO. Also uses reset_gpio to toggle reset of the CODEC Signed-off-by: Brian Austin Signed-off-by: Mark Brown --- include/sound/cs42l52.h | 2 ++ sound/soc/codecs/cs42l52.c | 21 +++++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sound/cs42l52.h b/include/sound/cs42l52.h index 4c68955f7330..7c2be4a51894 100644 --- a/include/sound/cs42l52.h +++ b/include/sound/cs42l52.h @@ -31,6 +31,8 @@ struct cs42l52_platform_data { /* Charge Pump Freq. Check datasheet Pg73 */ unsigned int chgfreq; + /* Reset GPIO */ + unsigned int reset_gpio; }; #endif /* __CS42L52_H */ diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index be2ba1b6fe4a..8367f3c571eb 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -1205,6 +1206,7 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client, const struct i2c_device_id *id) { struct cs42l52_private *cs42l52; + struct cs42l52_platform_data *pdata = dev_get_platdata(&i2c_client->dev); int ret; unsigned int devid = 0; unsigned int reg; @@ -1222,11 +1224,22 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client, return ret; } - i2c_set_clientdata(i2c_client, cs42l52); + if (pdata) + cs42l52->pdata = *pdata; - if (dev_get_platdata(&i2c_client->dev)) - memcpy(&cs42l52->pdata, dev_get_platdata(&i2c_client->dev), - sizeof(cs42l52->pdata)); + if (cs42l52->pdata.reset_gpio) { + ret = gpio_request_one(cs42l52->pdata.reset_gpio, + GPIOF_OUT_INIT_HIGH, "CS42L52 /RST"); + if (ret < 0) { + dev_err(&i2c_client->dev, "Failed to request /RST %d: %d\n", + cs42l52->pdata.reset_gpio, ret); + return ret; + } + gpio_set_value_cansleep(cs42l52->pdata.reset_gpio, 0); + gpio_set_value_cansleep(cs42l52->pdata.reset_gpio, 1); + } + + i2c_set_clientdata(i2c_client, cs42l52); ret = regmap_register_patch(cs42l52->regmap, cs42l52_threshold_patch, ARRAY_SIZE(cs42l52_threshold_patch)); -- cgit v1.2.3 From e12483e0f3dbc32dad8fa1dc97efac22b6aee94f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 29 Oct 2013 16:37:11 +0100 Subject: ALSA: ak4114: Fix wrong register array size The size of the register cache array is actually 6 instead of 7, as it caches up to AK4114_REG_INT1_MASK. This resulted in unexpected access out of array range, although most of them aren't so serious (just reading one more byte on the stack at snd_ak4114_create()). Also, the check of cache size was wrongly done by checking with sizeof() instead of ARRAY_SIZE(). Fixed this together. (And yes, hardcoded numbers are bad, but I keep the coding style as is for making it clear what this patch actually does.) Spotted by coverity among several CIDs, e.g. 711621. Signed-off-by: Takashi Iwai --- include/sound/ak4114.h | 4 ++-- sound/i2c/other/ak4114.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/sound/ak4114.h b/include/sound/ak4114.h index 3ce69fd92523..52f02a60dba7 100644 --- a/include/sound/ak4114.h +++ b/include/sound/ak4114.h @@ -170,7 +170,7 @@ struct ak4114 { void * private_data; unsigned int init: 1; spinlock_t lock; - unsigned char regmap[7]; + unsigned char regmap[6]; unsigned char txcsb[5]; struct snd_kcontrol *kctls[AK4114_CONTROLS]; struct snd_pcm_substream *playback_substream; @@ -189,7 +189,7 @@ struct ak4114 { int snd_ak4114_create(struct snd_card *card, ak4114_read_t *read, ak4114_write_t *write, - const unsigned char pgm[7], const unsigned char txcsb[5], + const unsigned char pgm[6], const unsigned char txcsb[5], void *private_data, struct ak4114 **r_ak4114); void snd_ak4114_reg_write(struct ak4114 *ak4114, unsigned char reg, unsigned char mask, unsigned char val); void snd_ak4114_reinit(struct ak4114 *ak4114); diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c index 5bf4fca19e48..15ae0250eace 100644 --- a/sound/i2c/other/ak4114.c +++ b/sound/i2c/other/ak4114.c @@ -60,7 +60,7 @@ static void reg_dump(struct ak4114 *ak4114) printk(KERN_DEBUG "AK4114 REG DUMP:\n"); for (i = 0; i < 0x20; i++) - printk(KERN_DEBUG "reg[%02x] = %02x (%02x)\n", i, reg_read(ak4114, i), i < sizeof(ak4114->regmap) ? ak4114->regmap[i] : 0); + printk(KERN_DEBUG "reg[%02x] = %02x (%02x)\n", i, reg_read(ak4114, i), i < ARRAY_SIZE(ak4114->regmap) ? ak4114->regmap[i] : 0); } #endif @@ -81,7 +81,7 @@ static int snd_ak4114_dev_free(struct snd_device *device) int snd_ak4114_create(struct snd_card *card, ak4114_read_t *read, ak4114_write_t *write, - const unsigned char pgm[7], const unsigned char txcsb[5], + const unsigned char pgm[6], const unsigned char txcsb[5], void *private_data, struct ak4114 **r_ak4114) { struct ak4114 *chip; @@ -101,7 +101,7 @@ int snd_ak4114_create(struct snd_card *card, chip->private_data = private_data; INIT_DELAYED_WORK(&chip->work, ak4114_stats); - for (reg = 0; reg < 7; reg++) + for (reg = 0; reg < 6; reg++) chip->regmap[reg] = pgm[reg]; for (reg = 0; reg < 5; reg++) chip->txcsb[reg] = txcsb[reg]; @@ -142,7 +142,7 @@ static void ak4114_init_regs(struct ak4114 *chip) /* release reset, but leave powerdown */ reg_write(chip, AK4114_REG_PWRDN, (old | AK4114_RST) & ~AK4114_PWN); udelay(200); - for (reg = 1; reg < 7; reg++) + for (reg = 1; reg < 6; reg++) reg_write(chip, reg, chip->regmap[reg]); for (reg = 0; reg < 5; reg++) reg_write(chip, reg + AK4114_REG_TXCSB0, chip->txcsb[reg]); -- cgit v1.2.3 From f44f2a5417b2968a8724b352cc0b2545a6bcb1f4 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 7 Nov 2013 10:08:22 +0100 Subject: ALSA: compress: fix drain calls blocking other compress functions (v6) The drain and drain_notify callback were blocked by low level driver until the draining was complete. Due to this being invoked with big fat mutex held, others ops like reading timestamp, calling pause, drop were blocked. So to fix this we add a new snd_compr_drain_notify() API. This would be required to be invoked by low level driver when drain or partial drain has been completed by the DSP. Thus we make the drain and partial_drain callback as non blocking and driver returns immediately after notifying DSP. The waiting is done while releasing the lock so that other ops can go ahead. [ The commit 917f4b5cba78 was wrongly applied from the preliminary patch. This commit corrects to the final version. Sorry for inconvenience! -- tiwai ] Signed-off-by: Vinod Koul CC: stable@vger.kernel.org Signed-off-by: Takashi Iwai --- include/sound/compress_driver.h | 11 ++++------- sound/core/compress_offload.c | 31 +++++++++++++++++++------------ 2 files changed, 23 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h index 175ab3237b58..ae6c3b8ed2f5 100644 --- a/include/sound/compress_driver.h +++ b/include/sound/compress_driver.h @@ -48,8 +48,6 @@ struct snd_compr_ops; * the ring buffer * @total_bytes_transferred: cumulative bytes transferred by offload DSP * @sleep: poll sleep - * @wait: drain wait queue - * @drain_wake: condition for drain wake */ struct snd_compr_runtime { snd_pcm_state_t state; @@ -61,8 +59,6 @@ struct snd_compr_runtime { u64 total_bytes_available; u64 total_bytes_transferred; wait_queue_head_t sleep; - wait_queue_head_t wait; - unsigned int drain_wake; void *private_data; }; @@ -177,10 +173,11 @@ static inline void snd_compr_fragment_elapsed(struct snd_compr_stream *stream) static inline void snd_compr_drain_notify(struct snd_compr_stream *stream) { - snd_BUG_ON(!stream); + if (snd_BUG_ON(!stream)) + return; - stream->runtime->drain_wake = 1; - wake_up(&stream->runtime->wait); + stream->runtime->state = SNDRV_PCM_STATE_SETUP; + wake_up(&stream->runtime->sleep); } #endif diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 3eb47d0006a7..d9af6387f37c 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -123,7 +123,6 @@ static int snd_compr_open(struct inode *inode, struct file *f) } runtime->state = SNDRV_PCM_STATE_OPEN; init_waitqueue_head(&runtime->sleep); - init_waitqueue_head(&runtime->wait); data->stream.runtime = runtime; f->private_data = (void *)data; mutex_lock(&compr->lock); @@ -681,8 +680,6 @@ static int snd_compr_stop(struct snd_compr_stream *stream) return -EPERM; retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); if (!retval) { - stream->runtime->state = SNDRV_PCM_STATE_SETUP; - wake_up(&stream->runtime->sleep); snd_compr_drain_notify(stream); stream->runtime->total_bytes_available = 0; stream->runtime->total_bytes_transferred = 0; @@ -692,6 +689,8 @@ static int snd_compr_stop(struct snd_compr_stream *stream) static int snd_compress_wait_for_drain(struct snd_compr_stream *stream) { + int ret; + /* * We are called with lock held. So drop the lock while we wait for * drain complete notfication from the driver @@ -703,12 +702,24 @@ static int snd_compress_wait_for_drain(struct snd_compr_stream *stream) stream->runtime->state = SNDRV_PCM_STATE_DRAINING; mutex_unlock(&stream->device->lock); - wait_event(stream->runtime->wait, stream->runtime->drain_wake); + /* we wait for drain to complete here, drain can return when + * interruption occurred, wait returned error or success. + * For the first two cases we don't do anything different here and + * return after waking up + */ + + ret = wait_event_interruptible(stream->runtime->sleep, + (stream->runtime->state != SNDRV_PCM_STATE_DRAINING)); + if (ret == -ERESTARTSYS) + pr_debug("wait aborted by a signal"); + else if (ret) + pr_debug("wait for drain failed with %d\n", ret); + wake_up(&stream->runtime->sleep); mutex_lock(&stream->device->lock); - return 0; + return ret; } static int snd_compr_drain(struct snd_compr_stream *stream) @@ -719,17 +730,14 @@ static int snd_compr_drain(struct snd_compr_stream *stream) stream->runtime->state == SNDRV_PCM_STATE_SETUP) return -EPERM; - stream->runtime->drain_wake = 0; retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN); if (retval) { - pr_err("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval); + pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval); wake_up(&stream->runtime->sleep); return retval; } - retval = snd_compress_wait_for_drain(stream); - stream->runtime->state = SNDRV_PCM_STATE_SETUP; - return retval; + return snd_compress_wait_for_drain(stream); } static int snd_compr_next_track(struct snd_compr_stream *stream) @@ -764,10 +772,9 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream) if (stream->next_track == false) return -EPERM; - stream->runtime->drain_wake = 0; retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN); if (retval) { - pr_err("Partial drain returned failure\n"); + pr_debug("Partial drain returned failure\n"); wake_up(&stream->runtime->sleep); return retval; } -- cgit v1.2.3 From 3b098eb486868d57d7f2666d05b86c19a07df71b Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Thu, 7 Nov 2013 11:14:29 +0800 Subject: ALSA: include/uapi/sound/firewire.h: use "_UAPI" instead of "UAPI" When installing, "scripts/headers_install.sh" will strip guard macro' "_UAPI" to prevent from appearing it to users. And also, all another files which need uapi prefix always use "_UAPI", not "UAPI". So use "_UAPI" instead of "UAPI" on the guard macro, and also give a comment for "#endif". Signed-off-by: Chen Gang Acked-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- include/uapi/sound/firewire.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h index e86131ca49e5..59f5961302bf 100644 --- a/include/uapi/sound/firewire.h +++ b/include/uapi/sound/firewire.h @@ -1,5 +1,5 @@ -#ifndef UAPI_SOUND_FIREWIRE_H_INCLUDED -#define UAPI_SOUND_FIREWIRE_H_INCLUDED +#ifndef _UAPI_SOUND_FIREWIRE_H_INCLUDED +#define _UAPI_SOUND_FIREWIRE_H_INCLUDED #include @@ -48,4 +48,4 @@ struct snd_firewire_get_info { * Returns -EBUSY if the driver is already streaming. */ -#endif +#endif /* _UAPI_SOUND_FIREWIRE_H_INCLUDED */ -- cgit v1.2.3