From faff4bb067d15a3bc0dde8c50cbc1a7075e314de Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 7 Jan 2011 22:36:11 -0700 Subject: ASoC: Export debugfs root dentry A couple Tegra ASoC drivers will create debugfs entries. Mark requested these by under debugfs/asoc/ not just debugfs/. To enable this, export the dentry representing debugfs/asoc/. Also, rename debugfs_root -> asoc_debugfs_root now it's exported to prevent potential symbol name clashes. Signed-off-by: Stephen Warren Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/soc.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 74921f20a1d8..96aadbba85b2 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -756,4 +756,8 @@ static inline void *snd_soc_pcm_get_drvdata(struct snd_soc_pcm_runtime *rtd) #include +#ifdef CONFIG_DEBUG_FS +extern struct dentry *asoc_debugfs_root; +#endif + #endif -- cgit v1.2.3-59-g8ed1b From 8a9dab1a555e3f2088c68cae792dfd7e854e65e4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 10 Jan 2011 22:25:21 +0000 Subject: ASoC: Update name of debugfs root symbol to snd_soc_ Everything else is using snd_soc_ so we should use it here too. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 2 +- sound/soc/soc-core.c | 20 ++++++++++---------- sound/soc/tegra/tegra_das.c | 5 +++-- sound/soc/tegra/tegra_i2s.c | 2 +- 4 files changed, 15 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 96aadbba85b2..c477058ff98a 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -757,7 +757,7 @@ static inline void *snd_soc_pcm_get_drvdata(struct snd_soc_pcm_runtime *rtd) #include #ifdef CONFIG_DEBUG_FS -extern struct dentry *asoc_debugfs_root; +extern struct dentry *snd_soc_debugfs_root; #endif #endif diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 0484e504b589..2ff708a41119 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -48,8 +48,8 @@ static DEFINE_MUTEX(pcm_mutex); static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq); #ifdef CONFIG_DEBUG_FS -struct dentry *asoc_debugfs_root; -EXPORT_SYMBOL_GPL(asoc_debugfs_root); +struct dentry *snd_soc_debugfs_root; +EXPORT_SYMBOL_GPL(snd_soc_debugfs_root); #endif static DEFINE_MUTEX(client_mutex); @@ -361,7 +361,7 @@ static const struct file_operations platform_list_fops = { static void soc_init_card_debugfs(struct snd_soc_card *card) { card->debugfs_card_root = debugfs_create_dir(card->name, - asoc_debugfs_root); + snd_soc_debugfs_root); if (!card->debugfs_card_root) { dev_warn(card->dev, "ASoC: Failed to create codec debugfs directory\n"); @@ -3584,22 +3584,22 @@ EXPORT_SYMBOL_GPL(snd_soc_unregister_codec); static int __init snd_soc_init(void) { #ifdef CONFIG_DEBUG_FS - asoc_debugfs_root = debugfs_create_dir("asoc", NULL); - if (IS_ERR(asoc_debugfs_root) || !asoc_debugfs_root) { + snd_soc_debugfs_root = debugfs_create_dir("asoc", NULL); + if (IS_ERR(snd_soc_debugfs_root) || !snd_soc_debugfs_root) { printk(KERN_WARNING "ASoC: Failed to create debugfs directory\n"); - asoc_debugfs_root = NULL; + snd_soc_debugfs_root = NULL; } - if (!debugfs_create_file("codecs", 0444, asoc_debugfs_root, NULL, + if (!debugfs_create_file("codecs", 0444, snd_soc_debugfs_root, NULL, &codec_list_fops)) pr_warn("ASoC: Failed to create CODEC list debugfs file\n"); - if (!debugfs_create_file("dais", 0444, asoc_debugfs_root, NULL, + if (!debugfs_create_file("dais", 0444, snd_soc_debugfs_root, NULL, &dai_list_fops)) pr_warn("ASoC: Failed to create DAI list debugfs file\n"); - if (!debugfs_create_file("platforms", 0444, asoc_debugfs_root, NULL, + if (!debugfs_create_file("platforms", 0444, snd_soc_debugfs_root, NULL, &platform_list_fops)) pr_warn("ASoC: Failed to create platform list debugfs file\n"); #endif @@ -3611,7 +3611,7 @@ module_init(snd_soc_init); static void __exit snd_soc_exit(void) { #ifdef CONFIG_DEBUG_FS - debugfs_remove_recursive(asoc_debugfs_root); + debugfs_remove_recursive(snd_soc_debugfs_root); #endif platform_driver_unregister(&soc_driver); } diff --git a/sound/soc/tegra/tegra_das.c b/sound/soc/tegra/tegra_das.c index 796d36d5188a..01eb9c9301de 100644 --- a/sound/soc/tegra/tegra_das.c +++ b/sound/soc/tegra/tegra_das.c @@ -144,8 +144,9 @@ static const struct file_operations tegra_das_debug_fops = { static void tegra_das_debug_add(struct tegra_das *das) { - das->debug = debugfs_create_file(DRV_NAME, S_IRUGO, asoc_debugfs_root, - das, &tegra_das_debug_fops); + das->debug = debugfs_create_file(DRV_NAME, S_IRUGO, + snd_soc_debugfs_root, das, + &tegra_das_debug_fops); } static void tegra_das_debug_remove(struct tegra_das *das) diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c index 9b7a22af52a6..1730509c8ac2 100644 --- a/sound/soc/tegra/tegra_i2s.c +++ b/sound/soc/tegra/tegra_i2s.c @@ -104,7 +104,7 @@ static void tegra_i2s_debug_add(struct tegra_i2s *i2s, int id) char name[] = DRV_NAME ".0"; snprintf(name, sizeof(name), DRV_NAME".%1d", id); - i2s->debug = debugfs_create_file(name, S_IRUGO, asoc_debugfs_root, + i2s->debug = debugfs_create_file(name, S_IRUGO, snd_soc_debugfs_root, i2s, &tegra_i2s_debug_fops); } -- cgit v1.2.3-59-g8ed1b From aea170a099793abcd0e6de46b947458073204241 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Wed, 12 Jan 2011 10:38:58 +0000 Subject: ASoC: soc-cache: Add reg_size as a member to snd_soc_codec Simplify the use of reg_size, by calculating it once and storing it in the codec structure for later reference. The value of reg_size is reg_cache_size * reg_word_size. Signed-off-by: Dimitris Papastamos Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/soc.h | 1 + sound/soc/soc-cache.c | 26 ++++++++------------------ sound/soc/soc-core.c | 1 + 3 files changed, 10 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index c477058ff98a..d609232da82a 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -459,6 +459,7 @@ 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 */ /* runtime */ struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */ diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 19b29fb3ca4b..b2e333f5a388 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -1100,34 +1100,28 @@ static inline int snd_soc_lzo_get_blkindex(struct snd_soc_codec *codec, unsigned int reg) { const struct snd_soc_codec_driver *codec_drv; - size_t reg_size; codec_drv = codec->driver; - reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; return (reg * codec_drv->reg_word_size) / - DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count()); + DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count()); } static inline int snd_soc_lzo_get_blkpos(struct snd_soc_codec *codec, unsigned int reg) { const struct snd_soc_codec_driver *codec_drv; - size_t reg_size; codec_drv = codec->driver; - reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; - return reg % (DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count()) / + return reg % (DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count()) / codec_drv->reg_word_size); } static inline int snd_soc_lzo_get_blksize(struct snd_soc_codec *codec) { const struct snd_soc_codec_driver *codec_drv; - size_t reg_size; codec_drv = codec->driver; - reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; - return DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count()); + return DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count()); } static int snd_soc_lzo_cache_sync(struct snd_soc_codec *codec) @@ -1287,7 +1281,7 @@ static int snd_soc_lzo_cache_exit(struct snd_soc_codec *codec) static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec) { struct snd_soc_lzo_ctx **lzo_blocks; - size_t reg_size, bmp_size; + size_t bmp_size; const struct snd_soc_codec_driver *codec_drv; int ret, tofree, i, blksize, blkcount; const char *p, *end; @@ -1295,7 +1289,6 @@ static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec) ret = 0; codec_drv = codec->driver; - reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; /* * If we have not been given a default register cache @@ -1307,8 +1300,7 @@ static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec) tofree = 1; if (!codec->reg_def_copy) { - codec->reg_def_copy = kzalloc(reg_size, - GFP_KERNEL); + codec->reg_def_copy = kzalloc(codec->reg_size, GFP_KERNEL); if (!codec->reg_def_copy) return -ENOMEM; } @@ -1356,7 +1348,7 @@ static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec) blksize = snd_soc_lzo_get_blksize(codec); p = codec->reg_def_copy; - end = codec->reg_def_copy + reg_size; + end = codec->reg_def_copy + codec->reg_size; /* compress the register map and fill the lzo blocks */ for (i = 0; i < blkcount; ++i, p += blksize) { lzo_blocks[i]->src = p; @@ -1441,16 +1433,14 @@ 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; - size_t reg_size; codec_drv = codec->driver; - reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; if (codec->reg_def_copy) codec->reg_cache = kmemdup(codec->reg_def_copy, - reg_size, GFP_KERNEL); + codec->reg_size, GFP_KERNEL); else - codec->reg_cache = kzalloc(reg_size, GFP_KERNEL); + codec->reg_cache = kzalloc(codec->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 04475c18003c..cbac50b69c39 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3500,6 +3500,7 @@ int snd_soc_register_codec(struct device *dev, /* 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; /* 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 __devinitconst the -- cgit v1.2.3-59-g8ed1b From 066d16c3e8194677a9aaeb06a45e4014387d16f1 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Thu, 13 Jan 2011 12:20:36 +0000 Subject: ASoC: soc-cache: Add support for default readable()/volatile() functions For common scenarios, device drivers can provide a table of all the registers that are at least either readable/writable/volatile. The idea is that if a register lookup fails, all of its read/write/vol members will be zero and will be treated as default. This also reduces the size of the register access array. Signed-off-by: Dimitris Papastamos Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/soc.h | 22 ++++++++++++++++++++++ sound/soc/soc-cache.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index d609232da82a..b8acf99ac89d 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -276,6 +276,10 @@ 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); /* Utility functions to get clock rates from various things */ int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots); @@ -366,6 +370,22 @@ int snd_soc_get_volsw_2r_sx(struct snd_kcontrol *kcontrol, int snd_soc_put_volsw_2r_sx(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 * @@ -515,6 +535,8 @@ 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 */ diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 1a36b36c5baa..d97a59f6a249 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -1603,3 +1603,52 @@ int snd_soc_cache_sync(struct snd_soc_codec *codec) return -EINVAL; } EXPORT_SYMBOL_GPL(snd_soc_cache_sync); + +static int snd_soc_get_reg_access_index(struct snd_soc_codec *codec, + unsigned int reg) +{ + const struct snd_soc_codec_driver *codec_drv; + unsigned int min, max, index; + + codec_drv = codec->driver; + min = 0; + max = codec_drv->reg_access_size - 1; + do { + index = (min + max) / 2; + if (codec_drv->reg_access_default[index].reg == reg) + return index; + if (codec_drv->reg_access_default[index].reg < reg) + min = index + 1; + else + max = index; + } while (min <= max); + return -1; +} + +int snd_soc_default_volatile_register(struct snd_soc_codec *codec, + unsigned int reg) +{ + int index; + + if (reg >= codec->driver->reg_cache_size) + return 1; + index = snd_soc_get_reg_access_index(codec, reg); + if (index < 0) + return 0; + return codec->driver->reg_access_default[index].vol; +} +EXPORT_SYMBOL_GPL(snd_soc_default_volatile_register); + +int snd_soc_default_readable_register(struct snd_soc_codec *codec, + unsigned int reg) +{ + int index; + + if (reg >= codec->driver->reg_cache_size) + return 1; + index = snd_soc_get_reg_access_index(codec, reg); + if (index < 0) + return 0; + return codec->driver->reg_access_default[index].read; +} +EXPORT_SYMBOL_GPL(snd_soc_default_readable_register); -- cgit v1.2.3-59-g8ed1b From d4754ec91c7b094298f0b2ba02327e6887671edc Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Thu, 13 Jan 2011 12:20:37 +0000 Subject: ASoC: Update users of readable_register()/volatile_register() Signed-off-by: Dimitris Papastamos Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/soc.h | 4 ++-- sound/soc/codecs/cs4270.c | 4 ++-- sound/soc/codecs/max98088.c | 2 +- sound/soc/codecs/wm8523.c | 2 +- sound/soc/codecs/wm8804.c | 2 +- sound/soc/codecs/wm8900.c | 2 +- sound/soc/codecs/wm8903.c | 2 +- sound/soc/codecs/wm8904.c | 2 +- sound/soc/codecs/wm8961.c | 2 +- sound/soc/codecs/wm8962.c | 4 ++-- sound/soc/codecs/wm8993.c | 2 +- sound/soc/codecs/wm8994.c | 10 +++++----- sound/soc/codecs/wm8995.c | 2 +- sound/soc/codecs/wm9081.c | 2 +- sound/soc/codecs/wm9090.c | 4 ++-- sound/soc/soc-core.c | 4 ++-- 16 files changed, 25 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index b8acf99ac89d..97d1832bb9df 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -529,8 +529,8 @@ struct snd_soc_codec_driver { int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); int (*display_register)(struct snd_soc_codec *, char *, size_t, unsigned int); - int (*volatile_register)(unsigned int); - int (*readable_register)(unsigned int); + int (*volatile_register)(struct snd_soc_codec *, unsigned int); + int (*readable_register)(struct snd_soc_codec *, unsigned int); short reg_cache_size; short reg_cache_step; short reg_word_size; diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 8b51245f2318..c0fccadaea9a 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -193,12 +193,12 @@ static struct cs4270_mode_ratios cs4270_mode_ratios[] = { /* The number of MCLK/LRCK ratios supported by the CS4270 */ #define NUM_MCLK_RATIOS ARRAY_SIZE(cs4270_mode_ratios) -static int cs4270_reg_is_readable(unsigned int reg) +static int cs4270_reg_is_readable(struct snd_soc_codec *codec, unsigned int reg) { return (reg >= CS4270_FIRSTREG) && (reg <= CS4270_LASTREG); } -static int cs4270_reg_is_volatile(unsigned int reg) +static int cs4270_reg_is_volatile(struct snd_soc_codec *codec, unsigned int reg) { /* Unreadable registers are considered volatile */ if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG)) diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 37133c40e762..b6ecc7e89673 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -608,7 +608,7 @@ static struct { { 0xFF, 0x00, 1 }, /* FF */ }; -static int max98088_volatile_register(unsigned int reg) +static int max98088_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { return max98088_access[reg].vol; } diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index 5eb2f501ce32..83e86f077ee1 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -58,7 +58,7 @@ static const u16 wm8523_reg[WM8523_REGISTER_COUNT] = { 0x0000, /* R8 - ZERO_DETECT */ }; -static int wm8523_volatile_register(unsigned int reg) +static int wm8523_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8523_DEVICE_ID: diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c index 6dae1b40c9f7..6785688f8806 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c @@ -175,7 +175,7 @@ static int txsrc_put(struct snd_kcontrol *kcontrol, return 0; } -static int wm8804_volatile(unsigned int reg) +static int wm8804_volatile(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8804_RST_DEVID1: diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index cd0959926d12..449ea09a193d 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -180,7 +180,7 @@ static const u16 wm8900_reg_defaults[WM8900_MAXREG] = { /* Remaining registers all zero */ }; -static int wm8900_volatile_register(unsigned int reg) +static int wm8900_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8900_REG_ID: diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 987476a5895f..a2a446cb1807 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -232,7 +232,7 @@ struct wm8903_priv { int mic_delay; }; -static int wm8903_volatile_register(unsigned int reg) +static int wm8903_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8903_SW_RESET_AND_ID: diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 9de44a4c05c0..17a8fe9b39b9 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -596,7 +596,7 @@ static struct { { 0x003F, 0x003F, 0 }, /* R248 - FLL NCO Test 1 */ }; -static int wm8904_volatile_register(unsigned int reg) +static int wm8904_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { return wm8904_access[reg].vol; } diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index 55252e7d02c9..cdee8103d09b 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -291,7 +291,7 @@ struct wm8961_priv { int sysclk; }; -static int wm8961_volatile_register(unsigned int reg) +static int wm8961_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8961_SOFTWARE_RESET: diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index b9cb1fcf8c92..7c02924beddf 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -1938,7 +1938,7 @@ static const struct wm8962_reg_access { [21139] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21139 - VSS_XTS32_0 */ }; -static int wm8962_volatile_register(unsigned int reg) +static int wm8962_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { if (wm8962_reg_access[reg].vol) return 1; @@ -1946,7 +1946,7 @@ static int wm8962_volatile_register(unsigned int reg) return 0; } -static int wm8962_readable_register(unsigned int reg) +static int wm8962_readable_register(struct snd_soc_codec *codec, unsigned int reg) { if (wm8962_reg_access[reg].read) return 1; diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 18c0d9ce7c32..379fa22c5b6c 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -242,7 +242,7 @@ struct wm8993_priv { int fll_src; }; -static int wm8993_volatile(unsigned int reg) +static int wm8993_volatile(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8993_SOFTWARE_RESET: diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 247a6a99feb8..0bb0bb40b842 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -109,7 +109,7 @@ struct wm8994_priv { struct wm8994_pdata *pdata; }; -static int wm8994_readable(unsigned int reg) +static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8994_GPIO_1: @@ -136,7 +136,7 @@ static int wm8994_readable(unsigned int reg) return wm8994_access_masks[reg].readable != 0; } -static int wm8994_volatile(unsigned int reg) +static int wm8994_volatile(struct snd_soc_codec *codec, unsigned int reg) { if (reg >= WM8994_CACHE_SIZE) return 1; @@ -164,7 +164,7 @@ static int wm8994_write(struct snd_soc_codec *codec, unsigned int reg, BUG_ON(reg > WM8994_MAX_REGISTER); - if (!wm8994_volatile(reg)) { + if (!wm8994_volatile(codec, reg)) { ret = snd_soc_cache_write(codec, reg, value); if (ret != 0) dev_err(codec->dev, "Cache write to %x failed: %d\n", @@ -182,7 +182,7 @@ static unsigned int wm8994_read(struct snd_soc_codec *codec, BUG_ON(reg > WM8994_MAX_REGISTER); - if (!wm8994_volatile(reg) && wm8994_readable(reg) && + if (!wm8994_volatile(codec, reg) && wm8994_readable(codec, reg) && reg < codec->driver->reg_cache_size) { ret = snd_soc_cache_read(codec, reg, &val); if (ret >= 0) @@ -2943,7 +2943,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) /* Read our current status back from the chip - we don't want to * reset as this may interfere with the GPIO or LDO operation. */ for (i = 0; i < WM8994_CACHE_SIZE; i++) { - if (!wm8994_readable(i) || wm8994_volatile(i)) + if (!wm8994_readable(codec, i) || wm8994_volatile(codec, i)) continue; ret = wm8994_reg_read(codec->control_data, i); diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index ac210ccebd4b..f0f678de489f 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -909,7 +909,7 @@ static const struct snd_soc_dapm_route wm8995_intercon[] = { { "SPK2R", NULL, "SPK2R Driver" } }; -static int wm8995_volatile(unsigned int reg) +static int wm8995_volatile(struct snd_soc_codec *codec, unsigned int reg) { /* out of bounds registers are generally considered * volatile to support register banks that are partially diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 43825b2102a5..5c224dd917d7 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -169,7 +169,7 @@ struct wm9081_priv { struct wm9081_retune_mobile_config *retune; }; -static int wm9081_volatile_register(unsigned int reg) +static int wm9081_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM9081_SOFTWARE_RESET: diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c index a788c4297046..d40bfc9f8805 100644 --- a/sound/soc/codecs/wm9090.c +++ b/sound/soc/codecs/wm9090.c @@ -144,7 +144,7 @@ struct wm9090_priv { void *control_data; }; -static int wm9090_volatile(unsigned int reg) +static int wm9090_volatile(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM9090_SOFTWARE_RESET: @@ -518,7 +518,7 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec, for (i = 1; i < codec->driver->reg_cache_size; i++) { if (reg_cache[i] == wm9090_reg_defaults[i]) continue; - if (wm9090_volatile(i)) + if (wm9090_volatile(codec, i)) continue; ret = snd_soc_write(codec, i, reg_cache[i]); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index cbac50b69c39..b5e5758456bd 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -84,7 +84,7 @@ static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) count += sprintf(buf, "%s registers\n", codec->name); for (i = 0; i < codec->driver->reg_cache_size; i += step) { - if (codec->driver->readable_register && !codec->driver->readable_register(i)) + if (codec->driver->readable_register && !codec->driver->readable_register(codec, i)) continue; count += sprintf(buf + count, "%2x: ", i); @@ -2030,7 +2030,7 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg) { if (codec->driver->volatile_register) - return codec->driver->volatile_register(reg); + return codec->driver->volatile_register(codec, reg); else return 0; } -- cgit v1.2.3-59-g8ed1b From 1500b7b5ffaacb8199e0a61162f5d349fb19acbe Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Thu, 13 Jan 2011 12:20:38 +0000 Subject: ASoC: Automatically assign the default readable()/volatile() functions Ensure that all calls to readable_register()/volatile_register() go via the snd_soc_codec function pointers. If the default register access table has been given but no functions for handling readable()/volatile() registers, use the default ones provided by soc-cache. Signed-off-by: Dimitris Papastamos Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/soc.h | 2 ++ sound/soc/soc-core.c | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 97d1832bb9df..accb8a16c165 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -480,6 +480,8 @@ struct snd_soc_codec { 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); /* runtime */ struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index b5e5758456bd..30d76e8bc9df 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -84,7 +84,7 @@ static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) count += sprintf(buf, "%s registers\n", codec->name); for (i = 0; i < codec->driver->reg_cache_size; i += step) { - if (codec->driver->readable_register && !codec->driver->readable_register(codec, i)) + if (codec->readable_register && !codec->readable_register(codec, i)) continue; count += sprintf(buf + count, "%2x: ", i); @@ -2029,8 +2029,8 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) */ int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg) { - if (codec->driver->volatile_register) - return codec->driver->volatile_register(codec, reg); + if (codec->volatile_register) + return codec->volatile_register(codec, reg); else return 0; } @@ -3489,6 +3489,8 @@ int snd_soc_register_codec(struct device *dev, codec->write = codec_drv->write; codec->read = codec_drv->read; + codec->volatile_register = codec_drv->volatile_register; + codec->readable_register = codec_drv->readable_register; codec->dapm.bias_level = SND_SOC_BIAS_OFF; codec->dapm.dev = dev; codec->dapm.codec = codec; @@ -3517,6 +3519,13 @@ int snd_soc_register_codec(struct device *dev, } } + if (codec_drv->reg_access_size && codec_drv->reg_access_default) { + if (!codec->volatile_register) + codec->volatile_register = snd_soc_default_volatile_register; + if (!codec->readable_register) + codec->readable_register = snd_soc_default_readable_register; + } + 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-59-g8ed1b From 4e10bda05d6c7d4aba509bbbab5ba748d54c702f Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 13 Jan 2011 22:48:52 +0530 Subject: ASoC: soc core add inline to handle card list initialzation Currently the soc_probe initializes the card hence it does the card list initialzation. But if machines directly register the card they would need to do these steps, so putting them as inline would save lot of code This patch adds an inline to do list initialzation Signed-off-by: Vinod Koul Signed-off-by: Harsha Priya Signed-off-by: Mark Brown --- include/sound/soc.h | 10 ++++++++++ sound/soc/soc-core.c | 7 +------ 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index accb8a16c165..541ddfaa1243 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -779,6 +779,16 @@ static inline void *snd_soc_pcm_get_drvdata(struct snd_soc_pcm_runtime *rtd) return dev_get_drvdata(&rtd->dev); } +static inline void snd_soc_initialize_card_lists(struct snd_soc_card *card) +{ + INIT_LIST_HEAD(&card->dai_dev_list); + INIT_LIST_HEAD(&card->codec_dev_list); + INIT_LIST_HEAD(&card->platform_dev_list); + INIT_LIST_HEAD(&card->widgets); + INIT_LIST_HEAD(&card->paths); + INIT_LIST_HEAD(&card->dapm_list); +} + #include #ifdef CONFIG_DEBUG_FS diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 9c5e7cff3f01..83057127b2fa 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1872,12 +1872,7 @@ static int soc_probe(struct platform_device *pdev) /* Bodge while we unpick instantiation */ card->dev = &pdev->dev; - INIT_LIST_HEAD(&card->dai_dev_list); - INIT_LIST_HEAD(&card->codec_dev_list); - INIT_LIST_HEAD(&card->platform_dev_list); - INIT_LIST_HEAD(&card->widgets); - INIT_LIST_HEAD(&card->paths); - INIT_LIST_HEAD(&card->dapm_list); + snd_soc_initialize_card_lists(card); ret = snd_soc_register_card(card); if (ret != 0) { -- cgit v1.2.3-59-g8ed1b From 70a7ca34dbdcc6f0ed332baf2b308bab2871424a Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 14 Jan 2011 19:22:48 +0530 Subject: ASoC: soc core allow machine driver to register the card The machine driver can't register the card directly and need to do this thru soc-audio device creation This patch allows the register and unregister card to be directly called by machine drivers Signed-off-by: Vinod Koul Signed-off-by: Harsha Priya Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/soc.h | 2 ++ sound/soc/soc-core.c | 21 +++++++++++---------- 2 files changed, 13 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 541ddfaa1243..9952254974b3 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -258,6 +258,8 @@ enum snd_soc_compress_type { SND_SOC_RBTREE_COMPRESSION }; +int snd_soc_register_card(struct snd_soc_card *card); +int snd_soc_unregister_card(struct snd_soc_card *card); int snd_soc_register_platform(struct device *dev, struct snd_soc_platform_driver *platform_drv); void snd_soc_unregister_platform(struct device *dev); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 83057127b2fa..69117b686fdc 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -58,8 +58,6 @@ static LIST_HEAD(dai_list); static LIST_HEAD(platform_list); static LIST_HEAD(codec_list); -static int snd_soc_register_card(struct snd_soc_card *card); -static int snd_soc_unregister_card(struct snd_soc_card *card); static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num); /* @@ -1870,6 +1868,13 @@ static int soc_probe(struct platform_device *pdev) struct snd_soc_card *card = platform_get_drvdata(pdev); int ret = 0; + /* + * no card, so machine driver should be registering card + * we should not be here in that case so ret error + */ + if (!card) + return -EINVAL; + /* Bodge while we unpick instantiation */ card->dev = &pdev->dev; snd_soc_initialize_card_lists(card); @@ -3105,11 +3110,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute); * * @card: Card to register * - * Note that currently this is an internal only function: it will be - * exposed to machine drivers after further backporting of ASoC v2 - * registration APIs. */ -static int snd_soc_register_card(struct snd_soc_card *card) +int snd_soc_register_card(struct snd_soc_card *card) { int i; @@ -3141,17 +3143,15 @@ static int snd_soc_register_card(struct snd_soc_card *card) return 0; } +EXPORT_SYMBOL_GPL(snd_soc_register_card); /** * snd_soc_unregister_card - Unregister a card with the ASoC core * * @card: Card to unregister * - * Note that currently this is an internal only function: it will be - * exposed to machine drivers after further backporting of ASoC v2 - * registration APIs. */ -static int snd_soc_unregister_card(struct snd_soc_card *card) +int snd_soc_unregister_card(struct snd_soc_card *card) { if (card->instantiated) soc_cleanup_card_resources(card); @@ -3162,6 +3162,7 @@ static int snd_soc_unregister_card(struct snd_soc_card *card) return 0; } +EXPORT_SYMBOL_GPL(snd_soc_unregister_card); /* * Simplify DAI link configuration by removing ".-1" from device names -- cgit v1.2.3-59-g8ed1b From 20e4859dedfc7e7b620d1756b29f8483c5be5fcc Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 15 Jan 2011 13:40:50 +0000 Subject: ASoC: Add support for sequencing within With larger devices there may be many widgets of the same type in series in an audio path. Allow drivers to specify an additional level of ordering within each widget type by adding a subsequence number to widgets and then splitting operations on widgets so that widgets of the same type but different sequence numbers are processed separately. A typical example would be a supply widget which requires that another widget be enabled to provide power or clocking. SND_SOC_DAPM_PGA_S() and SND_SOC_DAPM_SUPPLY_S() macros are provided allowing this to be used with PGAs and supplies as these are the most commonly affected widgets. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc-dapm.h | 13 +++++++++++++ sound/soc/soc-dapm.c | 11 ++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 8031769ac485..a3760c93a8a3 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -157,6 +157,18 @@ .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \ .event = wevent, .event_flags = wflags} +/* additional sequencing control within an event type */ +#define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, wcontrols, \ + wncontrols, wevent, wflags) \ +{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \ + .event = wevent, .event_flags = wflags, .subseq = wsubseq} +#define SND_SOC_DAPM_SUPPLY_S(wname, wsubseq, wreg, wshift, winvert, wevent, \ + wflags) \ +{ .id = snd_soc_dapm_supply, .name = wname, .reg = wreg, \ + .shift = wshift, .invert = winvert, .event = wevent, \ + .event_flags = wflags, .subseq = wsubseq} + /* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */ #define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ @@ -450,6 +462,7 @@ struct snd_soc_dapm_widget { unsigned char ext:1; /* has external widgets */ unsigned char force:1; /* force state */ unsigned char ignore_suspend:1; /* kept enabled over suspend */ + int subseq; /* sort within widget type */ int (*power_check)(struct snd_soc_dapm_widget *w); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 57e1c9f71149..eb7436c7acad 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -737,6 +737,12 @@ static int dapm_seq_compare(struct snd_soc_dapm_widget *a, if (sort[a->id] != sort[b->id]) return sort[a->id] - sort[b->id]; + if (a->subseq != b->subseq) { + if (power_up) + return a->subseq - b->subseq; + else + return b->subseq - a->subseq; + } if (a->reg != b->reg) return a->reg - b->reg; if (a->dapm != b->dapm) @@ -869,6 +875,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_widget *w, *n; LIST_HEAD(pending); int cur_sort = -1; + int cur_subseq = -1; int cur_reg = SND_SOC_NOPM; struct snd_soc_dapm_context *cur_dapm = NULL; int ret; @@ -884,12 +891,13 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, /* Do we need to apply any queued changes? */ if (sort[w->id] != cur_sort || w->reg != cur_reg || - w->dapm != cur_dapm) { + w->dapm != cur_dapm || w->subseq != cur_subseq) { if (!list_empty(&pending)) dapm_seq_run_coalesced(cur_dapm, &pending); INIT_LIST_HEAD(&pending); cur_sort = -1; + cur_subseq = -1; cur_reg = SND_SOC_NOPM; cur_dapm = NULL; } @@ -934,6 +942,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, default: /* Queue it up for application */ cur_sort = sort[w->id]; + cur_subseq = w->subseq; cur_reg = w->reg; cur_dapm = w->dapm; list_move(&w->power_list, &pending); -- cgit v1.2.3-59-g8ed1b From 474b62d6eee733abdcd36f8e3e5ce504fbb9110b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 18 Jan 2011 16:14:44 +0000 Subject: ASoC: Provide per widget type callback when executing DAPM sequences Many modern devices have features such as DC servos which take time to start. Currently these are handled by per-widget events but this makes it difficult to paralleise operations on multiple widgets, meaning delays can end up being needlessly serialised. By providing a callback to drivers when all widgets of a given type have been handled during a DAPM sequence the core allows drivers to start operations separately and wait for them to complete much more simply. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc-dapm.h | 3 +++ include/sound/soc.h | 3 +++ sound/soc/soc-core.c | 1 + sound/soc/soc-dapm.c | 16 +++++++++++++++- 4 files changed, 22 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index a3760c93a8a3..6c9ae237814b 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -500,6 +500,9 @@ struct snd_soc_dapm_context { struct snd_soc_dapm_update *update; + void (*seq_notifier)(struct snd_soc_dapm_context *, + enum snd_soc_dapm_type); + struct device *dev; /* from parent - for debug */ struct snd_soc_codec *codec; /* parent codec */ struct snd_soc_card *card; /* parent card */ diff --git a/include/sound/soc.h b/include/sound/soc.h index 9952254974b3..d244f9013767 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -546,6 +546,9 @@ struct snd_soc_codec_driver { /* codec bias level */ int (*set_bias_level)(struct snd_soc_codec *, enum snd_soc_bias_level level); + + void (*seq_notifier)(struct snd_soc_dapm_context *, + enum snd_soc_dapm_type); }; /* SoC platform interface */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 9e68984423b2..b0e7689159c1 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3496,6 +3496,7 @@ int snd_soc_register_codec(struct device *dev, codec->dapm.bias_level = SND_SOC_BIAS_OFF; codec->dapm.dev = dev; codec->dapm.codec = codec; + codec->dapm.seq_notifier = codec_drv->seq_notifier; codec->dev = dev; codec->driver = codec_drv; codec->num_dai = num_dai; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index eb7436c7acad..37b376f4c75d 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -878,7 +878,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, int cur_subseq = -1; int cur_reg = SND_SOC_NOPM; struct snd_soc_dapm_context *cur_dapm = NULL; - int ret; + int ret, i; int *sort; if (power_up) @@ -895,6 +895,13 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, if (!list_empty(&pending)) dapm_seq_run_coalesced(cur_dapm, &pending); + if (cur_dapm && cur_dapm->seq_notifier) { + for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) + if (sort[i] == cur_sort) + cur_dapm->seq_notifier(cur_dapm, + i); + } + INIT_LIST_HEAD(&pending); cur_sort = -1; cur_subseq = -1; @@ -956,6 +963,13 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, if (!list_empty(&pending)) dapm_seq_run_coalesced(dapm, &pending); + + if (cur_dapm && cur_dapm->seq_notifier) { + for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) + if (sort[i] == cur_sort) + cur_dapm->seq_notifier(cur_dapm, + i); + } } static void dapm_widget_update(struct snd_soc_dapm_context *dapm) -- cgit v1.2.3-59-g8ed1b From dad8e7aeeb83a26d267e757e4c1cf69591850477 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Wed, 19 Jan 2011 14:53:36 +0000 Subject: ASoC: soc-cache: Introduce the cache_bypass option This is primarily needed to avoid writing back to the cache whenever we are syncing the cache with the hardware. This gives a performance benefit especially for large register maps. Signed-off-by: Dimitris Papastamos Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/soc.h | 1 + sound/soc/soc-cache.c | 36 ++++++++++++++++++++++++------------ 2 files changed, 25 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index d244f9013767..c184f84a354c 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -488,6 +488,7 @@ struct snd_soc_codec { /* runtime */ struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */ unsigned int active; + unsigned int cache_bypass:1; /* Suppress access to the cache */ unsigned int cache_only:1; /* Suppress writes to hardware */ unsigned int cache_sync:1; /* Cache needs to be synced to hardware */ unsigned int suspended:1; /* Codec is in suspend PM state */ diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index d97a59f6a249..1ebff9f12b4e 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -25,7 +25,8 @@ static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec, unsigned int val; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { if (codec->cache_only) return -1; @@ -49,7 +50,8 @@ static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg, data[1] = value & 0x00ff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; @@ -106,7 +108,8 @@ static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec, unsigned int val; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { if (codec->cache_only) return -1; @@ -130,7 +133,8 @@ static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg, data[1] = value & 0x00ff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; @@ -191,7 +195,8 @@ static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg, data[1] = value & 0xff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; @@ -216,7 +221,8 @@ static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec, reg &= 0xff; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { if (codec->cache_only) return -1; @@ -271,7 +277,8 @@ static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg, data[2] = value & 0xff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; @@ -295,7 +302,8 @@ static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec, unsigned int val; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { if (codec->cache_only) return -1; @@ -450,7 +458,8 @@ static unsigned int snd_soc_16_8_read(struct snd_soc_codec *codec, reg &= 0xff; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { if (codec->cache_only) return -1; @@ -476,7 +485,8 @@ static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg, reg &= 0xff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; @@ -568,7 +578,8 @@ static unsigned int snd_soc_16_16_read(struct snd_soc_codec *codec, unsigned int val; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { if (codec->cache_only) return -1; @@ -595,7 +606,8 @@ static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg, data[3] = value & 0xff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; -- cgit v1.2.3-59-g8ed1b From 7cfe56172ac14d2031f1896ecb629033f71caafa Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 20 Jan 2011 13:52:08 -0700 Subject: ASoC: wm8903: Expose GPIOs through gpiolib Also, update platform_data GPIO handling to have an explicit "don't touch this pin" option. Add #defines for the GPIO pin functions. Signed-off-by: Stephen Warren Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/wm8903.h | 20 +++++++- sound/soc/codecs/wm8903.c | 126 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 144 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sound/wm8903.h b/include/sound/wm8903.h index b4a0db2307ef..86172cf4339f 100644 --- a/include/sound/wm8903.h +++ b/include/sound/wm8903.h @@ -36,6 +36,21 @@ #define WM8903_MICBIAS_ENA_SHIFT 0 /* MICBIAS_ENA */ #define WM8903_MICBIAS_ENA_WIDTH 1 /* MICBIAS_ENA */ +/* + * WM8903_GPn_FN values + * + * See datasheets for list of valid values per pin + */ +#define WM8903_GPn_FN_GPIO_OUTPUT 0 +#define WM8903_GPn_FN_BCLK 1 +#define WM8903_GPn_FN_IRQ_OUTPT 2 +#define WM8903_GPn_FN_GPIO_INPUT 3 +#define WM8903_GPn_FN_MICBIAS_CURRENT_DETECT 4 +#define WM8903_GPn_FN_MICBIAS_SHORT_DETECT 5 +#define WM8903_GPn_FN_DMIC_LR_CLK_OUTPUT 6 +#define WM8903_GPn_FN_FLL_LOCK_OUTPUT 8 +#define WM8903_GPn_FN_FLL_CLOCK_OUTPUT 9 + /* * R116 (0x74) - GPIO Control 1 */ @@ -231,6 +246,8 @@ #define WM8903_GP5_DB_SHIFT 0 /* GP5_DB */ #define WM8903_GP5_DB_WIDTH 1 /* GP5_DB */ +#define WM8903_NUM_GPIO 5 + struct wm8903_platform_data { bool irq_active_low; /* Set if IRQ active low, default high */ @@ -243,7 +260,8 @@ struct wm8903_platform_data { int micdet_delay; /* Delay after microphone detection (ms) */ - u32 gpio_cfg[5]; /* Default register values for GPIO pin mux */ + int gpio_base; + u32 gpio_cfg[WM8903_NUM_GPIO]; /* Default register values for GPIO pin mux */ }; #endif diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index a2a446cb1807..9c4f2c4febc2 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -2,6 +2,7 @@ * wm8903.c -- WM8903 ALSA SoC Audio driver * * Copyright 2008 Wolfson Microelectronics + * Copyright 2011 NVIDIA, Inc. * * Author: Mark Brown * @@ -19,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -213,6 +215,7 @@ static u16 wm8903_reg_defaults[] = { }; struct wm8903_priv { + struct snd_soc_codec *codec; int sysclk; int irq; @@ -230,6 +233,10 @@ struct wm8903_priv { int mic_short; int mic_last_report; int mic_delay; + +#ifdef CONFIG_GPIOLIB + struct gpio_chip gpio_chip; +#endif }; static int wm8903_volatile_register(struct snd_soc_codec *codec, unsigned int reg) @@ -1635,6 +1642,119 @@ static int wm8903_resume(struct snd_soc_codec *codec) return 0; } +#ifdef CONFIG_GPIOLIB +static inline struct wm8903_priv *gpio_to_wm8903(struct gpio_chip *chip) +{ + return container_of(chip, struct wm8903_priv, gpio_chip); +} + +static int wm8903_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + if (offset >= WM8903_NUM_GPIO) + return -EINVAL; + + return 0; +} + +static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset) +{ + struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); + struct snd_soc_codec *codec = wm8903->codec; + unsigned int mask, val; + + mask = WM8903_GP1_FN_MASK | WM8903_GP1_DIR_MASK; + val = (WM8903_GPn_FN_GPIO_INPUT << WM8903_GP1_FN_SHIFT) | + WM8903_GP1_DIR; + + return snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset, + mask, val); +} + +static int wm8903_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); + struct snd_soc_codec *codec = wm8903->codec; + int reg; + + reg = snd_soc_read(codec, WM8903_GPIO_CONTROL_1 + offset); + + return (reg & WM8903_GP1_LVL_MASK) >> WM8903_GP1_LVL_SHIFT; +} + +static int wm8903_gpio_direction_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); + struct snd_soc_codec *codec = wm8903->codec; + unsigned int mask, val; + + mask = WM8903_GP1_FN_MASK | WM8903_GP1_DIR_MASK | WM8903_GP1_LVL_MASK; + val = (WM8903_GPn_FN_GPIO_OUTPUT << WM8903_GP1_FN_SHIFT) | + (value << WM8903_GP2_LVL_SHIFT); + + return snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset, + mask, val); +} + +static void wm8903_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); + struct snd_soc_codec *codec = wm8903->codec; + + snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset, + WM8903_GP1_LVL_MASK, value << WM8903_GP1_LVL_SHIFT); +} + +static struct gpio_chip wm8903_template_chip = { + .label = "wm8903", + .owner = THIS_MODULE, + .request = wm8903_gpio_request, + .direction_input = wm8903_gpio_direction_in, + .get = wm8903_gpio_get, + .direction_output = wm8903_gpio_direction_out, + .set = wm8903_gpio_set, + .can_sleep = 1, +}; + +static void wm8903_init_gpio(struct snd_soc_codec *codec) +{ + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); + struct wm8903_platform_data *pdata = dev_get_platdata(codec->dev); + int ret; + + wm8903->gpio_chip = wm8903_template_chip; + wm8903->gpio_chip.ngpio = WM8903_NUM_GPIO; + wm8903->gpio_chip.dev = codec->dev; + + if (pdata && pdata->gpio_base) + wm8903->gpio_chip.base = pdata->gpio_base; + else + wm8903->gpio_chip.base = -1; + + ret = gpiochip_add(&wm8903->gpio_chip); + if (ret != 0) + dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret); +} + +static void wm8903_free_gpio(struct snd_soc_codec *codec) +{ + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = gpiochip_remove(&wm8903->gpio_chip); + if (ret != 0) + dev_err(codec->dev, "Failed to remove GPIOs: %d\n", ret); +} +#else +static void wm8903_init_gpio(struct snd_soc_codec *codec) +{ +} + +static void wm8903_free_gpio(struct snd_soc_codec *codec) +{ +} +#endif + static int wm8903_probe(struct snd_soc_codec *codec) { struct wm8903_platform_data *pdata = dev_get_platdata(codec->dev); @@ -1643,6 +1763,7 @@ static int wm8903_probe(struct snd_soc_codec *codec) int trigger, irq_pol; u16 val; + wm8903->codec = codec; init_completion(&wm8903->wseq); ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C); @@ -1667,7 +1788,7 @@ static int wm8903_probe(struct snd_soc_codec *codec) /* Set up GPIOs and microphone detection */ if (pdata) { for (i = 0; i < ARRAY_SIZE(pdata->gpio_cfg); i++) { - if (!pdata->gpio_cfg[i]) + if (pdata->gpio_cfg[i] == WM8903_GPIO_NO_CONFIG) continue; snd_soc_write(codec, WM8903_GPIO_CONTROL_1 + i, @@ -1749,12 +1870,15 @@ static int wm8903_probe(struct snd_soc_codec *codec) ARRAY_SIZE(wm8903_snd_controls)); wm8903_add_widgets(codec); + wm8903_init_gpio(codec); + return ret; } /* power down chip */ static int wm8903_remove(struct snd_soc_codec *codec) { + wm8903_free_gpio(codec); wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } -- cgit v1.2.3-59-g8ed1b From 67b22517d8e48a97e1d2ab10d095c538bbb2374c Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Wed, 19 Jan 2011 21:22:06 +0300 Subject: ASoC: CS4271 codec support Added support for CS4271 codec to ASoC. Signed-off-by: Alexander Sverdlin Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/cs4271.h | 25 ++ sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/cs4271.c | 630 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 661 insertions(+) create mode 100644 include/sound/cs4271.h create mode 100644 sound/soc/codecs/cs4271.c (limited to 'include') diff --git a/include/sound/cs4271.h b/include/sound/cs4271.h new file mode 100644 index 000000000000..16f8d325d3dc --- /dev/null +++ b/include/sound/cs4271.h @@ -0,0 +1,25 @@ +/* + * Definitions for CS4271 ASoC codec driver + * + * Copyright (c) 2010 Alexander Sverdlin + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __CS4271_H +#define __CS4271_H + +struct cs4271_platform_data { + int gpio_nreset; /* GPIO driving Reset pin, if any */ + int gpio_disable; /* GPIO that disable serial bus, if any */ +}; + +#endif /* __CS4271_H */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index a9cb2a04ad56..e239345a4d5d 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -26,6 +26,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC select SND_SOC_CS42L51 if I2C select SND_SOC_CS4270 if I2C + select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI select SND_SOC_CX20442 select SND_SOC_DA7210 if I2C select SND_SOC_JZ4740_CODEC if SOC_JZ4740 @@ -157,6 +158,9 @@ config SND_SOC_CS4270_VD33_ERRATA bool depends on SND_SOC_CS4270 +config SND_SOC_CS4271 + tristate + config SND_SOC_CX20442 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 68e76af894b9..83b7accd7037 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -12,6 +12,7 @@ snd-soc-ak4671-objs := ak4671.o snd-soc-cq93vc-objs := cq93vc.o snd-soc-cs42l51-objs := cs42l51.o snd-soc-cs4270-objs := cs4270.o +snd-soc-cs4271-objs := cs4271.o snd-soc-cx20442-objs := cx20442.o snd-soc-da7210-objs := da7210.o snd-soc-dmic-objs := dmic.o @@ -93,6 +94,7 @@ obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o +obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c new file mode 100644 index 000000000000..237ece3f1046 --- /dev/null +++ b/sound/soc/codecs/cs4271.c @@ -0,0 +1,630 @@ +/* + * CS4271 ASoC codec driver + * + * Copyright (c) 2010 Alexander Sverdlin + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This driver support CS4271 codec being master or slave, working + * in control port mode, connected either via SPI or I2C. + * The data format accepted is I2S or left-justified. + * DAPM support not implemented. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CS4271_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +/* + * CS4271 registers + * High byte represents SPI chip address (0x10) + write command (0) + * Low byte - codec register address + */ +#define CS4271_MODE1 0x2001 /* Mode Control 1 */ +#define CS4271_DACCTL 0x2002 /* DAC Control */ +#define CS4271_DACVOL 0x2003 /* DAC Volume & Mixing Control */ +#define CS4271_VOLA 0x2004 /* DAC Channel A Volume Control */ +#define CS4271_VOLB 0x2005 /* DAC Channel B Volume Control */ +#define CS4271_ADCCTL 0x2006 /* ADC Control */ +#define CS4271_MODE2 0x2007 /* Mode Control 2 */ +#define CS4271_CHIPID 0x2008 /* Chip ID */ + +#define CS4271_FIRSTREG CS4271_MODE1 +#define CS4271_LASTREG CS4271_MODE2 +#define CS4271_NR_REGS ((CS4271_LASTREG & 0xFF) + 1) + +/* Bit masks for the CS4271 registers */ +#define CS4271_MODE1_MODE_MASK 0xC0 +#define CS4271_MODE1_MODE_1X 0x00 +#define CS4271_MODE1_MODE_2X 0x80 +#define CS4271_MODE1_MODE_4X 0xC0 + +#define CS4271_MODE1_DIV_MASK 0x30 +#define CS4271_MODE1_DIV_1 0x00 +#define CS4271_MODE1_DIV_15 0x10 +#define CS4271_MODE1_DIV_2 0x20 +#define CS4271_MODE1_DIV_3 0x30 + +#define CS4271_MODE1_MASTER 0x08 + +#define CS4271_MODE1_DAC_DIF_MASK 0x07 +#define CS4271_MODE1_DAC_DIF_LJ 0x00 +#define CS4271_MODE1_DAC_DIF_I2S 0x01 +#define CS4271_MODE1_DAC_DIF_RJ16 0x02 +#define CS4271_MODE1_DAC_DIF_RJ24 0x03 +#define CS4271_MODE1_DAC_DIF_RJ20 0x04 +#define CS4271_MODE1_DAC_DIF_RJ18 0x05 + +#define CS4271_DACCTL_AMUTE 0x80 +#define CS4271_DACCTL_IF_SLOW 0x40 + +#define CS4271_DACCTL_DEM_MASK 0x30 +#define CS4271_DACCTL_DEM_DIS 0x00 +#define CS4271_DACCTL_DEM_441 0x10 +#define CS4271_DACCTL_DEM_48 0x20 +#define CS4271_DACCTL_DEM_32 0x30 + +#define CS4271_DACCTL_SVRU 0x08 +#define CS4271_DACCTL_SRD 0x04 +#define CS4271_DACCTL_INVA 0x02 +#define CS4271_DACCTL_INVB 0x01 + +#define CS4271_DACVOL_BEQUA 0x40 +#define CS4271_DACVOL_SOFT 0x20 +#define CS4271_DACVOL_ZEROC 0x10 + +#define CS4271_DACVOL_ATAPI_MASK 0x0F +#define CS4271_DACVOL_ATAPI_M_M 0x00 +#define CS4271_DACVOL_ATAPI_M_BR 0x01 +#define CS4271_DACVOL_ATAPI_M_BL 0x02 +#define CS4271_DACVOL_ATAPI_M_BLR2 0x03 +#define CS4271_DACVOL_ATAPI_AR_M 0x04 +#define CS4271_DACVOL_ATAPI_AR_BR 0x05 +#define CS4271_DACVOL_ATAPI_AR_BL 0x06 +#define CS4271_DACVOL_ATAPI_AR_BLR2 0x07 +#define CS4271_DACVOL_ATAPI_AL_M 0x08 +#define CS4271_DACVOL_ATAPI_AL_BR 0x09 +#define CS4271_DACVOL_ATAPI_AL_BL 0x0A +#define CS4271_DACVOL_ATAPI_AL_BLR2 0x0B +#define CS4271_DACVOL_ATAPI_ALR2_M 0x0C +#define CS4271_DACVOL_ATAPI_ALR2_BR 0x0D +#define CS4271_DACVOL_ATAPI_ALR2_BL 0x0E +#define CS4271_DACVOL_ATAPI_ALR2_BLR2 0x0F + +#define CS4271_VOLA_MUTE 0x80 +#define CS4271_VOLA_VOL_MASK 0x7F +#define CS4271_VOLB_MUTE 0x80 +#define CS4271_VOLB_VOL_MASK 0x7F + +#define CS4271_ADCCTL_DITHER16 0x20 + +#define CS4271_ADCCTL_ADC_DIF_MASK 0x10 +#define CS4271_ADCCTL_ADC_DIF_LJ 0x00 +#define CS4271_ADCCTL_ADC_DIF_I2S 0x10 + +#define CS4271_ADCCTL_MUTEA 0x08 +#define CS4271_ADCCTL_MUTEB 0x04 +#define CS4271_ADCCTL_HPFDA 0x02 +#define CS4271_ADCCTL_HPFDB 0x01 + +#define CS4271_MODE2_LOOP 0x10 +#define CS4271_MODE2_MUTECAEQUB 0x08 +#define CS4271_MODE2_FREEZE 0x04 +#define CS4271_MODE2_CPEN 0x02 +#define CS4271_MODE2_PDN 0x01 + +#define CS4271_CHIPID_PART_MASK 0xF0 +#define CS4271_CHIPID_REV_MASK 0x0F + +/* + * Default CS4271 power-up configuration + * Array contains non-existing in hw register at address 0 + * Array do not include Chip ID, as codec driver does not use + * registers read operations at all + */ +static const u8 cs4271_dflt_reg[CS4271_NR_REGS] = { + 0, + 0, + CS4271_DACCTL_AMUTE, + CS4271_DACVOL_SOFT | CS4271_DACVOL_ATAPI_AL_BR, + 0, + 0, + 0, + 0, +}; + +struct cs4271_private { + /* SND_SOC_I2C or SND_SOC_SPI */ + enum snd_soc_control_type bus_type; + void *control_data; + unsigned int mclk; + bool master; + bool deemph; + /* Current sample rate for de-emphasis control */ + int rate; + /* GPIO driving Reset pin, if any */ + int gpio_nreset; + /* GPIO that disable serial bus, if any */ + int gpio_disable; +}; + +struct cs4271_clk_cfg { + unsigned int ratio; /* MCLK / sample rate */ + u8 speed_mode; /* codec speed mode: 1x, 2x, 4x */ + u8 mclk_master; /* ratio bit mask for Master mode */ + u8 mclk_slave; /* ratio bit mask for Slave mode */ +}; + +static struct cs4271_clk_cfg cs4271_clk_tab[] = { + {64, CS4271_MODE1_MODE_4X, CS4271_MODE1_DIV_1, CS4271_MODE1_DIV_1}, + {96, CS4271_MODE1_MODE_4X, CS4271_MODE1_DIV_15, CS4271_MODE1_DIV_1}, + {128, CS4271_MODE1_MODE_2X, CS4271_MODE1_DIV_1, CS4271_MODE1_DIV_1}, + {192, CS4271_MODE1_MODE_2X, CS4271_MODE1_DIV_15, CS4271_MODE1_DIV_1}, + {256, CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_1, CS4271_MODE1_DIV_1}, + {384, CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_15, CS4271_MODE1_DIV_1}, + {512, CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_2, CS4271_MODE1_DIV_1}, + {768, CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_3, CS4271_MODE1_DIV_3}, + {1024, CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_3, CS4271_MODE1_DIV_3} +}; + +#define CS4171_NR_RATIOS ARRAY_SIZE(cs4271_clk_tab) + +/* + * @freq is the desired MCLK rate + * MCLK rate should (c) be the sample rate, multiplied by one of the + * ratios listed in cs4271_mclk_fs_ratios table + */ +static int cs4271_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + + cs4271->mclk = freq; + return 0; +} + +static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + unsigned int val = 0; + + switch (format & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + cs4271->master = 0; + break; + case SND_SOC_DAIFMT_CBM_CFM: + cs4271->master = 1; + val |= CS4271_MODE1_MASTER; + break; + default: + dev_err(codec->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_LEFT_J: + val |= CS4271_MODE1_DAC_DIF_LJ; + snd_soc_update_bits(codec, CS4271_ADCCTL, + CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_LJ); + break; + case SND_SOC_DAIFMT_I2S: + val |= CS4271_MODE1_DAC_DIF_I2S; + snd_soc_update_bits(codec, CS4271_ADCCTL, + CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_I2S); + break; + default: + dev_err(codec->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + snd_soc_update_bits(codec, CS4271_MODE1, + CS4271_MODE1_DAC_DIF_MASK | CS4271_MODE1_MASTER, val); + + return 0; +} + +static int cs4271_deemph[] = {0, 44100, 48000, 32000}; + +static int cs4271_set_deemph(struct snd_soc_codec *codec) +{ + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + int i; + int val = CS4271_DACCTL_DEM_DIS; + + if (cs4271->deemph) { + /* Find closest de-emphasis freq */ + val = 1; + for (i = 2; i < ARRAY_SIZE(cs4271_deemph); i++) + if (abs(cs4271_deemph[i] - cs4271->rate) < + abs(cs4271_deemph[val] - cs4271->rate)) + val = i; + val <<= 4; + } + + return snd_soc_update_bits(codec, CS4271_DACCTL, + CS4271_DACCTL_DEM_MASK, val); +} + +static int cs4271_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = cs4271->deemph; + return 0; +} + +static int cs4271_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + + cs4271->deemph = ucontrol->value.enumerated.item[0]; + return cs4271_set_deemph(codec); +} + +static int cs4271_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + unsigned int i, ratio, val; + + cs4271->rate = params_rate(params); + ratio = cs4271->mclk / cs4271->rate; + for (i = 0; i < CS4171_NR_RATIOS; i++) + if (cs4271_clk_tab[i].ratio == ratio) + break; + + if ((i == CS4171_NR_RATIOS) || ((ratio == 1024) && cs4271->master)) { + dev_err(codec->dev, "Invalid sample rate\n"); + return -EINVAL; + } + + /* Configure DAC */ + val = cs4271_clk_tab[i].speed_mode; + + if (cs4271->master) + val |= cs4271_clk_tab[i].mclk_master; + else + val |= cs4271_clk_tab[i].mclk_slave; + + snd_soc_update_bits(codec, CS4271_MODE1, + CS4271_MODE1_MODE_MASK | CS4271_MODE1_DIV_MASK, val); + + return cs4271_set_deemph(codec); +} + +static int cs4271_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + int val_a = 0; + int val_b = 0; + + if (mute) { + val_a = CS4271_VOLA_MUTE; + val_b = CS4271_VOLB_MUTE; + } + + snd_soc_update_bits(codec, CS4271_VOLA, CS4271_VOLA_MUTE, val_a); + snd_soc_update_bits(codec, CS4271_VOLB, CS4271_VOLB_MUTE, val_b); + + return 0; +} + +/* CS4271 controls */ +static DECLARE_TLV_DB_SCALE(cs4271_dac_tlv, -12700, 100, 0); + +static const struct snd_kcontrol_new cs4271_snd_controls[] = { + SOC_DOUBLE_R_TLV("Master Playback Volume", CS4271_VOLA, CS4271_VOLB, + 0, 0x7F, 1, cs4271_dac_tlv), + SOC_SINGLE("Digital Loopback Switch", CS4271_MODE2, 4, 1, 0), + SOC_SINGLE("Soft Ramp Switch", CS4271_DACVOL, 5, 1, 0), + SOC_SINGLE("Zero Cross Switch", CS4271_DACVOL, 4, 1, 0), + SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0, + cs4271_get_deemph, cs4271_put_deemph), + SOC_SINGLE("Auto-Mute Switch", CS4271_DACCTL, 7, 1, 0), + SOC_SINGLE("Slow Roll Off Filter Switch", CS4271_DACCTL, 6, 1, 0), + SOC_SINGLE("Soft Volume Ramp-Up Switch", CS4271_DACCTL, 3, 1, 0), + SOC_SINGLE("Soft Ramp-Down Switch", CS4271_DACCTL, 2, 1, 0), + SOC_SINGLE("Left Channel Inversion Switch", CS4271_DACCTL, 1, 1, 0), + SOC_SINGLE("Right Channel Inversion Switch", CS4271_DACCTL, 0, 1, 0), + SOC_DOUBLE("Master Capture Switch", CS4271_ADCCTL, 3, 2, 1, 1), + SOC_SINGLE("Dither 16-Bit Data Switch", CS4271_ADCCTL, 5, 1, 0), + SOC_DOUBLE("High Pass Filter Switch", CS4271_ADCCTL, 1, 0, 1, 1), + SOC_DOUBLE_R("Master Playback Switch", CS4271_VOLA, CS4271_VOLB, + 7, 1, 1), +}; + +static struct snd_soc_dai_ops cs4271_dai_ops = { + .hw_params = cs4271_hw_params, + .set_sysclk = cs4271_set_dai_sysclk, + .set_fmt = cs4271_set_dai_fmt, + .digital_mute = cs4271_digital_mute, +}; + +struct snd_soc_dai_driver cs4271_dai = { + .name = "cs4271-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = CS4271_PCM_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = CS4271_PCM_FORMATS, + }, + .ops = &cs4271_dai_ops, + .symmetric_rates = 1, +}; + +#ifdef CONFIG_PM +static int cs4271_soc_suspend(struct snd_soc_codec *codec, pm_message_t mesg) +{ + /* Set power-down bit */ + snd_soc_update_bits(codec, CS4271_MODE2, 0, CS4271_MODE2_PDN); + return 0; +} + +static int cs4271_soc_resume(struct snd_soc_codec *codec) +{ + /* Restore codec state */ + snd_soc_cache_sync(codec); + /* then disable the power-down bit */ + snd_soc_update_bits(codec, CS4271_MODE2, CS4271_MODE2_PDN, 0); + return 0; +} +#else +#define cs4271_soc_suspend NULL +#define cs4271_soc_resume NULL +#endif /* CONFIG_PM */ + +static int cs4271_probe(struct snd_soc_codec *codec) +{ + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + struct cs4271_platform_data *cs4271plat = codec->dev->platform_data; + int ret; + int gpio_nreset = -EINVAL; + int gpio_disable = -EINVAL; + + codec->control_data = cs4271->control_data; + + if (cs4271plat) { + if (gpio_is_valid(cs4271plat->gpio_nreset)) + gpio_nreset = cs4271plat->gpio_nreset; + if (gpio_is_valid(cs4271plat->gpio_disable)) + gpio_disable = cs4271plat->gpio_disable; + } + + if (gpio_disable >= 0) + if (gpio_request(gpio_disable, "CS4271 Disable")) + gpio_disable = -EINVAL; + if (gpio_disable >= 0) + gpio_direction_output(gpio_disable, 0); + + if (gpio_nreset >= 0) + if (gpio_request(gpio_nreset, "CS4271 Reset")) + gpio_nreset = -EINVAL; + if (gpio_nreset >= 0) { + /* Reset codec */ + gpio_direction_output(gpio_nreset, 0); + udelay(1); + gpio_set_value(gpio_nreset, 1); + /* Give the codec time to wake up */ + udelay(1); + } + + cs4271->gpio_nreset = gpio_nreset; + cs4271->gpio_disable = gpio_disable; + + /* + * In case of I2C, chip address specified in board data. + * So cache IO operations use 8 bit codec register address. + * In case of SPI, chip address and register address + * passed together as 16 bit value. + * Anyway, register address is masked with 0xFF inside + * soc-cache code. + */ + if (cs4271->bus_type == SND_SOC_SPI) + ret = snd_soc_codec_set_cache_io(codec, 16, 8, + cs4271->bus_type); + else + ret = snd_soc_codec_set_cache_io(codec, 8, 8, + cs4271->bus_type); + if (ret) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + + snd_soc_update_bits(codec, CS4271_MODE2, 0, + CS4271_MODE2_PDN | CS4271_MODE2_CPEN); + snd_soc_update_bits(codec, CS4271_MODE2, CS4271_MODE2_PDN, 0); + /* Power-up sequence requires 85 uS */ + udelay(85); + + return snd_soc_add_controls(codec, cs4271_snd_controls, + ARRAY_SIZE(cs4271_snd_controls)); +} + +static int cs4271_remove(struct snd_soc_codec *codec) +{ + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + int gpio_nreset, gpio_disable; + + gpio_nreset = cs4271->gpio_nreset; + gpio_disable = cs4271->gpio_disable; + + if (gpio_is_valid(gpio_nreset)) { + /* Set codec to the reset state */ + gpio_set_value(gpio_nreset, 0); + gpio_free(gpio_nreset); + } + + if (gpio_is_valid(gpio_disable)) + gpio_free(gpio_disable); + + return 0; +}; + +struct snd_soc_codec_driver soc_codec_dev_cs4271 = { + .probe = cs4271_probe, + .remove = cs4271_remove, + .suspend = cs4271_soc_suspend, + .resume = cs4271_soc_resume, + .reg_cache_default = cs4271_dflt_reg, + .reg_cache_size = ARRAY_SIZE(cs4271_dflt_reg), + .reg_word_size = sizeof(cs4271_dflt_reg[0]), + .compress_type = SND_SOC_FLAT_COMPRESSION, +}; + +#if defined(CONFIG_SPI_MASTER) +static int __devinit cs4271_spi_probe(struct spi_device *spi) +{ + struct cs4271_private *cs4271; + + cs4271 = devm_kzalloc(&spi->dev, sizeof(*cs4271), GFP_KERNEL); + if (!cs4271) + return -ENOMEM; + + spi_set_drvdata(spi, cs4271); + cs4271->control_data = spi; + cs4271->bus_type = SND_SOC_SPI; + + return snd_soc_register_codec(&spi->dev, &soc_codec_dev_cs4271, + &cs4271_dai, 1); +} + +static int __devexit cs4271_spi_remove(struct spi_device *spi) +{ + snd_soc_unregister_codec(&spi->dev); + return 0; +} + +static struct spi_driver cs4271_spi_driver = { + .driver = { + .name = "cs4271", + .owner = THIS_MODULE, + }, + .probe = cs4271_spi_probe, + .remove = __devexit_p(cs4271_spi_remove), +}; +#endif /* defined(CONFIG_SPI_MASTER) */ + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static struct i2c_device_id cs4271_i2c_id[] = { + {"cs4271", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id); + +static int __devinit cs4271_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cs4271_private *cs4271; + + cs4271 = devm_kzalloc(&client->dev, sizeof(*cs4271), GFP_KERNEL); + if (!cs4271) + return -ENOMEM; + + i2c_set_clientdata(client, cs4271); + cs4271->control_data = client; + cs4271->bus_type = SND_SOC_I2C; + + return snd_soc_register_codec(&client->dev, &soc_codec_dev_cs4271, + &cs4271_dai, 1); +} + +static int __devexit cs4271_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static struct i2c_driver cs4271_i2c_driver = { + .driver = { + .name = "cs4271", + .owner = THIS_MODULE, + }, + .id_table = cs4271_i2c_id, + .probe = cs4271_i2c_probe, + .remove = __devexit_p(cs4271_i2c_remove), +}; +#endif /* defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) */ + +/* + * We only register our serial bus driver here without + * assignment to particular chip. So if any of the below + * fails, there is some problem with I2C or SPI subsystem. + * In most cases this module will be compiled with support + * of only one serial bus. + */ +static int __init cs4271_modinit(void) +{ + int ret; + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + ret = i2c_add_driver(&cs4271_i2c_driver); + if (ret) { + pr_err("Failed to register CS4271 I2C driver: %d\n", ret); + return ret; + } +#endif + +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&cs4271_spi_driver); + if (ret) { + pr_err("Failed to register CS4271 SPI driver: %d\n", ret); + return ret; + } +#endif + + return 0; +} +module_init(cs4271_modinit); + +static void __exit cs4271_modexit(void) +{ +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&cs4271_spi_driver); +#endif + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + i2c_del_driver(&cs4271_i2c_driver); +#endif +} +module_exit(cs4271_modexit); + +MODULE_AUTHOR("Alexander Sverdlin "); +MODULE_DESCRIPTION("Cirrus Logic CS4271 ALSA SoC Codec Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From c358e640a669b528b32af5442c92b856de623e1c Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Fri, 21 Jan 2011 15:29:02 +0000 Subject: ASoC: soc-cache: Add trace event for snd_soc_cache_sync() This patch makes it easy to see when the syncing process begins and ends. You can also enable the snd_soc_reg_write tracepoint to see which registers are being synced. Signed-off-by: Dimitris Papastamos Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/trace/events/asoc.h | 25 +++++++++++++++++++++++++ sound/soc/soc-cache.c | 10 ++++++++++ 2 files changed, 35 insertions(+) (limited to 'include') diff --git a/include/trace/events/asoc.h b/include/trace/events/asoc.h index 186e84db4b54..ae973d2e27a1 100644 --- a/include/trace/events/asoc.h +++ b/include/trace/events/asoc.h @@ -229,6 +229,31 @@ TRACE_EVENT(snd_soc_jack_notify, TP_printk("jack=%s %x", __get_str(name), (int)__entry->val) ); +TRACE_EVENT(snd_soc_cache_sync, + + TP_PROTO(struct snd_soc_codec *codec, const char *type, + const char *status), + + TP_ARGS(codec, type, status), + + TP_STRUCT__entry( + __string( name, codec->name ) + __string( status, status ) + __string( type, type ) + __field( int, id ) + ), + + TP_fast_assign( + __assign_str(name, codec->name); + __assign_str(status, status); + __assign_str(type, type); + __entry->id = codec->id; + ), + + TP_printk("codec=%s.%d type=%s status=%s", __get_str(name), + (int)__entry->id, __get_str(type), __get_str(status)) +); + #endif /* _TRACE_ASOC_H */ /* This part must be outside protection */ diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index f83483963791..db66dc44add2 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -18,6 +18,8 @@ #include #include +#include + static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec, unsigned int reg) { @@ -1601,18 +1603,26 @@ EXPORT_SYMBOL_GPL(snd_soc_cache_write); int snd_soc_cache_sync(struct snd_soc_codec *codec) { int ret; + const char *name; if (!codec->cache_sync) { return 0; } + if (codec->cache_ops->name) + name = codec->cache_ops->name; + else + name = "unknown"; + if (codec->cache_ops && codec->cache_ops->sync) { if (codec->cache_ops->name) dev_dbg(codec->dev, "Syncing %s cache for %s codec\n", codec->cache_ops->name, codec->name); + trace_snd_soc_cache_sync(codec, name, "start"); ret = codec->cache_ops->sync(codec); if (!ret) codec->cache_sync = 0; + trace_snd_soc_cache_sync(codec, name, "end"); return ret; } -- cgit v1.2.3-59-g8ed1b From 4d805f7b6607f6e547dc22e5d57c201e43d21c05 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 20 Jan 2011 11:46:02 +0900 Subject: ASoC: sh: fsi: Add snd_soc_dai_set_fmt support This patch add snd_soc_dai_ops :: set_fmt to FSI driver and select master/slave clock mode by snd_soc_dai_set_fmt on fsi-xxx.c instead of platform infomation code. This patch remove fsi_is_master function which is no longer needed. Signed-off-by: Kuninori Morimoto Acked-by: Liam Girdwood Acked-by: Paul Mundt Signed-off-by: Mark Brown --- arch/arm/mach-shmobile/board-ag5evm.c | 4 +-- arch/arm/mach-shmobile/board-ap4evb.c | 2 -- arch/arm/mach-shmobile/board-mackerel.c | 2 -- arch/sh/boards/mach-ecovec24/setup.c | 2 -- arch/sh/boards/mach-se/7724/setup.c | 2 -- include/sound/sh_fsi.h | 8 +---- sound/soc/sh/fsi-ak4642.c | 13 +++++--- sound/soc/sh/fsi-da7210.c | 12 ++++++-- sound/soc/sh/fsi-hdmi.c | 11 +++++++ sound/soc/sh/fsi.c | 54 +++++++++++++++++++-------------- 10 files changed, 63 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-shmobile/board-ag5evm.c b/arch/arm/mach-shmobile/board-ag5evm.c index c18a740a4159..9ee55e0fbeb1 100644 --- a/arch/arm/mach-shmobile/board-ag5evm.c +++ b/arch/arm/mach-shmobile/board-ag5evm.c @@ -119,9 +119,7 @@ static struct platform_device keysc_device = { /* FSI A */ static struct sh_fsi_platform_info fsi_info = { - .porta_flags = SH_FSI_OUT_SLAVE_MODE | - SH_FSI_IN_SLAVE_MODE | - SH_FSI_OFMT(I2S) | + .porta_flags = SH_FSI_OFMT(I2S) | SH_FSI_IFMT(I2S), }; diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c index 3cf0951caa2d..d503a74e30e4 100644 --- a/arch/arm/mach-shmobile/board-ap4evb.c +++ b/arch/arm/mach-shmobile/board-ap4evb.c @@ -674,8 +674,6 @@ static int fsi_set_rate(struct device *dev, int is_porta, int rate, int enable) static struct sh_fsi_platform_info fsi_info = { .porta_flags = SH_FSI_BRS_INV | - SH_FSI_OUT_SLAVE_MODE | - SH_FSI_IN_SLAVE_MODE | SH_FSI_OFMT(PCM) | SH_FSI_IFMT(PCM), diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c index 7b15d21f0f68..425962d5b29c 100644 --- a/arch/arm/mach-shmobile/board-mackerel.c +++ b/arch/arm/mach-shmobile/board-mackerel.c @@ -611,8 +611,6 @@ fsi_set_rate_end: static struct sh_fsi_platform_info fsi_info = { .porta_flags = SH_FSI_BRS_INV | - SH_FSI_OUT_SLAVE_MODE | - SH_FSI_IN_SLAVE_MODE | SH_FSI_OFMT(PCM) | SH_FSI_IFMT(PCM), diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c index 33b662999fc6..037416f346cf 100644 --- a/arch/sh/boards/mach-ecovec24/setup.c +++ b/arch/sh/boards/mach-ecovec24/setup.c @@ -724,8 +724,6 @@ static struct platform_device camera_devices[] = { /* FSI */ static struct sh_fsi_platform_info fsi_info = { .portb_flags = SH_FSI_BRS_INV | - SH_FSI_OUT_SLAVE_MODE | - SH_FSI_IN_SLAVE_MODE | SH_FSI_OFMT(I2S) | SH_FSI_IFMT(I2S), }; diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c index 527679394a25..b4aef05dd8b5 100644 --- a/arch/sh/boards/mach-se/7724/setup.c +++ b/arch/sh/boards/mach-se/7724/setup.c @@ -287,8 +287,6 @@ static struct platform_device ceu1_device = { /* change J20, J21, J22 pin to 1-2 connection to use slave mode */ static struct sh_fsi_platform_info fsi_info = { .porta_flags = SH_FSI_BRS_INV | - SH_FSI_OUT_SLAVE_MODE | - SH_FSI_IN_SLAVE_MODE | SH_FSI_OFMT(PCM) | SH_FSI_IFMT(PCM), }; diff --git a/include/sound/sh_fsi.h b/include/sound/sh_fsi.h index d79894192ae3..18e43279f70f 100644 --- a/include/sound/sh_fsi.h +++ b/include/sound/sh_fsi.h @@ -17,12 +17,11 @@ /* flags format - * 0xABCDEEFF + * 0xABC0EEFF * * A: channel size for TDM (input) * B: channel size for TDM (ooutput) * C: inversion - * D: mode * E: input format * F: output format */ @@ -46,11 +45,6 @@ #define SH_FSI_LRS_INV (1 << 22) #define SH_FSI_BRS_INV (1 << 23) -/* mode */ -#define SH_FSI_MODE_MASK 0x000F0000 -#define SH_FSI_IN_SLAVE_MODE (1 << 16) /* default master mode */ -#define SH_FSI_OUT_SLAVE_MODE (1 << 17) /* default master mode */ - /* DI format */ #define SH_FSI_FMT_MASK 0x000000FF #define SH_FSI_IFMT(x) (((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 8) diff --git a/sound/soc/sh/fsi-ak4642.c b/sound/soc/sh/fsi-ak4642.c index a722a4c661ff..ce058c749e6a 100644 --- a/sound/soc/sh/fsi-ak4642.c +++ b/sound/soc/sh/fsi-ak4642.c @@ -23,15 +23,20 @@ struct fsi_ak4642_data { static int fsi_ak4642_dai_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *codec = rtd->codec_dai; + struct snd_soc_dai *cpu = rtd->cpu_dai; int ret; - ret = snd_soc_dai_set_fmt(dai, SND_SOC_DAIFMT_LEFT_J | - SND_SOC_DAIFMT_CBM_CFM); + ret = snd_soc_dai_set_fmt(codec, SND_SOC_DAIFMT_LEFT_J | + SND_SOC_DAIFMT_CBM_CFM); if (ret < 0) return ret; - ret = snd_soc_dai_set_sysclk(dai, 0, 11289600, 0); + ret = snd_soc_dai_set_sysclk(codec, 0, 11289600, 0); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_CBS_CFS); return ret; } diff --git a/sound/soc/sh/fsi-da7210.c b/sound/soc/sh/fsi-da7210.c index e8df9da92f71..9b24ed466ab1 100644 --- a/sound/soc/sh/fsi-da7210.c +++ b/sound/soc/sh/fsi-da7210.c @@ -15,11 +15,19 @@ static int fsi_da7210_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *codec = rtd->codec_dai; + struct snd_soc_dai *cpu = rtd->cpu_dai; + int ret; - return snd_soc_dai_set_fmt(dai, + ret = snd_soc_dai_set_fmt(codec, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_CBS_CFS); + + return ret; } static struct snd_soc_dai_link fsi_da7210_dai = { diff --git a/sound/soc/sh/fsi-hdmi.c b/sound/soc/sh/fsi-hdmi.c index a52dd8ec71d3..96d8ce3f3211 100644 --- a/sound/soc/sh/fsi-hdmi.c +++ b/sound/soc/sh/fsi-hdmi.c @@ -12,6 +12,16 @@ #include #include +static int fsi_hdmi_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *cpu = rtd->cpu_dai; + int ret; + + ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_CBM_CFM); + + return ret; +} + static struct snd_soc_dai_link fsi_dai_link = { .name = "HDMI", .stream_name = "HDMI", @@ -19,6 +29,7 @@ static struct snd_soc_dai_link fsi_dai_link = { .codec_dai_name = "sh_mobile_hdmi-hifi", .platform_name = "sh_fsi2", .codec_name = "sh-mobile-hdmi", + .init = fsi_hdmi_dai_init, }; static struct snd_soc_card fsi_soc_card = { diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 1d0a16e80919..5f39f364effd 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -78,6 +78,8 @@ /* CKG1 */ #define ACKMD_MASK 0x00007000 #define BPFMD_MASK 0x00000700 +#define DIMD (1 << 4) +#define DOMD (1 << 0) /* A/B MST_CTLR */ #define BP (1 << 4) /* Fix the signal of Biphase output */ @@ -292,21 +294,6 @@ static inline struct fsi_stream *fsi_get_stream(struct fsi_priv *fsi, return is_play ? &fsi->playback : &fsi->capture; } -static int fsi_is_master_mode(struct fsi_priv *fsi, int is_play) -{ - u32 mode; - u32 flags = fsi_get_info_flags(fsi); - - mode = is_play ? SH_FSI_OUT_SLAVE_MODE : SH_FSI_IN_SLAVE_MODE; - - /* return - * 1 : master mode - * 0 : slave mode - */ - - return (mode & flags) != mode; -} - static u32 fsi_get_port_shift(struct fsi_priv *fsi, int is_play) { int is_porta = fsi_is_port_a(fsi); @@ -764,19 +751,11 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, u32 fmt; u32 data; int is_play = fsi_is_play(substream); - int is_master; io = fsi_get_stream(fsi, is_play); pm_runtime_get_sync(dai->dev); - /* CKG1 */ - data = is_play ? (1 << 0) : (1 << 4); - is_master = fsi_is_master_mode(fsi, is_play); - if (is_master) - fsi_reg_mask_set(fsi, CKG1, data, data); - else - fsi_reg_mask_set(fsi, CKG1, data, 0); /* clock inversion (CKG2) */ data = 0; @@ -893,6 +872,34 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } +static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai); + u32 data = 0; + int ret; + + pm_runtime_get_sync(dai->dev); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + data = DIMD | DOMD; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + ret = -EINVAL; + goto set_fmt_exit; + } + fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data); + ret = 0; + +set_fmt_exit: + pm_runtime_put_sync(dai->dev); + + return ret; +} + static int fsi_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -979,6 +986,7 @@ static struct snd_soc_dai_ops fsi_dai_ops = { .startup = fsi_dai_startup, .shutdown = fsi_dai_shutdown, .trigger = fsi_dai_trigger, + .set_fmt = fsi_dai_set_fmt, .hw_params = fsi_dai_hw_params, }; -- cgit v1.2.3-59-g8ed1b From 181e055e6bed80afbf8ba2bb5e3ce84fbd3f633c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 24 Jan 2011 14:05:25 +0000 Subject: ASoC: Fix type for snd_soc_volatile_register() We generally refer to registers as unsigned ints (including in the underlying CODEC driver operation). Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 3 ++- sound/soc/soc-core.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index c184f84a354c..1355ef029d82 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -267,7 +267,8 @@ int snd_soc_register_codec(struct device *dev, const struct snd_soc_codec_driver *codec_drv, struct snd_soc_dai_driver *dai_drv, int num_dai); void snd_soc_unregister_codec(struct device *dev); -int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg); +int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, + unsigned int reg); int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, int addr_bits, int data_bits, enum snd_soc_control_type control); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index b0e7689159c1..14861f95f629 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2029,7 +2029,8 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) * * Boolean function indiciating if a CODEC register is volatile. */ -int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg) +int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, + unsigned int reg) { if (codec->volatile_register) return codec->volatile_register(codec, reg); -- cgit v1.2.3-59-g8ed1b From 3d23c73fa0a47e8aecd2a4d8f280f45f6f7611a1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 24 Jan 2011 21:51:25 +0000 Subject: ASoC: Remove controls from sequenced PGA arguments We have zero users for PGA controls and the core support for them was removed a while ago so no point in cut'n'pasting them into new macros, even if it's too much hassle to update the existing ones. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc-dapm.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 6c9ae237814b..6a25e6993859 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -158,11 +158,11 @@ .event = wevent, .event_flags = wflags} /* additional sequencing control within an event type */ -#define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, wcontrols, \ - wncontrols, wevent, wflags) \ +#define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, \ + wevent, wflags) \ { .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \ - .event = wevent, .event_flags = wflags, .subseq = wsubseq} + .invert = winvert, .event = wevent, .event_flags = wflags, \ + .subseq = wsubseq} #define SND_SOC_DAPM_SUPPLY_S(wname, wsubseq, wreg, wshift, winvert, wevent, \ wflags) \ { .id = snd_soc_dapm_supply, .name = wname, .reg = wreg, \ -- cgit v1.2.3-59-g8ed1b From f17c13ca52d5c5a6a164536244a6debb8cd17983 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 24 Jan 2011 10:43:19 +0900 Subject: ASoC: sh: fsi: modify selection method of I2S/PCM/SPDIF format Current format selection of FSI-codecs depended on platform information for FSI, and chip default settings for codecs. It is not understandable/formal method. This patch modify FSI and FSI-codecs to use snd_soc_dai_set_fmt. But FSI can use I2S/PCM and SPDIF format today. It can be selected to I2S/PCM by snd_soc_dai_set_fmt, but can not select SPDIF. So, this patch change FSI platform information to have DAI/SPDIF mode. If platform selects DAI mode (default), FSI-codecs can select I2S/PCM by snd_soc_dai_set_fmt, and if it is SPDIF mode, FSI become SPDIF format. Signed-off-by: Kuninori Morimoto Acked-by: Paul Mundt Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- arch/arm/mach-shmobile/board-ag5evm.c | 8 --- arch/arm/mach-shmobile/board-ap4evb.c | 6 +- arch/arm/mach-shmobile/board-mackerel.c | 6 +- arch/sh/boards/mach-ecovec24/setup.c | 4 +- arch/sh/boards/mach-se/7724/setup.c | 4 +- include/sound/sh_fsi.h | 70 ++++++--------------- sound/soc/sh/fsi-ak4642.c | 3 +- sound/soc/sh/fsi-da7210.c | 3 +- sound/soc/sh/fsi.c | 106 +++++++++++++++++--------------- 9 files changed, 84 insertions(+), 126 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-shmobile/board-ag5evm.c b/arch/arm/mach-shmobile/board-ag5evm.c index 9ee55e0fbeb1..343362d02075 100644 --- a/arch/arm/mach-shmobile/board-ag5evm.c +++ b/arch/arm/mach-shmobile/board-ag5evm.c @@ -118,11 +118,6 @@ static struct platform_device keysc_device = { }; /* FSI A */ -static struct sh_fsi_platform_info fsi_info = { - .porta_flags = SH_FSI_OFMT(I2S) | - SH_FSI_IFMT(I2S), -}; - static struct resource fsi_resources[] = { [0] = { .name = "FSI", @@ -141,9 +136,6 @@ static struct platform_device fsi_device = { .id = -1, .num_resources = ARRAY_SIZE(fsi_resources), .resource = fsi_resources, - .dev = { - .platform_data = &fsi_info, - }, }; static struct resource sh_mmcif_resources[] = { diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c index 920ed81f1c61..17f528a76a1c 100644 --- a/arch/arm/mach-shmobile/board-ap4evb.c +++ b/arch/arm/mach-shmobile/board-ap4evb.c @@ -673,14 +673,12 @@ static int fsi_set_rate(struct device *dev, int is_porta, int rate, int enable) } static struct sh_fsi_platform_info fsi_info = { - .porta_flags = SH_FSI_BRS_INV | - SH_FSI_OFMT(PCM) | - SH_FSI_IFMT(PCM), + .porta_flags = SH_FSI_BRS_INV, .portb_flags = SH_FSI_BRS_INV | SH_FSI_BRM_INV | SH_FSI_LRS_INV | - SH_FSI_OFMT(SPDIF), + SH_FSI_FMT_SPDIF, .set_rate = fsi_set_rate, }; diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c index aa4bcc347044..73b8c90b5072 100644 --- a/arch/arm/mach-shmobile/board-mackerel.c +++ b/arch/arm/mach-shmobile/board-mackerel.c @@ -614,14 +614,12 @@ fsi_set_rate_end: } static struct sh_fsi_platform_info fsi_info = { - .porta_flags = SH_FSI_BRS_INV | - SH_FSI_OFMT(PCM) | - SH_FSI_IFMT(PCM), + .porta_flags = SH_FSI_BRS_INV, .portb_flags = SH_FSI_BRS_INV | SH_FSI_BRM_INV | SH_FSI_LRS_INV | - SH_FSI_OFMT(SPDIF), + SH_FSI_FMT_SPDIF, .set_rate = fsi_set_rate, }; diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c index 037416f346cf..b96b79b970b2 100644 --- a/arch/sh/boards/mach-ecovec24/setup.c +++ b/arch/sh/boards/mach-ecovec24/setup.c @@ -723,9 +723,7 @@ static struct platform_device camera_devices[] = { /* FSI */ static struct sh_fsi_platform_info fsi_info = { - .portb_flags = SH_FSI_BRS_INV | - SH_FSI_OFMT(I2S) | - SH_FSI_IFMT(I2S), + .portb_flags = SH_FSI_BRS_INV, }; static struct resource fsi_resources[] = { diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c index b4aef05dd8b5..c8bcf6a19b55 100644 --- a/arch/sh/boards/mach-se/7724/setup.c +++ b/arch/sh/boards/mach-se/7724/setup.c @@ -286,9 +286,7 @@ static struct platform_device ceu1_device = { /* FSI */ /* change J20, J21, J22 pin to 1-2 connection to use slave mode */ static struct sh_fsi_platform_info fsi_info = { - .porta_flags = SH_FSI_BRS_INV | - SH_FSI_OFMT(PCM) | - SH_FSI_IFMT(PCM), + .porta_flags = SH_FSI_BRS_INV, }; static struct resource fsi_resources[] = { diff --git a/include/sound/sh_fsi.h b/include/sound/sh_fsi.h index 18e43279f70f..9a155f9d0a12 100644 --- a/include/sound/sh_fsi.h +++ b/include/sound/sh_fsi.h @@ -15,61 +15,29 @@ #define FSI_PORT_A 0 #define FSI_PORT_B 1 -/* flags format - - * 0xABC0EEFF - * - * A: channel size for TDM (input) - * B: channel size for TDM (ooutput) - * C: inversion - * E: input format - * F: output format - */ - #include #include -/* TDM channel */ -#define SH_FSI_SET_CH_I(x) ((x & 0xF) << 28) -#define SH_FSI_SET_CH_O(x) ((x & 0xF) << 24) - -#define SH_FSI_CH_IMASK 0xF0000000 -#define SH_FSI_CH_OMASK 0x0F000000 -#define SH_FSI_GET_CH_I(x) ((x & SH_FSI_CH_IMASK) >> 28) -#define SH_FSI_GET_CH_O(x) ((x & SH_FSI_CH_OMASK) >> 24) - -/* clock inversion */ -#define SH_FSI_INVERSION_MASK 0x00F00000 -#define SH_FSI_LRM_INV (1 << 20) -#define SH_FSI_BRM_INV (1 << 21) -#define SH_FSI_LRS_INV (1 << 22) -#define SH_FSI_BRS_INV (1 << 23) - -/* DI format */ -#define SH_FSI_FMT_MASK 0x000000FF -#define SH_FSI_IFMT(x) (((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 8) -#define SH_FSI_OFMT(x) (((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 0) -#define SH_FSI_GET_IFMT(x) ((x >> 8) & SH_FSI_FMT_MASK) -#define SH_FSI_GET_OFMT(x) ((x >> 0) & SH_FSI_FMT_MASK) - -#define SH_FSI_FMT_MONO 0 -#define SH_FSI_FMT_MONO_DELAY 1 -#define SH_FSI_FMT_PCM 2 -#define SH_FSI_FMT_I2S 3 -#define SH_FSI_FMT_TDM 4 -#define SH_FSI_FMT_TDM_DELAY 5 -#define SH_FSI_FMT_SPDIF 6 - - -#define SH_FSI_IFMT_TDM_CH(x) \ - (SH_FSI_IFMT(TDM) | SH_FSI_SET_CH_I(x)) -#define SH_FSI_IFMT_TDM_DELAY_CH(x) \ - (SH_FSI_IFMT(TDM_DELAY) | SH_FSI_SET_CH_I(x)) +/* + * flags format + * + * 0x000000BA + * + * A: inversion + * B: format mode + */ -#define SH_FSI_OFMT_TDM_CH(x) \ - (SH_FSI_OFMT(TDM) | SH_FSI_SET_CH_O(x)) -#define SH_FSI_OFMT_TDM_DELAY_CH(x) \ - (SH_FSI_OFMT(TDM_DELAY) | SH_FSI_SET_CH_O(x)) +/* A: clock inversion */ +#define SH_FSI_INVERSION_MASK 0x0000000F +#define SH_FSI_LRM_INV (1 << 0) +#define SH_FSI_BRM_INV (1 << 1) +#define SH_FSI_LRS_INV (1 << 2) +#define SH_FSI_BRS_INV (1 << 3) + +/* B: format mode */ +#define SH_FSI_FMT_MASK 0x000000F0 +#define SH_FSI_FMT_DAI (0 << 4) +#define SH_FSI_FMT_SPDIF (1 << 4) /* diff --git a/sound/soc/sh/fsi-ak4642.c b/sound/soc/sh/fsi-ak4642.c index ce058c749e6a..d6f4703b3c07 100644 --- a/sound/soc/sh/fsi-ak4642.c +++ b/sound/soc/sh/fsi-ak4642.c @@ -36,7 +36,8 @@ static int fsi_ak4642_dai_init(struct snd_soc_pcm_runtime *rtd) if (ret < 0) return ret; - ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_CBS_CFS); + ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_LEFT_J | + SND_SOC_DAIFMT_CBS_CFS); return ret; } diff --git a/sound/soc/sh/fsi-da7210.c b/sound/soc/sh/fsi-da7210.c index 9b24ed466ab1..dbafd7ac5590 100644 --- a/sound/soc/sh/fsi-da7210.c +++ b/sound/soc/sh/fsi-da7210.c @@ -25,7 +25,8 @@ static int fsi_da7210_init(struct snd_soc_pcm_runtime *rtd) if (ret < 0) return ret; - ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_CBS_CFS); + ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_CBS_CFS); return ret; } diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 3c53693d7266..0c9997e2d8c0 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -757,9 +757,7 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct fsi_priv *fsi = fsi_get_priv(substream); - struct fsi_master *master = fsi_get_master(fsi); u32 flags = fsi_get_info_flags(fsi); - u32 fmt; u32 data; int is_play = fsi_is_play(substream); @@ -779,54 +777,6 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, fsi_reg_write(fsi, CKG2, data); - /* do fmt, di fmt */ - data = 0; - fmt = is_play ? SH_FSI_GET_OFMT(flags) : SH_FSI_GET_IFMT(flags); - switch (fmt) { - case SH_FSI_FMT_MONO: - data = CR_MONO; - fsi->chan_num = 1; - break; - case SH_FSI_FMT_MONO_DELAY: - data = CR_MONO_D; - fsi->chan_num = 1; - break; - case SH_FSI_FMT_PCM: - data = CR_PCM; - fsi->chan_num = 2; - break; - case SH_FSI_FMT_I2S: - data = CR_I2S; - fsi->chan_num = 2; - break; - case SH_FSI_FMT_TDM: - fsi->chan_num = is_play ? - SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags); - data = CR_TDM | (fsi->chan_num - 1); - break; - case SH_FSI_FMT_TDM_DELAY: - fsi->chan_num = is_play ? - SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags); - data = CR_TDM_D | (fsi->chan_num - 1); - break; - case SH_FSI_FMT_SPDIF: - if (master->core->ver < 2) { - dev_err(dai->dev, "This FSI can not use SPDIF\n"); - return -EINVAL; - } - data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM; - fsi->chan_num = 2; - fsi_spdif_clk_ctrl(fsi, 1); - fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD); - break; - default: - dev_err(dai->dev, "unknown format.\n"); - return -EINVAL; - } - is_play ? - fsi_reg_write(fsi, DO_FMT, data) : - fsi_reg_write(fsi, DI_FMT, data); - /* irq clear */ fsi_irq_disable(fsi, is_play); fsi_irq_clear_status(fsi); @@ -881,9 +831,52 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } +static int fsi_set_fmt_dai(struct fsi_priv *fsi, unsigned int fmt) +{ + u32 data = 0; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + data = CR_I2S; + fsi->chan_num = 2; + break; + case SND_SOC_DAIFMT_LEFT_J: + data = CR_PCM; + fsi->chan_num = 2; + break; + default: + return -EINVAL; + } + + fsi_reg_write(fsi, DO_FMT, data); + fsi_reg_write(fsi, DI_FMT, data); + + return 0; +} + +static int fsi_set_fmt_spdif(struct fsi_priv *fsi) +{ + struct fsi_master *master = fsi_get_master(fsi); + u32 data = 0; + + if (master->core->ver < 2) + return -EINVAL; + + data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM; + fsi->chan_num = 2; + fsi_spdif_clk_ctrl(fsi, 1); + fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD); + + fsi_reg_write(fsi, DO_FMT, data); + fsi_reg_write(fsi, DI_FMT, data); + + return 0; +} + static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai); + u32 flags = fsi_get_info_flags(fsi); u32 data = 0; int ret; @@ -901,7 +894,18 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) goto set_fmt_exit; } fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data); - ret = 0; + + /* set format */ + switch (flags & SH_FSI_FMT_MASK) { + case SH_FSI_FMT_DAI: + ret = fsi_set_fmt_dai(fsi, fmt & SND_SOC_DAIFMT_FORMAT_MASK); + break; + case SH_FSI_FMT_SPDIF: + ret = fsi_set_fmt_spdif(fsi); + break; + default: + ret = -EINVAL; + } set_fmt_exit: pm_runtime_put_sync(dai->dev); -- cgit v1.2.3-59-g8ed1b From 0dca1793063c28dde8f6c49c9c72203fe5cb6efc Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Wed, 26 Jan 2011 19:32:14 +0100 Subject: ALSA: hdspm - Add support for RME RayDAT and AIO Incorporate changes by Florian Faber into hdspm.c. Code taken from http://wiki.linuxproaudio.org/index.php/Driver:hdspe Heavily reworked to mostly comply with the coding standard (whitespace fixes, line width, C++ style comments) The code was tested and confirmed to be working on RME RayDAT. Signed-off-by: Adrian Knoth Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- include/sound/hdspm.h | 349 +++- sound/pci/rme9652/hdspm.c | 4231 +++++++++++++++++++++++++++++++++------------ 2 files changed, 3424 insertions(+), 1156 deletions(-) (limited to 'include') diff --git a/include/sound/hdspm.h b/include/sound/hdspm.h index 81990b2bcc98..c3f18194b08e 100644 --- a/include/sound/hdspm.h +++ b/include/sound/hdspm.h @@ -3,8 +3,8 @@ /* * Copyright (C) 2003 Winfried Ritsch (IEM) * based on hdsp.h from Thomas Charbonnel (thomas@undata.org) - * - * + * + * * 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 @@ -23,50 +23,41 @@ /* Maximum channels is 64 even on 56Mode you have 64playbacks to matrix */ #define HDSPM_MAX_CHANNELS 64 -/* -------------------- IOCTL Peak/RMS Meters -------------------- */ - -/* peam rms level structure like we get from hardware - - maybe in future we can memory map it so I just copy it - to user on ioctl call now an dont change anything - rms are made out of low and high values - where (long) ????_rms = (????_rms_l >> 8) + ((????_rms_h & 0xFFFFFF00)<<24) - (i asume so from the code) -*/ - -struct hdspm_peak_rms { - - unsigned int level_offset[1024]; +enum hdspm_io_type { + MADI, + MADIface, + AIO, + AES32, + RayDAT +}; - unsigned int input_peak[64]; - unsigned int playback_peak[64]; - unsigned int output_peak[64]; - unsigned int xxx_peak[64]; /* not used */ +enum hdspm_speed { + ss, + ds, + qs +}; - unsigned int reserved[256]; /* not used */ +/* -------------------- IOCTL Peak/RMS Meters -------------------- */ - unsigned int input_rms_l[64]; - unsigned int playback_rms_l[64]; - unsigned int output_rms_l[64]; - unsigned int xxx_rms_l[64]; /* not used */ +struct hdspm_peak_rms { + uint32_t input_peaks[64]; + uint32_t playback_peaks[64]; + uint32_t output_peaks[64]; - unsigned int input_rms_h[64]; - unsigned int playback_rms_h[64]; - unsigned int output_rms_h[64]; - unsigned int xxx_rms_h[64]; /* not used */ -}; + uint64_t input_rms[64]; + uint64_t playback_rms[64]; + uint64_t output_rms[64]; -struct hdspm_peak_rms_ioctl { - struct hdspm_peak_rms *peak; + uint8_t speed; /* enum {ss, ds, qs} */ + int status2; }; -/* use indirect access due to the limit of ioctl bit size */ #define SNDRV_HDSPM_IOCTL_GET_PEAK_RMS \ - _IOR('H', 0x40, struct hdspm_peak_rms_ioctl) + _IOR('H', 0x42, struct hdspm_peak_rms) /* ------------ CONFIG block IOCTL ---------------------- */ -struct hdspm_config_info { +struct hdspm_config { unsigned char pref_sync_ref; unsigned char wordclock_sync_check; unsigned char madi_sync_check; @@ -80,18 +71,121 @@ struct hdspm_config_info { unsigned int analog_out; }; -#define SNDRV_HDSPM_IOCTL_GET_CONFIG_INFO \ - _IOR('H', 0x41, struct hdspm_config_info) +#define SNDRV_HDSPM_IOCTL_GET_CONFIG \ + _IOR('H', 0x41, struct hdspm_config) + +/** + * If there's a TCO (TimeCode Option) board installed, + * there are further options and status data available. + * The hdspm_ltc structure contains the current SMPTE + * timecode and some status information and can be + * obtained via SNDRV_HDSPM_IOCTL_GET_LTC or in the + * hdspm_status struct. + **/ + +enum hdspm_ltc_format { + format_invalid, + fps_24, + fps_25, + fps_2997, + fps_30 +}; + +enum hdspm_ltc_frame { + frame_invalid, + drop_frame, + full_frame +}; + +enum hdspm_ltc_input_format { + ntsc, + pal, + no_video +}; + +struct hdspm_ltc { + unsigned int ltc; + + enum hdspm_ltc_format format; + enum hdspm_ltc_frame frame; + enum hdspm_ltc_input_format input_format; +}; + +#define SNDRV_HDSPM_IOCTL_GET_LTC _IOR('H', 0x46, struct hdspm_mixer_ioctl) + +/** + * The status data reflects the device's current state + * as determined by the card's configuration and + * connection status. + **/ + +enum hdspm_sync { + hdspm_sync_no_lock = 0, + hdspm_sync_lock = 1, + hdspm_sync_sync = 2 +}; + +enum hdspm_madi_input { + hdspm_input_optical = 0, + hdspm_input_coax = 1 +}; + +enum hdspm_madi_channel_format { + hdspm_format_ch_64 = 0, + hdspm_format_ch_56 = 1 +}; + +enum hdspm_madi_frame_format { + hdspm_frame_48 = 0, + hdspm_frame_96 = 1 +}; + +enum hdspm_syncsource { + syncsource_wc = 0, + syncsource_madi = 1, + syncsource_tco = 2, + syncsource_sync = 3, + syncsource_none = 4 +}; + +struct hdspm_status { + uint8_t card_type; /* enum hdspm_io_type */ + enum hdspm_syncsource autosync_source; + uint64_t card_clock; + uint32_t master_period; + + union { + struct { + uint8_t sync_wc; /* enum hdspm_sync */ + uint8_t sync_madi; /* enum hdspm_sync */ + uint8_t sync_tco; /* enum hdspm_sync */ + uint8_t sync_in; /* enum hdspm_sync */ + uint8_t madi_input; /* enum hdspm_madi_input */ + uint8_t channel_format; /* enum hdspm_madi_channel_format */ + uint8_t frame_format; /* enum hdspm_madi_frame_format */ + } madi; + } card_specific; +}; -/* get Soundcard Version */ +#define SNDRV_HDSPM_IOCTL_GET_STATUS \ + _IOR('H', 0x47, struct hdspm_status) + +/** + * Get information about the card and its add-ons. + **/ + +#define HDSPM_ADDON_TCO 1 struct hdspm_version { + uint8_t card_type; /* enum hdspm_io_type */ + char cardname[20]; + unsigned int serial; unsigned short firmware_rev; + int addons; }; -#define SNDRV_HDSPM_IOCTL_GET_VERSION _IOR('H', 0x43, struct hdspm_version) - +#define SNDRV_HDSPM_IOCTL_GET_VERSION _IOR('H', 0x48, struct hdspm_version) /* ------------- get Matrix Mixer IOCTL --------------- */ @@ -103,7 +197,7 @@ struct hdspm_version { /* equivalent to hardware definition, maybe for future feature of mmap of * them */ -/* each of 64 outputs has 64 infader and 64 outfader: +/* each of 64 outputs has 64 infader and 64 outfader: Ins to Outs mixer[out].in[in], Outstreams to Outs mixer[out].pb[pb] */ #define HDSPM_MIXER_CHANNELS HDSPM_MAX_CHANNELS @@ -131,4 +225,175 @@ typedef struct hdspm_version hdspm_version_t; typedef struct hdspm_channelfader snd_hdspm_channelfader_t; typedef struct hdspm_mixer hdspm_mixer_t; -#endif /* __SOUND_HDSPM_H */ +/* These tables map the ALSA channels 1..N to the channels that we + need to use in order to find the relevant channel buffer. RME + refers to this kind of mapping as between "the ADAT channel and + the DMA channel." We index it using the logical audio channel, + and the value is the DMA channel (i.e. channel buffer number) + where the data for that channel can be read/written from/to. +*/ + +char channel_map_unity_ss[HDSPM_MAX_CHANNELS] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63 +}; + +char channel_map_unity_ds[HDSPM_MAX_CHANNELS] = { + 0, 2, 4, 6, 8, 10, 12, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, 50, 52, 54, 56, 58, 60, 62, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +char channel_map_unity_qs[HDSPM_MAX_CHANNELS] = { + 0, 4, 8, 12, 16, 20, 24, 28, + 32, 36, 40, 44, 48, 52, 56, 60, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +char channel_map_raydat_ss[HDSPM_MAX_CHANNELS] = { + 4, 5, 6, 7, 8, 9, 10, 11, /* ADAT 1 */ + 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT 2 */ + 20, 21, 22, 23, 24, 25, 26, 27, /* ADAT 3 */ + 28, 29, 30, 31, 32, 33, 34, 35, /* ADAT 4 */ + 0, 1, /* AES */ + 2, 3, /* SPDIF */ + -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +char channel_map_raydat_ds[HDSPM_MAX_CHANNELS] = { + 4, 5, 6, 7, /* ADAT 1 */ + 8, 9, 10, 11, /* ADAT 2 */ + 12, 13, 14, 15, /* ADAT 3 */ + 16, 17, 18, 19, /* ADAT 4 */ + 0, 1, /* AES */ + 2, 3, /* SPDIF */ + -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +char channel_map_raydat_qs[HDSPM_MAX_CHANNELS] = { + 4, 5, /* ADAT 1 */ + 6, 7, /* ADAT 2 */ + 8, 9, /* ADAT 3 */ + 10, 11, /* ADAT 4 */ + 0, 1, /* AES */ + 2, 3, /* SPDIF */ + -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +char channel_map_aio_in_ss[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line in */ + 8, 9, /* aes in, */ + 10, 11, /* spdif in */ + 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT in */ + -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +char channel_map_aio_out_ss[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line out */ + 8, 9, /* aes out */ + 10, 11, /* spdif out */ + 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT out */ + 6, 7, /* phone out */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +char channel_map_aio_in_ds[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line in */ + 8, 9, /* aes in */ + 10, 11, /* spdif in */ + 12, 14, 16, 18, /* adat in */ + -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +char channel_map_aio_out_ds[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line out */ + 8, 9, /* aes out */ + 10, 11, /* spdif out */ + 12, 14, 16, 18, /* adat out */ + 6, 7, /* phone out */ + -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +char channel_map_aio_in_qs[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line in */ + 8, 9, /* aes in */ + 10, 11, /* spdif in */ + 12, 16, /* adat in */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +char channel_map_aio_out_qs[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line out */ + 8, 9, /* aes out */ + 10, 11, /* spdif out */ + 12, 16, /* adat out */ + 6, 7, /* phone out */ + -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +#endif diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index f5eadfc0672a..2db871d9a007 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -8,6 +8,21 @@ * Modified 2006-06-01 for AES32 support by Remy Bruno * * + * Modified 2009-04-13 for proper metering by Florian Faber + * + * + * Modified 2009-04-14 for native float support by Florian Faber + * + * + * Modified 2009-04-26 fixed bug in rms metering by Florian Faber + * + * + * Modified 2009-04-30 added hw serial number support by Florian Faber + * + * Modified 2011-01-14 added S/PDIF input on RayDATs by Adrian Knoth + * + * Modified 2011-01-25 variable period sizes on RayDAT/AIO by Adrian Knoth + * * 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 @@ -35,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -47,15 +63,6 @@ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */ -/* Disable precise pointer at start */ -static int precise_ptr[SNDRV_CARDS]; - -/* Send all playback to line outs */ -static int line_outs_monitor[SNDRV_CARDS]; - -/* Enable Analog Outs on Channel 63/64 by default */ -static int enable_monitor[SNDRV_CARDS]; - module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for RME HDSPM interface."); @@ -65,42 +72,39 @@ MODULE_PARM_DESC(id, "ID string for RME HDSPM interface."); module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable/disable specific HDSPM soundcards."); -module_param_array(precise_ptr, bool, NULL, 0444); -MODULE_PARM_DESC(precise_ptr, "Enable or disable precise pointer."); - -module_param_array(line_outs_monitor, bool, NULL, 0444); -MODULE_PARM_DESC(line_outs_monitor, - "Send playback streams to analog outs by default."); - -module_param_array(enable_monitor, bool, NULL, 0444); -MODULE_PARM_DESC(enable_monitor, - "Enable Analog Out on Channel 63/64 by default."); MODULE_AUTHOR - ("Winfried Ritsch , " - "Paul Davis , " - "Marcus Andersson, Thomas Charbonnel , " - "Remy Bruno "); +( + "Winfried Ritsch , " + "Paul Davis , " + "Marcus Andersson, Thomas Charbonnel , " + "Remy Bruno , " + "Florian Faber , " + "Adrian Knoth " +); MODULE_DESCRIPTION("RME HDSPM"); MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); -/* --- Write registers. --- +/* --- Write registers. --- These are defined as byte-offsets from the iobase value. */ +#define HDSPM_WR_SETTINGS 0 +#define HDSPM_outputBufferAddress 32 +#define HDSPM_inputBufferAddress 36 #define HDSPM_controlRegister 64 #define HDSPM_interruptConfirmation 96 #define HDSPM_control2Reg 256 /* not in specs ???????? */ #define HDSPM_freqReg 256 /* for AES32 */ -#define HDSPM_midiDataOut0 352 /* just believe in old code */ -#define HDSPM_midiDataOut1 356 +#define HDSPM_midiDataOut0 352 /* just believe in old code */ +#define HDSPM_midiDataOut1 356 #define HDSPM_eeprom_wr 384 /* for AES32 */ /* DMA enable for 64 channels, only Bit 0 is relevant */ -#define HDSPM_outputEnableBase 512 /* 512-767 input DMA */ +#define HDSPM_outputEnableBase 512 /* 512-767 input DMA */ #define HDSPM_inputEnableBase 768 /* 768-1023 output DMA */ -/* 16 page addresses for each of the 64 channels DMA buffer in and out +/* 16 page addresses for each of the 64 channels DMA buffer in and out (each 64k=16*4k) Buffer must be 4k aligned (which is default i386 ????) */ #define HDSPM_pageAddressBufferOut 8192 #define HDSPM_pageAddressBufferIn (HDSPM_pageAddressBufferOut+64*16*4) @@ -119,22 +123,84 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_statusRegister2 192 #define HDSPM_timecodeRegister 128 +/* AIO, RayDAT */ +#define HDSPM_RD_STATUS_0 0 +#define HDSPM_RD_STATUS_1 64 +#define HDSPM_RD_STATUS_2 128 +#define HDSPM_RD_STATUS_3 192 + +#define HDSPM_RD_TCO 256 +#define HDSPM_RD_PLL_FREQ 512 +#define HDSPM_WR_TCO 128 + +#define HDSPM_TCO1_TCO_lock 0x00000001 +#define HDSPM_TCO1_WCK_Input_Range_LSB 0x00000002 +#define HDSPM_TCO1_WCK_Input_Range_MSB 0x00000004 +#define HDSPM_TCO1_LTC_Input_valid 0x00000008 +#define HDSPM_TCO1_WCK_Input_valid 0x00000010 +#define HDSPM_TCO1_Video_Input_Format_NTSC 0x00000020 +#define HDSPM_TCO1_Video_Input_Format_PAL 0x00000040 + +#define HDSPM_TCO1_set_TC 0x00000100 +#define HDSPM_TCO1_set_drop_frame_flag 0x00000200 +#define HDSPM_TCO1_LTC_Format_LSB 0x00000400 +#define HDSPM_TCO1_LTC_Format_MSB 0x00000800 + +#define HDSPM_TCO2_TC_run 0x00010000 +#define HDSPM_TCO2_WCK_IO_ratio_LSB 0x00020000 +#define HDSPM_TCO2_WCK_IO_ratio_MSB 0x00040000 +#define HDSPM_TCO2_set_num_drop_frames_LSB 0x00080000 +#define HDSPM_TCO2_set_num_drop_frames_MSB 0x00100000 +#define HDSPM_TCO2_set_jam_sync 0x00200000 +#define HDSPM_TCO2_set_flywheel 0x00400000 + +#define HDSPM_TCO2_set_01_4 0x01000000 +#define HDSPM_TCO2_set_pull_down 0x02000000 +#define HDSPM_TCO2_set_pull_up 0x04000000 +#define HDSPM_TCO2_set_freq 0x08000000 +#define HDSPM_TCO2_set_term_75R 0x10000000 +#define HDSPM_TCO2_set_input_LSB 0x20000000 +#define HDSPM_TCO2_set_input_MSB 0x40000000 +#define HDSPM_TCO2_set_freq_from_app 0x80000000 + + +#define HDSPM_midiDataOut0 352 +#define HDSPM_midiDataOut1 356 +#define HDSPM_midiDataOut2 368 + #define HDSPM_midiDataIn0 360 #define HDSPM_midiDataIn1 364 +#define HDSPM_midiDataIn2 372 +#define HDSPM_midiDataIn3 376 /* status is data bytes in MIDI-FIFO (0-128) */ -#define HDSPM_midiStatusOut0 384 -#define HDSPM_midiStatusOut1 388 -#define HDSPM_midiStatusIn0 392 -#define HDSPM_midiStatusIn1 396 +#define HDSPM_midiStatusOut0 384 +#define HDSPM_midiStatusOut1 388 +#define HDSPM_midiStatusOut2 400 + +#define HDSPM_midiStatusIn0 392 +#define HDSPM_midiStatusIn1 396 +#define HDSPM_midiStatusIn2 404 +#define HDSPM_midiStatusIn3 408 /* the meters are regular i/o-mapped registers, but offset considerably from the rest. the peak registers are reset - when read; the least-significant 4 bits are full-scale counters; + when read; the least-significant 4 bits are full-scale counters; the actual peak value is in the most-significant 24 bits. */ -#define HDSPM_MADI_peakrmsbase 4096 /* 4096-8191 2x64x32Bit Meters */ + +#define HDSPM_MADI_INPUT_PEAK 4096 +#define HDSPM_MADI_PLAYBACK_PEAK 4352 +#define HDSPM_MADI_OUTPUT_PEAK 4608 + +#define HDSPM_MADI_INPUT_RMS_L 6144 +#define HDSPM_MADI_PLAYBACK_RMS_L 6400 +#define HDSPM_MADI_OUTPUT_RMS_L 6656 + +#define HDSPM_MADI_INPUT_RMS_H 7168 +#define HDSPM_MADI_PLAYBACK_RMS_H 7424 +#define HDSPM_MADI_OUTPUT_RMS_H 7680 /* --- Control Register bits --------- */ #define HDSPM_Start (1<<0) /* start engine */ @@ -143,7 +209,9 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_Latency1 (1<<2) /* where n is defined */ #define HDSPM_Latency2 (1<<3) /* by Latency{2,1,0} */ -#define HDSPM_ClockModeMaster (1<<4) /* 1=Master, 0=Slave/Autosync */ +#define HDSPM_ClockModeMaster (1<<4) /* 1=Master, 0=Autosync */ +#define HDSPM_c0Master 0x1 /* Master clock bit in settings + register [RayDAT, AIO] */ #define HDSPM_AudioInterruptEnable (1<<5) /* what do you think ? */ @@ -157,7 +225,7 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); 56channelMODE=0 */ /* MADI ONLY*/ #define HDSPM_Emphasis (1<<10) /* Emphasis */ /* AES32 ONLY */ -#define HDSPM_AutoInp (1<<11) /* Auto Input (takeover) == Safe Mode, +#define HDSPM_AutoInp (1<<11) /* Auto Input (takeover) == Safe Mode, 0=off, 1=on */ /* MADI ONLY */ #define HDSPM_Dolby (1<<11) /* Dolby = "NonAudio" ?? */ /* AES32 ONLY */ @@ -166,22 +234,23 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); */ #define HDSPM_InputSelect1 (1<<15) /* should be 0 */ -#define HDSPM_SyncRef0 (1<<16) /* 0=WOrd, 1=MADI */ -#define HDSPM_SyncRef1 (1<<17) /* for AES32: SyncRefN codes the AES # */ #define HDSPM_SyncRef2 (1<<13) #define HDSPM_SyncRef3 (1<<25) #define HDSPM_SMUX (1<<18) /* Frame ??? */ /* MADI ONY */ -#define HDSPM_clr_tms (1<<19) /* clear track marker, do not use +#define HDSPM_clr_tms (1<<19) /* clear track marker, do not use AES additional bits in lower 5 Audiodatabits ??? */ #define HDSPM_taxi_reset (1<<20) /* ??? */ /* MADI ONLY ? */ #define HDSPM_WCK48 (1<<20) /* Frame ??? = HDSPM_SMUX */ /* AES32 ONLY */ -#define HDSPM_Midi0InterruptEnable (1<<22) -#define HDSPM_Midi1InterruptEnable (1<<23) +#define HDSPM_Midi0InterruptEnable 0x0400000 +#define HDSPM_Midi1InterruptEnable 0x0800000 +#define HDSPM_Midi2InterruptEnable 0x0200000 +#define HDSPM_Midi3InterruptEnable 0x4000000 #define HDSPM_LineOut (1<<24) /* Analog Out on channel 63/64 on=1, mute=0 */ +#define HDSPe_FLOAT_FORMAT 0x2000000 #define HDSPM_DS_DoubleWire (1<<26) /* AES32 ONLY */ #define HDSPM_QS_DoubleWire (1<<27) /* AES32 ONLY */ @@ -198,11 +267,18 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_InputCoaxial (HDSPM_InputSelect0) #define HDSPM_SyncRefMask (HDSPM_SyncRef0|HDSPM_SyncRef1|\ HDSPM_SyncRef2|HDSPM_SyncRef3) -#define HDSPM_SyncRef_Word 0 -#define HDSPM_SyncRef_MADI (HDSPM_SyncRef0) -#define HDSPM_SYNC_FROM_WORD 0 /* Preferred sync reference */ -#define HDSPM_SYNC_FROM_MADI 1 /* choices - used by "pref_sync_ref" */ +#define HDSPM_c0_SyncRef0 0x2 +#define HDSPM_c0_SyncRef1 0x4 +#define HDSPM_c0_SyncRef2 0x8 +#define HDSPM_c0_SyncRef3 0x10 +#define HDSPM_c0_SyncRefMask (HDSPM_c0_SyncRef0 | HDSPM_c0_SyncRef1 |\ + HDSPM_c0_SyncRef2 | HDSPM_c0_SyncRef3) + +#define HDSPM_SYNC_FROM_WORD 0 /* Preferred sync reference */ +#define HDSPM_SYNC_FROM_MADI 1 /* choices - used by "pref_sync_ref" */ +#define HDSPM_SYNC_FROM_TCO 2 +#define HDSPM_SYNC_FROM_SYNC_IN 3 #define HDSPM_Frequency32KHz HDSPM_Frequency0 #define HDSPM_Frequency44_1KHz HDSPM_Frequency1 @@ -216,17 +292,6 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_Frequency192KHz (HDSPM_QuadSpeed|HDSPM_Frequency1|\ HDSPM_Frequency0) -/* --- for internal discrimination */ -#define HDSPM_CLOCK_SOURCE_AUTOSYNC 0 /* Sample Clock Sources */ -#define HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ 1 -#define HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ 2 -#define HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ 3 -#define HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ 4 -#define HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ 5 -#define HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ 6 -#define HDSPM_CLOCK_SOURCE_INTERNAL_128KHZ 7 -#define HDSPM_CLOCK_SOURCE_INTERNAL_176_4KHZ 8 -#define HDSPM_CLOCK_SOURCE_INTERNAL_192KHZ 9 /* Synccheck Status */ #define HDSPM_SYNC_CHECK_NO_LOCK 0 @@ -236,14 +301,16 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); /* AutoSync References - used by "autosync_ref" control switch */ #define HDSPM_AUTOSYNC_FROM_WORD 0 #define HDSPM_AUTOSYNC_FROM_MADI 1 -#define HDSPM_AUTOSYNC_FROM_NONE 2 +#define HDSPM_AUTOSYNC_FROM_TCO 2 +#define HDSPM_AUTOSYNC_FROM_SYNC_IN 3 +#define HDSPM_AUTOSYNC_FROM_NONE 4 /* Possible sources of MADI input */ #define HDSPM_OPTICAL 0 /* optical */ #define HDSPM_COAXIAL 1 /* BNC */ #define hdspm_encode_latency(x) (((x)<<1) & HDSPM_LatencyMask) -#define hdspm_decode_latency(x) (((x) & HDSPM_LatencyMask)>>1) +#define hdspm_decode_latency(x) ((((x) & HDSPM_LatencyMask)>>1)) #define hdspm_encode_in(x) (((x)&0x3)<<14) #define hdspm_decode_in(x) (((x)>>14)&0x3) @@ -270,13 +337,21 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_AB_int (1<<2) /* InputChannel Opt=0, Coax=1 * (like inp0) */ + #define HDSPM_madiLock (1<<3) /* MADI Locked =1, no=0 */ +#define HDSPM_madiSync (1<<18) /* MADI is in sync */ + +#define HDSPM_tcoLock 0x00000020 /* Optional TCO locked status FOR HDSPe MADI! */ +#define HDSPM_tcoSync 0x10000000 /* Optional TCO sync status */ + +#define HDSPM_syncInLock 0x00010000 /* Sync In lock status FOR HDSPe MADI! */ +#define HDSPM_syncInSync 0x00020000 /* Sync In sync status FOR HDSPe MADI! */ #define HDSPM_BufferPositionMask 0x000FFC0 /* Bit 6..15 : h/w buffer pointer */ - /* since 64byte accurate last 6 bits - are not used */ + /* since 64byte accurate, last 6 bits are not used */ + + -#define HDSPM_madiSync (1<<18) /* MADI is in sync */ #define HDSPM_DoubleSpeedStatus (1<<19) /* (input) card in double speed */ #define HDSPM_madiFreq0 (1<<22) /* system freq 0=error */ @@ -287,8 +362,19 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_BufferID (1<<26) /* (Double)Buffer ID toggles with * Interrupt */ -#define HDSPM_midi0IRQPending (1<<30) /* MIDI IRQ is pending */ -#define HDSPM_midi1IRQPending (1<<31) /* and aktiv */ +#define HDSPM_tco_detect 0x08000000 +#define HDSPM_tco_lock 0x20000000 + +#define HDSPM_s2_tco_detect 0x00000040 +#define HDSPM_s2_AEBO_D 0x00000080 +#define HDSPM_s2_AEBI_D 0x00000100 + + +#define HDSPM_midi0IRQPending 0x40000000 +#define HDSPM_midi1IRQPending 0x80000000 +#define HDSPM_midi2IRQPending 0x20000000 +#define HDSPM_midi2IRQPendingAES 0x00000020 +#define HDSPM_midi3IRQPending 0x00200000 /* --- status bit helpers */ #define HDSPM_madiFreqMask (HDSPM_madiFreq0|HDSPM_madiFreq1|\ @@ -317,7 +403,10 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_wc_freq2 (1<<7) /* 100=64, 101=88.2, 110=96, */ /* missing Bit for 111=128, 1000=176.4, 1001=192 */ -#define HDSPM_SelSyncRef0 (1<<8) /* Sync Source in slave mode */ +#define HDSPM_SyncRef0 0x10000 /* Sync Reference */ +#define HDSPM_SyncRef1 0x20000 + +#define HDSPM_SelSyncRef0 (1<<8) /* AutoSync Source */ #define HDSPM_SelSyncRef1 (1<<9) /* 000=word, 001=MADI, */ #define HDSPM_SelSyncRef2 (1<<10) /* 111=no valid signal */ @@ -331,11 +420,19 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_wcFreq88_2 (HDSPM_wc_freq0|HDSPM_wc_freq2) #define HDSPM_wcFreq96 (HDSPM_wc_freq1|HDSPM_wc_freq2) +#define HDSPM_status1_F_0 0x0400000 +#define HDSPM_status1_F_1 0x0800000 +#define HDSPM_status1_F_2 0x1000000 +#define HDSPM_status1_F_3 0x2000000 +#define HDSPM_status1_freqMask (HDSPM_status1_F_0|HDSPM_status1_F_1|HDSPM_status1_F_2|HDSPM_status1_F_3) + #define HDSPM_SelSyncRefMask (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|\ HDSPM_SelSyncRef2) #define HDSPM_SelSyncRef_WORD 0 #define HDSPM_SelSyncRef_MADI (HDSPM_SelSyncRef0) +#define HDSPM_SelSyncRef_TCO (HDSPM_SelSyncRef1) +#define HDSPM_SelSyncRef_SyncIn (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1) #define HDSPM_SelSyncRef_NVALID (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|\ HDSPM_SelSyncRef2) @@ -345,7 +442,7 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); /* status */ #define HDSPM_AES32_wcLock 0x0200000 #define HDSPM_AES32_wcFreq_bit 22 -/* (status >> HDSPM_AES32_wcFreq_bit) & 0xF gives WC frequency (cf function +/* (status >> HDSPM_AES32_wcFreq_bit) & 0xF gives WC frequency (cf function HDSPM_bit2freq */ #define HDSPM_AES32_syncref_bit 16 /* (status >> HDSPM_AES32_syncref_bit) & 0xF gives sync source */ @@ -398,28 +495,184 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define MADI_DS_CHANNELS 32 #define MADI_QS_CHANNELS 16 +#define RAYDAT_SS_CHANNELS 36 +#define RAYDAT_DS_CHANNELS 20 +#define RAYDAT_QS_CHANNELS 12 + +#define AIO_IN_SS_CHANNELS 14 +#define AIO_IN_DS_CHANNELS 10 +#define AIO_IN_QS_CHANNELS 8 +#define AIO_OUT_SS_CHANNELS 16 +#define AIO_OUT_DS_CHANNELS 12 +#define AIO_OUT_QS_CHANNELS 10 + /* the size of a substream (1 mono data stream) */ #define HDSPM_CHANNEL_BUFFER_SAMPLES (16*1024) #define HDSPM_CHANNEL_BUFFER_BYTES (4*HDSPM_CHANNEL_BUFFER_SAMPLES) /* the size of the area we need to allocate for DMA transfers. the size is the same regardless of the number of channels, and - also the latency to use. + also the latency to use. for one direction !!! */ #define HDSPM_DMA_AREA_BYTES (HDSPM_MAX_CHANNELS * HDSPM_CHANNEL_BUFFER_BYTES) #define HDSPM_DMA_AREA_KILOBYTES (HDSPM_DMA_AREA_BYTES/1024) /* revisions >= 230 indicate AES32 card */ -#define HDSPM_AESREVISION 230 +#define HDSPM_MADI_REV 210 +#define HDSPM_RAYDAT_REV 211 +#define HDSPM_AIO_REV 212 +#define HDSPM_MADIFACE_REV 213 +#define HDSPM_AES_REV 240 /* speed factor modes */ #define HDSPM_SPEED_SINGLE 0 #define HDSPM_SPEED_DOUBLE 1 #define HDSPM_SPEED_QUAD 2 + /* names for speed modes */ static char *hdspm_speed_names[] = { "single", "double", "quad" }; +static char *texts_autosync_aes_tco[] = { "Word Clock", + "AES1", "AES2", "AES3", "AES4", + "AES5", "AES6", "AES7", "AES8", + "TCO" }; +static char *texts_autosync_aes[] = { "Word Clock", + "AES1", "AES2", "AES3", "AES4", + "AES5", "AES6", "AES7", "AES8" }; +static char *texts_autosync_madi_tco[] = { "Word Clock", + "MADI", "TCO", "Sync In" }; +static char *texts_autosync_madi[] = { "Word Clock", + "MADI", "Sync In" }; + +static char *texts_autosync_raydat_tco[] = { + "Word Clock", + "ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4", + "AES", "SPDIF", "TCO", "Sync In" +}; +static char *texts_autosync_raydat[] = { + "Word Clock", + "ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4", + "AES", "SPDIF", "Sync In" +}; +static char *texts_autosync_aio_tco[] = { + "Word Clock", + "ADAT", "AES", "SPDIF", "TCO", "Sync In" +}; +static char *texts_autosync_aio[] = { "Word Clock", + "ADAT", "AES", "SPDIF", "Sync In" }; + +static char *texts_freq[] = { + "No Lock", + "32 kHz", + "44.1 kHz", + "48 kHz", + "64 kHz", + "88.2 kHz", + "96 kHz", + "128 kHz", + "176.4 kHz", + "192 kHz" +}; + +static char *texts_sync_status[] = { + "no lock", + "lock", + "sync" +}; + +static char *texts_ports_madi[] = { + "MADI.1", "MADI.2", "MADI.3", "MADI.4", "MADI.5", "MADI.6", + "MADI.7", "MADI.8", "MADI.9", "MADI.10", "MADI.11", "MADI.12", + "MADI.13", "MADI.14", "MADI.15", "MADI.16", "MADI.17", "MADI.18", + "MADI.19", "MADI.20", "MADI.21", "MADI.22", "MADI.23", "MADI.24", + "MADI.25", "MADI.26", "MADI.27", "MADI.28", "MADI.29", "MADI.30", + "MADI.31", "MADI.32", "MADI.33", "MADI.34", "MADI.35", "MADI.36", + "MADI.37", "MADI.38", "MADI.39", "MADI.40", "MADI.41", "MADI.42", + "MADI.43", "MADI.44", "MADI.45", "MADI.46", "MADI.47", "MADI.48", + "MADI.49", "MADI.50", "MADI.51", "MADI.52", "MADI.53", "MADI.54", + "MADI.55", "MADI.56", "MADI.57", "MADI.58", "MADI.59", "MADI.60", + "MADI.61", "MADI.62", "MADI.63", "MADI.64", +}; + + +static char *texts_ports_raydat_ss[] = { + "ADAT1.1", "ADAT1.2", "ADAT1.3", "ADAT1.4", "ADAT1.5", "ADAT1.6", + "ADAT1.7", "ADAT1.8", "ADAT2.1", "ADAT2.2", "ADAT2.3", "ADAT2.4", + "ADAT2.5", "ADAT2.6", "ADAT2.7", "ADAT2.8", "ADAT3.1", "ADAT3.2", + "ADAT3.3", "ADAT3.4", "ADAT3.5", "ADAT3.6", "ADAT3.7", "ADAT3.8", + "ADAT4.1", "ADAT4.2", "ADAT4.3", "ADAT4.4", "ADAT4.5", "ADAT4.6", + "ADAT4.7", "ADAT4.8", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R" +}; + +static char *texts_ports_raydat_ds[] = { + "ADAT1.1", "ADAT1.2", "ADAT1.3", "ADAT1.4", + "ADAT2.1", "ADAT2.2", "ADAT2.3", "ADAT2.4", + "ADAT3.1", "ADAT3.2", "ADAT3.3", "ADAT3.4", + "ADAT4.1", "ADAT4.2", "ADAT4.3", "ADAT4.4", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R" +}; + +static char *texts_ports_raydat_qs[] = { + "ADAT1.1", "ADAT1.2", + "ADAT2.1", "ADAT2.2", + "ADAT3.1", "ADAT3.2", + "ADAT4.1", "ADAT4.2", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R" +}; + + +static char *texts_ports_aio_in_ss[] = { + "Analogue.L", "Analogue.R", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R", + "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6", + "ADAT.7", "ADAT.8" +}; + +static char *texts_ports_aio_out_ss[] = { + "Analogue.L", "Analogue.R", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R", + "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6", + "ADAT.7", "ADAT.8", + "Phone.L", "Phone.R" +}; + +static char *texts_ports_aio_in_ds[] = { + "Analogue.L", "Analogue.R", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R", + "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4" +}; + +static char *texts_ports_aio_out_ds[] = { + "Analogue.L", "Analogue.R", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R", + "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", + "Phone.L", "Phone.R" +}; + +static char *texts_ports_aio_in_qs[] = { + "Analogue.L", "Analogue.R", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R", + "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4" +}; + +static char *texts_ports_aio_out_qs[] = { + "Analogue.L", "Analogue.R", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R", + "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", + "Phone.L", "Phone.R" +}; + struct hdspm_midi { struct hdspm *hdspm; int id; @@ -430,6 +683,21 @@ struct hdspm_midi { struct timer_list timer; spinlock_t lock; int pending; + int dataIn; + int statusIn; + int dataOut; + int statusOut; + int ie; + int irq; +}; + +struct hdspm_tco { + int input; + int framerate; + int wordclock; + int samplerate; + int pull; + int term; /* 0 = off, 1 = on */ }; struct hdspm { @@ -441,21 +709,39 @@ struct hdspm { char *card_name; /* for procinfo */ unsigned short firmware_rev; /* dont know if relevant (yes if AES32)*/ - unsigned char is_aes32; /* indicates if card is AES32 */ + uint8_t io_type; - int precise_ptr; /* use precise pointers, to be tested */ int monitor_outs; /* set up monitoring outs init flag */ u32 control_register; /* cached value */ u32 control2_register; /* cached value */ + u32 settings_register; - struct hdspm_midi midi[2]; + struct hdspm_midi midi[4]; struct tasklet_struct midi_tasklet; size_t period_bytes; - unsigned char ss_channels; /* channels of card in single speed */ - unsigned char ds_channels; /* Double Speed */ - unsigned char qs_channels; /* Quad Speed */ + unsigned char ss_in_channels; + unsigned char ds_in_channels; + unsigned char qs_in_channels; + unsigned char ss_out_channels; + unsigned char ds_out_channels; + unsigned char qs_out_channels; + + unsigned char max_channels_in; + unsigned char max_channels_out; + + char *channel_map_in; + char *channel_map_out; + + char *channel_map_in_ss, *channel_map_in_ds, *channel_map_in_qs; + char *channel_map_out_ss, *channel_map_out_ds, *channel_map_out_qs; + + char **port_names_in; + char **port_names_out; + + char **port_names_in_ss, **port_names_in_ds, **port_names_in_qs; + char **port_names_out_ss, **port_names_out_ds, **port_names_out_qs; unsigned char *playback_buffer; /* suitably aligned address */ unsigned char *capture_buffer; /* suitably aligned address */ @@ -468,14 +754,13 @@ struct hdspm { int last_internal_sample_rate; int system_sample_rate; - char *channel_map; /* channel map for DS and Quadspeed */ - int dev; /* Hardware vars... */ int irq; unsigned long port; void __iomem *iobase; int irq_count; /* for debug */ + int midiPorts; struct snd_card *card; /* one card */ struct snd_pcm *pcm; /* has one pcm */ @@ -487,28 +772,15 @@ struct hdspm { struct snd_kcontrol *playback_mixer_ctls[HDSPM_MAX_CHANNELS]; /* but input to much, so not used */ struct snd_kcontrol *input_mixer_ctls[HDSPM_MAX_CHANNELS]; - /* full mixer accessible over mixer ioctl or hwdep-device */ + /* full mixer accessable over mixer ioctl or hwdep-device */ struct hdspm_mixer *mixer; -}; + struct hdspm_tco *tco; /* NULL if no TCO detected */ -/* These tables map the ALSA channels 1..N to the channels that we - need to use in order to find the relevant channel buffer. RME - refer to this kind of mapping as between "the ADAT channel and - the DMA channel." We index it using the logical audio channel, - and the value is the DMA channel (i.e. channel buffer number) - where the data for that channel can be read/written from/to. -*/ + char **texts_autosync; + int texts_autosync_items; -static char channel_map_madi_ss[HDSPM_MAX_CHANNELS] = { - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63 + cycles_t last_interrupt; }; @@ -532,11 +804,11 @@ static int __devinit snd_hdspm_create_alsa_devices(struct snd_card *card, static int __devinit snd_hdspm_create_pcm(struct snd_card *card, struct hdspm * hdspm); -static inline void snd_hdspm_initialize_midi_flush(struct hdspm * hdspm); -static int hdspm_update_simple_mixer_controls(struct hdspm * hdspm); -static int hdspm_autosync_ref(struct hdspm * hdspm); -static int snd_hdspm_set_defaults(struct hdspm * hdspm); -static void hdspm_set_sgbuf(struct hdspm * hdspm, +static inline void snd_hdspm_initialize_midi_flush(struct hdspm *hdspm); +static int hdspm_update_simple_mixer_controls(struct hdspm *hdspm); +static int hdspm_autosync_ref(struct hdspm *hdspm); +static int snd_hdspm_set_defaults(struct hdspm *hdspm); +static void hdspm_set_sgbuf(struct hdspm *hdspm, struct snd_pcm_substream *substream, unsigned int reg, int channels); @@ -550,7 +822,7 @@ static inline int HDSPM_bit2freq(int n) return bit2freq_tab[n]; } -/* Write/read to/from HDSPM with Addresses in Bytes +/* Write/read to/from HDSPM with Adresses in Bytes not words but only 32Bit writes are allowed */ static inline void hdspm_write(struct hdspm * hdspm, unsigned int reg, @@ -564,8 +836,8 @@ static inline unsigned int hdspm_read(struct hdspm * hdspm, unsigned int reg) return readl(hdspm->iobase + reg); } -/* for each output channel (chan) I have an Input (in) and Playback (pb) Fader - mixer is write only on hardware so we have to cache him for read +/* for each output channel (chan) I have an Input (in) and Playback (pb) Fader + mixer is write only on hardware so we have to cache him for read each fader is a u32, but uses only the first 16 bit */ static inline int hdspm_read_in_gain(struct hdspm * hdspm, unsigned int chan, @@ -641,30 +913,67 @@ static int snd_hdspm_use_is_exclusive(struct hdspm *hdspm) /* check for external sample rate */ static int hdspm_external_sample_rate(struct hdspm *hdspm) { - if (hdspm->is_aes32) { - unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); - unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister); - unsigned int timecode = - hdspm_read(hdspm, HDSPM_timecodeRegister); + unsigned int status, status2, timecode; + int syncref, rate = 0, rate_bits; - int syncref = hdspm_autosync_ref(hdspm); + switch (hdspm->io_type) { + case AES32: + status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + status = hdspm_read(hdspm, HDSPM_statusRegister); + timecode = hdspm_read(hdspm, HDSPM_timecodeRegister); + + syncref = hdspm_autosync_ref(hdspm); if (syncref == HDSPM_AES32_AUTOSYNC_FROM_WORD && status & HDSPM_AES32_wcLock) - return HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) - & 0xF); + return HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF); + if (syncref >= HDSPM_AES32_AUTOSYNC_FROM_AES1 && - syncref <= HDSPM_AES32_AUTOSYNC_FROM_AES8 && - status2 & (HDSPM_LockAES >> - (syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1))) - return HDSPM_bit2freq((timecode >> - (4*(syncref-HDSPM_AES32_AUTOSYNC_FROM_AES1))) & 0xF); + syncref <= HDSPM_AES32_AUTOSYNC_FROM_AES8 && + status2 & (HDSPM_LockAES >> + (syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1))) + return HDSPM_bit2freq((timecode >> (4*(syncref-HDSPM_AES32_AUTOSYNC_FROM_AES1))) & 0xF); return 0; - } else { - unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); - unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister); - unsigned int rate_bits; - int rate = 0; + break; + + case MADIface: + status = hdspm_read(hdspm, HDSPM_statusRegister); + + if (!(status & HDSPM_madiLock)) { + rate = 0; /* no lock */ + } else { + switch (status & (HDSPM_status1_freqMask)) { + case HDSPM_status1_F_0*1: + rate = 32000; break; + case HDSPM_status1_F_0*2: + rate = 44100; break; + case HDSPM_status1_F_0*3: + rate = 48000; break; + case HDSPM_status1_F_0*4: + rate = 64000; break; + case HDSPM_status1_F_0*5: + rate = 88200; break; + case HDSPM_status1_F_0*6: + rate = 96000; break; + case HDSPM_status1_F_0*7: + rate = 128000; break; + case HDSPM_status1_F_0*8: + rate = 176400; break; + case HDSPM_status1_F_0*9: + rate = 192000; break; + default: + rate = 0; break; + } + } + + break; + + case MADI: + case AIO: + case RayDAT: + status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + status = hdspm_read(hdspm, HDSPM_statusRegister); + rate = 0; /* if wordclock has synced freq and wordclock is valid */ if ((status2 & HDSPM_wcLock) != 0 && @@ -672,6 +981,7 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm) rate_bits = status2 & HDSPM_wcFreqMask; + switch (rate_bits) { case HDSPM_wcFreq32: rate = 32000; @@ -691,7 +1001,6 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm) case HDSPM_wcFreq96: rate = 96000; break; - /* Quadspeed Bit missing ???? */ default: rate = 0; break; @@ -702,10 +1011,10 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm) * word has priority to MADI */ if (rate != 0 && - (status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD) + (status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD) return rate; - /* maby a madi input (which is taken if sel sync is madi) */ + /* maybe a madi input (which is taken if sel sync is madi) */ if (status & HDSPM_madiLock) { rate_bits = status & HDSPM_madiFreqMask; @@ -742,36 +1051,26 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm) break; } } - return rate; + break; } + + return rate; } /* Latency function */ -static inline void hdspm_compute_period_size(struct hdspm * hdspm) +static inline void hdspm_compute_period_size(struct hdspm *hdspm) { - hdspm->period_bytes = - 1 << ((hdspm_decode_latency(hdspm->control_register) + 8)); + hdspm->period_bytes = 1 << ((hdspm_decode_latency(hdspm->control_register) + 8)); } -static snd_pcm_uframes_t hdspm_hw_pointer(struct hdspm * hdspm) + +static snd_pcm_uframes_t hdspm_hw_pointer(struct hdspm *hdspm) { int position; position = hdspm_read(hdspm, HDSPM_statusRegister); - - if (!hdspm->precise_ptr) - return (position & HDSPM_BufferID) ? - (hdspm->period_bytes / 4) : 0; - - /* hwpointer comes in bytes and is 64Bytes accurate (by docu since - PCI Burst) - i have experimented that it is at most 64 Byte to much for playing - so substraction of 64 byte should be ok for ALSA, but use it only - for application where you know what you do since if you come to - near with record pointer it can be a disaster */ - position &= HDSPM_BufferPositionMask; - position = ((position - 64) % (2 * hdspm->period_bytes)) / 4; + position /= 4; /* Bytes per sample */ return position; } @@ -805,7 +1104,7 @@ static void hdspm_silence_playback(struct hdspm *hdspm) } } -static int hdspm_set_interrupt_interval(struct hdspm * s, unsigned int frames) +static int hdspm_set_interrupt_interval(struct hdspm *s, unsigned int frames) { int n; @@ -829,21 +1128,53 @@ static int hdspm_set_interrupt_interval(struct hdspm * s, unsigned int frames) return 0; } +static u64 hdspm_calc_dds_value(struct hdspm *hdspm, u64 period) +{ + u64 freq_const; + + if (period == 0) + return 0; + + switch (hdspm->io_type) { + case MADI: + case AES32: + freq_const = 110069313433624ULL; + break; + case RayDAT: + case AIO: + freq_const = 104857600000000ULL; + break; + case MADIface: + freq_const = 131072000000000ULL; + } + + return div_u64(freq_const, period); +} + + static void hdspm_set_dds_value(struct hdspm *hdspm, int rate) { u64 n; - + if (rate >= 112000) rate /= 4; else if (rate >= 56000) rate /= 2; - /* RME says n = 104857600000000, but in the windows MADI driver, I see: -// return 104857600000000 / rate; // 100 MHz - return 110100480000000 / rate; // 105 MHz - */ - /* n = 104857600000000ULL; */ /* = 2^20 * 10^8 */ - n = 110100480000000ULL; /* Value checked for AES32 and MADI */ + switch (hdspm->io_type) { + case MADIface: + n = 131072000000000ULL; /* 125 MHz */ + break; + case MADI: + case AES32: + n = 110069313433624ULL; /* 105 MHz */ + break; + case RayDAT: + case AIO: + n = 104857600000000ULL; /* 100 MHz */ + break; + } + n = div_u64(n, rate); /* n should be less than 2^32 for being written to FREQ register */ snd_BUG_ON(n >> 32); @@ -864,13 +1195,13 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally) if (!(hdspm->control_register & HDSPM_ClockModeMaster)) { - /* SLAVE --- */ + /* SLAVE --- */ if (called_internally) { - /* request from ctl or card initialization - just make a warning an remember setting - for future master mode switching */ - + /* request from ctl or card initialization + just make a warning an remember setting + for future master mode switching */ + snd_printk(KERN_WARNING "HDSPM: " "Warning: device is not running " "as a clock master.\n"); @@ -907,7 +1238,7 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally) Note that a similar but essentially insoluble problem exists for externally-driven rate changes. All we can do is to flag rate - changes in the read/write routines. + changes in the read/write routines. */ if (current_rate <= 48000) @@ -975,16 +1306,35 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally) /* For AES32, need to set DDS value in FREQ register For MADI, also apparently */ hdspm_set_dds_value(hdspm, rate); - - if (hdspm->is_aes32 && rate != current_rate) + + if (AES32 == hdspm->io_type && rate != current_rate) hdspm_write(hdspm, HDSPM_eeprom_wr, 0); - - /* For AES32 and for MADI (at least rev 204), channel_map needs to - * always be channel_map_madi_ss, whatever the sample rate */ - hdspm->channel_map = channel_map_madi_ss; hdspm->system_sample_rate = rate; + if (rate <= 48000) { + hdspm->channel_map_in = hdspm->channel_map_in_ss; + hdspm->channel_map_out = hdspm->channel_map_out_ss; + hdspm->max_channels_in = hdspm->ss_in_channels; + hdspm->max_channels_out = hdspm->ss_out_channels; + hdspm->port_names_in = hdspm->port_names_in_ss; + hdspm->port_names_out = hdspm->port_names_out_ss; + } else if (rate <= 96000) { + hdspm->channel_map_in = hdspm->channel_map_in_ds; + hdspm->channel_map_out = hdspm->channel_map_out_ds; + hdspm->max_channels_in = hdspm->ds_in_channels; + hdspm->max_channels_out = hdspm->ds_out_channels; + hdspm->port_names_in = hdspm->port_names_in_ds; + hdspm->port_names_out = hdspm->port_names_out_ds; + } else { + hdspm->channel_map_in = hdspm->channel_map_in_qs; + hdspm->channel_map_out = hdspm->channel_map_out_qs; + hdspm->max_channels_in = hdspm->qs_in_channels; + hdspm->max_channels_out = hdspm->qs_out_channels; + hdspm->port_names_in = hdspm->port_names_in_qs; + hdspm->port_names_out = hdspm->port_names_out_qs; + } + if (not_set != 0) return -1; @@ -1019,39 +1369,26 @@ static inline unsigned char snd_hdspm_midi_read_byte (struct hdspm *hdspm, int id) { /* the hardware already does the relevant bit-mask with 0xff */ - if (id) - return hdspm_read(hdspm, HDSPM_midiDataIn1); - else - return hdspm_read(hdspm, HDSPM_midiDataIn0); + return hdspm_read(hdspm, hdspm->midi[id].dataIn); } static inline void snd_hdspm_midi_write_byte (struct hdspm *hdspm, int id, int val) { /* the hardware already does the relevant bit-mask with 0xff */ - if (id) - hdspm_write(hdspm, HDSPM_midiDataOut1, val); - else - hdspm_write(hdspm, HDSPM_midiDataOut0, val); + return hdspm_write(hdspm, hdspm->midi[id].dataOut, val); } static inline int snd_hdspm_midi_input_available (struct hdspm *hdspm, int id) { - if (id) - return (hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xff); - else - return (hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xff); + return hdspm_read(hdspm, hdspm->midi[id].statusIn) & 0xFF; } static inline int snd_hdspm_midi_output_possible (struct hdspm *hdspm, int id) { int fifo_bytes_used; - if (id) - fifo_bytes_used = hdspm_read(hdspm, HDSPM_midiStatusOut1); - else - fifo_bytes_used = hdspm_read(hdspm, HDSPM_midiStatusOut0); - fifo_bytes_used &= 0xff; + fifo_bytes_used = hdspm_read(hdspm, hdspm->midi[id].statusOut) & 0xFF; if (fifo_bytes_used < 128) return 128 - fifo_bytes_used; @@ -1074,7 +1411,7 @@ static int snd_hdspm_midi_output_write (struct hdspm_midi *hmidi) unsigned char buf[128]; /* Output is not interrupt driven */ - + spin_lock_irqsave (&hmidi->lock, flags); if (hmidi->output && !snd_rawmidi_transmit_empty (hmidi->output)) { @@ -1083,11 +1420,11 @@ static int snd_hdspm_midi_output_write (struct hdspm_midi *hmidi) if (n_pending > 0) { if (n_pending > (int)sizeof (buf)) n_pending = sizeof (buf); - + to_write = snd_rawmidi_transmit (hmidi->output, buf, n_pending); if (to_write > 0) { - for (i = 0; i < to_write; ++i) + for (i = 0; i < to_write; ++i) snd_hdspm_midi_write_byte (hmidi->hdspm, hmidi->id, buf[i]); @@ -1127,12 +1464,11 @@ static int snd_hdspm_midi_input_read (struct hdspm_midi *hmidi) } } hmidi->pending = 0; - if (hmidi->id) - hmidi->hdspm->control_register |= HDSPM_Midi1InterruptEnable; - else - hmidi->hdspm->control_register |= HDSPM_Midi0InterruptEnable; + + hmidi->hdspm->control_register |= hmidi->ie; hdspm_write(hmidi->hdspm, HDSPM_controlRegister, hmidi->hdspm->control_register); + spin_unlock_irqrestore (&hmidi->lock, flags); return snd_hdspm_midi_output_write (hmidi); } @@ -1143,20 +1479,18 @@ snd_hdspm_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) struct hdspm *hdspm; struct hdspm_midi *hmidi; unsigned long flags; - u32 ie; hmidi = substream->rmidi->private_data; hdspm = hmidi->hdspm; - ie = hmidi->id ? - HDSPM_Midi1InterruptEnable : HDSPM_Midi0InterruptEnable; + spin_lock_irqsave (&hdspm->lock, flags); if (up) { - if (!(hdspm->control_register & ie)) { + if (!(hdspm->control_register & hmidi->ie)) { snd_hdspm_flush_midi_input (hdspm, hmidi->id); - hdspm->control_register |= ie; + hdspm->control_register |= hmidi->ie; } } else { - hdspm->control_register &= ~ie; + hdspm->control_register &= ~hmidi->ie; } hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); @@ -1167,14 +1501,14 @@ static void snd_hdspm_midi_output_timer(unsigned long data) { struct hdspm_midi *hmidi = (struct hdspm_midi *) data; unsigned long flags; - + snd_hdspm_midi_output_write(hmidi); spin_lock_irqsave (&hmidi->lock, flags); /* this does not bump hmidi->istimer, because the kernel automatically removed the timer when it expired, and we are now adding it back, thus - leaving istimer wherever it was set before. + leaving istimer wherever it was set before. */ if (hmidi->istimer) { @@ -1288,22 +1622,103 @@ static int __devinit snd_hdspm_create_midi (struct snd_card *card, hdspm->midi[id].hdspm = hdspm; spin_lock_init (&hdspm->midi[id].lock); - sprintf (buf, "%s MIDI %d", card->shortname, id+1); - err = snd_rawmidi_new (card, buf, id, 1, 1, &hdspm->midi[id].rmidi); - if (err < 0) - return err; + if (0 == id) { + if (MADIface == hdspm->io_type) { + /* MIDI-over-MADI on HDSPe MADIface */ + hdspm->midi[0].dataIn = HDSPM_midiDataIn2; + hdspm->midi[0].statusIn = HDSPM_midiStatusIn2; + hdspm->midi[0].dataOut = HDSPM_midiDataOut2; + hdspm->midi[0].statusOut = HDSPM_midiStatusOut2; + hdspm->midi[0].ie = HDSPM_Midi2InterruptEnable; + hdspm->midi[0].irq = HDSPM_midi2IRQPending; + } else { + hdspm->midi[0].dataIn = HDSPM_midiDataIn0; + hdspm->midi[0].statusIn = HDSPM_midiStatusIn0; + hdspm->midi[0].dataOut = HDSPM_midiDataOut0; + hdspm->midi[0].statusOut = HDSPM_midiStatusOut0; + hdspm->midi[0].ie = HDSPM_Midi0InterruptEnable; + hdspm->midi[0].irq = HDSPM_midi0IRQPending; + } + } else if (1 == id) { + hdspm->midi[1].dataIn = HDSPM_midiDataIn1; + hdspm->midi[1].statusIn = HDSPM_midiStatusIn1; + hdspm->midi[1].dataOut = HDSPM_midiDataOut1; + hdspm->midi[1].statusOut = HDSPM_midiStatusOut1; + hdspm->midi[1].ie = HDSPM_Midi1InterruptEnable; + hdspm->midi[1].irq = HDSPM_midi1IRQPending; + } else if ((2 == id) && (MADI == hdspm->io_type)) { + /* MIDI-over-MADI on HDSPe MADI */ + hdspm->midi[2].dataIn = HDSPM_midiDataIn2; + hdspm->midi[2].statusIn = HDSPM_midiStatusIn2; + hdspm->midi[2].dataOut = HDSPM_midiDataOut2; + hdspm->midi[2].statusOut = HDSPM_midiStatusOut2; + hdspm->midi[2].ie = HDSPM_Midi2InterruptEnable; + hdspm->midi[2].irq = HDSPM_midi2IRQPending; + } else if (2 == id) { + /* TCO MTC, read only */ + hdspm->midi[2].dataIn = HDSPM_midiDataIn2; + hdspm->midi[2].statusIn = HDSPM_midiStatusIn2; + hdspm->midi[2].dataOut = -1; + hdspm->midi[2].statusOut = -1; + hdspm->midi[2].ie = HDSPM_Midi2InterruptEnable; + hdspm->midi[2].irq = HDSPM_midi2IRQPendingAES; + } else if (3 == id) { + /* TCO MTC on HDSPe MADI */ + hdspm->midi[3].dataIn = HDSPM_midiDataIn3; + hdspm->midi[3].statusIn = HDSPM_midiStatusIn3; + hdspm->midi[3].dataOut = -1; + hdspm->midi[3].statusOut = -1; + hdspm->midi[3].ie = HDSPM_Midi3InterruptEnable; + hdspm->midi[3].irq = HDSPM_midi3IRQPending; + } + + if ((id < 2) || ((2 == id) && ((MADI == hdspm->io_type) || + (MADIface == hdspm->io_type)))) { + if ((id == 0) && (MADIface == hdspm->io_type)) { + sprintf(buf, "%s MIDIoverMADI", card->shortname); + } else if ((id == 2) && (MADI == hdspm->io_type)) { + sprintf(buf, "%s MIDIoverMADI", card->shortname); + } else { + sprintf(buf, "%s MIDI %d", card->shortname, id+1); + } + err = snd_rawmidi_new(card, buf, id, 1, 1, + &hdspm->midi[id].rmidi); + if (err < 0) + return err; - sprintf(hdspm->midi[id].rmidi->name, "HDSPM MIDI %d", id+1); - hdspm->midi[id].rmidi->private_data = &hdspm->midi[id]; + sprintf(hdspm->midi[id].rmidi->name, "%s MIDI %d", + card->id, id+1); + hdspm->midi[id].rmidi->private_data = &hdspm->midi[id]; + + snd_rawmidi_set_ops(hdspm->midi[id].rmidi, + SNDRV_RAWMIDI_STREAM_OUTPUT, + &snd_hdspm_midi_output); + snd_rawmidi_set_ops(hdspm->midi[id].rmidi, + SNDRV_RAWMIDI_STREAM_INPUT, + &snd_hdspm_midi_input); + + hdspm->midi[id].rmidi->info_flags |= + SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + } else { + /* TCO MTC, read only */ + sprintf(buf, "%s MTC %d", card->shortname, id+1); + err = snd_rawmidi_new(card, buf, id, 1, 1, + &hdspm->midi[id].rmidi); + if (err < 0) + return err; + + sprintf(hdspm->midi[id].rmidi->name, + "%s MTC %d", card->id, id+1); + hdspm->midi[id].rmidi->private_data = &hdspm->midi[id]; - snd_rawmidi_set_ops(hdspm->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, - &snd_hdspm_midi_output); - snd_rawmidi_set_ops(hdspm->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_INPUT, - &snd_hdspm_midi_input); + snd_rawmidi_set_ops(hdspm->midi[id].rmidi, + SNDRV_RAWMIDI_STREAM_INPUT, + &snd_hdspm_midi_input); - hdspm->midi[id].rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | - SNDRV_RAWMIDI_INFO_INPUT | - SNDRV_RAWMIDI_INFO_DUPLEX; + hdspm->midi[id].rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; + } return 0; } @@ -1312,12 +1727,15 @@ static int __devinit snd_hdspm_create_midi (struct snd_card *card, static void hdspm_midi_tasklet(unsigned long arg) { struct hdspm *hdspm = (struct hdspm *)arg; - - if (hdspm->midi[0].pending) - snd_hdspm_midi_input_read (&hdspm->midi[0]); - if (hdspm->midi[1].pending) - snd_hdspm_midi_input_read (&hdspm->midi[1]); -} + int i = 0; + + while (i < hdspm->midiPorts) { + if (hdspm->midi[i].pending) + snd_hdspm_midi_input_read(&hdspm->midi[i]); + + i++; + } +} /*----------------------------------------------------------------------------- @@ -1326,6 +1744,22 @@ static void hdspm_midi_tasklet(unsigned long arg) /* get the system sample rate which is set */ + +/** + * Calculate the real sample rate from the + * current DDS value. + **/ +static int hdspm_get_system_sample_rate(struct hdspm *hdspm) +{ + unsigned int period, rate; + + period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ); + rate = hdspm_calc_dds_value(hdspm, period); + + return rate; +} + + #define HDSPM_SYSTEM_SAMPLE_RATE(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -1340,112 +1774,251 @@ static int snd_hdspm_info_system_sample_rate(struct snd_kcontrol *kcontrol, { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; + uinfo->value.integer.min = 27000; + uinfo->value.integer.max = 207000; + uinfo->value.integer.step = 1; return 0; } + static int snd_hdspm_get_system_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value * ucontrol) { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - ucontrol->value.enumerated.item[0] = hdspm->system_sample_rate; + ucontrol->value.integer.value[0] = hdspm_get_system_sample_rate(hdspm); + return 0; +} + + +/** + * Returns the WordClock sample rate class for the given card. + **/ +static int hdspm_get_wc_sample_rate(struct hdspm *hdspm) +{ + int status; + + switch (hdspm->io_type) { + case RayDAT: + case AIO: + status = hdspm_read(hdspm, HDSPM_RD_STATUS_1); + return (status >> 16) & 0xF; + break; + default: + break; + } + + + return 0; +} + + +/** + * Returns the TCO sample rate class for the given card. + **/ +static int hdspm_get_tco_sample_rate(struct hdspm *hdspm) +{ + int status; + + if (hdspm->tco) { + switch (hdspm->io_type) { + case RayDAT: + case AIO: + status = hdspm_read(hdspm, HDSPM_RD_STATUS_1); + return (status >> 20) & 0xF; + break; + default: + break; + } + } + + return 0; +} + + +/** + * Returns the SYNC_IN sample rate class for the given card. + **/ +static int hdspm_get_sync_in_sample_rate(struct hdspm *hdspm) +{ + int status; + + if (hdspm->tco) { + switch (hdspm->io_type) { + case RayDAT: + case AIO: + status = hdspm_read(hdspm, HDSPM_RD_STATUS_2); + return (status >> 12) & 0xF; + break; + default: + break; + } + } + return 0; } + +/** + * Returns the sample rate class for input source for + * 'new style' cards like the AIO and RayDAT. + **/ +static int hdspm_get_s1_sample_rate(struct hdspm *hdspm, unsigned int idx) +{ + int status = hdspm_read(hdspm, HDSPM_RD_STATUS_2); + + return (status >> (idx*4)) & 0xF; +} + + + #define HDSPM_AUTOSYNC_SAMPLE_RATE(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = xindex, \ - .access = SNDRV_CTL_ELEM_ACCESS_READ, \ - .info = snd_hdspm_info_autosync_sample_rate, \ - .get = snd_hdspm_get_autosync_sample_rate \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .private_value = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ, \ + .info = snd_hdspm_info_autosync_sample_rate, \ + .get = snd_hdspm_get_autosync_sample_rate \ } + static int snd_hdspm_info_autosync_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "32000", "44100", "48000", - "64000", "88200", "96000", - "128000", "176400", "192000", - "None" - }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = 10; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); + texts_freq[uinfo->value.enumerated.item]); return 0; } + static int snd_hdspm_get_autosync_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value * ucontrol) { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - switch (hdspm_external_sample_rate(hdspm)) { - case 32000: - ucontrol->value.enumerated.item[0] = 0; - break; - case 44100: - ucontrol->value.enumerated.item[0] = 1; - break; - case 48000: - ucontrol->value.enumerated.item[0] = 2; - break; - case 64000: - ucontrol->value.enumerated.item[0] = 3; - break; - case 88200: - ucontrol->value.enumerated.item[0] = 4; - break; - case 96000: - ucontrol->value.enumerated.item[0] = 5; - break; - case 128000: - ucontrol->value.enumerated.item[0] = 6; - break; - case 176400: - ucontrol->value.enumerated.item[0] = 7; - break; - case 192000: - ucontrol->value.enumerated.item[0] = 8; - break; + switch (hdspm->io_type) { + case RayDAT: + switch (kcontrol->private_value) { + case 0: + ucontrol->value.enumerated.item[0] = + hdspm_get_wc_sample_rate(hdspm); + break; + case 7: + ucontrol->value.enumerated.item[0] = + hdspm_get_tco_sample_rate(hdspm); + break; + case 8: + ucontrol->value.enumerated.item[0] = + hdspm_get_sync_in_sample_rate(hdspm); + break; + default: + ucontrol->value.enumerated.item[0] = + hdspm_get_s1_sample_rate(hdspm, + kcontrol->private_value-1); + } + case AIO: + switch (kcontrol->private_value) { + case 0: /* WC */ + ucontrol->value.enumerated.item[0] = + hdspm_get_wc_sample_rate(hdspm); + break; + case 4: /* TCO */ + ucontrol->value.enumerated.item[0] = + hdspm_get_tco_sample_rate(hdspm); + break; + case 5: /* SYNC_IN */ + ucontrol->value.enumerated.item[0] = + hdspm_get_sync_in_sample_rate(hdspm); + break; + default: + ucontrol->value.enumerated.item[0] = + hdspm_get_s1_sample_rate(hdspm, + ucontrol->id.index-1); + } default: - ucontrol->value.enumerated.item[0] = 9; + break; } - return 0; -} -#define HDSPM_SYSTEM_CLOCK_MODE(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = xindex, \ - .access = SNDRV_CTL_ELEM_ACCESS_READ, \ - .info = snd_hdspm_info_system_clock_mode, \ - .get = snd_hdspm_get_system_clock_mode, \ + return 0; } +#define HDSPM_SYSTEM_CLOCK_MODE(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_system_clock_mode, \ + .get = snd_hdspm_get_system_clock_mode, \ + .put = snd_hdspm_put_system_clock_mode, \ +} + + +/** + * Returns the system clock mode for the given card. + * @returns 0 - master, 1 - slave + **/ +static int hdspm_system_clock_mode(struct hdspm *hdspm) +{ + switch (hdspm->io_type) { + case AIO: + case RayDAT: + if (hdspm->settings_register & HDSPM_c0Master) + return 0; + break; -static int hdspm_system_clock_mode(struct hdspm * hdspm) -{ - /* Always reflect the hardware info, rme is never wrong !!!! */ + default: + if (hdspm->control_register & HDSPM_ClockModeMaster) + return 0; + } - if (hdspm->control_register & HDSPM_ClockModeMaster) - return 0; return 1; } -static int snd_hdspm_info_system_clock_mode(struct snd_kcontrol *kcontrol, + +/** + * Sets the system clock mode. + * @param mode 0 - master, 1 - slave + **/ +static void hdspm_set_system_clock_mode(struct hdspm *hdspm, int mode) +{ + switch (hdspm->io_type) { + case AIO: + case RayDAT: + if (0 == mode) + hdspm->settings_register |= HDSPM_c0Master; + else + hdspm->settings_register &= ~HDSPM_c0Master; + + hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register); + break; + + default: + if (0 == mode) + hdspm->control_register |= HDSPM_ClockModeMaster; + else + hdspm->control_register &= ~HDSPM_ClockModeMaster; + + hdspm_write(hdspm, HDSPM_controlRegister, + hdspm->control_register); + } +} + + +static int snd_hdspm_info_system_clock_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "Master", "Slave" }; + static char *texts[] = { "Master", "AutoSync" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; @@ -1463,96 +2036,83 @@ static int snd_hdspm_get_system_clock_mode(struct snd_kcontrol *kcontrol, { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - ucontrol->value.enumerated.item[0] = - hdspm_system_clock_mode(hdspm); + ucontrol->value.enumerated.item[0] = hdspm_system_clock_mode(hdspm); return 0; } -#define HDSPM_CLOCK_SOURCE(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = xindex, \ - .info = snd_hdspm_info_clock_source, \ - .get = snd_hdspm_get_clock_source, \ - .put = snd_hdspm_put_clock_source \ +static int snd_hdspm_put_system_clock_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + int val; + + if (!snd_hdspm_use_is_exclusive(hdspm)) + return -EBUSY; + + val = ucontrol->value.enumerated.item[0]; + if (val < 0) + val = 0; + else if (val > 1) + val = 1; + + hdspm_set_system_clock_mode(hdspm, val); + + return 0; +} + + +#define HDSPM_INTERNAL_CLOCK(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .info = snd_hdspm_info_clock_source, \ + .get = snd_hdspm_get_clock_source, \ + .put = snd_hdspm_put_clock_source \ } + static int hdspm_clock_source(struct hdspm * hdspm) { - if (hdspm->control_register & HDSPM_ClockModeMaster) { - switch (hdspm->system_sample_rate) { - case 32000: - return 1; - case 44100: - return 2; - case 48000: - return 3; - case 64000: - return 4; - case 88200: - return 5; - case 96000: - return 6; - case 128000: - return 7; - case 176400: - return 8; - case 192000: - return 9; - default: - return 3; - } - } else { - return 0; + switch (hdspm->system_sample_rate) { + case 32000: return 0; + case 44100: return 1; + case 48000: return 2; + case 64000: return 3; + case 88200: return 4; + case 96000: return 5; + case 128000: return 6; + case 176400: return 7; + case 192000: return 8; } + + return -1; } static int hdspm_set_clock_source(struct hdspm * hdspm, int mode) { int rate; switch (mode) { - - case HDSPM_CLOCK_SOURCE_AUTOSYNC: - if (hdspm_external_sample_rate(hdspm) != 0) { - hdspm->control_register &= ~HDSPM_ClockModeMaster; - hdspm_write(hdspm, HDSPM_controlRegister, - hdspm->control_register); - return 0; - } - return -1; - case HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ: - rate = 32000; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ: - rate = 44100; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ: - rate = 48000; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ: - rate = 64000; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ: - rate = 88200; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ: - rate = 96000; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_128KHZ: - rate = 128000; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_176_4KHZ: - rate = 176400; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_192KHZ: - rate = 192000; - break; - + case 0: + rate = 32000; break; + case 1: + rate = 44100; break; + case 2: + rate = 48000; break; + case 3: + rate = 64000; break; + case 4: + rate = 88200; break; + case 5: + rate = 96000; break; + case 6: + rate = 128000; break; + case 7: + rate = 176400; break; + case 8: + rate = 192000; break; default: - rate = 44100; + rate = 48000; } - hdspm->control_register |= HDSPM_ClockModeMaster; - hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); hdspm_set_rate(hdspm, rate, 1); return 0; } @@ -1560,25 +2120,16 @@ static int hdspm_set_clock_source(struct hdspm * hdspm, int mode) static int snd_hdspm_info_clock_source(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "AutoSync", - "Internal 32.0 kHz", "Internal 44.1 kHz", - "Internal 48.0 kHz", - "Internal 64.0 kHz", "Internal 88.2 kHz", - "Internal 96.0 kHz", - "Internal 128.0 kHz", "Internal 176.4 kHz", - "Internal 192.0 kHz" - }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; - uinfo->value.enumerated.items = 10; + uinfo->value.enumerated.items = 9; if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); + texts_freq[uinfo->value.enumerated.item+1]); return 0; } @@ -1615,134 +2166,301 @@ static int snd_hdspm_put_clock_source(struct snd_kcontrol *kcontrol, return change; } -#define HDSPM_PREF_SYNC_REF(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = xindex, \ - .info = snd_hdspm_info_pref_sync_ref, \ - .get = snd_hdspm_get_pref_sync_ref, \ - .put = snd_hdspm_put_pref_sync_ref \ -} +#define HDSPM_PREF_SYNC_REF(xname, xindex) \ +{.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_pref_sync_ref, \ + .get = snd_hdspm_get_pref_sync_ref, \ + .put = snd_hdspm_put_pref_sync_ref \ +} + + +/** + * Returns the current preferred sync reference setting. + * The semantics of the return value are depending on the + * card, please see the comments for clarification. + **/ static int hdspm_pref_sync_ref(struct hdspm * hdspm) { - /* Notice that this looks at the requested sync source, - not the one actually in use. - */ - if (hdspm->is_aes32) { + switch (hdspm->io_type) { + case AES32: switch (hdspm->control_register & HDSPM_SyncRefMask) { - /* number gives AES index, except for 0 which - corresponds to WordClock */ - case 0: return 0; - case HDSPM_SyncRef0: return 1; - case HDSPM_SyncRef1: return 2; - case HDSPM_SyncRef1+HDSPM_SyncRef0: return 3; - case HDSPM_SyncRef2: return 4; - case HDSPM_SyncRef2+HDSPM_SyncRef0: return 5; - case HDSPM_SyncRef2+HDSPM_SyncRef1: return 6; - case HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0: return 7; - case HDSPM_SyncRef3: return 8; + case 0: return 0; /* WC */ + case HDSPM_SyncRef0: return 1; /* AES 1 */ + case HDSPM_SyncRef1: return 2; /* AES 2 */ + case HDSPM_SyncRef1+HDSPM_SyncRef0: return 3; /* AES 3 */ + case HDSPM_SyncRef2: return 4; /* AES 4 */ + case HDSPM_SyncRef2+HDSPM_SyncRef0: return 5; /* AES 5 */ + case HDSPM_SyncRef2+HDSPM_SyncRef1: return 6; /* AES 6 */ + case HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0: + return 7; /* AES 7 */ + case HDSPM_SyncRef3: return 8; /* AES 8 */ + case HDSPM_SyncRef3+HDSPM_SyncRef0: return 9; /* TCO */ } - } else { - switch (hdspm->control_register & HDSPM_SyncRefMask) { - case HDSPM_SyncRef_Word: - return HDSPM_SYNC_FROM_WORD; - case HDSPM_SyncRef_MADI: - return HDSPM_SYNC_FROM_MADI; + break; + + case MADI: + case MADIface: + if (hdspm->tco) { + switch (hdspm->control_register & HDSPM_SyncRefMask) { + case 0: return 0; /* WC */ + case HDSPM_SyncRef0: return 1; /* MADI */ + case HDSPM_SyncRef1: return 2; /* TCO */ + case HDSPM_SyncRef1+HDSPM_SyncRef0: + return 3; /* SYNC_IN */ + } + } else { + switch (hdspm->control_register & HDSPM_SyncRefMask) { + case 0: return 0; /* WC */ + case HDSPM_SyncRef0: return 1; /* MADI */ + case HDSPM_SyncRef1+HDSPM_SyncRef0: + return 2; /* SYNC_IN */ + } + } + break; + + case RayDAT: + if (hdspm->tco) { + switch ((hdspm->settings_register & + HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) { + case 0: return 0; /* WC */ + case 3: return 1; /* ADAT 1 */ + case 4: return 2; /* ADAT 2 */ + case 5: return 3; /* ADAT 3 */ + case 6: return 4; /* ADAT 4 */ + case 1: return 5; /* AES */ + case 2: return 6; /* SPDIF */ + case 9: return 7; /* TCO */ + case 10: return 8; /* SYNC_IN */ + } + } else { + switch ((hdspm->settings_register & + HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) { + case 0: return 0; /* WC */ + case 3: return 1; /* ADAT 1 */ + case 4: return 2; /* ADAT 2 */ + case 5: return 3; /* ADAT 3 */ + case 6: return 4; /* ADAT 4 */ + case 1: return 5; /* AES */ + case 2: return 6; /* SPDIF */ + case 10: return 7; /* SYNC_IN */ + } } + + break; + + case AIO: + if (hdspm->tco) { + switch ((hdspm->settings_register & + HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) { + case 0: return 0; /* WC */ + case 3: return 1; /* ADAT */ + case 1: return 2; /* AES */ + case 2: return 3; /* SPDIF */ + case 9: return 4; /* TCO */ + case 10: return 5; /* SYNC_IN */ + } + } else { + switch ((hdspm->settings_register & + HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) { + case 0: return 0; /* WC */ + case 3: return 1; /* ADAT */ + case 1: return 2; /* AES */ + case 2: return 3; /* SPDIF */ + case 10: return 4; /* SYNC_IN */ + } + } + + break; } - return HDSPM_SYNC_FROM_WORD; + return -1; } + +/** + * Set the preferred sync reference to . The semantics + * of are depending on the card type, see the comments + * for clarification. + **/ static int hdspm_set_pref_sync_ref(struct hdspm * hdspm, int pref) { - hdspm->control_register &= ~HDSPM_SyncRefMask; + int p = 0; - if (hdspm->is_aes32) { - switch (pref) { - case 0: - hdspm->control_register |= 0; - break; - case 1: - hdspm->control_register |= HDSPM_SyncRef0; - break; - case 2: - hdspm->control_register |= HDSPM_SyncRef1; - break; - case 3: - hdspm->control_register |= HDSPM_SyncRef1+HDSPM_SyncRef0; - break; - case 4: - hdspm->control_register |= HDSPM_SyncRef2; - break; - case 5: - hdspm->control_register |= HDSPM_SyncRef2+HDSPM_SyncRef0; - break; - case 6: - hdspm->control_register |= HDSPM_SyncRef2+HDSPM_SyncRef1; - break; - case 7: - hdspm->control_register |= - HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0; - break; - case 8: - hdspm->control_register |= HDSPM_SyncRef3; - break; - default: - return -1; - } - } else { + switch (hdspm->io_type) { + case AES32: + hdspm->control_register &= ~HDSPM_SyncRefMask; switch (pref) { - case HDSPM_SYNC_FROM_MADI: - hdspm->control_register |= HDSPM_SyncRef_MADI; + case 0: /* WC */ + break; + case 1: /* AES 1 */ + hdspm->control_register |= HDSPM_SyncRef0; + break; + case 2: /* AES 2 */ + hdspm->control_register |= HDSPM_SyncRef1; + break; + case 3: /* AES 3 */ + hdspm->control_register |= + HDSPM_SyncRef1+HDSPM_SyncRef0; + break; + case 4: /* AES 4 */ + hdspm->control_register |= HDSPM_SyncRef2; + break; + case 5: /* AES 5 */ + hdspm->control_register |= + HDSPM_SyncRef2+HDSPM_SyncRef0; + break; + case 6: /* AES 6 */ + hdspm->control_register |= + HDSPM_SyncRef2+HDSPM_SyncRef1; + break; + case 7: /* AES 7 */ + hdspm->control_register |= + HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0; break; - case HDSPM_SYNC_FROM_WORD: - hdspm->control_register |= HDSPM_SyncRef_Word; + case 8: /* AES 8 */ + hdspm->control_register |= HDSPM_SyncRef3; + break; + case 9: /* TCO */ + hdspm->control_register |= + HDSPM_SyncRef3+HDSPM_SyncRef0; break; default: return -1; } + + break; + + case MADI: + case MADIface: + hdspm->control_register &= ~HDSPM_SyncRefMask; + if (hdspm->tco) { + switch (pref) { + case 0: /* WC */ + break; + case 1: /* MADI */ + hdspm->control_register |= HDSPM_SyncRef0; + break; + case 2: /* TCO */ + hdspm->control_register |= HDSPM_SyncRef1; + break; + case 3: /* SYNC_IN */ + hdspm->control_register |= + HDSPM_SyncRef0+HDSPM_SyncRef1; + break; + default: + return -1; + } + } else { + switch (pref) { + case 0: /* WC */ + break; + case 1: /* MADI */ + hdspm->control_register |= HDSPM_SyncRef0; + break; + case 2: /* SYNC_IN */ + hdspm->control_register |= + HDSPM_SyncRef0+HDSPM_SyncRef1; + break; + default: + return -1; + } + } + + break; + + case RayDAT: + if (hdspm->tco) { + switch (pref) { + case 0: p = 0; break; /* WC */ + case 1: p = 3; break; /* ADAT 1 */ + case 2: p = 4; break; /* ADAT 2 */ + case 3: p = 5; break; /* ADAT 3 */ + case 4: p = 6; break; /* ADAT 4 */ + case 5: p = 1; break; /* AES */ + case 6: p = 2; break; /* SPDIF */ + case 7: p = 9; break; /* TCO */ + case 8: p = 10; break; /* SYNC_IN */ + default: return -1; + } + } else { + switch (pref) { + case 0: p = 0; break; /* WC */ + case 1: p = 3; break; /* ADAT 1 */ + case 2: p = 4; break; /* ADAT 2 */ + case 3: p = 5; break; /* ADAT 3 */ + case 4: p = 6; break; /* ADAT 4 */ + case 5: p = 1; break; /* AES */ + case 6: p = 2; break; /* SPDIF */ + case 7: p = 10; break; /* SYNC_IN */ + default: return -1; + } + } + break; + + case AIO: + if (hdspm->tco) { + switch (pref) { + case 0: p = 0; break; /* WC */ + case 1: p = 3; break; /* ADAT */ + case 2: p = 1; break; /* AES */ + case 3: p = 2; break; /* SPDIF */ + case 4: p = 9; break; /* TCO */ + case 5: p = 10; break; /* SYNC_IN */ + default: return -1; + } + } else { + switch (pref) { + case 0: p = 0; break; /* WC */ + case 1: p = 3; break; /* ADAT */ + case 2: p = 1; break; /* AES */ + case 3: p = 2; break; /* SPDIF */ + case 4: p = 10; break; /* SYNC_IN */ + default: return -1; + } + } + break; } - hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); + + switch (hdspm->io_type) { + case RayDAT: + case AIO: + hdspm->settings_register &= ~HDSPM_c0_SyncRefMask; + hdspm->settings_register |= HDSPM_c0_SyncRef0 * p; + hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register); + break; + + case MADI: + case MADIface: + case AES32: + hdspm_write(hdspm, HDSPM_controlRegister, + hdspm->control_register); + } + return 0; } + static int snd_hdspm_info_pref_sync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - if (hdspm->is_aes32) { - static char *texts[] = { "Word", "AES1", "AES2", "AES3", - "AES4", "AES5", "AES6", "AES7", "AES8" }; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - - uinfo->value.enumerated.items = 9; - - if (uinfo->value.enumerated.item >= - uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - } else { - static char *texts[] = { "Word", "MADI" }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = hdspm->texts_autosync_items; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; - uinfo->value.enumerated.items = 2; + strcpy(uinfo->value.enumerated.name, + hdspm->texts_autosync[uinfo->value.enumerated.item]); - if (uinfo->value.enumerated.item >= - uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - } return 0; } @@ -1750,32 +2468,41 @@ static int snd_hdspm_get_pref_sync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + int psf = hdspm_pref_sync_ref(hdspm); - ucontrol->value.enumerated.item[0] = hdspm_pref_sync_ref(hdspm); - return 0; + if (psf >= 0) { + ucontrol->value.enumerated.item[0] = psf; + return 0; + } + + return -1; } static int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - int change, max; - unsigned int val; - - max = hdspm->is_aes32 ? 9 : 2; + int val, change = 0; if (!snd_hdspm_use_is_exclusive(hdspm)) return -EBUSY; - val = ucontrol->value.enumerated.item[0] % max; + val = ucontrol->value.enumerated.item[0]; + + if (val < 0) + val = 0; + else if (val >= hdspm->texts_autosync_items) + val = hdspm->texts_autosync_items-1; spin_lock_irq(&hdspm->lock); - change = (int) val != hdspm_pref_sync_ref(hdspm); - hdspm_set_pref_sync_ref(hdspm, val); + if (val != hdspm_pref_sync_ref(hdspm)) + change = (0 == hdspm_set_pref_sync_ref(hdspm, val)) ? 1 : 0; + spin_unlock_irq(&hdspm->lock); return change; } + #define HDSPM_AUTOSYNC_REF(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -1785,18 +2512,18 @@ static int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol, .get = snd_hdspm_get_autosync_ref, \ } -static int hdspm_autosync_ref(struct hdspm * hdspm) +static int hdspm_autosync_ref(struct hdspm *hdspm) { - if (hdspm->is_aes32) { + if (AES32 == hdspm->io_type) { unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister); - unsigned int syncref = (status >> HDSPM_AES32_syncref_bit) & - 0xF; + unsigned int syncref = + (status >> HDSPM_AES32_syncref_bit) & 0xF; if (syncref == 0) return HDSPM_AES32_AUTOSYNC_FROM_WORD; if (syncref <= 8) return syncref; return HDSPM_AES32_AUTOSYNC_FROM_NONE; - } else { + } else if (MADI == hdspm->io_type) { /* This looks at the autosync selected sync reference */ unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); @@ -1805,22 +2532,27 @@ static int hdspm_autosync_ref(struct hdspm * hdspm) return HDSPM_AUTOSYNC_FROM_WORD; case HDSPM_SelSyncRef_MADI: return HDSPM_AUTOSYNC_FROM_MADI; + case HDSPM_SelSyncRef_TCO: + return HDSPM_AUTOSYNC_FROM_TCO; + case HDSPM_SelSyncRef_SyncIn: + return HDSPM_AUTOSYNC_FROM_SYNC_IN; case HDSPM_SelSyncRef_NVALID: return HDSPM_AUTOSYNC_FROM_NONE; default: return 0; } - return 0; } + return 0; } + static int snd_hdspm_info_autosync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - if (hdspm->is_aes32) { + if (AES32 == hdspm->io_type) { static char *texts[] = { "WordClock", "AES1", "AES2", "AES3", "AES4", "AES5", "AES6", "AES7", "AES8", "None"}; @@ -1833,14 +2565,15 @@ static int snd_hdspm_info_autosync_ref(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.items - 1; strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - } else { - static char *texts[] = { "WordClock", "MADI", "None" }; + } else if (MADI == hdspm->io_type) { + static char *texts[] = {"Word Clock", "MADI", "TCO", + "Sync In", "None" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; - uinfo->value.enumerated.items = 3; + uinfo->value.enumerated.items = 5; if (uinfo->value.enumerated.item >= - uinfo->value.enumerated.items) + uinfo->value.enumerated.items) uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; strcpy(uinfo->value.enumerated.name, @@ -1858,6 +2591,7 @@ static int snd_hdspm_get_autosync_ref(struct snd_kcontrol *kcontrol, return 0; } + #define HDSPM_LINE_OUT(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -1914,6 +2648,7 @@ static int snd_hdspm_put_line_out(struct snd_kcontrol *kcontrol, return change; } + #define HDSPM_TX_64(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -1969,6 +2704,7 @@ static int snd_hdspm_put_tx_64(struct snd_kcontrol *kcontrol, return change; } + #define HDSPM_C_TMS(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2024,6 +2760,7 @@ static int snd_hdspm_put_c_tms(struct snd_kcontrol *kcontrol, return change; } + #define HDSPM_SAFE_MODE(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2079,6 +2816,7 @@ static int snd_hdspm_put_safe_mode(struct snd_kcontrol *kcontrol, return change; } + #define HDSPM_EMPHASIS(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2134,6 +2872,7 @@ static int snd_hdspm_put_emphasis(struct snd_kcontrol *kcontrol, return change; } + #define HDSPM_DOLBY(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2189,6 +2928,7 @@ static int snd_hdspm_put_dolby(struct snd_kcontrol *kcontrol, return change; } + #define HDSPM_PROFESSIONAL(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2315,6 +3055,7 @@ static int snd_hdspm_put_input_select(struct snd_kcontrol *kcontrol, return change; } + #define HDSPM_DS_WIRE(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2386,6 +3127,7 @@ static int snd_hdspm_put_ds_wire(struct snd_kcontrol *kcontrol, return change; } + #define HDSPM_QS_WIRE(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2472,15 +3214,6 @@ static int snd_hdspm_put_qs_wire(struct snd_kcontrol *kcontrol, return change; } -/* Simple Mixer - deprecated since to much faders ??? - MIXER interface says output (source, destination, value) - where source > MAX_channels are playback channels - on MADICARD - - playback mixer matrix: [channelout+64] [output] [value] - - input(thru) mixer matrix: [channelin] [output] [value] - (better do 2 kontrols for separation ?) -*/ #define HDSPM_MIXER(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ @@ -2586,7 +3319,7 @@ static int snd_hdspm_put_mixer(struct snd_kcontrol *kcontrol, /* The simple mixer control(s) provide gain control for the basic 1:1 mappings of playback streams to output - streams. + streams. */ #define HDSPM_PLAYBACK_MIXER \ @@ -2604,7 +3337,7 @@ static int snd_hdspm_info_playback_mixer(struct snd_kcontrol *kcontrol, uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; - uinfo->value.integer.max = 65536; + uinfo->value.integer.max = 64; uinfo->value.integer.step = 1; return 0; } @@ -2614,28 +3347,17 @@ static int snd_hdspm_get_playback_mixer(struct snd_kcontrol *kcontrol, { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); int channel; - int mapped_channel; channel = ucontrol->id.index - 1; if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS)) return -EINVAL; - mapped_channel = hdspm->channel_map[channel]; - if (mapped_channel < 0) - return -EINVAL; - spin_lock_irq(&hdspm->lock); ucontrol->value.integer.value[0] = - hdspm_read_pb_gain(hdspm, mapped_channel, mapped_channel); + (hdspm_read_pb_gain(hdspm, channel, channel)*64)/UNITY_GAIN; spin_unlock_irq(&hdspm->lock); - /* - snd_printdd("get pb mixer index %d, channel %d, mapped_channel %d, " - "value %d\n", - ucontrol->id.index, channel, mapped_channel, - ucontrol->value.integer.value[0]); - */ return 0; } @@ -2645,7 +3367,6 @@ static int snd_hdspm_put_playback_mixer(struct snd_kcontrol *kcontrol, struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); int change; int channel; - int mapped_channel; int gain; if (!snd_hdspm_use_is_exclusive(hdspm)) @@ -2656,59 +3377,60 @@ static int snd_hdspm_put_playback_mixer(struct snd_kcontrol *kcontrol, if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS)) return -EINVAL; - mapped_channel = hdspm->channel_map[channel]; - if (mapped_channel < 0) - return -EINVAL; - - gain = ucontrol->value.integer.value[0]; + gain = ucontrol->value.integer.value[0]*UNITY_GAIN/64; spin_lock_irq(&hdspm->lock); change = - gain != hdspm_read_pb_gain(hdspm, mapped_channel, - mapped_channel); + gain != hdspm_read_pb_gain(hdspm, channel, + channel); if (change) - hdspm_write_pb_gain(hdspm, mapped_channel, mapped_channel, + hdspm_write_pb_gain(hdspm, channel, channel, gain); spin_unlock_irq(&hdspm->lock); return change; } -#define HDSPM_WC_SYNC_CHECK(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = xindex, \ - .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ - .info = snd_hdspm_info_sync_check, \ - .get = snd_hdspm_get_wc_sync_check \ +#define HDSPM_SYNC_CHECK(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .private_value = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_sync_check, \ + .get = snd_hdspm_get_sync_check \ } + static int snd_hdspm_info_sync_check(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "No Lock", "Lock", "Sync" }; + static char *texts[] = { "No Lock", "Lock", "Sync", "N/A" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; - uinfo->value.enumerated.items = 3; + uinfo->value.enumerated.items = 4; if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; + uinfo->value.enumerated.items - 1; strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); + texts[uinfo->value.enumerated.item]); return 0; } -static int hdspm_wc_sync_check(struct hdspm * hdspm) +static int hdspm_wc_sync_check(struct hdspm *hdspm) { - if (hdspm->is_aes32) { - int status = hdspm_read(hdspm, HDSPM_statusRegister); - if (status & HDSPM_AES32_wcLock) { - /* I don't know how to differenciate sync from lock. - Doing as if sync for now */ + int status, status2; + + switch (hdspm->io_type) { + case AES32: + status = hdspm_read(hdspm, HDSPM_statusRegister); + if (status & HDSPM_wcSync) return 2; - } + else if (status & HDSPM_wcLock) + return 1; return 0; - } else { - int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + break; + + case MADI: + status2 = hdspm_read(hdspm, HDSPM_statusRegister2); if (status2 & HDSPM_wcLock) { if (status2 & HDSPM_wcSync) return 2; @@ -2716,29 +3438,30 @@ static int hdspm_wc_sync_check(struct hdspm * hdspm) return 1; } return 0; - } -} + break; -static int snd_hdspm_get_wc_sync_check(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + case RayDAT: + case AIO: + status = hdspm_read(hdspm, HDSPM_statusRegister); - ucontrol->value.enumerated.item[0] = hdspm_wc_sync_check(hdspm); - return 0; -} + if (status & 0x2000000) + return 2; + else if (status & 0x1000000) + return 1; + return 0; + break; -#define HDSPM_MADI_SYNC_CHECK(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = xindex, \ - .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ - .info = snd_hdspm_info_sync_check, \ - .get = snd_hdspm_get_madisync_sync_check \ + case MADIface: + break; + } + + + return 3; } -static int hdspm_madisync_sync_check(struct hdspm * hdspm) + +static int hdspm_madi_sync_check(struct hdspm *hdspm) { int status = hdspm_read(hdspm, HDSPM_statusRegister); if (status & HDSPM_madiLock) { @@ -2750,89 +3473,727 @@ static int hdspm_madisync_sync_check(struct hdspm * hdspm) return 0; } -static int snd_hdspm_get_madisync_sync_check(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value * - ucontrol) -{ - struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - ucontrol->value.enumerated.item[0] = - hdspm_madisync_sync_check(hdspm); - return 0; -} +static int hdspm_s1_sync_check(struct hdspm *hdspm, int idx) +{ + int status, lock, sync; + status = hdspm_read(hdspm, HDSPM_RD_STATUS_1); -#define HDSPM_AES_SYNC_CHECK(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = xindex, \ - .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ - .info = snd_hdspm_info_sync_check, \ - .get = snd_hdspm_get_aes_sync_check \ -} + lock = (status & (0x1<> idx)) { - /* I don't know how to differenciate sync from lock. - Doing as if sync for now */ + if (lock && sync) return 2; - } + else if (lock) + return 1; return 0; } -static int snd_hdspm_get_aes_sync_check(struct snd_kcontrol *kcontrol, + +static int hdspm_sync_in_sync_check(struct hdspm *hdspm) +{ + int status, lock = 0, sync = 0; + + switch (hdspm->io_type) { + case RayDAT: + case AIO: + status = hdspm_read(hdspm, HDSPM_RD_STATUS_3); + lock = (status & 0x400) ? 1 : 0; + sync = (status & 0x800) ? 1 : 0; + break; + + case MADI: + case AES32: + status = hdspm_read(hdspm, HDSPM_statusRegister2); + lock = (status & 0x400000) ? 1 : 0; + sync = (status & 0x800000) ? 1 : 0; + break; + + case MADIface: + break; + } + + if (lock && sync) + return 2; + else if (lock) + return 1; + + return 0; +} + +static int hdspm_aes_sync_check(struct hdspm *hdspm, int idx) +{ + int status2, lock, sync; + status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + + lock = (status2 & (0x0080 >> idx)) ? 1 : 0; + sync = (status2 & (0x8000 >> idx)) ? 1 : 0; + + if (sync) + return 2; + else if (lock) + return 1; + return 0; +} + + +static int hdspm_tco_sync_check(struct hdspm *hdspm) +{ + int status; + + if (hdspm->tco) { + switch (hdspm->io_type) { + case MADI: + case AES32: + status = hdspm_read(hdspm, HDSPM_statusRegister); + if (status & HDSPM_tcoLock) { + if (status & HDSPM_tcoSync) + return 2; + else + return 1; + } + return 0; + + break; + + case RayDAT: + case AIO: + status = hdspm_read(hdspm, HDSPM_RD_STATUS_1); + + if (status & 0x8000000) + return 2; /* Sync */ + if (status & 0x4000000) + return 1; /* Lock */ + return 0; /* No signal */ + break; + + default: + break; + } + } + + return 3; /* N/A */ +} + + +static int snd_hdspm_get_sync_check(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + int val = -1; + + switch (hdspm->io_type) { + case RayDAT: + switch (kcontrol->private_value) { + case 0: /* WC */ + val = hdspm_wc_sync_check(hdspm); break; + case 7: /* TCO */ + val = hdspm_tco_sync_check(hdspm); break; + case 8: /* SYNC IN */ + val = hdspm_sync_in_sync_check(hdspm); break; + default: + val = hdspm_s1_sync_check(hdspm, ucontrol->id.index-1); + } + + case AIO: + switch (kcontrol->private_value) { + case 0: /* WC */ + val = hdspm_wc_sync_check(hdspm); break; + case 4: /* TCO */ + val = hdspm_tco_sync_check(hdspm); break; + case 5: /* SYNC IN */ + val = hdspm_sync_in_sync_check(hdspm); break; + default: + val = hdspm_s1_sync_check(hdspm, ucontrol->id.index-1); + } + + case MADI: + switch (kcontrol->private_value) { + case 0: /* WC */ + val = hdspm_wc_sync_check(hdspm); break; + case 1: /* MADI */ + val = hdspm_madi_sync_check(hdspm); break; + case 2: /* TCO */ + val = hdspm_tco_sync_check(hdspm); break; + case 3: /* SYNC_IN */ + val = hdspm_sync_in_sync_check(hdspm); break; + } + + case MADIface: + val = hdspm_madi_sync_check(hdspm); /* MADI */ + break; + + case AES32: + switch (kcontrol->private_value) { + case 0: /* WC */ + val = hdspm_wc_sync_check(hdspm); break; + case 9: /* TCO */ + val = hdspm_tco_sync_check(hdspm); break; + case 10 /* SYNC IN */: + val = hdspm_sync_in_sync_check(hdspm); break; + default: + val = hdspm_aes_sync_check(hdspm, + ucontrol->id.index-1); + } + + } + + if (-1 == val) + val = 3; + + ucontrol->value.enumerated.item[0] = val; + return 0; +} + + + +/** + * TCO controls + **/ +static void hdspm_tco_write(struct hdspm *hdspm) +{ + unsigned int tc[4] = { 0, 0, 0, 0}; + + switch (hdspm->tco->input) { + case 0: + tc[2] |= HDSPM_TCO2_set_input_MSB; + break; + case 1: + tc[2] |= HDSPM_TCO2_set_input_LSB; + break; + default: + break; + } + + switch (hdspm->tco->framerate) { + case 1: + tc[1] |= HDSPM_TCO1_LTC_Format_LSB; + break; + case 2: + tc[1] |= HDSPM_TCO1_LTC_Format_MSB; + break; + case 3: + tc[1] |= HDSPM_TCO1_LTC_Format_MSB + + HDSPM_TCO1_set_drop_frame_flag; + break; + case 4: + tc[1] |= HDSPM_TCO1_LTC_Format_LSB + + HDSPM_TCO1_LTC_Format_MSB; + break; + case 5: + tc[1] |= HDSPM_TCO1_LTC_Format_LSB + + HDSPM_TCO1_LTC_Format_MSB + + HDSPM_TCO1_set_drop_frame_flag; + break; + default: + break; + } + + switch (hdspm->tco->wordclock) { + case 1: + tc[2] |= HDSPM_TCO2_WCK_IO_ratio_LSB; + break; + case 2: + tc[2] |= HDSPM_TCO2_WCK_IO_ratio_MSB; + break; + default: + break; + } + + switch (hdspm->tco->samplerate) { + case 1: + tc[2] |= HDSPM_TCO2_set_freq; + break; + case 2: + tc[2] |= HDSPM_TCO2_set_freq_from_app; + break; + default: + break; + } + + switch (hdspm->tco->pull) { + case 1: + tc[2] |= HDSPM_TCO2_set_pull_up; + break; + case 2: + tc[2] |= HDSPM_TCO2_set_pull_down; + break; + case 3: + tc[2] |= HDSPM_TCO2_set_pull_up + HDSPM_TCO2_set_01_4; + break; + case 4: + tc[2] |= HDSPM_TCO2_set_pull_down + HDSPM_TCO2_set_01_4; + break; + default: + break; + } + + if (1 == hdspm->tco->term) { + tc[2] |= HDSPM_TCO2_set_term_75R; + } + + hdspm_write(hdspm, HDSPM_WR_TCO, tc[0]); + hdspm_write(hdspm, HDSPM_WR_TCO+4, tc[1]); + hdspm_write(hdspm, HDSPM_WR_TCO+8, tc[2]); + hdspm_write(hdspm, HDSPM_WR_TCO+12, tc[3]); +} + + +#define HDSPM_TCO_SAMPLE_RATE(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_tco_sample_rate, \ + .get = snd_hdspm_get_tco_sample_rate, \ + .put = snd_hdspm_put_tco_sample_rate \ +} + +static int snd_hdspm_info_tco_sample_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[] = { "44.1 kHz", "48 kHz" }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int snd_hdspm_get_tco_sample_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = hdspm->tco->samplerate; + + return 0; +} + +static int snd_hdspm_put_tco_sample_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + if (hdspm->tco->samplerate != ucontrol->value.enumerated.item[0]) { + hdspm->tco->samplerate = ucontrol->value.enumerated.item[0]; + + hdspm_tco_write(hdspm); + + return 1; + } + + return 0; +} + + +#define HDSPM_TCO_PULL(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_tco_pull, \ + .get = snd_hdspm_get_tco_pull, \ + .put = snd_hdspm_put_tco_pull \ +} + +static int snd_hdspm_info_tco_pull(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[] = { "0", "+ 0.1 %", "- 0.1 %", "+ 4 %", "- 4 %" }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 5; + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int snd_hdspm_get_tco_pull(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = hdspm->tco->pull; + + return 0; +} + +static int snd_hdspm_put_tco_pull(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + if (hdspm->tco->pull != ucontrol->value.enumerated.item[0]) { + hdspm->tco->pull = ucontrol->value.enumerated.item[0]; + + hdspm_tco_write(hdspm); + + return 1; + } + + return 0; +} + +#define HDSPM_TCO_WCK_CONVERSION(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_tco_wck_conversion, \ + .get = snd_hdspm_get_tco_wck_conversion, \ + .put = snd_hdspm_put_tco_wck_conversion \ +} + +static int snd_hdspm_info_tco_wck_conversion(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[] = { "1:1", "44.1 -> 48", "48 -> 44.1" }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int snd_hdspm_get_tco_wck_conversion(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = hdspm->tco->wordclock; + + return 0; +} + +static int snd_hdspm_put_tco_wck_conversion(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + if (hdspm->tco->wordclock != ucontrol->value.enumerated.item[0]) { + hdspm->tco->wordclock = ucontrol->value.enumerated.item[0]; + + hdspm_tco_write(hdspm); + + return 1; + } + + return 0; +} + + +#define HDSPM_TCO_FRAME_RATE(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_tco_frame_rate, \ + .get = snd_hdspm_get_tco_frame_rate, \ + .put = snd_hdspm_put_tco_frame_rate \ +} + +static int snd_hdspm_info_tco_frame_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[] = { "24 fps", "25 fps", "29.97fps", + "29.97 dfps", "30 fps", "30 dfps" }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 6; + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int snd_hdspm_get_tco_frame_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - int offset; struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - offset = ucontrol->id.index - 1; - if (offset < 0 || offset >= 8) - return -EINVAL; + ucontrol->value.enumerated.item[0] = hdspm->tco->framerate; - ucontrol->value.enumerated.item[0] = - hdspm_aes_sync_check(hdspm, offset); return 0; } +static int snd_hdspm_put_tco_frame_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); -static struct snd_kcontrol_new snd_hdspm_controls_madi[] = { + if (hdspm->tco->framerate != ucontrol->value.enumerated.item[0]) { + hdspm->tco->framerate = ucontrol->value.enumerated.item[0]; - HDSPM_MIXER("Mixer", 0), -/* 'Sample Clock Source' complies with the alsa control naming scheme */ - HDSPM_CLOCK_SOURCE("Sample Clock Source", 0), + hdspm_tco_write(hdspm); + + return 1; + } + + return 0; +} + +#define HDSPM_TCO_SYNC_SOURCE(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_tco_sync_source, \ + .get = snd_hdspm_get_tco_sync_source, \ + .put = snd_hdspm_put_tco_sync_source \ +} + +static int snd_hdspm_info_tco_sync_source(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[] = { "LTC", "Video", "WCK" }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int snd_hdspm_get_tco_sync_source(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = hdspm->tco->input; + + return 0; +} + +static int snd_hdspm_put_tco_sync_source(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + if (hdspm->tco->input != ucontrol->value.enumerated.item[0]) { + hdspm->tco->input = ucontrol->value.enumerated.item[0]; + + hdspm_tco_write(hdspm); + + return 1; + } + + return 0; +} + + +#define HDSPM_TCO_WORD_TERM(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_tco_word_term, \ + .get = snd_hdspm_get_tco_word_term, \ + .put = snd_hdspm_put_tco_word_term \ +} + +static int snd_hdspm_info_tco_word_term(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + + return 0; +} + + +static int snd_hdspm_get_tco_word_term(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = hdspm->tco->term; + + return 0; +} + + +static int snd_hdspm_put_tco_word_term(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + if (hdspm->tco->term != ucontrol->value.enumerated.item[0]) { + hdspm->tco->term = ucontrol->value.enumerated.item[0]; + + hdspm_tco_write(hdspm); + + return 1; + } + + return 0; +} + + + + +static struct snd_kcontrol_new snd_hdspm_controls_madi[] = { + HDSPM_MIXER("Mixer", 0), + HDSPM_INTERNAL_CLOCK("Internal Clock", 0), HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0), HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0), HDSPM_AUTOSYNC_REF("AutoSync Reference", 0), HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0), -/* 'External Rate' complies with the alsa control naming scheme */ - HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0), - HDSPM_WC_SYNC_CHECK("Word Clock Lock Status", 0), - HDSPM_MADI_SYNC_CHECK("MADI Sync Lock Status", 0), + HDSPM_SYNC_CHECK("WC SyncCheck", 0), + HDSPM_SYNC_CHECK("MADI SyncCheck", 1), + HDSPM_SYNC_CHECK("TCO SyncCHeck", 2), + HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 3), HDSPM_LINE_OUT("Line Out", 0), HDSPM_TX_64("TX 64 channels mode", 0), HDSPM_C_TMS("Clear Track Marker", 0), HDSPM_SAFE_MODE("Safe Mode", 0), + HDSPM_INPUT_SELECT("Input Select", 0) +}; + + +static struct snd_kcontrol_new snd_hdspm_controls_madiface[] = { + HDSPM_MIXER("Mixer", 0), + HDSPM_INTERNAL_CLOCK("Internal Clock", 0), + HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0), + HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0), + HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0), + HDSPM_SYNC_CHECK("MADI SyncCheck", 0), + HDSPM_TX_64("TX 64 channels mode", 0), + HDSPM_C_TMS("Clear Track Marker", 0), + HDSPM_SAFE_MODE("Safe Mode", 0), HDSPM_INPUT_SELECT("Input Select", 0), }; -static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = { +static struct snd_kcontrol_new snd_hdspm_controls_aio[] = { + HDSPM_MIXER("Mixer", 0), + HDSPM_INTERNAL_CLOCK("Internal Clock", 0), + HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0), + HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0), + HDSPM_AUTOSYNC_REF("AutoSync Reference", 0), + HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0), + HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0), + HDSPM_SYNC_CHECK("WC SyncCheck", 0), + HDSPM_SYNC_CHECK("AES SyncCheck", 1), + HDSPM_SYNC_CHECK("SPDIF SyncCheck", 2), + HDSPM_SYNC_CHECK("ADAT SyncCheck", 3), + HDSPM_SYNC_CHECK("TCO SyncCheck", 4), + HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 5), + HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES Frequency", 1), + HDSPM_AUTOSYNC_SAMPLE_RATE("SPDIF Frequency", 2), + HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT Frequency", 3), + HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 4), + HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 5) + + /* + HDSPM_INPUT_SELECT("Input Select", 0), + HDSPM_SPDIF_OPTICAL("SPDIF Out Optical", 0), + HDSPM_PROFESSIONAL("SPDIF Out Professional", 0); + HDSPM_SPDIF_IN("SPDIF In", 0); + HDSPM_BREAKOUT_CABLE("Breakout Cable", 0); + HDSPM_INPUT_LEVEL("Input Level", 0); + HDSPM_OUTPUT_LEVEL("Output Level", 0); + HDSPM_PHONES("Phones", 0); + */ +}; +static struct snd_kcontrol_new snd_hdspm_controls_raydat[] = { + HDSPM_MIXER("Mixer", 0), + HDSPM_INTERNAL_CLOCK("Internal Clock", 0), + HDSPM_SYSTEM_CLOCK_MODE("Clock Mode", 0), + HDSPM_PREF_SYNC_REF("Pref Sync Ref", 0), + HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0), + HDSPM_SYNC_CHECK("WC SyncCheck", 0), + HDSPM_SYNC_CHECK("AES SyncCheck", 1), + HDSPM_SYNC_CHECK("SPDIF SyncCheck", 2), + HDSPM_SYNC_CHECK("ADAT1 SyncCheck", 3), + HDSPM_SYNC_CHECK("ADAT2 SyncCheck", 4), + HDSPM_SYNC_CHECK("ADAT3 SyncCheck", 5), + HDSPM_SYNC_CHECK("ADAT4 SyncCheck", 6), + HDSPM_SYNC_CHECK("TCO SyncCheck", 7), + HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 8), + HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES Frequency", 1), + HDSPM_AUTOSYNC_SAMPLE_RATE("SPDIF Frequency", 2), + HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT1 Frequency", 3), + HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT2 Frequency", 4), + HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT3 Frequency", 5), + HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT4 Frequency", 6), + HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 7), + HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 8) +}; + +static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = { HDSPM_MIXER("Mixer", 0), -/* 'Sample Clock Source' complies with the alsa control naming scheme */ - HDSPM_CLOCK_SOURCE("Sample Clock Source", 0), - + HDSPM_INTERNAL_CLOCK("Internal Clock", 0), HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0), HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0), HDSPM_AUTOSYNC_REF("AutoSync Reference", 0), HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0), -/* 'External Rate' complies with the alsa control naming scheme */ HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0), - HDSPM_WC_SYNC_CHECK("Word Clock Lock Status", 0), -/* HDSPM_AES_SYNC_CHECK("AES Lock Status", 0),*/ /* created in snd_hdspm_create_controls() */ + HDSPM_SYNC_CHECK("WC Sync Check", 0), + HDSPM_SYNC_CHECK("AES1 Sync Check", 1), + HDSPM_SYNC_CHECK("AES2 Sync Check", 2), + HDSPM_SYNC_CHECK("AES3 Sync Check", 3), + HDSPM_SYNC_CHECK("AES4 Sync Check", 4), + HDSPM_SYNC_CHECK("AES5 Sync Check", 5), + HDSPM_SYNC_CHECK("AES6 Sync Check", 6), + HDSPM_SYNC_CHECK("AES7 Sync Check", 7), + HDSPM_SYNC_CHECK("AES8 Sync Check", 8), + HDSPM_SYNC_CHECK("TCO Sync Check", 9), + HDSPM_SYNC_CHECK("SYNC IN Sync Check", 10), + HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES1 Frequency", 1), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES2 Frequency", 2), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES3 Frequency", 3), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES4 Frequency", 4), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES5 Frequency", 5), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES6 Frequency", 6), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES7 Frequency", 7), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES8 Frequency", 8), + HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 9), + HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 10), HDSPM_LINE_OUT("Line Out", 0), HDSPM_EMPHASIS("Emphasis", 0), HDSPM_DOLBY("Non Audio", 0), @@ -2842,6 +4203,19 @@ static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = { HDSPM_QS_WIRE("Quad Speed Wire Mode", 0), }; + + +/* Control elements for the optional TCO module */ +static struct snd_kcontrol_new snd_hdspm_controls_tco[] = { + HDSPM_TCO_SAMPLE_RATE("TCO Sample Rate", 0), + HDSPM_TCO_PULL("TCO Pull", 0), + HDSPM_TCO_WCK_CONVERSION("TCO WCK Conversion", 0), + HDSPM_TCO_FRAME_RATE("TCO Frame Rate", 0), + HDSPM_TCO_SYNC_SOURCE("TCO Sync Source", 0), + HDSPM_TCO_WORD_TERM("TCO Word Term", 0) +}; + + static struct snd_kcontrol_new snd_hdspm_playback_mixer = HDSPM_PLAYBACK_MIXER; @@ -2849,78 +4223,76 @@ static int hdspm_update_simple_mixer_controls(struct hdspm * hdspm) { int i; - for (i = hdspm->ds_channels; i < hdspm->ss_channels; ++i) { + for (i = hdspm->ds_out_channels; i < hdspm->ss_out_channels; ++i) { if (hdspm->system_sample_rate > 48000) { hdspm->playback_mixer_ctls[i]->vd[0].access = - SNDRV_CTL_ELEM_ACCESS_INACTIVE | - SNDRV_CTL_ELEM_ACCESS_READ | - SNDRV_CTL_ELEM_ACCESS_VOLATILE; + SNDRV_CTL_ELEM_ACCESS_INACTIVE | + SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE; } else { hdspm->playback_mixer_ctls[i]->vd[0].access = - SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_VOLATILE; + SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_VOLATILE; } snd_ctl_notify(hdspm->card, SNDRV_CTL_EVENT_MASK_VALUE | - SNDRV_CTL_EVENT_MASK_INFO, - &hdspm->playback_mixer_ctls[i]->id); + SNDRV_CTL_EVENT_MASK_INFO, + &hdspm->playback_mixer_ctls[i]->id); } return 0; } -static int snd_hdspm_create_controls(struct snd_card *card, struct hdspm * hdspm) +static int snd_hdspm_create_controls(struct snd_card *card, + struct hdspm *hdspm) { unsigned int idx, limit; int err; struct snd_kcontrol *kctl; + struct snd_kcontrol_new *list = NULL; - /* add control list first */ - if (hdspm->is_aes32) { - struct snd_kcontrol_new aes_sync_ctl = - HDSPM_AES_SYNC_CHECK("AES Lock Status", 0); + switch (hdspm->io_type) { + case MADI: + list = snd_hdspm_controls_madi; + limit = ARRAY_SIZE(snd_hdspm_controls_madi); + break; + case MADIface: + list = snd_hdspm_controls_madiface; + limit = ARRAY_SIZE(snd_hdspm_controls_madiface); + break; + case AIO: + list = snd_hdspm_controls_aio; + limit = ARRAY_SIZE(snd_hdspm_controls_aio); + break; + case RayDAT: + list = snd_hdspm_controls_raydat; + limit = ARRAY_SIZE(snd_hdspm_controls_raydat); + break; + case AES32: + list = snd_hdspm_controls_aes32; + limit = ARRAY_SIZE(snd_hdspm_controls_aes32); + break; + } - for (idx = 0; idx < ARRAY_SIZE(snd_hdspm_controls_aes32); - idx++) { - err = snd_ctl_add(card, - snd_ctl_new1(&snd_hdspm_controls_aes32[idx], - hdspm)); - if (err < 0) - return err; - } - for (idx = 1; idx <= 8; idx++) { - aes_sync_ctl.index = idx; + if (NULL != list) { + for (idx = 0; idx < limit; idx++) { err = snd_ctl_add(card, - snd_ctl_new1(&aes_sync_ctl, hdspm)); - if (err < 0) - return err; - } - } else { - for (idx = 0; idx < ARRAY_SIZE(snd_hdspm_controls_madi); - idx++) { - err = snd_ctl_add(card, - snd_ctl_new1(&snd_hdspm_controls_madi[idx], - hdspm)); + snd_ctl_new1(&list[idx], hdspm)); if (err < 0) return err; } } - /* Channel playback mixer as default control - Note: the whole matrix would be 128*HDSPM_MIXER_CHANNELS Faders, - thats too * big for any alsamixer they are accessible via special - IOCTL on hwdep and the mixer 2dimensional mixer control - */ + /* create simple 1:1 playback mixer controls */ snd_hdspm_playback_mixer.name = "Chn"; - limit = HDSPM_MAX_CHANNELS; - - /* The index values are one greater than the channel ID so that - * alsamixer will display them correctly. We want to use the index - * for fast lookup of the relevant channel, but if we use it at all, - * most ALSA software does the wrong thing with it ... - */ - + if (hdspm->system_sample_rate >= 128000) { + limit = hdspm->qs_out_channels; + } else if (hdspm->system_sample_rate >= 64000) { + limit = hdspm->ds_out_channels; + } else { + limit = hdspm->ss_out_channels; + } for (idx = 0; idx < limit; ++idx) { snd_hdspm_playback_mixer.index = idx + 1; kctl = snd_ctl_new1(&snd_hdspm_playback_mixer, hdspm); @@ -2930,11 +4302,24 @@ static int snd_hdspm_create_controls(struct snd_card *card, struct hdspm * hdspm hdspm->playback_mixer_ctls[idx] = kctl; } + + if (hdspm->tco) { + /* add tco control elements */ + list = snd_hdspm_controls_tco; + limit = ARRAY_SIZE(snd_hdspm_controls_tco); + for (idx = 0; idx < limit; idx++) { + err = snd_ctl_add(card, + snd_ctl_new1(&list[idx], hdspm)); + if (err < 0) + return err; + } + } + return 0; } /*------------------------------------------------------------ - /proc interface + /proc interface ------------------------------------------------------------*/ static void @@ -2942,72 +4327,178 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry, struct snd_info_buffer *buffer) { struct hdspm *hdspm = entry->private_data; - unsigned int status; - unsigned int status2; + unsigned int status, status2, control, freq; + char *pref_sync_ref; char *autosync_ref; char *system_clock_mode; - char *clock_source; char *insel; - char *syncref; int x, x2; + /* TCO stuff */ + int a, ltc, frames, seconds, minutes, hours; + unsigned int period; + u64 freq_const = 0; + u32 rate; + status = hdspm_read(hdspm, HDSPM_statusRegister); status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + control = hdspm->control_register; + freq = hdspm_read(hdspm, HDSPM_timecodeRegister); snd_iprintf(buffer, "%s (Card #%d) Rev.%x Status2first3bits: %x\n", - hdspm->card_name, hdspm->card->number + 1, - hdspm->firmware_rev, - (status2 & HDSPM_version0) | - (status2 & HDSPM_version1) | (status2 & - HDSPM_version2)); + hdspm->card_name, hdspm->card->number + 1, + hdspm->firmware_rev, + (status2 & HDSPM_version0) | + (status2 & HDSPM_version1) | (status2 & + HDSPM_version2)); + + snd_iprintf(buffer, "HW Serial: 0x%06x%06x\n", + (hdspm_read(hdspm, HDSPM_midiStatusIn1)>>8) & 0xFFFFFF, + (hdspm_read(hdspm, HDSPM_midiStatusIn0)>>8) & 0xFFFFFF); snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n", - hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase); + hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase); snd_iprintf(buffer, "--- System ---\n"); snd_iprintf(buffer, - "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n", - status & HDSPM_audioIRQPending, - (status & HDSPM_midi0IRQPending) ? 1 : 0, - (status & HDSPM_midi1IRQPending) ? 1 : 0, - hdspm->irq_count); + "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n", + status & HDSPM_audioIRQPending, + (status & HDSPM_midi0IRQPending) ? 1 : 0, + (status & HDSPM_midi1IRQPending) ? 1 : 0, + hdspm->irq_count); snd_iprintf(buffer, - "HW pointer: id = %d, rawptr = %d (%d->%d) " - "estimated= %ld (bytes)\n", - ((status & HDSPM_BufferID) ? 1 : 0), - (status & HDSPM_BufferPositionMask), - (status & HDSPM_BufferPositionMask) % - (2 * (int)hdspm->period_bytes), - ((status & HDSPM_BufferPositionMask) - 64) % - (2 * (int)hdspm->period_bytes), - (long) hdspm_hw_pointer(hdspm) * 4); + "HW pointer: id = %d, rawptr = %d (%d->%d) " + "estimated= %ld (bytes)\n", + ((status & HDSPM_BufferID) ? 1 : 0), + (status & HDSPM_BufferPositionMask), + (status & HDSPM_BufferPositionMask) % + (2 * (int)hdspm->period_bytes), + ((status & HDSPM_BufferPositionMask) - 64) % + (2 * (int)hdspm->period_bytes), + (long) hdspm_hw_pointer(hdspm) * 4); snd_iprintf(buffer, - "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n", - hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF, - hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF, - hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF, - hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF); + "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n", + hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF); snd_iprintf(buffer, - "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, " - "status2=0x%x\n", - hdspm->control_register, hdspm->control2_register, - status, status2); + "MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n", + hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF); + snd_iprintf(buffer, + "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, " + "status2=0x%x\n", + hdspm->control_register, hdspm->control2_register, + status, status2); + if (status & HDSPM_tco_detect) { + snd_iprintf(buffer, "TCO module detected.\n"); + a = hdspm_read(hdspm, HDSPM_RD_TCO+4); + if (a & HDSPM_TCO1_LTC_Input_valid) { + snd_iprintf(buffer, " LTC valid, "); + switch (a & (HDSPM_TCO1_LTC_Format_LSB | + HDSPM_TCO1_LTC_Format_MSB)) { + case 0: + snd_iprintf(buffer, "24 fps, "); + break; + case HDSPM_TCO1_LTC_Format_LSB: + snd_iprintf(buffer, "25 fps, "); + break; + case HDSPM_TCO1_LTC_Format_MSB: + snd_iprintf(buffer, "29.97 fps, "); + break; + default: + snd_iprintf(buffer, "30 fps, "); + break; + } + if (a & HDSPM_TCO1_set_drop_frame_flag) { + snd_iprintf(buffer, "drop frame\n"); + } else { + snd_iprintf(buffer, "full frame\n"); + } + } else { + snd_iprintf(buffer, " no LTC\n"); + } + if (a & HDSPM_TCO1_Video_Input_Format_NTSC) { + snd_iprintf(buffer, " Video: NTSC\n"); + } else if (a & HDSPM_TCO1_Video_Input_Format_PAL) { + snd_iprintf(buffer, " Video: PAL\n"); + } else { + snd_iprintf(buffer, " No video\n"); + } + if (a & HDSPM_TCO1_TCO_lock) { + snd_iprintf(buffer, " Sync: lock\n"); + } else { + snd_iprintf(buffer, " Sync: no lock\n"); + } + + switch (hdspm->io_type) { + case MADI: + case AES32: + freq_const = 110069313433624ULL; + break; + case RayDAT: + case AIO: + freq_const = 104857600000000ULL; + break; + case MADIface: + break; /* no TCO possible */ + } + + period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ); + snd_iprintf(buffer, " period: %u\n", period); + + + /* rate = freq_const/period; */ + rate = div_u64(freq_const, period); + + if (control & HDSPM_QuadSpeed) { + rate *= 4; + } else if (control & HDSPM_DoubleSpeed) { + rate *= 2; + } + + snd_iprintf(buffer, " Frequency: %u Hz\n", + (unsigned int) rate); + + ltc = hdspm_read(hdspm, HDSPM_RD_TCO); + frames = ltc & 0xF; + ltc >>= 4; + frames += (ltc & 0x3) * 10; + ltc >>= 4; + seconds = ltc & 0xF; + ltc >>= 4; + seconds += (ltc & 0x7) * 10; + ltc >>= 4; + minutes = ltc & 0xF; + ltc >>= 4; + minutes += (ltc & 0x7) * 10; + ltc >>= 4; + hours = ltc & 0xF; + ltc >>= 4; + hours += (ltc & 0x3) * 10; + snd_iprintf(buffer, + " LTC In: %02d:%02d:%02d:%02d\n", + hours, minutes, seconds, frames); + + } else { + snd_iprintf(buffer, "No TCO module detected.\n"); + } snd_iprintf(buffer, "--- Settings ---\n"); x = 1 << (6 + hdspm_decode_latency(hdspm->control_register & - HDSPM_LatencyMask)); + HDSPM_LatencyMask)); snd_iprintf(buffer, - "Size (Latency): %d samples (2 periods of %lu bytes)\n", - x, (unsigned long) hdspm->period_bytes); + "Size (Latency): %d samples (2 periods of %lu bytes)\n", + x, (unsigned long) hdspm->period_bytes); - snd_iprintf(buffer, "Line out: %s, Precise Pointer: %s\n", - (hdspm->control_register & HDSPM_LineOut) ? "on " : "off", - (hdspm->precise_ptr) ? "on" : "off"); + snd_iprintf(buffer, "Line out: %s\n", + (hdspm->control_register & HDSPM_LineOut) ? "on " : "off"); switch (hdspm->control_register & HDSPM_InputMask) { case HDSPM_InputOptical: @@ -3017,63 +4508,22 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry, insel = "Coaxial"; break; default: - insel = "Unknown"; - } - - switch (hdspm->control_register & HDSPM_SyncRefMask) { - case HDSPM_SyncRef_Word: - syncref = "WordClock"; - break; - case HDSPM_SyncRef_MADI: - syncref = "MADI"; - break; - default: - syncref = "Unknown"; + insel = "Unkown"; } - snd_iprintf(buffer, "Inputsel = %s, SyncRef = %s\n", insel, - syncref); snd_iprintf(buffer, - "ClearTrackMarker = %s, Transmit in %s Channel Mode, " - "Auto Input %s\n", - (hdspm-> - control_register & HDSPM_clr_tms) ? "on" : "off", - (hdspm-> - control_register & HDSPM_TX_64ch) ? "64" : "56", - (hdspm-> - control_register & HDSPM_AutoInp) ? "on" : "off"); + "ClearTrackMarker = %s, Transmit in %s Channel Mode, " + "Auto Input %s\n", + (hdspm->control_register & HDSPM_clr_tms) ? "on" : "off", + (hdspm->control_register & HDSPM_TX_64ch) ? "64" : "56", + (hdspm->control_register & HDSPM_AutoInp) ? "on" : "off"); + - switch (hdspm_clock_source(hdspm)) { - case HDSPM_CLOCK_SOURCE_AUTOSYNC: - clock_source = "AutoSync"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ: - clock_source = "Internal 32 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ: - clock_source = "Internal 44.1 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ: - clock_source = "Internal 48 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ: - clock_source = "Internal 64 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ: - clock_source = "Internal 88.2 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ: - clock_source = "Internal 96 kHz"; - break; - default: - clock_source = "Error"; - } - snd_iprintf(buffer, "Sample Clock Source: %s\n", clock_source); if (!(hdspm->control_register & HDSPM_ClockModeMaster)) - system_clock_mode = "Slave"; + system_clock_mode = "AutoSync"; else system_clock_mode = "Master"; - snd_iprintf(buffer, "System Clock Mode: %s\n", system_clock_mode); + snd_iprintf(buffer, "AutoSync Reference: %s\n", system_clock_mode); switch (hdspm_pref_sync_ref(hdspm)) { case HDSPM_SYNC_FROM_WORD: @@ -3082,15 +4532,21 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry, case HDSPM_SYNC_FROM_MADI: pref_sync_ref = "MADI Sync"; break; + case HDSPM_SYNC_FROM_TCO: + pref_sync_ref = "TCO"; + break; + case HDSPM_SYNC_FROM_SYNC_IN: + pref_sync_ref = "Sync In"; + break; default: pref_sync_ref = "XXXX Clock"; break; } snd_iprintf(buffer, "Preferred Sync Reference: %s\n", - pref_sync_ref); + pref_sync_ref); snd_iprintf(buffer, "System Clock Frequency: %d\n", - hdspm->system_sample_rate); + hdspm->system_sample_rate); snd_iprintf(buffer, "--- Status:\n"); @@ -3099,12 +4555,18 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry, x2 = status2 & HDSPM_wcSync; snd_iprintf(buffer, "Inputs MADI=%s, WordClock=%s\n", - (status & HDSPM_madiLock) ? (x ? "Sync" : "Lock") : - "NoLock", - (status2 & HDSPM_wcLock) ? (x2 ? "Sync" : "Lock") : - "NoLock"); + (status & HDSPM_madiLock) ? (x ? "Sync" : "Lock") : + "NoLock", + (status2 & HDSPM_wcLock) ? (x2 ? "Sync" : "Lock") : + "NoLock"); switch (hdspm_autosync_ref(hdspm)) { + case HDSPM_AUTOSYNC_FROM_SYNC_IN: + autosync_ref = "Sync In"; + break; + case HDSPM_AUTOSYNC_FROM_TCO: + autosync_ref = "TCO"; + break; case HDSPM_AUTOSYNC_FROM_WORD: autosync_ref = "Word Clock"; break; @@ -3119,15 +4581,15 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry, break; } snd_iprintf(buffer, - "AutoSync: Reference= %s, Freq=%d (MADI = %d, Word = %d)\n", - autosync_ref, hdspm_external_sample_rate(hdspm), - (status & HDSPM_madiFreqMask) >> 22, - (status2 & HDSPM_wcFreqMask) >> 5); + "AutoSync: Reference= %s, Freq=%d (MADI = %d, Word = %d)\n", + autosync_ref, hdspm_external_sample_rate(hdspm), + (status & HDSPM_madiFreqMask) >> 22, + (status2 & HDSPM_wcFreqMask) >> 5); snd_iprintf(buffer, "Input: %s, Mode=%s\n", - (status & HDSPM_AB_int) ? "Coax" : "Optical", - (status & HDSPM_RX_64ch) ? "64 channels" : - "56 channels"); + (status & HDSPM_AB_int) ? "Coax" : "Optical", + (status & HDSPM_RX_64ch) ? "64 channels" : + "56 channels"); snd_iprintf(buffer, "\n"); } @@ -3142,8 +4604,6 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry, unsigned int timecode; int pref_syncref; char *autosync_ref; - char *system_clock_mode; - char *clock_source; int x; status = hdspm_read(hdspm, HDSPM_statusRegister); @@ -3183,24 +4643,27 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry, hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF, hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF); snd_iprintf(buffer, - "Register: ctrl1=0x%x, status1=0x%x, status2=0x%x, " - "timecode=0x%x\n", - hdspm->control_register, - status, status2, timecode); + "MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n", + hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF); + snd_iprintf(buffer, + "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, " + "status2=0x%x\n", + hdspm->control_register, hdspm->control2_register, + status, status2); snd_iprintf(buffer, "--- Settings ---\n"); x = 1 << (6 + hdspm_decode_latency(hdspm->control_register & - HDSPM_LatencyMask)); + HDSPM_LatencyMask)); snd_iprintf(buffer, "Size (Latency): %d samples (2 periods of %lu bytes)\n", x, (unsigned long) hdspm->period_bytes); - snd_iprintf(buffer, "Line out: %s, Precise Pointer: %s\n", + snd_iprintf(buffer, "Line out: %s\n", (hdspm-> - control_register & HDSPM_LineOut) ? "on " : "off", - (hdspm->precise_ptr) ? "on" : "off"); + control_register & HDSPM_LineOut) ? "on " : "off"); snd_iprintf(buffer, "ClearTrackMarker %s, Emphasis %s, Dolby %s\n", @@ -3211,46 +4674,6 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry, (hdspm-> control_register & HDSPM_Dolby) ? "on" : "off"); - switch (hdspm_clock_source(hdspm)) { - case HDSPM_CLOCK_SOURCE_AUTOSYNC: - clock_source = "AutoSync"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ: - clock_source = "Internal 32 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ: - clock_source = "Internal 44.1 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ: - clock_source = "Internal 48 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ: - clock_source = "Internal 64 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ: - clock_source = "Internal 88.2 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ: - clock_source = "Internal 96 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_128KHZ: - clock_source = "Internal 128 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_176_4KHZ: - clock_source = "Internal 176.4 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_192KHZ: - clock_source = "Internal 192 kHz"; - break; - default: - clock_source = "Error"; - } - snd_iprintf(buffer, "Sample Clock Source: %s\n", clock_source); - if (!(hdspm->control_register & HDSPM_ClockModeMaster)) - system_clock_mode = "Slave"; - else - system_clock_mode = "Master"; - snd_iprintf(buffer, "System Clock Mode: %s\n", system_clock_mode); pref_syncref = hdspm_pref_sync_ref(hdspm); if (pref_syncref == 0) @@ -3274,38 +4697,108 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry, snd_iprintf(buffer, "--- Status:\n"); snd_iprintf(buffer, "Word: %s Frequency: %d\n", - (status & HDSPM_AES32_wcLock)? "Sync " : "No Lock", + (status & HDSPM_AES32_wcLock) ? "Sync " : "No Lock", HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF)); for (x = 0; x < 8; x++) { snd_iprintf(buffer, "AES%d: %s Frequency: %d\n", x+1, (status2 & (HDSPM_LockAES >> x)) ? - "Sync ": "No Lock", + "Sync " : "No Lock", HDSPM_bit2freq((timecode >> (4*x)) & 0xF)); } switch (hdspm_autosync_ref(hdspm)) { - case HDSPM_AES32_AUTOSYNC_FROM_NONE: autosync_ref="None"; break; - case HDSPM_AES32_AUTOSYNC_FROM_WORD: autosync_ref="Word Clock"; break; - case HDSPM_AES32_AUTOSYNC_FROM_AES1: autosync_ref="AES1"; break; - case HDSPM_AES32_AUTOSYNC_FROM_AES2: autosync_ref="AES2"; break; - case HDSPM_AES32_AUTOSYNC_FROM_AES3: autosync_ref="AES3"; break; - case HDSPM_AES32_AUTOSYNC_FROM_AES4: autosync_ref="AES4"; break; - case HDSPM_AES32_AUTOSYNC_FROM_AES5: autosync_ref="AES5"; break; - case HDSPM_AES32_AUTOSYNC_FROM_AES6: autosync_ref="AES6"; break; - case HDSPM_AES32_AUTOSYNC_FROM_AES7: autosync_ref="AES7"; break; - case HDSPM_AES32_AUTOSYNC_FROM_AES8: autosync_ref="AES8"; break; - default: autosync_ref = "---"; break; + case HDSPM_AES32_AUTOSYNC_FROM_NONE: + autosync_ref = "None"; break; + case HDSPM_AES32_AUTOSYNC_FROM_WORD: + autosync_ref = "Word Clock"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES1: + autosync_ref = "AES1"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES2: + autosync_ref = "AES2"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES3: + autosync_ref = "AES3"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES4: + autosync_ref = "AES4"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES5: + autosync_ref = "AES5"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES6: + autosync_ref = "AES6"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES7: + autosync_ref = "AES7"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES8: + autosync_ref = "AES8"; break; + default: + autosync_ref = "---"; break; } snd_iprintf(buffer, "AutoSync ref = %s\n", autosync_ref); snd_iprintf(buffer, "\n"); } +static void +snd_hdspm_proc_read_raydat(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct hdspm *hdspm = entry->private_data; + unsigned int status1, status2, status3, control, i; + unsigned int lock, sync; + + status1 = hdspm_read(hdspm, HDSPM_RD_STATUS_1); /* s1 */ + status2 = hdspm_read(hdspm, HDSPM_RD_STATUS_2); /* freq */ + status3 = hdspm_read(hdspm, HDSPM_RD_STATUS_3); /* s2 */ + + control = hdspm->control_register; + + snd_iprintf(buffer, "STATUS1: 0x%08x\n", status1); + snd_iprintf(buffer, "STATUS2: 0x%08x\n", status2); + snd_iprintf(buffer, "STATUS3: 0x%08x\n", status3); + + + snd_iprintf(buffer, "\n*** CLOCK MODE\n\n"); + + snd_iprintf(buffer, "Clock mode : %s\n", + (hdspm_system_clock_mode(hdspm) == 0) ? "master" : "slave"); + snd_iprintf(buffer, "System frequency: %d Hz\n", + hdspm_get_system_sample_rate(hdspm)); + + snd_iprintf(buffer, "\n*** INPUT STATUS\n\n"); + + lock = 0x1; + sync = 0x100; + + for (i = 0; i < 8; i++) { + snd_iprintf(buffer, "s1_input %d: Lock %d, Sync %d, Freq %s\n", + i, + (status1 & lock) ? 1 : 0, + (status1 & sync) ? 1 : 0, + texts_freq[(status2 >> (i * 4)) & 0xF]); + + lock = lock<<1; + sync = sync<<1; + } + + snd_iprintf(buffer, "WC input: Lock %d, Sync %d, Freq %s\n", + (status1 & 0x1000000) ? 1 : 0, + (status1 & 0x2000000) ? 1 : 0, + texts_freq[(status1 >> 16) & 0xF]); + + snd_iprintf(buffer, "TCO input: Lock %d, Sync %d, Freq %s\n", + (status1 & 0x4000000) ? 1 : 0, + (status1 & 0x8000000) ? 1 : 0, + texts_freq[(status1 >> 20) & 0xF]); + + snd_iprintf(buffer, "SYNC IN: Lock %d, Sync %d, Freq %s\n", + (status3 & 0x400) ? 1 : 0, + (status3 & 0x800) ? 1 : 0, + texts_freq[(status2 >> 12) & 0xF]); + +} + #ifdef CONFIG_SND_DEBUG static void -snd_hdspm_proc_read_debug(struct snd_info_entry * entry, +snd_hdspm_proc_read_debug(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct hdspm *hdspm = entry->private_data; @@ -3322,16 +4815,68 @@ snd_hdspm_proc_read_debug(struct snd_info_entry * entry, #endif +static void snd_hdspm_proc_ports_in(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct hdspm *hdspm = entry->private_data; + int i; + + snd_iprintf(buffer, "# generated by hdspm\n"); + + for (i = 0; i < hdspm->max_channels_in; i++) { + snd_iprintf(buffer, "%d=%s\n", i+1, hdspm->port_names_in[i]); + } +} + +static void snd_hdspm_proc_ports_out(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct hdspm *hdspm = entry->private_data; + int i; + + snd_iprintf(buffer, "# generated by hdspm\n"); + + for (i = 0; i < hdspm->max_channels_out; i++) { + snd_iprintf(buffer, "%d=%s\n", i+1, hdspm->port_names_out[i]); + } +} + -static void __devinit snd_hdspm_proc_init(struct hdspm * hdspm) +static void __devinit snd_hdspm_proc_init(struct hdspm *hdspm) { struct snd_info_entry *entry; - if (!snd_card_proc_new(hdspm->card, "hdspm", &entry)) - snd_info_set_text_ops(entry, hdspm, - hdspm->is_aes32 ? - snd_hdspm_proc_read_aes32 : - snd_hdspm_proc_read_madi); + if (!snd_card_proc_new(hdspm->card, "hdspm", &entry)) { + switch (hdspm->io_type) { + case AES32: + snd_info_set_text_ops(entry, hdspm, + snd_hdspm_proc_read_aes32); + break; + case MADI: + snd_info_set_text_ops(entry, hdspm, + snd_hdspm_proc_read_madi); + break; + case MADIface: + /* snd_info_set_text_ops(entry, hdspm, + snd_hdspm_proc_read_madiface); */ + break; + case RayDAT: + snd_info_set_text_ops(entry, hdspm, + snd_hdspm_proc_read_raydat); + break; + case AIO: + break; + } + } + + if (!snd_card_proc_new(hdspm->card, "ports.in", &entry)) { + snd_info_set_text_ops(entry, hdspm, snd_hdspm_proc_ports_in); + } + + if (!snd_card_proc_new(hdspm->card, "ports.out", &entry)) { + snd_info_set_text_ops(entry, hdspm, snd_hdspm_proc_ports_out); + } + #ifdef CONFIG_SND_DEBUG /* debug file to read all hdspm registers */ if (!snd_card_proc_new(hdspm->card, "debug", &entry)) @@ -3341,47 +4886,48 @@ static void __devinit snd_hdspm_proc_init(struct hdspm * hdspm) } /*------------------------------------------------------------ - hdspm intitialize + hdspm intitialize ------------------------------------------------------------*/ static int snd_hdspm_set_defaults(struct hdspm * hdspm) { - unsigned int i; - /* ASSUMPTION: hdspm->lock is either held, or there is no need to hold it (e.g. during module initialization). - */ + */ /* set defaults: */ - if (hdspm->is_aes32) + hdspm->settings_register = 0; + + switch (hdspm->io_type) { + case MADI: + case MADIface: + hdspm->control_register = + 0x2 + 0x8 + 0x10 + 0x80 + 0x400 + 0x4000 + 0x1000000; + break; + + case RayDAT: + case AIO: + hdspm->settings_register = 0x1 + 0x1000; + /* Magic values are: LAT_0, LAT_2, Master, freq1, tx64ch, inp_0, + * line_out */ + hdspm->control_register = + 0x2 + 0x8 + 0x10 + 0x80 + 0x400 + 0x4000 + 0x1000000; + break; + + case AES32: hdspm->control_register = HDSPM_ClockModeMaster | /* Master Cloack Mode on */ - hdspm_encode_latency(7) | /* latency maximum = - * 8192 samples - */ + hdspm_encode_latency(7) | /* latency max=8192samples */ HDSPM_SyncRef0 | /* AES1 is syncclock */ HDSPM_LineOut | /* Analog output in */ HDSPM_Professional; /* Professional mode */ - else - hdspm->control_register = - HDSPM_ClockModeMaster | /* Master Cloack Mode on */ - hdspm_encode_latency(7) | /* latency maximum = - * 8192 samples - */ - HDSPM_InputCoaxial | /* Input Coax not Optical */ - HDSPM_SyncRef_MADI | /* Madi is syncclock */ - HDSPM_LineOut | /* Analog output in */ - HDSPM_TX_64ch | /* transmit in 64ch mode */ - HDSPM_AutoInp; /* AutoInput chossing (takeover) */ - - /* ! HDSPM_Frequency0|HDSPM_Frequency1 = 44.1khz */ - /* ! HDSPM_DoubleSpeed HDSPM_QuadSpeed = normal speed */ - /* ! HDSPM_clr_tms = do not clear bits in track marks */ + break; + } hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); - if (!hdspm->is_aes32) { + if (AES32 == hdspm->io_type) { /* No control2 register for AES32 */ #ifdef SNDRV_BIG_ENDIAN hdspm->control2_register = HDSPM_BIGENDIAN_MODE; @@ -3397,57 +4943,59 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm) all_in_all_mixer(hdspm, 0 * UNITY_GAIN); - if (line_outs_monitor[hdspm->dev]) { - - snd_printk(KERN_INFO "HDSPM: " - "sending all playback streams to line outs.\n"); - - for (i = 0; i < HDSPM_MIXER_CHANNELS; i++) { - if (hdspm_write_pb_gain(hdspm, i, i, UNITY_GAIN)) - return -EIO; - } + if (hdspm->io_type == AIO || hdspm->io_type == RayDAT) { + hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register); } /* set a default rate so that the channel map is set up. */ - hdspm->channel_map = channel_map_madi_ss; - hdspm_set_rate(hdspm, 44100, 1); + hdspm_set_rate(hdspm, 48000, 1); return 0; } /*------------------------------------------------------------ - interrupt + interrupt ------------------------------------------------------------*/ static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id) { struct hdspm *hdspm = (struct hdspm *) dev_id; unsigned int status; - int audio; - int midi0; - int midi1; - unsigned int midi0status; - unsigned int midi1status; - int schedule = 0; + int i, audio, midi, schedule = 0; + /* cycles_t now; */ status = hdspm_read(hdspm, HDSPM_statusRegister); audio = status & HDSPM_audioIRQPending; - midi0 = status & HDSPM_midi0IRQPending; - midi1 = status & HDSPM_midi1IRQPending; + midi = status & (HDSPM_midi0IRQPending | HDSPM_midi1IRQPending | + HDSPM_midi2IRQPending | HDSPM_midi3IRQPending); + + /* now = get_cycles(); */ + /** + * LAT_2..LAT_0 period counter (win) counter (mac) + * 6 4096 ~256053425 ~514672358 + * 5 2048 ~128024983 ~257373821 + * 4 1024 ~64023706 ~128718089 + * 3 512 ~32005945 ~64385999 + * 2 256 ~16003039 ~32260176 + * 1 128 ~7998738 ~16194507 + * 0 64 ~3998231 ~8191558 + **/ + /* + snd_printk(KERN_INFO "snd_hdspm_interrupt %llu @ %llx\n", + now-hdspm->last_interrupt, status & 0xFFC0); + hdspm->last_interrupt = now; + */ - if (!audio && !midi0 && !midi1) + if (!audio && !midi) return IRQ_NONE; hdspm_write(hdspm, HDSPM_interruptConfirmation, 0); hdspm->irq_count++; - midi0status = hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xff; - midi1status = hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xff; if (audio) { - if (hdspm->capture_substream) snd_pcm_period_elapsed(hdspm->capture_substream); @@ -3455,118 +5003,44 @@ static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id) snd_pcm_period_elapsed(hdspm->playback_substream); } - if (midi0 && midi0status) { - /* we disable interrupts for this input until processing - * is done - */ - hdspm->control_register &= ~HDSPM_Midi0InterruptEnable; - hdspm_write(hdspm, HDSPM_controlRegister, - hdspm->control_register); - hdspm->midi[0].pending = 1; - schedule = 1; - } - if (midi1 && midi1status) { - /* we disable interrupts for this input until processing - * is done - */ - hdspm->control_register &= ~HDSPM_Midi1InterruptEnable; - hdspm_write(hdspm, HDSPM_controlRegister, - hdspm->control_register); - hdspm->midi[1].pending = 1; - schedule = 1; + if (midi) { + i = 0; + while (i < hdspm->midiPorts) { + if ((hdspm_read(hdspm, + hdspm->midi[i].statusIn) & 0xff) && + (status & hdspm->midi[i].irq)) { + /* we disable interrupts for this input until + * processing is done + */ + hdspm->control_register &= ~hdspm->midi[i].ie; + hdspm_write(hdspm, HDSPM_controlRegister, + hdspm->control_register); + hdspm->midi[i].pending = 1; + schedule = 1; + } + + i++; + } + + if (schedule) + tasklet_hi_schedule(&hdspm->midi_tasklet); } - if (schedule) - tasklet_schedule(&hdspm->midi_tasklet); + return IRQ_HANDLED; } /*------------------------------------------------------------ - pcm interface + pcm interface ------------------------------------------------------------*/ -static snd_pcm_uframes_t snd_hdspm_hw_pointer(struct snd_pcm_substream * - substream) +static snd_pcm_uframes_t snd_hdspm_hw_pointer(struct snd_pcm_substream + *substream) { struct hdspm *hdspm = snd_pcm_substream_chip(substream); return hdspm_hw_pointer(hdspm); } -static char *hdspm_channel_buffer_location(struct hdspm * hdspm, - int stream, int channel) -{ - int mapped_channel; - - if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS)) - return NULL; - - mapped_channel = hdspm->channel_map[channel]; - if (mapped_channel < 0) - return NULL; - - if (stream == SNDRV_PCM_STREAM_CAPTURE) - return hdspm->capture_buffer + - mapped_channel * HDSPM_CHANNEL_BUFFER_BYTES; - else - return hdspm->playback_buffer + - mapped_channel * HDSPM_CHANNEL_BUFFER_BYTES; -} - - -/* dont know why need it ??? */ -static int snd_hdspm_playback_copy(struct snd_pcm_substream *substream, - int channel, snd_pcm_uframes_t pos, - void __user *src, snd_pcm_uframes_t count) -{ - struct hdspm *hdspm = snd_pcm_substream_chip(substream); - char *channel_buf; - - if (snd_BUG_ON(pos + count > HDSPM_CHANNEL_BUFFER_BYTES / 4)) - return -EINVAL; - - channel_buf = - hdspm_channel_buffer_location(hdspm, substream->pstr->stream, - channel); - - if (snd_BUG_ON(!channel_buf)) - return -EIO; - - return copy_from_user(channel_buf + pos * 4, src, count * 4); -} - -static int snd_hdspm_capture_copy(struct snd_pcm_substream *substream, - int channel, snd_pcm_uframes_t pos, - void __user *dst, snd_pcm_uframes_t count) -{ - struct hdspm *hdspm = snd_pcm_substream_chip(substream); - char *channel_buf; - - if (snd_BUG_ON(pos + count > HDSPM_CHANNEL_BUFFER_BYTES / 4)) - return -EINVAL; - - channel_buf = - hdspm_channel_buffer_location(hdspm, substream->pstr->stream, - channel); - if (snd_BUG_ON(!channel_buf)) - return -EIO; - return copy_to_user(dst, channel_buf + pos * 4, count * 4); -} - -static int snd_hdspm_hw_silence(struct snd_pcm_substream *substream, - int channel, snd_pcm_uframes_t pos, - snd_pcm_uframes_t count) -{ - struct hdspm *hdspm = snd_pcm_substream_chip(substream); - char *channel_buf; - - channel_buf = - hdspm_channel_buffer_location(hdspm, substream->pstr->stream, - channel); - if (snd_BUG_ON(!channel_buf)) - return -EIO; - memset(channel_buf + pos * 4, 0, count * 4); - return 0; -} static int snd_hdspm_reset(struct snd_pcm_substream *substream) { @@ -3589,7 +5063,7 @@ static int snd_hdspm_reset(struct snd_pcm_substream *substream) snd_pcm_group_for_each_entry(s, substream) { if (s == other) { oruntime->status->hw_ptr = - runtime->status->hw_ptr; + runtime->status->hw_ptr; break; } } @@ -3621,19 +5095,19 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream, /* The other stream is open, and not by the same task as this one. Make sure that the parameters that matter are the same. - */ + */ if (params_rate(params) != hdspm->system_sample_rate) { spin_unlock_irq(&hdspm->lock); _snd_pcm_hw_param_setempty(params, - SNDRV_PCM_HW_PARAM_RATE); + SNDRV_PCM_HW_PARAM_RATE); return -EBUSY; } if (params_period_size(params) != hdspm->period_bytes / 4) { spin_unlock_irq(&hdspm->lock); _snd_pcm_hw_param_setempty(params, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + SNDRV_PCM_HW_PARAM_PERIOD_SIZE); return -EBUSY; } @@ -3646,18 +5120,20 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream, spin_lock_irq(&hdspm->lock); err = hdspm_set_rate(hdspm, params_rate(params), 0); if (err < 0) { + snd_printk(KERN_INFO "err on hdspm_set_rate: %d\n", err); spin_unlock_irq(&hdspm->lock); _snd_pcm_hw_param_setempty(params, - SNDRV_PCM_HW_PARAM_RATE); + SNDRV_PCM_HW_PARAM_RATE); return err; } spin_unlock_irq(&hdspm->lock); err = hdspm_set_interrupt_interval(hdspm, - params_period_size(params)); + params_period_size(params)); if (err < 0) { + snd_printk(KERN_INFO "err on hdspm_set_interrupt_interval: %d\n", err); _snd_pcm_hw_param_setempty(params, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + SNDRV_PCM_HW_PARAM_PERIOD_SIZE); return err; } @@ -3667,10 +5143,13 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream, /* malloc all buffer even if not enabled to get sure */ /* Update for MADI rev 204: we need to allocate for all channels, * otherwise it doesn't work at 96kHz */ + err = - snd_pcm_lib_malloc_pages(substream, HDSPM_DMA_AREA_BYTES); - if (err < 0) + snd_pcm_lib_malloc_pages(substream, HDSPM_DMA_AREA_BYTES); + if (err < 0) { + snd_printk(KERN_INFO "err on snd_pcm_lib_malloc_pages: %d\n", err); return err; + } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -3681,7 +5160,7 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream, snd_hdspm_enable_out(hdspm, i, 1); hdspm->playback_buffer = - (unsigned char *) substream->runtime->dma_area; + (unsigned char *) substream->runtime->dma_area; snd_printdd("Allocated sample buffer for playback at %p\n", hdspm->playback_buffer); } else { @@ -3692,23 +5171,40 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream, snd_hdspm_enable_in(hdspm, i, 1); hdspm->capture_buffer = - (unsigned char *) substream->runtime->dma_area; + (unsigned char *) substream->runtime->dma_area; snd_printdd("Allocated sample buffer for capture at %p\n", hdspm->capture_buffer); } + /* snd_printdd("Allocated sample buffer for %s at 0x%08X\n", substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "capture", snd_pcm_sgbuf_get_addr(substream, 0)); - */ + */ /* - snd_printdd("set_hwparams: %s %d Hz, %d channels, bs = %d\n", - substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? - "playback" : "capture", - params_rate(params), params_channels(params), - params_buffer_size(params)); - */ + snd_printdd("set_hwparams: %s %d Hz, %d channels, bs = %d\n", + substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + "playback" : "capture", + params_rate(params), params_channels(params), + params_buffer_size(params)); + */ + + + /* Switch to native float format if requested */ + if (SNDRV_PCM_FORMAT_FLOAT_LE == params_format(params)) { + if (!(hdspm->control_register & HDSPe_FLOAT_FORMAT)) + snd_printk(KERN_INFO "hdspm: Switching to native 32bit LE float format.\n"); + + hdspm->control_register |= HDSPe_FLOAT_FORMAT; + } else if (SNDRV_PCM_FORMAT_S32_LE == params_format(params)) { + if (hdspm->control_register & HDSPe_FLOAT_FORMAT) + snd_printk(KERN_INFO "hdspm: Switching to native 32bit LE integer format.\n"); + + hdspm->control_register &= ~HDSPe_FLOAT_FORMAT; + } + hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); + return 0; } @@ -3719,14 +5215,14 @@ static int snd_hdspm_hw_free(struct snd_pcm_substream *substream) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - /* params_channels(params) should be enough, + /* params_channels(params) should be enough, but to get sure in case of error */ - for (i = 0; i < HDSPM_MAX_CHANNELS; ++i) + for (i = 0; i < hdspm->max_channels_out; ++i) snd_hdspm_enable_out(hdspm, i, 0); hdspm->playback_buffer = NULL; } else { - for (i = 0; i < HDSPM_MAX_CHANNELS; ++i) + for (i = 0; i < hdspm->max_channels_in; ++i) snd_hdspm_enable_in(hdspm, i, 0); hdspm->capture_buffer = NULL; @@ -3738,37 +5234,58 @@ static int snd_hdspm_hw_free(struct snd_pcm_substream *substream) return 0; } + static int snd_hdspm_channel_info(struct snd_pcm_substream *substream, - struct snd_pcm_channel_info * info) + struct snd_pcm_channel_info *info) { struct hdspm *hdspm = snd_pcm_substream_chip(substream); - int mapped_channel; - if (snd_BUG_ON(info->channel >= HDSPM_MAX_CHANNELS)) - return -EINVAL; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (snd_BUG_ON(info->channel >= hdspm->max_channels_out)) { + snd_printk(KERN_INFO "snd_hdspm_channel_info: output channel out of range (%d)\n", info->channel); + return -EINVAL; + } - mapped_channel = hdspm->channel_map[info->channel]; - if (mapped_channel < 0) - return -EINVAL; + if (hdspm->channel_map_out[info->channel] < 0) { + snd_printk(KERN_INFO "snd_hdspm_channel_info: output channel %d mapped out\n", info->channel); + return -EINVAL; + } + + info->offset = hdspm->channel_map_out[info->channel] * + HDSPM_CHANNEL_BUFFER_BYTES; + } else { + if (snd_BUG_ON(info->channel >= hdspm->max_channels_in)) { + snd_printk(KERN_INFO "snd_hdspm_channel_info: input channel out of range (%d)\n", info->channel); + return -EINVAL; + } + + if (hdspm->channel_map_in[info->channel] < 0) { + snd_printk(KERN_INFO "snd_hdspm_channel_info: input channel %d mapped out\n", info->channel); + return -EINVAL; + } + + info->offset = hdspm->channel_map_in[info->channel] * + HDSPM_CHANNEL_BUFFER_BYTES; + } - info->offset = mapped_channel * HDSPM_CHANNEL_BUFFER_BYTES; info->first = 0; info->step = 32; return 0; } + static int snd_hdspm_ioctl(struct snd_pcm_substream *substream, - unsigned int cmd, void *arg) + unsigned int cmd, void *arg) { switch (cmd) { case SNDRV_PCM_IOCTL1_RESET: return snd_hdspm_reset(substream); case SNDRV_PCM_IOCTL1_CHANNEL_INFO: - { - struct snd_pcm_channel_info *info = arg; - return snd_hdspm_channel_info(substream, info); - } + { + struct snd_pcm_channel_info *info = arg; + return snd_hdspm_channel_info(substream, info); + } default: break; } @@ -3815,19 +5332,19 @@ static int snd_hdspm_trigger(struct snd_pcm_substream *substream, int cmd) } if (cmd == SNDRV_PCM_TRIGGER_START) { if (!(running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) - && substream->stream == - SNDRV_PCM_STREAM_CAPTURE) + && substream->stream == + SNDRV_PCM_STREAM_CAPTURE) hdspm_silence_playback(hdspm); } else { if (running && - substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + substream->stream == SNDRV_PCM_STREAM_PLAYBACK) hdspm_silence_playback(hdspm); } } else { if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) hdspm_silence_playback(hdspm); } - _ok: +_ok: snd_pcm_trigger_done(substream, substream); if (!hdspm->running && running) hdspm_start_audio(hdspm); @@ -3844,8 +5361,18 @@ static int snd_hdspm_prepare(struct snd_pcm_substream *substream) return 0; } -static unsigned int period_sizes[] = - { 64, 128, 256, 512, 1024, 2048, 4096, 8192 }; +static unsigned int period_sizes_old[] = { + 64, 128, 256, 512, 1024, 2048, 4096 +}; + +static unsigned int period_sizes_new[] = { + 32, 64, 128, 256, 512, 1024, 2048, 4096 +}; + +/* RayDAT and AIO always have a buffer of 16384 samples per channel */ +static unsigned int raydat_aio_buffer_sizes[] = { + 16384 +}; static struct snd_pcm_hardware snd_hdspm_playback_subinfo = { .info = (SNDRV_PCM_INFO_MMAP | @@ -3866,9 +5393,9 @@ static struct snd_pcm_hardware snd_hdspm_playback_subinfo = { .buffer_bytes_max = HDSPM_CHANNEL_BUFFER_BYTES * HDSPM_MAX_CHANNELS, .period_bytes_min = (64 * 4), - .period_bytes_max = (8192 * 4) * HDSPM_MAX_CHANNELS, + .period_bytes_max = (4096 * 4) * HDSPM_MAX_CHANNELS, .periods_min = 2, - .periods_max = 2, + .periods_max = 512, .fifo_size = 0 }; @@ -3891,20 +5418,66 @@ static struct snd_pcm_hardware snd_hdspm_capture_subinfo = { .buffer_bytes_max = HDSPM_CHANNEL_BUFFER_BYTES * HDSPM_MAX_CHANNELS, .period_bytes_min = (64 * 4), - .period_bytes_max = (8192 * 4) * HDSPM_MAX_CHANNELS, + .period_bytes_max = (4096 * 4) * HDSPM_MAX_CHANNELS, .periods_min = 2, - .periods_max = 2, + .periods_max = 512, .fifo_size = 0 }; -static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes = { - .count = ARRAY_SIZE(period_sizes), - .list = period_sizes, +static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes_old = { + .count = ARRAY_SIZE(period_sizes_old), + .list = period_sizes_old, + .mask = 0 +}; + +static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes_new = { + .count = ARRAY_SIZE(period_sizes_new), + .list = period_sizes_new, + .mask = 0 +}; + +static struct snd_pcm_hw_constraint_list hw_constraints_raydat_io_buffer = { + .count = ARRAY_SIZE(raydat_aio_buffer_sizes), + .list = raydat_aio_buffer_sizes, .mask = 0 }; +static int snd_hdspm_hw_rule_in_channels_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct hdspm *hdspm = rule->private; + struct snd_interval *c = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *r = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + + if (r->min > 96000 && r->max <= 192000) { + struct snd_interval t = { + .min = hdspm->qs_in_channels, + .max = hdspm->qs_in_channels, + .integer = 1, + }; + return snd_interval_refine(c, &t); + } else if (r->min > 48000 && r->max <= 96000) { + struct snd_interval t = { + .min = hdspm->ds_in_channels, + .max = hdspm->ds_in_channels, + .integer = 1, + }; + return snd_interval_refine(c, &t); + } else if (r->max < 64000) { + struct snd_interval t = { + .min = hdspm->ss_in_channels, + .max = hdspm->ss_in_channels, + .integer = 1, + }; + return snd_interval_refine(c, &t); + } + + return 0; +} -static int snd_hdspm_hw_rule_channels_rate(struct snd_pcm_hw_params *params, +static int snd_hdspm_hw_rule_out_channels_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule * rule) { struct hdspm *hdspm = rule->private; @@ -3913,25 +5486,33 @@ static int snd_hdspm_hw_rule_channels_rate(struct snd_pcm_hw_params *params, struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - if (r->min > 48000 && r->max <= 96000) { + if (r->min > 96000 && r->max <= 192000) { + struct snd_interval t = { + .min = hdspm->qs_out_channels, + .max = hdspm->qs_out_channels, + .integer = 1, + }; + return snd_interval_refine(c, &t); + } else if (r->min > 48000 && r->max <= 96000) { struct snd_interval t = { - .min = hdspm->ds_channels, - .max = hdspm->ds_channels, + .min = hdspm->ds_out_channels, + .max = hdspm->ds_out_channels, .integer = 1, }; return snd_interval_refine(c, &t); } else if (r->max < 64000) { struct snd_interval t = { - .min = hdspm->ss_channels, - .max = hdspm->ss_channels, + .min = hdspm->ss_out_channels, + .max = hdspm->ss_out_channels, .integer = 1, }; return snd_interval_refine(c, &t); + } else { } return 0; } -static int snd_hdspm_hw_rule_rate_channels(struct snd_pcm_hw_params *params, +static int snd_hdspm_hw_rule_rate_in_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule * rule) { struct hdspm *hdspm = rule->private; @@ -3940,42 +5521,92 @@ static int snd_hdspm_hw_rule_rate_channels(struct snd_pcm_hw_params *params, struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - if (c->min >= hdspm->ss_channels) { + if (c->min >= hdspm->ss_in_channels) { struct snd_interval t = { .min = 32000, .max = 48000, .integer = 1, }; return snd_interval_refine(r, &t); - } else if (c->max <= hdspm->ds_channels) { + } else if (c->max <= hdspm->qs_in_channels) { + struct snd_interval t = { + .min = 128000, + .max = 192000, + .integer = 1, + }; + return snd_interval_refine(r, &t); + } else if (c->max <= hdspm->ds_in_channels) { struct snd_interval t = { .min = 64000, .max = 96000, .integer = 1, }; + return snd_interval_refine(r, &t); + } + + return 0; +} +static int snd_hdspm_hw_rule_rate_out_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct hdspm *hdspm = rule->private; + struct snd_interval *c = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *r = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + if (c->min >= hdspm->ss_out_channels) { + struct snd_interval t = { + .min = 32000, + .max = 48000, + .integer = 1, + }; + return snd_interval_refine(r, &t); + } else if (c->max <= hdspm->qs_out_channels) { + struct snd_interval t = { + .min = 128000, + .max = 192000, + .integer = 1, + }; + return snd_interval_refine(r, &t); + } else if (c->max <= hdspm->ds_out_channels) { + struct snd_interval t = { + .min = 64000, + .max = 96000, + .integer = 1, + }; return snd_interval_refine(r, &t); } + return 0; } -static int snd_hdspm_hw_rule_channels(struct snd_pcm_hw_params *params, +static int snd_hdspm_hw_rule_in_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { unsigned int list[3]; struct hdspm *hdspm = rule->private; struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - if (hdspm->is_aes32) { - list[0] = hdspm->qs_channels; - list[1] = hdspm->ds_channels; - list[2] = hdspm->ss_channels; - return snd_interval_list(c, 3, list, 0); - } else { - list[0] = hdspm->ds_channels; - list[1] = hdspm->ss_channels; - return snd_interval_list(c, 2, list, 0); - } + + list[0] = hdspm->qs_in_channels; + list[1] = hdspm->ds_in_channels; + list[2] = hdspm->ss_in_channels; + return snd_interval_list(c, 3, list, 0); +} + +static int snd_hdspm_hw_rule_out_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + unsigned int list[3]; + struct hdspm *hdspm = rule->private; + struct snd_interval *c = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + list[0] = hdspm->qs_out_channels; + list[1] = hdspm->ds_out_channels; + list[2] = hdspm->ss_out_channels; + return snd_interval_list(c, 3, list, 0); } @@ -3999,6 +5630,7 @@ static int snd_hdspm_playback_open(struct snd_pcm_substream *substream) snd_pcm_set_sync(substream); + runtime->hw = snd_hdspm_playback_subinfo; if (hdspm->capture_substream == NULL) @@ -4011,24 +5643,38 @@ static int snd_hdspm_playback_open(struct snd_pcm_substream *substream) snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); - snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - &hw_constraints_period_sizes); + switch (hdspm->io_type) { + case AIO: + case RayDAT: + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + &hw_constraints_period_sizes_new); + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + &hw_constraints_raydat_io_buffer); + + break; + + default: + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + &hw_constraints_period_sizes_old); + } - if (hdspm->is_aes32) { + if (AES32 == hdspm->io_type) { snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hdspm_hw_constraints_aes32_sample_rates); } else { snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - snd_hdspm_hw_rule_channels, hdspm, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); + snd_hdspm_hw_rule_out_channels, hdspm, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - snd_hdspm_hw_rule_channels_rate, hdspm, - SNDRV_PCM_HW_PARAM_RATE, -1); + snd_hdspm_hw_rule_out_channels_rate, hdspm, + SNDRV_PCM_HW_PARAM_RATE, -1); snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - snd_hdspm_hw_rule_rate_channels, hdspm, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); + snd_hdspm_hw_rule_rate_out_channels, hdspm, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); } return 0; } @@ -4066,22 +5712,36 @@ static int snd_hdspm_capture_open(struct snd_pcm_substream *substream) spin_unlock_irq(&hdspm->lock); snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); - snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - &hw_constraints_period_sizes); - if (hdspm->is_aes32) { + switch (hdspm->io_type) { + case AIO: + case RayDAT: + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + &hw_constraints_period_sizes_new); + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + &hw_constraints_raydat_io_buffer); + break; + + default: + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + &hw_constraints_period_sizes_old); + } + + if (AES32 == hdspm->io_type) { snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hdspm_hw_constraints_aes32_sample_rates); } else { snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - snd_hdspm_hw_rule_channels, hdspm, + snd_hdspm_hw_rule_in_channels, hdspm, SNDRV_PCM_HW_PARAM_CHANNELS, -1); snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - snd_hdspm_hw_rule_channels_rate, hdspm, + snd_hdspm_hw_rule_in_channels_rate, hdspm, SNDRV_PCM_HW_PARAM_RATE, -1); snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - snd_hdspm_hw_rule_rate_channels, hdspm, + snd_hdspm_hw_rule_rate_in_channels, hdspm, SNDRV_PCM_HW_PARAM_CHANNELS, -1); } return 0; @@ -4100,41 +5760,136 @@ static int snd_hdspm_capture_release(struct snd_pcm_substream *substream) return 0; } -static int snd_hdspm_hwdep_ioctl(struct snd_hwdep * hw, struct file *file, - unsigned int cmd, unsigned long arg) +static int snd_hdspm_hwdep_dummy_op(struct snd_hwdep *hw, struct file *file) +{ + /* we have nothing to initialize but the call is required */ + return 0; +} + +static inline int copy_u32_le(void __user *dest, void __iomem *src) +{ + u32 val = readl(src); + return copy_to_user(dest, &val, 4); +} + +static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, + unsigned int cmd, unsigned long __user arg) { + void __user *argp = (void __user *)arg; struct hdspm *hdspm = hw->private_data; struct hdspm_mixer_ioctl mixer; - struct hdspm_config_info info; + struct hdspm_config info; + struct hdspm_status status; struct hdspm_version hdspm_version; - struct hdspm_peak_rms_ioctl rms; + struct hdspm_peak_rms levels; + struct hdspm_ltc ltc; + unsigned int statusregister; + long unsigned int s; + int i = 0; switch (cmd) { case SNDRV_HDSPM_IOCTL_GET_PEAK_RMS: - if (copy_from_user(&rms, (void __user *)arg, sizeof(rms))) + for (i = 0; i < HDSPM_MAX_CHANNELS; i++) { + levels.input_peaks[i] = + readl(hdspm->iobase + + HDSPM_MADI_INPUT_PEAK + i*4); + levels.playback_peaks[i] = + readl(hdspm->iobase + + HDSPM_MADI_PLAYBACK_PEAK + i*4); + levels.output_peaks[i] = + readl(hdspm->iobase + + HDSPM_MADI_OUTPUT_PEAK + i*4); + + levels.input_rms[i] = + ((uint64_t) readl(hdspm->iobase + + HDSPM_MADI_INPUT_RMS_H + i*4) << 32) | + (uint64_t) readl(hdspm->iobase + + HDSPM_MADI_INPUT_RMS_L + i*4); + levels.playback_rms[i] = + ((uint64_t)readl(hdspm->iobase + + HDSPM_MADI_PLAYBACK_RMS_H+i*4) << 32) | + (uint64_t)readl(hdspm->iobase + + HDSPM_MADI_PLAYBACK_RMS_L + i*4); + levels.output_rms[i] = + ((uint64_t)readl(hdspm->iobase + + HDSPM_MADI_OUTPUT_RMS_H + i*4) << 32) | + (uint64_t)readl(hdspm->iobase + + HDSPM_MADI_OUTPUT_RMS_L + i*4); + } + + if (hdspm->system_sample_rate > 96000) { + levels.speed = qs; + } else if (hdspm->system_sample_rate > 48000) { + levels.speed = ds; + } else { + levels.speed = ss; + } + levels.status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + + s = copy_to_user(argp, &levels, sizeof(struct hdspm_peak_rms)); + if (0 != s) { + /* snd_printk(KERN_ERR "copy_to_user(.., .., %lu): %lu + [Levels]\n", sizeof(struct hdspm_peak_rms), s); + */ return -EFAULT; - /* maybe there is a chance to memorymap in future - * so dont touch just copy - */ - if(copy_to_user_fromio((void __user *)rms.peak, - hdspm->iobase+HDSPM_MADI_peakrmsbase, - sizeof(struct hdspm_peak_rms)) != 0 ) + } + break; + + case SNDRV_HDSPM_IOCTL_GET_LTC: + ltc.ltc = hdspm_read(hdspm, HDSPM_RD_TCO); + i = hdspm_read(hdspm, HDSPM_RD_TCO + 4); + if (i & HDSPM_TCO1_LTC_Input_valid) { + switch (i & (HDSPM_TCO1_LTC_Format_LSB | + HDSPM_TCO1_LTC_Format_MSB)) { + case 0: + ltc.format = fps_24; + break; + case HDSPM_TCO1_LTC_Format_LSB: + ltc.format = fps_25; + break; + case HDSPM_TCO1_LTC_Format_MSB: + ltc.format = fps_2997; + break; + default: + ltc.format = 30; + break; + } + if (i & HDSPM_TCO1_set_drop_frame_flag) { + ltc.frame = drop_frame; + } else { + ltc.frame = full_frame; + } + } else { + ltc.format = format_invalid; + ltc.frame = frame_invalid; + } + if (i & HDSPM_TCO1_Video_Input_Format_NTSC) { + ltc.input_format = ntsc; + } else if (i & HDSPM_TCO1_Video_Input_Format_PAL) { + ltc.input_format = pal; + } else { + ltc.input_format = no_video; + } + + s = copy_to_user(argp, <c, sizeof(struct hdspm_ltc)); + if (0 != s) { + /* + snd_printk(KERN_ERR "copy_to_user(.., .., %lu): %lu [LTC]\n", sizeof(struct hdspm_ltc), s); */ return -EFAULT; + } break; - - case SNDRV_HDSPM_IOCTL_GET_CONFIG_INFO: + case SNDRV_HDSPM_IOCTL_GET_CONFIG: - memset(&info, 0, sizeof(info)); spin_lock_irq(&hdspm->lock); info.pref_sync_ref = hdspm_pref_sync_ref(hdspm); info.wordclock_sync_check = hdspm_wc_sync_check(hdspm); info.system_sample_rate = hdspm->system_sample_rate; info.autosync_sample_rate = - hdspm_external_sample_rate(hdspm); + hdspm_external_sample_rate(hdspm); info.system_clock_mode = hdspm_system_clock_mode(hdspm); info.clock_source = hdspm_clock_source(hdspm); info.autosync_ref = hdspm_autosync_ref(hdspm); @@ -4145,10 +5900,58 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep * hw, struct file *file, return -EFAULT; break; + case SNDRV_HDSPM_IOCTL_GET_STATUS: + status.card_type = hdspm->io_type; + + status.autosync_source = hdspm_autosync_ref(hdspm); + + status.card_clock = 110069313433624ULL; + status.master_period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ); + + switch (hdspm->io_type) { + case MADI: + case MADIface: + status.card_specific.madi.sync_wc = + hdspm_wc_sync_check(hdspm); + status.card_specific.madi.sync_madi = + hdspm_madi_sync_check(hdspm); + status.card_specific.madi.sync_tco = + hdspm_tco_sync_check(hdspm); + status.card_specific.madi.sync_in = + hdspm_sync_in_sync_check(hdspm); + + statusregister = + hdspm_read(hdspm, HDSPM_statusRegister); + status.card_specific.madi.madi_input = + (statusregister & HDSPM_AB_int) ? 1 : 0; + status.card_specific.madi.channel_format = + (statusregister & HDSPM_TX_64ch) ? 1 : 0; + /* TODO: Mac driver sets it when f_s>48kHz */ + status.card_specific.madi.frame_format = 0; + + default: + break; + } + + if (copy_to_user((void __user *) arg, &status, sizeof(status))) + return -EFAULT; + + + break; + case SNDRV_HDSPM_IOCTL_GET_VERSION: + hdspm_version.card_type = hdspm->io_type; + strncpy(hdspm_version.cardname, hdspm->card_name, + sizeof(hdspm_version.cardname)); + hdspm_version.serial = (hdspm_read(hdspm, + HDSPM_midiStatusIn0)>>8) & 0xFFFFFF; hdspm_version.firmware_rev = hdspm->firmware_rev; + hdspm_version.addons = 0; + if (hdspm->tco) + hdspm_version.addons |= HDSPM_ADDON_TCO; + if (copy_to_user((void __user *) arg, &hdspm_version, - sizeof(hdspm_version))) + sizeof(hdspm_version))) return -EFAULT; break; @@ -4156,7 +5959,7 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep * hw, struct file *file, if (copy_from_user(&mixer, (void __user *)arg, sizeof(mixer))) return -EFAULT; if (copy_to_user((void __user *)mixer.mixer, hdspm->mixer, - sizeof(struct hdspm_mixer))) + sizeof(struct hdspm_mixer))) return -EFAULT; break; @@ -4175,8 +5978,6 @@ static struct snd_pcm_ops snd_hdspm_playback_ops = { .prepare = snd_hdspm_prepare, .trigger = snd_hdspm_trigger, .pointer = snd_hdspm_hw_pointer, - .copy = snd_hdspm_playback_copy, - .silence = snd_hdspm_hw_silence, .page = snd_pcm_sgbuf_ops_page, }; @@ -4189,7 +5990,6 @@ static struct snd_pcm_ops snd_hdspm_capture_ops = { .prepare = snd_hdspm_prepare, .trigger = snd_hdspm_trigger, .pointer = snd_hdspm_hw_pointer, - .copy = snd_hdspm_capture_copy, .page = snd_pcm_sgbuf_ops_page, }; @@ -4207,16 +6007,18 @@ static int __devinit snd_hdspm_create_hwdep(struct snd_card *card, hw->private_data = hdspm; strcpy(hw->name, "HDSPM hwdep interface"); + hw->ops.open = snd_hdspm_hwdep_dummy_op; hw->ops.ioctl = snd_hdspm_hwdep_ioctl; + hw->ops.release = snd_hdspm_hwdep_dummy_op; return 0; } /*------------------------------------------------------------ - memory interface + memory interface ------------------------------------------------------------*/ -static int __devinit snd_hdspm_preallocate_memory(struct hdspm * hdspm) +static int __devinit snd_hdspm_preallocate_memory(struct hdspm *hdspm) { int err; struct snd_pcm *pcm; @@ -4228,7 +6030,7 @@ static int __devinit snd_hdspm_preallocate_memory(struct hdspm * hdspm) err = snd_pcm_lib_preallocate_pages_for_all(pcm, - SNDRV_DMA_TYPE_DEV_SG, + SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(hdspm->pci), wanted, wanted); @@ -4242,19 +6044,23 @@ static int __devinit snd_hdspm_preallocate_memory(struct hdspm * hdspm) return 0; } -static void hdspm_set_sgbuf(struct hdspm * hdspm, + +static void hdspm_set_sgbuf(struct hdspm *hdspm, struct snd_pcm_substream *substream, unsigned int reg, int channels) { int i; + + /* continuous memory segment */ for (i = 0; i < (channels * 16); i++) hdspm_write(hdspm, reg + 4 * i, - snd_pcm_sgbuf_get_addr(substream, 4096 * i)); + snd_pcm_sgbuf_get_addr(substream, 4096 * i)); } + /* ------------- ALSA Devices ---------------------------- */ static int __devinit snd_hdspm_create_pcm(struct snd_card *card, - struct hdspm * hdspm) + struct hdspm *hdspm) { struct snd_pcm *pcm; int err; @@ -4290,20 +6096,21 @@ static inline void snd_hdspm_initialize_midi_flush(struct hdspm * hdspm) static int __devinit snd_hdspm_create_alsa_devices(struct snd_card *card, struct hdspm * hdspm) { - int err; + int err, i; snd_printdd("Create card...\n"); err = snd_hdspm_create_pcm(card, hdspm); if (err < 0) return err; - err = snd_hdspm_create_midi(card, hdspm, 0); - if (err < 0) - return err; - - err = snd_hdspm_create_midi(card, hdspm, 1); - if (err < 0) - return err; + i = 0; + while (i < hdspm->midiPorts) { + err = snd_hdspm_create_midi(card, hdspm, i); + if (err < 0) { + return err; + } + i++; + } err = snd_hdspm_create_controls(card, hdspm); if (err < 0) @@ -4346,37 +6153,49 @@ static int __devinit snd_hdspm_create_alsa_devices(struct snd_card *card, } static int __devinit snd_hdspm_create(struct snd_card *card, - struct hdspm *hdspm, - int precise_ptr, int enable_monitor) -{ + struct hdspm *hdspm) { + struct pci_dev *pci = hdspm->pci; int err; unsigned long io_extent; hdspm->irq = -1; - - spin_lock_init(&hdspm->midi[0].lock); - spin_lock_init(&hdspm->midi[1].lock); - hdspm->card = card; spin_lock_init(&hdspm->lock); - tasklet_init(&hdspm->midi_tasklet, - hdspm_midi_tasklet, (unsigned long) hdspm); - pci_read_config_word(hdspm->pci, - PCI_CLASS_REVISION, &hdspm->firmware_rev); - - hdspm->is_aes32 = (hdspm->firmware_rev >= HDSPM_AESREVISION); + PCI_CLASS_REVISION, &hdspm->firmware_rev); strcpy(card->mixername, "Xilinx FPGA"); - if (hdspm->is_aes32) { - strcpy(card->driver, "HDSPAES32"); - hdspm->card_name = "RME HDSPM AES32"; - } else { - strcpy(card->driver, "HDSPM"); - hdspm->card_name = "RME HDSPM MADI"; + strcpy(card->driver, "HDSPM"); + + switch (hdspm->firmware_rev) { + case HDSPM_MADI_REV: + hdspm->io_type = MADI; + hdspm->card_name = "RME MADI"; + hdspm->midiPorts = 3; + break; + case HDSPM_RAYDAT_REV: + hdspm->io_type = RayDAT; + hdspm->card_name = "RME RayDAT"; + hdspm->midiPorts = 2; + break; + case HDSPM_AIO_REV: + hdspm->io_type = AIO; + hdspm->card_name = "RME AIO"; + hdspm->midiPorts = 1; + break; + case HDSPM_MADIFACE_REV: + hdspm->io_type = MADIface; + hdspm->card_name = "RME MADIface"; + hdspm->midiPorts = 1; + break; + case HDSPM_AES_REV: + hdspm->io_type = AES32; + hdspm->card_name = "RME AES32"; + hdspm->midiPorts = 2; + break; } err = pci_enable_device(pci); @@ -4393,22 +6212,21 @@ static int __devinit snd_hdspm_create(struct snd_card *card, io_extent = pci_resource_len(pci, 0); snd_printdd("grabbed memory region 0x%lx-0x%lx\n", - hdspm->port, hdspm->port + io_extent - 1); - + hdspm->port, hdspm->port + io_extent - 1); hdspm->iobase = ioremap_nocache(hdspm->port, io_extent); if (!hdspm->iobase) { snd_printk(KERN_ERR "HDSPM: " - "unable to remap region 0x%lx-0x%lx\n", - hdspm->port, hdspm->port + io_extent - 1); + "unable to remap region 0x%lx-0x%lx\n", + hdspm->port, hdspm->port + io_extent - 1); return -EBUSY; } snd_printdd("remapped region (0x%lx) 0x%lx-0x%lx\n", - (unsigned long)hdspm->iobase, hdspm->port, - hdspm->port + io_extent - 1); + (unsigned long)hdspm->iobase, hdspm->port, + hdspm->port + io_extent - 1); if (request_irq(pci->irq, snd_hdspm_interrupt, - IRQF_SHARED, "hdspm", hdspm)) { + IRQF_SHARED, "hdspm", hdspm)) { snd_printk(KERN_ERR "HDSPM: unable to use IRQ %d\n", pci->irq); return -EBUSY; } @@ -4416,23 +6234,195 @@ static int __devinit snd_hdspm_create(struct snd_card *card, snd_printdd("use IRQ %d\n", pci->irq); hdspm->irq = pci->irq; - hdspm->precise_ptr = precise_ptr; - - hdspm->monitor_outs = enable_monitor; snd_printdd("kmalloc Mixer memory of %zd Bytes\n", - sizeof(struct hdspm_mixer)); + sizeof(struct hdspm_mixer)); hdspm->mixer = kzalloc(sizeof(struct hdspm_mixer), GFP_KERNEL); if (!hdspm->mixer) { snd_printk(KERN_ERR "HDSPM: " - "unable to kmalloc Mixer memory of %d Bytes\n", - (int)sizeof(struct hdspm_mixer)); + "unable to kmalloc Mixer memory of %d Bytes\n", + (int)sizeof(struct hdspm_mixer)); return err; } - hdspm->ss_channels = MADI_SS_CHANNELS; - hdspm->ds_channels = MADI_DS_CHANNELS; - hdspm->qs_channels = MADI_QS_CHANNELS; + hdspm->port_names_in = NULL; + hdspm->port_names_out = NULL; + + switch (hdspm->io_type) { + case AES32: + break; + + case MADI: + case MADIface: + hdspm->ss_in_channels = hdspm->ss_out_channels = + MADI_SS_CHANNELS; + hdspm->ds_in_channels = hdspm->ds_out_channels = + MADI_DS_CHANNELS; + hdspm->qs_in_channels = hdspm->qs_out_channels = + MADI_QS_CHANNELS; + + hdspm->channel_map_in_ss = hdspm->channel_map_out_ss = + channel_map_unity_ss; + hdspm->channel_map_in_ds = hdspm->channel_map_out_ss = + channel_map_unity_ss; + hdspm->channel_map_in_qs = hdspm->channel_map_out_ss = + channel_map_unity_ss; + + hdspm->port_names_in_ss = hdspm->port_names_out_ss = + texts_ports_madi; + hdspm->port_names_in_ds = hdspm->port_names_out_ds = + texts_ports_madi; + hdspm->port_names_in_qs = hdspm->port_names_out_qs = + texts_ports_madi; + break; + + case AIO: + if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBI_D)) { + snd_printk(KERN_INFO "HDSPM: AEB input board found, but not supported\n"); + } + + hdspm->ss_in_channels = AIO_IN_SS_CHANNELS; + hdspm->ds_in_channels = AIO_IN_DS_CHANNELS; + hdspm->qs_in_channels = AIO_IN_QS_CHANNELS; + hdspm->ss_out_channels = AIO_OUT_SS_CHANNELS; + hdspm->ds_out_channels = AIO_OUT_DS_CHANNELS; + hdspm->qs_out_channels = AIO_OUT_QS_CHANNELS; + + hdspm->channel_map_out_ss = channel_map_aio_out_ss; + hdspm->channel_map_out_ds = channel_map_aio_out_ds; + hdspm->channel_map_out_qs = channel_map_aio_out_qs; + + hdspm->channel_map_in_ss = channel_map_aio_in_ss; + hdspm->channel_map_in_ds = channel_map_aio_in_ds; + hdspm->channel_map_in_qs = channel_map_aio_in_qs; + + hdspm->port_names_in_ss = texts_ports_aio_in_ss; + hdspm->port_names_out_ss = texts_ports_aio_out_ss; + hdspm->port_names_in_ds = texts_ports_aio_in_ds; + hdspm->port_names_out_ds = texts_ports_aio_out_ds; + hdspm->port_names_in_qs = texts_ports_aio_in_qs; + hdspm->port_names_out_qs = texts_ports_aio_out_qs; + + break; + + case RayDAT: + hdspm->ss_in_channels = hdspm->ss_out_channels = + RAYDAT_SS_CHANNELS; + hdspm->ds_in_channels = hdspm->ds_out_channels = + RAYDAT_DS_CHANNELS; + hdspm->qs_in_channels = hdspm->qs_out_channels = + RAYDAT_QS_CHANNELS; + + hdspm->max_channels_in = RAYDAT_SS_CHANNELS; + hdspm->max_channels_out = RAYDAT_SS_CHANNELS; + + hdspm->channel_map_in_ss = hdspm->channel_map_out_ss = + channel_map_raydat_ss; + hdspm->channel_map_in_ds = hdspm->channel_map_out_ds = + channel_map_raydat_ds; + hdspm->channel_map_in_qs = hdspm->channel_map_out_qs = + channel_map_raydat_qs; + hdspm->channel_map_in = hdspm->channel_map_out = + channel_map_raydat_ss; + + hdspm->port_names_in_ss = hdspm->port_names_out_ss = + texts_ports_raydat_ss; + hdspm->port_names_in_ds = hdspm->port_names_out_ds = + texts_ports_raydat_ds; + hdspm->port_names_in_qs = hdspm->port_names_out_qs = + texts_ports_raydat_qs; + + + break; + + } + + /* TCO detection */ + switch (hdspm->io_type) { + case AIO: + case RayDAT: + if (hdspm_read(hdspm, HDSPM_statusRegister2) & + HDSPM_s2_tco_detect) { + hdspm->midiPorts++; + hdspm->tco = kzalloc(sizeof(struct hdspm_tco), + GFP_KERNEL); + if (NULL != hdspm->tco) { + hdspm_tco_write(hdspm); + } + snd_printk(KERN_INFO "HDSPM: AIO/RayDAT TCO module found\n"); + } else { + hdspm->tco = NULL; + } + break; + + case MADI: + if (hdspm_read(hdspm, HDSPM_statusRegister) & HDSPM_tco_detect) { + hdspm->midiPorts++; + hdspm->tco = kzalloc(sizeof(struct hdspm_tco), + GFP_KERNEL); + if (NULL != hdspm->tco) { + hdspm_tco_write(hdspm); + } + snd_printk(KERN_INFO "HDSPM: MADI TCO module found\n"); + } else { + hdspm->tco = NULL; + } + break; + + default: + hdspm->tco = NULL; + } + + /* texts */ + switch (hdspm->io_type) { + case AES32: + if (hdspm->tco) { + hdspm->texts_autosync = texts_autosync_aes_tco; + hdspm->texts_autosync_items = 10; + } else { + hdspm->texts_autosync = texts_autosync_aes; + hdspm->texts_autosync_items = 9; + } + break; + + case MADI: + if (hdspm->tco) { + hdspm->texts_autosync = texts_autosync_madi_tco; + hdspm->texts_autosync_items = 4; + } else { + hdspm->texts_autosync = texts_autosync_madi; + hdspm->texts_autosync_items = 3; + } + break; + + case MADIface: + + break; + + case RayDAT: + if (hdspm->tco) { + hdspm->texts_autosync = texts_autosync_raydat_tco; + hdspm->texts_autosync_items = 9; + } else { + hdspm->texts_autosync = texts_autosync_raydat; + hdspm->texts_autosync_items = 8; + } + break; + + case AIO: + if (hdspm->tco) { + hdspm->texts_autosync = texts_autosync_aio_tco; + hdspm->texts_autosync_items = 6; + } else { + hdspm->texts_autosync = texts_autosync_aio; + hdspm->texts_autosync_items = 5; + } + break; + + } + + tasklet_init(&hdspm->midi_tasklet, + hdspm_midi_tasklet, (unsigned long) hdspm); snd_printdd("create alsa devices.\n"); err = snd_hdspm_create_alsa_devices(card, hdspm); @@ -4444,6 +6434,7 @@ static int __devinit snd_hdspm_create(struct snd_card *card, return 0; } + static int snd_hdspm_free(struct hdspm * hdspm) { @@ -4452,7 +6443,8 @@ static int snd_hdspm_free(struct hdspm * hdspm) /* stop th audio, and cancel all interrupts */ hdspm->control_register &= ~(HDSPM_Start | HDSPM_AudioInterruptEnable | - HDSPM_Midi0InterruptEnable | HDSPM_Midi1InterruptEnable); + HDSPM_Midi0InterruptEnable | HDSPM_Midi1InterruptEnable | + HDSPM_Midi2InterruptEnable | HDSPM_Midi3InterruptEnable); hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); } @@ -4472,6 +6464,7 @@ static int snd_hdspm_free(struct hdspm * hdspm) return 0; } + static void snd_hdspm_card_free(struct snd_card *card) { struct hdspm *hdspm = card->private_data; @@ -4480,6 +6473,7 @@ static void snd_hdspm_card_free(struct snd_card *card) snd_hdspm_free(hdspm); } + static int __devinit snd_hdspm_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { @@ -4496,7 +6490,7 @@ static int __devinit snd_hdspm_probe(struct pci_dev *pci, } err = snd_card_create(index[dev], id[dev], - THIS_MODULE, sizeof(struct hdspm), &card); + THIS_MODULE, sizeof(struct hdspm), &card); if (err < 0) return err; @@ -4507,16 +6501,25 @@ static int __devinit snd_hdspm_probe(struct pci_dev *pci, snd_card_set_dev(card, &pci->dev); - err = snd_hdspm_create(card, hdspm, precise_ptr[dev], - enable_monitor[dev]); + err = snd_hdspm_create(card, hdspm); if (err < 0) { snd_card_free(card); return err; } - strcpy(card->shortname, "HDSPM MADI"); - sprintf(card->longname, "%s at 0x%lx, irq %d", hdspm->card_name, - hdspm->port, hdspm->irq); + if (hdspm->io_type != MADIface) { + sprintf(card->shortname, "%s_%x", + hdspm->card_name, + (hdspm_read(hdspm, HDSPM_midiStatusIn0)>>8) & 0xFFFFFF); + sprintf(card->longname, "%s S/N 0x%x at 0x%lx, irq %d", + hdspm->card_name, + (hdspm_read(hdspm, HDSPM_midiStatusIn0)>>8) & 0xFFFFFF, + hdspm->port, hdspm->irq); + } else { + sprintf(card->shortname, "%s", hdspm->card_name); + sprintf(card->longname, "%s at 0x%lx, irq %d", + hdspm->card_name, hdspm->port, hdspm->irq); + } err = snd_card_register(card); if (err < 0) { -- cgit v1.2.3-59-g8ed1b From 55a57606b26665870f2993dc53a43daad157dbcd Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Thu, 27 Jan 2011 11:23:15 +0100 Subject: ALSA: [hdspm] Move static mapping arrays to .c As requested by Takashi and Jaroslav, these arrays should not be in the header file. Signed-off-by: Adrian Knoth Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- include/sound/hdspm.h | 170 --------------------------------------------- sound/pci/rme9652/hdspm.c | 171 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+), 170 deletions(-) (limited to 'include') diff --git a/include/sound/hdspm.h b/include/sound/hdspm.h index c3f18194b08e..1774ff5ff632 100644 --- a/include/sound/hdspm.h +++ b/include/sound/hdspm.h @@ -225,175 +225,5 @@ typedef struct hdspm_version hdspm_version_t; typedef struct hdspm_channelfader snd_hdspm_channelfader_t; typedef struct hdspm_mixer hdspm_mixer_t; -/* These tables map the ALSA channels 1..N to the channels that we - need to use in order to find the relevant channel buffer. RME - refers to this kind of mapping as between "the ADAT channel and - the DMA channel." We index it using the logical audio channel, - and the value is the DMA channel (i.e. channel buffer number) - where the data for that channel can be read/written from/to. -*/ - -char channel_map_unity_ss[HDSPM_MAX_CHANNELS] = { - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63 -}; - -char channel_map_unity_ds[HDSPM_MAX_CHANNELS] = { - 0, 2, 4, 6, 8, 10, 12, 14, - 16, 18, 20, 22, 24, 26, 28, 30, - 32, 34, 36, 38, 40, 42, 44, 46, - 48, 50, 52, 54, 56, 58, 60, 62, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -}; - -char channel_map_unity_qs[HDSPM_MAX_CHANNELS] = { - 0, 4, 8, 12, 16, 20, 24, 28, - 32, 36, 40, 44, 48, 52, 56, 60, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -}; - -char channel_map_raydat_ss[HDSPM_MAX_CHANNELS] = { - 4, 5, 6, 7, 8, 9, 10, 11, /* ADAT 1 */ - 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT 2 */ - 20, 21, 22, 23, 24, 25, 26, 27, /* ADAT 3 */ - 28, 29, 30, 31, 32, 33, 34, 35, /* ADAT 4 */ - 0, 1, /* AES */ - 2, 3, /* SPDIF */ - -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -}; - -char channel_map_raydat_ds[HDSPM_MAX_CHANNELS] = { - 4, 5, 6, 7, /* ADAT 1 */ - 8, 9, 10, 11, /* ADAT 2 */ - 12, 13, 14, 15, /* ADAT 3 */ - 16, 17, 18, 19, /* ADAT 4 */ - 0, 1, /* AES */ - 2, 3, /* SPDIF */ - -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -}; - -char channel_map_raydat_qs[HDSPM_MAX_CHANNELS] = { - 4, 5, /* ADAT 1 */ - 6, 7, /* ADAT 2 */ - 8, 9, /* ADAT 3 */ - 10, 11, /* ADAT 4 */ - 0, 1, /* AES */ - 2, 3, /* SPDIF */ - -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -}; - -char channel_map_aio_in_ss[HDSPM_MAX_CHANNELS] = { - 0, 1, /* line in */ - 8, 9, /* aes in, */ - 10, 11, /* spdif in */ - 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT in */ - -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -}; - -char channel_map_aio_out_ss[HDSPM_MAX_CHANNELS] = { - 0, 1, /* line out */ - 8, 9, /* aes out */ - 10, 11, /* spdif out */ - 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT out */ - 6, 7, /* phone out */ - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -}; - -char channel_map_aio_in_ds[HDSPM_MAX_CHANNELS] = { - 0, 1, /* line in */ - 8, 9, /* aes in */ - 10, 11, /* spdif in */ - 12, 14, 16, 18, /* adat in */ - -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1 -}; - -char channel_map_aio_out_ds[HDSPM_MAX_CHANNELS] = { - 0, 1, /* line out */ - 8, 9, /* aes out */ - 10, 11, /* spdif out */ - 12, 14, 16, 18, /* adat out */ - 6, 7, /* phone out */ - -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1 -}; - -char channel_map_aio_in_qs[HDSPM_MAX_CHANNELS] = { - 0, 1, /* line in */ - 8, 9, /* aes in */ - 10, 11, /* spdif in */ - 12, 16, /* adat in */ - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1 -}; - -char channel_map_aio_out_qs[HDSPM_MAX_CHANNELS] = { - 0, 1, /* line out */ - 8, 9, /* aes out */ - 10, 11, /* spdif out */ - 12, 16, /* adat out */ - 6, 7, /* phone out */ - -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1 -}; #endif diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 2db871d9a007..28a1eb3f4d02 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -673,6 +673,177 @@ static char *texts_ports_aio_out_qs[] = { "Phone.L", "Phone.R" }; +/* These tables map the ALSA channels 1..N to the channels that we + need to use in order to find the relevant channel buffer. RME + refers to this kind of mapping as between "the ADAT channel and + the DMA channel." We index it using the logical audio channel, + and the value is the DMA channel (i.e. channel buffer number) + where the data for that channel can be read/written from/to. +*/ + +static char channel_map_unity_ss[HDSPM_MAX_CHANNELS] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63 +}; + +static char channel_map_unity_ds[HDSPM_MAX_CHANNELS] = { + 0, 2, 4, 6, 8, 10, 12, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, 50, 52, 54, 56, 58, 60, 62, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static char channel_map_unity_qs[HDSPM_MAX_CHANNELS] = { + 0, 4, 8, 12, 16, 20, 24, 28, + 32, 36, 40, 44, 48, 52, 56, 60, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static char channel_map_raydat_ss[HDSPM_MAX_CHANNELS] = { + 4, 5, 6, 7, 8, 9, 10, 11, /* ADAT 1 */ + 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT 2 */ + 20, 21, 22, 23, 24, 25, 26, 27, /* ADAT 3 */ + 28, 29, 30, 31, 32, 33, 34, 35, /* ADAT 4 */ + 0, 1, /* AES */ + 2, 3, /* SPDIF */ + -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static char channel_map_raydat_ds[HDSPM_MAX_CHANNELS] = { + 4, 5, 6, 7, /* ADAT 1 */ + 8, 9, 10, 11, /* ADAT 2 */ + 12, 13, 14, 15, /* ADAT 3 */ + 16, 17, 18, 19, /* ADAT 4 */ + 0, 1, /* AES */ + 2, 3, /* SPDIF */ + -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static char channel_map_raydat_qs[HDSPM_MAX_CHANNELS] = { + 4, 5, /* ADAT 1 */ + 6, 7, /* ADAT 2 */ + 8, 9, /* ADAT 3 */ + 10, 11, /* ADAT 4 */ + 0, 1, /* AES */ + 2, 3, /* SPDIF */ + -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static char channel_map_aio_in_ss[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line in */ + 8, 9, /* aes in, */ + 10, 11, /* spdif in */ + 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT in */ + -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static char channel_map_aio_out_ss[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line out */ + 8, 9, /* aes out */ + 10, 11, /* spdif out */ + 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT out */ + 6, 7, /* phone out */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static char channel_map_aio_in_ds[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line in */ + 8, 9, /* aes in */ + 10, 11, /* spdif in */ + 12, 14, 16, 18, /* adat in */ + -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +static char channel_map_aio_out_ds[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line out */ + 8, 9, /* aes out */ + 10, 11, /* spdif out */ + 12, 14, 16, 18, /* adat out */ + 6, 7, /* phone out */ + -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +static char channel_map_aio_in_qs[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line in */ + 8, 9, /* aes in */ + 10, 11, /* spdif in */ + 12, 16, /* adat in */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +static char channel_map_aio_out_qs[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line out */ + 8, 9, /* aes out */ + 10, 11, /* spdif out */ + 12, 16, /* adat out */ + 6, 7, /* phone out */ + -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + struct hdspm_midi { struct hdspm *hdspm; int id; -- cgit v1.2.3-59-g8ed1b From 70b2ac126a60c87145ae8a8eb1b4dccaa0bf5468 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 26 Jan 2011 14:05:25 +0000 Subject: ASoC: Use card rather than soc-audio device to card PM functions The platform device for the card is tied closely to the soc-audio implementation which we're currently trying to remove in favour of allowing cards to have their own devices. Begin removing it by replacing it with the card in the suspend and resume callbacks we give to cards, also taking the opportunity to remove the legacy suspend types which are currently hard coded anyway. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 8 ++++---- sound/soc/pxa/raumfeld.c | 4 ++-- sound/soc/pxa/zylonite.c | 5 ++--- sound/soc/soc-core.c | 9 ++++----- 4 files changed, 12 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 1355ef029d82..4a489ae44a6e 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -654,10 +654,10 @@ struct snd_soc_card { /* the pre and post PM functions are used to do any PM work before and * after the codec and DAI's do any PM work. */ - int (*suspend_pre)(struct platform_device *pdev, pm_message_t state); - int (*suspend_post)(struct platform_device *pdev, pm_message_t state); - int (*resume_pre)(struct platform_device *pdev); - int (*resume_post)(struct platform_device *pdev); + int (*suspend_pre)(struct snd_soc_card *card); + int (*suspend_post)(struct snd_soc_card *card); + int (*resume_pre)(struct snd_soc_card *card); + int (*resume_post)(struct snd_soc_card *card); /* callbacks */ int (*set_bias_level)(struct snd_soc_card *, diff --git a/sound/soc/pxa/raumfeld.c b/sound/soc/pxa/raumfeld.c index 0fd60f423036..db1dd560a585 100644 --- a/sound/soc/pxa/raumfeld.c +++ b/sound/soc/pxa/raumfeld.c @@ -151,13 +151,13 @@ static struct snd_soc_ops raumfeld_cs4270_ops = { .hw_params = raumfeld_cs4270_hw_params, }; -static int raumfeld_line_suspend(struct platform_device *pdev, pm_message_t state) +static int raumfeld_line_suspend(struct snd_soc_card *card) { raumfeld_enable_audio(false); return 0; } -static int raumfeld_line_resume(struct platform_device *pdev) +static int raumfeld_line_resume(struct snd_soc_card *card) { raumfeld_enable_audio(true); return 0; diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c index b222a7d72027..7b729013a728 100644 --- a/sound/soc/pxa/zylonite.c +++ b/sound/soc/pxa/zylonite.c @@ -226,8 +226,7 @@ static int zylonite_remove(struct platform_device *pdev) return 0; } -static int zylonite_suspend_post(struct platform_device *pdev, - pm_message_t state) +static int zylonite_suspend_post(struct snd_soc_card *card) { if (clk_pout) clk_disable(pout); @@ -235,7 +234,7 @@ static int zylonite_suspend_post(struct platform_device *pdev, return 0; } -static int zylonite_resume_pre(struct platform_device *pdev) +static int zylonite_resume_pre(struct snd_soc_card *card) { int ret = 0; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 14861f95f629..446838e7d3ec 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1011,7 +1011,7 @@ static int soc_suspend(struct device *dev) } if (card->suspend_pre) - card->suspend_pre(pdev, PMSG_SUSPEND); + card->suspend_pre(card); for (i = 0; i < card->num_rtd; i++) { struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; @@ -1078,7 +1078,7 @@ static int soc_suspend(struct device *dev) } if (card->suspend_post) - card->suspend_post(pdev, PMSG_SUSPEND); + card->suspend_post(card); return 0; } @@ -1090,7 +1090,6 @@ static void soc_resume_deferred(struct work_struct *work) { struct snd_soc_card *card = container_of(work, struct snd_soc_card, deferred_resume_work); - struct platform_device *pdev = to_platform_device(card->dev); struct snd_soc_codec *codec; int i; @@ -1104,7 +1103,7 @@ static void soc_resume_deferred(struct work_struct *work) snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D2); if (card->resume_pre) - card->resume_pre(pdev); + card->resume_pre(card); /* resume AC97 DAIs */ for (i = 0; i < card->num_rtd; i++) { @@ -1179,7 +1178,7 @@ static void soc_resume_deferred(struct work_struct *work) } if (card->resume_post) - card->resume_post(pdev); + card->resume_post(card); dev_dbg(card->dev, "resume work completed\n"); -- cgit v1.2.3-59-g8ed1b From e7361ec4996c170c63c4ac379085896db85ff34d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 26 Jan 2011 14:17:20 +0000 Subject: ASoC: Replace pdev with card in machine driver probe and remove In order to support cards instantiated without using soc-audio remove the use of the platform device in the card probe() and remove() ops. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 4 ++-- sound/soc/fsl/mpc8610_hpcd.c | 6 ++---- sound/soc/fsl/p1022_ds.c | 6 ++---- sound/soc/pxa/tosa.c | 4 ++-- sound/soc/pxa/zylonite.c | 4 ++-- sound/soc/soc-core.c | 8 +++----- 6 files changed, 13 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 4a489ae44a6e..2d10090a08c0 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -649,8 +649,8 @@ struct snd_soc_card { bool instantiated; - int (*probe)(struct platform_device *pdev); - int (*remove)(struct platform_device *pdev); + int (*probe)(struct snd_soc_card *card); + int (*remove)(struct snd_soc_card *card); /* the pre and post PM functions are used to do any PM work before and * after the codec and DAI's do any PM work. */ diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c index 7d7847a1e66b..c16c6b2eff95 100644 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ b/sound/soc/fsl/mpc8610_hpcd.c @@ -53,9 +53,8 @@ struct mpc8610_hpcd_data { * * Here we program the DMACR and PMUXCR registers. */ -static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device) +static int mpc8610_hpcd_machine_probe(struct snd_soc_card *card) { - struct snd_soc_card *card = platform_get_drvdata(sound_device); struct mpc8610_hpcd_data *machine_data = container_of(card, struct mpc8610_hpcd_data, card); struct ccsr_guts_86xx __iomem *guts; @@ -138,9 +137,8 @@ static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) * This function is called to remove the sound device for one SSI. We * de-program the DMACR and PMUXCR register. */ -static int mpc8610_hpcd_machine_remove(struct platform_device *sound_device) +static int mpc8610_hpcd_machine_remove(struct snd_soc_card *card) { - struct snd_soc_card *card = platform_get_drvdata(sound_device); struct mpc8610_hpcd_data *machine_data = container_of(card, struct mpc8610_hpcd_data, card); struct ccsr_guts_86xx __iomem *guts; diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c index 026b756961e0..66e0b68af147 100644 --- a/sound/soc/fsl/p1022_ds.c +++ b/sound/soc/fsl/p1022_ds.c @@ -85,9 +85,8 @@ struct machine_data { * * Here we program the DMACR and PMUXCR registers. */ -static int p1022_ds_machine_probe(struct platform_device *sound_device) +static int p1022_ds_machine_probe(struct snd_soc_card *card) { - struct snd_soc_card *card = platform_get_drvdata(sound_device); struct machine_data *mdata = container_of(card, struct machine_data, card); struct ccsr_guts_85xx __iomem *guts; @@ -160,9 +159,8 @@ static int p1022_ds_startup(struct snd_pcm_substream *substream) * This function is called to remove the sound device for one SSI. We * de-program the DMACR and PMUXCR register. */ -static int p1022_ds_machine_remove(struct platform_device *sound_device) +static int p1022_ds_machine_remove(struct snd_soc_card *card) { - struct snd_soc_card *card = platform_get_drvdata(sound_device); struct machine_data *mdata = container_of(card, struct machine_data, card); struct ccsr_guts_85xx __iomem *guts; diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index f75804ef0897..489139a31cf9 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c @@ -237,7 +237,7 @@ static struct snd_soc_dai_link tosa_dai[] = { }, }; -static int tosa_probe(struct platform_device *dev) +static int tosa_probe(struct snd_soc_card *card) { int ret; @@ -251,7 +251,7 @@ static int tosa_probe(struct platform_device *dev) return ret; } -static int tosa_remove(struct platform_device *dev) +static int tosa_remove(struct snd_soc_card *card) { gpio_free(TOSA_GPIO_L_MUTE); return 0; diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c index 7b729013a728..c5858296b48a 100644 --- a/sound/soc/pxa/zylonite.c +++ b/sound/soc/pxa/zylonite.c @@ -189,7 +189,7 @@ static struct snd_soc_dai_link zylonite_dai[] = { }, }; -static int zylonite_probe(struct platform_device *pdev) +static int zylonite_probe(struct snd_soc_card *card) { int ret; @@ -216,7 +216,7 @@ static int zylonite_probe(struct platform_device *pdev) return 0; } -static int zylonite_remove(struct platform_device *pdev) +static int zylonite_remove(struct snd_soc_card *card) { if (clk_pout) { clk_disable(pout); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 446838e7d3ec..4bc2365bf1dd 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1717,7 +1717,6 @@ static int snd_soc_init_codec_cache(struct snd_soc_codec *codec, static void snd_soc_instantiate_card(struct snd_soc_card *card) { - struct platform_device *pdev = to_platform_device(card->dev); struct snd_soc_codec *codec; struct snd_soc_codec_conf *codec_conf; enum snd_soc_compress_type compress_type; @@ -1781,7 +1780,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) /* initialise the sound card only once */ if (card->probe) { - ret = card->probe(pdev); + ret = card->probe(card); if (ret < 0) goto card_probe_error; } @@ -1842,7 +1841,7 @@ probe_dai_err: card_probe_error: if (card->remove) - card->remove(pdev); + card->remove(card); snd_card_free(card->snd_card); @@ -1888,7 +1887,6 @@ static int soc_probe(struct platform_device *pdev) static int soc_cleanup_card_resources(struct snd_soc_card *card) { - struct platform_device *pdev = to_platform_device(card->dev); int i; /* make sure any delayed work runs */ @@ -1909,7 +1907,7 @@ static int soc_cleanup_card_resources(struct snd_soc_card *card) /* remove the card */ if (card->remove) - card->remove(pdev); + card->remove(card); kfree(card->rtd); snd_card_free(card->snd_card); -- cgit v1.2.3-59-g8ed1b From 6f8ab4ac292f81b9246ddf363bf1c6a2fc7a0629 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 26 Jan 2011 14:59:27 +0000 Subject: ASoC: Export card PM callbacks for use in direct registered cards Allow hookup of cards registered directly with the core to the PM operations by exporting the device power management operations to modules, also exporting the default PM operations since it is expected that most cards will end up using exactly the same setup. Note that the callbacks require that the driver data for the card be the snd_soc_card. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 5 +++++ sound/soc/soc-core.c | 34 +++++++++++++++++----------------- 2 files changed, 22 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 2d10090a08c0..7e8cf4f318a9 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -260,6 +260,9 @@ enum snd_soc_compress_type { int snd_soc_register_card(struct snd_soc_card *card); int snd_soc_unregister_card(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); int snd_soc_register_platform(struct device *dev, struct snd_soc_platform_driver *platform_drv); void snd_soc_unregister_platform(struct device *dev); @@ -802,4 +805,6 @@ static inline void snd_soc_initialize_card_lists(struct snd_soc_card *card) extern struct dentry *snd_soc_debugfs_root; #endif +extern const struct dev_pm_ops snd_soc_pm_ops; + #endif diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4bc2365bf1dd..5dffc7a469c0 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -965,12 +965,11 @@ static struct snd_pcm_ops soc_pcm_ops = { .pointer = soc_pcm_pointer, }; -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP /* powers down audio subsystem for suspend */ -static int soc_suspend(struct device *dev) +int snd_soc_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct snd_soc_card *card = platform_get_drvdata(pdev); + struct snd_soc_card *card = dev_get_drvdata(dev); struct snd_soc_codec *codec; int i; @@ -1082,6 +1081,7 @@ static int soc_suspend(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(snd_soc_suspend); /* deferred resume work, so resume can complete before we finished * setting our codec back up, which can be very slow on I2C @@ -1187,10 +1187,9 @@ static void soc_resume_deferred(struct work_struct *work) } /* powers up audio subsystem after a suspend */ -static int soc_resume(struct device *dev) +int snd_soc_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct snd_soc_card *card = platform_get_drvdata(pdev); + struct snd_soc_card *card = dev_get_drvdata(dev); int i; /* AC97 devices might have other drivers hanging off them so @@ -1212,9 +1211,10 @@ static int soc_resume(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(snd_soc_resume); #else -#define soc_suspend NULL -#define soc_resume NULL +#define snd_soc_suspend NULL +#define snd_soc_resume NULL #endif static struct snd_soc_dai_ops null_dai_ops = { @@ -1924,10 +1924,9 @@ static int soc_remove(struct platform_device *pdev) return 0; } -static int soc_poweroff(struct device *dev) +int snd_soc_poweroff(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct snd_soc_card *card = platform_get_drvdata(pdev); + struct snd_soc_card *card = dev_get_drvdata(dev); int i; if (!card->instantiated) @@ -1944,11 +1943,12 @@ static int soc_poweroff(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(snd_soc_poweroff); -static const struct dev_pm_ops soc_pm_ops = { - .suspend = soc_suspend, - .resume = soc_resume, - .poweroff = soc_poweroff, +const struct dev_pm_ops snd_soc_pm_ops = { + .suspend = snd_soc_suspend, + .resume = snd_soc_resume, + .poweroff = snd_soc_poweroff, }; /* ASoC platform driver */ @@ -1956,7 +1956,7 @@ static struct platform_driver soc_driver = { .driver = { .name = "soc-audio", .owner = THIS_MODULE, - .pm = &soc_pm_ops, + .pm = &snd_soc_pm_ops, }, .probe = soc_probe, .remove = soc_remove, -- cgit v1.2.3-59-g8ed1b From aaee8ef146111566e1c607bdf368d73fb966be2e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 26 Jan 2011 20:53:50 +0000 Subject: ASoC: Make cache status available via debugfs Could just as well live in sysfs but sysfs doesn't have the simple value export helpers debugfs does. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 4 ++-- sound/soc/soc-core.c | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 7e8cf4f318a9..64856d656f15 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -493,14 +493,14 @@ struct snd_soc_codec { struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */ unsigned int active; unsigned int cache_bypass:1; /* Suppress access to the cache */ - unsigned int cache_only:1; /* Suppress writes to hardware */ - unsigned int cache_sync:1; /* Cache needs to be synced to hardware */ unsigned int suspended:1; /* Codec is in suspend PM state */ unsigned int probed:1; /* Codec has been probed */ unsigned int ac97_registered:1; /* Codec has been AC97 registered */ unsigned int ac97_created:1; /* Codec has been created by SoC */ unsigned int sysfs_registered:1; /* codec has been sysfs registered */ unsigned int cache_init:1; /* codec cache has been initialized */ + u32 cache_only; /* Suppress writes to hardware */ + u32 cache_sync; /* Cache needs to be synced to hardware */ /* codec IO */ void *control_data; /* codec control (i2c/3wire) data */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 5dffc7a469c0..8af47f7a8fcb 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -235,6 +235,11 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec) return; } + debugfs_create_bool("cache_sync", 0444, codec->debugfs_codec_root, + &codec->cache_sync); + debugfs_create_bool("cache_only", 0444, codec->debugfs_codec_root, + &codec->cache_only); + codec->debugfs_reg = debugfs_create_file("codec_reg", 0644, codec->debugfs_codec_root, codec, &codec_reg_fops); -- cgit v1.2.3-59-g8ed1b From f85a9e0d260905f98d4ca6b66f0e64f63a729dba Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 26 Jan 2011 21:41:28 +0000 Subject: ASoC: Add subsequence information to seq_notify callbacks Allows drivers to distinguish which subsequence is being notified when they get called back. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc-dapm.h | 2 +- include/sound/soc.h | 2 +- sound/soc/soc-dapm.c | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 6a25e6993859..979ed84e07d6 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -501,7 +501,7 @@ struct snd_soc_dapm_context { struct snd_soc_dapm_update *update; void (*seq_notifier)(struct snd_soc_dapm_context *, - enum snd_soc_dapm_type); + enum snd_soc_dapm_type, int); struct device *dev; /* from parent - for debug */ struct snd_soc_codec *codec; /* parent codec */ diff --git a/include/sound/soc.h b/include/sound/soc.h index 64856d656f15..7ecdaefd1b63 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -553,7 +553,7 @@ struct snd_soc_codec_driver { enum snd_soc_bias_level level); void (*seq_notifier)(struct snd_soc_dapm_context *, - enum snd_soc_dapm_type); + enum snd_soc_dapm_type, int); }; /* SoC platform interface */ diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 37b376f4c75d..0f94fd057f29 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -899,7 +899,8 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) if (sort[i] == cur_sort) cur_dapm->seq_notifier(cur_dapm, - i); + i, + cur_subseq); } INIT_LIST_HEAD(&pending); @@ -968,7 +969,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) if (sort[i] == cur_sort) cur_dapm->seq_notifier(cur_dapm, - i); + i, cur_subseq); } } -- cgit v1.2.3-59-g8ed1b From ea18e137baf3e3e9212bfd7b071555fc712159b5 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 26 Jan 2011 11:04:08 +0100 Subject: ALSA: Release v1.0.24 Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- include/sound/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/version.h b/include/sound/version.h index bf69a5b7e65f..8fc5321e1ecc 100644 --- a/include/sound/version.h +++ b/include/sound/version.h @@ -1,3 +1,3 @@ /* include/version.h */ -#define CONFIG_SND_VERSION "1.0.23" +#define CONFIG_SND_VERSION "1.0.24" #define CONFIG_SND_DATE "" -- cgit v1.2.3-59-g8ed1b From dddf3e4c257879bc35cda3f542507c43f2648a2a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 28 Jan 2011 13:11:47 +0000 Subject: ASoC: Add card driver data Provide driver data for cards within the card structure. To simplify the implementation of the PM operations we don't use the struct device driver data as this is used by the core to retrieve the card in callbacks from the device model and PM core. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 7ecdaefd1b63..4b6c0a8c332f 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -705,6 +705,8 @@ struct snd_soc_card { struct dentry *debugfs_pop_time; #endif u32 pop_time; + + void *drvdata; }; /* SoC machine DAI configuration, glues a codec and cpu DAI together */ @@ -756,6 +758,17 @@ unsigned int snd_soc_write(struct snd_soc_codec *codec, /* device driver data */ +static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card, + void *data) +{ + card->drvdata = data; +} + +static inline void *snd_soc_card_get_drvdata(struct snd_soc_card *card) +{ + return card->drvdata; +} + static inline void snd_soc_codec_set_drvdata(struct snd_soc_codec *codec, void *data) { -- cgit v1.2.3-59-g8ed1b From a98a0bc6c92eacd181417a9c0ccd2e8028066622 Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Thu, 3 Feb 2011 03:11:45 +0300 Subject: ASoC: CS4271: Move Chip Select control out of the CODEC code. Move Chip Select control out of the CODEC code for CS4271. Signed-off-by: Alexander Sverdlin Reviewed-by: H Hartley Sweeten Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/cs4271.h | 1 - sound/soc/codecs/cs4271.c | 22 +++------------------- 2 files changed, 3 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/sound/cs4271.h b/include/sound/cs4271.h index 16f8d325d3dc..50a059e7d116 100644 --- a/include/sound/cs4271.h +++ b/include/sound/cs4271.h @@ -19,7 +19,6 @@ struct cs4271_platform_data { int gpio_nreset; /* GPIO driving Reset pin, if any */ - int gpio_disable; /* GPIO that disable serial bus, if any */ }; #endif /* __CS4271_H */ diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index 9c5b7db0ce6a..1791796216c8 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -441,22 +441,11 @@ static int cs4271_probe(struct snd_soc_codec *codec) struct cs4271_platform_data *cs4271plat = codec->dev->platform_data; int ret; int gpio_nreset = -EINVAL; - int gpio_disable = -EINVAL; codec->control_data = cs4271->control_data; - if (cs4271plat) { - if (gpio_is_valid(cs4271plat->gpio_nreset)) - gpio_nreset = cs4271plat->gpio_nreset; - if (gpio_is_valid(cs4271plat->gpio_disable)) - gpio_disable = cs4271plat->gpio_disable; - } - - if (gpio_disable >= 0) - if (gpio_request(gpio_disable, "CS4271 Disable")) - gpio_disable = -EINVAL; - if (gpio_disable >= 0) - gpio_direction_output(gpio_disable, 0); + if (cs4271plat && gpio_is_valid(cs4271plat->gpio_nreset)) + gpio_nreset = cs4271plat->gpio_nreset; if (gpio_nreset >= 0) if (gpio_request(gpio_nreset, "CS4271 Reset")) @@ -471,7 +460,6 @@ static int cs4271_probe(struct snd_soc_codec *codec) } cs4271->gpio_nreset = gpio_nreset; - cs4271->gpio_disable = gpio_disable; /* * In case of I2C, chip address specified in board data. @@ -509,10 +497,9 @@ static int cs4271_probe(struct snd_soc_codec *codec) static int cs4271_remove(struct snd_soc_codec *codec) { struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); - int gpio_nreset, gpio_disable; + int gpio_nreset; gpio_nreset = cs4271->gpio_nreset; - gpio_disable = cs4271->gpio_disable; if (gpio_is_valid(gpio_nreset)) { /* Set codec to the reset state */ @@ -520,9 +507,6 @@ static int cs4271_remove(struct snd_soc_codec *codec) gpio_free(gpio_nreset); } - if (gpio_is_valid(gpio_disable)) - gpio_free(gpio_disable); - return 0; }; -- cgit v1.2.3-59-g8ed1b From fa9879edebdaad4cfcd2dbe3eaa2ba0dc4f0a262 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 9 Feb 2011 14:44:17 +0530 Subject: ASoC: add support for multiple jack types This patch adds soc-jack support for adding voltage zones and for detecting jack type Signed-off-by: Vinod Koul Signed-off-by: Harsha Priya Signed-off-by: Mark Brown --- include/sound/soc.h | 23 +++++++++++++++++++++++ sound/soc/soc-jack.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 4b6c0a8c332f..4ccf1e4e0dd0 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -234,6 +234,7 @@ struct snd_soc_codec; struct snd_soc_codec_driver; struct soc_enum; struct snd_soc_jack; +struct snd_soc_jack_zone; struct snd_soc_jack_pin; struct snd_soc_cache_ops; #include @@ -307,6 +308,9 @@ void snd_soc_jack_notifier_register(struct snd_soc_jack *jack, struct notifier_block *nb); void snd_soc_jack_notifier_unregister(struct snd_soc_jack *jack, struct notifier_block *nb); +int snd_soc_jack_add_zones(struct snd_soc_jack *jack, int count, + struct snd_soc_jack_zone *zones); +int snd_soc_jack_get_type(struct snd_soc_jack *jack, int micbias_voltage); #ifdef CONFIG_GPIOLIB int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, struct snd_soc_jack_gpio *gpios); @@ -406,6 +410,24 @@ struct snd_soc_jack_pin { bool invert; }; +/** + * struct snd_soc_jack_zone - Describes voltage zones of jack detection + * + * @min_mv: start voltage in mv + * @max_mv: end voltage in mv + * @jack_type: type of jack that is expected for this voltage + * @debounce_time: debounce_time for jack, codec driver should wait for this + * duration before reading the adc for voltages + * @:list: list container + */ +struct snd_soc_jack_zone { + unsigned int min_mv; + unsigned int max_mv; + unsigned int jack_type; + unsigned int debounce_time; + struct list_head list; +}; + /** * struct snd_soc_jack_gpio - Describes a gpio pin for jack detection * @@ -435,6 +457,7 @@ struct snd_soc_jack { struct list_head pins; int status; struct blocking_notifier_head notifier; + struct list_head jack_zones; }; /* SoC PCM stream information */ diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index ac5a5bc7375a..99dbaf756b44 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -37,6 +37,7 @@ int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type, { jack->codec = codec; INIT_LIST_HEAD(&jack->pins); + INIT_LIST_HEAD(&jack->jack_zones); BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier); return snd_jack_new(codec->card->snd_card, id, type, &jack->jack); @@ -111,6 +112,51 @@ out: } EXPORT_SYMBOL_GPL(snd_soc_jack_report); +/** + * snd_soc_jack_add_zones - Associate voltage zones with jack + * + * @jack: ASoC jack + * @count: Number of zones + * @zone: Array of zones + * + * After this function has been called the zones specified in the + * array will be associated with the jack. + */ +int snd_soc_jack_add_zones(struct snd_soc_jack *jack, int count, + struct snd_soc_jack_zone *zones) +{ + int i; + + for (i = 0; i < count; i++) { + INIT_LIST_HEAD(&zones[i].list); + list_add(&(zones[i].list), &jack->jack_zones); + } + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_jack_add_zones); + +/** + * snd_soc_jack_get_type - Based on the mic bias value, this function returns + * the type of jack from the zones delcared in the jack type + * + * @micbias_voltage: mic bias voltage at adc channel when jack is plugged in + * + * Based on the mic bias value passed, this function helps identify + * the type of jack from the already delcared jack zones + */ +int snd_soc_jack_get_type(struct snd_soc_jack *jack, int micbias_voltage) +{ + struct snd_soc_jack_zone *zone; + + list_for_each_entry(zone, &jack->jack_zones, list) { + if (micbias_voltage >= zone->min_mv && + micbias_voltage < zone->max_mv) + return zone->jack_type; + } + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_jack_get_type); + /** * snd_soc_jack_add_pins - Associate DAPM pins with an ASoC jack * -- cgit v1.2.3-59-g8ed1b From fea952e5cc23ea94b4677ca20774cdc3cea014e2 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 14 Feb 2011 11:00:47 +0100 Subject: ALSA: core: sparse cleanups Change the core code where sparse complains. In most cases, this means just adding annotations to confirm that we indeed want to do the dirty things we're doing. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- include/sound/mixer_oss.h | 3 ++ include/sound/pcm.h | 91 +++++++++++++++++++++--------------------- sound/core/device.c | 7 ++-- sound/core/memalloc.c | 3 +- sound/core/oss/linear.c | 7 ++-- sound/core/oss/mixer_oss.c | 10 +++-- sound/core/oss/mulaw.c | 2 +- sound/core/oss/pcm_oss.c | 51 ++++++++++++----------- sound/core/oss/pcm_plugin.c | 40 ++++++++++--------- sound/core/oss/pcm_plugin.h | 11 ++--- sound/core/oss/route.c | 6 +-- sound/core/pcm.c | 10 ++--- sound/core/pcm_misc.c | 35 ++++++++-------- sound/core/pcm_native.c | 2 +- sound/core/seq/seq_clientmgr.c | 7 +--- sound/core/seq/seq_memory.c | 6 +-- sound/core/seq/seq_memory.h | 4 ++ sound/core/vmaster.c | 2 +- 18 files changed, 160 insertions(+), 137 deletions(-) (limited to 'include') diff --git a/include/sound/mixer_oss.h b/include/sound/mixer_oss.h index 51fbcb4a277a..13cb0b430a1b 100644 --- a/include/sound/mixer_oss.h +++ b/include/sound/mixer_oss.h @@ -73,6 +73,9 @@ struct snd_mixer_oss_file { struct snd_mixer_oss *mixer; }; +int snd_mixer_oss_ioctl_card(struct snd_card *card, + unsigned int cmd, unsigned long arg); + #endif /* CONFIG_SND_MIXER_OSS */ #endif /* __SOUND_MIXER_OSS_H */ diff --git a/include/sound/pcm.h b/include/sound/pcm.h index e731f8d71934..430a9cc045e2 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -136,48 +136,49 @@ struct snd_pcm_ops { SNDRV_PCM_RATE_88200|SNDRV_PCM_RATE_96000) #define SNDRV_PCM_RATE_8000_192000 (SNDRV_PCM_RATE_8000_96000|SNDRV_PCM_RATE_176400|\ SNDRV_PCM_RATE_192000) -#define SNDRV_PCM_FMTBIT_S8 (1ULL << SNDRV_PCM_FORMAT_S8) -#define SNDRV_PCM_FMTBIT_U8 (1ULL << SNDRV_PCM_FORMAT_U8) -#define SNDRV_PCM_FMTBIT_S16_LE (1ULL << SNDRV_PCM_FORMAT_S16_LE) -#define SNDRV_PCM_FMTBIT_S16_BE (1ULL << SNDRV_PCM_FORMAT_S16_BE) -#define SNDRV_PCM_FMTBIT_U16_LE (1ULL << SNDRV_PCM_FORMAT_U16_LE) -#define SNDRV_PCM_FMTBIT_U16_BE (1ULL << SNDRV_PCM_FORMAT_U16_BE) -#define SNDRV_PCM_FMTBIT_S24_LE (1ULL << SNDRV_PCM_FORMAT_S24_LE) -#define SNDRV_PCM_FMTBIT_S24_BE (1ULL << SNDRV_PCM_FORMAT_S24_BE) -#define SNDRV_PCM_FMTBIT_U24_LE (1ULL << SNDRV_PCM_FORMAT_U24_LE) -#define SNDRV_PCM_FMTBIT_U24_BE (1ULL << SNDRV_PCM_FORMAT_U24_BE) -#define SNDRV_PCM_FMTBIT_S32_LE (1ULL << SNDRV_PCM_FORMAT_S32_LE) -#define SNDRV_PCM_FMTBIT_S32_BE (1ULL << SNDRV_PCM_FORMAT_S32_BE) -#define SNDRV_PCM_FMTBIT_U32_LE (1ULL << SNDRV_PCM_FORMAT_U32_LE) -#define SNDRV_PCM_FMTBIT_U32_BE (1ULL << SNDRV_PCM_FORMAT_U32_BE) -#define SNDRV_PCM_FMTBIT_FLOAT_LE (1ULL << SNDRV_PCM_FORMAT_FLOAT_LE) -#define SNDRV_PCM_FMTBIT_FLOAT_BE (1ULL << SNDRV_PCM_FORMAT_FLOAT_BE) -#define SNDRV_PCM_FMTBIT_FLOAT64_LE (1ULL << SNDRV_PCM_FORMAT_FLOAT64_LE) -#define SNDRV_PCM_FMTBIT_FLOAT64_BE (1ULL << SNDRV_PCM_FORMAT_FLOAT64_BE) -#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE (1ULL << SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE) -#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE (1ULL << SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE) -#define SNDRV_PCM_FMTBIT_MU_LAW (1ULL << SNDRV_PCM_FORMAT_MU_LAW) -#define SNDRV_PCM_FMTBIT_A_LAW (1ULL << SNDRV_PCM_FORMAT_A_LAW) -#define SNDRV_PCM_FMTBIT_IMA_ADPCM (1ULL << SNDRV_PCM_FORMAT_IMA_ADPCM) -#define SNDRV_PCM_FMTBIT_MPEG (1ULL << SNDRV_PCM_FORMAT_MPEG) -#define SNDRV_PCM_FMTBIT_GSM (1ULL << SNDRV_PCM_FORMAT_GSM) -#define SNDRV_PCM_FMTBIT_SPECIAL (1ULL << SNDRV_PCM_FORMAT_SPECIAL) -#define SNDRV_PCM_FMTBIT_S24_3LE (1ULL << SNDRV_PCM_FORMAT_S24_3LE) -#define SNDRV_PCM_FMTBIT_U24_3LE (1ULL << SNDRV_PCM_FORMAT_U24_3LE) -#define SNDRV_PCM_FMTBIT_S24_3BE (1ULL << SNDRV_PCM_FORMAT_S24_3BE) -#define SNDRV_PCM_FMTBIT_U24_3BE (1ULL << SNDRV_PCM_FORMAT_U24_3BE) -#define SNDRV_PCM_FMTBIT_S20_3LE (1ULL << SNDRV_PCM_FORMAT_S20_3LE) -#define SNDRV_PCM_FMTBIT_U20_3LE (1ULL << SNDRV_PCM_FORMAT_U20_3LE) -#define SNDRV_PCM_FMTBIT_S20_3BE (1ULL << SNDRV_PCM_FORMAT_S20_3BE) -#define SNDRV_PCM_FMTBIT_U20_3BE (1ULL << SNDRV_PCM_FORMAT_U20_3BE) -#define SNDRV_PCM_FMTBIT_S18_3LE (1ULL << SNDRV_PCM_FORMAT_S18_3LE) -#define SNDRV_PCM_FMTBIT_U18_3LE (1ULL << SNDRV_PCM_FORMAT_U18_3LE) -#define SNDRV_PCM_FMTBIT_S18_3BE (1ULL << SNDRV_PCM_FORMAT_S18_3BE) -#define SNDRV_PCM_FMTBIT_U18_3BE (1ULL << SNDRV_PCM_FORMAT_U18_3BE) -#define SNDRV_PCM_FMTBIT_G723_24 (1ULL << SNDRV_PCM_FORMAT_G723_24) -#define SNDRV_PCM_FMTBIT_G723_24_1B (1ULL << SNDRV_PCM_FORMAT_G723_24_1B) -#define SNDRV_PCM_FMTBIT_G723_40 (1ULL << SNDRV_PCM_FORMAT_G723_40) -#define SNDRV_PCM_FMTBIT_G723_40_1B (1ULL << SNDRV_PCM_FORMAT_G723_40_1B) +#define _SNDRV_PCM_FMTBIT(fmt) (1ULL << (__force int)SNDRV_PCM_FORMAT_##fmt) +#define SNDRV_PCM_FMTBIT_S8 _SNDRV_PCM_FMTBIT(S8) +#define SNDRV_PCM_FMTBIT_U8 _SNDRV_PCM_FMTBIT(U8) +#define SNDRV_PCM_FMTBIT_S16_LE _SNDRV_PCM_FMTBIT(S16_LE) +#define SNDRV_PCM_FMTBIT_S16_BE _SNDRV_PCM_FMTBIT(S16_BE) +#define SNDRV_PCM_FMTBIT_U16_LE _SNDRV_PCM_FMTBIT(U16_LE) +#define SNDRV_PCM_FMTBIT_U16_BE _SNDRV_PCM_FMTBIT(U16_BE) +#define SNDRV_PCM_FMTBIT_S24_LE _SNDRV_PCM_FMTBIT(S24_LE) +#define SNDRV_PCM_FMTBIT_S24_BE _SNDRV_PCM_FMTBIT(S24_BE) +#define SNDRV_PCM_FMTBIT_U24_LE _SNDRV_PCM_FMTBIT(U24_LE) +#define SNDRV_PCM_FMTBIT_U24_BE _SNDRV_PCM_FMTBIT(U24_BE) +#define SNDRV_PCM_FMTBIT_S32_LE _SNDRV_PCM_FMTBIT(S32_LE) +#define SNDRV_PCM_FMTBIT_S32_BE _SNDRV_PCM_FMTBIT(S32_BE) +#define SNDRV_PCM_FMTBIT_U32_LE _SNDRV_PCM_FMTBIT(U32_LE) +#define SNDRV_PCM_FMTBIT_U32_BE _SNDRV_PCM_FMTBIT(U32_BE) +#define SNDRV_PCM_FMTBIT_FLOAT_LE _SNDRV_PCM_FMTBIT(FLOAT_LE) +#define SNDRV_PCM_FMTBIT_FLOAT_BE _SNDRV_PCM_FMTBIT(FLOAT_BE) +#define SNDRV_PCM_FMTBIT_FLOAT64_LE _SNDRV_PCM_FMTBIT(FLOAT64_LE) +#define SNDRV_PCM_FMTBIT_FLOAT64_BE _SNDRV_PCM_FMTBIT(FLOAT64_BE) +#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE _SNDRV_PCM_FMTBIT(IEC958_SUBFRAME_LE) +#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE _SNDRV_PCM_FMTBIT(IEC958_SUBFRAME_BE) +#define SNDRV_PCM_FMTBIT_MU_LAW _SNDRV_PCM_FMTBIT(MU_LAW) +#define SNDRV_PCM_FMTBIT_A_LAW _SNDRV_PCM_FMTBIT(A_LAW) +#define SNDRV_PCM_FMTBIT_IMA_ADPCM _SNDRV_PCM_FMTBIT(IMA_ADPCM) +#define SNDRV_PCM_FMTBIT_MPEG _SNDRV_PCM_FMTBIT(MPEG) +#define SNDRV_PCM_FMTBIT_GSM _SNDRV_PCM_FMTBIT(GSM) +#define SNDRV_PCM_FMTBIT_SPECIAL _SNDRV_PCM_FMTBIT(SPECIAL) +#define SNDRV_PCM_FMTBIT_S24_3LE _SNDRV_PCM_FMTBIT(S24_3LE) +#define SNDRV_PCM_FMTBIT_U24_3LE _SNDRV_PCM_FMTBIT(U24_3LE) +#define SNDRV_PCM_FMTBIT_S24_3BE _SNDRV_PCM_FMTBIT(S24_3BE) +#define SNDRV_PCM_FMTBIT_U24_3BE _SNDRV_PCM_FMTBIT(U24_3BE) +#define SNDRV_PCM_FMTBIT_S20_3LE _SNDRV_PCM_FMTBIT(S20_3LE) +#define SNDRV_PCM_FMTBIT_U20_3LE _SNDRV_PCM_FMTBIT(U20_3LE) +#define SNDRV_PCM_FMTBIT_S20_3BE _SNDRV_PCM_FMTBIT(S20_3BE) +#define SNDRV_PCM_FMTBIT_U20_3BE _SNDRV_PCM_FMTBIT(U20_3BE) +#define SNDRV_PCM_FMTBIT_S18_3LE _SNDRV_PCM_FMTBIT(S18_3LE) +#define SNDRV_PCM_FMTBIT_U18_3LE _SNDRV_PCM_FMTBIT(U18_3LE) +#define SNDRV_PCM_FMTBIT_S18_3BE _SNDRV_PCM_FMTBIT(S18_3BE) +#define SNDRV_PCM_FMTBIT_U18_3BE _SNDRV_PCM_FMTBIT(U18_3BE) +#define SNDRV_PCM_FMTBIT_G723_24 _SNDRV_PCM_FMTBIT(G723_24) +#define SNDRV_PCM_FMTBIT_G723_24_1B _SNDRV_PCM_FMTBIT(G723_24_1B) +#define SNDRV_PCM_FMTBIT_G723_40 _SNDRV_PCM_FMTBIT(G723_40) +#define SNDRV_PCM_FMTBIT_G723_40_1B _SNDRV_PCM_FMTBIT(G723_40_1B) #ifdef SNDRV_LITTLE_ENDIAN #define SNDRV_PCM_FMTBIT_S16 SNDRV_PCM_FMTBIT_S16_LE @@ -490,7 +491,7 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream, int snd_pcm_status(struct snd_pcm_substream *substream, struct snd_pcm_status *status); int snd_pcm_start(struct snd_pcm_substream *substream); -int snd_pcm_stop(struct snd_pcm_substream *substream, int status); +int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t status); int snd_pcm_drain_done(struct snd_pcm_substream *substream); #ifdef CONFIG_PM int snd_pcm_suspend(struct snd_pcm_substream *substream); @@ -748,8 +749,8 @@ static inline const struct snd_interval *hw_param_interval_c(const struct snd_pc return ¶ms->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; } -#define params_access(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_ACCESS)) -#define params_format(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_FORMAT)) +#define params_access(p) ((__force snd_pcm_access_t)snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_ACCESS))) +#define params_format(p) ((__force snd_pcm_format_t)snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_FORMAT))) #define params_subformat(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_SUBFORMAT)) #define params_channels(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_CHANNELS)->min #define params_rate(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_RATE)->min diff --git a/sound/core/device.c b/sound/core/device.c index a67dfac08c03..2d1ad4b0cd65 100644 --- a/sound/core/device.c +++ b/sound/core/device.c @@ -225,15 +225,16 @@ int snd_device_free_all(struct snd_card *card, snd_device_cmd_t cmd) { struct snd_device *dev; int err; - unsigned int range_low, range_high; + unsigned int range_low, range_high, type; if (snd_BUG_ON(!card)) return -ENXIO; - range_low = cmd * SNDRV_DEV_TYPE_RANGE_SIZE; + range_low = (__force unsigned int)cmd * SNDRV_DEV_TYPE_RANGE_SIZE; range_high = range_low + SNDRV_DEV_TYPE_RANGE_SIZE - 1; __again: list_for_each_entry(dev, &card->devices, list) { - if (dev->type >= range_low && dev->type <= range_high) { + type = (__force unsigned int)dev->type; + if (type >= range_low && type <= range_high) { if ((err = snd_device_free(card, dev->device_data)) < 0) return err; goto __again; diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index 9e92441f9b78..16bd9c03679b 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -192,7 +192,8 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, dmab->bytes = 0; switch (type) { case SNDRV_DMA_TYPE_CONTINUOUS: - dmab->area = snd_malloc_pages(size, (unsigned long)device); + dmab->area = snd_malloc_pages(size, + (__force gfp_t)(unsigned long)device); dmab->addr = 0; break; #ifdef CONFIG_HAS_DMA diff --git a/sound/core/oss/linear.c b/sound/core/oss/linear.c index 4c1d16827199..13b3f6f49fae 100644 --- a/sound/core/oss/linear.c +++ b/sound/core/oss/linear.c @@ -114,7 +114,8 @@ static snd_pcm_sframes_t linear_transfer(struct snd_pcm_plugin *plugin, return frames; } -static void init_data(struct linear_priv *data, int src_format, int dst_format) +static void init_data(struct linear_priv *data, + snd_pcm_format_t src_format, snd_pcm_format_t dst_format) { int src_le, dst_le, src_bytes, dst_bytes; @@ -140,9 +141,9 @@ static void init_data(struct linear_priv *data, int src_format, int dst_format) if (snd_pcm_format_signed(src_format) != snd_pcm_format_signed(dst_format)) { if (dst_le) - data->flip = cpu_to_le32(0x80000000); + data->flip = (__force u32)cpu_to_le32(0x80000000); else - data->flip = cpu_to_be32(0x80000000); + data->flip = (__force u32)cpu_to_be32(0x80000000); } } diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 822dd56993ca..d8359cfeca15 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -190,9 +190,10 @@ static int snd_mixer_oss_get_recsrc(struct snd_mixer_oss_file *fmixer) return -EIO; if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */ int err; - if ((err = mixer->get_recsrc(fmixer, &result)) < 0) + unsigned int index; + if ((err = mixer->get_recsrc(fmixer, &index)) < 0) return err; - result = 1 << result; + result = 1 << index; } else { struct snd_mixer_oss_slot *pslot; int chn; @@ -214,6 +215,7 @@ static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsr struct snd_mixer_oss *mixer = fmixer->mixer; struct snd_mixer_oss_slot *pslot; int chn, active; + unsigned int index; int result = 0; if (mixer == NULL) @@ -222,8 +224,8 @@ static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsr if (recsrc & ~mixer->oss_recsrc) recsrc &= ~mixer->oss_recsrc; mixer->put_recsrc(fmixer, ffz(~recsrc)); - mixer->get_recsrc(fmixer, &result); - result = 1 << result; + mixer->get_recsrc(fmixer, &index); + result = 1 << index; } for (chn = 0; chn < 31; chn++) { pslot = &mixer->slots[chn]; diff --git a/sound/core/oss/mulaw.c b/sound/core/oss/mulaw.c index f7649d4d950b..7915564bd394 100644 --- a/sound/core/oss/mulaw.c +++ b/sound/core/oss/mulaw.c @@ -274,7 +274,7 @@ static snd_pcm_sframes_t mulaw_transfer(struct snd_pcm_plugin *plugin, return frames; } -static void init_data(struct mulaw_priv *data, int format) +static void init_data(struct mulaw_priv *data, snd_pcm_format_t format) { #ifdef SNDRV_LITTLE_ENDIAN data->cvt_endian = snd_pcm_format_big_endian(format) > 0; diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index a2e4eb324699..23c34a02894b 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -41,6 +41,7 @@ #include #include #include +#include #define OSS_ALSAEMULVER _SIOR ('M', 249, int) @@ -60,7 +61,6 @@ MODULE_PARM_DESC(nonblock_open, "Don't block opening busy PCM devices."); MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM); MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM1); -extern int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned long arg); static int snd_pcm_oss_get_rate(struct snd_pcm_oss_file *pcm_oss_file); static int snd_pcm_oss_get_channels(struct snd_pcm_oss_file *pcm_oss_file); static int snd_pcm_oss_get_format(struct snd_pcm_oss_file *pcm_oss_file); @@ -656,7 +656,7 @@ snd_pcm_uframes_t get_hw_ptr_period(struct snd_pcm_runtime *runtime) #define AFMT_AC3 0x00000400 #define AFMT_VORBIS 0x00000800 -static int snd_pcm_oss_format_from(int format) +static snd_pcm_format_t snd_pcm_oss_format_from(int format) { switch (format) { case AFMT_MU_LAW: return SNDRV_PCM_FORMAT_MU_LAW; @@ -680,7 +680,7 @@ static int snd_pcm_oss_format_from(int format) } } -static int snd_pcm_oss_format_to(int format) +static int snd_pcm_oss_format_to(snd_pcm_format_t format) { switch (format) { case SNDRV_PCM_FORMAT_MU_LAW: return AFMT_MU_LAW; @@ -843,7 +843,8 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) size_t oss_frame_size; int err; int direct; - int format, sformat, n; + snd_pcm_format_t format, sformat; + int n; struct snd_mask sformat_mask; struct snd_mask mask; @@ -868,11 +869,11 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) _snd_pcm_hw_param_min(sparams, SNDRV_PCM_HW_PARAM_PERIODS, 2, 0); snd_mask_none(&mask); if (atomic_read(&substream->mmap_count)) - snd_mask_set(&mask, SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); + snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); else { - snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_INTERLEAVED); + snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED); if (!direct) - snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); + snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); } err = snd_pcm_hw_param_mask(substream, sparams, SNDRV_PCM_HW_PARAM_ACCESS, &mask); if (err < 0) { @@ -891,19 +892,22 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) else sformat = snd_pcm_plug_slave_format(format, &sformat_mask); - if (sformat < 0 || !snd_mask_test(&sformat_mask, sformat)) { - for (sformat = 0; sformat <= SNDRV_PCM_FORMAT_LAST; sformat++) { - if (snd_mask_test(&sformat_mask, sformat) && + if ((__force int)sformat < 0 || + !snd_mask_test(&sformat_mask, (__force int)sformat)) { + for (sformat = (__force snd_pcm_format_t)0; + (__force int)sformat <= (__force int)SNDRV_PCM_FORMAT_LAST; + sformat = (__force snd_pcm_format_t)((__force int)sformat + 1)) { + if (snd_mask_test(&sformat_mask, (__force int)sformat) && snd_pcm_oss_format_to(sformat) >= 0) break; } - if (sformat > SNDRV_PCM_FORMAT_LAST) { + if ((__force int)sformat > (__force int)SNDRV_PCM_FORMAT_LAST) { snd_printd("Cannot find a format!!!\n"); err = -EINVAL; goto failure; } } - err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, sformat, 0); + err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, (__force int)sformat, 0); if (err < 0) goto failure; @@ -912,9 +916,9 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) } else { _snd_pcm_hw_params_any(params); _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS, - SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0); + (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0); _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT, - snd_pcm_oss_format_from(runtime->oss.format), 0); + (__force int)snd_pcm_oss_format_from(runtime->oss.format), 0); _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS, runtime->oss.channels, 0); _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE, @@ -1185,10 +1189,10 @@ snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, const if (in_kernel) { mm_segment_t fs; fs = snd_enter_user(); - ret = snd_pcm_lib_write(substream, (void __user *)ptr, frames); + ret = snd_pcm_lib_write(substream, (void __force __user *)ptr, frames); snd_leave_user(fs); } else { - ret = snd_pcm_lib_write(substream, (void __user *)ptr, frames); + ret = snd_pcm_lib_write(substream, (void __force __user *)ptr, frames); } if (ret != -EPIPE && ret != -ESTRPIPE) break; @@ -1230,10 +1234,10 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p if (in_kernel) { mm_segment_t fs; fs = snd_enter_user(); - ret = snd_pcm_lib_read(substream, (void __user *)ptr, frames); + ret = snd_pcm_lib_read(substream, (void __force __user *)ptr, frames); snd_leave_user(fs); } else { - ret = snd_pcm_lib_read(substream, (void __user *)ptr, frames); + ret = snd_pcm_lib_read(substream, (void __force __user *)ptr, frames); } if (ret == -EPIPE) { if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { @@ -1333,7 +1337,7 @@ static ssize_t snd_pcm_oss_write2(struct snd_pcm_substream *substream, const cha struct snd_pcm_plugin_channel *channels; size_t oss_frame_bytes = (runtime->oss.plugin_first->src_width * runtime->oss.plugin_first->src_format.channels) / 8; if (!in_kernel) { - if (copy_from_user(runtime->oss.buffer, (const char __user *)buf, bytes)) + if (copy_from_user(runtime->oss.buffer, (const char __force __user *)buf, bytes)) return -EFAULT; buf = runtime->oss.buffer; } @@ -1429,7 +1433,7 @@ static ssize_t snd_pcm_oss_read2(struct snd_pcm_substream *substream, char *buf, struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_sframes_t frames, frames1; #ifdef CONFIG_SND_PCM_OSS_PLUGINS - char __user *final_dst = (char __user *)buf; + char __user *final_dst = (char __force __user *)buf; if (runtime->oss.plugin_first) { struct snd_pcm_plugin_channel *channels; size_t oss_frame_bytes = (runtime->oss.plugin_last->dst_width * runtime->oss.plugin_last->dst_format.channels) / 8; @@ -1549,6 +1553,7 @@ static int snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size) { struct snd_pcm_runtime *runtime; ssize_t result = 0; + snd_pcm_state_t state; long res; wait_queue_t wait; @@ -1570,9 +1575,9 @@ static int snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size) result = 0; set_current_state(TASK_INTERRUPTIBLE); snd_pcm_stream_lock_irq(substream); - res = runtime->status->state; + state = runtime->status->state; snd_pcm_stream_unlock_irq(substream); - if (res != SNDRV_PCM_STATE_RUNNING) { + if (state != SNDRV_PCM_STATE_RUNNING) { set_current_state(TASK_RUNNING); break; } @@ -1658,7 +1663,7 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file) size1); size1 /= runtime->channels; /* frames */ fs = snd_enter_user(); - snd_pcm_lib_write(substream, (void __user *)runtime->oss.buffer, size1); + snd_pcm_lib_write(substream, (void __force __user *)runtime->oss.buffer, size1); snd_leave_user(fs); } } else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) { diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c index 6751daa3bb50..71cc3ddf5c15 100644 --- a/sound/core/oss/pcm_plugin.c +++ b/sound/core/oss/pcm_plugin.c @@ -264,7 +264,7 @@ snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pc return frames; } -static int snd_pcm_plug_formats(struct snd_mask *mask, int format) +static int snd_pcm_plug_formats(struct snd_mask *mask, snd_pcm_format_t format) { struct snd_mask formats = *mask; u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | @@ -276,16 +276,16 @@ static int snd_pcm_plug_formats(struct snd_mask *mask, int format) SNDRV_PCM_FMTBIT_U24_3BE | SNDRV_PCM_FMTBIT_S24_3BE | SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE); - snd_mask_set(&formats, SNDRV_PCM_FORMAT_MU_LAW); + snd_mask_set(&formats, (__force int)SNDRV_PCM_FORMAT_MU_LAW); if (formats.bits[0] & (u32)linfmts) formats.bits[0] |= (u32)linfmts; if (formats.bits[1] & (u32)(linfmts >> 32)) formats.bits[1] |= (u32)(linfmts >> 32); - return snd_mask_test(&formats, format); + return snd_mask_test(&formats, (__force int)format); } -static int preferred_formats[] = { +static snd_pcm_format_t preferred_formats[] = { SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_FORMAT_S16_BE, SNDRV_PCM_FORMAT_U16_LE, @@ -306,24 +306,25 @@ static int preferred_formats[] = { SNDRV_PCM_FORMAT_U8 }; -int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask) +snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, + struct snd_mask *format_mask) { int i; - if (snd_mask_test(format_mask, format)) + if (snd_mask_test(format_mask, (__force int)format)) return format; - if (! snd_pcm_plug_formats(format_mask, format)) - return -EINVAL; + if (!snd_pcm_plug_formats(format_mask, format)) + return (__force snd_pcm_format_t)-EINVAL; if (snd_pcm_format_linear(format)) { unsigned int width = snd_pcm_format_width(format); int unsignd = snd_pcm_format_unsigned(format) > 0; int big = snd_pcm_format_big_endian(format) > 0; unsigned int badness, best = -1; - int best_format = -1; + snd_pcm_format_t best_format = (__force snd_pcm_format_t)-1; for (i = 0; i < ARRAY_SIZE(preferred_formats); i++) { - int f = preferred_formats[i]; + snd_pcm_format_t f = preferred_formats[i]; unsigned int w; - if (!snd_mask_test(format_mask, f)) + if (!snd_mask_test(format_mask, (__force int)f)) continue; w = snd_pcm_format_width(f); if (w >= width) @@ -337,17 +338,20 @@ int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask) best = badness; } } - return best_format >= 0 ? best_format : -EINVAL; + if ((__force int)best_format >= 0) + return best_format; + else + return (__force snd_pcm_format_t)-EINVAL; } else { switch (format) { case SNDRV_PCM_FORMAT_MU_LAW: for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) { - int format1 = preferred_formats[i]; - if (snd_mask_test(format_mask, format1)) + snd_pcm_format_t format1 = preferred_formats[i]; + if (snd_mask_test(format_mask, (__force int)format1)) return format1; } default: - return -EINVAL; + return (__force snd_pcm_format_t)-EINVAL; } } } @@ -359,7 +363,7 @@ int snd_pcm_plug_format_plugins(struct snd_pcm_substream *plug, struct snd_pcm_plugin_format tmpformat; struct snd_pcm_plugin_format dstformat; struct snd_pcm_plugin_format srcformat; - int src_access, dst_access; + snd_pcm_access_t src_access, dst_access; struct snd_pcm_plugin *plugin = NULL; int err; int stream = snd_pcm_plug_stream(plug); @@ -641,7 +645,7 @@ snd_pcm_sframes_t snd_pcm_plug_read_transfer(struct snd_pcm_substream *plug, str } int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_area, size_t dst_offset, - size_t samples, int format) + size_t samples, snd_pcm_format_t format) { /* FIXME: sub byte resolution and odd dst_offset */ unsigned char *dst; @@ -688,7 +692,7 @@ int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_area, size_t dst int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_area, size_t src_offset, const struct snd_pcm_channel_area *dst_area, size_t dst_offset, - size_t samples, int format) + size_t samples, snd_pcm_format_t format) { /* FIXME: sub byte resolution and odd dst_offset */ char *src, *dst; diff --git a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h index b9afab603711..a5035c2369a6 100644 --- a/sound/core/oss/pcm_plugin.h +++ b/sound/core/oss/pcm_plugin.h @@ -46,7 +46,7 @@ struct snd_pcm_plugin_channel { }; struct snd_pcm_plugin_format { - int format; + snd_pcm_format_t format; unsigned int rate; unsigned int channels; }; @@ -58,7 +58,7 @@ struct snd_pcm_plugin { struct snd_pcm_plugin_format dst_format; /* destination format */ int src_width; /* sample width in bits */ int dst_width; /* sample width in bits */ - int access; + snd_pcm_access_t access; snd_pcm_sframes_t (*src_frames)(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t dst_frames); snd_pcm_sframes_t (*dst_frames)(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t src_frames); snd_pcm_sframes_t (*client_channels)(struct snd_pcm_plugin *plugin, @@ -125,7 +125,8 @@ int snd_pcm_plug_format_plugins(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_pcm_hw_params *slave_params); -int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask); +snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, + struct snd_mask *format_mask); int snd_pcm_plugin_append(struct snd_pcm_plugin *plugin); @@ -146,12 +147,12 @@ snd_pcm_sframes_t snd_pcm_plugin_client_channels(struct snd_pcm_plugin *plugin, int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_channel, size_t dst_offset, - size_t samples, int format); + size_t samples, snd_pcm_format_t format); int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_channel, size_t src_offset, const struct snd_pcm_channel_area *dst_channel, size_t dst_offset, - size_t samples, int format); + size_t samples, snd_pcm_format_t format); void *snd_pcm_plug_buf_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t size); void snd_pcm_plug_buf_unlock(struct snd_pcm_substream *plug, void *ptr); diff --git a/sound/core/oss/route.c b/sound/core/oss/route.c index bbe25d8c450a..c8171f5783c8 100644 --- a/sound/core/oss/route.c +++ b/sound/core/oss/route.c @@ -25,7 +25,7 @@ #include "pcm_plugin.h" static void zero_areas(struct snd_pcm_plugin_channel *dvp, int ndsts, - snd_pcm_uframes_t frames, int format) + snd_pcm_uframes_t frames, snd_pcm_format_t format) { int dst = 0; for (; dst < ndsts; ++dst) { @@ -38,7 +38,7 @@ static void zero_areas(struct snd_pcm_plugin_channel *dvp, int ndsts, static inline void copy_area(const struct snd_pcm_plugin_channel *src_channel, struct snd_pcm_plugin_channel *dst_channel, - snd_pcm_uframes_t frames, int format) + snd_pcm_uframes_t frames, snd_pcm_format_t format) { dst_channel->enabled = 1; snd_pcm_area_copy(&src_channel->area, 0, &dst_channel->area, 0, frames, format); @@ -51,7 +51,7 @@ static snd_pcm_sframes_t route_transfer(struct snd_pcm_plugin *plugin, { int nsrcs, ndsts, dst; struct snd_pcm_plugin_channel *dvp; - int format; + snd_pcm_format_t format; if (snd_BUG_ON(!plugin || !src_channels || !dst_channels)) return -ENXIO; diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 6b4b1287b314..ee9abb2d9001 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -211,9 +211,9 @@ static char *snd_pcm_format_names[] = { const char *snd_pcm_format_name(snd_pcm_format_t format) { - if (format >= ARRAY_SIZE(snd_pcm_format_names)) + if ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names)) return "Unknown"; - return snd_pcm_format_names[format]; + return snd_pcm_format_names[(__force unsigned int)format]; } EXPORT_SYMBOL_GPL(snd_pcm_format_name); @@ -269,12 +269,12 @@ static const char *snd_pcm_stream_name(int stream) static const char *snd_pcm_access_name(snd_pcm_access_t access) { - return snd_pcm_access_names[access]; + return snd_pcm_access_names[(__force int)access]; } static const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat) { - return snd_pcm_subformat_names[subformat]; + return snd_pcm_subformat_names[(__force int)subformat]; } static const char *snd_pcm_tstamp_mode_name(int mode) @@ -284,7 +284,7 @@ static const char *snd_pcm_tstamp_mode_name(int mode) static const char *snd_pcm_state_name(snd_pcm_state_t state) { - return snd_pcm_state_names[state]; + return snd_pcm_state_names[(__force int)state]; } #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index 434af3c56d52..88f02e3866e0 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -35,7 +35,10 @@ struct pcm_format_data { unsigned char silence[8]; /* silence data to fill */ }; -static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = { +/* we do lots of calculations on snd_pcm_format_t; shut up sparse */ +#define INT __force int + +static struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = { [SNDRV_PCM_FORMAT_S8] = { .width = 8, .phys = 8, .le = -1, .signd = 1, .silence = {}, @@ -215,9 +218,9 @@ static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = { int snd_pcm_format_signed(snd_pcm_format_t format) { int val; - if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) return -EINVAL; - if ((val = pcm_formats[format].signd) < 0) + if ((val = pcm_formats[(INT)format].signd) < 0) return -EINVAL; return val; } @@ -266,9 +269,9 @@ EXPORT_SYMBOL(snd_pcm_format_linear); int snd_pcm_format_little_endian(snd_pcm_format_t format) { int val; - if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) return -EINVAL; - if ((val = pcm_formats[format].le) < 0) + if ((val = pcm_formats[(INT)format].le) < 0) return -EINVAL; return val; } @@ -304,9 +307,9 @@ EXPORT_SYMBOL(snd_pcm_format_big_endian); int snd_pcm_format_width(snd_pcm_format_t format) { int val; - if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) return -EINVAL; - if ((val = pcm_formats[format].width) == 0) + if ((val = pcm_formats[(INT)format].width) == 0) return -EINVAL; return val; } @@ -323,9 +326,9 @@ EXPORT_SYMBOL(snd_pcm_format_width); int snd_pcm_format_physical_width(snd_pcm_format_t format) { int val; - if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) return -EINVAL; - if ((val = pcm_formats[format].phys) == 0) + if ((val = pcm_formats[(INT)format].phys) == 0) return -EINVAL; return val; } @@ -358,11 +361,11 @@ EXPORT_SYMBOL(snd_pcm_format_size); */ const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format) { - if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) return NULL; - if (! pcm_formats[format].phys) + if (! pcm_formats[(INT)format].phys) return NULL; - return pcm_formats[format].silence; + return pcm_formats[(INT)format].silence; } EXPORT_SYMBOL(snd_pcm_format_silence_64); @@ -382,16 +385,16 @@ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int int width; unsigned char *dst, *pat; - if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) return -EINVAL; if (samples == 0) return 0; - width = pcm_formats[format].phys; /* physical width */ - pat = pcm_formats[format].silence; + width = pcm_formats[(INT)format].phys; /* physical width */ + pat = pcm_formats[(INT)format].silence; if (! width) return -EINVAL; /* signed or 1 byte data */ - if (pcm_formats[format].signd == 1 || width <= 8) { + if (pcm_formats[(INT)format].signd == 1 || width <= 8) { unsigned int bytes = samples * width / 8; memset(data, *pat, bytes); return 0; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 4be45e7be8ad..ae42b6509ce4 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -941,7 +941,7 @@ static struct action_ops snd_pcm_action_stop = { * * The state of each stream is then changed to the given state unconditionally. */ -int snd_pcm_stop(struct snd_pcm_substream *substream, int state) +int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t state) { return snd_pcm_action(&snd_pcm_action_stop, substream, state); } diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 99a485f13648..f2436d33fbf7 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -1052,7 +1052,7 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf, } else { #ifdef CONFIG_COMPAT if (client->convert32 && snd_seq_ev_is_varusr(&event)) { - void *ptr = compat_ptr(event.data.raw32.d[1]); + void *ptr = (void __force *)compat_ptr(event.data.raw32.d[1]); event.data.ext.ptr = ptr; } #endif @@ -2407,7 +2407,7 @@ int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg) if (client == NULL) return -ENXIO; fs = snd_enter_user(); - result = snd_seq_do_ioctl(client, cmd, (void __user *)arg); + result = snd_seq_do_ioctl(client, cmd, (void __force __user *)arg); snd_leave_user(fs); return result; } @@ -2497,9 +2497,6 @@ static void snd_seq_info_dump_ports(struct snd_info_buffer *buffer, } -void snd_seq_info_pool(struct snd_info_buffer *buffer, - struct snd_seq_pool *pool, char *space); - /* exported to seq_info.c */ void snd_seq_info_clients_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index 7fb55436287f..7f50c1437675 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -86,7 +86,7 @@ int snd_seq_dump_var_event(const struct snd_seq_event *event, if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) { char buf[32]; - char __user *curptr = (char __user *)event->data.ext.ptr; + char __user *curptr = (char __force __user *)event->data.ext.ptr; while (len > 0) { int size = sizeof(buf); if (len < size) @@ -157,7 +157,7 @@ int snd_seq_expand_var_event(const struct snd_seq_event *event, int count, char if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) { if (! in_kernel) return -EINVAL; - if (copy_from_user(buf, (void __user *)event->data.ext.ptr, len)) + if (copy_from_user(buf, (void __force __user *)event->data.ext.ptr, len)) return -EFAULT; return newlen; } @@ -343,7 +343,7 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event, tmp->event = src->event; src = src->next; } else if (is_usrptr) { - if (copy_from_user(&tmp->event, (char __user *)buf, size)) { + if (copy_from_user(&tmp->event, (char __force __user *)buf, size)) { err = -EFAULT; goto __error; } diff --git a/sound/core/seq/seq_memory.h b/sound/core/seq/seq_memory.h index 63e91431a29f..4a2ec779b8a7 100644 --- a/sound/core/seq/seq_memory.h +++ b/sound/core/seq/seq_memory.h @@ -24,6 +24,8 @@ #include #include +struct snd_info_buffer; + /* container for sequencer event (internal use) */ struct snd_seq_event_cell { struct snd_seq_event event; @@ -99,5 +101,7 @@ void snd_sequencer_memory_done(void); /* polling */ int snd_seq_pool_poll_wait(struct snd_seq_pool *pool, struct file *file, poll_table *wait); +void snd_seq_info_pool(struct snd_info_buffer *buffer, + struct snd_seq_pool *pool, char *space); #endif diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c index 3b9b550109cb..a89948ae9e8d 100644 --- a/sound/core/vmaster.c +++ b/sound/core/vmaster.c @@ -18,7 +18,7 @@ * a subset of information returned via ctl info callback */ struct link_ctl_info { - int type; /* value type */ + snd_ctl_elem_type_t type; /* value type */ int count; /* item count */ int min_val, max_val; /* min, max values */ }; -- cgit v1.2.3-59-g8ed1b From 03c2d87a2112a6548aa3f9635e76d3611c3df53c Mon Sep 17 00:00:00 2001 From: Andreas Mohr Date: Thu, 17 Feb 2011 00:17:53 +0100 Subject: ALSA: ac97: replace open-coded, error-prone stuff with AC97 bit defines Use AC97 macros (sometimes already existing, or newly added) instead of error-prone repetition of open-coded values. Signed-off-by: Andreas Mohr Signed-off-by: Takashi Iwai --- include/sound/ac97_codec.h | 5 +++ sound/pci/ac97/ac97_codec.c | 83 ++++++++++++++++++++++++++------------------- 2 files changed, 54 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h index b602f475cdbb..f1dcefe4532b 100644 --- a/include/sound/ac97_codec.h +++ b/include/sound/ac97_codec.h @@ -96,6 +96,10 @@ #define AC97_FUNC_INFO 0x68 /* Function Information */ #define AC97_SENSE_INFO 0x6a /* Sense Details */ +/* volume controls */ +#define AC97_MUTE_MASK_MONO 0x8000 +#define AC97_MUTE_MASK_STEREO 0x8080 + /* slot allocation */ #define AC97_SLOT_TAG 0 #define AC97_SLOT_CMD_ADDR 1 @@ -138,6 +142,7 @@ #define AC97_BC_18BIT_ADC 0x0100 /* 18-bit ADC resolution */ #define AC97_BC_20BIT_ADC 0x0200 /* 20-bit ADC resolution */ #define AC97_BC_ADC_MASK 0x0300 +#define AC97_BC_3D_TECH_ID_MASK 0x7c00 /* Per-vendor ID of 3D enhancement */ /* general purpose */ #define AC97_GP_DRSS_MASK 0x0c00 /* double rate slot select */ diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index cb62d178b3e0..2cc56b5c5397 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -590,9 +590,9 @@ static int snd_ac97_put_volsw(struct snd_kcontrol *kcontrol, snd_ac97_page_restore(ac97, page_save); #ifdef CONFIG_SND_AC97_POWER_SAVE /* check analog mixer power-down */ - if ((val_mask & 0x8000) && + if ((val_mask & AC97_PD_EAPD) && (kcontrol->private_value & (1<<30))) { - if (val & 0x8000) + if (val & AC97_PD_EAPD) ac97->power_up &= ~(1 << (reg>>1)); else ac97->power_up |= 1 << (reg>>1); @@ -1035,20 +1035,20 @@ static int snd_ac97_dev_free(struct snd_device *device) static int snd_ac97_try_volume_mix(struct snd_ac97 * ac97, int reg) { - unsigned short val, mask = 0x8000; + unsigned short val, mask = AC97_MUTE_MASK_MONO; if (! snd_ac97_valid_reg(ac97, reg)) return 0; switch (reg) { case AC97_MASTER_TONE: - return ac97->caps & 0x04 ? 1 : 0; + return ac97->caps & AC97_BC_BASS_TREBLE ? 1 : 0; case AC97_HEADPHONE: - return ac97->caps & 0x10 ? 1 : 0; + return ac97->caps & AC97_BC_HEADPHONE ? 1 : 0; case AC97_REC_GAIN_MIC: - return ac97->caps & 0x01 ? 1 : 0; + return ac97->caps & AC97_BC_DEDICATED_MIC ? 1 : 0; case AC97_3D_CONTROL: - if (ac97->caps & 0x7c00) { + if (ac97->caps & AC97_BC_3D_TECH_ID_MASK) { val = snd_ac97_read(ac97, reg); /* if nonzero - fixed and we can't set it */ return val == 0; @@ -1104,7 +1104,10 @@ static void check_volume_resolution(struct snd_ac97 *ac97, int reg, unsigned cha *lo_max = *hi_max = 0; for (i = 0 ; i < ARRAY_SIZE(cbit); i++) { unsigned short val; - snd_ac97_write(ac97, reg, 0x8080 | cbit[i] | (cbit[i] << 8)); + snd_ac97_write( + ac97, reg, + AC97_MUTE_MASK_STEREO | cbit[i] | (cbit[i] << 8) + ); /* Do the read twice due to buffers on some ac97 codecs. * e.g. The STAC9704 returns exactly what you wrote to the register * if you read it immediately. This causes the detect routine to fail. @@ -1139,14 +1142,14 @@ static void snd_ac97_change_volume_params2(struct snd_ac97 * ac97, int reg, int unsigned short val, val1; *max = 63; - val = 0x8080 | (0x20 << shift); + val = AC97_MUTE_MASK_STEREO | (0x20 << shift); snd_ac97_write(ac97, reg, val); val1 = snd_ac97_read(ac97, reg); if (val != val1) { *max = 31; } /* reset volume to zero */ - snd_ac97_write_cache(ac97, reg, 0x8080); + snd_ac97_write_cache(ac97, reg, AC97_MUTE_MASK_STEREO); } static inline int printable(unsigned int x) @@ -1183,16 +1186,16 @@ static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg, if (! snd_ac97_valid_reg(ac97, reg)) return 0; - mute_mask = 0x8000; + mute_mask = AC97_MUTE_MASK_MONO; val = snd_ac97_read(ac97, reg); if (check_stereo || (ac97->flags & AC97_STEREO_MUTES)) { /* check whether both mute bits work */ - val1 = val | 0x8080; + val1 = val | AC97_MUTE_MASK_STEREO; snd_ac97_write(ac97, reg, val1); if (val1 == snd_ac97_read(ac97, reg)) - mute_mask = 0x8080; + mute_mask = AC97_MUTE_MASK_STEREO; } - if (mute_mask == 0x8080) { + if (mute_mask == AC97_MUTE_MASK_STEREO) { struct snd_kcontrol_new tmp = AC97_DOUBLE(name, reg, 15, 7, 1, 1); if (check_amix) tmp.private_value |= (1 << 30); @@ -1268,9 +1271,11 @@ static int snd_ac97_cvol_new(struct snd_card *card, char *name, int reg, unsigne err = snd_ctl_add(card, kctl); if (err < 0) return err; - snd_ac97_write_cache(ac97, reg, - (snd_ac97_read(ac97, reg) & 0x8080) | - lo_max | (hi_max << 8)); + snd_ac97_write_cache( + ac97, reg, + (snd_ac97_read(ac97, reg) & AC97_MUTE_MASK_STEREO) + | lo_max | (hi_max << 8) + ); return 0; } @@ -1332,7 +1337,7 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) return err; } - ac97->regs[AC97_CENTER_LFE_MASTER] = 0x8080; + ac97->regs[AC97_CENTER_LFE_MASTER] = AC97_MUTE_MASK_STEREO; /* build center controls */ if ((snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER)) @@ -1410,8 +1415,12 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0) return err; set_tlv_db_scale(kctl, db_scale_4bit); - snd_ac97_write_cache(ac97, AC97_PC_BEEP, - snd_ac97_read(ac97, AC97_PC_BEEP) | 0x801e); + snd_ac97_write_cache( + ac97, + AC97_PC_BEEP, + (snd_ac97_read(ac97, AC97_PC_BEEP) + | AC97_MUTE_MASK_MONO | 0x001e) + ); } /* build Phone controls */ @@ -1545,7 +1554,7 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) } /* build Simulated Stereo Enhancement control */ - if (ac97->caps & 0x0008) { + if (ac97->caps & AC97_BC_SIM_STEREO) { if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_STEREO_ENHANCEMENT], ac97))) < 0) return err; } @@ -1557,7 +1566,7 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) } /* build Loudness control */ - if (ac97->caps & 0x0020) { + if (ac97->caps & AC97_BC_LOUDNESS) { if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOUDNESS], ac97))) < 0) return err; } @@ -2542,8 +2551,8 @@ void snd_ac97_resume(struct snd_ac97 *ac97) schedule_timeout_uninterruptible(1); } while (time_after_eq(end_time, jiffies)); /* FIXME: extra delay */ - ac97->bus->ops->write(ac97, AC97_MASTER, 0x8000); - if (snd_ac97_read(ac97, AC97_MASTER) != 0x8000) + ac97->bus->ops->write(ac97, AC97_MASTER, AC97_MUTE_MASK_MONO); + if (snd_ac97_read(ac97, AC97_MASTER) != AC97_MUTE_MASK_MONO) msleep(250); } else { end_time = jiffies + msecs_to_jiffies(100); @@ -2747,12 +2756,12 @@ static int master_mute_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem int rshift = (kcontrol->private_value >> 12) & 0x0f; unsigned short mask; if (shift != rshift) - mask = 0x8080; + mask = AC97_MUTE_MASK_STEREO; else - mask = 0x8000; - snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, + mask = AC97_MUTE_MASK_MONO; + snd_ac97_update_bits(ac97, AC97_POWERDOWN, AC97_PD_EAPD, (ac97->regs[AC97_MASTER] & mask) == mask ? - 0x8000 : 0); + AC97_PD_EAPD : 0); } return err; } @@ -2765,7 +2774,10 @@ static int tune_mute_led(struct snd_ac97 *ac97) return -ENOENT; msw->put = master_mute_sw_put; snd_ac97_remove_ctl(ac97, "External Amplifier", NULL); - snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, 0x8000); /* mute LED on */ + snd_ac97_update_bits( + ac97, AC97_POWERDOWN, + AC97_PD_EAPD, AC97_PD_EAPD /* mute LED on */ + ); ac97->scaps |= AC97_SCAP_EAPD_LED; return 0; } @@ -2780,12 +2792,12 @@ static int hp_master_mute_sw_put(struct snd_kcontrol *kcontrol, int rshift = (kcontrol->private_value >> 12) & 0x0f; unsigned short mask; if (shift != rshift) - mask = 0x8080; + mask = AC97_MUTE_MASK_STEREO; else - mask = 0x8000; - snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, + mask = AC97_MUTE_MASK_MONO; + snd_ac97_update_bits(ac97, AC97_POWERDOWN, AC97_PD_EAPD, (ac97->regs[AC97_MASTER] & mask) == mask ? - 0x8000 : 0); + AC97_PD_EAPD : 0); } return err; } @@ -2801,7 +2813,10 @@ static int tune_hp_mute_led(struct snd_ac97 *ac97) snd_ac97_remove_ctl(ac97, "External Amplifier", NULL); snd_ac97_remove_ctl(ac97, "Headphone Playback", "Switch"); snd_ac97_remove_ctl(ac97, "Headphone Playback", "Volume"); - snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, 0x8000); /* mute LED on */ + snd_ac97_update_bits( + ac97, AC97_POWERDOWN, + AC97_PD_EAPD, AC97_PD_EAPD /* mute LED on */ + ); return 0; } -- cgit v1.2.3-59-g8ed1b From 7887ab3a274dc5f1d1d94ca0cd41ae495d01f94f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 17 Feb 2011 16:35:55 -0800 Subject: ASoC: Allow GPIO jack detection to be configured as a wake source Some systems wish to use jacks as wake sources. Provide a wake flag in the GPIO configuration which causes the driver to enable the IRQ as a wake source. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 3 +++ sound/soc/soc-jack.c | 8 ++++++++ 2 files changed, 11 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 4ccf1e4e0dd0..fb57c33482e5 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -436,6 +436,7 @@ struct snd_soc_jack_zone { * @report: value to report when jack detected * @invert: report presence in low state * @debouce_time: debouce time in ms + * @wake: enable as wake source */ #ifdef CONFIG_GPIOLIB struct snd_soc_jack_gpio { @@ -444,6 +445,8 @@ struct snd_soc_jack_gpio { int report; int invert; int debounce_time; + bool wake; + struct snd_soc_jack *jack; struct delayed_work work; diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index 4579ee090bbf..1382251ed2a2 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -330,6 +330,14 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, if (ret) goto err; + if (gpios[i].wake) { + ret = set_irq_wake(gpio_to_irq(gpios[i].gpio), 1); + if (ret != 0) + printk(KERN_ERR + "Failed to mark GPIO %d as wake source: %d\n", + gpios[i].gpio, ret); + } + #ifdef CONFIG_GPIO_SYSFS /* Expose GPIO value over sysfs for diagnostic purposes */ gpio_export(gpios[i].gpio, false); -- cgit v1.2.3-59-g8ed1b From fadddc8753ccfab26ee57f3205d6926fe4be1350 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 17 Feb 2011 16:41:42 -0800 Subject: ASoC: Add kerneldoc for jack_status_check callback Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index fb57c33482e5..65d865f7e8c0 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -437,6 +437,9 @@ struct snd_soc_jack_zone { * @invert: report presence in low state * @debouce_time: debouce time in ms * @wake: enable as wake source + * @jack_status_check: callback function which overrides the detection + * to provide more complex checks (eg, reading an + * ADC). */ #ifdef CONFIG_GPIOLIB struct snd_soc_jack_gpio { -- cgit v1.2.3-59-g8ed1b From 9b7c525dfaa9a1b5f01db1f3a1edc50bbb6eb739 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 17 Feb 2011 20:05:44 -0800 Subject: ASoC: Support WM8958 direct microphone detection IRQ Allow direct routing of the WM8958 microphone detection signal to a GPIO to be used, saving the need to demux the interrupt. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/linux/mfd/wm8994/pdata.h | 5 ++++ sound/soc/codecs/wm8994.c | 57 ++++++++++++++++++++++++---------------- 2 files changed, 40 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/include/linux/mfd/wm8994/pdata.h b/include/linux/mfd/wm8994/pdata.h index 9eab263658be..06869466b7f0 100644 --- a/include/linux/mfd/wm8994/pdata.h +++ b/include/linux/mfd/wm8994/pdata.h @@ -103,6 +103,11 @@ struct wm8994_pdata { unsigned int lineout1fb:1; unsigned int lineout2fb:1; + /* IRQ for microphone detection if brought out directly as a + * signal. + */ + int micdet_irq; + /* Microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */ unsigned int micbias1_lvl:1; unsigned int micbias2_lvl:1; diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index b23e91027d64..1ad6e3db7804 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -104,6 +104,7 @@ struct wm8994_priv { void *jack_cb_data; bool jack_is_mic; bool jack_is_video; + int micdet_irq; int revision; struct wm8994_pdata *pdata; @@ -3102,6 +3103,12 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) wm8994->pdata = dev_get_platdata(codec->dev->parent); wm8994->codec = codec; + if (wm8994->pdata && wm8994->pdata->micdet_irq) + wm8994->micdet_irq = wm8994->pdata->micdet_irq; + else if (wm8994->pdata && wm8994->pdata->irq_base) + wm8994->micdet_irq = wm8994->pdata->irq_base + + WM8994_IRQ_MIC1_DET; + pm_runtime_enable(codec->dev); pm_runtime_resume(codec->dev); @@ -3150,14 +3157,17 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) switch (control->type) { case WM8994: - ret = wm8994_request_irq(codec->control_data, - WM8994_IRQ_MIC1_DET, - wm8994_mic_irq, "Mic 1 detect", - wm8994); - if (ret != 0) - dev_warn(codec->dev, - "Failed to request Mic1 detect IRQ: %d\n", - ret); + if (wm8994->micdet_irq) { + ret = request_threaded_irq(wm8994->micdet_irq, NULL, + wm8994_mic_irq, + IRQF_TRIGGER_RISING, + "Mic1 detect", + wm8994); + if (ret != 0) + dev_warn(codec->dev, + "Failed to request Mic1 detect IRQ: %d\n", + ret); + } ret = wm8994_request_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, @@ -3188,15 +3198,17 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) break; case WM8958: - ret = wm8994_request_irq(codec->control_data, - WM8994_IRQ_MIC1_DET, - wm8958_mic_irq, "Mic detect", - wm8994); - if (ret != 0) - dev_warn(codec->dev, - "Failed to request Mic detect IRQ: %d\n", - ret); - break; + if (wm8994->micdet_irq) { + ret = request_threaded_irq(wm8994->micdet_irq, NULL, + wm8958_mic_irq, + IRQF_TRIGGER_RISING, + "Mic detect", + wm8994); + if (ret != 0) + dev_warn(codec->dev, + "Failed to request Mic detect IRQ: %d\n", + ret); + } } /* Remember if AIFnLRCLK is configured as a GPIO. This should be @@ -3328,7 +3340,8 @@ err_irq: wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_SHRT, wm8994); wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_DET, wm8994); wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, wm8994); - wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_DET, wm8994); + if (wm8994->micdet_irq) + free_irq(wm8994->micdet_irq, wm8994); err: kfree(wm8994); return ret; @@ -3345,8 +3358,8 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec) switch (control->type) { case WM8994: - wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_SHRT, - wm8994); + if (wm8994->micdet_irq) + free_irq(wm8994->micdet_irq, wm8994); wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_DET, wm8994); wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, @@ -3356,8 +3369,8 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec) break; case WM8958: - wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_DET, - wm8994); + if (wm8994->micdet_irq) + free_irq(wm8994->micdet_irq, wm8994); break; } kfree(wm8994->retune_mobile_texts); -- cgit v1.2.3-59-g8ed1b From 48e028eccabc9c246bfad175262582a1ce34a316 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 21 Feb 2011 17:11:59 -0800 Subject: ASoC: Support configuration of WM8958 microphone bias analogue parameters The WM8958 has a different microphone bias architecture to WM8994 so needs different configuration to WM8994. Support this in platform data. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/linux/mfd/wm8994/pdata.h | 7 +++++-- include/linux/mfd/wm8994/registers.h | 2 ++ sound/soc/codecs/wm8994.c | 7 +++++++ 3 files changed, 14 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/mfd/wm8994/pdata.h b/include/linux/mfd/wm8994/pdata.h index 06869466b7f0..466b1c777aff 100644 --- a/include/linux/mfd/wm8994/pdata.h +++ b/include/linux/mfd/wm8994/pdata.h @@ -108,13 +108,16 @@ struct wm8994_pdata { */ int micdet_irq; - /* Microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */ + /* WM8994 microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */ unsigned int micbias1_lvl:1; unsigned int micbias2_lvl:1; - /* Jack detect threashold levels, see datasheet for values */ + /* WM8994 jack detect threashold levels, see datasheet for values */ unsigned int jd_scthr:2; unsigned int jd_thr:2; + + /* WM8958 microphone bias configuration */ + int micbias[2]; }; #endif diff --git a/include/linux/mfd/wm8994/registers.h b/include/linux/mfd/wm8994/registers.h index be072faec6f0..f3ee84284670 100644 --- a/include/linux/mfd/wm8994/registers.h +++ b/include/linux/mfd/wm8994/registers.h @@ -63,6 +63,8 @@ #define WM8994_MICBIAS 0x3A #define WM8994_LDO_1 0x3B #define WM8994_LDO_2 0x3C +#define WM8958_MICBIAS1 0x3D +#define WM8958_MICBIAS2 0x3E #define WM8994_CHARGE_PUMP_1 0x4C #define WM8958_CHARGE_PUMP_2 0x4D #define WM8994_CLASS_W_1 0x51 diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 1ad6e3db7804..9b9c15ffb7d2 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -2855,6 +2855,13 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994) else snd_soc_add_controls(wm8994->codec, wm8994_eq_controls, ARRAY_SIZE(wm8994_eq_controls)); + + for (i = 0; i < ARRAY_SIZE(pdata->micbias); i++) { + if (pdata->micbias[i]) { + snd_soc_write(codec, WM8958_MICBIAS1 + i, + pdata->micbias[i] & 0xffff); + } + } } /** -- cgit v1.2.3-59-g8ed1b From 4a5f7bda8fe9d0ed08ed4c5beb5dc3fa62f09d05 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 1 Mar 2011 20:10:46 +0000 Subject: ASoC: Add platform data for WM9081 IRQ pin configuration The WM9081 IRQ output can be either active high or active low and can support either CMOS or open drain modes. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/wm9081.h | 9 ++++++--- sound/soc/codecs/wm9081.c | 29 +++++++++++++++++++---------- 2 files changed, 25 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/sound/wm9081.h b/include/sound/wm9081.h index e173ddbf6bd4..f34b0b1716d8 100644 --- a/include/sound/wm9081.h +++ b/include/sound/wm9081.h @@ -17,9 +17,12 @@ struct wm9081_retune_mobile_setting { u16 config[20]; }; -struct wm9081_retune_mobile_config { - struct wm9081_retune_mobile_setting *configs; - int num_configs; +struct wm9081_pdata { + bool irq_high; /* IRQ is active high */ + bool irq_cmos; /* IRQ is in CMOS mode */ + + struct wm9081_retune_mobile_setting *retune_configs; + int num_retune_configs; }; #endif diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 2103623a0776..7883f3ed797b 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -167,7 +167,7 @@ struct wm9081_priv { int fll_fref; int fll_fout; int tdm_width; - struct wm9081_retune_mobile_config *retune; + struct wm9081_pdata pdata; }; static int wm9081_volatile_register(struct snd_soc_codec *codec, unsigned int reg) @@ -1082,21 +1082,22 @@ static int wm9081_hw_params(struct snd_pcm_substream *substream, aif4 |= wm9081->bclk / wm9081->fs; /* Apply a ReTune Mobile configuration if it's in use */ - if (wm9081->retune) { - struct wm9081_retune_mobile_config *retune = wm9081->retune; + if (wm9081->pdata.num_retune_configs) { + struct wm9081_pdata *pdata = &wm9081->pdata; struct wm9081_retune_mobile_setting *s; int eq1; best = 0; - best_val = abs(retune->configs[0].rate - wm9081->fs); - for (i = 0; i < retune->num_configs; i++) { - cur_val = abs(retune->configs[i].rate - wm9081->fs); + best_val = abs(pdata->retune_configs[0].rate - wm9081->fs); + for (i = 0; i < pdata->num_retune_configs; i++) { + cur_val = abs(pdata->retune_configs[i].rate - + wm9081->fs); if (cur_val < best_val) { best_val = cur_val; best = i; } } - s = &retune->configs[best]; + s = &pdata->retune_configs[best]; dev_dbg(codec->dev, "ReTune Mobile %s tuned for %dHz\n", s->name, s->rate); @@ -1255,6 +1256,14 @@ static int wm9081_probe(struct snd_soc_codec *codec) return ret; } + reg = 0; + if (wm9081->pdata.irq_high) + reg |= WM9081_IRQ_POL; + if (!wm9081->pdata.irq_cmos) + reg |= WM9081_IRQ_OP_CTRL; + snd_soc_update_bits(codec, WM9081_INTERRUPT_CONTROL, + WM9081_IRQ_POL | WM9081_IRQ_OP_CTRL, reg); + wm9081_set_bias_level(codec, SND_SOC_BIAS_STANDBY); /* Enable zero cross by default */ @@ -1266,7 +1275,7 @@ static int wm9081_probe(struct snd_soc_codec *codec) snd_soc_add_controls(codec, wm9081_snd_controls, ARRAY_SIZE(wm9081_snd_controls)); - if (!wm9081->retune) { + if (!wm9081->pdata.num_retune_configs) { dev_dbg(codec->dev, "No ReTune Mobile data, using normal EQ\n"); snd_soc_add_controls(codec, wm9081_eq_controls, @@ -1343,8 +1352,8 @@ static __devinit int wm9081_i2c_probe(struct i2c_client *i2c, wm9081->control_data = i2c; if (dev_get_platdata(&i2c->dev)) - memcpy(&wm9081->retune, dev_get_platdata(&i2c->dev), - sizeof(wm9081->retune)); + memcpy(&wm9081->pdata, dev_get_platdata(&i2c->dev), + sizeof(wm9081->pdata)); ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm9081, &wm9081_dai, 1); -- cgit v1.2.3-59-g8ed1b From e37a4970cd7ab6aec9e848cd3c355fd47fd18afd Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Mar 2011 18:21:57 +0000 Subject: ASoC: Add a per-card DAPM context This means that rather than adding the board specific DAPM widgets to a random CODEC DAPM context they can be added to the card itself which is a bit cleaner. Previously there only was one DAPM context and it was tied to the single supported CODEC. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 3 +++ sound/soc/soc-core.c | 13 +++++++++++++ 2 files changed, 16 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 65d865f7e8c0..8064cd130356 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -729,6 +729,9 @@ struct snd_soc_card { struct list_head paths; struct list_head dapm_list; + /* Generic DAPM context for the card */ + struct snd_soc_dapm_context dapm; + #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_card_root; struct dentry *debugfs_pop_time; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 64befac3f9c3..24bfc3ff8e17 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1837,6 +1837,11 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) } card->snd_card->dev = card->dev; + card->dapm.bias_level = SND_SOC_BIAS_OFF; + card->dapm.dev = card->dev; + card->dapm.card = card; + list_add(&card->dapm.list, &card->dapm_list); + #ifdef CONFIG_PM_SLEEP /* deferred resume work */ INIT_WORK(&card->deferred_resume_work, soc_resume_deferred); @@ -1867,6 +1872,14 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) } } + card->dapm.debugfs_dapm = debugfs_create_dir("dapm", + card->debugfs_card_root); + if (!card->dapm.debugfs_dapm) + printk(KERN_WARNING + "Failed to create card DAPM debugfs directory\n"); + + snd_soc_dapm_debugfs_init(&card->dapm); + snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname), "%s", card->name); snprintf(card->snd_card->longname, sizeof(card->snd_card->longname), -- cgit v1.2.3-59-g8ed1b From b8ad29debd7401d257da923480d32838172c431a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Mar 2011 18:35:51 +0000 Subject: ASoC: Allow card DAPM widgets and routes to be set up at registration These will be added after all devices are registered and allow most DAI init functions in machine drivers to be replaced by simple data. Regular controls are not supported as the registration function still works in terms of CODECs. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 8 ++++++++ sound/soc/soc-core.c | 7 +++++++ 2 files changed, 15 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 8064cd130356..11d59bd13886 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -718,6 +718,14 @@ struct snd_soc_card { struct snd_soc_pcm_runtime *rtd_aux; int num_aux_rtd; + /* + * Card-specific routes and widgets. + */ + struct snd_soc_dapm_widget *dapm_widgets; + int num_dapm_widgets; + struct snd_soc_dapm_route *dapm_routes; + int num_dapm_routes; + struct work_struct deferred_resume_work; /* lists of probed devices belonging to this card */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 24bfc3ff8e17..6a2839c18447 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1872,6 +1872,13 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) } } + if (card->dapm_widgets) + snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets, + card->num_dapm_widgets); + if (card->dapm_routes) + snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, + card->num_dapm_routes); + card->dapm.debugfs_dapm = debugfs_create_dir("dapm", card->debugfs_card_root); if (!card->dapm.debugfs_dapm) -- cgit v1.2.3-59-g8ed1b From 28e9ad921d3b7defd8940a3e30e8241c8ed734db Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Mar 2011 18:36:34 +0000 Subject: ASoC: Add a late_probe() callback to cards This is run after the DAPM widgets and routes are added, allowing setup of things like jacks using the routes. The main card probe() is run before anything else so can't be used for this purpose. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 1 + sound/soc/soc-core.c | 9 +++++++++ 2 files changed, 10 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 11d59bd13886..9c2a6dd170f1 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -682,6 +682,7 @@ struct snd_soc_card { bool instantiated; int (*probe)(struct snd_soc_card *card); + int (*late_probe)(struct snd_soc_card *card); int (*remove)(struct snd_soc_card *card); /* the pre and post PM functions are used to do any PM work before and diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 6a2839c18447..8926d38fc5a3 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1892,6 +1892,15 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) snprintf(card->snd_card->longname, sizeof(card->snd_card->longname), "%s", card->name); + if (card->late_probe) { + ret = card->late_probe(card); + if (ret < 0) { + dev_err(card->dev, "%s late_probe() failed: %d\n", + card->name, ret); + goto probe_aux_dev_err; + } + } + ret = snd_card_register(card->snd_card); if (ret < 0) { printk(KERN_ERR "asoc: failed to register soundcard for %s\n", card->name); -- cgit v1.2.3-59-g8ed1b From 1d471cd1261a44a3b28350bef7e5113a4609c106 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Wed, 2 Mar 2011 14:52:32 +0100 Subject: ASoC: Add TI tlv320aic32x4 codec support. This patch adds support for tlv320aic3205 and tlv320aic3254 codecs. It doesn't include miniDSP support for aic3254. Signed-off-by: Javier Martin Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/tlv320aic32x4.h | 31 ++ sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/tlv320aic32x4.c | 794 +++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/tlv320aic32x4.h | 143 +++++++ 5 files changed, 974 insertions(+) create mode 100644 include/sound/tlv320aic32x4.h create mode 100644 sound/soc/codecs/tlv320aic32x4.c create mode 100644 sound/soc/codecs/tlv320aic32x4.h (limited to 'include') diff --git a/include/sound/tlv320aic32x4.h b/include/sound/tlv320aic32x4.h new file mode 100644 index 000000000000..c009f70b4029 --- /dev/null +++ b/include/sound/tlv320aic32x4.h @@ -0,0 +1,31 @@ +/* + * tlv320aic32x4.h -- TLV320AIC32X4 Soc Audio driver platform data + * + * Copyright 2011 Vista Silicon S.L. + * + * Author: Javier Martin + * + * 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 _AIC32X4_PDATA_H +#define _AIC32X4_PDATA_H + +#define AIC32X4_PWR_MICBIAS_2075_LDOIN 0x00000001 +#define AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE 0x00000002 +#define AIC32X4_PWR_AIC32X4_LDO_ENABLE 0x00000004 +#define AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36 0x00000008 +#define AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED 0x00000010 + +#define AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K 0x00000001 +#define AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K 0x00000002 + +struct aic32x4_pdata { + u32 power_cfg; + u32 micpga_routing; + bool swapdacs; +}; + +#endif diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index c04da1871297..82a46309ded6 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -40,6 +40,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_STAC9766 if SND_SOC_AC97_BUS select SND_SOC_TLV320AIC23 if I2C select SND_SOC_TLV320AIC26 if SPI_MASTER + select SND_SOC_TVL320AIC32X4 if I2C select SND_SOC_TLV320AIC3X if I2C select SND_SOC_TPA6130A2 if I2C select SND_SOC_TLV320DAC33 if I2C @@ -206,6 +207,9 @@ config SND_SOC_TLV320AIC26 tristate "TI TLV320AIC26 Codec support" if SND_SOC_OF_SIMPLE depends on SPI +config SND_SOC_TVL320AIC32X4 + tristate + config SND_SOC_TLV320AIC3X tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 3bbb08c512d0..b43f9d418c9b 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -28,6 +28,7 @@ snd-soc-stac9766-objs := stac9766.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic26-objs := tlv320aic26.o snd-soc-tlv320aic3x-objs := tlv320aic3x.o +snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o snd-soc-tlv320dac33-objs := tlv320dac33.o snd-soc-twl4030-objs := twl4030.o snd-soc-twl6040-objs := twl6040.o @@ -112,6 +113,7 @@ obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o +obj-$(CONFIG_SND_SOC_TVL320AIC32X4) += snd-soc-tlv320aic32x4.o obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c new file mode 100644 index 000000000000..ee82e3896039 --- /dev/null +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -0,0 +1,794 @@ +/* + * linux/sound/soc/codecs/tlv320aic32x4.c + * + * Copyright 2011 Vista Silicon S.L. + * + * Author: Javier Martin + * + * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tlv320aic32x4.h" + +struct aic32x4_rate_divs { + u32 mclk; + u32 rate; + u8 p_val; + u8 pll_j; + u16 pll_d; + u16 dosr; + u8 ndac; + u8 mdac; + u8 aosr; + u8 nadc; + u8 madc; + u8 blck_N; +}; + +struct aic32x4_priv { + u32 sysclk; + s32 master; + u8 page_no; + void *control_data; + u32 power_cfg; + u32 micpga_routing; + bool swapdacs; +}; + +/* 0dB min, 1dB steps */ +static DECLARE_TLV_DB_SCALE(tlv_step_1, 0, 100, 0); +/* 0dB min, 0.5dB steps */ +static DECLARE_TLV_DB_SCALE(tlv_step_0_5, 0, 50, 0); + +static const struct snd_kcontrol_new aic32x4_snd_controls[] = { + SOC_DOUBLE_R_TLV("PCM Playback Volume", AIC32X4_LDACVOL, + AIC32X4_RDACVOL, 0, 0x30, 0, tlv_step_0_5), + SOC_DOUBLE_R_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN, + AIC32X4_HPRGAIN, 0, 0x1D, 0, tlv_step_1), + SOC_DOUBLE_R_TLV("LO Driver Gain Volume", AIC32X4_LOLGAIN, + AIC32X4_LORGAIN, 0, 0x1D, 0, tlv_step_1), + SOC_DOUBLE_R("HP DAC Playback Switch", AIC32X4_HPLGAIN, + AIC32X4_HPRGAIN, 6, 0x01, 1), + SOC_DOUBLE_R("LO DAC Playback Switch", AIC32X4_LOLGAIN, + AIC32X4_LORGAIN, 6, 0x01, 1), + SOC_DOUBLE_R("Mic PGA Switch", AIC32X4_LMICPGAVOL, + AIC32X4_RMICPGAVOL, 7, 0x01, 1), + + SOC_SINGLE("ADCFGA Left Mute Switch", AIC32X4_ADCFGA, 7, 1, 0), + SOC_SINGLE("ADCFGA Right Mute Switch", AIC32X4_ADCFGA, 3, 1, 0), + + SOC_DOUBLE_R_TLV("ADC Level Volume", AIC32X4_LADCVOL, + AIC32X4_RADCVOL, 0, 0x28, 0, tlv_step_0_5), + SOC_DOUBLE_R_TLV("PGA Level Volume", AIC32X4_LMICPGAVOL, + AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5), + + SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0), + + SOC_SINGLE("AGC Left Switch", AIC32X4_LAGC1, 7, 1, 0), + SOC_SINGLE("AGC Right Switch", AIC32X4_RAGC1, 7, 1, 0), + SOC_DOUBLE_R("AGC Target Level", AIC32X4_LAGC1, AIC32X4_RAGC1, + 4, 0x07, 0), + SOC_DOUBLE_R("AGC Gain Hysteresis", AIC32X4_LAGC1, AIC32X4_RAGC1, + 0, 0x03, 0), + SOC_DOUBLE_R("AGC Hysteresis", AIC32X4_LAGC2, AIC32X4_RAGC2, + 6, 0x03, 0), + SOC_DOUBLE_R("AGC Noise Threshold", AIC32X4_LAGC2, AIC32X4_RAGC2, + 1, 0x1F, 0), + SOC_DOUBLE_R("AGC Max PGA", AIC32X4_LAGC3, AIC32X4_RAGC3, + 0, 0x7F, 0), + SOC_DOUBLE_R("AGC Attack Time", AIC32X4_LAGC4, AIC32X4_RAGC4, + 3, 0x1F, 0), + SOC_DOUBLE_R("AGC Decay Time", AIC32X4_LAGC5, AIC32X4_RAGC5, + 3, 0x1F, 0), + SOC_DOUBLE_R("AGC Noise Debounce", AIC32X4_LAGC6, AIC32X4_RAGC6, + 0, 0x1F, 0), + SOC_DOUBLE_R("AGC Signal Debounce", AIC32X4_LAGC7, AIC32X4_RAGC7, + 0, 0x0F, 0), +}; + +static const struct aic32x4_rate_divs aic32x4_divs[] = { + /* 8k rate */ + {AIC32X4_FREQ_12000000, 8000, 1, 7, 6800, 768, 5, 3, 128, 5, 18, 24}, + {AIC32X4_FREQ_24000000, 8000, 2, 7, 6800, 768, 15, 1, 64, 45, 4, 24}, + {AIC32X4_FREQ_25000000, 8000, 2, 7, 3728, 768, 15, 1, 64, 45, 4, 24}, + /* 11.025k rate */ + {AIC32X4_FREQ_12000000, 11025, 1, 7, 5264, 512, 8, 2, 128, 8, 8, 16}, + {AIC32X4_FREQ_24000000, 11025, 2, 7, 5264, 512, 16, 1, 64, 32, 4, 16}, + /* 16k rate */ + {AIC32X4_FREQ_12000000, 16000, 1, 7, 6800, 384, 5, 3, 128, 5, 9, 12}, + {AIC32X4_FREQ_24000000, 16000, 2, 7, 6800, 384, 15, 1, 64, 18, 5, 12}, + {AIC32X4_FREQ_25000000, 16000, 2, 7, 3728, 384, 15, 1, 64, 18, 5, 12}, + /* 22.05k rate */ + {AIC32X4_FREQ_12000000, 22050, 1, 7, 5264, 256, 4, 4, 128, 4, 8, 8}, + {AIC32X4_FREQ_24000000, 22050, 2, 7, 5264, 256, 16, 1, 64, 16, 4, 8}, + {AIC32X4_FREQ_25000000, 22050, 2, 7, 2253, 256, 16, 1, 64, 16, 4, 8}, + /* 32k rate */ + {AIC32X4_FREQ_12000000, 32000, 1, 7, 1680, 192, 2, 7, 64, 2, 21, 6}, + {AIC32X4_FREQ_24000000, 32000, 2, 7, 1680, 192, 7, 2, 64, 7, 6, 6}, + /* 44.1k rate */ + {AIC32X4_FREQ_12000000, 44100, 1, 7, 5264, 128, 2, 8, 128, 2, 8, 4}, + {AIC32X4_FREQ_24000000, 44100, 2, 7, 5264, 128, 8, 2, 64, 8, 4, 4}, + {AIC32X4_FREQ_25000000, 44100, 2, 7, 2253, 128, 8, 2, 64, 8, 4, 4}, + /* 48k rate */ + {AIC32X4_FREQ_12000000, 48000, 1, 8, 1920, 128, 2, 8, 128, 2, 8, 4}, + {AIC32X4_FREQ_24000000, 48000, 2, 8, 1920, 128, 8, 2, 64, 8, 4, 4}, + {AIC32X4_FREQ_25000000, 48000, 2, 7, 8643, 128, 8, 2, 64, 8, 4, 4} +}; + +static const struct snd_kcontrol_new hpl_output_mixer_controls[] = { + SOC_DAPM_SINGLE("L_DAC Switch", AIC32X4_HPLROUTE, 3, 1, 0), + SOC_DAPM_SINGLE("IN1_L Switch", AIC32X4_HPLROUTE, 2, 1, 0), +}; + +static const struct snd_kcontrol_new hpr_output_mixer_controls[] = { + SOC_DAPM_SINGLE("R_DAC Switch", AIC32X4_HPRROUTE, 3, 1, 0), + SOC_DAPM_SINGLE("IN1_R Switch", AIC32X4_HPRROUTE, 2, 1, 0), +}; + +static const struct snd_kcontrol_new lol_output_mixer_controls[] = { + SOC_DAPM_SINGLE("L_DAC Switch", AIC32X4_LOLROUTE, 3, 1, 0), +}; + +static const struct snd_kcontrol_new lor_output_mixer_controls[] = { + SOC_DAPM_SINGLE("R_DAC Switch", AIC32X4_LORROUTE, 3, 1, 0), +}; + +static const struct snd_kcontrol_new left_input_mixer_controls[] = { + SOC_DAPM_SINGLE("IN1_L P Switch", AIC32X4_LMICPGAPIN, 6, 1, 0), + SOC_DAPM_SINGLE("IN2_L P Switch", AIC32X4_LMICPGAPIN, 4, 1, 0), + SOC_DAPM_SINGLE("IN3_L P Switch", AIC32X4_LMICPGAPIN, 2, 1, 0), +}; + +static const struct snd_kcontrol_new right_input_mixer_controls[] = { + SOC_DAPM_SINGLE("IN1_R P Switch", AIC32X4_RMICPGAPIN, 6, 1, 0), + SOC_DAPM_SINGLE("IN2_R P Switch", AIC32X4_RMICPGAPIN, 4, 1, 0), + SOC_DAPM_SINGLE("IN3_R P Switch", AIC32X4_RMICPGAPIN, 2, 1, 0), +}; + +static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = { + SND_SOC_DAPM_DAC("Left DAC", "Left Playback", AIC32X4_DACSETUP, 7, 0), + SND_SOC_DAPM_MIXER("HPL Output Mixer", SND_SOC_NOPM, 0, 0, + &hpl_output_mixer_controls[0], + ARRAY_SIZE(hpl_output_mixer_controls)), + SND_SOC_DAPM_PGA("HPL Power", AIC32X4_OUTPWRCTL, 5, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("LOL Output Mixer", SND_SOC_NOPM, 0, 0, + &lol_output_mixer_controls[0], + ARRAY_SIZE(lol_output_mixer_controls)), + SND_SOC_DAPM_PGA("LOL Power", AIC32X4_OUTPWRCTL, 3, 0, NULL, 0), + + SND_SOC_DAPM_DAC("Right DAC", "Right Playback", AIC32X4_DACSETUP, 6, 0), + SND_SOC_DAPM_MIXER("HPR Output Mixer", SND_SOC_NOPM, 0, 0, + &hpr_output_mixer_controls[0], + ARRAY_SIZE(hpr_output_mixer_controls)), + SND_SOC_DAPM_PGA("HPR Power", AIC32X4_OUTPWRCTL, 4, 0, NULL, 0), + SND_SOC_DAPM_MIXER("LOR Output Mixer", SND_SOC_NOPM, 0, 0, + &lor_output_mixer_controls[0], + ARRAY_SIZE(lor_output_mixer_controls)), + SND_SOC_DAPM_PGA("LOR Power", AIC32X4_OUTPWRCTL, 2, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Left Input Mixer", SND_SOC_NOPM, 0, 0, + &left_input_mixer_controls[0], + ARRAY_SIZE(left_input_mixer_controls)), + SND_SOC_DAPM_MIXER("Right Input Mixer", SND_SOC_NOPM, 0, 0, + &right_input_mixer_controls[0], + ARRAY_SIZE(right_input_mixer_controls)), + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", AIC32X4_ADCSETUP, 7, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", AIC32X4_ADCSETUP, 6, 0), + SND_SOC_DAPM_MICBIAS("Mic Bias", AIC32X4_MICBIAS, 6, 0), + + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("LOL"), + SND_SOC_DAPM_OUTPUT("LOR"), + SND_SOC_DAPM_INPUT("IN1_L"), + SND_SOC_DAPM_INPUT("IN1_R"), + SND_SOC_DAPM_INPUT("IN2_L"), + SND_SOC_DAPM_INPUT("IN2_R"), + SND_SOC_DAPM_INPUT("IN3_L"), + SND_SOC_DAPM_INPUT("IN3_R"), +}; + +static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = { + /* Left Output */ + {"HPL Output Mixer", "L_DAC Switch", "Left DAC"}, + {"HPL Output Mixer", "IN1_L Switch", "IN1_L"}, + + {"HPL Power", NULL, "HPL Output Mixer"}, + {"HPL", NULL, "HPL Power"}, + + {"LOL Output Mixer", "L_DAC Switch", "Left DAC"}, + + {"LOL Power", NULL, "LOL Output Mixer"}, + {"LOL", NULL, "LOL Power"}, + + /* Right Output */ + {"HPR Output Mixer", "R_DAC Switch", "Right DAC"}, + {"HPR Output Mixer", "IN1_R Switch", "IN1_R"}, + + {"HPR Power", NULL, "HPR Output Mixer"}, + {"HPR", NULL, "HPR Power"}, + + {"LOR Output Mixer", "R_DAC Switch", "Right DAC"}, + + {"LOR Power", NULL, "LOR Output Mixer"}, + {"LOR", NULL, "LOR Power"}, + + /* Left input */ + {"Left Input Mixer", "IN1_L P Switch", "IN1_L"}, + {"Left Input Mixer", "IN2_L P Switch", "IN2_L"}, + {"Left Input Mixer", "IN3_L P Switch", "IN3_L"}, + + {"Left ADC", NULL, "Left Input Mixer"}, + + /* Right Input */ + {"Right Input Mixer", "IN1_R P Switch", "IN1_R"}, + {"Right Input Mixer", "IN2_R P Switch", "IN2_R"}, + {"Right Input Mixer", "IN3_R P Switch", "IN3_R"}, + + {"Right ADC", NULL, "Right Input Mixer"}, +}; + +static inline int aic32x4_change_page(struct snd_soc_codec *codec, + unsigned int new_page) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u8 data[2]; + int ret; + + data[0] = 0x00; + data[1] = new_page & 0xff; + + ret = codec->hw_write(codec->control_data, data, 2); + if (ret == 2) { + aic32x4->page_no = new_page; + return 0; + } else { + return ret; + } +} + +static int aic32x4_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int val) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + unsigned int page = reg / 128; + unsigned int fixed_reg = reg % 128; + u8 data[2]; + int ret; + + /* A write to AIC32X4_PSEL is really a non-explicit page change */ + if (reg == AIC32X4_PSEL) + return aic32x4_change_page(codec, val); + + if (aic32x4->page_no != page) { + ret = aic32x4_change_page(codec, page); + if (ret != 0) + return ret; + } + + data[0] = fixed_reg & 0xff; + data[1] = val & 0xff; + + if (codec->hw_write(codec->control_data, data, 2) == 2) + return 0; + else + return -EIO; +} + +static unsigned int aic32x4_read(struct snd_soc_codec *codec, unsigned int reg) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + unsigned int page = reg / 128; + unsigned int fixed_reg = reg % 128; + int ret; + + if (aic32x4->page_no != page) { + ret = aic32x4_change_page(codec, page); + if (ret != 0) + return ret; + } + return i2c_smbus_read_byte_data(codec->control_data, fixed_reg & 0xff); +} + +static inline int aic32x4_get_divs(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(aic32x4_divs); i++) { + if ((aic32x4_divs[i].rate == rate) + && (aic32x4_divs[i].mclk == mclk)) { + return i; + } + } + printk(KERN_ERR "aic32x4: master clock and sample rate is not supported\n"); + return -EINVAL; +} + +static int aic32x4_add_widgets(struct snd_soc_codec *codec) +{ + snd_soc_dapm_new_controls(codec, aic32x4_dapm_widgets, + ARRAY_SIZE(aic32x4_dapm_widgets)); + + snd_soc_dapm_add_routes(codec, aic32x4_dapm_routes, + ARRAY_SIZE(aic32x4_dapm_routes)); + + snd_soc_dapm_new_widgets(codec); + return 0; +} + +static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + + switch (freq) { + case AIC32X4_FREQ_12000000: + case AIC32X4_FREQ_24000000: + case AIC32X4_FREQ_25000000: + aic32x4->sysclk = freq; + return 0; + } + printk(KERN_ERR "aic32x4: invalid frequency to set DAI system clock\n"); + return -EINVAL; +} + +static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u8 iface_reg_1; + u8 iface_reg_2; + u8 iface_reg_3; + + iface_reg_1 = snd_soc_read(codec, AIC32X4_IFACE1); + iface_reg_1 = iface_reg_1 & ~(3 << 6 | 3 << 2); + iface_reg_2 = snd_soc_read(codec, AIC32X4_IFACE2); + iface_reg_2 = 0; + iface_reg_3 = snd_soc_read(codec, AIC32X4_IFACE3); + iface_reg_3 = iface_reg_3 & ~(1 << 3); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + aic32x4->master = 1; + iface_reg_1 |= AIC32X4_BCLKMASTER | AIC32X4_WCLKMASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + aic32x4->master = 0; + break; + default: + printk(KERN_ERR "aic32x4: invalid DAI master/slave interface\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_DSP_A: + iface_reg_1 |= (AIC32X4_DSP_MODE << AIC32X4_PLLJ_SHIFT); + iface_reg_3 |= (1 << 3); /* invert bit clock */ + iface_reg_2 = 0x01; /* add offset 1 */ + break; + case SND_SOC_DAIFMT_DSP_B: + iface_reg_1 |= (AIC32X4_DSP_MODE << AIC32X4_PLLJ_SHIFT); + iface_reg_3 |= (1 << 3); /* invert bit clock */ + break; + case SND_SOC_DAIFMT_RIGHT_J: + iface_reg_1 |= + (AIC32X4_RIGHT_JUSTIFIED_MODE << AIC32X4_PLLJ_SHIFT); + break; + case SND_SOC_DAIFMT_LEFT_J: + iface_reg_1 |= + (AIC32X4_LEFT_JUSTIFIED_MODE << AIC32X4_PLLJ_SHIFT); + break; + default: + printk(KERN_ERR "aic32x4: invalid DAI interface format\n"); + return -EINVAL; + } + + snd_soc_write(codec, AIC32X4_IFACE1, iface_reg_1); + snd_soc_write(codec, AIC32X4_IFACE2, iface_reg_2); + snd_soc_write(codec, AIC32X4_IFACE3, iface_reg_3); + return 0; +} + +static int aic32x4_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u8 data; + int i; + + i = aic32x4_get_divs(aic32x4->sysclk, params_rate(params)); + if (i < 0) { + printk(KERN_ERR "aic32x4: sampling rate not supported\n"); + return i; + } + + /* Use PLL as CODEC_CLKIN and DAC_MOD_CLK as BDIV_CLKIN */ + snd_soc_write(codec, AIC32X4_CLKMUX, AIC32X4_PLLCLKIN); + snd_soc_write(codec, AIC32X4_IFACE3, AIC32X4_DACMOD2BCLK); + + /* We will fix R value to 1 and will make P & J=K.D as varialble */ + data = snd_soc_read(codec, AIC32X4_PLLPR); + data &= ~(7 << 4); + snd_soc_write(codec, AIC32X4_PLLPR, + (data | (aic32x4_divs[i].p_val << 4) | 0x01)); + + snd_soc_write(codec, AIC32X4_PLLJ, aic32x4_divs[i].pll_j); + + snd_soc_write(codec, AIC32X4_PLLDMSB, (aic32x4_divs[i].pll_d >> 8)); + snd_soc_write(codec, AIC32X4_PLLDLSB, + (aic32x4_divs[i].pll_d & 0xff)); + + /* NDAC divider value */ + data = snd_soc_read(codec, AIC32X4_NDAC); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_NDAC, data | aic32x4_divs[i].ndac); + + /* MDAC divider value */ + data = snd_soc_read(codec, AIC32X4_MDAC); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_MDAC, data | aic32x4_divs[i].mdac); + + /* DOSR MSB & LSB values */ + snd_soc_write(codec, AIC32X4_DOSRMSB, aic32x4_divs[i].dosr >> 8); + snd_soc_write(codec, AIC32X4_DOSRLSB, + (aic32x4_divs[i].dosr & 0xff)); + + /* NADC divider value */ + data = snd_soc_read(codec, AIC32X4_NADC); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_NADC, data | aic32x4_divs[i].nadc); + + /* MADC divider value */ + data = snd_soc_read(codec, AIC32X4_MADC); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_MADC, data | aic32x4_divs[i].madc); + + /* AOSR value */ + snd_soc_write(codec, AIC32X4_AOSR, aic32x4_divs[i].aosr); + + /* BCLK N divider */ + data = snd_soc_read(codec, AIC32X4_BCLKN); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_BCLKN, data | aic32x4_divs[i].blck_N); + + data = snd_soc_read(codec, AIC32X4_IFACE1); + data = data & ~(3 << 4); + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + data |= (AIC32X4_WORD_LEN_20BITS << AIC32X4_DOSRMSB_SHIFT); + break; + case SNDRV_PCM_FORMAT_S24_LE: + data |= (AIC32X4_WORD_LEN_24BITS << AIC32X4_DOSRMSB_SHIFT); + break; + case SNDRV_PCM_FORMAT_S32_LE: + data |= (AIC32X4_WORD_LEN_32BITS << AIC32X4_DOSRMSB_SHIFT); + break; + } + snd_soc_write(codec, AIC32X4_IFACE1, data); + + return 0; +} + +static int aic32x4_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + u8 dac_reg; + + dac_reg = snd_soc_read(codec, AIC32X4_DACMUTE) & ~AIC32X4_MUTEON; + if (mute) + snd_soc_write(codec, AIC32X4_DACMUTE, dac_reg | AIC32X4_MUTEON); + else + snd_soc_write(codec, AIC32X4_DACMUTE, dac_reg); + return 0; +} + +static int aic32x4_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u8 value; + + switch (level) { + case SND_SOC_BIAS_ON: + if (aic32x4->master) { + /* Switch on PLL */ + value = snd_soc_read(codec, AIC32X4_PLLPR); + snd_soc_write(codec, AIC32X4_PLLPR, + (value | AIC32X4_PLLEN)); + + /* Switch on NDAC Divider */ + value = snd_soc_read(codec, AIC32X4_NDAC); + snd_soc_write(codec, AIC32X4_NDAC, + value | AIC32X4_NDACEN); + + /* Switch on MDAC Divider */ + value = snd_soc_read(codec, AIC32X4_MDAC); + snd_soc_write(codec, AIC32X4_MDAC, + value | AIC32X4_MDACEN); + + /* Switch on NADC Divider */ + value = snd_soc_read(codec, AIC32X4_NADC); + snd_soc_write(codec, AIC32X4_NADC, + value | AIC32X4_MDACEN); + + /* Switch on MADC Divider */ + value = snd_soc_read(codec, AIC32X4_MADC); + snd_soc_write(codec, AIC32X4_MADC, + value | AIC32X4_MDACEN); + + /* Switch on BCLK_N Divider */ + value = snd_soc_read(codec, AIC32X4_BCLKN); + snd_soc_write(codec, AIC32X4_BCLKN, + value | AIC32X4_BCLKEN); + } + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (aic32x4->master) { + /* Switch off PLL */ + value = snd_soc_read(codec, AIC32X4_PLLPR); + snd_soc_write(codec, AIC32X4_PLLPR, + (value & ~AIC32X4_PLLEN)); + + /* Switch off NDAC Divider */ + value = snd_soc_read(codec, AIC32X4_NDAC); + snd_soc_write(codec, AIC32X4_NDAC, + value & ~AIC32X4_NDACEN); + + /* Switch off MDAC Divider */ + value = snd_soc_read(codec, AIC32X4_MDAC); + snd_soc_write(codec, AIC32X4_MDAC, + value & ~AIC32X4_MDACEN); + + /* Switch off NADC Divider */ + value = snd_soc_read(codec, AIC32X4_NADC); + snd_soc_write(codec, AIC32X4_NADC, + value & ~AIC32X4_NDACEN); + + /* Switch off MADC Divider */ + value = snd_soc_read(codec, AIC32X4_MADC); + snd_soc_write(codec, AIC32X4_MADC, + value & ~AIC32X4_MDACEN); + value = snd_soc_read(codec, AIC32X4_BCLKN); + + /* Switch off BCLK_N Divider */ + snd_soc_write(codec, AIC32X4_BCLKN, + value & ~AIC32X4_BCLKEN); + } + break; + case SND_SOC_BIAS_OFF: + break; + } + codec->bias_level = level; + return 0; +} + +#define AIC32X4_RATES SNDRV_PCM_RATE_8000_48000 +#define AIC32X4_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ + | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_ops aic32x4_ops = { + .hw_params = aic32x4_hw_params, + .digital_mute = aic32x4_mute, + .set_fmt = aic32x4_set_dai_fmt, + .set_sysclk = aic32x4_set_dai_sysclk, +}; + +static struct snd_soc_dai_driver aic32x4_dai = { + .name = "tlv320aic32x4-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AIC32X4_RATES, + .formats = AIC32X4_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AIC32X4_RATES, + .formats = AIC32X4_FORMATS,}, + .ops = &aic32x4_ops, + .symmetric_rates = 1, +}; + +static int aic32x4_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + aic32x4_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int aic32x4_resume(struct snd_soc_codec *codec) +{ + aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + return 0; +} + +static int aic32x4_probe(struct snd_soc_codec *codec) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u32 tmp_reg; + + codec->hw_write = (hw_write_t) i2c_master_send; + codec->control_data = aic32x4->control_data; + + snd_soc_write(codec, AIC32X4_RESET, 0x01); + + /* Power platform configuration */ + if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) { + snd_soc_write(codec, AIC32X4_MICBIAS, AIC32X4_MICBIAS_LDOIN | + AIC32X4_MICBIAS_2075V); + } + if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE) { + snd_soc_write(codec, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE); + } + if (aic32x4->power_cfg & AIC32X4_PWR_AIC32X4_LDO_ENABLE) { + snd_soc_write(codec, AIC32X4_LDOCTL, AIC32X4_LDOCTLEN); + } + tmp_reg = snd_soc_read(codec, AIC32X4_CMMODE); + if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36) { + tmp_reg |= AIC32X4_LDOIN_18_36; + } + if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED) { + tmp_reg |= AIC32X4_LDOIN2HP; + } + snd_soc_write(codec, AIC32X4_CMMODE, tmp_reg); + + /* Do DACs need to be swapped? */ + if (aic32x4->swapdacs) { + snd_soc_write(codec, AIC32X4_DACSETUP, AIC32X4_LDAC2RCHN | AIC32X4_RDAC2LCHN); + } else { + snd_soc_write(codec, AIC32X4_DACSETUP, AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN); + } + + /* Mic PGA routing */ + if (aic32x4->micpga_routing | AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K) { + snd_soc_write(codec, AIC32X4_LMICPGANIN, AIC32X4_LMICPGANIN_IN2R_10K); + } + if (aic32x4->micpga_routing | AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K) { + snd_soc_write(codec, AIC32X4_RMICPGANIN, AIC32X4_RMICPGANIN_IN1L_10K); + } + + aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_add_controls(codec, aic32x4_snd_controls, + ARRAY_SIZE(aic32x4_snd_controls)); + aic32x4_add_widgets(codec); + + return 0; +} + +static int aic32x4_remove(struct snd_soc_codec *codec) +{ + aic32x4_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = { + .read = aic32x4_read, + .write = aic32x4_write, + .probe = aic32x4_probe, + .remove = aic32x4_remove, + .suspend = aic32x4_suspend, + .resume = aic32x4_resume, + .set_bias_level = aic32x4_set_bias_level, +}; + +static __devinit int aic32x4_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct aic32x4_pdata *pdata = i2c->dev.platform_data; + struct aic32x4_priv *aic32x4; + int ret; + + aic32x4 = kzalloc(sizeof(struct aic32x4_priv), GFP_KERNEL); + if (aic32x4 == NULL) + return -ENOMEM; + + aic32x4->control_data = i2c; + i2c_set_clientdata(i2c, aic32x4); + + if (pdata) { + aic32x4->power_cfg = pdata->power_cfg; + aic32x4->swapdacs = pdata->swapdacs; + aic32x4->micpga_routing = pdata->micpga_routing; + } else { + aic32x4->power_cfg = 0; + aic32x4->swapdacs = false; + aic32x4->micpga_routing = 0; + } + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_aic32x4, &aic32x4_dai, 1); + if (ret < 0) + kfree(aic32x4); + return ret; +} + +static __devexit int aic32x4_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + kfree(i2c_get_clientdata(client)); + return 0; +} + +static const struct i2c_device_id aic32x4_i2c_id[] = { + { "tlv320aic32x4", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id); + +static struct i2c_driver aic32x4_i2c_driver = { + .driver = { + .name = "tlv320aic32x4", + .owner = THIS_MODULE, + }, + .probe = aic32x4_i2c_probe, + .remove = __devexit_p(aic32x4_i2c_remove), + .id_table = aic32x4_i2c_id, +}; + +static int __init aic32x4_modinit(void) +{ + int ret = 0; + + ret = i2c_add_driver(&aic32x4_i2c_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register aic32x4 I2C driver: %d\n", + ret); + } + return ret; +} +module_init(aic32x4_modinit); + +static void __exit aic32x4_exit(void) +{ + i2c_del_driver(&aic32x4_i2c_driver); +} +module_exit(aic32x4_exit); + +MODULE_DESCRIPTION("ASoC tlv320aic32x4 codec driver"); +MODULE_AUTHOR("Javier Martin "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h new file mode 100644 index 000000000000..aae2b2440398 --- /dev/null +++ b/sound/soc/codecs/tlv320aic32x4.h @@ -0,0 +1,143 @@ +/* + * tlv320aic32x4.h + * + * 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 _TLV320AIC32X4_H +#define _TLV320AIC32X4_H + +/* tlv320aic32x4 register space (in decimal to match datasheet) */ + +#define AIC32X4_PAGE1 128 + +#define AIC32X4_PSEL 0 +#define AIC32X4_RESET 1 +#define AIC32X4_CLKMUX 4 +#define AIC32X4_PLLPR 5 +#define AIC32X4_PLLJ 6 +#define AIC32X4_PLLDMSB 7 +#define AIC32X4_PLLDLSB 8 +#define AIC32X4_NDAC 11 +#define AIC32X4_MDAC 12 +#define AIC32X4_DOSRMSB 13 +#define AIC32X4_DOSRLSB 14 +#define AIC32X4_NADC 18 +#define AIC32X4_MADC 19 +#define AIC32X4_AOSR 20 +#define AIC32X4_CLKMUX2 25 +#define AIC32X4_CLKOUTM 26 +#define AIC32X4_IFACE1 27 +#define AIC32X4_IFACE2 28 +#define AIC32X4_IFACE3 29 +#define AIC32X4_BCLKN 30 +#define AIC32X4_IFACE4 31 +#define AIC32X4_IFACE5 32 +#define AIC32X4_IFACE6 33 +#define AIC32X4_DOUTCTL 53 +#define AIC32X4_DINCTL 54 +#define AIC32X4_DACSPB 60 +#define AIC32X4_ADCSPB 61 +#define AIC32X4_DACSETUP 63 +#define AIC32X4_DACMUTE 64 +#define AIC32X4_LDACVOL 65 +#define AIC32X4_RDACVOL 66 +#define AIC32X4_ADCSETUP 81 +#define AIC32X4_ADCFGA 82 +#define AIC32X4_LADCVOL 83 +#define AIC32X4_RADCVOL 84 +#define AIC32X4_LAGC1 86 +#define AIC32X4_LAGC2 87 +#define AIC32X4_LAGC3 88 +#define AIC32X4_LAGC4 89 +#define AIC32X4_LAGC5 90 +#define AIC32X4_LAGC6 91 +#define AIC32X4_LAGC7 92 +#define AIC32X4_RAGC1 94 +#define AIC32X4_RAGC2 95 +#define AIC32X4_RAGC3 96 +#define AIC32X4_RAGC4 97 +#define AIC32X4_RAGC5 98 +#define AIC32X4_RAGC6 99 +#define AIC32X4_RAGC7 100 +#define AIC32X4_PWRCFG (AIC32X4_PAGE1 + 1) +#define AIC32X4_LDOCTL (AIC32X4_PAGE1 + 2) +#define AIC32X4_OUTPWRCTL (AIC32X4_PAGE1 + 9) +#define AIC32X4_CMMODE (AIC32X4_PAGE1 + 10) +#define AIC32X4_HPLROUTE (AIC32X4_PAGE1 + 12) +#define AIC32X4_HPRROUTE (AIC32X4_PAGE1 + 13) +#define AIC32X4_LOLROUTE (AIC32X4_PAGE1 + 14) +#define AIC32X4_LORROUTE (AIC32X4_PAGE1 + 15) +#define AIC32X4_HPLGAIN (AIC32X4_PAGE1 + 16) +#define AIC32X4_HPRGAIN (AIC32X4_PAGE1 + 17) +#define AIC32X4_LOLGAIN (AIC32X4_PAGE1 + 18) +#define AIC32X4_LORGAIN (AIC32X4_PAGE1 + 19) +#define AIC32X4_HEADSTART (AIC32X4_PAGE1 + 20) +#define AIC32X4_MICBIAS (AIC32X4_PAGE1 + 51) +#define AIC32X4_LMICPGAPIN (AIC32X4_PAGE1 + 52) +#define AIC32X4_LMICPGANIN (AIC32X4_PAGE1 + 54) +#define AIC32X4_RMICPGAPIN (AIC32X4_PAGE1 + 55) +#define AIC32X4_RMICPGANIN (AIC32X4_PAGE1 + 57) +#define AIC32X4_FLOATINGINPUT (AIC32X4_PAGE1 + 58) +#define AIC32X4_LMICPGAVOL (AIC32X4_PAGE1 + 59) +#define AIC32X4_RMICPGAVOL (AIC32X4_PAGE1 + 60) + +#define AIC32X4_FREQ_12000000 12000000 +#define AIC32X4_FREQ_24000000 24000000 +#define AIC32X4_FREQ_25000000 25000000 + +#define AIC32X4_WORD_LEN_16BITS 0x00 +#define AIC32X4_WORD_LEN_20BITS 0x01 +#define AIC32X4_WORD_LEN_24BITS 0x02 +#define AIC32X4_WORD_LEN_32BITS 0x03 + +#define AIC32X4_I2S_MODE 0x00 +#define AIC32X4_DSP_MODE 0x01 +#define AIC32X4_RIGHT_JUSTIFIED_MODE 0x02 +#define AIC32X4_LEFT_JUSTIFIED_MODE 0x03 + +#define AIC32X4_AVDDWEAKDISABLE 0x08 +#define AIC32X4_LDOCTLEN 0x01 + +#define AIC32X4_LDOIN_18_36 0x01 +#define AIC32X4_LDOIN2HP 0x02 + +#define AIC32X4_DACSPBLOCK_MASK 0x1f +#define AIC32X4_ADCSPBLOCK_MASK 0x1f + +#define AIC32X4_PLLJ_SHIFT 6 +#define AIC32X4_DOSRMSB_SHIFT 4 + +#define AIC32X4_PLLCLKIN 0x03 + +#define AIC32X4_MICBIAS_LDOIN 0x08 +#define AIC32X4_MICBIAS_2075V 0x60 + +#define AIC32X4_LMICPGANIN_IN2R_10K 0x10 +#define AIC32X4_RMICPGANIN_IN1L_10K 0x10 + +#define AIC32X4_LMICPGAVOL_NOGAIN 0x80 +#define AIC32X4_RMICPGAVOL_NOGAIN 0x80 + +#define AIC32X4_BCLKMASTER 0x08 +#define AIC32X4_WCLKMASTER 0x04 +#define AIC32X4_PLLEN (0x01 << 7) +#define AIC32X4_NDACEN (0x01 << 7) +#define AIC32X4_MDACEN (0x01 << 7) +#define AIC32X4_NADCEN (0x01 << 7) +#define AIC32X4_MADCEN (0x01 << 7) +#define AIC32X4_BCLKEN (0x01 << 7) +#define AIC32X4_DACEN (0x03 << 6) +#define AIC32X4_RDAC2LCHN (0x02 << 2) +#define AIC32X4_LDAC2RCHN (0x02 << 4) +#define AIC32X4_LDAC2LCHN (0x01 << 4) +#define AIC32X4_RDAC2RCHN (0x01 << 2) + +#define AIC32X4_SSTEP2WCLK 0x01 +#define AIC32X4_MUTEON 0x0C +#define AIC32X4_DACMOD2BCLK 0x01 + +#endif /* _TLV320AIC32X4_H */ -- cgit v1.2.3-59-g8ed1b From 89b95ac09e408b5d88a8b3792dc76c863e45fb31 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 7 Mar 2011 16:38:44 +0000 Subject: ASoC: Add DAPM widget and path data to CODEC driver structure Allow a slight simplification of CODEC drivers by allowing DAPM routes and widgets to be provided in a table. They will be instantiated at the end of CODEC probe. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 6 ++++++ sound/soc/soc-core.c | 12 ++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 9c2a6dd170f1..6f197589b6d7 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -562,6 +562,12 @@ struct snd_soc_codec_driver { pm_message_t state); int (*resume)(struct snd_soc_codec *); + /* Default DAPM setup, added after probe() is run */ + const struct snd_soc_dapm_widget *dapm_widgets; + int num_dapm_widgets; + const struct snd_soc_dapm_route *dapm_routes; + int num_dapm_routes; + /* codec IO */ unsigned int (*read)(struct snd_soc_codec *, unsigned int); int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index be34f6b95386..c12f2bd23a4e 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1464,6 +1464,7 @@ static int soc_probe_codec(struct snd_soc_card *card, struct snd_soc_codec *codec) { int ret = 0; + const struct snd_soc_codec_driver *driver = codec->driver; codec->card = card; codec->dapm.card = card; @@ -1472,8 +1473,8 @@ static int soc_probe_codec(struct snd_soc_card *card, if (!try_module_get(codec->dev->driver->owner)) return -ENODEV; - if (codec->driver->probe) { - ret = codec->driver->probe(codec); + if (driver->probe) { + ret = driver->probe(codec); if (ret < 0) { dev_err(codec->dev, "asoc: failed to probe CODEC %s: %d\n", @@ -1482,6 +1483,13 @@ static int soc_probe_codec(struct snd_soc_card *card, } } + if (driver->dapm_widgets) + snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets, + driver->num_dapm_widgets); + if (driver->dapm_routes) + snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes, + driver->num_dapm_routes); + soc_init_codec_debugfs(codec); /* mark codec as probed and add to card codec list */ -- cgit v1.2.3-59-g8ed1b From ec4ee52a8f5fb5b8e235ae9f02589d60d54740cc Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 7 Mar 2011 20:58:11 +0000 Subject: ASoC: Provide CODEC clocking operations and API calls When multi component systems use DAIless amplifiers which require clocking configuration it is at best hard to use the current clocking API as this requires a DAI even though the device may not even have one. Address this by adding set_sysclk() and set_pll() operations and APIs for CODECs. In order to avoid issues with devices which could be used either with or without DAIs make the DAI variants call through to their CODEC counterparts if there is no DAI specific operation. Converting over entirely would create problems for multi-DAI devices which offer per-DAI clocking setup. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 11 +++++++++++ sound/soc/soc-core.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 6f197589b6d7..14f601f3e189 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -259,6 +259,11 @@ enum snd_soc_compress_type { SND_SOC_RBTREE_COMPRESSION }; +int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id, + unsigned int freq, int dir); +int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out); + int snd_soc_register_card(struct snd_soc_card *card); int snd_soc_unregister_card(struct snd_soc_card *card); int snd_soc_suspend(struct device *dev); @@ -568,6 +573,12 @@ struct snd_soc_codec_driver { const struct snd_soc_dapm_route *dapm_routes; int num_dapm_routes; + /* codec wide operations */ + int (*set_sysclk)(struct snd_soc_codec *codec, + int clk_id, unsigned int freq, int dir); + int (*set_pll)(struct snd_soc_codec *codec, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out); + /* codec IO */ unsigned int (*read)(struct snd_soc_codec *, unsigned int); int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index c12f2bd23a4e..c2ec6cb05631 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3064,11 +3064,33 @@ int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, { if (dai->driver && dai->driver->ops->set_sysclk) return dai->driver->ops->set_sysclk(dai, clk_id, freq, dir); + else if (dai->codec && dai->codec->driver->set_sysclk) + return dai->codec->driver->set_sysclk(dai->codec, clk_id, + freq, dir); else return -EINVAL; } EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk); +/** + * snd_soc_codec_set_sysclk - configure CODEC system or master clock. + * @codec: CODEC + * @clk_id: DAI specific clock ID + * @freq: new clock frequency in Hz + * @dir: new clock direction - input/output. + * + * Configures the CODEC master (MCLK) or system (SYSCLK) clocking. + */ +int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id, + unsigned int freq, int dir) +{ + if (codec->driver->set_sysclk) + return codec->driver->set_sysclk(codec, clk_id, freq, dir); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_codec_set_sysclk); + /** * snd_soc_dai_set_clkdiv - configure DAI clock dividers. * @dai: DAI @@ -3105,11 +3127,35 @@ int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, if (dai->driver && dai->driver->ops->set_pll) return dai->driver->ops->set_pll(dai, pll_id, source, freq_in, freq_out); + else if (dai->codec && dai->codec->driver->set_pll) + return dai->codec->driver->set_pll(dai->codec, pll_id, source, + freq_in, freq_out); else return -EINVAL; } EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll); +/* + * snd_soc_codec_set_pll - configure codec PLL. + * @codec: CODEC + * @pll_id: DAI specific PLL ID + * @source: DAI specific source for the PLL + * @freq_in: PLL input clock frequency in Hz + * @freq_out: requested PLL output clock frequency in Hz + * + * Configures and enables PLL to generate output clock based on input clock. + */ +int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + if (codec->driver->set_pll) + return codec->driver->set_pll(codec, pll_id, source, + freq_in, freq_out); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_codec_set_pll); + /** * snd_soc_dai_set_fmt - configure DAI hardware audio format. * @dai: DAI -- cgit v1.2.3-59-g8ed1b From efb7ac3f9c28fcb379c51f987b63174f727b7453 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 8 Mar 2011 17:23:24 +0000 Subject: ASoC: Fix prefixing of DAPM controls by factoring prefix into snd_soc_cnew() Currently will ignore prefixes when creating DAPM controls. Since currently all control creation goes through snd_soc_cnew() we can fix this by factoring the prefixing into that function. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 3 ++- sound/soc/soc-core.c | 45 +++++++++++++++++++++++++++++++-------------- sound/soc/soc-dapm.c | 16 ++++++++++++++-- 3 files changed, 47 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 14f601f3e189..bfa4836ea107 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -340,7 +340,8 @@ void snd_soc_free_ac97_codec(struct snd_soc_codec *codec); *Controls */ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, - void *data, char *long_name); + void *data, char *long_name, + const char *prefix); int snd_soc_add_controls(struct snd_soc_codec *codec, const struct snd_kcontrol_new *controls, int num_controls); int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index db3075dd11fe..17efacdb248a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2344,22 +2344,45 @@ EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams); * @_template: control template * @data: control private data * @long_name: control long name + * @prefix: control name prefix * * Create a new mixer control from a template control. * * Returns 0 for success, else error. */ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, - void *data, char *long_name) + void *data, char *long_name, + const char *prefix) { struct snd_kcontrol_new template; + struct snd_kcontrol *kcontrol; + char *name = NULL; + int name_len; memcpy(&template, _template, sizeof(template)); - if (long_name) - template.name = long_name; template.index = 0; - return snd_ctl_new1(&template, data); + if (!long_name) + long_name = template.name; + + if (prefix) { + name_len = strlen(long_name) + strlen(prefix) + 2; + name = kmalloc(name_len, GFP_ATOMIC); + if (!name) + return NULL; + + snprintf(name, name_len, "%s %s", prefix, long_name); + + template.name = name; + } else { + template.name = long_name; + } + + kcontrol = snd_ctl_new1(&template, data); + + kfree(name); + + return kcontrol; } EXPORT_SYMBOL_GPL(snd_soc_cnew); @@ -2378,22 +2401,16 @@ int snd_soc_add_controls(struct snd_soc_codec *codec, const struct snd_kcontrol_new *controls, int num_controls) { struct snd_card *card = codec->card->snd_card; - char prefixed_name[44], *name; int err, i; for (i = 0; i < num_controls; i++) { const struct snd_kcontrol_new *control = &controls[i]; - if (codec->name_prefix) { - snprintf(prefixed_name, sizeof(prefixed_name), "%s %s", - codec->name_prefix, control->name); - name = prefixed_name; - } else { - name = control->name; - } - err = snd_ctl_add(card, snd_soc_cnew(control, codec, name)); + err = snd_ctl_add(card, snd_soc_cnew(control, codec, + control->name, + codec->name_prefix)); if (err < 0) { dev_err(codec->dev, "%s: Failed to add %s: %d\n", - codec->name, name, err); + codec->name, control->name, err); return err; } } diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 570db8819d9b..a6fb85d46416 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -369,6 +369,12 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm, size_t name_len; struct snd_soc_dapm_path *path; struct snd_card *card = dapm->card->snd_card; + const char *prefix; + + if (dapm->codec) + prefix = dapm->codec->name_prefix; + else + prefix = NULL; /* add kcontrol */ for (i = 0; i < w->num_kcontrols; i++) { @@ -409,7 +415,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm, path->long_name[name_len - 1] = '\0'; path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w, - path->long_name); + path->long_name, prefix); ret = snd_ctl_add(card, path->kcontrol); if (ret < 0) { dev_err(dapm->dev, @@ -431,6 +437,7 @@ static int dapm_new_mux(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_path *path = NULL; struct snd_kcontrol *kcontrol; struct snd_card *card = dapm->card->snd_card; + const char *prefix; int ret = 0; if (!w->num_kcontrols) { @@ -438,7 +445,12 @@ static int dapm_new_mux(struct snd_soc_dapm_context *dapm, return -EINVAL; } - kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name); + if (dapm->codec) + prefix = dapm->codec->name_prefix; + else + prefix = NULL; + + kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name, prefix); ret = snd_ctl_add(card, kcontrol); if (ret < 0) -- cgit v1.2.3-59-g8ed1b From 3cbdd7533148f00444013700af89548b8cf32646 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 29 Aug 2008 16:09:01 +0200 Subject: ALSA: Add snd_ctl_activate_id() Added a new API function snd_ctl_activate_id() for activate / inactivate the control element dynamically. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown --- include/sound/control.h | 2 ++ sound/core/control.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) (limited to 'include') diff --git a/include/sound/control.h b/include/sound/control.h index 7715e6f00d38..e67db2869360 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -115,6 +115,8 @@ int snd_ctl_add(struct snd_card * card, struct snd_kcontrol * kcontrol); int snd_ctl_remove(struct snd_card * card, struct snd_kcontrol * kcontrol); int snd_ctl_remove_id(struct snd_card * card, struct snd_ctl_elem_id *id); int snd_ctl_rename_id(struct snd_card * card, struct snd_ctl_elem_id *src_id, struct snd_ctl_elem_id *dst_id); +int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id, + int active); struct snd_kcontrol *snd_ctl_find_numid(struct snd_card * card, unsigned int numid); struct snd_kcontrol *snd_ctl_find_id(struct snd_card * card, struct snd_ctl_elem_id *id); diff --git a/sound/core/control.c b/sound/core/control.c index 9ce00ed20fba..db51e4e64984 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -465,6 +465,52 @@ error: return ret; } +/** + * snd_ctl_activate_id - activate/inactivate the control of the given id + * @card: the card instance + * @id: the control id to activate/inactivate + * @active: non-zero to activate + * + * Finds the control instance with the given id, and activate or + * inactivate the control together with notification, if changed. + * + * Returns 0 if unchanged, 1 if changed, or a negative error code on failure. + */ +int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id, + int active) +{ + struct snd_kcontrol *kctl; + struct snd_kcontrol_volatile *vd; + unsigned int index_offset; + int ret; + + down_write(&card->controls_rwsem); + kctl = snd_ctl_find_id(card, id); + if (kctl == NULL) { + ret = -ENOENT; + goto unlock; + } + index_offset = snd_ctl_get_ioff(kctl, &kctl->id); + vd = &kctl->vd[index_offset]; + ret = 0; + if (active) { + if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)) + goto unlock; + vd->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + } else { + if (vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE) + goto unlock; + vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + } + ret = 1; + unlock: + up_write(&card->controls_rwsem); + if (ret > 0) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, id); + return ret; +} +EXPORT_SYMBOL_GPL(snd_ctl_activate_id); + /** * snd_ctl_rename_id - replace the id of a control on the card * @card: the card instance -- cgit v1.2.3-59-g8ed1b From 31ef9134eb52636d383a7d0626cbbd345cb94f2f Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Tue, 15 Mar 2011 07:53:21 +0100 Subject: ALSA: add LaCie FireWire Speakers/Griffin FireWave Surround driver Add a driver for two playback-only FireWire devices based on the OXFW970 chip. v2: better AMDTP API abstraction; fix fw_unit leak; small fixes v3: cache the iPCR value v4: FireWave constraints; fix fw_device reference counting; fix PCR caching; small changes and fixes v5: volume/mute support; fix crashing due to pcm stop races v6: fix build; one-channel volume for LaCie v7: use signed values to make volume (range checks) work; fix function block IDs for volume/mute; always use channel 0 for LaCie volume Signed-off-by: Clemens Ladisch Acked-by: Stefan Richter Tested-by: Jay Fenlason Signed-off-by: Takashi Iwai --- drivers/firewire/core-iso.c | 1 + drivers/firewire/core.h | 3 - include/linux/firewire.h | 7 + sound/Kconfig | 2 + sound/Makefile | 2 +- sound/firewire/Kconfig | 25 ++ sound/firewire/Makefile | 6 + sound/firewire/amdtp.c | 549 ++++++++++++++++++++++++++ sound/firewire/amdtp.h | 157 ++++++++ sound/firewire/cmp.c | 305 ++++++++++++++ sound/firewire/cmp.h | 41 ++ sound/firewire/fcp.c | 223 +++++++++++ sound/firewire/fcp.h | 12 + sound/firewire/iso-resources.c | 224 +++++++++++ sound/firewire/iso-resources.h | 39 ++ sound/firewire/lib.c | 85 ++++ sound/firewire/lib.h | 19 + sound/firewire/packets-buffer.c | 74 ++++ sound/firewire/packets-buffer.h | 26 ++ sound/firewire/speakers.c | 855 ++++++++++++++++++++++++++++++++++++++++ 20 files changed, 2651 insertions(+), 4 deletions(-) create mode 100644 sound/firewire/Kconfig create mode 100644 sound/firewire/Makefile create mode 100644 sound/firewire/amdtp.c create mode 100644 sound/firewire/amdtp.h create mode 100644 sound/firewire/cmp.c create mode 100644 sound/firewire/cmp.h create mode 100644 sound/firewire/fcp.c create mode 100644 sound/firewire/fcp.h create mode 100644 sound/firewire/iso-resources.c create mode 100644 sound/firewire/iso-resources.h create mode 100644 sound/firewire/lib.c create mode 100644 sound/firewire/lib.h create mode 100644 sound/firewire/packets-buffer.c create mode 100644 sound/firewire/packets-buffer.h create mode 100644 sound/firewire/speakers.c (limited to 'include') diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index c003fa4e2db1..c8658888e67b 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -362,3 +362,4 @@ void fw_iso_resource_manage(struct fw_card *card, int generation, *channel = ret; } } +EXPORT_SYMBOL(fw_iso_resource_manage); diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index f8dfcf1c6cbe..25e729cde2f7 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -147,9 +147,6 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event); /* -iso */ int fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma); -void fw_iso_resource_manage(struct fw_card *card, int generation, - u64 channels_mask, int *channel, int *bandwidth, - bool allocate, __be32 buffer[2]); /* -topology */ diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 9a3f5f9383f6..fc023d67676f 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -42,6 +42,10 @@ #define CSR_BROADCAST_CHANNEL 0x234 #define CSR_CONFIG_ROM 0x400 #define CSR_CONFIG_ROM_END 0x800 +#define CSR_OMPR 0x900 +#define CSR_OPCR(i) (0x904 + (i) * 4) +#define CSR_IMPR 0x980 +#define CSR_IPCR(i) (0x984 + (i) * 4) #define CSR_FCP_COMMAND 0xB00 #define CSR_FCP_RESPONSE 0xD00 #define CSR_FCP_END 0xF00 @@ -441,5 +445,8 @@ int fw_iso_context_start(struct fw_iso_context *ctx, int cycle, int sync, int tags); int fw_iso_context_stop(struct fw_iso_context *ctx); void fw_iso_context_destroy(struct fw_iso_context *ctx); +void fw_iso_resource_manage(struct fw_card *card, int generation, + u64 channels_mask, int *channel, int *bandwidth, + bool allocate, __be32 buffer[2]); #endif /* _LINUX_FIREWIRE_H */ diff --git a/sound/Kconfig b/sound/Kconfig index fcad760f5691..1fef141ef8e7 100644 --- a/sound/Kconfig +++ b/sound/Kconfig @@ -97,6 +97,8 @@ source "sound/sh/Kconfig" # here assuming USB is defined before ALSA source "sound/usb/Kconfig" +source "sound/firewire/Kconfig" + # the following will depend on the order of config. # here assuming PCMCIA is defined before ALSA source "sound/pcmcia/Kconfig" diff --git a/sound/Makefile b/sound/Makefile index ec467decfa79..ce9132b1c395 100644 --- a/sound/Makefile +++ b/sound/Makefile @@ -6,7 +6,7 @@ obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o obj-$(CONFIG_SOUND_PRIME) += oss/ obj-$(CONFIG_DMASOUND) += oss/ obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \ - sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ + firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ obj-$(CONFIG_SND_AOA) += aoa/ # This one must be compilable even if sound is configured out diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig new file mode 100644 index 000000000000..e486f48660fb --- /dev/null +++ b/sound/firewire/Kconfig @@ -0,0 +1,25 @@ +menuconfig SND_FIREWIRE + bool "FireWire sound devices" + depends on FIREWIRE + default y + help + Support for IEEE-1394/FireWire/iLink sound devices. + +if SND_FIREWIRE && FIREWIRE + +config SND_FIREWIRE_LIB + tristate + depends on SND_PCM + +config SND_FIREWIRE_SPEAKERS + tristate "FireWire speakers" + select SND_PCM + select SND_FIREWIRE_LIB + help + Say Y here to include support for the Griffin FireWave Surround + and the LaCie FireWire Speakers. + + To compile this driver as a module, choose M here: the module + will be called snd-firewire-speakers. + +endif # SND_FIREWIRE diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile new file mode 100644 index 000000000000..e5b1634d9ad4 --- /dev/null +++ b/sound/firewire/Makefile @@ -0,0 +1,6 @@ +snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \ + fcp.o cmp.o amdtp.o +snd-firewire-speakers-objs := speakers.o + +obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o +obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c new file mode 100644 index 000000000000..09f70ee4d04a --- /dev/null +++ b/sound/firewire/amdtp.c @@ -0,0 +1,549 @@ +/* + * Audio and Music Data Transmission Protocol (IEC 61883-6) streams + * with Common Isochronous Packet (IEC 61883-1) headers + * + * Copyright (c) Clemens Ladisch + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include +#include +#include +#include +#include +#include +#include "amdtp.h" + +#define TICKS_PER_CYCLE 3072 +#define CYCLES_PER_SECOND 8000 +#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND) + +#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 µs */ + +#define TAG_CIP 1 + +#define CIP_EOH (1u << 31) +#define CIP_FMT_AM (0x10 << 24) +#define AMDTP_FDF_AM824 (0 << 19) +#define AMDTP_FDF_SFC_SHIFT 16 + +/* TODO: make these configurable */ +#define INTERRUPT_INTERVAL 16 +#define QUEUE_LENGTH 48 + +/** + * amdtp_out_stream_init - initialize an AMDTP output stream structure + * @s: the AMDTP output stream to initialize + * @unit: the target of the stream + * @flags: the packet transmission method to use + */ +int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, + enum cip_out_flags flags) +{ + if (flags != CIP_NONBLOCKING) + return -EINVAL; + + s->unit = fw_unit_get(unit); + s->flags = flags; + s->context = ERR_PTR(-1); + mutex_init(&s->mutex); + + return 0; +} +EXPORT_SYMBOL(amdtp_out_stream_init); + +/** + * amdtp_out_stream_destroy - free stream resources + * @s: the AMDTP output stream to destroy + */ +void amdtp_out_stream_destroy(struct amdtp_out_stream *s) +{ + WARN_ON(!IS_ERR(s->context)); + mutex_destroy(&s->mutex); + fw_unit_put(s->unit); +} +EXPORT_SYMBOL(amdtp_out_stream_destroy); + +/** + * amdtp_out_stream_set_rate - set the sample rate + * @s: the AMDTP output stream to configure + * @rate: the sample rate + * + * The sample rate must be set before the stream is started, and must not be + * changed while the stream is running. + */ +void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate) +{ + static const struct { + unsigned int rate; + unsigned int syt_interval; + } rate_info[] = { + [CIP_SFC_32000] = { 32000, 8, }, + [CIP_SFC_44100] = { 44100, 8, }, + [CIP_SFC_48000] = { 48000, 8, }, + [CIP_SFC_88200] = { 88200, 16, }, + [CIP_SFC_96000] = { 96000, 16, }, + [CIP_SFC_176400] = { 176400, 32, }, + [CIP_SFC_192000] = { 192000, 32, }, + }; + unsigned int sfc; + + if (WARN_ON(!IS_ERR(s->context))) + return; + + for (sfc = 0; sfc < ARRAY_SIZE(rate_info); ++sfc) + if (rate_info[sfc].rate == rate) { + s->sfc = sfc; + s->syt_interval = rate_info[sfc].syt_interval; + return; + } + WARN_ON(1); +} +EXPORT_SYMBOL(amdtp_out_stream_set_rate); + +/** + * amdtp_out_stream_get_max_payload - get the stream's packet size + * @s: the AMDTP output stream + * + * This function must not be called before the stream has been configured + * with amdtp_out_stream_set_hw_params(), amdtp_out_stream_set_pcm(), and + * amdtp_out_stream_set_midi(). + */ +unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s) +{ + static const unsigned int max_data_blocks[] = { + [CIP_SFC_32000] = 4, + [CIP_SFC_44100] = 6, + [CIP_SFC_48000] = 6, + [CIP_SFC_88200] = 12, + [CIP_SFC_96000] = 12, + [CIP_SFC_176400] = 23, + [CIP_SFC_192000] = 24, + }; + + s->data_block_quadlets = s->pcm_channels; + s->data_block_quadlets += DIV_ROUND_UP(s->midi_ports, 8); + + return 8 + max_data_blocks[s->sfc] * 4 * s->data_block_quadlets; +} +EXPORT_SYMBOL(amdtp_out_stream_get_max_payload); + +static void amdtp_write_s16(struct amdtp_out_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); +static void amdtp_write_s32(struct amdtp_out_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); + +/** + * amdtp_out_stream_set_pcm_format - set the PCM format + * @s: the AMDTP output stream to configure + * @format: the format of the ALSA PCM device + * + * The sample format must be set before the stream is started, and must not be + * changed while the stream is running. + */ +void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, + snd_pcm_format_t format) +{ + if (WARN_ON(!IS_ERR(s->context))) + return; + + switch (format) { + default: + WARN_ON(1); + /* fall through */ + case SNDRV_PCM_FORMAT_S16: + s->transfer_samples = amdtp_write_s16; + break; + case SNDRV_PCM_FORMAT_S32: + s->transfer_samples = amdtp_write_s32; + break; + } +} +EXPORT_SYMBOL(amdtp_out_stream_set_pcm_format); + +static unsigned int calculate_data_blocks(struct amdtp_out_stream *s) +{ + unsigned int phase, data_blocks; + + if (!cip_sfc_is_base_44100(s->sfc)) { + /* Sample_rate / 8000 is an integer, and precomputed. */ + data_blocks = s->data_block_state; + } else { + phase = s->data_block_state; + + /* + * This calculates the number of data blocks per packet so that + * 1) the overall rate is correct and exactly synchronized to + * the bus clock, and + * 2) packets with a rounded-up number of blocks occur as early + * as possible in the sequence (to prevent underruns of the + * device's buffer). + */ + if (s->sfc == CIP_SFC_44100) + /* 6 6 5 6 5 6 5 ... */ + data_blocks = 5 + ((phase & 1) ^ + (phase == 0 || phase >= 40)); + else + /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */ + data_blocks = 11 * (s->sfc >> 1) + (phase == 0); + if (++phase >= (80 >> (s->sfc >> 1))) + phase = 0; + s->data_block_state = phase; + } + + return data_blocks; +} + +static unsigned int calculate_syt(struct amdtp_out_stream *s, + unsigned int cycle) +{ + unsigned int syt_offset, phase, index, syt; + + if (s->last_syt_offset < TICKS_PER_CYCLE) { + if (!cip_sfc_is_base_44100(s->sfc)) + syt_offset = s->last_syt_offset + s->syt_offset_state; + else { + /* + * The time, in ticks, of the n'th SYT_INTERVAL sample is: + * n * SYT_INTERVAL * 24576000 / sample_rate + * Modulo TICKS_PER_CYCLE, the difference between successive + * elements is about 1386.23. Rounding the results of this + * formula to the SYT precision results in a sequence of + * differences that begins with: + * 1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ... + * This code generates _exactly_ the same sequence. + */ + phase = s->syt_offset_state; + index = phase % 13; + syt_offset = s->last_syt_offset; + syt_offset += 1386 + ((index && !(index & 3)) || + phase == 146); + if (++phase >= 147) + phase = 0; + s->syt_offset_state = phase; + } + } else + syt_offset = s->last_syt_offset - TICKS_PER_CYCLE; + s->last_syt_offset = syt_offset; + + syt_offset += TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE; + syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12; + syt += syt_offset % TICKS_PER_CYCLE; + + return syt & 0xffff; +} + +static void amdtp_write_s32(struct amdtp_out_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, frame_step, i, c; + const u32 *src; + + channels = s->pcm_channels; + src = (void *)runtime->dma_area + + s->pcm_buffer_pointer * (runtime->frame_bits / 8); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + frame_step = s->data_block_quadlets - channels; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + *buffer = cpu_to_be32((*src >> 8) | 0x40000000); + src++; + buffer++; + } + buffer += frame_step; + if (--remaining_frames == 0) + src = (void *)runtime->dma_area; + } +} + +static void amdtp_write_s16(struct amdtp_out_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, frame_step, i, c; + const u16 *src; + + channels = s->pcm_channels; + src = (void *)runtime->dma_area + + s->pcm_buffer_pointer * (runtime->frame_bits / 8); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + frame_step = s->data_block_quadlets - channels; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + *buffer = cpu_to_be32((*src << 8) | 0x40000000); + src++; + buffer++; + } + buffer += frame_step; + if (--remaining_frames == 0) + src = (void *)runtime->dma_area; + } +} + +static void amdtp_fill_pcm_silence(struct amdtp_out_stream *s, + __be32 *buffer, unsigned int frames) +{ + unsigned int i, c; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < s->pcm_channels; ++c) + buffer[c] = cpu_to_be32(0x40000000); + buffer += s->data_block_quadlets; + } +} + +static void amdtp_fill_midi(struct amdtp_out_stream *s, + __be32 *buffer, unsigned int frames) +{ + unsigned int i; + + for (i = 0; i < frames; ++i) + buffer[s->pcm_channels + i * s->data_block_quadlets] = + cpu_to_be32(0x80000000); +} + +static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle) +{ + __be32 *buffer; + unsigned int data_blocks, syt, ptr; + struct snd_pcm_substream *pcm; + struct fw_iso_packet packet; + int err; + + data_blocks = calculate_data_blocks(s); + syt = calculate_syt(s, cycle); + + buffer = s->buffer.packets[s->packet_counter].buffer; + buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | + (s->data_block_quadlets << 16) | + s->data_block_counter); + buffer[1] = cpu_to_be32(CIP_EOH | CIP_FMT_AM | AMDTP_FDF_AM824 | + (s->sfc << AMDTP_FDF_SFC_SHIFT) | syt); + buffer += 2; + + pcm = ACCESS_ONCE(s->pcm); + if (pcm) + s->transfer_samples(s, pcm, buffer, data_blocks); + else + amdtp_fill_pcm_silence(s, buffer, data_blocks); + if (s->midi_ports) + amdtp_fill_midi(s, buffer, data_blocks); + + s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff; + + packet.payload_length = 8 + data_blocks * 4 * s->data_block_quadlets; + packet.interrupt = IS_ALIGNED(s->packet_counter + 1, + INTERRUPT_INTERVAL); + packet.skip = 0; + packet.tag = TAG_CIP; + packet.sy = 0; + packet.header_length = 0; + + err = fw_iso_context_queue(s->context, &packet, &s->buffer.iso_buffer, + s->buffer.packets[s->packet_counter].offset); + if (err < 0) + dev_err(&s->unit->device, "queueing error: %d\n", err); + + if (++s->packet_counter >= QUEUE_LENGTH) + s->packet_counter = 0; + + if (pcm) { + ptr = s->pcm_buffer_pointer + data_blocks; + if (ptr >= pcm->runtime->buffer_size) + ptr -= pcm->runtime->buffer_size; + ACCESS_ONCE(s->pcm_buffer_pointer) = ptr; + + s->pcm_period_pointer += data_blocks; + if (s->pcm_period_pointer >= pcm->runtime->period_size) { + s->pcm_period_pointer -= pcm->runtime->period_size; + snd_pcm_period_elapsed(pcm); + } + } +} + +static void out_packet_callback(struct fw_iso_context *context, u32 cycle, + size_t header_length, void *header, void *data) +{ + struct amdtp_out_stream *s = data; + unsigned int i, packets = header_length / 4; + + /* + * Compute the cycle of the last queued packet. + * (We need only the four lowest bits for the SYT, so we can ignore + * that bits 0-11 must wrap around at 3072.) + */ + cycle += QUEUE_LENGTH - packets; + + for (i = 0; i < packets; ++i) + queue_out_packet(s, ++cycle); +} + +static int queue_initial_skip_packets(struct amdtp_out_stream *s) +{ + struct fw_iso_packet skip_packet = { + .skip = 1, + }; + unsigned int i; + int err; + + for (i = 0; i < QUEUE_LENGTH; ++i) { + skip_packet.interrupt = IS_ALIGNED(s->packet_counter + 1, + INTERRUPT_INTERVAL); + err = fw_iso_context_queue(s->context, &skip_packet, NULL, 0); + if (err < 0) + return err; + if (++s->packet_counter >= QUEUE_LENGTH) + s->packet_counter = 0; + } + + return 0; +} + +/** + * amdtp_out_stream_start - start sending packets + * @s: the AMDTP output stream to start + * @channel: the isochronous channel on the bus + * @speed: firewire speed code + * + * The stream cannot be started until it has been configured with + * amdtp_out_stream_set_hw_params(), amdtp_out_stream_set_pcm(), and + * amdtp_out_stream_set_midi(); and it must be started before any + * PCM or MIDI device can be started. + */ +int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed) +{ + static const struct { + unsigned int data_block; + unsigned int syt_offset; + } initial_state[] = { + [CIP_SFC_32000] = { 4, 3072 }, + [CIP_SFC_48000] = { 6, 1024 }, + [CIP_SFC_96000] = { 12, 1024 }, + [CIP_SFC_192000] = { 24, 1024 }, + [CIP_SFC_44100] = { 0, 67 }, + [CIP_SFC_88200] = { 0, 67 }, + [CIP_SFC_176400] = { 0, 67 }, + }; + int err; + + mutex_lock(&s->mutex); + + if (WARN_ON(!IS_ERR(s->context) || + (!s->pcm_channels && !s->midi_ports))) { + err = -EBADFD; + goto err_unlock; + } + + s->data_block_state = initial_state[s->sfc].data_block; + s->syt_offset_state = initial_state[s->sfc].syt_offset; + s->last_syt_offset = TICKS_PER_CYCLE; + + err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, + amdtp_out_stream_get_max_payload(s), + DMA_TO_DEVICE); + if (err < 0) + goto err_unlock; + + s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, + FW_ISO_CONTEXT_TRANSMIT, + channel, speed, 0, + out_packet_callback, s); + if (IS_ERR(s->context)) { + err = PTR_ERR(s->context); + if (err == -EBUSY) + dev_err(&s->unit->device, + "no free output stream on this controller\n"); + goto err_buffer; + } + + amdtp_out_stream_update(s); + + s->packet_counter = 0; + s->data_block_counter = 0; + err = queue_initial_skip_packets(s); + if (err < 0) + goto err_context; + + err = fw_iso_context_start(s->context, -1, 0, 0); + if (err < 0) + goto err_context; + + mutex_unlock(&s->mutex); + + return 0; + +err_context: + fw_iso_context_destroy(s->context); + s->context = ERR_PTR(-1); +err_buffer: + iso_packets_buffer_destroy(&s->buffer, s->unit); +err_unlock: + mutex_unlock(&s->mutex); + + return err; +} +EXPORT_SYMBOL(amdtp_out_stream_start); + +/** + * amdtp_out_stream_update - update the stream after a bus reset + * @s: the AMDTP output stream + */ +void amdtp_out_stream_update(struct amdtp_out_stream *s) +{ + ACCESS_ONCE(s->source_node_id_field) = + (fw_parent_device(s->unit)->card->node_id & 0x3f) << 24; +} +EXPORT_SYMBOL(amdtp_out_stream_update); + +/** + * amdtp_out_stream_stop - stop sending packets + * @s: the AMDTP output stream to stop + * + * All PCM and MIDI devices of the stream must be stopped before the stream + * itself can be stopped. + */ +void amdtp_out_stream_stop(struct amdtp_out_stream *s) +{ + mutex_lock(&s->mutex); + + if (IS_ERR(s->context)) { + mutex_unlock(&s->mutex); + return; + } + + fw_iso_context_stop(s->context); + fw_iso_context_destroy(s->context); + s->context = ERR_PTR(-1); + iso_packets_buffer_destroy(&s->buffer, s->unit); + + mutex_unlock(&s->mutex); +} +EXPORT_SYMBOL(amdtp_out_stream_stop); + +/** + * amdtp_out_stream_pcm_abort - abort the running PCM device + * @s: the AMDTP stream about to be stopped + * + * If the isochronous stream needs to be stopped asynchronously, call this + * function first to stop the PCM device. + */ +void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s) +{ + struct snd_pcm_substream *pcm; + + pcm = ACCESS_ONCE(s->pcm); + if (pcm) { + snd_pcm_stream_lock_irq(pcm); + if (snd_pcm_running(pcm)) + snd_pcm_stop(pcm, SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock_irq(pcm); + } +} +EXPORT_SYMBOL(amdtp_out_stream_pcm_abort); diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h new file mode 100644 index 000000000000..02dc1a664b55 --- /dev/null +++ b/sound/firewire/amdtp.h @@ -0,0 +1,157 @@ +#ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED +#define SOUND_FIREWIRE_AMDTP_H_INCLUDED + +#include +#include +#include "packets-buffer.h" + +/** + * enum cip_out_flags - describes details of the streaming protocol + * @CIP_NONBLOCKING: In non-blocking mode, each packet contains + * sample_rate/8000 samples, with rounding up or down to adjust + * for clock skew and left-over fractional samples. This should + * be used if supported by the device. + */ +enum cip_out_flags { + CIP_NONBLOCKING = 0, +}; + +/** + * enum cip_sfc - a stream's sample rate + */ +enum cip_sfc { + CIP_SFC_32000 = 0, + CIP_SFC_44100 = 1, + CIP_SFC_48000 = 2, + CIP_SFC_88200 = 3, + CIP_SFC_96000 = 4, + CIP_SFC_176400 = 5, + CIP_SFC_192000 = 6, +}; + +#define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ + SNDRV_PCM_FMTBIT_S32) + +struct fw_unit; +struct fw_iso_context; +struct snd_pcm_substream; + +struct amdtp_out_stream { + struct fw_unit *unit; + enum cip_out_flags flags; + struct fw_iso_context *context; + struct mutex mutex; + + enum cip_sfc sfc; + unsigned int data_block_quadlets; + unsigned int pcm_channels; + unsigned int midi_ports; + void (*transfer_samples)(struct amdtp_out_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); + + unsigned int syt_interval; + unsigned int source_node_id_field; + struct iso_packets_buffer buffer; + + struct snd_pcm_substream *pcm; + + unsigned int packet_counter; + unsigned int data_block_counter; + + unsigned int data_block_state; + + unsigned int last_syt_offset; + unsigned int syt_offset_state; + + unsigned int pcm_buffer_pointer; + unsigned int pcm_period_pointer; +}; + +int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, + enum cip_out_flags flags); +void amdtp_out_stream_destroy(struct amdtp_out_stream *s); + +void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate); +unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s); + +int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed); +void amdtp_out_stream_update(struct amdtp_out_stream *s); +void amdtp_out_stream_stop(struct amdtp_out_stream *s); + +void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, + snd_pcm_format_t format); +void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s); + +/** + * amdtp_out_stream_set_pcm - configure format of PCM samples + * @s: the AMDTP output stream to be configured + * @pcm_channels: the number of PCM samples in each data block, to be encoded + * as AM824 multi-bit linear audio + * + * This function must not be called while the stream is running. + */ +static inline void amdtp_out_stream_set_pcm(struct amdtp_out_stream *s, + unsigned int pcm_channels) +{ + s->pcm_channels = pcm_channels; +} + +/** + * amdtp_out_stream_set_midi - configure format of MIDI data + * @s: the AMDTP output stream to be configured + * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels) + * + * This function must not be called while the stream is running. + */ +static inline void amdtp_out_stream_set_midi(struct amdtp_out_stream *s, + unsigned int midi_ports) +{ + s->midi_ports = midi_ports; +} + +/** + * amdtp_out_stream_pcm_prepare - prepare PCM device for running + * @s: the AMDTP output stream + * + * This function should be called from the PCM device's .prepare callback. + */ +static inline void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s) +{ + s->pcm_buffer_pointer = 0; + s->pcm_period_pointer = 0; +} + +/** + * amdtp_out_stream_pcm_trigger - start/stop playback from a PCM device + * @s: the AMDTP output stream + * @pcm: the PCM device to be started, or %NULL to stop the current device + * + * Call this function on a running isochronous stream to enable the actual + * transmission of PCM data. This function should be called from the PCM + * device's .trigger callback. + */ +static inline void amdtp_out_stream_pcm_trigger(struct amdtp_out_stream *s, + struct snd_pcm_substream *pcm) +{ + ACCESS_ONCE(s->pcm) = pcm; +} + +/** + * amdtp_out_stream_pcm_pointer - get the PCM buffer position + * @s: the AMDTP output stream that transports the PCM data + * + * Returns the current buffer position, in frames. + */ +static inline unsigned long +amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s) +{ + return ACCESS_ONCE(s->pcm_buffer_pointer); +} + +static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc) +{ + return sfc & 1; +} + +#endif diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c new file mode 100644 index 000000000000..c992dab4bb95 --- /dev/null +++ b/sound/firewire/cmp.c @@ -0,0 +1,305 @@ +/* + * Connection Management Procedures (IEC 61883-1) helper functions + * + * Copyright (c) Clemens Ladisch + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include +#include +#include +#include +#include +#include "lib.h" +#include "iso-resources.h" +#include "cmp.h" + +#define IMPR_SPEED_MASK 0xc0000000 +#define IMPR_SPEED_SHIFT 30 +#define IMPR_XSPEED_MASK 0x00000060 +#define IMPR_XSPEED_SHIFT 5 +#define IMPR_PLUGS_MASK 0x0000001f + +#define IPCR_ONLINE 0x80000000 +#define IPCR_BCAST_CONN 0x40000000 +#define IPCR_P2P_CONN_MASK 0x3f000000 +#define IPCR_P2P_CONN_SHIFT 24 +#define IPCR_CHANNEL_MASK 0x003f0000 +#define IPCR_CHANNEL_SHIFT 16 + +enum bus_reset_handling { + ABORT_ON_BUS_RESET, + SUCCEED_ON_BUS_RESET, +}; + +static __attribute__((format(printf, 2, 3))) +void cmp_error(struct cmp_connection *c, const char *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + dev_err(&c->resources.unit->device, "%cPCR%u: %pV", + 'i', c->pcr_index, &(struct va_format){ fmt, &va }); + va_end(va); +} + +static int pcr_modify(struct cmp_connection *c, + __be32 (*modify)(struct cmp_connection *c, __be32 old), + int (*check)(struct cmp_connection *c, __be32 pcr), + enum bus_reset_handling bus_reset_handling) +{ + struct fw_device *device = fw_parent_device(c->resources.unit); + __be32 *buffer = c->resources.buffer; + int generation = c->resources.generation; + int rcode, errors = 0; + __be32 old_arg; + int err; + + buffer[0] = c->last_pcr_value; + for (;;) { + old_arg = buffer[0]; + buffer[1] = modify(c, buffer[0]); + + rcode = fw_run_transaction( + device->card, TCODE_LOCK_COMPARE_SWAP, + device->node_id, generation, device->max_speed, + CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index), + buffer, 8); + + if (rcode == RCODE_COMPLETE) { + if (buffer[0] == old_arg) /* success? */ + break; + + if (check) { + err = check(c, buffer[0]); + if (err < 0) + return err; + } + } else if (rcode == RCODE_GENERATION) + goto bus_reset; + else if (rcode_is_permanent_error(rcode) || ++errors >= 3) + goto io_error; + } + c->last_pcr_value = buffer[1]; + + return 0; + +io_error: + cmp_error(c, "transaction failed: %s\n", rcode_string(rcode)); + return -EIO; + +bus_reset: + return bus_reset_handling == ABORT_ON_BUS_RESET ? -EAGAIN : 0; +} + + +/** + * cmp_connection_init - initializes a connection manager + * @c: the connection manager to initialize + * @unit: a unit of the target device + * @ipcr_index: the index of the iPCR on the target device + */ +int cmp_connection_init(struct cmp_connection *c, + struct fw_unit *unit, + unsigned int ipcr_index) +{ + __be32 impr_be; + u32 impr; + int err; + + err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, + CSR_REGISTER_BASE + CSR_IMPR, + &impr_be, 4); + if (err < 0) + return err; + impr = be32_to_cpu(impr_be); + + if (ipcr_index >= (impr & IMPR_PLUGS_MASK)) + return -EINVAL; + + c->connected = false; + mutex_init(&c->mutex); + fw_iso_resources_init(&c->resources, unit); + c->last_pcr_value = cpu_to_be32(0x80000000); + c->pcr_index = ipcr_index; + c->max_speed = (impr & IMPR_SPEED_MASK) >> IMPR_SPEED_SHIFT; + if (c->max_speed == SCODE_BETA) + c->max_speed += (impr & IMPR_XSPEED_MASK) >> IMPR_XSPEED_SHIFT; + + return 0; +} +EXPORT_SYMBOL(cmp_connection_init); + +/** + * cmp_connection_destroy - free connection manager resources + * @c: the connection manager + */ +void cmp_connection_destroy(struct cmp_connection *c) +{ + WARN_ON(c->connected); + mutex_destroy(&c->mutex); + fw_iso_resources_destroy(&c->resources); +} +EXPORT_SYMBOL(cmp_connection_destroy); + + +static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr) +{ + ipcr &= ~cpu_to_be32(IPCR_BCAST_CONN | + IPCR_P2P_CONN_MASK | + IPCR_CHANNEL_MASK); + ipcr |= cpu_to_be32(1 << IPCR_P2P_CONN_SHIFT); + ipcr |= cpu_to_be32(c->resources.channel << IPCR_CHANNEL_SHIFT); + + return ipcr; +} + +static int ipcr_set_check(struct cmp_connection *c, __be32 ipcr) +{ + if (ipcr & cpu_to_be32(IPCR_BCAST_CONN | + IPCR_P2P_CONN_MASK)) { + cmp_error(c, "plug is already in use\n"); + return -EBUSY; + } + if (!(ipcr & cpu_to_be32(IPCR_ONLINE))) { + cmp_error(c, "plug is not on-line\n"); + return -ECONNREFUSED; + } + + return 0; +} + +/** + * cmp_connection_establish - establish a connection to the target + * @c: the connection manager + * @max_payload_bytes: the amount of data (including CIP headers) per packet + * + * This function establishes a point-to-point connection from the local + * computer to the target by allocating isochronous resources (channel and + * bandwidth) and setting the target's input plug control register. When this + * function succeeds, the caller is responsible for starting transmitting + * packets. + */ +int cmp_connection_establish(struct cmp_connection *c, + unsigned int max_payload_bytes) +{ + int err; + + if (WARN_ON(c->connected)) + return -EISCONN; + + c->speed = min(c->max_speed, + fw_parent_device(c->resources.unit)->max_speed); + + mutex_lock(&c->mutex); + +retry_after_bus_reset: + err = fw_iso_resources_allocate(&c->resources, + max_payload_bytes, c->speed); + if (err < 0) + goto err_mutex; + + err = pcr_modify(c, ipcr_set_modify, ipcr_set_check, + ABORT_ON_BUS_RESET); + if (err == -EAGAIN) { + fw_iso_resources_free(&c->resources); + goto retry_after_bus_reset; + } + if (err < 0) + goto err_resources; + + c->connected = true; + + mutex_unlock(&c->mutex); + + return 0; + +err_resources: + fw_iso_resources_free(&c->resources); +err_mutex: + mutex_unlock(&c->mutex); + + return err; +} +EXPORT_SYMBOL(cmp_connection_establish); + +/** + * cmp_connection_update - update the connection after a bus reset + * @c: the connection manager + * + * This function must be called from the driver's .update handler to reestablish + * any connection that might have been active. + * + * Returns zero on success, or a negative error code. On an error, the + * connection is broken and the caller must stop transmitting iso packets. + */ +int cmp_connection_update(struct cmp_connection *c) +{ + int err; + + mutex_lock(&c->mutex); + + if (!c->connected) { + mutex_unlock(&c->mutex); + return 0; + } + + err = fw_iso_resources_update(&c->resources); + if (err < 0) + goto err_unconnect; + + err = pcr_modify(c, ipcr_set_modify, ipcr_set_check, + SUCCEED_ON_BUS_RESET); + if (err < 0) + goto err_resources; + + mutex_unlock(&c->mutex); + + return 0; + +err_resources: + fw_iso_resources_free(&c->resources); +err_unconnect: + c->connected = false; + mutex_unlock(&c->mutex); + + return err; +} +EXPORT_SYMBOL(cmp_connection_update); + + +static __be32 ipcr_break_modify(struct cmp_connection *c, __be32 ipcr) +{ + return ipcr & ~cpu_to_be32(IPCR_BCAST_CONN | IPCR_P2P_CONN_MASK); +} + +/** + * cmp_connection_break - break the connection to the target + * @c: the connection manager + * + * This function deactives the connection in the target's input plug control + * register, and frees the isochronous resources of the connection. Before + * calling this function, the caller should cease transmitting packets. + */ +void cmp_connection_break(struct cmp_connection *c) +{ + int err; + + mutex_lock(&c->mutex); + + if (!c->connected) { + mutex_unlock(&c->mutex); + return; + } + + err = pcr_modify(c, ipcr_break_modify, NULL, SUCCEED_ON_BUS_RESET); + if (err < 0) + cmp_error(c, "plug is still connected\n"); + + fw_iso_resources_free(&c->resources); + + c->connected = false; + + mutex_unlock(&c->mutex); +} +EXPORT_SYMBOL(cmp_connection_break); diff --git a/sound/firewire/cmp.h b/sound/firewire/cmp.h new file mode 100644 index 000000000000..f47de08feb12 --- /dev/null +++ b/sound/firewire/cmp.h @@ -0,0 +1,41 @@ +#ifndef SOUND_FIREWIRE_CMP_H_INCLUDED +#define SOUND_FIREWIRE_CMP_H_INCLUDED + +#include +#include +#include "iso-resources.h" + +struct fw_unit; + +/** + * struct cmp_connection - manages an isochronous connection to a device + * @speed: the connection's actual speed + * + * This structure manages (using CMP) an isochronous stream from the local + * computer to a device's input plug (iPCR). + * + * There is no corresponding oPCR created on the local computer, so it is not + * possible to overlay connections on top of this one. + */ +struct cmp_connection { + int speed; + /* private: */ + bool connected; + struct mutex mutex; + struct fw_iso_resources resources; + __be32 last_pcr_value; + unsigned int pcr_index; + unsigned int max_speed; +}; + +int cmp_connection_init(struct cmp_connection *connection, + struct fw_unit *unit, + unsigned int ipcr_index); +void cmp_connection_destroy(struct cmp_connection *connection); + +int cmp_connection_establish(struct cmp_connection *connection, + unsigned int max_payload); +int cmp_connection_update(struct cmp_connection *connection); +void cmp_connection_break(struct cmp_connection *connection); + +#endif diff --git a/sound/firewire/fcp.c b/sound/firewire/fcp.c new file mode 100644 index 000000000000..c20bd9c8f5ab --- /dev/null +++ b/sound/firewire/fcp.c @@ -0,0 +1,223 @@ +/* + * Function Control Protocol (IEC 61883-1) helper functions + * + * 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 "fcp.h" +#include "lib.h" + +#define CTS_AVC 0x00 + +#define ERROR_RETRIES 3 +#define ERROR_DELAY_MS 5 +#define FCP_TIMEOUT_MS 125 + +static DEFINE_SPINLOCK(transactions_lock); +static LIST_HEAD(transactions); + +enum fcp_state { + STATE_PENDING, + STATE_BUS_RESET, + STATE_COMPLETE, +}; + +struct fcp_transaction { + struct list_head list; + struct fw_unit *unit; + void *response_buffer; + unsigned int response_size; + unsigned int response_match_bytes; + enum fcp_state state; + wait_queue_head_t wait; +}; + +/** + * fcp_avc_transaction - send an AV/C command and wait for its response + * @unit: a unit on the target device + * @command: a buffer containing the command frame; must be DMA-able + * @command_size: the size of @command + * @response: a buffer for the response frame + * @response_size: the maximum size of @response + * @response_match_bytes: a bitmap specifying the bytes used to detect the + * correct response frame + * + * This function sends a FCP command frame to the target and waits for the + * corresponding response frame to be returned. + * + * Because it is possible for multiple FCP transactions to be active at the + * same time, the correct response frame is detected by the value of certain + * bytes. These bytes must be set in @response before calling this function, + * and the corresponding bits must be set in @response_match_bytes. + * + * @command and @response can point to the same buffer. + * + * Asynchronous operation (INTERIM, NOTIFY) is not supported at the moment. + * + * Returns the actual size of the response frame, or a negative error code. + */ +int fcp_avc_transaction(struct fw_unit *unit, + const void *command, unsigned int command_size, + void *response, unsigned int response_size, + unsigned int response_match_bytes) +{ + struct fcp_transaction t; + int tcode, ret, tries = 0; + + t.unit = unit; + t.response_buffer = response; + t.response_size = response_size; + t.response_match_bytes = response_match_bytes; + t.state = STATE_PENDING; + init_waitqueue_head(&t.wait); + + spin_lock_irq(&transactions_lock); + list_add_tail(&t.list, &transactions); + spin_unlock_irq(&transactions_lock); + + for (;;) { + tcode = command_size == 4 ? TCODE_WRITE_QUADLET_REQUEST + : TCODE_WRITE_BLOCK_REQUEST; + ret = snd_fw_transaction(t.unit, tcode, + CSR_REGISTER_BASE + CSR_FCP_COMMAND, + (void *)command, command_size); + if (ret < 0) + break; + + wait_event_timeout(t.wait, t.state != STATE_PENDING, + msecs_to_jiffies(FCP_TIMEOUT_MS)); + + if (t.state == STATE_COMPLETE) { + ret = t.response_size; + break; + } else if (t.state == STATE_BUS_RESET) { + msleep(ERROR_DELAY_MS); + } else if (++tries >= ERROR_RETRIES) { + dev_err(&t.unit->device, "FCP command timed out\n"); + ret = -EIO; + break; + } + } + + spin_lock_irq(&transactions_lock); + list_del(&t.list); + spin_unlock_irq(&transactions_lock); + + return ret; +} +EXPORT_SYMBOL(fcp_avc_transaction); + +/** + * fcp_bus_reset - inform the target handler about a bus reset + * @unit: the unit that might be used by fcp_avc_transaction() + * + * This function must be called from the driver's .update handler to inform + * the FCP transaction handler that a bus reset has happened. Any pending FCP + * transactions are retried. + */ +void fcp_bus_reset(struct fw_unit *unit) +{ + struct fcp_transaction *t; + + spin_lock_irq(&transactions_lock); + list_for_each_entry(t, &transactions, list) { + if (t->unit == unit && + t->state == STATE_PENDING) { + t->state = STATE_BUS_RESET; + wake_up(&t->wait); + } + } + spin_unlock_irq(&transactions_lock); +} +EXPORT_SYMBOL(fcp_bus_reset); + +/* checks whether the response matches the masked bytes in response_buffer */ +static bool is_matching_response(struct fcp_transaction *transaction, + const void *response, size_t length) +{ + const u8 *p1, *p2; + unsigned int mask, i; + + p1 = response; + p2 = transaction->response_buffer; + mask = transaction->response_match_bytes; + + for (i = 0; ; ++i) { + if ((mask & 1) && p1[i] != p2[i]) + return false; + mask >>= 1; + if (!mask) + return true; + if (--length == 0) + return false; + } +} + +static void fcp_response(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 fcp_transaction *t; + unsigned long flags; + + if (length < 1 || (*(const u8 *)data & 0xf0) != CTS_AVC) + return; + + spin_lock_irqsave(&transactions_lock, flags); + list_for_each_entry(t, &transactions, list) { + struct fw_device *device = fw_parent_device(t->unit); + if (device->card != card || + device->generation != generation) + continue; + smp_rmb(); /* node_id vs. generation */ + if (device->node_id != source) + continue; + + if (t->state == STATE_PENDING && + is_matching_response(t, data, length)) { + t->state = STATE_COMPLETE; + t->response_size = min((unsigned int)length, + t->response_size); + memcpy(t->response_buffer, data, t->response_size); + wake_up(&t->wait); + } + } + spin_unlock_irqrestore(&transactions_lock, flags); +} + +static struct fw_address_handler response_register_handler = { + .length = 0x200, + .address_callback = fcp_response, +}; + +static int __init fcp_module_init(void) +{ + static const struct fw_address_region response_register_region = { + .start = CSR_REGISTER_BASE + CSR_FCP_RESPONSE, + .end = CSR_REGISTER_BASE + CSR_FCP_END, + }; + + fw_core_add_address_handler(&response_register_handler, + &response_register_region); + + return 0; +} + +static void __exit fcp_module_exit(void) +{ + WARN_ON(!list_empty(&transactions)); + fw_core_remove_address_handler(&response_register_handler); +} + +module_init(fcp_module_init); +module_exit(fcp_module_exit); diff --git a/sound/firewire/fcp.h b/sound/firewire/fcp.h new file mode 100644 index 000000000000..86595688bd91 --- /dev/null +++ b/sound/firewire/fcp.h @@ -0,0 +1,12 @@ +#ifndef SOUND_FIREWIRE_FCP_H_INCLUDED +#define SOUND_FIREWIRE_FCP_H_INCLUDED + +struct fw_unit; + +int fcp_avc_transaction(struct fw_unit *unit, + const void *command, unsigned int command_size, + void *response, unsigned int response_size, + unsigned int response_match_bytes); +void fcp_bus_reset(struct fw_unit *unit); + +#endif diff --git a/sound/firewire/iso-resources.c b/sound/firewire/iso-resources.c new file mode 100644 index 000000000000..6f2b5f8651fd --- /dev/null +++ b/sound/firewire/iso-resources.c @@ -0,0 +1,224 @@ +/* + * isochronous resources helper functions + * + * Copyright (c) Clemens Ladisch + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "iso-resources.h" + +/** + * fw_iso_resources_init - initializes a &struct fw_iso_resources + * @r: the resource manager to initialize + * @unit: the device unit for which the resources will be needed + * + * If the device does not support all channel numbers, change @r->channels_mask + * after calling this function. + */ +void fw_iso_resources_init(struct fw_iso_resources *r, struct fw_unit *unit) +{ + r->channels_mask = ~0uLL; + r->unit = fw_unit_get(unit); + mutex_init(&r->mutex); + r->allocated = false; +} + +/** + * fw_iso_resources_destroy - destroy a resource manager + * @r: the resource manager that is no longer needed + */ +void fw_iso_resources_destroy(struct fw_iso_resources *r) +{ + WARN_ON(r->allocated); + mutex_destroy(&r->mutex); + fw_unit_put(r->unit); +} + +static unsigned int packet_bandwidth(unsigned int max_payload_bytes, int speed) +{ + unsigned int bytes, s400_bytes; + + /* iso packets have three header quadlets and quadlet-aligned payload */ + bytes = 3 * 4 + ALIGN(max_payload_bytes, 4); + + /* convert to bandwidth units (quadlets at S1600 = bytes at S400) */ + if (speed <= SCODE_400) + s400_bytes = bytes * (1 << (SCODE_400 - speed)); + else + s400_bytes = DIV_ROUND_UP(bytes, 1 << (speed - SCODE_400)); + + return s400_bytes; +} + +static int current_bandwidth_overhead(struct fw_card *card) +{ + /* + * Under the usual pessimistic assumption (cable length 4.5 m), the + * isochronous overhead for N cables is 1.797 µs + N * 0.494 µs, or + * 88.3 + N * 24.3 in bandwidth units. + * + * The calculation below tries to deduce N from the current gap count. + * If the gap count has been optimized by measuring the actual packet + * transmission time, this derived overhead should be near the actual + * overhead as well. + */ + return card->gap_count < 63 ? card->gap_count * 97 / 10 + 89 : 512; +} + +static int wait_isoch_resource_delay_after_bus_reset(struct fw_card *card) +{ + for (;;) { + s64 delay = (card->reset_jiffies + HZ) - get_jiffies_64(); + if (delay <= 0) + return 0; + if (schedule_timeout_interruptible(delay) > 0) + return -ERESTARTSYS; + } +} + +/** + * fw_iso_resources_allocate - allocate isochronous channel and bandwidth + * @r: the resource manager + * @max_payload_bytes: the amount of data (including CIP headers) per packet + * @speed: the speed (e.g., SCODE_400) at which the packets will be sent + * + * This function allocates one isochronous channel and enough bandwidth for the + * specified packet size. + * + * Returns the channel number that the caller must use for streaming, or + * a negative error code. Due to potentionally long delays, this function is + * interruptible and can return -ERESTARTSYS. On success, the caller is + * responsible for calling fw_iso_resources_update() on bus resets, and + * fw_iso_resources_free() when the resources are not longer needed. + */ +int fw_iso_resources_allocate(struct fw_iso_resources *r, + unsigned int max_payload_bytes, int speed) +{ + struct fw_card *card = fw_parent_device(r->unit)->card; + int bandwidth, channel, err; + + if (WARN_ON(r->allocated)) + return -EBADFD; + + r->bandwidth = packet_bandwidth(max_payload_bytes, speed); + +retry_after_bus_reset: + spin_lock_irq(&card->lock); + r->generation = card->generation; + r->bandwidth_overhead = current_bandwidth_overhead(card); + spin_unlock_irq(&card->lock); + + err = wait_isoch_resource_delay_after_bus_reset(card); + if (err < 0) + return err; + + mutex_lock(&r->mutex); + + bandwidth = r->bandwidth + r->bandwidth_overhead; + fw_iso_resource_manage(card, r->generation, r->channels_mask, + &channel, &bandwidth, true, r->buffer); + if (channel == -EAGAIN) { + mutex_unlock(&r->mutex); + goto retry_after_bus_reset; + } + if (channel >= 0) { + r->channel = channel; + r->allocated = true; + } else { + if (channel == -EBUSY) + dev_err(&r->unit->device, + "isochronous resources exhausted\n"); + else + dev_err(&r->unit->device, + "isochronous resource allocation failed\n"); + } + + mutex_unlock(&r->mutex); + + return channel; +} + +/** + * fw_iso_resources_update - update resource allocations after a bus reset + * @r: the resource manager + * + * This function must be called from the driver's .update handler to reallocate + * any resources that were allocated before the bus reset. It is safe to call + * this function if no resources are currently allocated. + * + * Returns a negative error code on failure. If this happens, the caller must + * stop streaming. + */ +int fw_iso_resources_update(struct fw_iso_resources *r) +{ + struct fw_card *card = fw_parent_device(r->unit)->card; + int bandwidth, channel; + + mutex_lock(&r->mutex); + + if (!r->allocated) { + mutex_unlock(&r->mutex); + return 0; + } + + spin_lock_irq(&card->lock); + r->generation = card->generation; + r->bandwidth_overhead = current_bandwidth_overhead(card); + spin_unlock_irq(&card->lock); + + bandwidth = r->bandwidth + r->bandwidth_overhead; + + fw_iso_resource_manage(card, r->generation, 1uLL << r->channel, + &channel, &bandwidth, true, r->buffer); + /* + * When another bus reset happens, pretend that the allocation + * succeeded; we will try again for the new generation later. + */ + if (channel < 0 && channel != -EAGAIN) { + r->allocated = false; + if (channel == -EBUSY) + dev_err(&r->unit->device, + "isochronous resources exhausted\n"); + else + dev_err(&r->unit->device, + "isochronous resource allocation failed\n"); + } + + mutex_unlock(&r->mutex); + + return channel; +} + +/** + * fw_iso_resources_free - frees allocated resources + * @r: the resource manager + * + * This function deallocates the channel and bandwidth, if allocated. + */ +void fw_iso_resources_free(struct fw_iso_resources *r) +{ + struct fw_card *card = fw_parent_device(r->unit)->card; + int bandwidth, channel; + + mutex_lock(&r->mutex); + + if (r->allocated) { + bandwidth = r->bandwidth + r->bandwidth_overhead; + fw_iso_resource_manage(card, r->generation, 1uLL << r->channel, + &channel, &bandwidth, false, r->buffer); + if (channel < 0) + dev_err(&r->unit->device, + "isochronous resource deallocation failed\n"); + + r->allocated = false; + } + + mutex_unlock(&r->mutex); +} diff --git a/sound/firewire/iso-resources.h b/sound/firewire/iso-resources.h new file mode 100644 index 000000000000..9feb9f8d4745 --- /dev/null +++ b/sound/firewire/iso-resources.h @@ -0,0 +1,39 @@ +#ifndef SOUND_FIREWIRE_ISO_RESOURCES_H_INCLUDED +#define SOUND_FIREWIRE_ISO_RESOURCES_H_INCLUDED + +#include +#include + +struct fw_unit; + +/** + * struct fw_iso_resources - manages channel/bandwidth allocation + * @channels_mask: if the device does not support all channel numbers, set this + * bit mask to something else than the default (all ones) + * + * This structure manages (de)allocation of isochronous resources (channel and + * bandwidth) for one isochronous stream. + */ +struct fw_iso_resources { + u64 channels_mask; + /* private: */ + struct fw_unit *unit; + struct mutex mutex; + unsigned int channel; + unsigned int bandwidth; /* in bandwidth units, without overhead */ + unsigned int bandwidth_overhead; + int generation; /* in which allocation is valid */ + bool allocated; + __be32 buffer[2]; +}; + +void fw_iso_resources_init(struct fw_iso_resources *r, + struct fw_unit *unit); +void fw_iso_resources_destroy(struct fw_iso_resources *r); + +int fw_iso_resources_allocate(struct fw_iso_resources *r, + unsigned int max_payload_bytes, int speed); +int fw_iso_resources_update(struct fw_iso_resources *r); +void fw_iso_resources_free(struct fw_iso_resources *r); + +#endif diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c new file mode 100644 index 000000000000..4750cea2210e --- /dev/null +++ b/sound/firewire/lib.c @@ -0,0 +1,85 @@ +/* + * miscellaneous helper functions + * + * Copyright (c) Clemens Ladisch + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include +#include +#include +#include +#include "lib.h" + +#define ERROR_RETRY_DELAY_MS 5 + +/** + * rcode_string - convert a firewire result code to a string + * @rcode: the result + */ +const char *rcode_string(unsigned int rcode) +{ + static const char *const names[] = { + [RCODE_COMPLETE] = "complete", + [RCODE_CONFLICT_ERROR] = "conflict error", + [RCODE_DATA_ERROR] = "data error", + [RCODE_TYPE_ERROR] = "type error", + [RCODE_ADDRESS_ERROR] = "address error", + [RCODE_SEND_ERROR] = "send error", + [RCODE_CANCELLED] = "cancelled", + [RCODE_BUSY] = "busy", + [RCODE_GENERATION] = "generation", + [RCODE_NO_ACK] = "no ack", + }; + + if (rcode < ARRAY_SIZE(names) && names[rcode]) + return names[rcode]; + else + return "unknown"; +} +EXPORT_SYMBOL(rcode_string); + +/** + * snd_fw_transaction - send a request and wait for its completion + * @unit: the driver's unit on the target device + * @tcode: the transaction code + * @offset: the address in the target's address space + * @buffer: input/output data + * @length: length of @buffer + * + * Submits an asynchronous request to the target device, and waits for the + * response. The node ID and the current generation are derived from @unit. + * On a bus reset or an error, the transaction is retried a few times. + * Returns zero on success, or a negative error code. + */ +int snd_fw_transaction(struct fw_unit *unit, int tcode, + u64 offset, void *buffer, size_t length) +{ + struct fw_device *device = fw_parent_device(unit); + int generation, rcode, tries = 0; + + for (;;) { + generation = device->generation; + smp_rmb(); /* node_id vs. generation */ + rcode = fw_run_transaction(device->card, tcode, + device->node_id, generation, + device->max_speed, offset, + buffer, length); + + if (rcode == RCODE_COMPLETE) + return 0; + + if (rcode_is_permanent_error(rcode) || ++tries >= 3) { + dev_err(&unit->device, "transaction failed: %s\n", + rcode_string(rcode)); + return -EIO; + } + + msleep(ERROR_RETRY_DELAY_MS); + } +} +EXPORT_SYMBOL(snd_fw_transaction); + +MODULE_DESCRIPTION("FireWire audio helper functions"); +MODULE_AUTHOR("Clemens Ladisch "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h new file mode 100644 index 000000000000..064f3fd9ab06 --- /dev/null +++ b/sound/firewire/lib.h @@ -0,0 +1,19 @@ +#ifndef SOUND_FIREWIRE_LIB_H_INCLUDED +#define SOUND_FIREWIRE_LIB_H_INCLUDED + +#include +#include + +struct fw_unit; + +int snd_fw_transaction(struct fw_unit *unit, int tcode, + u64 offset, void *buffer, size_t length); +const char *rcode_string(unsigned int rcode); + +/* returns true if retrying the transaction would not make sense */ +static inline bool rcode_is_permanent_error(int rcode) +{ + return rcode == RCODE_TYPE_ERROR || rcode == RCODE_ADDRESS_ERROR; +} + +#endif diff --git a/sound/firewire/packets-buffer.c b/sound/firewire/packets-buffer.c new file mode 100644 index 000000000000..1e20e60ba6a6 --- /dev/null +++ b/sound/firewire/packets-buffer.c @@ -0,0 +1,74 @@ +/* + * helpers for managing a buffer for many packets + * + * Copyright (c) Clemens Ladisch + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include +#include +#include "packets-buffer.h" + +/** + * iso_packets_buffer_init - allocates the memory for packets + * @b: the buffer structure to initialize + * @unit: the device at the other end of the stream + * @count: the number of packets + * @packet_size: the (maximum) size of a packet, in bytes + * @direction: %DMA_TO_DEVICE or %DMA_FROM_DEVICE + */ +int iso_packets_buffer_init(struct iso_packets_buffer *b, struct fw_unit *unit, + unsigned int count, unsigned int packet_size, + enum dma_data_direction direction) +{ + unsigned int packets_per_page, pages; + unsigned int i, page_index, offset_in_page; + void *p; + int err; + + b->packets = kmalloc(count * sizeof(*b->packets), GFP_KERNEL); + if (!b->packets) { + err = -ENOMEM; + goto error; + } + + packet_size = L1_CACHE_ALIGN(packet_size); + packets_per_page = PAGE_SIZE / packet_size; + if (WARN_ON(!packets_per_page)) { + err = -EINVAL; + goto error; + } + pages = DIV_ROUND_UP(count, packets_per_page); + + err = fw_iso_buffer_init(&b->iso_buffer, fw_parent_device(unit)->card, + pages, direction); + if (err < 0) + goto err_packets; + + for (i = 0; i < count; ++i) { + page_index = i / packets_per_page; + p = page_address(b->iso_buffer.pages[page_index]); + offset_in_page = (i % packets_per_page) * packet_size; + b->packets[i].buffer = p + offset_in_page; + b->packets[i].offset = page_index * PAGE_SIZE + offset_in_page; + } + + return 0; + +err_packets: + kfree(b->packets); +error: + return err; +} + +/** + * iso_packets_buffer_destroy - frees packet buffer resources + * @b: the buffer structure to free + * @unit: the device at the other end of the stream + */ +void iso_packets_buffer_destroy(struct iso_packets_buffer *b, + struct fw_unit *unit) +{ + fw_iso_buffer_destroy(&b->iso_buffer, fw_parent_device(unit)->card); + kfree(b->packets); +} diff --git a/sound/firewire/packets-buffer.h b/sound/firewire/packets-buffer.h new file mode 100644 index 000000000000..6513c5cb6ea9 --- /dev/null +++ b/sound/firewire/packets-buffer.h @@ -0,0 +1,26 @@ +#ifndef SOUND_FIREWIRE_PACKETS_BUFFER_H_INCLUDED +#define SOUND_FIREWIRE_PACKETS_BUFFER_H_INCLUDED + +#include +#include + +/** + * struct iso_packets_buffer - manages a buffer for many packets + * @iso_buffer: the memory containing the packets + * @packets: an array, with each element pointing to one packet + */ +struct iso_packets_buffer { + struct fw_iso_buffer iso_buffer; + struct { + void *buffer; + unsigned int offset; + } *packets; +}; + +int iso_packets_buffer_init(struct iso_packets_buffer *b, struct fw_unit *unit, + unsigned int count, unsigned int packet_size, + enum dma_data_direction direction); +void iso_packets_buffer_destroy(struct iso_packets_buffer *b, + struct fw_unit *unit); + +#endif diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c new file mode 100644 index 000000000000..f6b095ef075a --- /dev/null +++ b/sound/firewire/speakers.c @@ -0,0 +1,855 @@ +/* + * OXFW970-based speakers 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 "cmp.h" +#include "fcp.h" +#include "amdtp.h" +#include "lib.h" + +#define OXFORD_FIRMWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x50000) +/* 0x970?vvvv or 0x971?vvvv, where vvvv = firmware version */ + +#define OXFORD_HARDWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x90020) +#define OXFORD_HARDWARE_ID_OXFW970 0x39443841 +#define OXFORD_HARDWARE_ID_OXFW971 0x39373100 + +#define VENDOR_GRIFFIN 0x001292 +#define VENDOR_LACIE 0x00d04b + +#define SPECIFIER_1394TA 0x00a02d +#define VERSION_AVC 0x010001 + +struct device_info { + const char *driver_name; + const char *short_name; + const char *long_name; + int (*pcm_constraints)(struct snd_pcm_runtime *runtime); + unsigned int mixer_channels; + u8 mute_fb_id; + u8 volume_fb_id; +}; + +struct fwspk { + struct snd_card *card; + struct fw_unit *unit; + const struct device_info *device_info; + struct snd_pcm_substream *pcm; + struct mutex mutex; + struct cmp_connection connection; + struct amdtp_out_stream stream; + bool stream_running; + bool mute; + s16 volume[6]; + s16 volume_min; + s16 volume_max; +}; + +MODULE_DESCRIPTION("FireWire speakers driver"); +MODULE_AUTHOR("Clemens Ladisch "); +MODULE_LICENSE("GPL v2"); + +static int firewave_rate_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + static unsigned int stereo_rates[] = { 48000, 96000 }; + struct snd_interval *channels = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *rate = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + + /* two channels work only at 48/96 kHz */ + if (snd_interval_max(channels) < 6) + return snd_interval_list(rate, 2, stereo_rates, 0); + return 0; +} + +static int firewave_channels_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + static const struct snd_interval all_channels = { .min = 6, .max = 6 }; + struct snd_interval *rate = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + + /* 32/44.1 kHz work only with all six channels */ + if (snd_interval_max(rate) < 48000) + return snd_interval_refine(channels, &all_channels); + return 0; +} + +static int firewave_constraints(struct snd_pcm_runtime *runtime) +{ + static unsigned int channels_list[] = { 2, 6 }; + static struct snd_pcm_hw_constraint_list channels_list_constraint = { + .count = 2, + .list = channels_list, + }; + int err; + + runtime->hw.rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000; + runtime->hw.channels_max = 6; + + err = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + &channels_list_constraint); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + firewave_rate_constraint, NULL, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + firewave_channels_constraint, NULL, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + + return 0; +} + +static int lacie_speakers_constraints(struct snd_pcm_runtime *runtime) +{ + runtime->hw.rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000; + + return 0; +} + +static int fwspk_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, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 4 * 1024 * 1024, + .period_bytes_min = 1, + .period_bytes_max = UINT_MAX, + .periods_min = 1, + .periods_max = UINT_MAX, + }; + struct fwspk *fwspk = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + runtime->hw = hardware; + + err = fwspk->device_info->pcm_constraints(runtime); + if (err < 0) + return err; + err = snd_pcm_limit_hw_rates(runtime); + if (err < 0) + return err; + + 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 fwspk_close(struct snd_pcm_substream *substream) +{ + return 0; +} + +static void fwspk_stop_stream(struct fwspk *fwspk) +{ + if (fwspk->stream_running) { + amdtp_out_stream_stop(&fwspk->stream); + cmp_connection_break(&fwspk->connection); + fwspk->stream_running = false; + } +} + +static int fwspk_set_rate(struct fwspk *fwspk, unsigned int sfc) +{ + u8 *buf; + int err; + + buf = kmalloc(8, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf[0] = 0x00; /* AV/C, CONTROL */ + buf[1] = 0xff; /* unit */ + buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */ + buf[3] = 0x00; /* plug 0 */ + buf[4] = 0x90; /* format: audio */ + buf[5] = 0x00 | sfc; /* AM824, frequency */ + buf[6] = 0xff; /* SYT (not used) */ + buf[7] = 0xff; + + err = fcp_avc_transaction(fwspk->unit, buf, 8, buf, 8, + BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5)); + if (err < 0) + goto error; + if (err < 6 || buf[0] != 0x09 /* ACCEPTED */) { + dev_err(&fwspk->unit->device, "failed to set sample rate\n"); + err = -EIO; + goto error; + } + + err = 0; + +error: + kfree(buf); + + return err; +} + +static int fwspk_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct fwspk *fwspk = substream->private_data; + int err; + + mutex_lock(&fwspk->mutex); + fwspk_stop_stream(fwspk); + mutex_unlock(&fwspk->mutex); + + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + goto error; + + amdtp_out_stream_set_rate(&fwspk->stream, params_rate(hw_params)); + amdtp_out_stream_set_pcm(&fwspk->stream, params_channels(hw_params)); + + amdtp_out_stream_set_pcm_format(&fwspk->stream, + params_format(hw_params)); + + err = fwspk_set_rate(fwspk, fwspk->stream.sfc); + if (err < 0) + goto err_buffer; + + return 0; + +err_buffer: + snd_pcm_lib_free_vmalloc_buffer(substream); +error: + return err; +} + +static int fwspk_hw_free(struct snd_pcm_substream *substream) +{ + struct fwspk *fwspk = substream->private_data; + + mutex_lock(&fwspk->mutex); + fwspk_stop_stream(fwspk); + mutex_unlock(&fwspk->mutex); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int fwspk_prepare(struct snd_pcm_substream *substream) +{ + struct fwspk *fwspk = substream->private_data; + int err; + + mutex_lock(&fwspk->mutex); + + if (!fwspk->stream_running) { + err = cmp_connection_establish(&fwspk->connection, + amdtp_out_stream_get_max_payload(&fwspk->stream)); + if (err < 0) + goto err_mutex; + + err = amdtp_out_stream_start(&fwspk->stream, + fwspk->connection.resources.channel, + fwspk->connection.speed); + if (err < 0) + goto err_connection; + + fwspk->stream_running = true; + } + + mutex_unlock(&fwspk->mutex); + + amdtp_out_stream_pcm_prepare(&fwspk->stream); + + return 0; + +err_connection: + cmp_connection_break(&fwspk->connection); +err_mutex: + mutex_unlock(&fwspk->mutex); + + return err; +} + +static int fwspk_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct fwspk *fwspk = 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(&fwspk->stream, pcm); + return 0; +} + +static snd_pcm_uframes_t fwspk_pointer(struct snd_pcm_substream *substream) +{ + struct fwspk *fwspk = substream->private_data; + + return amdtp_out_stream_pcm_pointer(&fwspk->stream); +} + +static int fwspk_create_pcm(struct fwspk *fwspk) +{ + static struct snd_pcm_ops ops = { + .open = fwspk_open, + .close = fwspk_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = fwspk_hw_params, + .hw_free = fwspk_hw_free, + .prepare = fwspk_prepare, + .trigger = fwspk_trigger, + .pointer = fwspk_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; + struct snd_pcm *pcm; + int err; + + err = snd_pcm_new(fwspk->card, "OXFW970", 0, 1, 0, &pcm); + if (err < 0) + return err; + pcm->private_data = fwspk; + strcpy(pcm->name, fwspk->device_info->short_name); + fwspk->pcm = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + fwspk->pcm->ops = &ops; + return 0; +} + +enum control_action { CTL_READ, CTL_WRITE }; +enum control_attribute { + CTL_MIN = 0x02, + CTL_MAX = 0x03, + CTL_CURRENT = 0x10, +}; + +static int fwspk_mute_command(struct fwspk *fwspk, bool *value, + enum control_action action) +{ + u8 *buf; + u8 response_ok; + int err; + + buf = kmalloc(11, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (action == CTL_READ) { + buf[0] = 0x01; /* AV/C, STATUS */ + response_ok = 0x0c; /* STABLE */ + } else { + buf[0] = 0x00; /* AV/C, CONTROL */ + response_ok = 0x09; /* ACCEPTED */ + } + buf[1] = 0x08; /* audio unit 0 */ + buf[2] = 0xb8; /* FUNCTION BLOCK */ + buf[3] = 0x81; /* function block type: feature */ + buf[4] = fwspk->device_info->mute_fb_id; /* function block ID */ + buf[5] = 0x10; /* control attribute: current */ + buf[6] = 0x02; /* selector length */ + buf[7] = 0x00; /* audio channel number */ + buf[8] = 0x01; /* control selector: mute */ + buf[9] = 0x01; /* control data length */ + if (action == CTL_READ) + buf[10] = 0xff; + else + buf[10] = *value ? 0x70 : 0x60; + + err = fcp_avc_transaction(fwspk->unit, buf, 11, buf, 11, 0x3fe); + if (err < 0) + goto error; + if (err < 11) { + dev_err(&fwspk->unit->device, "short FCP response\n"); + err = -EIO; + goto error; + } + if (buf[0] != response_ok) { + dev_err(&fwspk->unit->device, "mute command failed\n"); + err = -EIO; + goto error; + } + if (action == CTL_READ) + *value = buf[10] == 0x70; + + err = 0; + +error: + kfree(buf); + + return err; +} + +static int fwspk_volume_command(struct fwspk *fwspk, s16 *value, + unsigned int channel, + enum control_attribute attribute, + enum control_action action) +{ + u8 *buf; + u8 response_ok; + int err; + + buf = kmalloc(12, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (action == CTL_READ) { + buf[0] = 0x01; /* AV/C, STATUS */ + response_ok = 0x0c; /* STABLE */ + } else { + buf[0] = 0x00; /* AV/C, CONTROL */ + response_ok = 0x09; /* ACCEPTED */ + } + buf[1] = 0x08; /* audio unit 0 */ + buf[2] = 0xb8; /* FUNCTION BLOCK */ + buf[3] = 0x81; /* function block type: feature */ + buf[4] = fwspk->device_info->volume_fb_id; /* function block ID */ + buf[5] = attribute; /* control attribute */ + buf[6] = 0x02; /* selector length */ + buf[7] = channel; /* audio channel number */ + buf[8] = 0x02; /* control selector: volume */ + buf[9] = 0x02; /* control data length */ + if (action == CTL_READ) { + buf[10] = 0xff; + buf[11] = 0xff; + } else { + buf[10] = *value >> 8; + buf[11] = *value; + } + + err = fcp_avc_transaction(fwspk->unit, buf, 12, buf, 12, 0x3fe); + if (err < 0) + goto error; + if (err < 12) { + dev_err(&fwspk->unit->device, "short FCP response\n"); + err = -EIO; + goto error; + } + if (buf[0] != response_ok) { + dev_err(&fwspk->unit->device, "volume command failed\n"); + err = -EIO; + goto error; + } + if (action == CTL_READ) + *value = (buf[10] << 8) | buf[11]; + + err = 0; + +error: + kfree(buf); + + return err; +} + +static int fwspk_mute_get(struct snd_kcontrol *control, + struct snd_ctl_elem_value *value) +{ + struct fwspk *fwspk = control->private_data; + + value->value.integer.value[0] = !fwspk->mute; + + return 0; +} + +static int fwspk_mute_put(struct snd_kcontrol *control, + struct snd_ctl_elem_value *value) +{ + struct fwspk *fwspk = control->private_data; + bool mute; + int err; + + mute = !value->value.integer.value[0]; + + if (mute == fwspk->mute) + return 0; + + err = fwspk_mute_command(fwspk, &mute, CTL_WRITE); + if (err < 0) + return err; + fwspk->mute = mute; + + return 1; +} + +static int fwspk_volume_info(struct snd_kcontrol *control, + struct snd_ctl_elem_info *info) +{ + struct fwspk *fwspk = control->private_data; + + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = fwspk->device_info->mixer_channels; + info->value.integer.min = fwspk->volume_min; + info->value.integer.max = fwspk->volume_max; + + return 0; +} + +static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 }; + +static int fwspk_volume_get(struct snd_kcontrol *control, + struct snd_ctl_elem_value *value) +{ + struct fwspk *fwspk = control->private_data; + unsigned int i; + + for (i = 0; i < fwspk->device_info->mixer_channels; ++i) + value->value.integer.value[channel_map[i]] = fwspk->volume[i]; + + return 0; +} + +static int fwspk_volume_put(struct snd_kcontrol *control, + struct snd_ctl_elem_value *value) +{ + struct fwspk *fwspk = control->private_data; + unsigned int i, changed_channels; + bool equal_values = true; + s16 volume; + int err; + + for (i = 0; i < fwspk->device_info->mixer_channels; ++i) { + if (value->value.integer.value[i] < fwspk->volume_min || + value->value.integer.value[i] > fwspk->volume_max) + return -EINVAL; + if (value->value.integer.value[i] != + value->value.integer.value[0]) + equal_values = false; + } + + changed_channels = 0; + for (i = 0; i < fwspk->device_info->mixer_channels; ++i) + if (value->value.integer.value[channel_map[i]] != + fwspk->volume[i]) + changed_channels |= 1 << (i + 1); + + if (equal_values && changed_channels != 0) + changed_channels = 1 << 0; + + for (i = 0; i <= fwspk->device_info->mixer_channels; ++i) { + volume = value->value.integer.value[channel_map[i ? i - 1 : 0]]; + if (changed_channels & (1 << i)) { + err = fwspk_volume_command(fwspk, &volume, i, + CTL_CURRENT, CTL_WRITE); + if (err < 0) + return err; + } + if (i > 0) + fwspk->volume[i - 1] = volume; + } + + return changed_channels != 0; +} + +static int fwspk_create_mixer(struct fwspk *fwspk) +{ + static const struct snd_kcontrol_new controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Switch", + .info = snd_ctl_boolean_mono_info, + .get = fwspk_mute_get, + .put = fwspk_mute_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Volume", + .info = fwspk_volume_info, + .get = fwspk_volume_get, + .put = fwspk_volume_put, + }, + }; + unsigned int i, first_ch; + int err; + + err = fwspk_volume_command(fwspk, &fwspk->volume_min, + 0, CTL_MIN, CTL_READ); + if (err < 0) + return err; + err = fwspk_volume_command(fwspk, &fwspk->volume_max, + 0, CTL_MAX, CTL_READ); + if (err < 0) + return err; + + err = fwspk_mute_command(fwspk, &fwspk->mute, CTL_READ); + if (err < 0) + return err; + + first_ch = fwspk->device_info->mixer_channels == 1 ? 0 : 1; + for (i = 0; i < fwspk->device_info->mixer_channels; ++i) { + err = fwspk_volume_command(fwspk, &fwspk->volume[i], + first_ch + i, CTL_CURRENT, CTL_READ); + if (err < 0) + return err; + } + + for (i = 0; i < ARRAY_SIZE(controls); ++i) { + err = snd_ctl_add(fwspk->card, + snd_ctl_new1(&controls[i], fwspk)); + if (err < 0) + return err; + } + + return 0; +} + +static u32 fwspk_read_firmware_version(struct fw_unit *unit) +{ + __be32 data; + int err; + + err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, + OXFORD_FIRMWARE_ID_ADDRESS, &data, 4); + return err >= 0 ? be32_to_cpu(data) : 0; +} + +static void fwspk_card_free(struct snd_card *card) +{ + struct fwspk *fwspk = card->private_data; + struct fw_device *dev = fw_parent_device(fwspk->unit); + + amdtp_out_stream_destroy(&fwspk->stream); + cmp_connection_destroy(&fwspk->connection); + fw_unit_put(fwspk->unit); + fw_device_put(dev); + mutex_destroy(&fwspk->mutex); +} + +static const struct device_info *__devinit fwspk_detect(struct fw_device *dev) +{ + static const struct device_info griffin_firewave = { + .driver_name = "FireWave", + .short_name = "FireWave", + .long_name = "Griffin FireWave Surround", + .pcm_constraints = firewave_constraints, + .mixer_channels = 6, + .mute_fb_id = 0x01, + .volume_fb_id = 0x02, + }; + static const struct device_info lacie_speakers = { + .driver_name = "FWSpeakers", + .short_name = "FireWire Speakers", + .long_name = "LaCie FireWire Speakers", + .pcm_constraints = lacie_speakers_constraints, + .mixer_channels = 1, + .mute_fb_id = 0x01, + .volume_fb_id = 0x01, + }; + struct fw_csr_iterator i; + int key, value; + + fw_csr_iterator_init(&i, dev->config_rom); + while (fw_csr_iterator_next(&i, &key, &value)) + if (key == CSR_VENDOR) + switch (value) { + case VENDOR_GRIFFIN: + return &griffin_firewave; + case VENDOR_LACIE: + return &lacie_speakers; + } + + return NULL; +} + +static int __devinit fwspk_probe(struct device *unit_dev) +{ + struct fw_unit *unit = fw_unit(unit_dev); + struct fw_device *fw_dev = fw_parent_device(unit); + struct snd_card *card; + struct fwspk *fwspk; + u32 firmware; + int err; + + err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*fwspk), &card); + if (err < 0) + return err; + snd_card_set_dev(card, unit_dev); + + fwspk = card->private_data; + fwspk->card = card; + mutex_init(&fwspk->mutex); + fw_device_get(fw_dev); + fwspk->unit = fw_unit_get(unit); + fwspk->device_info = fwspk_detect(fw_dev); + if (!fwspk->device_info) { + err = -ENODEV; + goto err_unit; + } + + err = cmp_connection_init(&fwspk->connection, unit, 0); + if (err < 0) + goto err_unit; + + err = amdtp_out_stream_init(&fwspk->stream, unit, CIP_NONBLOCKING); + if (err < 0) + goto err_connection; + + card->private_free = fwspk_card_free; + + strcpy(card->driver, fwspk->device_info->driver_name); + strcpy(card->shortname, fwspk->device_info->short_name); + firmware = fwspk_read_firmware_version(unit); + snprintf(card->longname, sizeof(card->longname), + "%s (OXFW%x %04x), GUID %08x%08x at %s, S%d", + fwspk->device_info->long_name, + firmware >> 20, firmware & 0xffff, + fw_dev->config_rom[3], fw_dev->config_rom[4], + dev_name(&unit->device), 100 << fw_dev->max_speed); + strcpy(card->mixername, "OXFW970"); + + err = fwspk_create_pcm(fwspk); + if (err < 0) + goto error; + + err = fwspk_create_mixer(fwspk); + if (err < 0) + goto error; + + err = snd_card_register(card); + if (err < 0) + goto error; + + dev_set_drvdata(unit_dev, fwspk); + + return 0; + +err_connection: + cmp_connection_destroy(&fwspk->connection); +err_unit: + fw_unit_put(fwspk->unit); + fw_device_put(fw_dev); + mutex_destroy(&fwspk->mutex); +error: + snd_card_free(card); + return err; +} + +static int __devexit fwspk_remove(struct device *dev) +{ + struct fwspk *fwspk = dev_get_drvdata(dev); + + snd_card_disconnect(fwspk->card); + + mutex_lock(&fwspk->mutex); + amdtp_out_stream_pcm_abort(&fwspk->stream); + fwspk_stop_stream(fwspk); + mutex_unlock(&fwspk->mutex); + + snd_card_free_when_closed(fwspk->card); + + return 0; +} + +static void fwspk_bus_reset(struct fw_unit *unit) +{ + struct fwspk *fwspk = dev_get_drvdata(&unit->device); + + fcp_bus_reset(fwspk->unit); + + if (cmp_connection_update(&fwspk->connection) < 0) { + mutex_lock(&fwspk->mutex); + amdtp_out_stream_pcm_abort(&fwspk->stream); + fwspk_stop_stream(fwspk); + mutex_unlock(&fwspk->mutex); + return; + } + + amdtp_out_stream_update(&fwspk->stream); +} + +static const struct ieee1394_device_id fwspk_id_table[] = { + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID | + IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION, + .vendor_id = VENDOR_GRIFFIN, + .model_id = 0x00f970, + .specifier_id = SPECIFIER_1394TA, + .version = VERSION_AVC, + }, + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID | + IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION, + .vendor_id = VENDOR_LACIE, + .model_id = 0x00f970, + .specifier_id = SPECIFIER_1394TA, + .version = VERSION_AVC, + }, + { } +}; +MODULE_DEVICE_TABLE(ieee1394, fwspk_id_table); + +static struct fw_driver fwspk_driver = { + .driver = { + .owner = THIS_MODULE, + .name = KBUILD_MODNAME, + .bus = &fw_bus_type, + .probe = fwspk_probe, + .remove = __devexit_p(fwspk_remove), + }, + .update = fwspk_bus_reset, + .id_table = fwspk_id_table, +}; + +static int __init alsa_fwspk_init(void) +{ + return driver_register(&fwspk_driver.driver); +} + +static void __exit alsa_fwspk_exit(void) +{ + driver_unregister(&fwspk_driver.driver); +} + +module_init(alsa_fwspk_init); +module_exit(alsa_fwspk_exit); -- cgit v1.2.3-59-g8ed1b