]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
Merge tag 'asoc-v3.13-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie...
authorTakashi Iwai <tiwai@suse.de>
Fri, 8 Nov 2013 15:45:38 +0000 (16:45 +0100)
committerTakashi Iwai <tiwai@suse.de>
Fri, 8 Nov 2013 15:45:38 +0000 (16:45 +0100)
ASoC: Updates for v3.13

Some additional fixes for v3.13, the majority of which are removals and
downgrades of BUG()s from Takashi.

113 files changed:
Documentation/ioctl/ioctl-number.txt
Documentation/laptops/thinkpad-acpi.txt
Documentation/sound/alsa/ALSA-Configuration.txt
Documentation/sound/alsa/Audiophile-Usb.txt
Documentation/sound/alsa/CMIPCI.txt
Documentation/sound/alsa/compress_offload.txt
Documentation/sound/alsa/soc/DPCM.txt
Documentation/sound/alsa/soc/dapm.txt
drivers/base/regmap/internal.h
drivers/base/regmap/regcache.c
drivers/base/regmap/regmap.c
drivers/platform/x86/thinkpad_acpi.c
include/linux/regmap.h
include/linux/thinkpad_acpi.h [new file with mode: 0644]
include/sound/ak4114.h
include/sound/compress_driver.h
include/sound/memalloc.h
include/uapi/sound/Kbuild
include/uapi/sound/asound.h
include/uapi/sound/firewire.h [new file with mode: 0644]
sound/arm/pxa2xx-ac97-lib.c
sound/arm/pxa2xx-ac97.c
sound/atmel/ac97c.c
sound/core/compress_offload.c
sound/core/init.c
sound/core/memalloc.c
sound/core/pcm_dmaengine.c
sound/core/pcm_native.c
sound/drivers/opl3/opl3_midi.c
sound/drivers/pcsp/pcsp.c
sound/firewire/Kconfig
sound/firewire/Makefile
sound/firewire/amdtp.c
sound/firewire/amdtp.h
sound/firewire/cmp.c
sound/firewire/dice-interface.h [new file with mode: 0644]
sound/firewire/dice.c [new file with mode: 0644]
sound/firewire/fcp.c
sound/firewire/isight.c
sound/firewire/lib.c
sound/firewire/lib.h
sound/firewire/scs1x.c
sound/firewire/speakers.c
sound/i2c/other/ak4114.c
sound/i2c/other/ak4xxx-adda.c
sound/isa/cmi8328.c
sound/isa/sb/sb16_csp.c
sound/mips/ad1843.c
sound/oss/sb_ess.c
sound/pci/ad1889.c
sound/pci/ali5451/ali5451.c
sound/pci/asihpi/asihpi.c
sound/pci/au88x0/au88x0_pcm.c
sound/pci/au88x0/au88x0_synth.c
sound/pci/azt3328.c
sound/pci/cs5535audio/cs5535audio_olpc.c
sound/pci/ctxfi/ctdaio.c
sound/pci/ctxfi/cthardware.c
sound/pci/emu10k1/emufx.c
sound/pci/hda/hda_auto_parser.c
sound/pci/hda/hda_beep.c
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_eld.c
sound/pci/hda/hda_generic.c
sound/pci/hda/hda_generic.h
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_jack.c
sound/pci/hda/hda_jack.h
sound/pci/hda/hda_local.h
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_ca0132.c
sound/pci/hda/patch_cirrus.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c
sound/pci/ice1712/psc724.c
sound/pci/ice1712/quartet.c
sound/pci/ice1712/wm8766.c
sound/pci/ice1712/wm8776.c
sound/pci/intel8x0.c
sound/pci/lola/lola.c
sound/pci/lx6464es/lx6464es.c
sound/pci/lx6464es/lx_core.c
sound/pci/rme96.c
sound/pci/rme9652/hdspm.c
sound/ppc/snd_ps3.c
sound/soc/atmel/atmel_ssc_dai.c
sound/soc/codecs/ak4641.c
sound/soc/codecs/mc13783.c
sound/soc/codecs/tas5086.c
sound/soc/soc-dapm.c
sound/soc/soc-generic-dmaengine-pcm.c
sound/soc/soc-jack.c
sound/soc/tegra/tegra20_i2s.c
sound/soc/tegra/tegra20_spdif.c
sound/soc/tegra/tegra30_ahub.c
sound/soc/tegra/tegra30_i2s.c
sound/sparc/cs4231.c
sound/usb/6fire/chip.c
sound/usb/caiaq/control.c
sound/usb/caiaq/device.c
sound/usb/caiaq/device.h
sound/usb/card.c
sound/usb/card.h
sound/usb/endpoint.c
sound/usb/endpoint.h
sound/usb/helper.c
sound/usb/mixer.c
sound/usb/pcm.c
sound/usb/stream.c
sound/usb/usbaudio.h

index 2a5f0e14efa351a73ef1eb53c392ecef7c9b4f6b..7cbfa3c4fc3d327c8b1d9c3ef8a4c1228998a69b 100644 (file)
@@ -138,6 +138,7 @@ Code  Seq#(hex)     Include File            Comments
 'H'    C0-DF   net/bluetooth/cmtp/cmtp.h       conflict!
 'H'    C0-DF   net/bluetooth/bnep/bnep.h       conflict!
 'H'    F1      linux/hid-roccat.h      <mailto:erazor_de@users.sourceforge.net>
+'H'    F8-FA   sound/firewire.h
 'I'    all     linux/isdn.h            conflict!
 'I'    00-0F   drivers/isdn/divert/isdn_divert.h       conflict!
 'I'    40-4F   linux/mISDNif.h         conflict!
index 86c52360ffe7326cce53541897d4aa98b86f0e8a..fc04c14de4bbc3705e14eddb97a214ca8a7f6331 100644 (file)
@@ -1,7 +1,7 @@
                     ThinkPad ACPI Extras Driver
 
-                            Version 0.24
-                        December 11th,  2009
+                            Version 0.25
+                        October 16th,  2013
 
                Borislav Deianov <borislav@users.sf.net>
              Henrique de Moraes Holschuh <hmh@hmh.eng.br>
@@ -741,6 +741,9 @@ compiled with the CONFIG_THINKPAD_ACPI_UNSAFE_LEDS option enabled.
 Distributions must never enable this option.  Individual users that
 are aware of the consequences are welcome to enabling it.
 
+Audio mute and microphone mute LEDs are supported, but currently not
+visible to userspace. They are used by the snd-hda-intel audio driver.
+
 procfs notes:
 
 The available commands are:
index 95731a08f25787ff77a03a4f542dec5791f120e6..b8dd0df76952077124714343fd205f3b6a28d940 100644 (file)
@@ -616,7 +616,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     As default, snd-dummy drivers doesn't allocate the real buffers
     but either ignores read/write or mmap a single dummy page to all
-    buffer pages, in order to save the resouces.  If your apps need
+    buffer pages, in order to save the resources.  If your apps need
     the read/ written buffer data to be consistent, pass fake_buffer=0
     option.
 
index 654dd3b694a8f3034a450a2e479062c9ee34f307..e7a5ed4dcae86c6fe462adef8bc7c8c58cc369fd 100644 (file)
@@ -232,7 +232,7 @@ The parameter can be given:
    # modprobe snd-usb-audio index=1 device_setup=0x09
 
  * Or while configuring the modules options in your modules configuration file
-   (tipically a .conf file in /etc/modprobe.d/ directory:
+   (typically a .conf file in /etc/modprobe.d/ directory:
        alias snd-card-1 snd-usb-audio
        options snd-usb-audio index=1 device_setup=0x09
 
index 16935c8561f7b5d037192e66606607c9dedcaa1e..4e36e6e809cabeadce0d4593e13ed34c18f429ee 100644 (file)
@@ -87,7 +87,7 @@ with 4 channels,
 
 and use the interleaved 4 channel data.
 
-There are some control switchs affecting to the speaker connections:
+There are some control switches affecting to the speaker connections:
 
 "Line-In Mode" - an enum control to change the behavior of line-in
        jack.  Either "Line-In", "Rear Output" or "Bass Output" can
index fd74ff26376e9a2c9a12bb99f33deb1613230630..630c492c3dc2374d621b5c70d190b9100100402a 100644 (file)
@@ -217,12 +217,12 @@ Not supported:
   would be enabled with ALSA kcontrols.
 
 - Audio policy/resource management. This API does not provide any
-  hooks to query the utilization of the audio DSP, nor any premption
+  hooks to query the utilization of the audio DSP, nor any preemption
   mechanisms.
 
-- No notion of underun/overrun. Since the bytes written are compressed
+- No notion of underrun/overrun. Since the bytes written are compressed
   in nature and data written/read doesn't translate directly to
-  rendered output in time, this does not deal with underrun/overun and
+  rendered output in time, this does not deal with underrun/overrun and
   maybe dealt in user-library
 
 Credits:
index aa8546f2d1443984592ce7917ae742dde54b7519..0110180b7ac63013b7253ee8080788b53b916fc2 100644 (file)
@@ -192,7 +192,7 @@ This BE DAI link connects DAI0 to the codec (in this case RT5460 AIF1). It sets
 the "no_pcm" flag to mark it has a BE and sets flags for supported stream
 directions using "dpcm_playback" and "dpcm_capture" above.
 
-The BE has also flags set for ignoreing suspend and PM down time. This allows
+The BE has also flags set for ignoring suspend and PM down time. This allows
 the BE to work in a hostless mode where the host CPU is not transferring data
 like a BT phone call :-
 
@@ -328,7 +328,7 @@ The host can control the hostless link either by :-
     between both DAIs.
 
  2) Hostless FE. This FE has a virtual connection to the BE DAI links on the DAPM
-    graph. Control is then carried out by the FE as regualar PCM operations.
+    graph. Control is then carried out by the FE as regular PCM operations.
     This method gives more control over the DAI links, but requires much more
     userspace code to control the link. Its recommended to use CODEC<->CODEC
     unless your HW needs more fine grained sequencing of the PCM ops.
index 7dfd88ce31ac195633b89d728e0dc7787a2f41bf..6faab4880006d4c2721846623a07eb6de6661299 100644 (file)
@@ -30,7 +30,7 @@ There are 4 power domains within DAPM
       machine driver and responds to asynchronous events e.g when HP
       are inserted
 
-   3. Path domain - audio susbsystem signal paths
+   3. Path domain - audio subsystem signal paths
       Automatically set when mixer and mux settings are changed by the user.
       e.g. alsamixer, amixer.
 
@@ -64,7 +64,7 @@ Audio DAPM widgets fall into a number of types:-
  o Speaker    - Speaker
  o Supply     - Power or clock supply widget used by other widgets.
  o Regulator  - External regulator that supplies power to audio components.
- o Clock      -        External clock that supplies clock to audio componnents.
+ o Clock      -        External clock that supplies clock to audio components.
  o AIF IN     - Audio Interface Input (with TDM slot mask).
  o AIF OUT    - Audio Interface Output (with TDM slot mask).
  o Siggen     - Signal Generator.
index 9010614f77937396399a30d43c0c5e4b984742c0..33414b1de2013b8e3fc68f31a70f8a56f2900cbe 100644 (file)
@@ -44,7 +44,6 @@ struct regmap_format {
 
 struct regmap_async {
        struct list_head list;
-       struct work_struct cleanup;
        struct regmap *map;
        void *work_buf;
 };
@@ -64,9 +63,11 @@ struct regmap {
        void *bus_context;
        const char *name;
 
+       bool async;
        spinlock_t async_lock;
        wait_queue_head_t async_waitq;
        struct list_head async_list;
+       struct list_head async_free;
        int async_ret;
 
 #ifdef CONFIG_DEBUG_FS
@@ -221,7 +222,7 @@ bool regcache_set_val(struct regmap *map, void *base, unsigned int idx,
 int regcache_lookup_reg(struct regmap *map, unsigned int reg);
 
 int _regmap_raw_write(struct regmap *map, unsigned int reg,
-                     const void *val, size_t val_len, bool async);
+                     const void *val, size_t val_len);
 
 void regmap_async_complete_cb(struct regmap_async *async, int ret);
 
index d6c2d691b6e862e9ffc29b468dbdc617bc3d4fc0..a36112af494ce34464d989e8037fa96e1a2a2c98 100644 (file)
@@ -631,8 +631,7 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data,
 
        map->cache_bypass = 1;
 
-       ret = _regmap_raw_write(map, base, *data, count * val_bytes,
-                               false);
+       ret = _regmap_raw_write(map, base, *data, count * val_bytes);
 
        map->cache_bypass = 0;
 
index 00152bf7ab1202b8bc0a0a06e91941ffdf9fe770..ccdac61ac5e2b2b6de72819d8911fd01ad1ecf60 100644 (file)
@@ -42,15 +42,6 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg,
 static int _regmap_bus_raw_write(void *context, unsigned int reg,
                                 unsigned int val);
 
-static void async_cleanup(struct work_struct *work)
-{
-       struct regmap_async *async = container_of(work, struct regmap_async,
-                                                 cleanup);
-
-       kfree(async->work_buf);
-       kfree(async);
-}
-
 bool regmap_reg_in_ranges(unsigned int reg,
                          const struct regmap_range *ranges,
                          unsigned int nranges)
@@ -465,6 +456,7 @@ struct regmap *regmap_init(struct device *dev,
 
        spin_lock_init(&map->async_lock);
        INIT_LIST_HEAD(&map->async_list);
+       INIT_LIST_HEAD(&map->async_free);
        init_waitqueue_head(&map->async_waitq);
 
        if (config->read_flag_mask || config->write_flag_mask) {
@@ -944,12 +936,22 @@ EXPORT_SYMBOL_GPL(regmap_reinit_cache);
  */
 void regmap_exit(struct regmap *map)
 {
+       struct regmap_async *async;
+
        regcache_exit(map);
        regmap_debugfs_exit(map);
        regmap_range_exit(map);
        if (map->bus && map->bus->free_context)
                map->bus->free_context(map->bus_context);
        kfree(map->work_buf);
+       while (!list_empty(&map->async_free)) {
+               async = list_first_entry_or_null(&map->async_free,
+                                                struct regmap_async,
+                                                list);
+               list_del(&async->list);
+               kfree(async->work_buf);
+               kfree(async);
+       }
        kfree(map);
 }
 EXPORT_SYMBOL_GPL(regmap_exit);
@@ -1041,7 +1043,7 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg,
 }
 
 int _regmap_raw_write(struct regmap *map, unsigned int reg,
-                     const void *val, size_t val_len, bool async)
+                     const void *val, size_t val_len)
 {
        struct regmap_range_node *range;
        unsigned long flags;
@@ -1093,7 +1095,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg,
                        dev_dbg(map->dev, "Writing window %d/%zu\n",
                                win_residue, val_len / map->format.val_bytes);
                        ret = _regmap_raw_write(map, reg, val, win_residue *
-                                               map->format.val_bytes, async);
+                                               map->format.val_bytes);
                        if (ret != 0)
                                return ret;
 
@@ -1116,21 +1118,42 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg,
 
        u8[0] |= map->write_flag_mask;
 
-       if (async && map->bus->async_write) {
-               struct regmap_async *async = map->bus->async_alloc();
-               if (!async)
-                       return -ENOMEM;
+       /*
+        * Essentially all I/O mechanisms will be faster with a single
+        * buffer to write.  Since register syncs often generate raw
+        * writes of single registers optimise that case.
+        */
+       if (val != work_val && val_len == map->format.val_bytes) {
+               memcpy(work_val, val, map->format.val_bytes);
+               val = work_val;
+       }
+
+       if (map->async && map->bus->async_write) {
+               struct regmap_async *async;
 
                trace_regmap_async_write_start(map->dev, reg, val_len);
 
-               async->work_buf = kzalloc(map->format.buf_size,
-                                         GFP_KERNEL | GFP_DMA);
-               if (!async->work_buf) {
-                       kfree(async);
-                       return -ENOMEM;
+               spin_lock_irqsave(&map->async_lock, flags);
+               async = list_first_entry_or_null(&map->async_free,
+                                                struct regmap_async,
+                                                list);
+               if (async)
+                       list_del(&async->list);
+               spin_unlock_irqrestore(&map->async_lock, flags);
+
+               if (!async) {
+                       async = map->bus->async_alloc();
+                       if (!async)
+                               return -ENOMEM;
+
+                       async->work_buf = kzalloc(map->format.buf_size,
+                                                 GFP_KERNEL | GFP_DMA);
+                       if (!async->work_buf) {
+                               kfree(async);
+                               return -ENOMEM;
+                       }
                }
 
-               INIT_WORK(&async->cleanup, async_cleanup);
                async->map = map;
 
                /* If the caller supplied the value we can use it safely. */
@@ -1154,11 +1177,8 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg,
                                ret);
 
                        spin_lock_irqsave(&map->async_lock, flags);
-                       list_del(&async->list);
+                       list_move(&async->list, &map->async_free);
                        spin_unlock_irqrestore(&map->async_lock, flags);
-
-                       kfree(async->work_buf);
-                       kfree(async);
                }
 
                return ret;
@@ -1255,7 +1275,7 @@ static int _regmap_bus_raw_write(void *context, unsigned int reg,
                                 map->work_buf +
                                 map->format.reg_bytes +
                                 map->format.pad_bytes,
-                                map->format.val_bytes, false);
+                                map->format.val_bytes);
 }
 
 static inline void *_regmap_map_get_context(struct regmap *map)
@@ -1319,6 +1339,37 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)
 }
 EXPORT_SYMBOL_GPL(regmap_write);
 
+/**
+ * regmap_write_async(): Write a value to a single register asynchronously
+ *
+ * @map: Register map to write to
+ * @reg: Register to write to
+ * @val: Value to be written
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+int regmap_write_async(struct regmap *map, unsigned int reg, unsigned int val)
+{
+       int ret;
+
+       if (reg % map->reg_stride)
+               return -EINVAL;
+
+       map->lock(map->lock_arg);
+
+       map->async = true;
+
+       ret = _regmap_write(map, reg, val);
+
+       map->async = false;
+
+       map->unlock(map->lock_arg);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(regmap_write_async);
+
 /**
  * regmap_raw_write(): Write raw values to one or more registers
  *
@@ -1347,7 +1398,7 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
 
        map->lock(map->lock_arg);
 
-       ret = _regmap_raw_write(map, reg, val, val_len, false);
+       ret = _regmap_raw_write(map, reg, val, val_len);
 
        map->unlock(map->lock_arg);
 
@@ -1496,8 +1547,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
                                return ret;
                }
        } else {
-               ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count,
-                                       false);
+               ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count);
        }
 
        if (val_bytes != 1)
@@ -1543,7 +1593,11 @@ int regmap_raw_write_async(struct regmap *map, unsigned int reg,
 
        map->lock(map->lock_arg);
 
-       ret = _regmap_raw_write(map, reg, val, val_len, true);
+       map->async = true;
+
+       ret = _regmap_raw_write(map, reg, val, val_len);
+
+       map->async = false;
 
        map->unlock(map->lock_arg);
 
@@ -1890,6 +1944,41 @@ int regmap_update_bits(struct regmap *map, unsigned int reg,
 }
 EXPORT_SYMBOL_GPL(regmap_update_bits);
 
+/**
+ * regmap_update_bits_async: Perform a read/modify/write cycle on the register
+ *                           map asynchronously
+ *
+ * @map: Register map to update
+ * @reg: Register to update
+ * @mask: Bitmask to change
+ * @val: New value for bitmask
+ *
+ * With most buses the read must be done synchronously so this is most
+ * useful for devices with a cache which do not need to interact with
+ * the hardware to determine the current register value.
+ *
+ * Returns zero for success, a negative number on error.
+ */
+int regmap_update_bits_async(struct regmap *map, unsigned int reg,
+                            unsigned int mask, unsigned int val)
+{
+       bool change;
+       int ret;
+
+       map->lock(map->lock_arg);
+
+       map->async = true;
+
+       ret = _regmap_update_bits(map, reg, mask, val, &change);
+
+       map->async = false;
+
+       map->unlock(map->lock_arg);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(regmap_update_bits_async);
+
 /**
  * regmap_update_bits_check: Perform a read/modify/write cycle on the
  *                           register map and report if updated
@@ -1915,6 +2004,43 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg,
 }
 EXPORT_SYMBOL_GPL(regmap_update_bits_check);
 
+/**
+ * regmap_update_bits_check_async: Perform a read/modify/write cycle on the
+ *                                 register map asynchronously and report if
+ *                                 updated
+ *
+ * @map: Register map to update
+ * @reg: Register to update
+ * @mask: Bitmask to change
+ * @val: New value for bitmask
+ * @change: Boolean indicating if a write was done
+ *
+ * With most buses the read must be done synchronously so this is most
+ * useful for devices with a cache which do not need to interact with
+ * the hardware to determine the current register value.
+ *
+ * Returns zero for success, a negative number on error.
+ */
+int regmap_update_bits_check_async(struct regmap *map, unsigned int reg,
+                                  unsigned int mask, unsigned int val,
+                                  bool *change)
+{
+       int ret;
+
+       map->lock(map->lock_arg);
+
+       map->async = true;
+
+       ret = _regmap_update_bits(map, reg, mask, val, change);
+
+       map->async = false;
+
+       map->unlock(map->lock_arg);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(regmap_update_bits_check_async);
+
 void regmap_async_complete_cb(struct regmap_async *async, int ret)
 {
        struct regmap *map = async->map;
@@ -1923,8 +2049,7 @@ void regmap_async_complete_cb(struct regmap_async *async, int ret)
        trace_regmap_async_io_complete(map->dev);
 
        spin_lock(&map->async_lock);
-
-       list_del(&async->list);
+       list_move(&async->list, &map->async_free);
        wake = list_empty(&map->async_list);
 
        if (ret != 0)
@@ -1932,8 +2057,6 @@ void regmap_async_complete_cb(struct regmap_async *async, int ret)
 
        spin_unlock(&map->async_lock);
 
-       schedule_work(&async->cleanup);
-
        if (wake)
                wake_up(&map->async_waitq);
 }
index 03ca6c139f1a5c9dd0cc821045bad7b5cb409d51..0b7efb269cf1ae565f52ced999340d45c19c914c 100644 (file)
@@ -23,7 +23,7 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
-#define TPACPI_VERSION "0.24"
+#define TPACPI_VERSION "0.25"
 #define TPACPI_SYSFS_VERSION 0x020700
 
 /*
@@ -88,6 +88,7 @@
 
 #include <linux/pci_ids.h>
 
+#include <linux/thinkpad_acpi.h>
 
 /* ThinkPad CMOS commands */
 #define TP_CMOS_VOLUME_DOWN    0
@@ -8350,6 +8351,91 @@ static struct ibm_struct fan_driver_data = {
        .resume = fan_resume,
 };
 
+/*************************************************************************
+ * Mute LED subdriver
+ */
+
+
+struct tp_led_table {
+       acpi_string name;
+       int on_value;
+       int off_value;
+       int state;
+};
+
+static struct tp_led_table led_tables[] = {
+       [TPACPI_LED_MUTE] = {
+               .name = "SSMS",
+               .on_value = 1,
+               .off_value = 0,
+       },
+       [TPACPI_LED_MICMUTE] = {
+               .name = "MMTS",
+               .on_value = 2,
+               .off_value = 0,
+       },
+};
+
+static int mute_led_on_off(struct tp_led_table *t, bool state)
+{
+       acpi_handle temp;
+       int output;
+
+       if (!ACPI_SUCCESS(acpi_get_handle(hkey_handle, t->name, &temp))) {
+               pr_warn("Thinkpad ACPI has no %s interface.\n", t->name);
+               return -EIO;
+       }
+
+       if (!acpi_evalf(hkey_handle, &output, t->name, "dd",
+                       state ? t->on_value : t->off_value))
+               return -EIO;
+
+       t->state = state;
+       return state;
+}
+
+int tpacpi_led_set(int whichled, bool on)
+{
+       struct tp_led_table *t;
+
+       if (whichled < 0 || whichled >= TPACPI_LED_MAX)
+               return -EINVAL;
+
+       t = &led_tables[whichled];
+       if (t->state < 0 || t->state == on)
+               return t->state;
+       return mute_led_on_off(t, on);
+}
+EXPORT_SYMBOL_GPL(tpacpi_led_set);
+
+static int mute_led_init(struct ibm_init_struct *iibm)
+{
+       acpi_handle temp;
+       int i;
+
+       for (i = 0; i < TPACPI_LED_MAX; i++) {
+               struct tp_led_table *t = &led_tables[i];
+               if (ACPI_SUCCESS(acpi_get_handle(hkey_handle, t->name, &temp)))
+                       mute_led_on_off(t, false);
+               else
+                       t->state = -ENODEV;
+       }
+       return 0;
+}
+
+static void mute_led_exit(void)
+{
+       int i;
+
+       for (i = 0; i < TPACPI_LED_MAX; i++)
+               tpacpi_led_set(i, false);
+}
+
+static struct ibm_struct mute_led_driver_data = {
+       .name = "mute_led",
+       .exit = mute_led_exit,
+};
+
 /****************************************************************************
  ****************************************************************************
  *
@@ -8768,6 +8854,10 @@ static struct ibm_init_struct ibms_init[] __initdata = {
                .init = fan_init,
                .data = &fan_driver_data,
        },
+       {
+               .init = mute_led_init,
+               .data = &mute_led_driver_data,
+       },
 };
 
 static int __init set_ibm_param(const char *val, struct kernel_param *kp)
index a12bea07f79ef89b184b6e1bab957f70fcf2e636..dc90b8c134a12a9d5b87755a3e27fcfa7b01e0c8 100644 (file)
@@ -374,6 +374,7 @@ int regmap_reinit_cache(struct regmap *map,
                        const struct regmap_config *config);
 struct regmap *dev_get_regmap(struct device *dev, const char *name);
 int regmap_write(struct regmap *map, unsigned int reg, unsigned int val);
+int regmap_write_async(struct regmap *map, unsigned int reg, unsigned int val);
 int regmap_raw_write(struct regmap *map, unsigned int reg,
                     const void *val, size_t val_len);
 int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
@@ -387,9 +388,14 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
                     size_t val_count);
 int regmap_update_bits(struct regmap *map, unsigned int reg,
                       unsigned int mask, unsigned int val);
+int regmap_update_bits_async(struct regmap *map, unsigned int reg,
+                            unsigned int mask, unsigned int val);
 int regmap_update_bits_check(struct regmap *map, unsigned int reg,
                             unsigned int mask, unsigned int val,
                             bool *change);
+int regmap_update_bits_check_async(struct regmap *map, unsigned int reg,
+                                  unsigned int mask, unsigned int val,
+                                  bool *change);
 int regmap_get_val_bytes(struct regmap *map);
 int regmap_async_complete(struct regmap *map);
 bool regmap_can_raw_write(struct regmap *map);
@@ -540,6 +546,13 @@ static inline int regmap_write(struct regmap *map, unsigned int reg,
        return -EINVAL;
 }
 
+static inline int regmap_write_async(struct regmap *map, unsigned int reg,
+                                    unsigned int val)
+{
+       WARN_ONCE(1, "regmap API is disabled");
+       return -EINVAL;
+}
+
 static inline int regmap_raw_write(struct regmap *map, unsigned int reg,
                                   const void *val, size_t val_len)
 {
@@ -589,6 +602,14 @@ static inline int regmap_update_bits(struct regmap *map, unsigned int reg,
        return -EINVAL;
 }
 
+static inline int regmap_update_bits_async(struct regmap *map,
+                                          unsigned int reg,
+                                          unsigned int mask, unsigned int val)
+{
+       WARN_ONCE(1, "regmap API is disabled");
+       return -EINVAL;
+}
+
 static inline int regmap_update_bits_check(struct regmap *map,
                                           unsigned int reg,
                                           unsigned int mask, unsigned int val,
@@ -598,6 +619,16 @@ static inline int regmap_update_bits_check(struct regmap *map,
        return -EINVAL;
 }
 
+static inline int regmap_update_bits_check_async(struct regmap *map,
+                                                unsigned int reg,
+                                                unsigned int mask,
+                                                unsigned int val,
+                                                bool *change)
+{
+       WARN_ONCE(1, "regmap API is disabled");
+       return -EINVAL;
+}
+
 static inline int regmap_get_val_bytes(struct regmap *map)
 {
        WARN_ONCE(1, "regmap API is disabled");
diff --git a/include/linux/thinkpad_acpi.h b/include/linux/thinkpad_acpi.h
new file mode 100644 (file)
index 0000000..361de59
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef __THINKPAD_ACPI_H__
+#define __THINKPAD_ACPI_H__
+
+/* These two functions return 0 if success, or negative error code
+   (e g -ENODEV if no led present) */
+
+enum {
+       TPACPI_LED_MUTE,
+       TPACPI_LED_MICMUTE,
+       TPACPI_LED_MAX,
+};
+
+int tpacpi_led_set(int whichled, bool on);
+
+#endif
index 3ce69fd925233188eb59756d2ba0b9be203d39aa..52f02a60dba731e500bbac2a355b1bd5d12a676f 100644 (file)
@@ -170,7 +170,7 @@ struct ak4114 {
        void * private_data;
        unsigned int init: 1;
        spinlock_t lock;
-       unsigned char regmap[7];
+       unsigned char regmap[6];
        unsigned char txcsb[5];
        struct snd_kcontrol *kctls[AK4114_CONTROLS];
        struct snd_pcm_substream *playback_substream;
@@ -189,7 +189,7 @@ struct ak4114 {
 
 int snd_ak4114_create(struct snd_card *card,
                      ak4114_read_t *read, ak4114_write_t *write,
-                     const unsigned char pgm[7], const unsigned char txcsb[5],
+                     const unsigned char pgm[6], const unsigned char txcsb[5],
                      void *private_data, struct ak4114 **r_ak4114);
 void snd_ak4114_reg_write(struct ak4114 *ak4114, unsigned char reg, unsigned char mask, unsigned char val);
 void snd_ak4114_reinit(struct ak4114 *ak4114);
index 9031a26249b56e1bcc1fde74bde36a5f50dfe463..ae6c3b8ed2f5010d1b7e801284e39ded7db964a6 100644 (file)
@@ -171,4 +171,13 @@ static inline void snd_compr_fragment_elapsed(struct snd_compr_stream *stream)
        wake_up(&stream->runtime->sleep);
 }
 
+static inline void snd_compr_drain_notify(struct snd_compr_stream *stream)
+{
+       if (snd_BUG_ON(!stream))
+               return;
+
+       stream->runtime->state = SNDRV_PCM_STATE_SETUP;
+       wake_up(&stream->runtime->sleep);
+}
+
 #endif
index cf15b8213df7e0bd7e25e6d003f23ffd1d375674..af998397041768a82ee347665e7040a9a4e44b9c 100644 (file)
@@ -52,6 +52,11 @@ struct snd_dma_device {
 #else
 #define SNDRV_DMA_TYPE_DEV_SG  SNDRV_DMA_TYPE_DEV /* no SG-buf support */
 #endif
+#ifdef CONFIG_GENERIC_ALLOCATOR
+#define SNDRV_DMA_TYPE_DEV_IRAM                4       /* generic device iram-buffer */
+#else
+#define SNDRV_DMA_TYPE_DEV_IRAM        SNDRV_DMA_TYPE_DEV
+#endif
 
 /*
  * info for buffer allocation
index 0f7d279ebde35fd1be21cc922def324eaacb240f..a7f27704f9807e7b14eadb9b08cf09c2c183a6a7 100644 (file)
@@ -5,6 +5,7 @@ header-y += asound_fm.h
 header-y += compress_offload.h
 header-y += compress_params.h
 header-y += emu10k1.h
+header-y += firewire.h
 header-y += hdsp.h
 header-y += hdspm.h
 header-y += sb16_csp.h
index 041203f20f6d1cb3cac42df78723356550729054..9fc6219d38482cd3dc8a34f8653d24dfeee2a792 100644 (file)
@@ -93,9 +93,10 @@ enum {
        SNDRV_HWDEP_IFACE_SB_RC,        /* SB Extigy/Audigy2NX remote control */
        SNDRV_HWDEP_IFACE_HDA,          /* HD-audio */
        SNDRV_HWDEP_IFACE_USB_STREAM,   /* direct access to usb stream */
+       SNDRV_HWDEP_IFACE_FW_DICE,      /* TC DICE FireWire device */
 
        /* Don't forget to change the following: */
-       SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_USB_STREAM
+       SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_DICE
 };
 
 struct snd_hwdep_info {
diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h
new file mode 100644 (file)
index 0000000..59f5961
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef _UAPI_SOUND_FIREWIRE_H_INCLUDED
+#define _UAPI_SOUND_FIREWIRE_H_INCLUDED
+
+#include <linux/ioctl.h>
+
+/* events can be read() from the hwdep device */
+
+#define SNDRV_FIREWIRE_EVENT_LOCK_STATUS       0x000010cc
+#define SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION 0xd1ce004e
+
+struct snd_firewire_event_common {
+       unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */
+};
+
+struct snd_firewire_event_lock_status {
+       unsigned int type;
+       unsigned int status; /* 0/1 = unlocked/locked */
+};
+
+struct snd_firewire_event_dice_notification {
+       unsigned int type;
+       unsigned int notification; /* DICE-specific bits */
+};
+
+union snd_firewire_event {
+       struct snd_firewire_event_common            common;
+       struct snd_firewire_event_lock_status       lock_status;
+       struct snd_firewire_event_dice_notification dice_notification;
+};
+
+
+#define SNDRV_FIREWIRE_IOCTL_GET_INFO _IOR('H', 0xf8, struct snd_firewire_get_info)
+#define SNDRV_FIREWIRE_IOCTL_LOCK      _IO('H', 0xf9)
+#define SNDRV_FIREWIRE_IOCTL_UNLOCK    _IO('H', 0xfa)
+
+#define SNDRV_FIREWIRE_TYPE_DICE       1
+/* Fireworks, AV/C, RME, MOTU, ... */
+
+struct snd_firewire_get_info {
+       unsigned int type; /* SNDRV_FIREWIRE_TYPE_xxx */
+       unsigned int card; /* same as fw_cdev_get_info.card */
+       unsigned char guid[8];
+       char device_name[16]; /* device node in /dev */
+};
+
+/*
+ * SNDRV_FIREWIRE_IOCTL_LOCK prevents the driver from streaming.
+ * Returns -EBUSY if the driver is already streaming.
+ */
+
+#endif /* _UAPI_SOUND_FIREWIRE_H_INCLUDED */
index 99a466822a7d9eb3d73dd56da74911c3cb3d0173..66de90ed30cafb43624ae087a008907257043b64 100644 (file)
@@ -208,7 +208,7 @@ bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97)
                pxa_ac97_warm_pxa3xx();
        else
 #endif
-               BUG();
+               snd_BUG();
 
        while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
                mdelay(1);
@@ -245,7 +245,7 @@ bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97)
                pxa_ac97_cold_pxa3xx();
        else
 #endif
-               BUG();
+               snd_BUG();
 
        while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
                mdelay(1);
index 5066a3768b2841a4f46d8cbb8116b4d8276334da..9a2ac1e0f77addbe779e919c83cf61ad22368111 100644 (file)
@@ -185,7 +185,7 @@ static int pxa2xx_ac97_probe(struct platform_device *dev)
                goto err;
 
        card->dev = &dev->dev;
-       strncpy(card->driver, dev->dev.driver->name, sizeof(card->driver));
+       strlcpy(card->driver, dev->dev.driver->name, sizeof(card->driver));
 
        ret = pxa2xx_pcm_new(card, &pxa2xx_ac97_pcm_client, &pxa2xx_ac97_pcm);
        if (ret)
index ae63d22c0f883e48ddb0b37ee6aad40ac6f8943c..c5f0ddd729b35a5ea0292b3c7a9347cb8aa04271 100644 (file)
@@ -34,7 +34,6 @@
 #include <linux/dw_dmac.h>
 
 #include <mach/cpu.h>
-#include <mach/gpio.h>
 
 #ifdef CONFIG_ARCH_AT91
 #include <mach/hardware.h>
index bea523a5d852e11f9d06e1d682e065b699f347c8..d9af6387f37c63acc631f670117b274c97e63824 100644 (file)
@@ -680,14 +680,48 @@ static int snd_compr_stop(struct snd_compr_stream *stream)
                return -EPERM;
        retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
        if (!retval) {
-               stream->runtime->state = SNDRV_PCM_STATE_SETUP;
-               wake_up(&stream->runtime->sleep);
+               snd_compr_drain_notify(stream);
                stream->runtime->total_bytes_available = 0;
                stream->runtime->total_bytes_transferred = 0;
        }
        return retval;
 }
 
+static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
+{
+       int ret;
+
+       /*
+        * We are called with lock held. So drop the lock while we wait for
+        * drain complete notfication from the driver
+        *
+        * It is expected that driver will notify the drain completion and then
+        * stream will be moved to SETUP state, even if draining resulted in an
+        * error. We can trigger next track after this.
+        */
+       stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
+       mutex_unlock(&stream->device->lock);
+
+       /* we wait for drain to complete here, drain can return when
+        * interruption occurred, wait returned error or success.
+        * For the first two cases we don't do anything different here and
+        * return after waking up
+        */
+
+       ret = wait_event_interruptible(stream->runtime->sleep,
+                       (stream->runtime->state != SNDRV_PCM_STATE_DRAINING));
+       if (ret == -ERESTARTSYS)
+               pr_debug("wait aborted by a signal");
+       else if (ret)
+               pr_debug("wait for drain failed with %d\n", ret);
+
+
+       wake_up(&stream->runtime->sleep);
+       mutex_lock(&stream->device->lock);
+
+       return ret;
+}
+
 static int snd_compr_drain(struct snd_compr_stream *stream)
 {
        int retval;
@@ -695,12 +729,15 @@ static int snd_compr_drain(struct snd_compr_stream *stream)
        if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
                        stream->runtime->state == SNDRV_PCM_STATE_SETUP)
                return -EPERM;
+
        retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
-       if (!retval) {
-               stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
+       if (retval) {
+               pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
                wake_up(&stream->runtime->sleep);
+               return retval;
        }
-       return retval;
+
+       return snd_compress_wait_for_drain(stream);
 }
 
 static int snd_compr_next_track(struct snd_compr_stream *stream)
@@ -736,9 +773,14 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream)
                return -EPERM;
 
        retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
+       if (retval) {
+               pr_debug("Partial drain returned failure\n");
+               wake_up(&stream->runtime->sleep);
+               return retval;
+       }
 
        stream->next_track = false;
-       return retval;
+       return snd_compress_wait_for_drain(stream);
 }
 
 static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
index 6b9087115da24806e8aba244127ac8ec3a4e93f8..1351f22f651cdf420ee3bfd7b550c3760b3d9a23 100644 (file)
@@ -66,7 +66,7 @@ static int module_slot_match(struct module *module, int idx)
 #ifdef MODULE
        const char *s1, *s2;
 
-       if (!module || !module->name || !slots[idx])
+       if (!module || !*module->name || !slots[idx])
                return 0;
 
        s1 = module->name;
@@ -597,7 +597,7 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *src,
        /* last resort... */
        snd_printk(KERN_ERR "unable to set card id (%s)\n", id);
        if (card->proc_root->name)
-               strcpy(card->id, card->proc_root->name);
+               strlcpy(card->id, card->proc_root->name, sizeof(card->id));
 }
 
 /**
index bdf826f4fe0ceda5e74bc7e9ad3dfe0a78e59b63..9d93f02c6285d7648ea72d99ac5286ab6a45d19f 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/seq_file.h>
 #include <asm/uaccess.h>
 #include <linux/dma-mapping.h>
+#include <linux/genalloc.h>
 #include <linux/moduleparam.h>
 #include <linux/mutex.h>
 #include <sound/memalloc.h>
@@ -157,6 +158,51 @@ static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr,
        dec_snd_pages(pg);
        dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma);
 }
+
+#ifdef CONFIG_GENERIC_ALLOCATOR
+/**
+ * snd_malloc_dev_iram - allocate memory from on-chip internal ram
+ * @dmab: buffer allocation record to store the allocated data
+ * @size: number of bytes to allocate from the iram
+ *
+ * This function requires iram phandle provided via of_node
+ */
+static void snd_malloc_dev_iram(struct snd_dma_buffer *dmab, size_t size)
+{
+       struct device *dev = dmab->dev.dev;
+       struct gen_pool *pool = NULL;
+
+       dmab->area = NULL;
+       dmab->addr = 0;
+
+       if (dev->of_node)
+               pool = of_get_named_gen_pool(dev->of_node, "iram", 0);
+
+       if (!pool)
+               return;
+
+       /* Assign the pool into private_data field */
+       dmab->private_data = pool;
+
+       dmab->area = (void *)gen_pool_alloc(pool, size);
+       if (!dmab->area)
+               return;
+
+       dmab->addr = gen_pool_virt_to_phys(pool, (unsigned long)dmab->area);
+}
+
+/**
+ * snd_free_dev_iram - free allocated specific memory from on-chip internal ram
+ * @dmab: buffer allocation record to store the allocated data
+ */
+static void snd_free_dev_iram(struct snd_dma_buffer *dmab)
+{
+       struct gen_pool *pool = dmab->private_data;
+
+       if (pool && dmab->area)
+               gen_pool_free(pool, (unsigned long)dmab->area, dmab->bytes);
+}
+#endif /* CONFIG_GENERIC_ALLOCATOR */
 #endif /* CONFIG_HAS_DMA */
 
 /*
@@ -197,6 +243,16 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
                dmab->addr = 0;
                break;
 #ifdef CONFIG_HAS_DMA
+#ifdef CONFIG_GENERIC_ALLOCATOR
+       case SNDRV_DMA_TYPE_DEV_IRAM:
+               snd_malloc_dev_iram(dmab, size);
+               if (dmab->area)
+                       break;
+               /* Internal memory might have limited size and no enough space,
+                * so if we fail to malloc, try to fetch memory traditionally.
+                */
+               dmab->dev.type = SNDRV_DMA_TYPE_DEV;
+#endif /* CONFIG_GENERIC_ALLOCATOR */
        case SNDRV_DMA_TYPE_DEV:
                dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr);
                break;
@@ -269,6 +325,11 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab)
                snd_free_pages(dmab->area, dmab->bytes);
                break;
 #ifdef CONFIG_HAS_DMA
+#ifdef CONFIG_GENERIC_ALLOCATOR
+       case SNDRV_DMA_TYPE_DEV_IRAM:
+               snd_free_dev_iram(dmab);
+               break;
+#endif /* CONFIG_GENERIC_ALLOCATOR */
        case SNDRV_DMA_TYPE_DEV:
                snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
                break;
index aa924d9b7986fd870cdf235a8753ad764af1d555..94d08733cb388cdda361e509701da59650714eeb 100644 (file)
@@ -63,23 +63,19 @@ int snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream,
        struct dma_slave_config *slave_config)
 {
        enum dma_slave_buswidth buswidth;
+       int bits;
 
-       switch (params_format(params)) {
-       case SNDRV_PCM_FORMAT_S8:
+       bits = snd_pcm_format_physical_width(params_format(params));
+       if (bits < 8 || bits > 64)
+               return -EINVAL;
+       else if (bits == 8)
                buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
-               break;
-       case SNDRV_PCM_FORMAT_S16_LE:
+       else if (bits == 16)
                buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
-               break;
-       case SNDRV_PCM_FORMAT_S18_3LE:
-       case SNDRV_PCM_FORMAT_S20_3LE:
-       case SNDRV_PCM_FORMAT_S24_LE:
-       case SNDRV_PCM_FORMAT_S32_LE:
+       else if (bits <= 32)
                buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
-               break;
-       default:
-               return -EINVAL;
-       }
+       else
+               buswidth = DMA_SLAVE_BUSWIDTH_8_BYTES;
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                slave_config->direction = DMA_MEM_TO_DEV;
index a68d4c6d702c80cd461def1c84c4a91a254b21a4..01a5e05ede95cddbbcaa6be3ddb4bcf13e3786ce 100644 (file)
@@ -2428,6 +2428,7 @@ static int snd_pcm_hwsync(struct snd_pcm_substream *substream)
        case SNDRV_PCM_STATE_DRAINING:
                if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
                        goto __badfd;
+               /* Fall through */
        case SNDRV_PCM_STATE_RUNNING:
                if ((err = snd_pcm_update_hw_ptr(substream)) < 0)
                        break;
@@ -2460,6 +2461,7 @@ static int snd_pcm_delay(struct snd_pcm_substream *substream,
        case SNDRV_PCM_STATE_DRAINING:
                if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
                        goto __badfd;
+               /* Fall through */
        case SNDRV_PCM_STATE_RUNNING:
                if ((err = snd_pcm_update_hw_ptr(substream)) < 0)
                        break;
@@ -3199,6 +3201,14 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream,
                             struct vm_area_struct *area)
 {
        area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
+#ifdef CONFIG_GENERIC_ALLOCATOR
+       if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_IRAM) {
+               area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
+               return remap_pfn_range(area, area->vm_start,
+                               substream->dma_buffer.addr >> PAGE_SHIFT,
+                               area->vm_end - area->vm_start, area->vm_page_prot);
+       }
+#endif /* CONFIG_GENERIC_ALLOCATOR */
 #ifdef ARCH_HAS_DMA_MMAP_COHERENT
        if (!substream->ops->page &&
            substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
index 0c796bcbc0a36947453b8a3632daae48d5b24abc..6c6d09a51f42838dc19d98dc63836f53839b5d2a 100644 (file)
@@ -390,6 +390,11 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
                voice = snd_opl3_oss_map[chan->number];         
        }
 
+       if (voice < 0) {
+               spin_unlock_irqrestore(&opl3->voice_lock, flags);
+               return;
+       }
+
        if (voice < MAX_OPL2_VOICES) {
                /* Left register block for voices 0 .. 8 */
                reg_side = OPL3_LEFT;
index 1c19cd7ad26e76af7c71b6615ae291aeed42faf7..f664bae3b9b057f1a0b04776c1d4329a6650ed4c 100644 (file)
@@ -46,8 +46,9 @@ static int snd_pcsp_create(struct snd_card *card)
        int err;
        int div, min_div, order;
 
+       hrtimer_get_res(CLOCK_MONOTONIC, &tp);
+
        if (!nopcm) {
-               hrtimer_get_res(CLOCK_MONOTONIC, &tp);
                if (tp.tv_sec || tp.tv_nsec > PCSP_MAX_PERIOD_NS) {
                        printk(KERN_ERR "PCSP: Timer resolution is not sufficient "
                                "(%linS)\n", tp.tv_nsec);
index ea063e1f87221e982558ec892024e5bdb7793b89..b3e274fe4a77438a463766c9d364045dbe8cbaf7 100644 (file)
@@ -11,6 +11,21 @@ config SND_FIREWIRE_LIB
        tristate
        depends on SND_PCM
 
+config SND_DICE
+       tristate "DICE-based DACs (EXPERIMENTAL)"
+       select SND_HWDEP
+       select SND_PCM
+       select SND_FIREWIRE_LIB
+       help
+         Say Y here to include support for many DACs based on the DICE
+         chip family (DICE-II/Jr/Mini) from TC Applied Technologies.
+
+         At the moment, this driver supports playback only.  If you
+         want to use devices that support capturing, use FFADO instead.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-dice.
+
 config SND_FIREWIRE_SPEAKERS
        tristate "FireWire speakers"
        select SND_PCM
index 460179df5bb526ee06f90fa7ffd03ef638024e3b..509955061d30f62d44216fb9913282091d0bca93 100644 (file)
@@ -1,10 +1,12 @@
 snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
                         fcp.o cmp.o amdtp.o
+snd-dice-objs := dice.o
 snd-firewire-speakers-objs := speakers.o
 snd-isight-objs := isight.o
 snd-scs1x-objs := scs1x.o
 
 obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o
+obj-$(CONFIG_SND_DICE) += snd-dice.o
 obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o
 obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
 obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
index ea995af6d049ec63b86f8fb59cf374640db9cc56..d3226892ad6b44953fd4d980d65874ac40535768 100644 (file)
@@ -42,9 +42,6 @@ static void pcm_period_tasklet(unsigned long data);
 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);
@@ -62,73 +59,91 @@ EXPORT_SYMBOL(amdtp_out_stream_init);
  */
 void amdtp_out_stream_destroy(struct amdtp_out_stream *s)
 {
-       WARN_ON(!IS_ERR(s->context));
+       WARN_ON(amdtp_out_stream_running(s));
        mutex_destroy(&s->mutex);
        fw_unit_put(s->unit);
 }
 EXPORT_SYMBOL(amdtp_out_stream_destroy);
 
+const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = {
+       [CIP_SFC_32000]  =  8,
+       [CIP_SFC_44100]  =  8,
+       [CIP_SFC_48000]  =  8,
+       [CIP_SFC_88200]  = 16,
+       [CIP_SFC_96000]  = 16,
+       [CIP_SFC_176400] = 32,
+       [CIP_SFC_192000] = 32,
+};
+EXPORT_SYMBOL(amdtp_syt_intervals);
+
 /**
- * amdtp_out_stream_set_rate - set the sample rate
+ * amdtp_out_stream_set_parameters - set stream parameters
  * @s: the AMDTP output stream to configure
  * @rate: the sample rate
+ * @pcm_channels: the number of PCM samples in each data block, to be encoded
+ *                as AM824 multi-bit linear audio
+ * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels)
  *
- * The sample rate must be set before the stream is started, and must not be
+ * The parameters 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)
+void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s,
+                                    unsigned int rate,
+                                    unsigned int pcm_channels,
+                                    unsigned int midi_ports)
 {
-       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, },
+       static const unsigned int rates[] = {
+               [CIP_SFC_32000]  =  32000,
+               [CIP_SFC_44100]  =  44100,
+               [CIP_SFC_48000]  =  48000,
+               [CIP_SFC_88200]  =  88200,
+               [CIP_SFC_96000]  =  96000,
+               [CIP_SFC_176400] = 176400,
+               [CIP_SFC_192000] = 192000,
        };
        unsigned int sfc;
 
-       if (WARN_ON(!IS_ERR(s->context)))
+       if (WARN_ON(amdtp_out_stream_running(s)))
                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;
-               }
+       for (sfc = 0; sfc < CIP_SFC_COUNT; ++sfc)
+               if (rates[sfc] == rate)
+                       goto sfc_found;
        WARN_ON(1);
+       return;
+
+sfc_found:
+       s->dual_wire = (s->flags & CIP_HI_DUALWIRE) && sfc > CIP_SFC_96000;
+       if (s->dual_wire) {
+               sfc -= 2;
+               rate /= 2;
+               pcm_channels *= 2;
+       }
+       s->sfc = sfc;
+       s->data_block_quadlets = pcm_channels + DIV_ROUND_UP(midi_ports, 8);
+       s->pcm_channels = pcm_channels;
+       s->midi_ports = midi_ports;
+
+       s->syt_interval = amdtp_syt_intervals[sfc];
+
+       /* default buffering in the device */
+       s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE;
+       if (s->flags & CIP_BLOCKING)
+               /* additional buffering needed to adjust for no-data packets */
+               s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate;
 }
-EXPORT_SYMBOL(amdtp_out_stream_set_rate);
+EXPORT_SYMBOL(amdtp_out_stream_set_parameters);
 
 /**
  * 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().
+ * with amdtp_out_stream_set_parameters().
  */
 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;
+       return 8 + s->syt_interval * s->data_block_quadlets * 4;
 }
 EXPORT_SYMBOL(amdtp_out_stream_get_max_payload);
 
@@ -138,19 +153,26 @@ static void amdtp_write_s16(struct amdtp_out_stream *s,
 static void amdtp_write_s32(struct amdtp_out_stream *s,
                            struct snd_pcm_substream *pcm,
                            __be32 *buffer, unsigned int frames);
+static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s,
+                                    struct snd_pcm_substream *pcm,
+                                    __be32 *buffer, unsigned int frames);
+static void amdtp_write_s32_dualwire(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.
+ * The sample format must be set after the other paramters (rate/PCM channels/
+ * MIDI) and 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)))
+       if (WARN_ON(amdtp_out_stream_running(s)))
                return;
 
        switch (format) {
@@ -158,10 +180,16 @@ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
                WARN_ON(1);
                /* fall through */
        case SNDRV_PCM_FORMAT_S16:
-               s->transfer_samples = amdtp_write_s16;
+               if (s->dual_wire)
+                       s->transfer_samples = amdtp_write_s16_dualwire;
+               else
+                       s->transfer_samples = amdtp_write_s16;
                break;
        case SNDRV_PCM_FORMAT_S32:
-               s->transfer_samples = amdtp_write_s32;
+               if (s->dual_wire)
+                       s->transfer_samples = amdtp_write_s32_dualwire;
+               else
+                       s->transfer_samples = amdtp_write_s32;
                break;
        }
 }
@@ -248,7 +276,7 @@ static unsigned int calculate_syt(struct amdtp_out_stream *s,
        s->last_syt_offset = syt_offset;
 
        if (syt_offset < TICKS_PER_CYCLE) {
-               syt_offset += TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE;
+               syt_offset += s->transfer_delay;
                syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12;
                syt += syt_offset % TICKS_PER_CYCLE;
 
@@ -268,7 +296,7 @@ static void amdtp_write_s32(struct amdtp_out_stream *s,
 
        channels = s->pcm_channels;
        src = (void *)runtime->dma_area +
-                       s->pcm_buffer_pointer * (runtime->frame_bits / 8);
+                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
        remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
        frame_step = s->data_block_quadlets - channels;
 
@@ -294,7 +322,7 @@ static void amdtp_write_s16(struct amdtp_out_stream *s,
 
        channels = s->pcm_channels;
        src = (void *)runtime->dma_area +
-                       s->pcm_buffer_pointer * (runtime->frame_bits / 8);
+                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
        remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
        frame_step = s->data_block_quadlets - channels;
 
@@ -310,6 +338,68 @@ static void amdtp_write_s16(struct amdtp_out_stream *s,
        }
 }
 
+static void amdtp_write_s32_dualwire(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, frame_adjust_1, frame_adjust_2, i, c;
+       const u32 *src;
+
+       channels = s->pcm_channels;
+       src = (void *)runtime->dma_area +
+                       s->pcm_buffer_pointer * (runtime->frame_bits / 8);
+       frame_adjust_1 = channels - 1;
+       frame_adjust_2 = 1 - (s->data_block_quadlets - channels);
+
+       channels /= 2;
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < channels; ++c) {
+                       *buffer = cpu_to_be32((*src >> 8) | 0x40000000);
+                       src++;
+                       buffer += 2;
+               }
+               buffer -= frame_adjust_1;
+               for (c = 0; c < channels; ++c) {
+                       *buffer = cpu_to_be32((*src >> 8) | 0x40000000);
+                       src++;
+                       buffer += 2;
+               }
+               buffer -= frame_adjust_2;
+       }
+}
+
+static void amdtp_write_s16_dualwire(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, frame_adjust_1, frame_adjust_2, i, c;
+       const u16 *src;
+
+       channels = s->pcm_channels;
+       src = (void *)runtime->dma_area +
+                       s->pcm_buffer_pointer * (runtime->frame_bits / 8);
+       frame_adjust_1 = channels - 1;
+       frame_adjust_2 = 1 - (s->data_block_quadlets - channels);
+
+       channels /= 2;
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < channels; ++c) {
+                       *buffer = cpu_to_be32((*src << 8) | 0x40000000);
+                       src++;
+                       buffer += 2;
+               }
+               buffer -= frame_adjust_1;
+               for (c = 0; c < channels; ++c) {
+                       *buffer = cpu_to_be32((*src << 8) | 0x40000000);
+                       src++;
+                       buffer += 2;
+               }
+               buffer -= frame_adjust_2;
+       }
+}
+
 static void amdtp_fill_pcm_silence(struct amdtp_out_stream *s,
                                   __be32 *buffer, unsigned int frames)
 {
@@ -344,8 +434,17 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
                return;
        index = s->packet_index;
 
-       data_blocks = calculate_data_blocks(s);
        syt = calculate_syt(s, cycle);
+       if (!(s->flags & CIP_BLOCKING)) {
+               data_blocks = calculate_data_blocks(s);
+       } else {
+               if (syt != 0xffff) {
+                       data_blocks = s->syt_interval;
+               } else {
+                       data_blocks = 0;
+                       syt = 0xffffff;
+               }
+       }
 
        buffer = s->buffer.packets[index].buffer;
        buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
@@ -386,6 +485,9 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
        s->packet_index = index;
 
        if (pcm) {
+               if (s->dual_wire)
+                       data_blocks *= 2;
+
                ptr = s->pcm_buffer_pointer + data_blocks;
                if (ptr >= pcm->runtime->buffer_size)
                        ptr -= pcm->runtime->buffer_size;
@@ -455,9 +557,8 @@ static int queue_initial_skip_packets(struct amdtp_out_stream *s)
  * @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.
+ * amdtp_out_stream_set_parameters() and amdtp_out_stream_set_pcm_format(),
+ * 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)
 {
@@ -477,7 +578,7 @@ int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed)
 
        mutex_lock(&s->mutex);
 
-       if (WARN_ON(!IS_ERR(s->context) ||
+       if (WARN_ON(amdtp_out_stream_running(s) ||
                    (!s->pcm_channels && !s->midi_ports))) {
                err = -EBADFD;
                goto err_unlock;
@@ -573,7 +674,7 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s)
 {
        mutex_lock(&s->mutex);
 
-       if (IS_ERR(s->context)) {
+       if (!amdtp_out_stream_running(s)) {
                mutex_unlock(&s->mutex);
                return;
        }
index f6103d68c4b1660bf9d61ba8d0c053086eec923b..839ebf812d79e47302e3c4c57b5f2f74695551b7 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED
 #define SOUND_FIREWIRE_AMDTP_H_INCLUDED
 
+#include <linux/err.h>
 #include <linux/interrupt.h>
 #include <linux/mutex.h>
 #include "packets-buffer.h"
  *     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.
+ * @CIP_BLOCKING: In blocking mode, each packet contains either zero or
+ *     SYT_INTERVAL samples, with these two types alternating so that
+ *     the overall sample rate comes out right.
+ * @CIP_HI_DUALWIRE: At rates above 96 kHz, pretend that the stream runs
+ *     at half the actual sample rate with twice the number of channels;
+ *     two samples of a channel are stored consecutively in the packet.
+ *     Requires blocking mode and SYT_INTERVAL-aligned PCM buffer size.
  */
 enum cip_out_flags {
-       CIP_NONBLOCKING = 0,
+       CIP_NONBLOCKING = 0x00,
+       CIP_BLOCKING    = 0x01,
+       CIP_HI_DUALWIRE = 0x02,
 };
 
 /**
@@ -27,6 +37,7 @@ enum cip_sfc {
        CIP_SFC_96000  = 4,
        CIP_SFC_176400 = 5,
        CIP_SFC_192000 = 6,
+       CIP_SFC_COUNT
 };
 
 #define AMDTP_OUT_PCM_FORMAT_BITS      (SNDRV_PCM_FMTBIT_S16 | \
@@ -43,6 +54,7 @@ struct amdtp_out_stream {
        struct mutex mutex;
 
        enum cip_sfc sfc;
+       bool dual_wire;
        unsigned int data_block_quadlets;
        unsigned int pcm_channels;
        unsigned int midi_ports;
@@ -51,6 +63,7 @@ struct amdtp_out_stream {
                                 __be32 *buffer, unsigned int frames);
 
        unsigned int syt_interval;
+       unsigned int transfer_delay;
        unsigned int source_node_id_field;
        struct iso_packets_buffer buffer;
 
@@ -74,7 +87,10 @@ 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);
+void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s,
+                                    unsigned int rate,
+                                    unsigned int pcm_channels,
+                                    unsigned int midi_ports);
 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);
@@ -87,31 +103,11 @@ void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s);
 unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s);
 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;
-}
+extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT];
 
-/**
- * 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)
+static inline bool amdtp_out_stream_running(struct amdtp_out_stream *s)
 {
-       s->midi_ports = midi_ports;
+       return !IS_ERR(s->context);
 }
 
 /**
index 645cb0ba429310f1a0c2569e1a7845a9bf0c4387..efdbf585e4046f7588f1e838139104beecc90c75 100644 (file)
@@ -48,9 +48,6 @@ static int pcr_modify(struct cmp_connection *c,
                      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);
-       int generation = c->resources.generation;
-       int rcode, errors = 0;
        __be32 old_arg, buffer[2];
        int err;
 
@@ -59,36 +56,31 @@ static int pcr_modify(struct cmp_connection *c,
                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,
+               err = snd_fw_transaction(
+                               c->resources.unit, TCODE_LOCK_COMPARE_SWAP,
                                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;
+                               buffer, 8,
+                               FW_FIXED_GENERATION | c->resources.generation);
+
+               if (err < 0) {
+                       if (err == -EAGAIN &&
+                           bus_reset_handling == SUCCEED_ON_BUS_RESET)
+                               err = 0;
+                       return err;
+               }
+
+               if (buffer[0] == old_arg) /* success? */
+                       break;
+
+               if (check) {
+                       err = check(c, buffer[0]);
+                       if (err < 0)
+                               return err;
+               }
        }
        c->last_pcr_value = buffer[1];
 
        return 0;
-
-io_error:
-       cmp_error(c, "transaction failed: %s\n", fw_rcode_string(rcode));
-       return -EIO;
-
-bus_reset:
-       return bus_reset_handling == ABORT_ON_BUS_RESET ? -EAGAIN : 0;
 }
 
 
@@ -108,7 +100,7 @@ int cmp_connection_init(struct cmp_connection *c,
 
        err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
                                 CSR_REGISTER_BASE + CSR_IMPR,
-                                &impr_be, 4);
+                                &impr_be, 4, 0);
        if (err < 0)
                return err;
        impr = be32_to_cpu(impr_be);
diff --git a/sound/firewire/dice-interface.h b/sound/firewire/dice-interface.h
new file mode 100644 (file)
index 0000000..27b044f
--- /dev/null
@@ -0,0 +1,371 @@
+#ifndef SOUND_FIREWIRE_DICE_INTERFACE_H_INCLUDED
+#define SOUND_FIREWIRE_DICE_INTERFACE_H_INCLUDED
+
+/*
+ * DICE device interface definitions
+ */
+
+/*
+ * Generally, all registers can be read like memory, i.e., with quadlet read or
+ * block read transactions with at least quadlet-aligned offset and length.
+ * Writes are not allowed except where noted; quadlet-sized registers must be
+ * written with a quadlet write transaction.
+ *
+ * All values are in big endian.  The DICE firmware runs on a little-endian CPU
+ * and just byte-swaps _all_ quadlets on the bus, so values without endianness
+ * (e.g. strings) get scrambled and must be byte-swapped again by the driver.
+ */
+
+/*
+ * Streaming is handled by the "DICE driver" interface.  Its registers are
+ * located in this private address space.
+ */
+#define DICE_PRIVATE_SPACE             0xffffe0000000uLL
+
+/*
+ * The registers are organized in several sections, which are organized
+ * separately to allow them to be extended individually.  Whether a register is
+ * supported can be detected by checking its offset against its section's size.
+ *
+ * The section offset values are relative to DICE_PRIVATE_SPACE; the offset/
+ * size values are measured in quadlets.  Read-only.
+ */
+#define DICE_GLOBAL_OFFSET             0x00
+#define DICE_GLOBAL_SIZE               0x04
+#define DICE_TX_OFFSET                 0x08
+#define DICE_TX_SIZE                   0x0c
+#define DICE_RX_OFFSET                 0x10
+#define DICE_RX_SIZE                   0x14
+#define DICE_EXT_SYNC_OFFSET           0x18
+#define DICE_EXT_SYNC_SIZE             0x1c
+#define DICE_UNUSED2_OFFSET            0x20
+#define DICE_UNUSED2_SIZE              0x24
+
+/*
+ * Global settings.
+ */
+
+/*
+ * Stores the full 64-bit address (node ID and offset in the node's address
+ * space) where the device will send notifications.  Must be changed with
+ * a compare/swap transaction by the owner.  This register is automatically
+ * cleared on a bus reset.
+ */
+#define GLOBAL_OWNER                   0x000
+#define  OWNER_NO_OWNER                        0xffff000000000000uLL
+#define  OWNER_NODE_SHIFT              48
+
+/*
+ * A bitmask with asynchronous events; read-only.  When any event(s) happen,
+ * the bits of previous events are cleared, and the value of this register is
+ * also written to the address stored in the owner register.
+ */
+#define GLOBAL_NOTIFICATION            0x008
+/* Some registers in the Rx/Tx sections may have changed. */
+#define  NOTIFY_RX_CFG_CHG             0x00000001
+#define  NOTIFY_TX_CFG_CHG             0x00000002
+/* Lock status of the current clock source may have changed. */
+#define  NOTIFY_LOCK_CHG               0x00000010
+/* Write to the clock select register has been finished. */
+#define  NOTIFY_CLOCK_ACCEPTED         0x00000020
+/* Lock status of some clock source has changed. */
+#define  NOTIFY_EXT_STATUS             0x00000040
+/* Other bits may be used for device-specific events. */
+
+/*
+ * A name that can be customized for each device; read/write.  Padded with zero
+ * bytes.  Quadlets are byte-swapped.  The encoding is whatever the host driver
+ * happens to be using.
+ */
+#define GLOBAL_NICK_NAME               0x00c
+#define  NICK_NAME_SIZE                        64
+
+/*
+ * The current sample rate and clock source; read/write.  Whether a clock
+ * source or sample rate is supported is device-specific; the internal clock
+ * source is always available.  Low/mid/high = up to 48/96/192 kHz.  This
+ * register can be changed even while streams are running.
+ */
+#define GLOBAL_CLOCK_SELECT            0x04c
+#define  CLOCK_SOURCE_MASK             0x000000ff
+#define  CLOCK_SOURCE_AES1             0x00000000
+#define  CLOCK_SOURCE_AES2             0x00000001
+#define  CLOCK_SOURCE_AES3             0x00000002
+#define  CLOCK_SOURCE_AES4             0x00000003
+#define  CLOCK_SOURCE_AES_ANY          0x00000004
+#define  CLOCK_SOURCE_ADAT             0x00000005
+#define  CLOCK_SOURCE_TDIF             0x00000006
+#define  CLOCK_SOURCE_WC               0x00000007
+#define  CLOCK_SOURCE_ARX1             0x00000008
+#define  CLOCK_SOURCE_ARX2             0x00000009
+#define  CLOCK_SOURCE_ARX3             0x0000000a
+#define  CLOCK_SOURCE_ARX4             0x0000000b
+#define  CLOCK_SOURCE_INTERNAL         0x0000000c
+#define  CLOCK_RATE_MASK               0x0000ff00
+#define  CLOCK_RATE_32000              0x00000000
+#define  CLOCK_RATE_44100              0x00000100
+#define  CLOCK_RATE_48000              0x00000200
+#define  CLOCK_RATE_88200              0x00000300
+#define  CLOCK_RATE_96000              0x00000400
+#define  CLOCK_RATE_176400             0x00000500
+#define  CLOCK_RATE_192000             0x00000600
+#define  CLOCK_RATE_ANY_LOW            0x00000700
+#define  CLOCK_RATE_ANY_MID            0x00000800
+#define  CLOCK_RATE_ANY_HIGH           0x00000900
+#define  CLOCK_RATE_NONE               0x00000a00
+#define  CLOCK_RATE_SHIFT              8
+
+/*
+ * Enable streaming; read/write.  Writing a non-zero value (re)starts all
+ * streams that have a valid iso channel set; zero stops all streams.  The
+ * streams' parameters must be configured before starting.  This register is
+ * automatically cleared on a bus reset.
+ */
+#define GLOBAL_ENABLE                  0x050
+
+/*
+ * Status of the sample clock; read-only.
+ */
+#define GLOBAL_STATUS                  0x054
+/* The current clock source is locked. */
+#define  STATUS_SOURCE_LOCKED          0x00000001
+/* The actual sample rate; CLOCK_RATE_32000-_192000 or _NONE. */
+#define  STATUS_NOMINAL_RATE_MASK      0x0000ff00
+
+/*
+ * Status of all clock sources; read-only.
+ */
+#define GLOBAL_EXTENDED_STATUS         0x058
+/*
+ * The _LOCKED bits always show the current status; any change generates
+ * a notification.
+ */
+#define  EXT_STATUS_AES1_LOCKED                0x00000001
+#define  EXT_STATUS_AES2_LOCKED                0x00000002
+#define  EXT_STATUS_AES3_LOCKED                0x00000004
+#define  EXT_STATUS_AES4_LOCKED                0x00000008
+#define  EXT_STATUS_ADAT_LOCKED                0x00000010
+#define  EXT_STATUS_TDIF_LOCKED                0x00000020
+#define  EXT_STATUS_ARX1_LOCKED                0x00000040
+#define  EXT_STATUS_ARX2_LOCKED                0x00000080
+#define  EXT_STATUS_ARX3_LOCKED                0x00000100
+#define  EXT_STATUS_ARX4_LOCKED                0x00000200
+#define  EXT_STATUS_WC_LOCKED          0x00000400
+/*
+ * The _SLIP bits do not generate notifications; a set bit indicates that an
+ * error occurred since the last time when this register was read with
+ * a quadlet read transaction.
+ */
+#define  EXT_STATUS_AES1_SLIP          0x00010000
+#define  EXT_STATUS_AES2_SLIP          0x00020000
+#define  EXT_STATUS_AES3_SLIP          0x00040000
+#define  EXT_STATUS_AES4_SLIP          0x00080000
+#define  EXT_STATUS_ADAT_SLIP          0x00100000
+#define  EXT_STATUS_TDIF_SLIP          0x00200000
+#define  EXT_STATUS_ARX1_SLIP          0x00400000
+#define  EXT_STATUS_ARX2_SLIP          0x00800000
+#define  EXT_STATUS_ARX3_SLIP          0x01000000
+#define  EXT_STATUS_ARX4_SLIP          0x02000000
+#define  EXT_STATUS_WC_SLIP            0x04000000
+
+/*
+ * The measured rate of the current clock source, in Hz; read-only.
+ */
+#define GLOBAL_SAMPLE_RATE             0x05c
+
+/*
+ * The version of the DICE driver specification that this device conforms to;
+ * read-only.
+ */
+#define GLOBAL_VERSION                 0x060
+
+/* Some old firmware versions do not have the following global registers: */
+
+/*
+ * Supported sample rates and clock sources; read-only.
+ */
+#define GLOBAL_CLOCK_CAPABILITIES      0x064
+#define  CLOCK_CAP_RATE_32000          0x00000001
+#define  CLOCK_CAP_RATE_44100          0x00000002
+#define  CLOCK_CAP_RATE_48000          0x00000004
+#define  CLOCK_CAP_RATE_88200          0x00000008
+#define  CLOCK_CAP_RATE_96000          0x00000010
+#define  CLOCK_CAP_RATE_176400         0x00000020
+#define  CLOCK_CAP_RATE_192000         0x00000040
+#define  CLOCK_CAP_SOURCE_AES1         0x00010000
+#define  CLOCK_CAP_SOURCE_AES2         0x00020000
+#define  CLOCK_CAP_SOURCE_AES3         0x00040000
+#define  CLOCK_CAP_SOURCE_AES4         0x00080000
+#define  CLOCK_CAP_SOURCE_AES_ANY      0x00100000
+#define  CLOCK_CAP_SOURCE_ADAT         0x00200000
+#define  CLOCK_CAP_SOURCE_TDIF         0x00400000
+#define  CLOCK_CAP_SOURCE_WC           0x00800000
+#define  CLOCK_CAP_SOURCE_ARX1         0x01000000
+#define  CLOCK_CAP_SOURCE_ARX2         0x02000000
+#define  CLOCK_CAP_SOURCE_ARX3         0x04000000
+#define  CLOCK_CAP_SOURCE_ARX4         0x08000000
+#define  CLOCK_CAP_SOURCE_INTERNAL     0x10000000
+
+/*
+ * Names of all clock sources; read-only.  Quadlets are byte-swapped.  Names
+ * are separated with one backslash, the list is terminated with two
+ * backslashes.  Unused clock sources are included.
+ */
+#define GLOBAL_CLOCK_SOURCE_NAMES      0x068
+#define  CLOCK_SOURCE_NAMES_SIZE       256
+
+/*
+ * Capture stream settings.  This section includes the number/size registers
+ * and the registers of all streams.
+ */
+
+/*
+ * The number of supported capture streams; read-only.
+ */
+#define TX_NUMBER                      0x000
+
+/*
+ * The size of one stream's register block, in quadlets; read-only.  The
+ * registers of the first stream follow immediately afterwards; the registers
+ * of the following streams are offset by this register's value.
+ */
+#define TX_SIZE                                0x004
+
+/*
+ * The isochronous channel number on which packets are sent, or -1 if the
+ * stream is not to be used; read/write.
+ */
+#define TX_ISOCHRONOUS                 0x008
+
+/*
+ * The number of audio channels; read-only.  There will be one quadlet per
+ * channel; the first channel is the first quadlet in a data block.
+ */
+#define TX_NUMBER_AUDIO                        0x00c
+
+/*
+ * The number of MIDI ports, 0-8; read-only.  If > 0, there will be one
+ * additional quadlet in each data block, following the audio quadlets.
+ */
+#define TX_NUMBER_MIDI                 0x010
+
+/*
+ * The speed at which the packets are sent, SCODE_100-_400; read/write.
+ */
+#define TX_SPEED                       0x014
+
+/*
+ * Names of all audio channels; read-only.  Quadlets are byte-swapped.  Names
+ * are separated with one backslash, the list is terminated with two
+ * backslashes.
+ */
+#define TX_NAMES                       0x018
+#define  TX_NAMES_SIZE                 256
+
+/*
+ * Audio IEC60958 capabilities; read-only.  Bitmask with one bit per audio
+ * channel.
+ */
+#define TX_AC3_CAPABILITIES            0x118
+
+/*
+ * Send audio data with IEC60958 label; read/write.  Bitmask with one bit per
+ * audio channel.  This register can be changed even while the stream is
+ * running.
+ */
+#define TX_AC3_ENABLE                  0x11c
+
+/*
+ * Playback stream settings.  This section includes the number/size registers
+ * and the registers of all streams.
+ */
+
+/*
+ * The number of supported playback streams; read-only.
+ */
+#define RX_NUMBER                      0x000
+
+/*
+ * The size of one stream's register block, in quadlets; read-only.  The
+ * registers of the first stream follow immediately afterwards; the registers
+ * of the following streams are offset by this register's value.
+ */
+#define RX_SIZE                                0x004
+
+/*
+ * The isochronous channel number on which packets are received, or -1 if the
+ * stream is not to be used; read/write.
+ */
+#define RX_ISOCHRONOUS                 0x008
+
+/*
+ * Index of first quadlet to be interpreted; read/write.  If > 0, that many
+ * quadlets at the beginning of each data block will be ignored, and all the
+ * audio and MIDI quadlets will follow.
+ */
+#define RX_SEQ_START                   0x00c
+
+/*
+ * The number of audio channels; read-only.  There will be one quadlet per
+ * channel.
+ */
+#define RX_NUMBER_AUDIO                        0x010
+
+/*
+ * The number of MIDI ports, 0-8; read-only.  If > 0, there will be one
+ * additional quadlet in each data block, following the audio quadlets.
+ */
+#define RX_NUMBER_MIDI                 0x014
+
+/*
+ * Names of all audio channels; read-only.  Quadlets are byte-swapped.  Names
+ * are separated with one backslash, the list is terminated with two
+ * backslashes.
+ */
+#define RX_NAMES                       0x018
+#define  RX_NAMES_SIZE                 256
+
+/*
+ * Audio IEC60958 capabilities; read-only.  Bitmask with one bit per audio
+ * channel.
+ */
+#define RX_AC3_CAPABILITIES            0x118
+
+/*
+ * Receive audio data with IEC60958 label; read/write.  Bitmask with one bit
+ * per audio channel.  This register can be changed even while the stream is
+ * running.
+ */
+#define RX_AC3_ENABLE                  0x11c
+
+/*
+ * Extended synchronization information.
+ * This section can be read completely with a block read request.
+ */
+
+/*
+ * Current clock source; read-only.
+ */
+#define EXT_SYNC_CLOCK_SOURCE          0x000
+
+/*
+ * Clock source is locked (boolean); read-only.
+ */
+#define EXT_SYNC_LOCKED                        0x004
+
+/*
+ * Current sample rate (CLOCK_RATE_* >> CLOCK_RATE_SHIFT), _32000-_192000 or
+ * _NONE; read-only.
+ */
+#define EXT_SYNC_RATE                  0x008
+
+/*
+ * ADAT user data bits; read-only.
+ */
+#define EXT_SYNC_ADAT_USER_DATA                0x00c
+/* The data bits, if available. */
+#define  ADAT_USER_DATA_MASK           0x0f
+/* The data bits are not available. */
+#define  ADAT_USER_DATA_NO_DATA                0x10
+
+#endif
diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
new file mode 100644 (file)
index 0000000..6feee66
--- /dev/null
@@ -0,0 +1,1494 @@
+/*
+ * TC Applied Technologies Digital Interface Communications Engine driver
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <linux/compat.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "amdtp.h"
+#include "iso-resources.h"
+#include "lib.h"
+#include "dice-interface.h"
+
+
+struct dice {
+       struct snd_card *card;
+       struct fw_unit *unit;
+       spinlock_t lock;
+       struct mutex mutex;
+       unsigned int global_offset;
+       unsigned int rx_offset;
+       unsigned int clock_caps;
+       unsigned int rx_channels[3];
+       unsigned int rx_midi_ports[3];
+       struct fw_address_handler notification_handler;
+       int owner_generation;
+       int dev_lock_count; /* > 0 driver, < 0 userspace */
+       bool dev_lock_changed;
+       bool global_enabled;
+       struct completion clock_accepted;
+       wait_queue_head_t hwdep_wait;
+       u32 notification_bits;
+       struct fw_iso_resources resources;
+       struct amdtp_out_stream stream;
+};
+
+MODULE_DESCRIPTION("DICE driver");
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_LICENSE("GPL v2");
+
+static const unsigned int dice_rates[] = {
+       /* mode 0 */
+       [0] =  32000,
+       [1] =  44100,
+       [2] =  48000,
+       /* mode 1 */
+       [3] =  88200,
+       [4] =  96000,
+       /* mode 2 */
+       [5] = 176400,
+       [6] = 192000,
+};
+
+static unsigned int rate_to_index(unsigned int rate)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(dice_rates); ++i)
+               if (dice_rates[i] == rate)
+                       return i;
+
+       return 0;
+}
+
+static unsigned int rate_index_to_mode(unsigned int rate_index)
+{
+       return ((int)rate_index - 1) / 2;
+}
+
+static void dice_lock_changed(struct dice *dice)
+{
+       dice->dev_lock_changed = true;
+       wake_up(&dice->hwdep_wait);
+}
+
+static int dice_try_lock(struct dice *dice)
+{
+       int err;
+
+       spin_lock_irq(&dice->lock);
+
+       if (dice->dev_lock_count < 0) {
+               err = -EBUSY;
+               goto out;
+       }
+
+       if (dice->dev_lock_count++ == 0)
+               dice_lock_changed(dice);
+       err = 0;
+
+out:
+       spin_unlock_irq(&dice->lock);
+
+       return err;
+}
+
+static void dice_unlock(struct dice *dice)
+{
+       spin_lock_irq(&dice->lock);
+
+       if (WARN_ON(dice->dev_lock_count <= 0))
+               goto out;
+
+       if (--dice->dev_lock_count == 0)
+               dice_lock_changed(dice);
+
+out:
+       spin_unlock_irq(&dice->lock);
+}
+
+static inline u64 global_address(struct dice *dice, unsigned int offset)
+{
+       return DICE_PRIVATE_SPACE + dice->global_offset + offset;
+}
+
+// TODO: rx index
+static inline u64 rx_address(struct dice *dice, unsigned int offset)
+{
+       return DICE_PRIVATE_SPACE + dice->rx_offset + offset;
+}
+
+static int dice_owner_set(struct dice *dice)
+{
+       struct fw_device *device = fw_parent_device(dice->unit);
+       __be64 *buffer;
+       int err, errors = 0;
+
+       buffer = kmalloc(2 * 8, GFP_KERNEL);
+       if (!buffer)
+               return -ENOMEM;
+
+       for (;;) {
+               buffer[0] = cpu_to_be64(OWNER_NO_OWNER);
+               buffer[1] = cpu_to_be64(
+                       ((u64)device->card->node_id << OWNER_NODE_SHIFT) |
+                       dice->notification_handler.offset);
+
+               dice->owner_generation = device->generation;
+               smp_rmb(); /* node_id vs. generation */
+               err = snd_fw_transaction(dice->unit,
+                                        TCODE_LOCK_COMPARE_SWAP,
+                                        global_address(dice, GLOBAL_OWNER),
+                                        buffer, 2 * 8,
+                                        FW_FIXED_GENERATION |
+                                                       dice->owner_generation);
+
+               if (err == 0) {
+                       if (buffer[0] != cpu_to_be64(OWNER_NO_OWNER)) {
+                               dev_err(&dice->unit->device,
+                                       "device is already in use\n");
+                               err = -EBUSY;
+                       }
+                       break;
+               }
+               if (err != -EAGAIN || ++errors >= 3)
+                       break;
+
+               msleep(20);
+       }
+
+       kfree(buffer);
+
+       return err;
+}
+
+static int dice_owner_update(struct dice *dice)
+{
+       struct fw_device *device = fw_parent_device(dice->unit);
+       __be64 *buffer;
+       int err;
+
+       if (dice->owner_generation == -1)
+               return 0;
+
+       buffer = kmalloc(2 * 8, GFP_KERNEL);
+       if (!buffer)
+               return -ENOMEM;
+
+       buffer[0] = cpu_to_be64(OWNER_NO_OWNER);
+       buffer[1] = cpu_to_be64(
+               ((u64)device->card->node_id << OWNER_NODE_SHIFT) |
+               dice->notification_handler.offset);
+
+       dice->owner_generation = device->generation;
+       smp_rmb(); /* node_id vs. generation */
+       err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
+                                global_address(dice, GLOBAL_OWNER),
+                                buffer, 2 * 8,
+                                FW_FIXED_GENERATION | dice->owner_generation);
+
+       if (err == 0) {
+               if (buffer[0] != cpu_to_be64(OWNER_NO_OWNER)) {
+                       dev_err(&dice->unit->device,
+                               "device is already in use\n");
+                       err = -EBUSY;
+               }
+       } else if (err == -EAGAIN) {
+               err = 0; /* try again later */
+       }
+
+       kfree(buffer);
+
+       if (err < 0)
+               dice->owner_generation = -1;
+
+       return err;
+}
+
+static void dice_owner_clear(struct dice *dice)
+{
+       struct fw_device *device = fw_parent_device(dice->unit);
+       __be64 *buffer;
+
+       buffer = kmalloc(2 * 8, GFP_KERNEL);
+       if (!buffer)
+               return;
+
+       buffer[0] = cpu_to_be64(
+               ((u64)device->card->node_id << OWNER_NODE_SHIFT) |
+               dice->notification_handler.offset);
+       buffer[1] = cpu_to_be64(OWNER_NO_OWNER);
+       snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
+                          global_address(dice, GLOBAL_OWNER),
+                          buffer, 2 * 8, FW_QUIET |
+                          FW_FIXED_GENERATION | dice->owner_generation);
+
+       kfree(buffer);
+
+       dice->owner_generation = -1;
+}
+
+static int dice_enable_set(struct dice *dice)
+{
+       __be32 value;
+       int err;
+
+       value = cpu_to_be32(1);
+       err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                global_address(dice, GLOBAL_ENABLE),
+                                &value, 4,
+                                FW_FIXED_GENERATION | dice->owner_generation);
+       if (err < 0)
+               return err;
+
+       dice->global_enabled = true;
+
+       return 0;
+}
+
+static void dice_enable_clear(struct dice *dice)
+{
+       __be32 value;
+
+       if (!dice->global_enabled)
+               return;
+
+       value = 0;
+       snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          global_address(dice, GLOBAL_ENABLE),
+                          &value, 4, FW_QUIET |
+                          FW_FIXED_GENERATION | dice->owner_generation);
+
+       dice->global_enabled = false;
+}
+
+static void dice_notification(struct fw_card *card, struct fw_request *request,
+                             int tcode, int destination, int source,
+                             int generation, unsigned long long offset,
+                             void *data, size_t length, void *callback_data)
+{
+       struct dice *dice = callback_data;
+       u32 bits;
+       unsigned long flags;
+
+       if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
+               fw_send_response(card, request, RCODE_TYPE_ERROR);
+               return;
+       }
+       if ((offset & 3) != 0) {
+               fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+               return;
+       }
+
+       bits = be32_to_cpup(data);
+
+       spin_lock_irqsave(&dice->lock, flags);
+       dice->notification_bits |= bits;
+       spin_unlock_irqrestore(&dice->lock, flags);
+
+       fw_send_response(card, request, RCODE_COMPLETE);
+
+       if (bits & NOTIFY_CLOCK_ACCEPTED)
+               complete(&dice->clock_accepted);
+       wake_up(&dice->hwdep_wait);
+}
+
+static int dice_rate_constraint(struct snd_pcm_hw_params *params,
+                               struct snd_pcm_hw_rule *rule)
+{
+       struct dice *dice = rule->private;
+       const struct snd_interval *channels =
+               hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       struct snd_interval *rate =
+               hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+       struct snd_interval allowed_rates = {
+               .min = UINT_MAX, .max = 0, .integer = 1
+       };
+       unsigned int i, mode;
+
+       for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) {
+               mode = rate_index_to_mode(i);
+               if ((dice->clock_caps & (1 << i)) &&
+                   snd_interval_test(channels, dice->rx_channels[mode])) {
+                       allowed_rates.min = min(allowed_rates.min,
+                                               dice_rates[i]);
+                       allowed_rates.max = max(allowed_rates.max,
+                                               dice_rates[i]);
+               }
+       }
+
+       return snd_interval_refine(rate, &allowed_rates);
+}
+
+static int dice_channels_constraint(struct snd_pcm_hw_params *params,
+                                   struct snd_pcm_hw_rule *rule)
+{
+       struct dice *dice = rule->private;
+       const struct snd_interval *rate =
+               hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+       struct snd_interval *channels =
+               hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       struct snd_interval allowed_channels = {
+               .min = UINT_MAX, .max = 0, .integer = 1
+       };
+       unsigned int i, mode;
+
+       for (i = 0; i < ARRAY_SIZE(dice_rates); ++i)
+               if ((dice->clock_caps & (1 << i)) &&
+                   snd_interval_test(rate, dice_rates[i])) {
+                       mode = rate_index_to_mode(i);
+                       allowed_channels.min = min(allowed_channels.min,
+                                                  dice->rx_channels[mode]);
+                       allowed_channels.max = max(allowed_channels.max,
+                                                  dice->rx_channels[mode]);
+               }
+
+       return snd_interval_refine(channels, &allowed_channels);
+}
+
+static int dice_open(struct snd_pcm_substream *substream)
+{
+       static const struct snd_pcm_hardware hardware = {
+               .info = SNDRV_PCM_INFO_MMAP |
+                       SNDRV_PCM_INFO_MMAP_VALID |
+                       SNDRV_PCM_INFO_BATCH |
+                       SNDRV_PCM_INFO_INTERLEAVED |
+                       SNDRV_PCM_INFO_BLOCK_TRANSFER,
+               .formats = AMDTP_OUT_PCM_FORMAT_BITS,
+               .channels_min = UINT_MAX,
+               .channels_max = 0,
+               .buffer_bytes_max = 16 * 1024 * 1024,
+               .period_bytes_min = 1,
+               .period_bytes_max = UINT_MAX,
+               .periods_min = 1,
+               .periods_max = UINT_MAX,
+       };
+       struct dice *dice = substream->private_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       unsigned int i;
+       int err;
+
+       err = dice_try_lock(dice);
+       if (err < 0)
+               goto error;
+
+       runtime->hw = hardware;
+
+       for (i = 0; i < ARRAY_SIZE(dice_rates); ++i)
+               if (dice->clock_caps & (1 << i))
+                       runtime->hw.rates |=
+                               snd_pcm_rate_to_rate_bit(dice_rates[i]);
+       snd_pcm_limit_hw_rates(runtime);
+
+       for (i = 0; i < 3; ++i)
+               if (dice->rx_channels[i]) {
+                       runtime->hw.channels_min = min(runtime->hw.channels_min,
+                                                      dice->rx_channels[i]);
+                       runtime->hw.channels_max = max(runtime->hw.channels_max,
+                                                      dice->rx_channels[i]);
+               }
+
+       err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+                                 dice_rate_constraint, dice,
+                                 SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+       if (err < 0)
+               goto err_lock;
+       err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                                 dice_channels_constraint, dice,
+                                 SNDRV_PCM_HW_PARAM_RATE, -1);
+       if (err < 0)
+               goto err_lock;
+
+       err = snd_pcm_hw_constraint_step(runtime, 0,
+                                        SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
+       if (err < 0)
+               goto err_lock;
+       err = snd_pcm_hw_constraint_step(runtime, 0,
+                                        SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
+       if (err < 0)
+               goto err_lock;
+
+       err = snd_pcm_hw_constraint_minmax(runtime,
+                                          SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+                                          5000, UINT_MAX);
+       if (err < 0)
+               goto err_lock;
+
+       err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+       if (err < 0)
+               goto err_lock;
+
+       return 0;
+
+err_lock:
+       dice_unlock(dice);
+error:
+       return err;
+}
+
+static int dice_close(struct snd_pcm_substream *substream)
+{
+       struct dice *dice = substream->private_data;
+
+       dice_unlock(dice);
+
+       return 0;
+}
+
+static int dice_stream_start_packets(struct dice *dice)
+{
+       int err;
+
+       if (amdtp_out_stream_running(&dice->stream))
+               return 0;
+
+       err = amdtp_out_stream_start(&dice->stream, dice->resources.channel,
+                                    fw_parent_device(dice->unit)->max_speed);
+       if (err < 0)
+               return err;
+
+       err = dice_enable_set(dice);
+       if (err < 0) {
+               amdtp_out_stream_stop(&dice->stream);
+               return err;
+       }
+
+       return 0;
+}
+
+static int dice_stream_start(struct dice *dice)
+{
+       __be32 channel;
+       int err;
+
+       if (!dice->resources.allocated) {
+               err = fw_iso_resources_allocate(&dice->resources,
+                               amdtp_out_stream_get_max_payload(&dice->stream),
+                               fw_parent_device(dice->unit)->max_speed);
+               if (err < 0)
+                       goto error;
+
+               channel = cpu_to_be32(dice->resources.channel);
+               err = snd_fw_transaction(dice->unit,
+                                        TCODE_WRITE_QUADLET_REQUEST,
+                                        rx_address(dice, RX_ISOCHRONOUS),
+                                        &channel, 4, 0);
+               if (err < 0)
+                       goto err_resources;
+       }
+
+       err = dice_stream_start_packets(dice);
+       if (err < 0)
+               goto err_rx_channel;
+
+       return 0;
+
+err_rx_channel:
+       channel = cpu_to_be32((u32)-1);
+       snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0);
+err_resources:
+       fw_iso_resources_free(&dice->resources);
+error:
+       return err;
+}
+
+static void dice_stream_stop_packets(struct dice *dice)
+{
+       if (amdtp_out_stream_running(&dice->stream)) {
+               dice_enable_clear(dice);
+               amdtp_out_stream_stop(&dice->stream);
+       }
+}
+
+static void dice_stream_stop(struct dice *dice)
+{
+       __be32 channel;
+
+       dice_stream_stop_packets(dice);
+
+       if (!dice->resources.allocated)
+               return;
+
+       channel = cpu_to_be32((u32)-1);
+       snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0);
+
+       fw_iso_resources_free(&dice->resources);
+}
+
+static int dice_change_rate(struct dice *dice, unsigned int clock_rate)
+{
+       __be32 value;
+       int err;
+
+       INIT_COMPLETION(dice->clock_accepted);
+
+       value = cpu_to_be32(clock_rate | CLOCK_SOURCE_ARX1);
+       err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                global_address(dice, GLOBAL_CLOCK_SELECT),
+                                &value, 4, 0);
+       if (err < 0)
+               return err;
+
+       if (!wait_for_completion_timeout(&dice->clock_accepted,
+                                        msecs_to_jiffies(100)))
+               dev_warn(&dice->unit->device, "clock change timed out\n");
+
+       return 0;
+}
+
+static int dice_hw_params(struct snd_pcm_substream *substream,
+                         struct snd_pcm_hw_params *hw_params)
+{
+       struct dice *dice = substream->private_data;
+       unsigned int rate_index, mode;
+       int err;
+
+       mutex_lock(&dice->mutex);
+       dice_stream_stop(dice);
+       mutex_unlock(&dice->mutex);
+
+       err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+                                              params_buffer_bytes(hw_params));
+       if (err < 0)
+               return err;
+
+       rate_index = rate_to_index(params_rate(hw_params));
+       err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT);
+       if (err < 0)
+               return err;
+
+       mode = rate_index_to_mode(rate_index);
+       amdtp_out_stream_set_parameters(&dice->stream,
+                                       params_rate(hw_params),
+                                       params_channels(hw_params),
+                                       dice->rx_midi_ports[mode]);
+       amdtp_out_stream_set_pcm_format(&dice->stream,
+                                       params_format(hw_params));
+
+       return 0;
+}
+
+static int dice_hw_free(struct snd_pcm_substream *substream)
+{
+       struct dice *dice = substream->private_data;
+
+       mutex_lock(&dice->mutex);
+       dice_stream_stop(dice);
+       mutex_unlock(&dice->mutex);
+
+       return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int dice_prepare(struct snd_pcm_substream *substream)
+{
+       struct dice *dice = substream->private_data;
+       int err;
+
+       mutex_lock(&dice->mutex);
+
+       if (amdtp_out_streaming_error(&dice->stream))
+               dice_stream_stop_packets(dice);
+
+       err = dice_stream_start(dice);
+       if (err < 0) {
+               mutex_unlock(&dice->mutex);
+               return err;
+       }
+
+       mutex_unlock(&dice->mutex);
+
+       amdtp_out_stream_pcm_prepare(&dice->stream);
+
+       return 0;
+}
+
+static int dice_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct dice *dice = substream->private_data;
+       struct snd_pcm_substream *pcm;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               pcm = substream;
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               pcm = NULL;
+               break;
+       default:
+               return -EINVAL;
+       }
+       amdtp_out_stream_pcm_trigger(&dice->stream, pcm);
+
+       return 0;
+}
+
+static snd_pcm_uframes_t dice_pointer(struct snd_pcm_substream *substream)
+{
+       struct dice *dice = substream->private_data;
+
+       return amdtp_out_stream_pcm_pointer(&dice->stream);
+}
+
+static int dice_create_pcm(struct dice *dice)
+{
+       static struct snd_pcm_ops ops = {
+               .open      = dice_open,
+               .close     = dice_close,
+               .ioctl     = snd_pcm_lib_ioctl,
+               .hw_params = dice_hw_params,
+               .hw_free   = dice_hw_free,
+               .prepare   = dice_prepare,
+               .trigger   = dice_trigger,
+               .pointer   = dice_pointer,
+               .page      = snd_pcm_lib_get_vmalloc_page,
+               .mmap      = snd_pcm_lib_mmap_vmalloc,
+       };
+       struct snd_pcm *pcm;
+       int err;
+
+       err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm);
+       if (err < 0)
+               return err;
+       pcm->private_data = dice;
+       strcpy(pcm->name, dice->card->shortname);
+       pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->ops = &ops;
+
+       return 0;
+}
+
+static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf,
+                           long count, loff_t *offset)
+{
+       struct dice *dice = hwdep->private_data;
+       DEFINE_WAIT(wait);
+       union snd_firewire_event event;
+
+       spin_lock_irq(&dice->lock);
+
+       while (!dice->dev_lock_changed && dice->notification_bits == 0) {
+               prepare_to_wait(&dice->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+               spin_unlock_irq(&dice->lock);
+               schedule();
+               finish_wait(&dice->hwdep_wait, &wait);
+               if (signal_pending(current))
+                       return -ERESTARTSYS;
+               spin_lock_irq(&dice->lock);
+       }
+
+       memset(&event, 0, sizeof(event));
+       if (dice->dev_lock_changed) {
+               event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+               event.lock_status.status = dice->dev_lock_count > 0;
+               dice->dev_lock_changed = false;
+
+               count = min(count, (long)sizeof(event.lock_status));
+       } else {
+               event.dice_notification.type = SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION;
+               event.dice_notification.notification = dice->notification_bits;
+               dice->notification_bits = 0;
+
+               count = min(count, (long)sizeof(event.dice_notification));
+       }
+
+       spin_unlock_irq(&dice->lock);
+
+       if (copy_to_user(buf, &event, count))
+               return -EFAULT;
+
+       return count;
+}
+
+static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+                                   poll_table *wait)
+{
+       struct dice *dice = hwdep->private_data;
+       unsigned int events;
+
+       poll_wait(file, &dice->hwdep_wait, wait);
+
+       spin_lock_irq(&dice->lock);
+       if (dice->dev_lock_changed || dice->notification_bits != 0)
+               events = POLLIN | POLLRDNORM;
+       else
+               events = 0;
+       spin_unlock_irq(&dice->lock);
+
+       return events;
+}
+
+static int dice_hwdep_get_info(struct dice *dice, void __user *arg)
+{
+       struct fw_device *dev = fw_parent_device(dice->unit);
+       struct snd_firewire_get_info info;
+
+       memset(&info, 0, sizeof(info));
+       info.type = SNDRV_FIREWIRE_TYPE_DICE;
+       info.card = dev->card->index;
+       *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+       *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+       strlcpy(info.device_name, dev_name(&dev->device),
+               sizeof(info.device_name));
+
+       if (copy_to_user(arg, &info, sizeof(info)))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int dice_hwdep_lock(struct dice *dice)
+{
+       int err;
+
+       spin_lock_irq(&dice->lock);
+
+       if (dice->dev_lock_count == 0) {
+               dice->dev_lock_count = -1;
+               err = 0;
+       } else {
+               err = -EBUSY;
+       }
+
+       spin_unlock_irq(&dice->lock);
+
+       return err;
+}
+
+static int dice_hwdep_unlock(struct dice *dice)
+{
+       int err;
+
+       spin_lock_irq(&dice->lock);
+
+       if (dice->dev_lock_count == -1) {
+               dice->dev_lock_count = 0;
+               err = 0;
+       } else {
+               err = -EBADFD;
+       }
+
+       spin_unlock_irq(&dice->lock);
+
+       return err;
+}
+
+static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+       struct dice *dice = hwdep->private_data;
+
+       spin_lock_irq(&dice->lock);
+       if (dice->dev_lock_count == -1)
+               dice->dev_lock_count = 0;
+       spin_unlock_irq(&dice->lock);
+
+       return 0;
+}
+
+static int dice_hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+                           unsigned int cmd, unsigned long arg)
+{
+       struct dice *dice = hwdep->private_data;
+
+       switch (cmd) {
+       case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+               return dice_hwdep_get_info(dice, (void __user *)arg);
+       case SNDRV_FIREWIRE_IOCTL_LOCK:
+               return dice_hwdep_lock(dice);
+       case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+               return dice_hwdep_unlock(dice);
+       default:
+               return -ENOIOCTLCMD;
+       }
+}
+
+#ifdef CONFIG_COMPAT
+static int dice_hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+                                  unsigned int cmd, unsigned long arg)
+{
+       return dice_hwdep_ioctl(hwdep, file, cmd,
+                               (unsigned long)compat_ptr(arg));
+}
+#else
+#define dice_hwdep_compat_ioctl NULL
+#endif
+
+static int dice_create_hwdep(struct dice *dice)
+{
+       static const struct snd_hwdep_ops ops = {
+               .read         = dice_hwdep_read,
+               .release      = dice_hwdep_release,
+               .poll         = dice_hwdep_poll,
+               .ioctl        = dice_hwdep_ioctl,
+               .ioctl_compat = dice_hwdep_compat_ioctl,
+       };
+       struct snd_hwdep *hwdep;
+       int err;
+
+       err = snd_hwdep_new(dice->card, "DICE", 0, &hwdep);
+       if (err < 0)
+               return err;
+       strcpy(hwdep->name, "DICE");
+       hwdep->iface = SNDRV_HWDEP_IFACE_FW_DICE;
+       hwdep->ops = ops;
+       hwdep->private_data = dice;
+       hwdep->exclusive = true;
+
+       return 0;
+}
+
+static int dice_proc_read_mem(struct dice *dice, void *buffer,
+                             unsigned int offset_q, unsigned int quadlets)
+{
+       unsigned int i;
+       int err;
+
+       err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
+                                DICE_PRIVATE_SPACE + 4 * offset_q,
+                                buffer, 4 * quadlets, 0);
+       if (err < 0)
+               return err;
+
+       for (i = 0; i < quadlets; ++i)
+               be32_to_cpus(&((u32 *)buffer)[i]);
+
+       return 0;
+}
+
+static const char *str_from_array(const char *const strs[], unsigned int count,
+                                 unsigned int i)
+{
+       if (i < count)
+               return strs[i];
+       else
+               return "(unknown)";
+}
+
+static void dice_proc_fixup_string(char *s, unsigned int size)
+{
+       unsigned int i;
+
+       for (i = 0; i < size; i += 4)
+               cpu_to_le32s((u32 *)(s + i));
+
+       for (i = 0; i < size - 2; ++i) {
+               if (s[i] == '\0')
+                       return;
+               if (s[i] == '\\' && s[i + 1] == '\\') {
+                       s[i + 2] = '\0';
+                       return;
+               }
+       }
+       s[size - 1] = '\0';
+}
+
+static void dice_proc_read(struct snd_info_entry *entry,
+                          struct snd_info_buffer *buffer)
+{
+       static const char *const section_names[5] = {
+               "global", "tx", "rx", "ext_sync", "unused2"
+       };
+       static const char *const clock_sources[] = {
+               "aes1", "aes2", "aes3", "aes4", "aes", "adat", "tdif",
+               "wc", "arx1", "arx2", "arx3", "arx4", "internal"
+       };
+       static const char *const rates[] = {
+               "32000", "44100", "48000", "88200", "96000", "176400", "192000",
+               "any low", "any mid", "any high", "none"
+       };
+       struct dice *dice = entry->private_data;
+       u32 sections[ARRAY_SIZE(section_names) * 2];
+       struct {
+               u32 number;
+               u32 size;
+       } tx_rx_header;
+       union {
+               struct {
+                       u32 owner_hi, owner_lo;
+                       u32 notification;
+                       char nick_name[NICK_NAME_SIZE];
+                       u32 clock_select;
+                       u32 enable;
+                       u32 status;
+                       u32 extended_status;
+                       u32 sample_rate;
+                       u32 version;
+                       u32 clock_caps;
+                       char clock_source_names[CLOCK_SOURCE_NAMES_SIZE];
+               } global;
+               struct {
+                       u32 iso;
+                       u32 number_audio;
+                       u32 number_midi;
+                       u32 speed;
+                       char names[TX_NAMES_SIZE];
+                       u32 ac3_caps;
+                       u32 ac3_enable;
+               } tx;
+               struct {
+                       u32 iso;
+                       u32 seq_start;
+                       u32 number_audio;
+                       u32 number_midi;
+                       char names[RX_NAMES_SIZE];
+                       u32 ac3_caps;
+                       u32 ac3_enable;
+               } rx;
+               struct {
+                       u32 clock_source;
+                       u32 locked;
+                       u32 rate;
+                       u32 adat_user_data;
+               } ext_sync;
+       } buf;
+       unsigned int quadlets, stream, i;
+
+       if (dice_proc_read_mem(dice, sections, 0, ARRAY_SIZE(sections)) < 0)
+               return;
+       snd_iprintf(buffer, "sections:\n");
+       for (i = 0; i < ARRAY_SIZE(section_names); ++i)
+               snd_iprintf(buffer, "  %s: offset %u, size %u\n",
+                           section_names[i],
+                           sections[i * 2], sections[i * 2 + 1]);
+
+       quadlets = min_t(u32, sections[1], sizeof(buf.global) / 4);
+       if (dice_proc_read_mem(dice, &buf.global, sections[0], quadlets) < 0)
+               return;
+       snd_iprintf(buffer, "global:\n");
+       snd_iprintf(buffer, "  owner: %04x:%04x%08x\n",
+                   buf.global.owner_hi >> 16,
+                   buf.global.owner_hi & 0xffff, buf.global.owner_lo);
+       snd_iprintf(buffer, "  notification: %08x\n", buf.global.notification);
+       dice_proc_fixup_string(buf.global.nick_name, NICK_NAME_SIZE);
+       snd_iprintf(buffer, "  nick name: %s\n", buf.global.nick_name);
+       snd_iprintf(buffer, "  clock select: %s %s\n",
+                   str_from_array(clock_sources, ARRAY_SIZE(clock_sources),
+                                  buf.global.clock_select & CLOCK_SOURCE_MASK),
+                   str_from_array(rates, ARRAY_SIZE(rates),
+                                  (buf.global.clock_select & CLOCK_RATE_MASK)
+                                  >> CLOCK_RATE_SHIFT));
+       snd_iprintf(buffer, "  enable: %u\n", buf.global.enable);
+       snd_iprintf(buffer, "  status: %slocked %s\n",
+                   buf.global.status & STATUS_SOURCE_LOCKED ? "" : "un",
+                   str_from_array(rates, ARRAY_SIZE(rates),
+                                  (buf.global.status &
+                                   STATUS_NOMINAL_RATE_MASK)
+                                  >> CLOCK_RATE_SHIFT));
+       snd_iprintf(buffer, "  ext status: %08x\n", buf.global.extended_status);
+       snd_iprintf(buffer, "  sample rate: %u\n", buf.global.sample_rate);
+       snd_iprintf(buffer, "  version: %u.%u.%u.%u\n",
+                   (buf.global.version >> 24) & 0xff,
+                   (buf.global.version >> 16) & 0xff,
+                   (buf.global.version >>  8) & 0xff,
+                   (buf.global.version >>  0) & 0xff);
+       if (quadlets >= 90) {
+               snd_iprintf(buffer, "  clock caps:");
+               for (i = 0; i <= 6; ++i)
+                       if (buf.global.clock_caps & (1 << i))
+                               snd_iprintf(buffer, " %s", rates[i]);
+               for (i = 0; i <= 12; ++i)
+                       if (buf.global.clock_caps & (1 << (16 + i)))
+                               snd_iprintf(buffer, " %s", clock_sources[i]);
+               snd_iprintf(buffer, "\n");
+               dice_proc_fixup_string(buf.global.clock_source_names,
+                                      CLOCK_SOURCE_NAMES_SIZE);
+               snd_iprintf(buffer, "  clock source names: %s\n",
+                           buf.global.clock_source_names);
+       }
+
+       if (dice_proc_read_mem(dice, &tx_rx_header, sections[2], 2) < 0)
+               return;
+       quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.tx));
+       for (stream = 0; stream < tx_rx_header.number; ++stream) {
+               if (dice_proc_read_mem(dice, &buf.tx, sections[2] + 2 +
+                                      stream * tx_rx_header.size,
+                                      quadlets) < 0)
+                       break;
+               snd_iprintf(buffer, "tx %u:\n", stream);
+               snd_iprintf(buffer, "  iso channel: %d\n", (int)buf.tx.iso);
+               snd_iprintf(buffer, "  audio channels: %u\n",
+                           buf.tx.number_audio);
+               snd_iprintf(buffer, "  midi ports: %u\n", buf.tx.number_midi);
+               snd_iprintf(buffer, "  speed: S%u\n", 100u << buf.tx.speed);
+               if (quadlets >= 68) {
+                       dice_proc_fixup_string(buf.tx.names, TX_NAMES_SIZE);
+                       snd_iprintf(buffer, "  names: %s\n", buf.tx.names);
+               }
+               if (quadlets >= 70) {
+                       snd_iprintf(buffer, "  ac3 caps: %08x\n",
+                                   buf.tx.ac3_caps);
+                       snd_iprintf(buffer, "  ac3 enable: %08x\n",
+                                   buf.tx.ac3_enable);
+               }
+       }
+
+       if (dice_proc_read_mem(dice, &tx_rx_header, sections[4], 2) < 0)
+               return;
+       quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.rx));
+       for (stream = 0; stream < tx_rx_header.number; ++stream) {
+               if (dice_proc_read_mem(dice, &buf.rx, sections[4] + 2 +
+                                      stream * tx_rx_header.size,
+                                      quadlets) < 0)
+                       break;
+               snd_iprintf(buffer, "rx %u:\n", stream);
+               snd_iprintf(buffer, "  iso channel: %d\n", (int)buf.rx.iso);
+               snd_iprintf(buffer, "  sequence start: %u\n", buf.rx.seq_start);
+               snd_iprintf(buffer, "  audio channels: %u\n",
+                           buf.rx.number_audio);
+               snd_iprintf(buffer, "  midi ports: %u\n", buf.rx.number_midi);
+               if (quadlets >= 68) {
+                       dice_proc_fixup_string(buf.rx.names, RX_NAMES_SIZE);
+                       snd_iprintf(buffer, "  names: %s\n", buf.rx.names);
+               }
+               if (quadlets >= 70) {
+                       snd_iprintf(buffer, "  ac3 caps: %08x\n",
+                                   buf.rx.ac3_caps);
+                       snd_iprintf(buffer, "  ac3 enable: %08x\n",
+                                   buf.rx.ac3_enable);
+               }
+       }
+
+       quadlets = min_t(u32, sections[7], sizeof(buf.ext_sync) / 4);
+       if (quadlets >= 4) {
+               if (dice_proc_read_mem(dice, &buf.ext_sync,
+                                      sections[6], 4) < 0)
+                       return;
+               snd_iprintf(buffer, "ext status:\n");
+               snd_iprintf(buffer, "  clock source: %s\n",
+                           str_from_array(clock_sources,
+                                          ARRAY_SIZE(clock_sources),
+                                          buf.ext_sync.clock_source));
+               snd_iprintf(buffer, "  locked: %u\n", buf.ext_sync.locked);
+               snd_iprintf(buffer, "  rate: %s\n",
+                           str_from_array(rates, ARRAY_SIZE(rates),
+                                          buf.ext_sync.rate));
+               snd_iprintf(buffer, "  adat user data: ");
+               if (buf.ext_sync.adat_user_data & ADAT_USER_DATA_NO_DATA)
+                       snd_iprintf(buffer, "-\n");
+               else
+                       snd_iprintf(buffer, "%x\n",
+                                   buf.ext_sync.adat_user_data);
+       }
+}
+
+static void dice_create_proc(struct dice *dice)
+{
+       struct snd_info_entry *entry;
+
+       if (!snd_card_proc_new(dice->card, "dice", &entry))
+               snd_info_set_text_ops(entry, dice, dice_proc_read);
+}
+
+static void dice_card_free(struct snd_card *card)
+{
+       struct dice *dice = card->private_data;
+
+       amdtp_out_stream_destroy(&dice->stream);
+       fw_core_remove_address_handler(&dice->notification_handler);
+       mutex_destroy(&dice->mutex);
+}
+
+#define OUI_WEISS              0x001c6a
+
+#define DICE_CATEGORY_ID       0x04
+#define WEISS_CATEGORY_ID      0x00
+
+static int dice_interface_check(struct fw_unit *unit)
+{
+       static const int min_values[10] = {
+               10, 0x64 / 4,
+               10, 0x18 / 4,
+               10, 0x18 / 4,
+               0, 0,
+               0, 0,
+       };
+       struct fw_device *device = fw_parent_device(unit);
+       struct fw_csr_iterator it;
+       int key, value, vendor = -1, model = -1, err;
+       unsigned int category, i;
+       __be32 pointers[ARRAY_SIZE(min_values)];
+       __be32 tx_data[4];
+       __be32 version;
+
+       /*
+        * Check that GUID and unit directory are constructed according to DICE
+        * rules, i.e., that the specifier ID is the GUID's OUI, and that the
+        * GUID chip ID consists of the 8-bit category ID, the 10-bit product
+        * ID, and a 22-bit serial number.
+        */
+       fw_csr_iterator_init(&it, unit->directory);
+       while (fw_csr_iterator_next(&it, &key, &value)) {
+               switch (key) {
+               case CSR_SPECIFIER_ID:
+                       vendor = value;
+                       break;
+               case CSR_MODEL:
+                       model = value;
+                       break;
+               }
+       }
+       if (vendor == OUI_WEISS)
+               category = WEISS_CATEGORY_ID;
+       else
+               category = DICE_CATEGORY_ID;
+       if (device->config_rom[3] != ((vendor << 8) | category) ||
+           device->config_rom[4] >> 22 != model)
+               return -ENODEV;
+
+       /*
+        * Check that the sub address spaces exist and are located inside the
+        * private address space.  The minimum values are chosen so that all
+        * minimally required registers are included.
+        */
+       err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST,
+                                DICE_PRIVATE_SPACE,
+                                pointers, sizeof(pointers), 0);
+       if (err < 0)
+               return -ENODEV;
+       for (i = 0; i < ARRAY_SIZE(pointers); ++i) {
+               value = be32_to_cpu(pointers[i]);
+               if (value < min_values[i] || value >= 0x40000)
+                       return -ENODEV;
+       }
+
+       /* We support playback only. Let capture devices be handled by FFADO. */
+       err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST,
+                                DICE_PRIVATE_SPACE +
+                                be32_to_cpu(pointers[2]) * 4,
+                                tx_data, sizeof(tx_data), 0);
+       if (err < 0 || (tx_data[0] && tx_data[3]))
+               return -ENODEV;
+
+       /*
+        * Check that the implemented DICE driver specification major version
+        * number matches.
+        */
+       err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
+                                DICE_PRIVATE_SPACE +
+                                be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION,
+                                &version, 4, 0);
+       if (err < 0)
+               return -ENODEV;
+       if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) {
+               dev_err(&unit->device,
+                       "unknown DICE version: 0x%08x\n", be32_to_cpu(version));
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static int highest_supported_mode_rate(struct dice *dice, unsigned int mode)
+{
+       int i;
+
+       for (i = ARRAY_SIZE(dice_rates) - 1; i >= 0; --i)
+               if ((dice->clock_caps & (1 << i)) &&
+                   rate_index_to_mode(i) == mode)
+                       return i;
+
+       return -1;
+}
+
+static int dice_read_mode_params(struct dice *dice, unsigned int mode)
+{
+       __be32 values[2];
+       int rate_index, err;
+
+       rate_index = highest_supported_mode_rate(dice, mode);
+       if (rate_index < 0) {
+               dice->rx_channels[mode] = 0;
+               dice->rx_midi_ports[mode] = 0;
+               return 0;
+       }
+
+       err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT);
+       if (err < 0)
+               return err;
+
+       err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
+                                rx_address(dice, RX_NUMBER_AUDIO),
+                                values, 2 * 4, 0);
+       if (err < 0)
+               return err;
+
+       dice->rx_channels[mode]   = be32_to_cpu(values[0]);
+       dice->rx_midi_ports[mode] = be32_to_cpu(values[1]);
+
+       return 0;
+}
+
+static int dice_read_params(struct dice *dice)
+{
+       __be32 pointers[6];
+       __be32 value;
+       int mode, err;
+
+       err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
+                                DICE_PRIVATE_SPACE,
+                                pointers, sizeof(pointers), 0);
+       if (err < 0)
+               return err;
+
+       dice->global_offset = be32_to_cpu(pointers[0]) * 4;
+       dice->rx_offset = be32_to_cpu(pointers[4]) * 4;
+
+       /* some very old firmwares don't tell about their clock support */
+       if (be32_to_cpu(pointers[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES + 4) {
+               err = snd_fw_transaction(
+                               dice->unit, TCODE_READ_QUADLET_REQUEST,
+                               global_address(dice, GLOBAL_CLOCK_CAPABILITIES),
+                               &value, 4, 0);
+               if (err < 0)
+                       return err;
+               dice->clock_caps = be32_to_cpu(value);
+       } else {
+               /* this should be supported by any device */
+               dice->clock_caps = CLOCK_CAP_RATE_44100 |
+                                  CLOCK_CAP_RATE_48000 |
+                                  CLOCK_CAP_SOURCE_ARX1 |
+                                  CLOCK_CAP_SOURCE_INTERNAL;
+       }
+
+       for (mode = 2; mode >= 0; --mode) {
+               err = dice_read_mode_params(dice, mode);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+static void dice_card_strings(struct dice *dice)
+{
+       struct snd_card *card = dice->card;
+       struct fw_device *dev = fw_parent_device(dice->unit);
+       char vendor[32], model[32];
+       unsigned int i;
+       int err;
+
+       strcpy(card->driver, "DICE");
+
+       strcpy(card->shortname, "DICE");
+       BUILD_BUG_ON(NICK_NAME_SIZE < sizeof(card->shortname));
+       err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
+                                global_address(dice, GLOBAL_NICK_NAME),
+                                card->shortname, sizeof(card->shortname), 0);
+       if (err >= 0) {
+               /* DICE strings are returned in "always-wrong" endianness */
+               BUILD_BUG_ON(sizeof(card->shortname) % 4 != 0);
+               for (i = 0; i < sizeof(card->shortname); i += 4)
+                       swab32s((u32 *)&card->shortname[i]);
+               card->shortname[sizeof(card->shortname) - 1] = '\0';
+       }
+
+       strcpy(vendor, "?");
+       fw_csr_string(dev->config_rom + 5, CSR_VENDOR, vendor, sizeof(vendor));
+       strcpy(model, "?");
+       fw_csr_string(dice->unit->directory, CSR_MODEL, model, sizeof(model));
+       snprintf(card->longname, sizeof(card->longname),
+                "%s %s (serial %u) at %s, S%d",
+                vendor, model, dev->config_rom[4] & 0x3fffff,
+                dev_name(&dice->unit->device), 100 << dev->max_speed);
+
+       strcpy(card->mixername, "DICE");
+}
+
+static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
+{
+       struct snd_card *card;
+       struct dice *dice;
+       __be32 clock_sel;
+       int err;
+
+       err = dice_interface_check(unit);
+       if (err < 0)
+               return err;
+
+       err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*dice), &card);
+       if (err < 0)
+               return err;
+       snd_card_set_dev(card, &unit->device);
+
+       dice = card->private_data;
+       dice->card = card;
+       spin_lock_init(&dice->lock);
+       mutex_init(&dice->mutex);
+       dice->unit = unit;
+       init_completion(&dice->clock_accepted);
+       init_waitqueue_head(&dice->hwdep_wait);
+
+       dice->notification_handler.length = 4;
+       dice->notification_handler.address_callback = dice_notification;
+       dice->notification_handler.callback_data = dice;
+       err = fw_core_add_address_handler(&dice->notification_handler,
+                                         &fw_high_memory_region);
+       if (err < 0)
+               goto err_mutex;
+
+       err = dice_owner_set(dice);
+       if (err < 0)
+               goto err_notification_handler;
+
+       err = dice_read_params(dice);
+       if (err < 0)
+               goto err_owner;
+
+       err = fw_iso_resources_init(&dice->resources, unit);
+       if (err < 0)
+               goto err_owner;
+       dice->resources.channels_mask = 0x00000000ffffffffuLL;
+
+       err = amdtp_out_stream_init(&dice->stream, unit,
+                                   CIP_BLOCKING | CIP_HI_DUALWIRE);
+       if (err < 0)
+               goto err_resources;
+
+       card->private_free = dice_card_free;
+
+       dice_card_strings(dice);
+
+       err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
+                                global_address(dice, GLOBAL_CLOCK_SELECT),
+                                &clock_sel, 4, 0);
+       if (err < 0)
+               goto error;
+       clock_sel &= cpu_to_be32(~CLOCK_SOURCE_MASK);
+       clock_sel |= cpu_to_be32(CLOCK_SOURCE_ARX1);
+       err = snd_fw_transaction(unit, TCODE_WRITE_QUADLET_REQUEST,
+                                global_address(dice, GLOBAL_CLOCK_SELECT),
+                                &clock_sel, 4, 0);
+       if (err < 0)
+               goto error;
+
+       err = dice_create_pcm(dice);
+       if (err < 0)
+               goto error;
+
+       err = dice_create_hwdep(dice);
+       if (err < 0)
+               goto error;
+
+       dice_create_proc(dice);
+
+       err = snd_card_register(card);
+       if (err < 0)
+               goto error;
+
+       dev_set_drvdata(&unit->device, dice);
+
+       return 0;
+
+err_resources:
+       fw_iso_resources_destroy(&dice->resources);
+err_owner:
+       dice_owner_clear(dice);
+err_notification_handler:
+       fw_core_remove_address_handler(&dice->notification_handler);
+err_mutex:
+       mutex_destroy(&dice->mutex);
+error:
+       snd_card_free(card);
+       return err;
+}
+
+static void dice_remove(struct fw_unit *unit)
+{
+       struct dice *dice = dev_get_drvdata(&unit->device);
+
+       amdtp_out_stream_pcm_abort(&dice->stream);
+
+       snd_card_disconnect(dice->card);
+
+       mutex_lock(&dice->mutex);
+
+       dice_stream_stop(dice);
+       dice_owner_clear(dice);
+
+       mutex_unlock(&dice->mutex);
+
+       snd_card_free_when_closed(dice->card);
+}
+
+static void dice_bus_reset(struct fw_unit *unit)
+{
+       struct dice *dice = dev_get_drvdata(&unit->device);
+
+       /*
+        * On a bus reset, the DICE firmware disables streaming and then goes
+        * off contemplating its own navel for hundreds of milliseconds before
+        * it can react to any of our attempts to reenable streaming.  This
+        * means that we lose synchronization anyway, so we force our streams
+        * to stop so that the application can restart them in an orderly
+        * manner.
+        */
+       amdtp_out_stream_pcm_abort(&dice->stream);
+
+       mutex_lock(&dice->mutex);
+
+       dice->global_enabled = false;
+       dice_stream_stop_packets(dice);
+
+       dice_owner_update(dice);
+
+       fw_iso_resources_update(&dice->resources);
+
+       mutex_unlock(&dice->mutex);
+}
+
+#define DICE_INTERFACE 0x000001
+
+static const struct ieee1394_device_id dice_id_table[] = {
+       {
+               .match_flags = IEEE1394_MATCH_VERSION,
+               .version     = DICE_INTERFACE,
+       },
+       { }
+};
+MODULE_DEVICE_TABLE(ieee1394, dice_id_table);
+
+static struct fw_driver dice_driver = {
+       .driver   = {
+               .owner  = THIS_MODULE,
+               .name   = KBUILD_MODNAME,
+               .bus    = &fw_bus_type,
+       },
+       .probe    = dice_probe,
+       .update   = dice_bus_reset,
+       .remove   = dice_remove,
+       .id_table = dice_id_table,
+};
+
+static int __init alsa_dice_init(void)
+{
+       return driver_register(&dice_driver.driver);
+}
+
+static void __exit alsa_dice_exit(void)
+{
+       driver_unregister(&dice_driver.driver);
+}
+
+module_init(alsa_dice_init);
+module_exit(alsa_dice_exit);
index ec578b5ad8da1f45738b76f714574243db76a1db..860c08073c5924ef1fad28d2a3bfed747f947879 100644 (file)
@@ -90,7 +90,7 @@ int fcp_avc_transaction(struct fw_unit *unit,
                                          : TCODE_WRITE_BLOCK_REQUEST;
                ret = snd_fw_transaction(t.unit, tcode,
                                         CSR_REGISTER_BASE + CSR_FCP_COMMAND,
-                                        (void *)command, command_size);
+                                        (void *)command, command_size, 0);
                if (ret < 0)
                        break;
 
index 58a5afefdc69f39ef6720626ad823b5ae55060c9..fd42e6b315e69d90e39deda16d553fc0baf97395 100644 (file)
@@ -217,7 +217,7 @@ static void isight_packet(struct fw_iso_context *context, u32 cycle,
 
 static int isight_connect(struct isight *isight)
 {
-       int ch, err, rcode, errors = 0;
+       int ch, err;
        __be32 value;
 
 retry_after_bus_reset:
@@ -230,27 +230,19 @@ retry_after_bus_reset:
        }
 
        value = cpu_to_be32(ch | (isight->device->max_speed << SPEED_SHIFT));
-       for (;;) {
-               rcode = fw_run_transaction(
-                               isight->device->card,
-                               TCODE_WRITE_QUADLET_REQUEST,
-                               isight->device->node_id,
-                               isight->resources.generation,
-                               isight->device->max_speed,
-                               isight->audio_base + REG_ISO_TX_CONFIG,
-                               &value, 4);
-               if (rcode == RCODE_COMPLETE) {
-                       return 0;
-               } else if (rcode == RCODE_GENERATION) {
-                       fw_iso_resources_free(&isight->resources);
-                       goto retry_after_bus_reset;
-               } else if (rcode_is_permanent_error(rcode) || ++errors >= 3) {
-                       err = -EIO;
-                       goto err_resources;
-               }
-               msleep(5);
+       err = snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                isight->audio_base + REG_ISO_TX_CONFIG,
+                                &value, 4, FW_FIXED_GENERATION |
+                                isight->resources.generation);
+       if (err == -EAGAIN) {
+               fw_iso_resources_free(&isight->resources);
+               goto retry_after_bus_reset;
+       } else if (err < 0) {
+               goto err_resources;
        }
 
+       return 0;
+
 err_resources:
        fw_iso_resources_free(&isight->resources);
 error:
@@ -315,17 +307,19 @@ static int isight_hw_params(struct snd_pcm_substream *substream,
 static int reg_read(struct isight *isight, int offset, __be32 *value)
 {
        return snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST,
-                                 isight->audio_base + offset, value, 4);
+                                 isight->audio_base + offset, value, 4, 0);
 }
 
 static int reg_write(struct isight *isight, int offset, __be32 value)
 {
        return snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
-                                 isight->audio_base + offset, &value, 4);
+                                 isight->audio_base + offset, &value, 4, 0);
 }
 
 static void isight_stop_streaming(struct isight *isight)
 {
+       __be32 value;
+
        if (!isight->context)
                return;
 
@@ -333,7 +327,10 @@ static void isight_stop_streaming(struct isight *isight)
        fw_iso_context_destroy(isight->context);
        isight->context = NULL;
        fw_iso_resources_free(&isight->resources);
-       reg_write(isight, REG_AUDIO_ENABLE, 0);
+       value = 0;
+       snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          isight->audio_base + REG_AUDIO_ENABLE,
+                          &value, 4, FW_QUIET);
 }
 
 static int isight_hw_free(struct snd_pcm_substream *substream)
index 14eb414983720fcc629bb55ae962c43be97e8205..7409edba9f06761b11d8582bd60fb43bcc8a0ff6 100644 (file)
@@ -11,7 +11,7 @@
 #include <linux/module.h>
 #include "lib.h"
 
-#define ERROR_RETRY_DELAY_MS   5
+#define ERROR_RETRY_DELAY_MS   20
 
 /**
  * snd_fw_transaction - send a request and wait for its completion
@@ -20,6 +20,9 @@
  * @offset: the address in the target's address space
  * @buffer: input/output data
  * @length: length of @buffer
+ * @flags: use %FW_FIXED_GENERATION and add the generation value to attempt the
+ *         request only in that generation; use %FW_QUIET to suppress error
+ *         messages
  *
  * Submits an asynchronous request to the target device, and waits for the
  * response.  The node ID and the current generation are derived from @unit.
  * 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)
+                      u64 offset, void *buffer, size_t length,
+                      unsigned int flags)
 {
        struct fw_device *device = fw_parent_device(unit);
        int generation, rcode, tries = 0;
 
+       generation = flags & FW_GENERATION_MASK;
        for (;;) {
-               generation = device->generation;
-               smp_rmb(); /* node_id vs. generation */
+               if (!(flags & FW_FIXED_GENERATION)) {
+                       generation = device->generation;
+                       smp_rmb(); /* node_id vs. generation */
+               }
                rcode = fw_run_transaction(device->card, tcode,
                                           device->node_id, generation,
                                           device->max_speed, offset,
@@ -43,9 +50,14 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode,
                if (rcode == RCODE_COMPLETE)
                        return 0;
 
+               if (rcode == RCODE_GENERATION && (flags & FW_FIXED_GENERATION))
+                       return -EAGAIN;
+
                if (rcode_is_permanent_error(rcode) || ++tries >= 3) {
-                       dev_err(&unit->device, "transaction failed: %s\n",
-                               fw_rcode_string(rcode));
+                       if (!(flags & FW_QUIET))
+                               dev_err(&unit->device,
+                                       "transaction failed: %s\n",
+                                       fw_rcode_string(rcode));
                        return -EIO;
                }
 
index aef301476ea943bf9bbff8a9bfb8a28b6661a906..02cfabc9c3c4ba53d18e7b827600491fd9c2df84 100644 (file)
@@ -6,8 +6,13 @@
 
 struct fw_unit;
 
+#define FW_GENERATION_MASK     0x00ff
+#define FW_FIXED_GENERATION    0x0100
+#define FW_QUIET               0x0200
+
 int snd_fw_transaction(struct fw_unit *unit, int tcode,
-                      u64 offset, void *buffer, size_t length);
+                      u64 offset, void *buffer, size_t length,
+                      unsigned int flags);
 
 /* returns true if retrying the transaction would not make sense */
 static inline bool rcode_is_permanent_error(int rcode)
index 505fc812319958e12e804a2917c6d6cd3b4df88d..858023cf4298c0cbf6022e16824ab26f085f2b59 100644 (file)
@@ -369,7 +369,7 @@ static int scs_init_hss_address(struct scs *scs)
        data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) |
                           scs->hss_handler.offset);
        err = snd_fw_transaction(scs->unit, TCODE_WRITE_BLOCK_REQUEST,
-                                HSS1394_ADDRESS, &data, 8);
+                                HSS1394_ADDRESS, &data, 8, 0);
        if (err < 0)
                dev_err(&scs->unit->device, "HSS1394 communication failed\n");
 
@@ -455,12 +455,16 @@ err_card:
 static void scs_update(struct fw_unit *unit)
 {
        struct scs *scs = dev_get_drvdata(&unit->device);
+       int generation;
        __be64 data;
 
        data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) |
                           scs->hss_handler.offset);
+       generation = fw_parent_device(unit)->generation;
+       smp_rmb(); /* node_id vs. generation */
        snd_fw_transaction(scs->unit, TCODE_WRITE_BLOCK_REQUEST,
-                          HSS1394_ADDRESS, &data, 8);
+                          HSS1394_ADDRESS, &data, 8,
+                          FW_FIXED_GENERATION | generation);
 }
 
 static void scs_remove(struct fw_unit *unit)
index fe9e6e2f2c5b2a825ae24a432541d45de8f029fb..cc8bc3a51bc147ab7a13c0a38ec84134b55f9da3 100644 (file)
@@ -52,7 +52,6 @@ struct fwspk {
        struct mutex mutex;
        struct cmp_connection connection;
        struct amdtp_out_stream stream;
-       bool stream_running;
        bool mute;
        s16 volume[6];
        s16 volume_min;
@@ -188,10 +187,9 @@ static int fwspk_close(struct snd_pcm_substream *substream)
 
 static void fwspk_stop_stream(struct fwspk *fwspk)
 {
-       if (fwspk->stream_running) {
+       if (amdtp_out_stream_running(&fwspk->stream)) {
                amdtp_out_stream_stop(&fwspk->stream);
                cmp_connection_break(&fwspk->connection);
-               fwspk->stream_running = false;
        }
 }
 
@@ -246,8 +244,10 @@ static int fwspk_hw_params(struct snd_pcm_substream *substream,
        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_parameters(&fwspk->stream,
+                                       params_rate(hw_params),
+                                       params_channels(hw_params),
+                                       0);
 
        amdtp_out_stream_set_pcm_format(&fwspk->stream,
                                        params_format(hw_params));
@@ -285,7 +285,7 @@ static int fwspk_prepare(struct snd_pcm_substream *substream)
        if (amdtp_out_streaming_error(&fwspk->stream))
                fwspk_stop_stream(fwspk);
 
-       if (!fwspk->stream_running) {
+       if (!amdtp_out_stream_running(&fwspk->stream)) {
                err = cmp_connection_establish(&fwspk->connection,
                        amdtp_out_stream_get_max_payload(&fwspk->stream));
                if (err < 0)
@@ -296,8 +296,6 @@ static int fwspk_prepare(struct snd_pcm_substream *substream)
                                        fwspk->connection.speed);
                if (err < 0)
                        goto err_connection;
-
-               fwspk->stream_running = true;
        }
 
        mutex_unlock(&fwspk->mutex);
@@ -647,7 +645,7 @@ static u32 fwspk_read_firmware_version(struct fw_unit *unit)
        int err;
 
        err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
-                                OXFORD_FIRMWARE_ID_ADDRESS, &data, 4);
+                                OXFORD_FIRMWARE_ID_ADDRESS, &data, 4, 0);
        return err >= 0 ? be32_to_cpu(data) : 0;
 }
 
index 5bf4fca19e48656916180ecc353109018b771aa3..15ae0250eacec065d90c4093127f4604bec84a99 100644 (file)
@@ -60,7 +60,7 @@ static void reg_dump(struct ak4114 *ak4114)
 
        printk(KERN_DEBUG "AK4114 REG DUMP:\n");
        for (i = 0; i < 0x20; i++)
-               printk(KERN_DEBUG "reg[%02x] = %02x (%02x)\n", i, reg_read(ak4114, i), i < sizeof(ak4114->regmap) ? ak4114->regmap[i] : 0);
+               printk(KERN_DEBUG "reg[%02x] = %02x (%02x)\n", i, reg_read(ak4114, i), i < ARRAY_SIZE(ak4114->regmap) ? ak4114->regmap[i] : 0);
 }
 #endif
 
@@ -81,7 +81,7 @@ static int snd_ak4114_dev_free(struct snd_device *device)
 
 int snd_ak4114_create(struct snd_card *card,
                      ak4114_read_t *read, ak4114_write_t *write,
-                     const unsigned char pgm[7], const unsigned char txcsb[5],
+                     const unsigned char pgm[6], const unsigned char txcsb[5],
                      void *private_data, struct ak4114 **r_ak4114)
 {
        struct ak4114 *chip;
@@ -101,7 +101,7 @@ int snd_ak4114_create(struct snd_card *card,
        chip->private_data = private_data;
        INIT_DELAYED_WORK(&chip->work, ak4114_stats);
 
-       for (reg = 0; reg < 7; reg++)
+       for (reg = 0; reg < 6; reg++)
                chip->regmap[reg] = pgm[reg];
        for (reg = 0; reg < 5; reg++)
                chip->txcsb[reg] = txcsb[reg];
@@ -142,7 +142,7 @@ static void ak4114_init_regs(struct ak4114 *chip)
        /* release reset, but leave powerdown */
        reg_write(chip, AK4114_REG_PWRDN, (old | AK4114_RST) & ~AK4114_PWN);
        udelay(200);
-       for (reg = 1; reg < 7; reg++)
+       for (reg = 1; reg < 6; reg++)
                reg_write(chip, reg, chip->regmap[reg]);
        for (reg = 0; reg < 5; reg++)
                reg_write(chip, reg + AK4114_REG_TXCSB0, chip->txcsb[reg]);
index ed726d1569e8d3e9dffd49ee2af31fc79b7b7bc8..f3735e64791cb2d76a7240d2f7b937b535d624c0 100644 (file)
@@ -583,7 +583,7 @@ static int ak4xxx_capture_source_info(struct snd_kcontrol *kcontrol,
        if (idx >= num_names)
                return -EINVAL;
        input_names = ak->adc_info[mixer_ch].input_names;
-       strncpy(uinfo->value.enumerated.name, input_names[idx],
+       strlcpy(uinfo->value.enumerated.name, input_names[idx],
                sizeof(uinfo->value.enumerated.name));
        return 0;
 }
index f84f073fc1e82db9738788f2d930d75993c98c2d..ab6b2dc043f1e399d3205ba802448433f61f357e 100644 (file)
@@ -126,6 +126,7 @@ static void snd_cmi8328_cfg_write(u16 port, u8 reg, u8 val)
        outb(val, port + 3);    /* yes, value goes to the same port as index */
 }
 
+#ifdef CONFIG_PM
 static void snd_cmi8328_cfg_save(u16 port, u8 cfg[])
 {
        cfg[0] = snd_cmi8328_cfg_read(port, CFG1);
@@ -139,6 +140,7 @@ static void snd_cmi8328_cfg_restore(u16 port, u8 cfg[])
        snd_cmi8328_cfg_write(port, CFG2, cfg[1]);
        snd_cmi8328_cfg_write(port, CFG3, cfg[2]);
 }
+#endif /* CONFIG_PM */
 
 static int snd_cmi8328_mixer(struct snd_wss *chip)
 {
index c1aa21edcb653b78dc664e97e75db7bd68bbe834..48da2276683d275ece81027f44d3906b347546b5 100644 (file)
@@ -208,6 +208,7 @@ static int snd_sb_csp_ioctl(struct snd_hwdep * hw, struct file *file, unsigned i
        switch (cmd) {
                /* get information */
        case SNDRV_SB_CSP_IOCTL_INFO:
+               memset(&info, 0, sizeof(info));
                *info.codec_name = *p->codec_name;
                info.func_nr = p->func_nr;
                info.acc_format = p->acc_format;
index c624510ec37476eccfec30d0e5c48ac2ec9647be..586907500ca534eb6ebea572795dc7725d8dade0 100644 (file)
@@ -276,7 +276,7 @@ static void ad1843_write_multi(struct snd_ad1843 *ad1843, int argcount, ...)
                if (reg == -1)
                        reg = fp->reg;
                else
-                       BUG_ON(reg != fp->reg);
+                       WARN_ON(reg != fp->reg);
                m = ((1 << fp->nbits) - 1) << fp->lo_bit;
                mask |= m;
                bits |= (value << fp->lo_bit) & m;
index c0be085e4a200e4878097138b9294fe0d4a0855c..0e7254bde4c271cb9591724c2c8da0210c0d4426 100644 (file)
@@ -1544,7 +1544,7 @@ static int ess_has_rec_mixer (int submodel)
                return 1;
        default:
                return 0;
-       };
+       }
 };
 
 #ifdef FKS_LOGGING
index d2b9d617aee52e1343a61e134e1c82f97db2c272..b680d03e24192e0079e733ec0079d7e6b3ef4904 100644 (file)
@@ -739,7 +739,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
        reg = ad1889_readw(chip, AD_DS_WADA);
        snd_iprintf(buffer, "Right: %s, -%d dB\n",
                        (reg & AD_DS_WADA_RWAM) ? "mute" : "unmute",
-                       ((reg & AD_DS_WADA_RWAA) >> 8) * 3);
+                       (reg & AD_DS_WADA_RWAA) * 3);
        
        reg = ad1889_readw(chip, AD_DS_WAS);
        snd_iprintf(buffer, "Wave samplerate: %u Hz\n", reg);
index 3dfa12b670ebfaf0a9ce5c04fed7710eea28b211..c6835a3d64fb016df4cc865df0469a37a7181411 100644 (file)
@@ -855,7 +855,6 @@ static void snd_ali_disable_spdif_out(struct snd_ali *codec)
 static void snd_ali_update_ptr(struct snd_ali *codec, int channel)
 {
        struct snd_ali_voice *pvoice;
-       struct snd_pcm_runtime *runtime;
        struct snd_ali_channel_control *pchregs;
        unsigned int old, mask;
 #ifdef ALI_DEBUG
@@ -872,7 +871,6 @@ static void snd_ali_update_ptr(struct snd_ali *codec, int channel)
                return;
 
        pvoice = &codec->synth.voices[channel];
-       runtime = pvoice->substream->runtime;
 
        udelay(100);
        spin_lock(&codec->reg_lock);
index dc632cdc38706ec74c18978739e0819257e1d3a8..5f2acd35dcb9f4ccf26d6263e8fcba1f6a984683 100644 (file)
@@ -1913,6 +1913,7 @@ static int snd_asihpi_tuner_band_put(struct snd_kcontrol *kcontrol,
        struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol);
        */
        u32 h_control = kcontrol->private_value;
+       unsigned int idx;
        u16 band;
        u16 tuner_bands[HPI_TUNER_BAND_LAST];
        u32 num_bands = 0;
@@ -1920,7 +1921,10 @@ static int snd_asihpi_tuner_band_put(struct snd_kcontrol *kcontrol,
        num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands,
                        HPI_TUNER_BAND_LAST);
 
-       band = tuner_bands[ucontrol->value.enumerated.item[0]];
+       idx = ucontrol->value.enumerated.item[0];
+       if (idx >= ARRAY_SIZE(tuner_bands))
+               idx = ARRAY_SIZE(tuner_bands) - 1;
+       band = tuner_bands[idx];
        hpi_handle_error(hpi_tuner_set_band(h_control, band));
 
        return 1;
@@ -2383,7 +2387,8 @@ static int snd_asihpi_clksrc_put(struct snd_kcontrol *kcontrol,
        struct snd_card_asihpi *asihpi =
                        (struct snd_card_asihpi *)(kcontrol->private_data);
        struct clk_cache *clkcache = &asihpi->cc;
-       int change, item;
+       unsigned int item;
+       int change;
        u32 h_control = kcontrol->private_value;
 
        change = 1;
index b46dc9b24dbd70d7c564c9a8853d8855f33a9ce7..9fb03b4ea925cb173e3e898950c9b4736c0ff787 100644 (file)
@@ -671,7 +671,7 @@ static int snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
                        return err;
                break;
 #endif
-       };
+       }
 
        if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_SPDIF) {
                for (i = 0; i < ARRAY_SIZE(snd_vortex_mixer_spdif); i++) {
index 8bef47311e45025aeeb6552054c50dd520a8ad9d..922a84bba2ef4738006e02dcc1c3920525ce8ec6 100644 (file)
@@ -219,7 +219,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
                */
                hwwrite(vortex->mmio, WT_RUN(wt), val);
                return 0xc;
-               break;
        case 1:         /* param 0 */
                /*
                printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
@@ -227,7 +226,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
                */
                hwwrite(vortex->mmio, WT_PARM(wt, 0), val);
                return 0xc;
-               break;
        case 2:         /* param 1 */
                /*
                printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
@@ -235,7 +233,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
                */
                hwwrite(vortex->mmio, WT_PARM(wt, 1), val);
                return 0xc;
-               break;
        case 3:         /* param 2 */
                /*
                printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
@@ -243,7 +240,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
                */
                hwwrite(vortex->mmio, WT_PARM(wt, 2), val);
                return 0xc;
-               break;
        case 4:         /* param 3 */
                /*
                printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
@@ -251,7 +247,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
                */
                hwwrite(vortex->mmio, WT_PARM(wt, 3), val);
                return 0xc;
-               break;
        case 6:         /* mute */
                /*
                printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
@@ -259,20 +254,17 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
                */
                hwwrite(vortex->mmio, WT_MUTE(wt), val);
                return 0xc;
-               break;
        case 0xb:
-               {               /* delay */
-                       /*
-                       printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
-                              WT_DELAY(wt,0), (int)val);
-                       */
-                       hwwrite(vortex->mmio, WT_DELAY(wt, 3), val);
-                       hwwrite(vortex->mmio, WT_DELAY(wt, 2), val);
-                       hwwrite(vortex->mmio, WT_DELAY(wt, 1), val);
-                       hwwrite(vortex->mmio, WT_DELAY(wt, 0), val);
-                       return 0xc;
-               }
-               break;
+                       /* delay */
+               /*
+               printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+                      WT_DELAY(wt,0), (int)val);
+               */
+               hwwrite(vortex->mmio, WT_DELAY(wt, 3), val);
+               hwwrite(vortex->mmio, WT_DELAY(wt, 2), val);
+               hwwrite(vortex->mmio, WT_DELAY(wt, 1), val);
+               hwwrite(vortex->mmio, WT_DELAY(wt, 0), val);
+               return 0xc;
                /* Global WT block parameters */
        case 5:         /* sramp */
                ecx = WT_SRAMP(wt);
@@ -291,7 +283,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
                break;
        default:
                return 0;
-               break;
        }
        /*
        printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", ecx, (int)val);
index c8e1216115936015a73e67df84e2c83ecc0d1975..1aef7128f7caa97e0daacaeb312ee60d1ec9225f 100644 (file)
@@ -715,14 +715,14 @@ snd_azf3328_mixer_ac97_read(struct snd_ac97 *ac97, unsigned short reg_ac97)
        const struct snd_azf3328 *chip = ac97->private_data;
        unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97);
        unsigned short reg_val = 0;
-       bool unsupported = 0;
+       bool unsupported = false;
 
        snd_azf3328_dbgmixer(
                "snd_azf3328_mixer_ac97_read reg_ac97 %u\n",
                        reg_ac97
        );
        if (reg_azf & AZF_AC97_REG_UNSUPPORTED)
-               unsupported = 1;
+               unsupported = true;
        else {
                if (reg_azf & AZF_AC97_REG_REAL_IO_READ)
                        reg_val = snd_azf3328_mixer_inw(chip,
@@ -759,7 +759,7 @@ snd_azf3328_mixer_ac97_read(struct snd_ac97 *ac97, unsigned short reg_ac97)
                                reg_val = azf_emulated_ac97_vendor_id & 0xffff;
                                break;
                        default:
-                               unsupported = 1;
+                               unsupported = true;
                                break;
                        }
                }
@@ -776,14 +776,14 @@ snd_azf3328_mixer_ac97_write(struct snd_ac97 *ac97,
 {
        const struct snd_azf3328 *chip = ac97->private_data;
        unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97);
-       bool unsupported = 0;
+       bool unsupported = false;
 
        snd_azf3328_dbgmixer(
                "snd_azf3328_mixer_ac97_write reg_ac97 %u val %u\n",
                        reg_ac97, val
        );
        if (reg_azf & AZF_AC97_REG_UNSUPPORTED)
-               unsupported = 1;
+               unsupported = true;
        else {
                if (reg_azf & AZF_AC97_REG_REAL_IO_WRITE)
                        snd_azf3328_mixer_outw(
@@ -808,7 +808,7 @@ snd_azf3328_mixer_ac97_write(struct snd_ac97 *ac97,
                                 */
                                break;
                        default:
-                               unsupported = 1;
+                               unsupported = true;
                                break;
                        }
                }
@@ -1559,7 +1559,7 @@ snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
        struct snd_azf3328_codec_data *codec = runtime->private_data;
        int result = 0;
        u16 flags1;
-       bool previously_muted = 0;
+       bool previously_muted = false;
        bool is_main_mixer_playback_codec = (AZF_CODEC_PLAYBACK == codec->type);
 
        snd_azf3328_dbgcalls("snd_azf3328_pcm_trigger cmd %d\n", cmd);
index da1cb9c4c76c2e24b46a477a93d6ff1d926d39a3..e6a44507d55700ef689d6b98438452a53b008a24 100644 (file)
@@ -161,13 +161,13 @@ int olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
        /* drop the original AD1888 HPF control */
        memset(&elem, 0, sizeof(elem));
        elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-       strncpy(elem.name, "High Pass Filter Enable", sizeof(elem.name));
+       strlcpy(elem.name, "High Pass Filter Enable", sizeof(elem.name));
        snd_ctl_remove_id(card, &elem);
 
        /* drop the original V_REFOUT control */
        memset(&elem, 0, sizeof(elem));
        elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-       strncpy(elem.name, "V_REFOUT Enable", sizeof(elem.name));
+       strlcpy(elem.name, "V_REFOUT Enable", sizeof(elem.name));
        snd_ctl_remove_id(card, &elem);
 
        /* add the OLPC-specific controls */
index 0c00eb4088efca6639bf35c554aefa73f9e926e5..84f86bf63b8fc31dc27700013608a65a7af970bb 100644 (file)
@@ -33,7 +33,7 @@ struct daio_rsc_idx {
        unsigned short right;
 };
 
-struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = {
+static struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = {
        [LINEO1] = {.left = 0x00, .right = 0x01},
        [LINEO2] = {.left = 0x18, .right = 0x19},
        [LINEO3] = {.left = 0x08, .right = 0x09},
@@ -44,7 +44,7 @@ struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = {
        [SPDIFI1] = {.left = 0x95, .right = 0x9d},
 };
 
-struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = {
+static struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = {
        [LINEO1] = {.left = 0x40, .right = 0x41},
        [LINEO2] = {.left = 0x60, .right = 0x61},
        [LINEO3] = {.left = 0x50, .right = 0x51},
index 110b8ace6d8a46f24cec3225682eb4952e8c6ef1..a689f255270641ab21f17474e8dbfe8ced37ae19 100644 (file)
@@ -69,7 +69,8 @@ unsigned int get_field(unsigned int data, unsigned int field)
 {
        int i;
 
-       BUG_ON(!field);
+       if (WARN_ON(!field))
+               return 0;
        /* @field should always be greater than 0 */
        for (i = 0; !(field & (1 << i)); )
                i++;
@@ -81,7 +82,8 @@ void set_field(unsigned int *data, unsigned int field, unsigned int value)
 {
        int i;
 
-       BUG_ON(!field);
+       if (WARN_ON(!field))
+               return;
        /* @field should always be greater than 0 */
        for (i = 0; !(field & (1 << i)); )
                i++;
index 0275209ca82e33bb4fd0c69569c31e08993a87d2..1f9c7c4bbcd8b0f3db5fb8552a24fba5dd9467f8 100644 (file)
@@ -1182,15 +1182,20 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
        u32 *gpr_map;
        mm_segment_t seg;
 
-       if ((icode = kzalloc(sizeof(*icode), GFP_KERNEL)) == NULL ||
-           (icode->gpr_map = (u_int32_t __user *)
-            kcalloc(512 + 256 + 256 + 2 * 1024, sizeof(u_int32_t),
-                    GFP_KERNEL)) == NULL ||
-           (controls = kcalloc(SND_EMU10K1_GPR_CONTROLS,
-                               sizeof(*controls), GFP_KERNEL)) == NULL) {
-               err = -ENOMEM;
-               goto __err;
-       }
+       err = -ENOMEM;
+       icode = kzalloc(sizeof(*icode), GFP_KERNEL);
+       if (!icode)
+               return err;
+
+       icode->gpr_map = (u_int32_t __user *) kcalloc(512 + 256 + 256 + 2 * 1024,
+                                                     sizeof(u_int32_t), GFP_KERNEL);
+       if (!icode->gpr_map)
+               goto __err_gpr;
+       controls = kcalloc(SND_EMU10K1_GPR_CONTROLS,
+                          sizeof(*controls), GFP_KERNEL);
+       if (!controls)
+               goto __err_ctrls;
+
        gpr_map = (u32 __force *)icode->gpr_map;
 
        icode->tram_data_map = icode->gpr_map + 512;
@@ -1741,12 +1746,12 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
        emu->support_tlv = 0; /* clear again */
        snd_leave_user(seg);
 
- __err:
+__err:
        kfree(controls);
-       if (icode != NULL) {
-               kfree((void __force *)icode->gpr_map);
-               kfree(icode);
-       }
+__err_ctrls:
+       kfree((void __force *)icode->gpr_map);
+__err_gpr:
+       kfree(icode);
        return err;
 }
 
@@ -1813,18 +1818,26 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
        u32 *gpr_map;
        mm_segment_t seg;
 
-       if ((icode = kzalloc(sizeof(*icode), GFP_KERNEL)) == NULL)
-               return -ENOMEM;
-       if ((icode->gpr_map = (u_int32_t __user *)
-            kcalloc(256 + 160 + 160 + 2 * 512, sizeof(u_int32_t),
-                    GFP_KERNEL)) == NULL ||
-            (controls = kcalloc(SND_EMU10K1_GPR_CONTROLS,
-                               sizeof(struct snd_emu10k1_fx8010_control_gpr),
-                               GFP_KERNEL)) == NULL ||
-           (ipcm = kzalloc(sizeof(*ipcm), GFP_KERNEL)) == NULL) {
-               err = -ENOMEM;
-               goto __err;
-       }
+       err = -ENOMEM;
+       icode = kzalloc(sizeof(*icode), GFP_KERNEL);
+       if (!icode)
+               return err;
+
+       icode->gpr_map = (u_int32_t __user *) kcalloc(256 + 160 + 160 + 2 * 512,
+                                                     sizeof(u_int32_t), GFP_KERNEL);
+       if (!icode->gpr_map)
+               goto __err_gpr;
+
+       controls = kcalloc(SND_EMU10K1_GPR_CONTROLS,
+                          sizeof(struct snd_emu10k1_fx8010_control_gpr),
+                          GFP_KERNEL);
+       if (!controls)
+               goto __err_ctrls;
+
+       ipcm = kzalloc(sizeof(*ipcm), GFP_KERNEL);
+       if (!ipcm)
+               goto __err_ipcm;
+
        gpr_map = (u32 __force *)icode->gpr_map;
 
        icode->tram_data_map = icode->gpr_map + 256;
@@ -2363,13 +2376,14 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
        snd_leave_user(seg);
        if (err >= 0)
                err = snd_emu10k1_ipcm_poke(emu, ipcm);
-      __err:
+__err:
        kfree(ipcm);
+__err_ipcm:
        kfree(controls);
-       if (icode != NULL) {
-               kfree((void __force *)icode->gpr_map);
-               kfree(icode);
-       }
+__err_ctrls:
+       kfree((void __force *)icode->gpr_map);
+__err_gpr:
+       kfree(icode);
        return err;
 }
 
index 48a9d004d6d938ac5365b52a83c6b67b7b18dcaa..853c6a69e29e81c04c9fda91ce3500cf6a60fcbc 100644 (file)
@@ -638,7 +638,7 @@ static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid,
                        /* don't add channel suffix for Headphone controls */
                        int idx = get_hp_label_index(codec, nid, cfg->hp_pins,
                                                     cfg->hp_outs);
-                       if (idx >= 0)
+                       if (idx >= 0 && indexp)
                                *indexp = idx;
                        sfx = "";
                }
index 63c99090a4ecc94ff617d1de8252ccf3ccccd609..98bce9830be0d345a7dec92f0916bd927bb48ab8 100644 (file)
@@ -110,6 +110,7 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
        case SND_BELL:
                if (hz)
                        hz = 1000;
+               /* fallthru */
        case SND_TONE:
                if (beep->linear_tone)
                        beep->tone = beep_linear_tone(beep, hz);
@@ -151,10 +152,8 @@ static int snd_hda_do_attach(struct hda_beep *beep)
        int err;
 
        input_dev = input_allocate_device();
-       if (!input_dev) {
-               printk(KERN_INFO "hda_beep: unable to allocate input device\n");
+       if (!input_dev)
                return -ENOMEM;
-       }
 
        /* setup digital beep device */
        input_dev->name = "HDA Digital PCBeep";
index 748c6a941963cbcb6108bc8aac90843cca7ee72f..dd5403d40830bfa2d97e6012b767fff52d64fddf 100644 (file)
@@ -565,7 +565,7 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
                range_val = !!(parm & (1 << (shift-1))); /* ranges */
                val = parm & mask;
                if (val == 0 && null_count++) {  /* no second chance */
-                       snd_printk(KERN_WARNING "hda_codec: "
+                       snd_printdd("hda_codec: "
                                   "invalid CONNECT_LIST verb %x[%i]:%x\n",
                                    nid, i, parm);
                        return 0;
@@ -2634,8 +2634,7 @@ static int map_slaves(struct hda_codec *codec, const char * const *slaves,
        items = codec->mixers.list;
        for (i = 0; i < codec->mixers.used; i++) {
                struct snd_kcontrol *sctl = items[i].kctl;
-               if (!sctl || !sctl->id.name ||
-                   sctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER)
+               if (!sctl || sctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER)
                        continue;
                for (s = slaves; *s; s++) {
                        char tmpname[sizeof(sctl->id.name)];
@@ -2662,7 +2661,7 @@ static int check_slave_present(void *data, struct snd_kcontrol *sctl)
 }
 
 /* guess the value corresponding to 0dB */
-static int get_kctl_0dB_offset(struct snd_kcontrol *kctl)
+static int get_kctl_0dB_offset(struct snd_kcontrol *kctl, int *step_to_check)
 {
        int _tlv[4];
        const int *tlv = NULL;
@@ -2677,8 +2676,19 @@ static int get_kctl_0dB_offset(struct snd_kcontrol *kctl)
                set_fs(fs);
        } else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ)
                tlv = kctl->tlv.p;
-       if (tlv && tlv[0] == SNDRV_CTL_TLVT_DB_SCALE)
-               val = -tlv[2] / tlv[3];
+       if (tlv && tlv[0] == SNDRV_CTL_TLVT_DB_SCALE) {
+               int step = tlv[3];
+               step &= ~TLV_DB_SCALE_MUTE;
+               if (!step)
+                       return -1;
+               if (*step_to_check && *step_to_check != step) {
+                       snd_printk(KERN_ERR "hda_codec: Mismatching dB step for vmaster slave (%d!=%d)\n",
+                                  *step_to_check, step);
+                       return -1;
+               }
+               *step_to_check = step;
+               val = -tlv[2] / step;
+       }
        return val;
 }
 
@@ -2699,7 +2709,7 @@ static int put_kctl_with_value(struct snd_kcontrol *kctl, int val)
 /* initialize the slave volume with 0dB */
 static int init_slave_0dB(void *data, struct snd_kcontrol *slave)
 {
-       int offset = get_kctl_0dB_offset(slave);
+       int offset = get_kctl_0dB_offset(slave, data);
        if (offset > 0)
                put_kctl_with_value(slave, offset);
        return 0;
@@ -2760,9 +2770,11 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
 
        /* init with master mute & zero volume */
        put_kctl_with_value(kctl, 0);
-       if (init_slave_vol)
+       if (init_slave_vol) {
+               int step = 0;
                map_slaves(codec, slaves, suffix,
-                          tlv ? init_slave_0dB : init_slave_unmute, kctl);
+                          tlv ? init_slave_0dB : init_slave_unmute, &step);
+       }
 
        if (ctl_ret)
                *ctl_ret = kctl;
@@ -5395,11 +5407,6 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
                        snd_hda_codec_setup_stream(codec,
                                                   mout->hp_out_nid[i],
                                                   stream_tag, 0, format);
-       for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
-               if (!mout->no_share_stream && mout->extra_out_nid[i])
-                       snd_hda_codec_setup_stream(codec,
-                                                  mout->extra_out_nid[i],
-                                                  stream_tag, 0, format);
 
        /* surrounds */
        for (i = 1; i < mout->num_dacs; i++) {
@@ -5410,6 +5417,20 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
                        snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
                                                   0, format);
        }
+
+       /* extra surrounds */
+       for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) {
+               int ch = 0;
+               if (!mout->extra_out_nid[i])
+                       break;
+               if (chs >= (i + 1) * 2)
+                       ch = i * 2;
+               else if (!mout->no_share_stream)
+                       break;
+               snd_hda_codec_setup_stream(codec, mout->extra_out_nid[i],
+                                          stream_tag, ch, format);
+       }
+
        return 0;
 }
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare);
index 7aa9870040c102df5b02aa0a94778c8e14c36024..77db69480c195cde15ee64dd13bb18b2f81aa2de 100644 (file)
@@ -698,6 +698,7 @@ struct hda_bus {
        unsigned int in_reset:1;        /* during reset operation */
        unsigned int power_keep_link_on:1; /* don't power off HDA link */
        unsigned int no_response_fallback:1; /* don't fallback at RIRB error */
+       unsigned int avoid_link_reset:1; /* don't reset link at runtime PM */
 
        int primary_dig_out_type;       /* primary digital out PCM type */
 };
index d0d7ac1e99d24cc606b441e6ec23470a75800dcd..32d3e3855a6eb99f298a97c3168e604a74ceae77 100644 (file)
@@ -2,6 +2,7 @@
  * Generic routines and proc interface for ELD(EDID Like Data) information
  *
  * Copyright(c) 2008 Intel Corporation.
+ * Copyright (c) 2013 Anssi Hannula <anssi.hannula@iki.fi>
  *
  * Authors:
  *             Wu Fengguang <wfg@linux.intel.com>
@@ -478,10 +479,9 @@ static void hdmi_print_sad_info(int i, struct cea_sad *a,
                snd_iprintf(buffer, "sad%d_profile\t\t%d\n", i, a->profile);
 }
 
-static void hdmi_print_eld_info(struct snd_info_entry *entry,
-                               struct snd_info_buffer *buffer)
+void snd_hdmi_print_eld_info(struct hdmi_eld *eld,
+                            struct snd_info_buffer *buffer)
 {
-       struct hdmi_eld *eld = entry->private_data;
        struct parsed_hdmi_eld *e = &eld->info;
        char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
        int i;
@@ -500,13 +500,10 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry,
                [4 ... 7] = "reserved"
        };
 
-       mutex_lock(&eld->lock);
        snd_iprintf(buffer, "monitor_present\t\t%d\n", eld->monitor_present);
        snd_iprintf(buffer, "eld_valid\t\t%d\n", eld->eld_valid);
-       if (!eld->eld_valid) {
-               mutex_unlock(&eld->lock);
+       if (!eld->eld_valid)
                return;
-       }
        snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name);
        snd_iprintf(buffer, "connection_type\t\t%s\n",
                                eld_connection_type_names[e->conn_type]);
@@ -528,13 +525,11 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry,
 
        for (i = 0; i < e->sad_count; i++)
                hdmi_print_sad_info(i, e->sad + i, buffer);
-       mutex_unlock(&eld->lock);
 }
 
-static void hdmi_write_eld_info(struct snd_info_entry *entry,
-                               struct snd_info_buffer *buffer)
+void snd_hdmi_write_eld_info(struct hdmi_eld *eld,
+                            struct snd_info_buffer *buffer)
 {
-       struct hdmi_eld *eld = entry->private_data;
        struct parsed_hdmi_eld *e = &eld->info;
        char line[64];
        char name[64];
@@ -542,7 +537,6 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry,
        long long val;
        unsigned int n;
 
-       mutex_lock(&eld->lock);
        while (!snd_info_get_line(buffer, line, sizeof(line))) {
                if (sscanf(line, "%s %llx", name, &val) != 2)
                        continue;
@@ -594,38 +588,7 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry,
                                e->sad_count = n + 1;
                }
        }
-       mutex_unlock(&eld->lock);
-}
-
-
-int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
-                        int index)
-{
-       char name[32];
-       struct snd_info_entry *entry;
-       int err;
-
-       snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index);
-       err = snd_card_proc_new(codec->bus->card, name, &entry);
-       if (err < 0)
-               return err;
-
-       snd_info_set_text_ops(entry, eld, hdmi_print_eld_info);
-       entry->c.text.write = hdmi_write_eld_info;
-       entry->mode |= S_IWUSR;
-       eld->proc_entry = entry;
-
-       return 0;
-}
-
-void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
-{
-       if (!codec->bus->shutdown && eld->proc_entry) {
-               snd_device_free(codec->bus->card, eld->proc_entry);
-               eld->proc_entry = NULL;
-       }
 }
-
 #endif /* CONFIG_PROC_FS */
 
 /* update PCM info based on ELD */
@@ -671,3 +634,153 @@ void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e,
        hinfo->maxbps = min(hinfo->maxbps, maxbps);
        hinfo->channels_max = min(hinfo->channels_max, channels_max);
 }
+
+
+/* ATI/AMD specific stuff (ELD emulation) */
+
+#define ATI_VERB_SET_AUDIO_DESCRIPTOR  0x776
+#define ATI_VERB_SET_SINK_INFO_INDEX   0x780
+#define ATI_VERB_GET_SPEAKER_ALLOCATION        0xf70
+#define ATI_VERB_GET_AUDIO_DESCRIPTOR  0xf76
+#define ATI_VERB_GET_AUDIO_VIDEO_DELAY 0xf7b
+#define ATI_VERB_GET_SINK_INFO_INDEX   0xf80
+#define ATI_VERB_GET_SINK_INFO_DATA    0xf81
+
+#define ATI_SPKALLOC_SPKALLOC          0x007f
+#define ATI_SPKALLOC_TYPE_HDMI         0x0100
+#define ATI_SPKALLOC_TYPE_DISPLAYPORT  0x0200
+
+/* first three bytes are just standard SAD */
+#define ATI_AUDIODESC_CHANNELS         0x00000007
+#define ATI_AUDIODESC_RATES            0x0000ff00
+#define ATI_AUDIODESC_LPCM_STEREO_RATES        0xff000000
+
+/* in standard HDMI VSDB format */
+#define ATI_DELAY_VIDEO_LATENCY                0x000000ff
+#define ATI_DELAY_AUDIO_LATENCY                0x0000ff00
+
+enum ati_sink_info_idx {
+       ATI_INFO_IDX_MANUFACTURER_ID    = 0,
+       ATI_INFO_IDX_PRODUCT_ID         = 1,
+       ATI_INFO_IDX_SINK_DESC_LEN      = 2,
+       ATI_INFO_IDX_PORT_ID_LOW        = 3,
+       ATI_INFO_IDX_PORT_ID_HIGH       = 4,
+       ATI_INFO_IDX_SINK_DESC_FIRST    = 5,
+       ATI_INFO_IDX_SINK_DESC_LAST     = 22, /* max len 18 bytes */
+};
+
+int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid,
+                        unsigned char *buf, int *eld_size, bool rev3_or_later)
+{
+       int spkalloc, ati_sad, aud_synch;
+       int sink_desc_len = 0;
+       int pos, i;
+
+       /* ATI/AMD does not have ELD, emulate it */
+
+       spkalloc = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SPEAKER_ALLOCATION, 0);
+
+       if (!spkalloc) {
+               snd_printd(KERN_INFO "HDMI ATI/AMD: no speaker allocation for ELD\n");
+               return -EINVAL;
+       }
+
+       memset(buf, 0, ELD_FIXED_BYTES + ELD_MAX_MNL + ELD_MAX_SAD * 3);
+
+       /* version */
+       buf[0] = ELD_VER_CEA_861D << 3;
+
+       /* speaker allocation from EDID */
+       buf[7] = spkalloc & ATI_SPKALLOC_SPKALLOC;
+
+       /* is DisplayPort? */
+       if (spkalloc & ATI_SPKALLOC_TYPE_DISPLAYPORT)
+               buf[5] |= 0x04;
+
+       pos = ELD_FIXED_BYTES;
+
+       if (rev3_or_later) {
+               int sink_info;
+
+               snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_LOW);
+               sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
+               put_unaligned_le32(sink_info, buf + 8);
+
+               snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_HIGH);
+               sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
+               put_unaligned_le32(sink_info, buf + 12);
+
+               snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_MANUFACTURER_ID);
+               sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
+               put_unaligned_le16(sink_info, buf + 16);
+
+               snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PRODUCT_ID);
+               sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
+               put_unaligned_le16(sink_info, buf + 18);
+
+               snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_LEN);
+               sink_desc_len = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
+
+               if (sink_desc_len > ELD_MAX_MNL) {
+                       snd_printd(KERN_INFO "HDMI ATI/AMD: Truncating HDMI sink description with length %d\n",
+                                  sink_desc_len);
+                       sink_desc_len = ELD_MAX_MNL;
+               }
+
+               buf[4] |= sink_desc_len;
+
+               for (i = 0; i < sink_desc_len; i++) {
+                       snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_FIRST + i);
+                       buf[pos++] = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
+               }
+       }
+
+       for (i = AUDIO_CODING_TYPE_LPCM; i <= AUDIO_CODING_TYPE_WMAPRO; i++) {
+               if (i == AUDIO_CODING_TYPE_SACD || i == AUDIO_CODING_TYPE_DST)
+                       continue; /* not handled by ATI/AMD */
+
+               snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_AUDIO_DESCRIPTOR, i << 3);
+               ati_sad = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_DESCRIPTOR, 0);
+
+               if (ati_sad & ATI_AUDIODESC_RATES) {
+                       /* format is supported, copy SAD as-is */
+                       buf[pos++] = (ati_sad & 0x0000ff) >> 0;
+                       buf[pos++] = (ati_sad & 0x00ff00) >> 8;
+                       buf[pos++] = (ati_sad & 0xff0000) >> 16;
+               }
+
+               if (i == AUDIO_CODING_TYPE_LPCM
+                   && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES)
+                   && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) >> 16 != (ati_sad & ATI_AUDIODESC_RATES)) {
+                       /* for PCM there is a separate stereo rate mask */
+                       buf[pos++] = ((ati_sad & 0x000000ff) & ~ATI_AUDIODESC_CHANNELS) | 0x1;
+                       /* rates from the extra byte */
+                       buf[pos++] = (ati_sad & 0xff000000) >> 24;
+                       buf[pos++] = (ati_sad & 0x00ff0000) >> 16;
+               }
+       }
+
+       if (pos == ELD_FIXED_BYTES + sink_desc_len) {
+               snd_printd(KERN_INFO "HDMI ATI/AMD: no audio descriptors for ELD\n");
+               return -EINVAL;
+       }
+
+       aud_synch = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_VIDEO_DELAY, 0);
+       if ((aud_synch & ATI_DELAY_VIDEO_LATENCY) && (aud_synch & ATI_DELAY_AUDIO_LATENCY)) {
+               int video_latency = (aud_synch & ATI_DELAY_VIDEO_LATENCY) - 1;
+               int audio_latency = ((aud_synch & ATI_DELAY_AUDIO_LATENCY) >> 8) - 1;
+
+               if (video_latency > audio_latency)
+                       buf[6] = min(video_latency - audio_latency, 0xfa);
+       }
+
+       /* Baseline length */
+       buf[2] = pos - 4;
+
+       /* SAD count */
+       buf[5] |= ((pos - ELD_FIXED_BYTES - sink_desc_len) / 3) << 4;
+
+       *eld_size = pos;
+
+       return 0;
+}
index b7c89dff7066758e950feec6842dab58f2de29ed..276f6e759bacee18dbbe1d5c969575906e34dacf 100644 (file)
@@ -549,11 +549,15 @@ static hda_nid_t look_for_out_mute_nid(struct hda_codec *codec,
 static hda_nid_t look_for_out_vol_nid(struct hda_codec *codec,
                                      struct nid_path *path)
 {
+       struct hda_gen_spec *spec = codec->spec;
        int i;
 
        for (i = path->depth - 1; i >= 0; i--) {
-               if (nid_has_volume(codec, path->path[i], HDA_OUTPUT))
-                       return path->path[i];
+               hda_nid_t nid = path->path[i];
+               if ((spec->out_vol_mask >> nid) & 1)
+                       continue;
+               if (nid_has_volume(codec, nid, HDA_OUTPUT))
+                       return nid;
        }
        return 0;
 }
index 48d44026705b0f05ac01c51fbdb199215195979f..7e45cb44d1514497385f2f6bc2b63251391cdaba 100644 (file)
@@ -242,6 +242,9 @@ struct hda_gen_spec {
        /* additional mute flags (only effective with auto_mute_via_amp=1) */
        u64 mute_bits;
 
+       /* bitmask for skipping volume controls */
+       u64 out_vol_mask;
+
        /* badness tables for output path evaluations */
        const struct badness_table *main_out_badness;
        const struct badness_table *extra_out_badness;
index 6e61a019aa5e4040e9f29bc58fdc35e799207f24..7a09404579a73ac729ad437c358f38ac3b2b807b 100644 (file)
@@ -169,6 +169,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
                         "{Intel, PPT},"
                         "{Intel, LPT},"
                         "{Intel, LPT_LP},"
+                        "{Intel, WPT_LP},"
                         "{Intel, HPT},"
                         "{Intel, PBG},"
                         "{Intel, SCH},"
@@ -568,6 +569,7 @@ enum {
        AZX_DRIVER_ICH,
        AZX_DRIVER_PCH,
        AZX_DRIVER_SCH,
+       AZX_DRIVER_HDMI,
        AZX_DRIVER_ATI,
        AZX_DRIVER_ATIHDMI,
        AZX_DRIVER_ATIHDMI_NS,
@@ -612,6 +614,11 @@ enum {
 #define AZX_DCAPS_INTEL_PCH \
        (AZX_DCAPS_INTEL_PCH_NOPM | AZX_DCAPS_PM_RUNTIME)
 
+#define AZX_DCAPS_INTEL_HASWELL \
+       (AZX_DCAPS_SCH_SNOOP | AZX_DCAPS_ALIGN_BUFSIZE | \
+        AZX_DCAPS_COUNT_LPIB_DELAY | AZX_DCAPS_PM_RUNTIME | \
+        AZX_DCAPS_I915_POWERWELL)
+
 /* quirks for ATI SB / AMD Hudson */
 #define AZX_DCAPS_PRESET_ATI_SB \
        (AZX_DCAPS_ATI_SNOOP | AZX_DCAPS_NO_TCSEL | \
@@ -642,6 +649,7 @@ static char *driver_short_names[] = {
        [AZX_DRIVER_ICH] = "HDA Intel",
        [AZX_DRIVER_PCH] = "HDA Intel PCH",
        [AZX_DRIVER_SCH] = "HDA Intel MID",
+       [AZX_DRIVER_HDMI] = "HDA Intel HDMI",
        [AZX_DRIVER_ATI] = "HDA ATI SB",
        [AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI",
        [AZX_DRIVER_ATIHDMI_NS] = "HDA ATI HDMI",
@@ -906,12 +914,12 @@ static void azx_update_rirb(struct azx *chip)
                        chip->rirb.res[addr] = res;
                        smp_wmb();
                        chip->rirb.cmds[addr]--;
-               } else
-                       snd_printk(KERN_ERR SFX "%s: spurious response %#x:%#x, "
-                                  "last cmd=%#08x\n",
+               } else if (printk_ratelimit()) {
+                       snd_printk(KERN_ERR SFX "%s: spurious response %#x:%#x, last cmd=%#08x\n",
                                   pci_name(chip->pci),
                                   res, res_ex,
                                   chip->last_cmd[addr]);
+               }
        }
 }
 
@@ -2986,7 +2994,8 @@ static int azx_runtime_suspend(struct device *dev)
                  STATESTS_INT_MASK);
 
        azx_stop_chip(chip);
-       azx_enter_link_reset(chip);
+       if (!chip->bus->avoid_link_reset)
+               azx_enter_link_reset(chip);
        azx_clear_irq_pending(chip);
        if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
                hda_display_power(false);
@@ -3985,16 +3994,16 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
        /* Lynx Point-LP */
        { PCI_DEVICE(0x8086, 0x9c21),
          .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+       /* Wildcat Point-LP */
+       { PCI_DEVICE(0x8086, 0x9ca0),
+         .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
        /* Haswell */
        { PCI_DEVICE(0x8086, 0x0a0c),
-         .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH |
-         AZX_DCAPS_I915_POWERWELL },
+         .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL },
        { PCI_DEVICE(0x8086, 0x0c0c),
-         .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH |
-         AZX_DCAPS_I915_POWERWELL },
+         .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL },
        { PCI_DEVICE(0x8086, 0x0d0c),
-         .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH |
-         AZX_DCAPS_I915_POWERWELL },
+         .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL },
        /* 5 Series/3400 */
        { PCI_DEVICE(0x8086, 0x3b56),
          .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM },
@@ -4074,6 +4083,22 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
          .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
        { PCI_DEVICE(0x1002, 0xaa48),
          .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+       { PCI_DEVICE(0x1002, 0xaa50),
+         .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+       { PCI_DEVICE(0x1002, 0xaa58),
+         .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+       { PCI_DEVICE(0x1002, 0xaa60),
+         .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+       { PCI_DEVICE(0x1002, 0xaa68),
+         .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+       { PCI_DEVICE(0x1002, 0xaa80),
+         .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+       { PCI_DEVICE(0x1002, 0xaa88),
+         .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+       { PCI_DEVICE(0x1002, 0xaa90),
+         .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+       { PCI_DEVICE(0x1002, 0xaa98),
+         .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
        { PCI_DEVICE(0x1002, 0x9902),
          .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI },
        { PCI_DEVICE(0x1002, 0xaaa0),
index 05b3e3e9108fda50f16f2472b2dad5bfbc870067..afe594411a565d855297fe5cecd4561b10022967 100644 (file)
@@ -286,7 +286,7 @@ void snd_hda_jack_report_sync(struct hda_codec *codec)
        jack = codec->jacktbl.list;
        for (i = 0; i < codec->jacktbl.used; i++, jack++)
                if (jack->nid) {
-                       if (!jack->kctl)
+                       if (!jack->kctl || jack->block_report)
                                continue;
                        state = get_jack_plug_state(jack->pin_sense);
                        snd_kctl_jack_report(codec->bus->card, jack->kctl, state);
index 379420c44eefac051e512b83c67790f9f3ce99a2..46e1ea83ce3c208da42afb98e8668ae3b48e8591 100644 (file)
@@ -28,6 +28,7 @@ struct hda_jack_tbl {
        unsigned int jack_detect:1;     /* capable of jack-detection? */
        unsigned int jack_dirty:1;      /* needs to update? */
        unsigned int phantom_jack:1;    /* a fixed, always present port? */
+       unsigned int block_report:1;    /* in a transitional state - do not report to userspace */
        hda_nid_t gating_jack;          /* valid when gating jack plugged */
        hda_nid_t gated_jack;           /* gated is dependent on this jack */
        struct snd_kcontrol *kctl;      /* assigned kctl for jack-detection */
index 2e7493ef8ee0643b1ee51b759198c233d23af6a5..d398b648bb5d1478d63e8d430d333eedda45beb3 100644 (file)
@@ -428,6 +428,7 @@ enum {
        HDA_FIXUP_ACT_PROBE,
        HDA_FIXUP_ACT_INIT,
        HDA_FIXUP_ACT_BUILD,
+       HDA_FIXUP_ACT_FREE,
 };
 
 int snd_hda_add_verbs(struct hda_codec *codec, const struct hda_verb *list);
@@ -751,10 +752,6 @@ struct hdmi_eld {
        int     eld_size;
        char    eld_buffer[ELD_MAX_SIZE];
        struct parsed_hdmi_eld info;
-       struct mutex lock;
-#ifdef CONFIG_PROC_FS
-       struct snd_info_entry *proc_entry;
-#endif
 };
 
 int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid);
@@ -766,21 +763,15 @@ void snd_hdmi_show_eld(struct parsed_hdmi_eld *e);
 void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e,
                              struct hda_pcm_stream *hinfo);
 
+int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid,
+                        unsigned char *buf, int *eld_size,
+                        bool rev3_or_later);
+
 #ifdef CONFIG_PROC_FS
-int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
-                        int index);
-void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld);
-#else
-static inline int snd_hda_eld_proc_new(struct hda_codec *codec,
-                                      struct hdmi_eld *eld,
-                                      int index)
-{
-       return 0;
-}
-static inline void snd_hda_eld_proc_free(struct hda_codec *codec,
-                                        struct hdmi_eld *eld)
-{
-}
+void snd_hdmi_print_eld_info(struct hdmi_eld *eld,
+                            struct snd_info_buffer *buffer);
+void snd_hdmi_write_eld_info(struct hdmi_eld *eld,
+                            struct snd_info_buffer *buffer);
 #endif
 
 #define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80
index 2aa2f579b4d66857fcf401000ac24ebde3ff6281..87d2e0335ae4c3288fb239562a68fec79bbaa5b9 100644 (file)
@@ -973,8 +973,11 @@ static void ad1884_fixup_thinkpad(struct hda_codec *codec,
 {
        struct ad198x_spec *spec = codec->spec;
 
-       if (action == HDA_FIXUP_ACT_PRE_PROBE)
+       if (action == HDA_FIXUP_ACT_PRE_PROBE) {
                spec->gen.keep_eapd_on = 1;
+               spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
+               spec->eapd_nid = 0x12;
+       }
 }
 
 /* set magic COEFs for dmic */
index 6e9876f27d959a64e14443ceab5efaed9f11d44b..54d14793725a7dd93623083d29f72f41a5ddc4bd 100644 (file)
@@ -759,7 +759,7 @@ struct ca0132_spec {
 /*
  * CA0132 codec access
  */
-unsigned int codec_send_command(struct hda_codec *codec, hda_nid_t nid,
+static unsigned int codec_send_command(struct hda_codec *codec, hda_nid_t nid,
                unsigned int verb, unsigned int parm, unsigned int *res)
 {
        unsigned int response;
index 18d9725015855c0ce3f33d174a3dfcd55836efeb..072755c8289c0ee440c5366bf04d5e074fcd2538 100644 (file)
@@ -597,6 +597,7 @@ static int patch_cs420x(struct hda_codec *codec)
  * Its layout is no longer compatible with CS4206/CS4207
  */
 enum {
+       CS4208_MAC_AUTO,
        CS4208_MBA6,
        CS4208_GPIO0,
 };
@@ -608,7 +609,12 @@ static const struct hda_model_fixup cs4208_models[] = {
 };
 
 static const struct snd_pci_quirk cs4208_fixup_tbl[] = {
-       /* codec SSID */
+       SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS4208_MAC_AUTO),
+       {} /* terminator */
+};
+
+/* codec SSID matching */
+static const struct snd_pci_quirk cs4208_mac_fixup_tbl[] = {
        SND_PCI_QUIRK(0x106b, 0x7100, "MacBookAir 6,1", CS4208_MBA6),
        SND_PCI_QUIRK(0x106b, 0x7200, "MacBookAir 6,2", CS4208_MBA6),
        {} /* terminator */
@@ -626,6 +632,20 @@ static void cs4208_fixup_gpio0(struct hda_codec *codec,
        }
 }
 
+static const struct hda_fixup cs4208_fixups[];
+
+/* remap the fixup from codec SSID and apply it */
+static void cs4208_fixup_mac(struct hda_codec *codec,
+                            const struct hda_fixup *fix, int action)
+{
+       if (action != HDA_FIXUP_ACT_PRE_PROBE)
+               return;
+       snd_hda_pick_fixup(codec, NULL, cs4208_mac_fixup_tbl, cs4208_fixups);
+       if (codec->fixup_id < 0 || codec->fixup_id == CS4208_MAC_AUTO)
+               codec->fixup_id = CS4208_GPIO0; /* default fixup */
+       snd_hda_apply_fixup(codec, action);
+}
+
 static const struct hda_fixup cs4208_fixups[] = {
        [CS4208_MBA6] = {
                .type = HDA_FIXUP_PINS,
@@ -637,6 +657,10 @@ static const struct hda_fixup cs4208_fixups[] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = cs4208_fixup_gpio0,
        },
+       [CS4208_MAC_AUTO] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = cs4208_fixup_mac,
+       },
 };
 
 /* correct the 0dB offset of input pins */
@@ -660,6 +684,8 @@ static int patch_cs4208(struct hda_codec *codec)
                return -ENOMEM;
 
        spec->gen.automute_hook = cs_automute;
+       /* exclude NID 0x10 (HP) from output volumes due to different steps */
+       spec->gen.out_vol_mask = 1ULL << 0x10;
 
        snd_hda_pick_fixup(codec, cs4208_models, cs4208_fixup_tbl,
                           cs4208_fixups);
index ec68eaea0336a008c78fb05c03a4d6c0e3f99c2a..c205bb1747fdf6a7d367d6ef54d718331d671c0b 100644 (file)
@@ -3208,11 +3208,17 @@ static int cx_auto_init(struct hda_codec *codec)
        return 0;
 }
 
+static void cx_auto_free(struct hda_codec *codec)
+{
+       snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_FREE);
+       snd_hda_gen_free(codec);
+}
+
 static const struct hda_codec_ops cx_auto_patch_ops = {
        .build_controls = cx_auto_build_controls,
        .build_pcms = snd_hda_gen_build_pcms,
        .init = cx_auto_init,
-       .free = snd_hda_gen_free,
+       .free = cx_auto_free,
        .unsol_event = snd_hda_jack_unsol_event,
 #ifdef CONFIG_PM
        .check_power_status = snd_hda_gen_check_power_status,
@@ -3232,8 +3238,84 @@ enum {
        CXT_FIXUP_HEADPHONE_MIC_PIN,
        CXT_FIXUP_HEADPHONE_MIC,
        CXT_FIXUP_GPIO1,
+       CXT_FIXUP_THINKPAD_ACPI,
 };
 
+#if IS_ENABLED(CONFIG_THINKPAD_ACPI)
+
+#include <linux/thinkpad_acpi.h>
+
+static int (*led_set_func)(int, bool);
+
+static void update_tpacpi_mute_led(void *private_data, int enabled)
+{
+       struct hda_codec *codec = private_data;
+       struct conexant_spec *spec = codec->spec;
+
+       if (spec->dynamic_eapd)
+               cx_auto_vmaster_hook(private_data, enabled);
+
+       if (led_set_func)
+               led_set_func(TPACPI_LED_MUTE, !enabled);
+}
+
+static void update_tpacpi_micmute_led(struct hda_codec *codec,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       if (!ucontrol || !led_set_func)
+               return;
+       if (strcmp("Capture Switch", ucontrol->id.name) == 0 && ucontrol->id.index == 0) {
+               /* TODO: How do I verify if it's a mono or stereo here? */
+               bool val = ucontrol->value.integer.value[0] || ucontrol->value.integer.value[1];
+               led_set_func(TPACPI_LED_MICMUTE, !val);
+       }
+}
+
+static void cxt_fixup_thinkpad_acpi(struct hda_codec *codec,
+                                 const struct hda_fixup *fix, int action)
+{
+       struct conexant_spec *spec = codec->spec;
+
+       bool removefunc = false;
+
+       if (action == HDA_FIXUP_ACT_PROBE) {
+               if (!led_set_func)
+                       led_set_func = symbol_request(tpacpi_led_set);
+               if (!led_set_func) {
+                       snd_printk(KERN_WARNING "Failed to find thinkpad-acpi symbol tpacpi_led_set\n");
+                       return;
+               }
+
+               removefunc = true;
+               if (led_set_func(TPACPI_LED_MUTE, false) >= 0) {
+                       spec->gen.vmaster_mute.hook = update_tpacpi_mute_led;
+                       removefunc = false;
+               }
+               if (led_set_func(TPACPI_LED_MICMUTE, false) >= 0) {
+                       if (spec->gen.num_adc_nids > 1)
+                               snd_printdd("Skipping micmute LED control due to several ADCs");
+                       else {
+                               spec->gen.cap_sync_hook = update_tpacpi_micmute_led;
+                               removefunc = false;
+                       }
+               }
+       }
+
+       if (led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) {
+               symbol_put(tpacpi_led_set);
+               led_set_func = NULL;
+       }
+}
+
+#else
+
+static void cxt_fixup_thinkpad_acpi(struct hda_codec *codec,
+                                 const struct hda_fixup *fix, int action)
+{
+}
+
+#endif
+
 static void cxt_fixup_stereo_dmic(struct hda_codec *codec,
                                  const struct hda_fixup *fix, int action)
 {
@@ -3344,6 +3426,8 @@ static const struct hda_fixup cxt_fixups[] = {
        [CXT_PINCFG_LENOVO_TP410] = {
                .type = HDA_FIXUP_PINS,
                .v.pins = cxt_pincfg_lenovo_tp410,
+               .chained = true,
+               .chain_id = CXT_FIXUP_THINKPAD_ACPI,
        },
        [CXT_PINCFG_LEMOTE_A1004] = {
                .type = HDA_FIXUP_PINS,
@@ -3385,6 +3469,10 @@ static const struct hda_fixup cxt_fixups[] = {
                        { }
                },
        },
+       [CXT_FIXUP_THINKPAD_ACPI] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = cxt_fixup_thinkpad_acpi,
+       },
 };
 
 static const struct snd_pci_quirk cxt5051_fixups[] = {
@@ -3507,7 +3595,7 @@ static int patch_conexant_auto(struct hda_codec *codec)
        return 0;
 
  error:
-       snd_hda_gen_free(codec);
+       cx_auto_free(codec);
        return err;
 }
 
@@ -3568,6 +3656,8 @@ static const struct hda_codec_preset snd_hda_preset_conexant[] = {
          .patch = patch_conexant_auto },
        { .id = 0x14f15115, .name = "CX20757",
          .patch = patch_conexant_auto },
+       { .id = 0x14f151d7, .name = "CX20952",
+         .patch = patch_conexant_auto },
        {} /* terminator */
 };
 
@@ -3594,6 +3684,7 @@ MODULE_ALIAS("snd-hda-codec-id:14f15111");
 MODULE_ALIAS("snd-hda-codec-id:14f15113");
 MODULE_ALIAS("snd-hda-codec-id:14f15114");
 MODULE_ALIAS("snd-hda-codec-id:14f15115");
+MODULE_ALIAS("snd-hda-codec-id:14f151d7");
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Conexant HD-audio codec");
index 50173d412ac5c0d5fb7095b3e3004e2c63c319a0..e68792311bb22850eb44ae0be6659ede770a1d06 100644 (file)
@@ -6,6 +6,7 @@
  *  Copyright (c) 2006 ATI Technologies Inc.
  *  Copyright (c) 2008 NVIDIA Corp.  All rights reserved.
  *  Copyright (c) 2008 Wei Ni <wni@nvidia.com>
+ *  Copyright (c) 2013 Anssi Hannula <anssi.hannula@iki.fi>
  *
  *  Authors:
  *                     Wu Fengguang <wfg@linux.intel.com>
@@ -45,6 +46,7 @@ module_param(static_hdmi_pcm, bool, 0644);
 MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
 
 #define is_haswell(codec)  ((codec)->vendor_id == 0x80862807)
+#define is_valleyview(codec) ((codec)->vendor_id == 0x80862882)
 
 struct hdmi_spec_per_cvt {
        hda_nid_t cvt_nid;
@@ -63,9 +65,11 @@ struct hdmi_spec_per_pin {
        hda_nid_t pin_nid;
        int num_mux_nids;
        hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
+       hda_nid_t cvt_nid;
 
        struct hda_codec *codec;
        struct hdmi_eld sink_eld;
+       struct mutex lock;
        struct delayed_work work;
        struct snd_kcontrol *eld_ctl;
        int repoll_count;
@@ -75,6 +79,42 @@ struct hdmi_spec_per_pin {
        bool chmap_set;         /* channel-map override by ALSA API? */
        unsigned char chmap[8]; /* ALSA API channel-map */
        char pcm_name[8];       /* filled in build_pcm callbacks */
+#ifdef CONFIG_PROC_FS
+       struct snd_info_entry *proc_entry;
+#endif
+};
+
+struct cea_channel_speaker_allocation;
+
+/* operations used by generic code that can be overridden by patches */
+struct hdmi_ops {
+       int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid,
+                          unsigned char *buf, int *eld_size);
+
+       /* get and set channel assigned to each HDMI ASP (audio sample packet) slot */
+       int (*pin_get_slot_channel)(struct hda_codec *codec, hda_nid_t pin_nid,
+                                   int asp_slot);
+       int (*pin_set_slot_channel)(struct hda_codec *codec, hda_nid_t pin_nid,
+                                   int asp_slot, int channel);
+
+       void (*pin_setup_infoframe)(struct hda_codec *codec, hda_nid_t pin_nid,
+                                   int ca, int active_channels, int conn_type);
+
+       /* enable/disable HBR (HD passthrough) */
+       int (*pin_hbr_setup)(struct hda_codec *codec, hda_nid_t pin_nid, bool hbr);
+
+       int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid,
+                           hda_nid_t pin_nid, u32 stream_tag, int format);
+
+       /* Helpers for producing the channel map TLVs. These can be overridden
+        * for devices that have non-standard mapping requirements. */
+       int (*chmap_cea_alloc_validate_get_type)(struct cea_channel_speaker_allocation *cap,
+                                                int channels);
+       void (*cea_alloc_to_tlv_chmap)(struct cea_channel_speaker_allocation *cap,
+                                      unsigned int *chmap, int channels);
+
+       /* check that the user-given chmap is supported */
+       int (*chmap_validate)(int ca, int channels, unsigned char *chmap);
 };
 
 struct hdmi_spec {
@@ -88,8 +128,9 @@ struct hdmi_spec {
        unsigned int channels_max; /* max over all cvts */
 
        struct hdmi_eld temp_eld;
+       struct hdmi_ops ops;
        /*
-        * Non-generic ATI/NVIDIA specific
+        * Non-generic VIA/NVIDIA specific
         */
        struct hda_multi_out multiout;
        struct hda_pcm_stream pcm_playback;
@@ -348,17 +389,19 @@ static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct hdmi_spec *spec = codec->spec;
+       struct hdmi_spec_per_pin *per_pin;
        struct hdmi_eld *eld;
        int pin_idx;
 
        uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
 
        pin_idx = kcontrol->private_value;
-       eld = &get_pin(spec, pin_idx)->sink_eld;
+       per_pin = get_pin(spec, pin_idx);
+       eld = &per_pin->sink_eld;
 
-       mutex_lock(&eld->lock);
+       mutex_lock(&per_pin->lock);
        uinfo->count = eld->eld_valid ? eld->eld_size : 0;
-       mutex_unlock(&eld->lock);
+       mutex_unlock(&per_pin->lock);
 
        return 0;
 }
@@ -368,15 +411,17 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct hdmi_spec *spec = codec->spec;
+       struct hdmi_spec_per_pin *per_pin;
        struct hdmi_eld *eld;
        int pin_idx;
 
        pin_idx = kcontrol->private_value;
-       eld = &get_pin(spec, pin_idx)->sink_eld;
+       per_pin = get_pin(spec, pin_idx);
+       eld = &per_pin->sink_eld;
 
-       mutex_lock(&eld->lock);
+       mutex_lock(&per_pin->lock);
        if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data)) {
-               mutex_unlock(&eld->lock);
+               mutex_unlock(&per_pin->lock);
                snd_BUG();
                return -EINVAL;
        }
@@ -386,7 +431,7 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
        if (eld->eld_valid)
                memcpy(ucontrol->value.bytes.data, eld->eld_buffer,
                       eld->eld_size);
-       mutex_unlock(&eld->lock);
+       mutex_unlock(&per_pin->lock);
 
        return 0;
 }
@@ -477,6 +522,68 @@ static void hdmi_set_channel_count(struct hda_codec *codec,
                                    AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
 }
 
+/*
+ * ELD proc files
+ */
+
+#ifdef CONFIG_PROC_FS
+static void print_eld_info(struct snd_info_entry *entry,
+                          struct snd_info_buffer *buffer)
+{
+       struct hdmi_spec_per_pin *per_pin = entry->private_data;
+
+       mutex_lock(&per_pin->lock);
+       snd_hdmi_print_eld_info(&per_pin->sink_eld, buffer);
+       mutex_unlock(&per_pin->lock);
+}
+
+static void write_eld_info(struct snd_info_entry *entry,
+                          struct snd_info_buffer *buffer)
+{
+       struct hdmi_spec_per_pin *per_pin = entry->private_data;
+
+       mutex_lock(&per_pin->lock);
+       snd_hdmi_write_eld_info(&per_pin->sink_eld, buffer);
+       mutex_unlock(&per_pin->lock);
+}
+
+static int eld_proc_new(struct hdmi_spec_per_pin *per_pin, int index)
+{
+       char name[32];
+       struct hda_codec *codec = per_pin->codec;
+       struct snd_info_entry *entry;
+       int err;
+
+       snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index);
+       err = snd_card_proc_new(codec->bus->card, name, &entry);
+       if (err < 0)
+               return err;
+
+       snd_info_set_text_ops(entry, per_pin, print_eld_info);
+       entry->c.text.write = write_eld_info;
+       entry->mode |= S_IWUSR;
+       per_pin->proc_entry = entry;
+
+       return 0;
+}
+
+static void eld_proc_free(struct hdmi_spec_per_pin *per_pin)
+{
+       if (!per_pin->codec->bus->shutdown && per_pin->proc_entry) {
+               snd_device_free(per_pin->codec->bus->card, per_pin->proc_entry);
+               per_pin->proc_entry = NULL;
+       }
+}
+#else
+static inline int eld_proc_new(struct hdmi_spec_per_pin *per_pin,
+                              int index)
+{
+       return 0;
+}
+static inline void eld_proc_free(struct hdmi_spec_per_pin *per_pin)
+{
+}
+#endif
 
 /*
  * Channel mapping routines
@@ -577,74 +684,91 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec,
                                       hda_nid_t pin_nid)
 {
 #ifdef CONFIG_SND_DEBUG_VERBOSE
+       struct hdmi_spec *spec = codec->spec;
        int i;
-       int slot;
+       int channel;
 
        for (i = 0; i < 8; i++) {
-               slot = snd_hda_codec_read(codec, pin_nid, 0,
-                                               AC_VERB_GET_HDMI_CHAN_SLOT, i);
+               channel = spec->ops.pin_get_slot_channel(codec, pin_nid, i);
                printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
-                                               slot >> 4, slot & 0xf);
+                                               channel, i);
        }
 #endif
 }
 
-
 static void hdmi_std_setup_channel_mapping(struct hda_codec *codec,
                                       hda_nid_t pin_nid,
                                       bool non_pcm,
                                       int ca)
 {
+       struct hdmi_spec *spec = codec->spec;
+       struct cea_channel_speaker_allocation *ch_alloc;
        int i;
        int err;
        int order;
        int non_pcm_mapping[8];
 
        order = get_channel_allocation_order(ca);
+       ch_alloc = &channel_allocations[order];
 
        if (hdmi_channel_mapping[ca][1] == 0) {
-               for (i = 0; i < channel_allocations[order].channels; i++)
-                       hdmi_channel_mapping[ca][i] = i | (i << 4);
-               for (; i < 8; i++)
-                       hdmi_channel_mapping[ca][i] = 0xf | (i << 4);
+               int hdmi_slot = 0;
+               /* fill actual channel mappings in ALSA channel (i) order */
+               for (i = 0; i < ch_alloc->channels; i++) {
+                       while (!ch_alloc->speakers[7 - hdmi_slot] && !WARN_ON(hdmi_slot >= 8))
+                               hdmi_slot++; /* skip zero slots */
+
+                       hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++;
+               }
+               /* fill the rest of the slots with ALSA channel 0xf */
+               for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++)
+                       if (!ch_alloc->speakers[7 - hdmi_slot])
+                               hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot;
        }
 
        if (non_pcm) {
-               for (i = 0; i < channel_allocations[order].channels; i++)
-                       non_pcm_mapping[i] = i | (i << 4);
+               for (i = 0; i < ch_alloc->channels; i++)
+                       non_pcm_mapping[i] = (i << 4) | i;
                for (; i < 8; i++)
-                       non_pcm_mapping[i] = 0xf | (i << 4);
+                       non_pcm_mapping[i] = (0xf << 4) | i;
        }
 
        for (i = 0; i < 8; i++) {
-               err = snd_hda_codec_write(codec, pin_nid, 0,
-                                         AC_VERB_SET_HDMI_CHAN_SLOT,
-                                         non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]);
+               int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i];
+               int hdmi_slot = slotsetup & 0x0f;
+               int channel = (slotsetup & 0xf0) >> 4;
+               err = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot, channel);
                if (err) {
                        snd_printdd(KERN_NOTICE
                                    "HDMI: channel mapping failed\n");
                        break;
                }
        }
-
-       hdmi_debug_channel_mapping(codec, pin_nid);
 }
 
 struct channel_map_table {
        unsigned char map;              /* ALSA API channel map position */
-       unsigned char cea_slot;         /* CEA slot value */
        int spk_mask;                   /* speaker position bit mask */
 };
 
 static struct channel_map_table map_tables[] = {
-       { SNDRV_CHMAP_FL,       0x00,   FL },
-       { SNDRV_CHMAP_FR,       0x01,   FR },
-       { SNDRV_CHMAP_RL,       0x04,   RL },
-       { SNDRV_CHMAP_RR,       0x05,   RR },
-       { SNDRV_CHMAP_LFE,      0x02,   LFE },
-       { SNDRV_CHMAP_FC,       0x03,   FC },
-       { SNDRV_CHMAP_RLC,      0x06,   RLC },
-       { SNDRV_CHMAP_RRC,      0x07,   RRC },
+       { SNDRV_CHMAP_FL,       FL },
+       { SNDRV_CHMAP_FR,       FR },
+       { SNDRV_CHMAP_RL,       RL },
+       { SNDRV_CHMAP_RR,       RR },
+       { SNDRV_CHMAP_LFE,      LFE },
+       { SNDRV_CHMAP_FC,       FC },
+       { SNDRV_CHMAP_RLC,      RLC },
+       { SNDRV_CHMAP_RRC,      RRC },
+       { SNDRV_CHMAP_RC,       RC },
+       { SNDRV_CHMAP_FLC,      FLC },
+       { SNDRV_CHMAP_FRC,      FRC },
+       { SNDRV_CHMAP_FLH,      FLH },
+       { SNDRV_CHMAP_FRH,      FRH },
+       { SNDRV_CHMAP_FLW,      FLW },
+       { SNDRV_CHMAP_FRW,      FRW },
+       { SNDRV_CHMAP_TC,       TC },
+       { SNDRV_CHMAP_FCH,      FCH },
        {} /* terminator */
 };
 
@@ -660,25 +784,19 @@ static int to_spk_mask(unsigned char c)
 }
 
 /* from ALSA API channel position to CEA slot */
-static int to_cea_slot(unsigned char c)
+static int to_cea_slot(int ordered_ca, unsigned char pos)
 {
-       struct channel_map_table *t = map_tables;
-       for (; t->map; t++) {
-               if (t->map == c)
-                       return t->cea_slot;
-       }
-       return 0x0f;
-}
+       int mask = to_spk_mask(pos);
+       int i;
 
-/* from CEA slot to ALSA API channel position */
-static int from_cea_slot(unsigned char c)
-{
-       struct channel_map_table *t = map_tables;
-       for (; t->map; t++) {
-               if (t->cea_slot == c)
-                       return t->map;
+       if (mask) {
+               for (i = 0; i < 8; i++) {
+                       if (channel_allocations[ordered_ca].speakers[7 - i] == mask)
+                               return i;
+               }
        }
-       return 0;
+
+       return -1;
 }
 
 /* from speaker bit mask to ALSA API channel position */
@@ -692,6 +810,14 @@ static int spk_to_chmap(int spk)
        return 0;
 }
 
+/* from CEA slot to ALSA API channel position */
+static int from_cea_slot(int ordered_ca, unsigned char slot)
+{
+       int mask = channel_allocations[ordered_ca].speakers[7 - slot];
+
+       return spk_to_chmap(mask);
+}
+
 /* get the CA index corresponding to the given ALSA API channel map */
 static int hdmi_manual_channel_allocation(int chs, unsigned char *map)
 {
@@ -718,18 +844,29 @@ static int hdmi_manual_channel_allocation(int chs, unsigned char *map)
 /* set up the channel slots for the given ALSA API channel map */
 static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec,
                                             hda_nid_t pin_nid,
-                                            int chs, unsigned char *map)
+                                            int chs, unsigned char *map,
+                                            int ca)
 {
-       int i;
-       for (i = 0; i < 8; i++) {
-               int val, err;
-               if (i < chs)
-                       val = to_cea_slot(map[i]);
-               else
-                       val = 0xf;
-               val |= (i << 4);
-               err = snd_hda_codec_write(codec, pin_nid, 0,
-                                         AC_VERB_SET_HDMI_CHAN_SLOT, val);
+       struct hdmi_spec *spec = codec->spec;
+       int ordered_ca = get_channel_allocation_order(ca);
+       int alsa_pos, hdmi_slot;
+       int assignments[8] = {[0 ... 7] = 0xf};
+
+       for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) {
+
+               hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]);
+
+               if (hdmi_slot < 0)
+                       continue; /* unassigned channel */
+
+               assignments[hdmi_slot] = alsa_pos;
+       }
+
+       for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) {
+               int err;
+
+               err = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot,
+                                                    assignments[hdmi_slot]);
                if (err)
                        return -EINVAL;
        }
@@ -740,9 +877,10 @@ static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec,
 static void hdmi_setup_fake_chmap(unsigned char *map, int ca)
 {
        int i;
+       int ordered_ca = get_channel_allocation_order(ca);
        for (i = 0; i < 8; i++) {
-               if (i < channel_allocations[ca].channels)
-                       map[i] = from_cea_slot((hdmi_channel_mapping[ca][i] >> 4) & 0x0f);
+               if (i < channel_allocations[ordered_ca].channels)
+                       map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f);
                else
                        map[i] = 0;
        }
@@ -755,11 +893,29 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec,
 {
        if (!non_pcm && chmap_set) {
                hdmi_manual_setup_channel_mapping(codec, pin_nid,
-                                                 channels, map);
+                                                 channels, map, ca);
        } else {
                hdmi_std_setup_channel_mapping(codec, pin_nid, non_pcm, ca);
                hdmi_setup_fake_chmap(map, ca);
        }
+
+       hdmi_debug_channel_mapping(codec, pin_nid);
+}
+
+static int hdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
+                                    int asp_slot, int channel)
+{
+       return snd_hda_codec_write(codec, pin_nid, 0,
+                                  AC_VERB_SET_HDMI_CHAN_SLOT,
+                                  (channel << 4) | asp_slot);
+}
+
+static int hdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
+                                    int asp_slot)
+{
+       return (snd_hda_codec_read(codec, pin_nid, 0,
+                                  AC_VERB_GET_HDMI_CHAN_SLOT,
+                                  asp_slot) & 0xf0) >> 4;
 }
 
 /*
@@ -883,15 +1039,64 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
        return true;
 }
 
+static void hdmi_pin_setup_infoframe(struct hda_codec *codec,
+                                    hda_nid_t pin_nid,
+                                    int ca, int active_channels,
+                                    int conn_type)
+{
+       union audio_infoframe ai;
+
+       if (conn_type == 0) { /* HDMI */
+               struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi;
+
+               hdmi_ai->type           = 0x84;
+               hdmi_ai->ver            = 0x01;
+               hdmi_ai->len            = 0x0a;
+               hdmi_ai->CC02_CT47      = active_channels - 1;
+               hdmi_ai->CA             = ca;
+               hdmi_checksum_audio_infoframe(hdmi_ai);
+       } else if (conn_type == 1) { /* DisplayPort */
+               struct dp_audio_infoframe *dp_ai = &ai.dp;
+
+               dp_ai->type             = 0x84;
+               dp_ai->len              = 0x1b;
+               dp_ai->ver              = 0x11 << 2;
+               dp_ai->CC02_CT47        = active_channels - 1;
+               dp_ai->CA               = ca;
+       } else {
+               snd_printd("HDMI: unknown connection type at pin %d\n",
+                           pin_nid);
+               return;
+       }
+
+       /*
+        * sizeof(ai) is used instead of sizeof(*hdmi_ai) or
+        * sizeof(*dp_ai) to avoid partial match/update problems when
+        * the user switches between HDMI/DP monitors.
+        */
+       if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes,
+                                       sizeof(ai))) {
+               snd_printdd("hdmi_pin_setup_infoframe: "
+                           "pin=%d channels=%d ca=0x%02x\n",
+                           pin_nid,
+                           active_channels, ca);
+               hdmi_stop_infoframe_trans(codec, pin_nid);
+               hdmi_fill_audio_infoframe(codec, pin_nid,
+                                           ai.bytes, sizeof(ai));
+               hdmi_start_infoframe_trans(codec, pin_nid);
+       }
+}
+
 static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
                                       struct hdmi_spec_per_pin *per_pin,
                                       bool non_pcm)
 {
+       struct hdmi_spec *spec = codec->spec;
        hda_nid_t pin_nid = per_pin->pin_nid;
        int channels = per_pin->channels;
+       int active_channels;
        struct hdmi_eld *eld;
-       int ca;
-       union audio_infoframe ai;
+       int ca, ordered_ca;
 
        if (!channels)
                return;
@@ -912,29 +1117,10 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
        if (ca < 0)
                ca = 0;
 
-       memset(&ai, 0, sizeof(ai));
-       if (eld->info.conn_type == 0) { /* HDMI */
-               struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi;
+       ordered_ca = get_channel_allocation_order(ca);
+       active_channels = channel_allocations[ordered_ca].channels;
 
-               hdmi_ai->type           = 0x84;
-               hdmi_ai->ver            = 0x01;
-               hdmi_ai->len            = 0x0a;
-               hdmi_ai->CC02_CT47      = channels - 1;
-               hdmi_ai->CA             = ca;
-               hdmi_checksum_audio_infoframe(hdmi_ai);
-       } else if (eld->info.conn_type == 1) { /* DisplayPort */
-               struct dp_audio_infoframe *dp_ai = &ai.dp;
-
-               dp_ai->type             = 0x84;
-               dp_ai->len              = 0x1b;
-               dp_ai->ver              = 0x11 << 2;
-               dp_ai->CC02_CT47        = channels - 1;
-               dp_ai->CA               = ca;
-       } else {
-               snd_printd("HDMI: unknown connection type at pin %d\n",
-                           pin_nid);
-               return;
-       }
+       hdmi_set_channel_count(codec, per_pin->cvt_nid, active_channels);
 
        /*
         * always configure channel mapping, it may have been changed by the
@@ -944,32 +1130,17 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
                                   channels, per_pin->chmap,
                                   per_pin->chmap_set);
 
-       /*
-        * sizeof(ai) is used instead of sizeof(*hdmi_ai) or
-        * sizeof(*dp_ai) to avoid partial match/update problems when
-        * the user switches between HDMI/DP monitors.
-        */
-       if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes,
-                                       sizeof(ai))) {
-               snd_printdd("hdmi_setup_audio_infoframe: "
-                           "pin=%d channels=%d\n",
-                           pin_nid,
-                           channels);
-               hdmi_stop_infoframe_trans(codec, pin_nid);
-               hdmi_fill_audio_infoframe(codec, pin_nid,
-                                           ai.bytes, sizeof(ai));
-               hdmi_start_infoframe_trans(codec, pin_nid);
-       }
+       spec->ops.pin_setup_infoframe(codec, pin_nid, ca, active_channels,
+                                     eld->info.conn_type);
 
        per_pin->non_pcm = non_pcm;
 }
 
-
 /*
  * Unsolicited events
  */
 
-static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll);
+static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll);
 
 static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
 {
@@ -995,8 +1166,8 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
        if (pin_idx < 0)
                return;
 
-       hdmi_present_sense(get_pin(spec, pin_idx), 1);
-       snd_hda_jack_report_sync(codec);
+       if (hdmi_present_sense(get_pin(spec, pin_idx), 1))
+               snd_hda_jack_report_sync(codec);
 }
 
 static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
@@ -1067,26 +1238,22 @@ static void haswell_verify_D0(struct hda_codec *codec,
 #define is_hbr_format(format) \
        ((format & AC_FMT_TYPE_NON_PCM) && (format & AC_FMT_CHAN_MASK) == 7)
 
-static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
-                             hda_nid_t pin_nid, u32 stream_tag, int format)
+static int hdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
+                             bool hbr)
 {
-       int pinctl;
-       int new_pinctl = 0;
-
-       if (is_haswell(codec))
-               haswell_verify_D0(codec, cvt_nid, pin_nid);
+       int pinctl, new_pinctl;
 
        if (snd_hda_query_pin_caps(codec, pin_nid) & AC_PINCAP_HBR) {
                pinctl = snd_hda_codec_read(codec, pin_nid, 0,
                                            AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
 
                new_pinctl = pinctl & ~AC_PINCTL_EPT;
-               if (is_hbr_format(format))
+               if (hbr)
                        new_pinctl |= AC_PINCTL_EPT_HBR;
                else
                        new_pinctl |= AC_PINCTL_EPT_NATIVE;
 
-               snd_printdd("hdmi_setup_stream: "
+               snd_printdd("hdmi_pin_hbr_setup: "
                            "NID=0x%x, %spinctl=0x%x\n",
                            pin_nid,
                            pinctl == new_pinctl ? "" : "new-",
@@ -1096,11 +1263,26 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
                        snd_hda_codec_write(codec, pin_nid, 0,
                                            AC_VERB_SET_PIN_WIDGET_CONTROL,
                                            new_pinctl);
+       } else if (hbr)
+               return -EINVAL;
 
-       }
-       if (is_hbr_format(format) && !new_pinctl) {
+       return 0;
+}
+
+static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
+                             hda_nid_t pin_nid, u32 stream_tag, int format)
+{
+       struct hdmi_spec *spec = codec->spec;
+       int err;
+
+       if (is_haswell(codec))
+               haswell_verify_D0(codec, cvt_nid, pin_nid);
+
+       err = spec->ops.pin_hbr_setup(codec, pin_nid, is_hbr_format(format));
+
+       if (err) {
                snd_printdd("hdmi_setup_stream: HBR is not supported\n");
-               return -EINVAL;
+               return err;
        }
 
        snd_hda_codec_setup_stream(codec, cvt_nid, stream_tag, 0, format);
@@ -1146,7 +1328,16 @@ static int hdmi_choose_cvt(struct hda_codec *codec,
        return 0;
 }
 
-static void haswell_config_cvts(struct hda_codec *codec,
+/* Intel HDMI workaround to fix audio routing issue:
+ * For some Intel display codecs, pins share the same connection list.
+ * So a conveter can be selected by multiple pins and playback on any of these
+ * pins will generate sound on the external display, because audio flows from
+ * the same converter to the display pipeline. Also muting one pin may make
+ * other pins have no sound output.
+ * So this function assures that an assigned converter for a pin is not selected
+ * by any other pins.
+ */
+static void intel_not_share_assigned_cvt(struct hda_codec *codec,
                        hda_nid_t pin_nid, int mux_idx)
 {
        struct hdmi_spec *spec = codec->spec;
@@ -1217,6 +1408,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
        per_cvt = get_cvt(spec, cvt_idx);
        /* Claim converter */
        per_cvt->assigned = 1;
+       per_pin->cvt_nid = per_cvt->cvt_nid;
        hinfo->nid = per_cvt->cvt_nid;
 
        snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
@@ -1224,8 +1416,8 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
                            mux_idx);
 
        /* configure unused pins to choose other converters */
-       if (is_haswell(codec))
-               haswell_config_cvts(codec, per_pin->pin_nid, mux_idx);
+       if (is_haswell(codec) || is_valleyview(codec))
+               intel_not_share_assigned_cvt(codec, per_pin->pin_nid, mux_idx);
 
        snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid);
 
@@ -1283,8 +1475,9 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
        return 0;
 }
 
-static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
+static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
 {
+       struct hda_jack_tbl *jack;
        struct hda_codec *codec = per_pin->codec;
        struct hdmi_spec *spec = codec->spec;
        struct hdmi_eld *eld = &spec->temp_eld;
@@ -1301,7 +1494,9 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
        int present = snd_hda_pin_sense(codec, pin_nid);
        bool update_eld = false;
        bool eld_changed = false;
+       bool ret;
 
+       mutex_lock(&per_pin->lock);
        pin_eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
        if (pin_eld->monitor_present)
                eld->eld_valid  = !!(present & AC_PINSENSE_ELDV);
@@ -1313,7 +1508,7 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
                codec->addr, pin_nid, pin_eld->monitor_present, eld->eld_valid);
 
        if (eld->eld_valid) {
-               if (snd_hdmi_get_eld(codec, pin_nid, eld->eld_buffer,
+               if (spec->ops.pin_get_eld(codec, pin_nid, eld->eld_buffer,
                                                     &eld->eld_size) < 0)
                        eld->eld_valid = false;
                else {
@@ -1331,11 +1526,10 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
                        queue_delayed_work(codec->bus->workq,
                                           &per_pin->work,
                                           msecs_to_jiffies(300));
-                       return;
+                       goto unlock;
                }
        }
 
-       mutex_lock(&pin_eld->lock);
        if (pin_eld->eld_valid && !eld->eld_valid) {
                update_eld = true;
                eld_changed = true;
@@ -1352,20 +1546,29 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
                pin_eld->eld_size = eld->eld_size;
                pin_eld->info = eld->info;
 
-               /* Haswell-specific workaround: re-setup when the transcoder is
-                * changed during the stream playback
+               /*
+                * Re-setup pin and infoframe. This is needed e.g. when
+                * - sink is first plugged-in (infoframe is not set up if !monitor_present)
+                * - transcoder can change during stream playback on Haswell
                 */
-               if (is_haswell(codec) &&
-                   eld->eld_valid && !old_eld_valid && per_pin->setup)
+               if (eld->eld_valid && !old_eld_valid && per_pin->setup)
                        hdmi_setup_audio_infoframe(codec, per_pin,
                                                   per_pin->non_pcm);
        }
-       mutex_unlock(&pin_eld->lock);
 
        if (eld_changed)
                snd_ctl_notify(codec->bus->card,
                               SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO,
                               &per_pin->eld_ctl->id);
+ unlock:
+       ret = !repoll || !pin_eld->monitor_present || pin_eld->eld_valid;
+
+       jack = snd_hda_jack_tbl_get(codec, pin_nid);
+       if (jack)
+               jack->block_report = !ret;
+
+       mutex_unlock(&per_pin->lock);
+       return ret;
 }
 
 static void hdmi_repoll_eld(struct work_struct *work)
@@ -1376,7 +1579,8 @@ static void hdmi_repoll_eld(struct work_struct *work)
        if (per_pin->repoll_count++ > 6)
                per_pin->repoll_count = 0;
 
-       hdmi_present_sense(per_pin, per_pin->repoll_count);
+       if (hdmi_present_sense(per_pin, per_pin->repoll_count))
+               snd_hda_jack_report_sync(per_pin->codec);
 }
 
 static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
@@ -1536,14 +1740,14 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
        bool non_pcm;
 
        non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
+       mutex_lock(&per_pin->lock);
        per_pin->channels = substream->runtime->channels;
        per_pin->setup = true;
 
-       hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels);
-
        hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
+       mutex_unlock(&per_pin->lock);
 
-       return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
+       return spec->ops.setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
 }
 
 static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
@@ -1579,11 +1783,14 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
                per_pin = get_pin(spec, pin_idx);
 
                snd_hda_spdif_ctls_unassign(codec, pin_idx);
+
+               mutex_lock(&per_pin->lock);
                per_pin->chmap_set = false;
                memset(per_pin->chmap, 0, sizeof(per_pin->chmap));
 
                per_pin->setup = false;
                per_pin->channels = 0;
+               mutex_unlock(&per_pin->lock);
        }
 
        return 0;
@@ -1612,14 +1819,40 @@ static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
+static int hdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap,
+                                                 int channels)
+{
+       /* If the speaker allocation matches the channel count, it is OK.*/
+       if (cap->channels != channels)
+               return -1;
+
+       /* all channels are remappable freely */
+       return SNDRV_CTL_TLVT_CHMAP_VAR;
+}
+
+static void hdmi_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap,
+                                       unsigned int *chmap, int channels)
+{
+       int count = 0;
+       int c;
+
+       for (c = 7; c >= 0; c--) {
+               int spk = cap->speakers[c];
+               if (!spk)
+                       continue;
+
+               chmap[count++] = spk_to_chmap(spk);
+       }
+
+       WARN_ON(count != channels);
+}
+
 static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
                              unsigned int size, unsigned int __user *tlv)
 {
        struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
        struct hda_codec *codec = info->private_data;
        struct hdmi_spec *spec = codec->spec;
-       const unsigned int valid_mask =
-               FL | FR | RL | RR | LFE | FC | RLC | RRC;
        unsigned int __user *dst;
        int chs, count = 0;
 
@@ -1630,18 +1863,19 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
        size -= 8;
        dst = tlv + 2;
        for (chs = 2; chs <= spec->channels_max; chs++) {
-               int i, c;
+               int i;
                struct cea_channel_speaker_allocation *cap;
                cap = channel_allocations;
                for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {
                        int chs_bytes = chs * 4;
-                       if (cap->channels != chs)
-                               continue;
-                       if (cap->spk_mask & ~valid_mask)
+                       int type = spec->ops.chmap_cea_alloc_validate_get_type(cap, chs);
+                       unsigned int tlv_chmap[8];
+
+                       if (type < 0)
                                continue;
                        if (size < 8)
                                return -ENOMEM;
-                       if (put_user(SNDRV_CTL_TLVT_CHMAP_VAR, dst) ||
+                       if (put_user(type, dst) ||
                            put_user(chs_bytes, dst + 1))
                                return -EFAULT;
                        dst += 2;
@@ -1651,14 +1885,10 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
                                return -ENOMEM;
                        size -= chs_bytes;
                        count += chs_bytes;
-                       for (c = 7; c >= 0; c--) {
-                               int spk = cap->speakers[c];
-                               if (!spk)
-                                       continue;
-                               if (put_user(spk_to_chmap(spk), dst))
-                                       return -EFAULT;
-                               dst++;
-                       }
+                       spec->ops.cea_alloc_to_tlv_chmap(cap, tlv_chmap, chs);
+                       if (copy_to_user(dst, tlv_chmap, chs_bytes))
+                               return -EFAULT;
+                       dst += chs;
                }
        }
        if (put_user(count, tlv + 1))
@@ -1692,7 +1922,7 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
        unsigned int ctl_idx;
        struct snd_pcm_substream *substream;
        unsigned char chmap[8];
-       int i, ca, prepared = 0;
+       int i, err, ca, prepared = 0;
 
        ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
        substream = snd_pcm_chmap_substream(info, ctl_idx);
@@ -1716,10 +1946,17 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
        ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap);
        if (ca < 0)
                return -EINVAL;
+       if (spec->ops.chmap_validate) {
+               err = spec->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap);
+               if (err)
+                       return err;
+       }
+       mutex_lock(&per_pin->lock);
        per_pin->chmap_set = true;
        memcpy(per_pin->chmap, chmap, sizeof(chmap));
        if (prepared)
                hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
+       mutex_unlock(&per_pin->lock);
 
        return 0;
 }
@@ -1836,12 +2073,11 @@ static int generic_hdmi_init_per_pins(struct hda_codec *codec)
 
        for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
                struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
-               struct hdmi_eld *eld = &per_pin->sink_eld;
 
                per_pin->codec = codec;
-               mutex_init(&eld->lock);
+               mutex_init(&per_pin->lock);
                INIT_DELAYED_WORK(&per_pin->work, hdmi_repoll_eld);
-               snd_hda_eld_proc_new(codec, eld, pin_idx);
+               eld_proc_new(per_pin, pin_idx);
        }
        return 0;
 }
@@ -1882,10 +2118,9 @@ static void generic_hdmi_free(struct hda_codec *codec)
 
        for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
                struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
-               struct hdmi_eld *eld = &per_pin->sink_eld;
 
                cancel_delayed_work(&per_pin->work);
-               snd_hda_eld_proc_free(codec, eld);
+               eld_proc_free(per_pin);
        }
 
        flush_workqueue(codec->bus->workq);
@@ -1922,6 +2157,17 @@ static const struct hda_codec_ops generic_hdmi_patch_ops = {
 #endif
 };
 
+static const struct hdmi_ops generic_standard_hdmi_ops = {
+       .pin_get_eld                            = snd_hdmi_get_eld,
+       .pin_get_slot_channel                   = hdmi_pin_get_slot_channel,
+       .pin_set_slot_channel                   = hdmi_pin_set_slot_channel,
+       .pin_setup_infoframe                    = hdmi_pin_setup_infoframe,
+       .pin_hbr_setup                          = hdmi_pin_hbr_setup,
+       .setup_stream                           = hdmi_setup_stream,
+       .chmap_cea_alloc_validate_get_type      = hdmi_chmap_cea_alloc_validate_get_type,
+       .cea_alloc_to_tlv_chmap                 = hdmi_cea_alloc_to_tlv_chmap,
+};
+
 
 static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
                                             hda_nid_t nid)
@@ -2004,6 +2250,7 @@ static int patch_generic_hdmi(struct hda_codec *codec)
        if (spec == NULL)
                return -ENOMEM;
 
+       spec->ops = generic_standard_hdmi_ops;
        codec->spec = spec;
        hdmi_array_init(spec, 4);
 
@@ -2559,49 +2806,398 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
 }
 
 /*
- * ATI-specific implementations
- *
- * FIXME: we may omit the whole this and use the generic code once after
- * it's confirmed to work.
+ * NVIDIA codecs ignore ASP mapping for 2ch - confirmed on:
+ * - 0x10de0015
+ * - 0x10de0040
  */
+static int nvhdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap,
+                                                   int channels)
+{
+       if (cap->ca_index == 0x00 && channels == 2)
+               return SNDRV_CTL_TLVT_CHMAP_FIXED;
+
+       return hdmi_chmap_cea_alloc_validate_get_type(cap, channels);
+}
+
+static int nvhdmi_chmap_validate(int ca, int chs, unsigned char *map)
+{
+       if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int patch_nvhdmi(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec;
+       int err;
+
+       err = patch_generic_hdmi(codec);
+       if (err)
+               return err;
+
+       spec = codec->spec;
+
+       spec->ops.chmap_cea_alloc_validate_get_type =
+               nvhdmi_chmap_cea_alloc_validate_get_type;
+       spec->ops.chmap_validate = nvhdmi_chmap_validate;
+
+       return 0;
+}
+
+/*
+ * ATI/AMD-specific implementations
+ */
+
+#define is_amdhdmi_rev3_or_later(codec) \
+       ((codec)->vendor_id == 0x1002aa01 && ((codec)->revision_id & 0xff00) >= 0x0300)
+#define has_amd_full_remap_support(codec) is_amdhdmi_rev3_or_later(codec)
+
+/* ATI/AMD specific HDA pin verbs, see the AMD HDA Verbs specification */
+#define ATI_VERB_SET_CHANNEL_ALLOCATION        0x771
+#define ATI_VERB_SET_DOWNMIX_INFO      0x772
+#define ATI_VERB_SET_MULTICHANNEL_01   0x777
+#define ATI_VERB_SET_MULTICHANNEL_23   0x778
+#define ATI_VERB_SET_MULTICHANNEL_45   0x779
+#define ATI_VERB_SET_MULTICHANNEL_67   0x77a
+#define ATI_VERB_SET_HBR_CONTROL       0x77c
+#define ATI_VERB_SET_MULTICHANNEL_1    0x785
+#define ATI_VERB_SET_MULTICHANNEL_3    0x786
+#define ATI_VERB_SET_MULTICHANNEL_5    0x787
+#define ATI_VERB_SET_MULTICHANNEL_7    0x788
+#define ATI_VERB_SET_MULTICHANNEL_MODE 0x789
+#define ATI_VERB_GET_CHANNEL_ALLOCATION        0xf71
+#define ATI_VERB_GET_DOWNMIX_INFO      0xf72
+#define ATI_VERB_GET_MULTICHANNEL_01   0xf77
+#define ATI_VERB_GET_MULTICHANNEL_23   0xf78
+#define ATI_VERB_GET_MULTICHANNEL_45   0xf79
+#define ATI_VERB_GET_MULTICHANNEL_67   0xf7a
+#define ATI_VERB_GET_HBR_CONTROL       0xf7c
+#define ATI_VERB_GET_MULTICHANNEL_1    0xf85
+#define ATI_VERB_GET_MULTICHANNEL_3    0xf86
+#define ATI_VERB_GET_MULTICHANNEL_5    0xf87
+#define ATI_VERB_GET_MULTICHANNEL_7    0xf88
+#define ATI_VERB_GET_MULTICHANNEL_MODE 0xf89
+
+/* AMD specific HDA cvt verbs */
+#define ATI_VERB_SET_RAMP_RATE         0x770
+#define ATI_VERB_GET_RAMP_RATE         0xf70
+
+#define ATI_OUT_ENABLE 0x1
+
+#define ATI_MULTICHANNEL_MODE_PAIRED   0
+#define ATI_MULTICHANNEL_MODE_SINGLE   1
+
+#define ATI_HBR_CAPABLE 0x01
+#define ATI_HBR_ENABLE 0x10
+
+static int atihdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid,
+                          unsigned char *buf, int *eld_size)
+{
+       /* call hda_eld.c ATI/AMD-specific function */
+       return snd_hdmi_get_eld_ati(codec, nid, buf, eld_size,
+                                   is_amdhdmi_rev3_or_later(codec));
+}
+
+static void atihdmi_pin_setup_infoframe(struct hda_codec *codec, hda_nid_t pin_nid, int ca,
+                                       int active_channels, int conn_type)
+{
+       snd_hda_codec_write(codec, pin_nid, 0, ATI_VERB_SET_CHANNEL_ALLOCATION, ca);
+}
+
+static int atihdmi_paired_swap_fc_lfe(int pos)
+{
+       /*
+        * ATI/AMD have automatic FC/LFE swap built-in
+        * when in pairwise mapping mode.
+        */
+
+       switch (pos) {
+               /* see channel_allocations[].speakers[] */
+               case 2: return 3;
+               case 3: return 2;
+               default: break;
+       }
+
+       return pos;
+}
+
+static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map)
+{
+       struct cea_channel_speaker_allocation *cap;
+       int i, j;
+
+       /* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */
+
+       cap = &channel_allocations[get_channel_allocation_order(ca)];
+       for (i = 0; i < chs; ++i) {
+               int mask = to_spk_mask(map[i]);
+               bool ok = false;
+               bool companion_ok = false;
+
+               if (!mask)
+                       continue;
+
+               for (j = 0 + i % 2; j < 8; j += 2) {
+                       int chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j);
+                       if (cap->speakers[chan_idx] == mask) {
+                               /* channel is in a supported position */
+                               ok = true;
+
+                               if (i % 2 == 0 && i + 1 < chs) {
+                                       /* even channel, check the odd companion */
+                                       int comp_chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j + 1);
+                                       int comp_mask_req = to_spk_mask(map[i+1]);
+                                       int comp_mask_act = cap->speakers[comp_chan_idx];
+
+                                       if (comp_mask_req == comp_mask_act)
+                                               companion_ok = true;
+                                       else
+                                               return -EINVAL;
+                               }
+                               break;
+                       }
+               }
+
+               if (!ok)
+                       return -EINVAL;
+
+               if (companion_ok)
+                       i++; /* companion channel already checked */
+       }
+
+       return 0;
+}
+
+static int atihdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
+                                       int hdmi_slot, int stream_channel)
+{
+       int verb;
+       int ati_channel_setup = 0;
+
+       if (hdmi_slot > 7)
+               return -EINVAL;
+
+       if (!has_amd_full_remap_support(codec)) {
+               hdmi_slot = atihdmi_paired_swap_fc_lfe(hdmi_slot);
+
+               /* In case this is an odd slot but without stream channel, do not
+                * disable the slot since the corresponding even slot could have a
+                * channel. In case neither have a channel, the slot pair will be
+                * disabled when this function is called for the even slot. */
+               if (hdmi_slot % 2 != 0 && stream_channel == 0xf)
+                       return 0;
+
+               hdmi_slot -= hdmi_slot % 2;
+
+               if (stream_channel != 0xf)
+                       stream_channel -= stream_channel % 2;
+       }
+
+       verb = ATI_VERB_SET_MULTICHANNEL_01 + hdmi_slot/2 + (hdmi_slot % 2) * 0x00e;
+
+       /* ati_channel_setup format: [7..4] = stream_channel_id, [1] = mute, [0] = enable */
 
-#define ATIHDMI_CVT_NID                0x02    /* audio converter */
-#define ATIHDMI_PIN_NID                0x03    /* HDMI output pin */
+       if (stream_channel != 0xf)
+               ati_channel_setup = (stream_channel << 4) | ATI_OUT_ENABLE;
 
-static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
-                                       struct hda_codec *codec,
-                                       unsigned int stream_tag,
-                                       unsigned int format,
-                                       struct snd_pcm_substream *substream)
+       return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup);
+}
+
+static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
+                                       int asp_slot)
+{
+       bool was_odd = false;
+       int ati_asp_slot = asp_slot;
+       int verb;
+       int ati_channel_setup;
+
+       if (asp_slot > 7)
+               return -EINVAL;
+
+       if (!has_amd_full_remap_support(codec)) {
+               ati_asp_slot = atihdmi_paired_swap_fc_lfe(asp_slot);
+               if (ati_asp_slot % 2 != 0) {
+                       ati_asp_slot -= 1;
+                       was_odd = true;
+               }
+       }
+
+       verb = ATI_VERB_GET_MULTICHANNEL_01 + ati_asp_slot/2 + (ati_asp_slot % 2) * 0x00e;
+
+       ati_channel_setup = snd_hda_codec_read(codec, pin_nid, 0, verb, 0);
+
+       if (!(ati_channel_setup & ATI_OUT_ENABLE))
+               return 0xf;
+
+       return ((ati_channel_setup & 0xf0) >> 4) + !!was_odd;
+}
+
+static int atihdmi_paired_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap,
+                                                           int channels)
+{
+       int c;
+
+       /*
+        * Pre-rev3 ATI/AMD codecs operate in a paired channel mode, so
+        * we need to take that into account (a single channel may take 2
+        * channel slots if we need to carry a silent channel next to it).
+        * On Rev3+ AMD codecs this function is not used.
+        */
+       int chanpairs = 0;
+
+       /* We only produce even-numbered channel count TLVs */
+       if ((channels % 2) != 0)
+               return -1;
+
+       for (c = 0; c < 7; c += 2) {
+               if (cap->speakers[c] || cap->speakers[c+1])
+                       chanpairs++;
+       }
+
+       if (chanpairs * 2 != channels)
+               return -1;
+
+       return SNDRV_CTL_TLVT_CHMAP_PAIRED;
+}
+
+static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap,
+                                                 unsigned int *chmap, int channels)
+{
+       /* produce paired maps for pre-rev3 ATI/AMD codecs */
+       int count = 0;
+       int c;
+
+       for (c = 7; c >= 0; c--) {
+               int chan = 7 - atihdmi_paired_swap_fc_lfe(7 - c);
+               int spk = cap->speakers[chan];
+               if (!spk) {
+                       /* add N/A channel if the companion channel is occupied */
+                       if (cap->speakers[chan + (chan % 2 ? -1 : 1)])
+                               chmap[count++] = SNDRV_CHMAP_NA;
+
+                       continue;
+               }
+
+               chmap[count++] = spk_to_chmap(spk);
+       }
+
+       WARN_ON(count != channels);
+}
+
+static int atihdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
+                                bool hbr)
+{
+       int hbr_ctl, hbr_ctl_new;
+
+       hbr_ctl = snd_hda_codec_read(codec, pin_nid, 0, ATI_VERB_GET_HBR_CONTROL, 0);
+       if (hbr_ctl & ATI_HBR_CAPABLE) {
+               if (hbr)
+                       hbr_ctl_new = hbr_ctl | ATI_HBR_ENABLE;
+               else
+                       hbr_ctl_new = hbr_ctl & ~ATI_HBR_ENABLE;
+
+               snd_printdd("atihdmi_pin_hbr_setup: "
+                               "NID=0x%x, %shbr-ctl=0x%x\n",
+                               pin_nid,
+                               hbr_ctl == hbr_ctl_new ? "" : "new-",
+                               hbr_ctl_new);
+
+               if (hbr_ctl != hbr_ctl_new)
+                       snd_hda_codec_write(codec, pin_nid, 0,
+                                               ATI_VERB_SET_HBR_CONTROL,
+                                               hbr_ctl_new);
+
+       } else if (hbr)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int atihdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
+                               hda_nid_t pin_nid, u32 stream_tag, int format)
+{
+
+       if (is_amdhdmi_rev3_or_later(codec)) {
+               int ramp_rate = 180; /* default as per AMD spec */
+               /* disable ramp-up/down for non-pcm as per AMD spec */
+               if (format & AC_FMT_TYPE_NON_PCM)
+                       ramp_rate = 0;
+
+               snd_hda_codec_write(codec, cvt_nid, 0, ATI_VERB_SET_RAMP_RATE, ramp_rate);
+       }
+
+       return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
+}
+
+
+static int atihdmi_init(struct hda_codec *codec)
 {
        struct hdmi_spec *spec = codec->spec;
-       struct hdmi_spec_per_cvt *per_cvt = get_cvt(spec, 0);
-       int chans = substream->runtime->channels;
-       int i, err;
+       int pin_idx, err;
 
-       err = simple_playback_pcm_prepare(hinfo, codec, stream_tag, format,
-                                         substream);
-       if (err < 0)
+       err = generic_hdmi_init(codec);
+
+       if (err)
                return err;
-       snd_hda_codec_write(codec, per_cvt->cvt_nid, 0,
-                           AC_VERB_SET_CVT_CHAN_COUNT, chans - 1);
-       /* FIXME: XXX */
-       for (i = 0; i < chans; i++) {
-               snd_hda_codec_write(codec, per_cvt->cvt_nid, 0,
-                                   AC_VERB_SET_HDMI_CHAN_SLOT,
-                                   (i << 4) | i);
+
+       for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+               struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+
+               /* make sure downmix information in infoframe is zero */
+               snd_hda_codec_write(codec, per_pin->pin_nid, 0, ATI_VERB_SET_DOWNMIX_INFO, 0);
+
+               /* enable channel-wise remap mode if supported */
+               if (has_amd_full_remap_support(codec))
+                       snd_hda_codec_write(codec, per_pin->pin_nid, 0,
+                                           ATI_VERB_SET_MULTICHANNEL_MODE,
+                                           ATI_MULTICHANNEL_MODE_SINGLE);
        }
+
        return 0;
 }
 
 static int patch_atihdmi(struct hda_codec *codec)
 {
        struct hdmi_spec *spec;
-       int err = patch_simple_hdmi(codec, ATIHDMI_CVT_NID, ATIHDMI_PIN_NID);
-       if (err < 0)
+       struct hdmi_spec_per_cvt *per_cvt;
+       int err, cvt_idx;
+
+       err = patch_generic_hdmi(codec);
+
+       if (err)
                return err;
+
+       codec->patch_ops.init = atihdmi_init;
+
        spec = codec->spec;
-       spec->pcm_playback.ops.prepare = atihdmi_playback_pcm_prepare;
+
+       spec->ops.pin_get_eld = atihdmi_pin_get_eld;
+       spec->ops.pin_get_slot_channel = atihdmi_pin_get_slot_channel;
+       spec->ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel;
+       spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe;
+       spec->ops.pin_hbr_setup = atihdmi_pin_hbr_setup;
+       spec->ops.setup_stream = atihdmi_setup_stream;
+
+       if (!has_amd_full_remap_support(codec)) {
+               /* override to ATI/AMD-specific versions with pairwise mapping */
+               spec->ops.chmap_cea_alloc_validate_get_type =
+                       atihdmi_paired_chmap_cea_alloc_validate_get_type;
+               spec->ops.cea_alloc_to_tlv_chmap = atihdmi_paired_cea_alloc_to_tlv_chmap;
+               spec->ops.chmap_validate = atihdmi_paired_chmap_validate;
+       }
+
+       /* ATI/AMD converters do not advertise all of their capabilities */
+       for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
+               per_cvt = get_cvt(spec, cvt_idx);
+               per_cvt->channels_max = max(per_cvt->channels_max, 8u);
+               per_cvt->rates |= SUPPORTED_RATES;
+               per_cvt->formats |= SUPPORTED_FORMATS;
+               per_cvt->maxbps = max(per_cvt->maxbps, 24u);
+       }
+
+       spec->channels_max = max(spec->channels_max, 8u);
+
        return 0;
 }
 
@@ -2621,7 +3217,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
 { .id = 0x1002793c, .name = "RS600 HDMI",      .patch = patch_atihdmi },
 { .id = 0x10027919, .name = "RS600 HDMI",      .patch = patch_atihdmi },
 { .id = 0x1002791a, .name = "RS690/780 HDMI",  .patch = patch_atihdmi },
-{ .id = 0x1002aa01, .name = "R6xx HDMI",       .patch = patch_generic_hdmi },
+{ .id = 0x1002aa01, .name = "R6xx HDMI",       .patch = patch_atihdmi },
 { .id = 0x10951390, .name = "SiI1390 HDMI",    .patch = patch_generic_hdmi },
 { .id = 0x10951392, .name = "SiI1392 HDMI",    .patch = patch_generic_hdmi },
 { .id = 0x17e80047, .name = "Chrontel HDMI",   .patch = patch_generic_hdmi },
@@ -2630,30 +3226,30 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
 { .id = 0x10de0005, .name = "MCP77/78 HDMI",   .patch = patch_nvhdmi_8ch_7x },
 { .id = 0x10de0006, .name = "MCP77/78 HDMI",   .patch = patch_nvhdmi_8ch_7x },
 { .id = 0x10de0007, .name = "MCP79/7A HDMI",   .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP",  .patch = patch_generic_hdmi },
-{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP",  .patch = patch_generic_hdmi },
-{ .id = 0x10de000c, .name = "MCP89 HDMI",      .patch = patch_generic_hdmi },
-{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP",  .patch = patch_generic_hdmi },
-{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP",  .patch = patch_generic_hdmi },
-{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP",  .patch = patch_generic_hdmi },
-{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP",  .patch = patch_generic_hdmi },
-{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP",  .patch = patch_generic_hdmi },
-{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP",  .patch = patch_generic_hdmi },
-{ .id = 0x10de0015, .name = "GPU 15 HDMI/DP",  .patch = patch_generic_hdmi },
-{ .id = 0x10de0016, .name = "GPU 16 HDMI/DP",  .patch = patch_generic_hdmi },
+{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP",  .patch = patch_nvhdmi },
+{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP",  .patch = patch_nvhdmi },
+{ .id = 0x10de000c, .name = "MCP89 HDMI",      .patch = patch_nvhdmi },
+{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP",  .patch = patch_nvhdmi },
+{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP",  .patch = patch_nvhdmi },
+{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP",  .patch = patch_nvhdmi },
+{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP",  .patch = patch_nvhdmi },
+{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP",  .patch = patch_nvhdmi },
+{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP",  .patch = patch_nvhdmi },
+{ .id = 0x10de0015, .name = "GPU 15 HDMI/DP",  .patch = patch_nvhdmi },
+{ .id = 0x10de0016, .name = "GPU 16 HDMI/DP",  .patch = patch_nvhdmi },
 /* 17 is known to be absent */
-{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP",  .patch = patch_generic_hdmi },
-{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP",  .patch = patch_generic_hdmi },
-{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP",  .patch = patch_generic_hdmi },
-{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP",  .patch = patch_generic_hdmi },
-{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP",  .patch = patch_generic_hdmi },
-{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP",  .patch = patch_generic_hdmi },
-{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP",  .patch = patch_generic_hdmi },
-{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP",  .patch = patch_generic_hdmi },
-{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP",  .patch = patch_generic_hdmi },
-{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP",  .patch = patch_generic_hdmi },
-{ .id = 0x10de0051, .name = "GPU 51 HDMI/DP",  .patch = patch_generic_hdmi },
-{ .id = 0x10de0060, .name = "GPU 60 HDMI/DP",  .patch = patch_generic_hdmi },
+{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP",  .patch = patch_nvhdmi },
+{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP",  .patch = patch_nvhdmi },
+{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP",  .patch = patch_nvhdmi },
+{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP",  .patch = patch_nvhdmi },
+{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP",  .patch = patch_nvhdmi },
+{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP",  .patch = patch_nvhdmi },
+{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP",  .patch = patch_nvhdmi },
+{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP",  .patch = patch_nvhdmi },
+{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP",  .patch = patch_nvhdmi },
+{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP",  .patch = patch_nvhdmi },
+{ .id = 0x10de0051, .name = "GPU 51 HDMI/DP",  .patch = patch_nvhdmi },
+{ .id = 0x10de0060, .name = "GPU 60 HDMI/DP",  .patch = patch_nvhdmi },
 { .id = 0x10de0067, .name = "MCP67 HDMI",      .patch = patch_nvhdmi_2ch },
 { .id = 0x10de8001, .name = "MCP73 HDMI",      .patch = patch_nvhdmi_2ch },
 { .id = 0x11069f80, .name = "VX900 HDMI/DP",   .patch = patch_via_hdmi },
@@ -2669,6 +3265,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
 { .id = 0x80862806, .name = "PantherPoint HDMI", .patch = patch_generic_hdmi },
 { .id = 0x80862807, .name = "Haswell HDMI",    .patch = patch_generic_hdmi },
 { .id = 0x80862880, .name = "CedarTrail HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x80862882, .name = "Valleyview2 HDMI",        .patch = patch_generic_hdmi },
 { .id = 0x808629fb, .name = "Crestline HDMI",  .patch = patch_generic_hdmi },
 {} /* terminator */
 };
@@ -2723,6 +3320,7 @@ MODULE_ALIAS("snd-hda-codec-id:80862805");
 MODULE_ALIAS("snd-hda-codec-id:80862806");
 MODULE_ALIAS("snd-hda-codec-id:80862807");
 MODULE_ALIAS("snd-hda-codec-id:80862880");
+MODULE_ALIAS("snd-hda-codec-id:80862882");
 MODULE_ALIAS("snd-hda-codec-id:808629fb");
 
 MODULE_LICENSE("GPL");
index 8ad554312b69196de4d9cf056024c2e8f07907bd..24d924d563aa9753b20816db5dddab0f8441528e 100644 (file)
@@ -554,8 +554,6 @@ do_sku:
                        nid = portd;
                else if (tmp == 3)
                        nid = porti;
-               else
-                       return 1;
                if (found_in_nid_list(nid, spec->gen.autocfg.line_out_pins,
                                      spec->gen.autocfg.line_outs))
                        return 1;
@@ -579,26 +577,35 @@ static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports)
 /*
  * COEF access helper functions
  */
-static int alc_read_coef_idx(struct hda_codec *codec,
-                       unsigned int coef_idx)
+
+static int alc_read_coefex_idx(struct hda_codec *codec,
+                                       hda_nid_t nid,
+                                       unsigned int coef_idx)
 {
        unsigned int val;
-       snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX,
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX,
                                coef_idx);
-       val = snd_hda_codec_read(codec, 0x20, 0,
+       val = snd_hda_codec_read(codec, nid, 0,
                                AC_VERB_GET_PROC_COEF, 0);
        return val;
 }
 
-static void alc_write_coef_idx(struct hda_codec *codec, unsigned int coef_idx,
+#define alc_read_coef_idx(codec, coef_idx) \
+       alc_read_coefex_idx(codec, 0x20, coef_idx)
+
+static void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+                                                       unsigned int coef_idx,
                                                        unsigned int coef_val)
 {
-       snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX,
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX,
                            coef_idx);
-       snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF,
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PROC_COEF,
                            coef_val);
 }
 
+#define alc_write_coef_idx(codec, coef_idx, coef_val) \
+       alc_write_coefex_idx(codec, 0x20, coef_idx, coef_val)
+
 /* a special bypass for COEF 0; read the cached value at the second time */
 static unsigned int alc_get_coef0(struct hda_codec *codec)
 {
@@ -831,7 +838,11 @@ static inline void alc_shutup(struct hda_codec *codec)
                snd_hda_shutup_pins(codec);
 }
 
-#define alc_free       snd_hda_gen_free
+static void alc_free(struct hda_codec *codec)
+{
+       snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_FREE);
+       snd_hda_gen_free(codec);
+}
 
 #ifdef CONFIG_PM
 static void alc_power_eapd(struct hda_codec *codec)
@@ -1043,6 +1054,7 @@ enum {
        ALC880_FIXUP_UNIWILL,
        ALC880_FIXUP_UNIWILL_DIG,
        ALC880_FIXUP_Z71V,
+       ALC880_FIXUP_ASUS_W5A,
        ALC880_FIXUP_3ST_BASE,
        ALC880_FIXUP_3ST,
        ALC880_FIXUP_3ST_DIG,
@@ -1213,6 +1225,26 @@ static const struct hda_fixup alc880_fixups[] = {
                        { }
                }
        },
+       [ALC880_FIXUP_ASUS_W5A] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       /* set up the whole pins as BIOS is utterly broken */
+                       { 0x14, 0x0121411f }, /* HP */
+                       { 0x15, 0x411111f0 }, /* N/A */
+                       { 0x16, 0x411111f0 }, /* N/A */
+                       { 0x17, 0x411111f0 }, /* N/A */
+                       { 0x18, 0x90a60160 }, /* mic */
+                       { 0x19, 0x411111f0 }, /* N/A */
+                       { 0x1a, 0x411111f0 }, /* N/A */
+                       { 0x1b, 0x411111f0 }, /* N/A */
+                       { 0x1c, 0x411111f0 }, /* N/A */
+                       { 0x1d, 0x411111f0 }, /* N/A */
+                       { 0x1e, 0xb743111e }, /* SPDIF out */
+                       { }
+               },
+               .chained = true,
+               .chain_id = ALC880_FIXUP_GPIO1,
+       },
        [ALC880_FIXUP_3ST_BASE] = {
                .type = HDA_FIXUP_PINS,
                .v.pins = (const struct hda_pintbl[]) {
@@ -1334,6 +1366,7 @@ static const struct hda_fixup alc880_fixups[] = {
 
 static const struct snd_pci_quirk alc880_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810),
+       SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS W5A", ALC880_FIXUP_ASUS_W5A),
        SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_FIXUP_Z71V),
        SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_FIXUP_GPIO1),
        SND_PCI_QUIRK(0x1558, 0x5401, "Clevo GPIO2", ALC880_FIXUP_GPIO2),
@@ -2388,6 +2421,7 @@ static const struct hda_verb alc268_beep_init_verbs[] = {
 enum {
        ALC268_FIXUP_INV_DMIC,
        ALC268_FIXUP_HP_EAPD,
+       ALC268_FIXUP_SPDIF,
 };
 
 static const struct hda_fixup alc268_fixups[] = {
@@ -2402,6 +2436,13 @@ static const struct hda_fixup alc268_fixups[] = {
                        {}
                }
        },
+       [ALC268_FIXUP_SPDIF] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       { 0x1e, 0x014b1180 }, /* enable SPDIF out */
+                       {}
+               }
+       },
 };
 
 static const struct hda_model_fixup alc268_fixup_models[] = {
@@ -2411,6 +2452,7 @@ static const struct hda_model_fixup alc268_fixup_models[] = {
 };
 
 static const struct snd_pci_quirk alc268_fixup_tbl[] = {
+       SND_PCI_QUIRK(0x1025, 0x0139, "Acer TravelMate 6293", ALC268_FIXUP_SPDIF),
        SND_PCI_QUIRK(0x1025, 0x015b, "Acer AOA 150 (ZG5)", ALC268_FIXUP_INV_DMIC),
        /* below is codec SSID since multiple Toshiba laptops have the
         * same PCI SSID 1179:ff00
@@ -2539,7 +2581,9 @@ enum {
        ALC269_TYPE_ALC282,
        ALC269_TYPE_ALC283,
        ALC269_TYPE_ALC284,
+       ALC269_TYPE_ALC285,
        ALC269_TYPE_ALC286,
+       ALC269_TYPE_ALC255,
 };
 
 /*
@@ -2558,6 +2602,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
        case ALC269_TYPE_ALC269VC:
        case ALC269_TYPE_ALC280:
        case ALC269_TYPE_ALC284:
+       case ALC269_TYPE_ALC285:
                ssids = alc269va_ssids;
                break;
        case ALC269_TYPE_ALC269VB:
@@ -2565,6 +2610,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
        case ALC269_TYPE_ALC282:
        case ALC269_TYPE_ALC283:
        case ALC269_TYPE_ALC286:
+       case ALC269_TYPE_ALC255:
                ssids = alc269_ssids;
                break;
        default:
@@ -2652,7 +2698,7 @@ static void alc283_shutup(struct hda_codec *codec)
                            AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
 
        if (hp_pin_sense)
-               msleep(85);
+               msleep(100);
 
        snd_hda_codec_write(codec, hp_pin, 0,
                            AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
@@ -2661,7 +2707,7 @@ static void alc283_shutup(struct hda_codec *codec)
        alc_write_coef_idx(codec, 0x46, val | (3 << 12));
 
        if (hp_pin_sense)
-               msleep(85);
+               msleep(100);
        snd_hda_shutup_pins(codec);
        alc_write_coef_idx(codec, 0x43, 0x9614);
 }
@@ -2944,6 +2990,23 @@ static void alc269_fixup_mic_mute_hook(void *private_data, int enabled)
                snd_hda_set_pin_ctl_cache(codec, spec->mute_led_nid, pinval);
 }
 
+/* Make sure the led works even in runtime suspend */
+static unsigned int led_power_filter(struct hda_codec *codec,
+                                                 hda_nid_t nid,
+                                                 unsigned int power_state)
+{
+       struct alc_spec *spec = codec->spec;
+
+       if (power_state != AC_PWRST_D3 || nid != spec->mute_led_nid)
+               return power_state;
+
+       /* Set pin ctl again, it might have just been set to 0 */
+       snd_hda_set_pin_ctl(codec, nid,
+                           snd_hda_codec_get_pin_target(codec, nid));
+
+       return AC_PWRST_D0;
+}
+
 static void alc269_fixup_hp_mute_led(struct hda_codec *codec,
                                     const struct hda_fixup *fix, int action)
 {
@@ -2963,6 +3026,7 @@ static void alc269_fixup_hp_mute_led(struct hda_codec *codec,
                spec->mute_led_nid = pin - 0x0a + 0x18;
                spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook;
                spec->gen.vmaster_mute_enum = 1;
+               codec->power_filter = led_power_filter;
                snd_printd("Detected mute LED for %x:%d\n", spec->mute_led_nid,
                           spec->mute_led_polarity);
                break;
@@ -2978,6 +3042,7 @@ static void alc269_fixup_hp_mute_led_mic1(struct hda_codec *codec,
                spec->mute_led_nid = 0x18;
                spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook;
                spec->gen.vmaster_mute_enum = 1;
+               codec->power_filter = led_power_filter;
        }
 }
 
@@ -2990,6 +3055,7 @@ static void alc269_fixup_hp_mute_led_mic2(struct hda_codec *codec,
                spec->mute_led_nid = 0x19;
                spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook;
                spec->gen.vmaster_mute_enum = 1;
+               codec->power_filter = led_power_filter;
        }
 }
 
@@ -3052,6 +3118,19 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
        int val;
 
        switch (codec->vendor_id) {
+       case 0x10ec0255:
+               /* LDO and MISC control */
+               alc_write_coef_idx(codec, 0x1b, 0x0c0b);
+               /* UAJ function set to menual mode */
+               alc_write_coef_idx(codec, 0x45, 0xd089);
+               /* Direct Drive HP Amp control(Set to verb control)*/
+               val = alc_read_coefex_idx(codec, 0x57, 0x05);
+               alc_write_coefex_idx(codec, 0x57, 0x05, val & ~(1<<14));
+               /* Set MIC2 Vref gate with HP */
+               alc_write_coef_idx(codec, 0x06, 0x6104);
+               /* Direct Drive HP Amp control */
+               alc_write_coefex_idx(codec, 0x57, 0x03, 0x8aa6);
+               break;
        case 0x10ec0283:
                alc_write_coef_idx(codec, 0x1b, 0x0c0b);
                alc_write_coef_idx(codec, 0x45, 0xc429);
@@ -3083,6 +3162,14 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
        int val;
 
        switch (codec->vendor_id) {
+       case 0x10ec0255:
+               alc_write_coef_idx(codec, 0x45, 0xc489);
+               snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+               alc_write_coefex_idx(codec, 0x57, 0x03, 0x8aa6);
+               /* Set MIC2 Vref gate to normal */
+               alc_write_coef_idx(codec, 0x06, 0x6100);
+               snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+               break;
        case 0x10ec0283:
                alc_write_coef_idx(codec, 0x45, 0xc429);
                snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
@@ -3114,6 +3201,12 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
 static void alc_headset_mode_default(struct hda_codec *codec)
 {
        switch (codec->vendor_id) {
+       case 0x10ec0255:
+               alc_write_coef_idx(codec, 0x45, 0xc089);
+               alc_write_coef_idx(codec, 0x45, 0xc489);
+               alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6);
+               alc_write_coef_idx(codec, 0x49, 0x0049);
+               break;
        case 0x10ec0283:
                alc_write_coef_idx(codec, 0x06, 0x2100);
                alc_write_coef_idx(codec, 0x32, 0x4ea3);
@@ -3137,6 +3230,12 @@ static void alc_headset_mode_default(struct hda_codec *codec)
 static void alc_headset_mode_ctia(struct hda_codec *codec)
 {
        switch (codec->vendor_id) {
+       case 0x10ec0255:
+               /* Set to CTIA type */
+               alc_write_coef_idx(codec, 0x45, 0xd489);
+               alc_write_coef_idx(codec, 0x1b, 0x0c2b);
+               alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6);
+               break;
        case 0x10ec0283:
                alc_write_coef_idx(codec, 0x45, 0xd429);
                alc_write_coef_idx(codec, 0x1b, 0x0c2b);
@@ -3159,6 +3258,12 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
 static void alc_headset_mode_omtp(struct hda_codec *codec)
 {
        switch (codec->vendor_id) {
+       case 0x10ec0255:
+               /* Set to OMTP Type */
+               alc_write_coef_idx(codec, 0x45, 0xe489);
+               alc_write_coef_idx(codec, 0x1b, 0x0c2b);
+               alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6);
+               break;
        case 0x10ec0283:
                alc_write_coef_idx(codec, 0x45, 0xe429);
                alc_write_coef_idx(codec, 0x1b, 0x0c2b);
@@ -3184,6 +3289,15 @@ static void alc_determine_headset_type(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
 
        switch (codec->vendor_id) {
+       case 0x10ec0255:
+               /* combo jack auto switch control(Check type)*/
+               alc_write_coef_idx(codec, 0x45, 0xd089);
+               /* combo jack auto switch control(Vref conteol) */
+               alc_write_coef_idx(codec, 0x49, 0x0149);
+               msleep(300);
+               val = alc_read_coef_idx(codec, 0x46);
+               is_ctia = (val & 0x0070) == 0x0070;
+               break;
        case 0x10ec0283:
                alc_write_coef_idx(codec, 0x45, 0xd029);
                msleep(300);
@@ -3330,6 +3444,21 @@ static void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec,
                alc_fixup_headset_mode(codec, fix, action);
 }
 
+static void alc_fixup_headset_mode_alc255(struct hda_codec *codec,
+                               const struct hda_fixup *fix, int action)
+{
+       if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+               /* Set to iphone type */
+               alc_write_coef_idx(codec, 0x1b, 0x880b);
+               alc_write_coef_idx(codec, 0x45, 0xd089);
+               alc_write_coef_idx(codec, 0x1b, 0x080b);
+               alc_write_coef_idx(codec, 0x46, 0x0004);
+               alc_write_coef_idx(codec, 0x1b, 0x0c0b);
+               msleep(30);
+       }
+       alc_fixup_headset_mode(codec, fix, action);
+}
+
 static void alc_fixup_headset_mode_alc668(struct hda_codec *codec,
                                const struct hda_fixup *fix, int action)
 {
@@ -3443,7 +3572,11 @@ static void alc283_fixup_chromebook(struct hda_codec *codec,
        switch (action) {
        case HDA_FIXUP_ACT_PRE_PROBE:
                alc283_chromebook_caps(codec);
+               /* Disable AA-loopback as it causes white noise */
+               spec->gen.mixer_nid = 0;
                spec->gen.hp_automute_hook = alc283_hp_automute_hook;
+               break;
+       case HDA_FIXUP_ACT_INIT:
                /* MIC2-VREF control */
                /* Set to manual mode */
                val = alc_read_coef_idx(codec, 0x06);
@@ -3514,6 +3647,74 @@ static void alc290_fixup_mono_speakers(struct hda_codec *codec,
                snd_hda_override_wcaps(codec, 0x03, 0);
 }
 
+#if IS_ENABLED(CONFIG_THINKPAD_ACPI)
+
+#include <linux/thinkpad_acpi.h>
+
+static int (*led_set_func)(int, bool);
+
+static void update_tpacpi_mute_led(void *private_data, int enabled)
+{
+       if (led_set_func)
+               led_set_func(TPACPI_LED_MUTE, !enabled);
+}
+
+static void update_tpacpi_micmute_led(struct hda_codec *codec,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       if (!ucontrol || !led_set_func)
+               return;
+       if (strcmp("Capture Switch", ucontrol->id.name) == 0 && ucontrol->id.index == 0) {
+               /* TODO: How do I verify if it's a mono or stereo here? */
+               bool val = ucontrol->value.integer.value[0] || ucontrol->value.integer.value[1];
+               led_set_func(TPACPI_LED_MICMUTE, !val);
+       }
+}
+
+static void alc_fixup_thinkpad_acpi(struct hda_codec *codec,
+                                 const struct hda_fixup *fix, int action)
+{
+       struct alc_spec *spec = codec->spec;
+       bool removefunc = false;
+
+       if (action == HDA_FIXUP_ACT_PROBE) {
+               if (!led_set_func)
+                       led_set_func = symbol_request(tpacpi_led_set);
+               if (!led_set_func) {
+                       snd_printk(KERN_WARNING "Failed to find thinkpad-acpi symbol tpacpi_led_set\n");
+                       return;
+               }
+
+               removefunc = true;
+               if (led_set_func(TPACPI_LED_MUTE, false) >= 0) {
+                       spec->gen.vmaster_mute.hook = update_tpacpi_mute_led;
+                       removefunc = false;
+               }
+               if (led_set_func(TPACPI_LED_MICMUTE, false) >= 0) {
+                       if (spec->gen.num_adc_nids > 1)
+                               snd_printdd("Skipping micmute LED control due to several ADCs");
+                       else {
+                               spec->gen.cap_sync_hook = update_tpacpi_micmute_led;
+                               removefunc = false;
+                       }
+               }
+       }
+
+       if (led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) {
+               symbol_put(tpacpi_led_set);
+               led_set_func = NULL;
+       }
+}
+
+#else
+
+static void alc_fixup_thinkpad_acpi(struct hda_codec *codec,
+                                 const struct hda_fixup *fix, int action)
+{
+}
+
+#endif
+
 enum {
        ALC269_FIXUP_SONY_VAIO,
        ALC275_FIXUP_SONY_VAIO_GPIO2,
@@ -3552,11 +3753,15 @@ enum {
        ALC271_FIXUP_HP_GATE_MIC_JACK,
        ALC269_FIXUP_ACER_AC700,
        ALC269_FIXUP_LIMIT_INT_MIC_BOOST,
+       ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED,
        ALC269VB_FIXUP_ORDISSIMO_EVE2,
        ALC283_FIXUP_CHROME_BOOK,
        ALC282_FIXUP_ASUS_TX300,
        ALC283_FIXUP_INT_MIC,
        ALC290_FIXUP_MONO_SPEAKERS,
+       ALC269_FIXUP_THINKPAD_ACPI,
+       ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+       ALC255_FIXUP_HEADSET_MODE,
 };
 
 static const struct hda_fixup alc269_fixups[] = {
@@ -3821,6 +4026,12 @@ static const struct hda_fixup alc269_fixups[] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc269_fixup_limit_int_mic_boost,
        },
+       [ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc269_fixup_limit_int_mic_boost,
+               .chained = true,
+               .chain_id = ALC269_FIXUP_HP_MUTE_LED_MIC1,
+       },
        [ALC269VB_FIXUP_ORDISSIMO_EVE2] = {
                .type = HDA_FIXUP_PINS,
                .v.pins = (const struct hda_pintbl[]) {
@@ -3854,6 +4065,26 @@ static const struct hda_fixup alc269_fixups[] = {
                .chained = true,
                .chain_id = ALC269_FIXUP_DELL3_MIC_NO_PRESENCE,
        },
+       [ALC269_FIXUP_THINKPAD_ACPI] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc_fixup_thinkpad_acpi,
+               .chained = true,
+               .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
+       },
+       [ALC255_FIXUP_DELL1_MIC_NO_PRESENCE] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+                       { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
+                       { }
+               },
+               .chained = true,
+               .chain_id = ALC255_FIXUP_HEADSET_MODE
+       },
+       [ALC255_FIXUP_HEADSET_MODE] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc_fixup_headset_mode_alc255,
+       },
 };
 
 static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -3896,12 +4127,15 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1028, 0x0609, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0613, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_MONO_SPEAKERS),
+       SND_PCI_QUIRK(0x1028, 0x061f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
+       SND_PCI_QUIRK(0x1028, 0x063f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x15cc, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x15cd, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
        SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x1973, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x1983, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+       SND_PCI_QUIRK(0x103c, 0x218b, "HP", ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED),
        SND_PCI_QUIRK(0x103c, 0x21ed, "HP Falco Chromebook", ALC283_FIXUP_CHROME_BOOK),
        SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED),
        SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),
@@ -3937,7 +4171,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK),
        SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
-       SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+       SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_THINKPAD_ACPI),
        SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC283_FIXUP_INT_MIC),
@@ -4125,9 +4359,16 @@ static int patch_alc269(struct hda_codec *codec)
        case 0x10ec0292:
                spec->codec_variant = ALC269_TYPE_ALC284;
                break;
+       case 0x10ec0285:
+       case 0x10ec0293:
+               spec->codec_variant = ALC269_TYPE_ALC285;
+               break;
        case 0x10ec0286:
                spec->codec_variant = ALC269_TYPE_ALC286;
                break;
+       case 0x10ec0255:
+               spec->codec_variant = ALC269_TYPE_ALC255;
+               break;
        }
 
        if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) {
@@ -4415,6 +4656,25 @@ static void alc272_fixup_mario(struct hda_codec *codec,
                       "hda_codec: failed to override amp caps for NID 0x2\n");
 }
 
+static const struct snd_pcm_chmap_elem asus_pcm_2_1_chmaps[] = {
+       { .channels = 2,
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+       { .channels = 4,
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+                  SNDRV_CHMAP_NA, SNDRV_CHMAP_LFE } }, /* LFE only on right */
+       { }
+};
+
+/* override the 2.1 chmap */
+static void alc662_fixup_bass_chmap(struct hda_codec *codec,
+                                   const struct hda_fixup *fix, int action)
+{
+       if (action == HDA_FIXUP_ACT_BUILD) {
+               struct alc_spec *spec = codec->spec;
+               spec->gen.pcm_rec[0].stream[0].chmap = asus_pcm_2_1_chmaps;
+       }
+}
+
 enum {
        ALC662_FIXUP_ASPIRE,
        ALC662_FIXUP_IDEAPAD,
@@ -4435,6 +4695,7 @@ enum {
        ALC662_FIXUP_INV_DMIC,
        ALC668_FIXUP_DELL_MIC_NO_PRESENCE,
        ALC668_FIXUP_HEADSET_MODE,
+       ALC662_FIXUP_BASS_CHMAP,
 };
 
 static const struct hda_fixup alc662_fixups[] = {
@@ -4609,6 +4870,12 @@ static const struct hda_fixup alc662_fixups[] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc_fixup_headset_mode_alc668,
        },
+       [ALC662_FIXUP_BASS_CHMAP] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc662_fixup_bass_chmap,
+               .chained = true,
+               .chain_id = ALC662_FIXUP_ASUS_MODE4
+       },
 };
 
 static const struct snd_pci_quirk alc662_fixup_tbl[] = {
@@ -4621,9 +4888,10 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE),
        SND_PCI_QUIRK(0x1028, 0x05d8, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x05db, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+       SND_PCI_QUIRK(0x1028, 0x0626, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),
-       SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_ASUS_MODE4),
-       SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_ASUS_MODE4),
+       SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_BASS_CHMAP),
+       SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_BASS_CHMAP),
        SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT),
        SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2),
        SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD),
@@ -4842,6 +5110,7 @@ static int patch_alc680(struct hda_codec *codec)
 static const struct hda_codec_preset snd_hda_preset_realtek[] = {
        { .id = 0x10ec0221, .name = "ALC221", .patch = patch_alc269 },
        { .id = 0x10ec0233, .name = "ALC233", .patch = patch_alc269 },
+       { .id = 0x10ec0255, .name = "ALC255", .patch = patch_alc269 },
        { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
        { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
        { .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 },
@@ -4855,9 +5124,11 @@ static const struct hda_codec_preset snd_hda_preset_realtek[] = {
        { .id = 0x10ec0282, .name = "ALC282", .patch = patch_alc269 },
        { .id = 0x10ec0283, .name = "ALC283", .patch = patch_alc269 },
        { .id = 0x10ec0284, .name = "ALC284", .patch = patch_alc269 },
+       { .id = 0x10ec0285, .name = "ALC285", .patch = patch_alc269 },
        { .id = 0x10ec0286, .name = "ALC286", .patch = patch_alc269 },
        { .id = 0x10ec0290, .name = "ALC290", .patch = patch_alc269 },
        { .id = 0x10ec0292, .name = "ALC292", .patch = patch_alc269 },
+       { .id = 0x10ec0293, .name = "ALC293", .patch = patch_alc269 },
        { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
          .patch = patch_alc861 },
        { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },
index fba0cef1c47fff7a49632bf663402785e5cf24f8..69a549a8234507e9b4ee14576717d779d05ff6c1 100644 (file)
@@ -2091,8 +2091,10 @@ static void stac92hd83xxx_fixup_hp_mic_led(struct hda_codec *codec,
 {
        struct sigmatel_spec *spec = codec->spec;
 
-       if (action == HDA_FIXUP_ACT_PRE_PROBE)
+       if (action == HDA_FIXUP_ACT_PRE_PROBE) {
                spec->mic_mute_led_gpio = 0x08; /* GPIO3 */
+               codec->bus->avoid_link_reset = 1;
+       }
 }
 
 static void stac92hd83xxx_fixup_headset_jack(struct hda_codec *codec,
index 302ac6ddd545b646748f6b8abfe10ab592a6c027..4019cf27d1177c221cec4e45be0d7cafe6edfff4 100644 (file)
@@ -203,12 +203,12 @@ static void psc724_set_jack_state(struct snd_ice1712 *ice, bool hp_connected)
        /* notify about master speaker mute change */
        memset(&elem_id, 0, sizeof(elem_id));
        elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-       strncpy(elem_id.name, "Master Speakers Playback Switch",
+       strlcpy(elem_id.name, "Master Speakers Playback Switch",
                                                sizeof(elem_id.name));
        kctl = snd_ctl_find_id(ice->card, &elem_id);
        snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
        /* and headphone mute change */
-       strncpy(elem_id.name, spec->wm8776.ctl[WM8776_CTL_HP_SW].name,
+       strlcpy(elem_id.name, spec->wm8776.ctl[WM8776_CTL_HP_SW].name,
                                                sizeof(elem_id.name));
        kctl = snd_ctl_find_id(ice->card, &elem_id);
        snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
index 975e0357bd5a047bdf54faa4970e76a42694bd89..71c6003ef338a04d5c400b3c0c300d58cf4ddede 100644 (file)
@@ -203,6 +203,7 @@ static const char * const ext_clock_names[3] = {"IEC958 In", "Word Clock 1xFS",
 #define AK4620_DEEMVOL_REG     0x03
 #define AK4620_SMUTE           (1<<7)
 
+#ifdef CONFIG_PROC_FS
 /*
  * Conversion from int value to its binary form. Used for debugging.
  * The output buffer must be allocated prior to calling the function.
@@ -227,6 +228,7 @@ static char *get_binary(char *buffer, int value)
        buffer[pos] = '\0';
        return buffer;
 }
+#endif /* CONFIG_PROC_FS */
 
 /*
  * Initial setup of the conversion array GPIO <-> rate
index e473f8a88f9c8e95db609ee4d677c689ffb141f4..21b373b2e2600e7f91f7ee680512502f79a96479 100644 (file)
@@ -253,7 +253,8 @@ static int snd_wm8766_ctl_get(struct snd_kcontrol *kcontrol,
        }
        if (wm->ctl[n].flags & WM8766_FLAG_INVERT) {
                val1 = wm->ctl[n].max - (val1 - wm->ctl[n].min);
-               val2 = wm->ctl[n].max - (val2 - wm->ctl[n].min);
+               if (wm->ctl[n].flags & WM8766_FLAG_STEREO)
+                       val2 = wm->ctl[n].max - (val2 - wm->ctl[n].min);
        }
        ucontrol->value.integer.value[0] = val1;
        if (wm->ctl[n].flags & WM8766_FLAG_STEREO)
index a3c05fe5daf93829a0eed48204fa128d9de914fd..e66c0da62014bfd75453770a2fdc8ddec106e827 100644 (file)
@@ -52,7 +52,7 @@ static void snd_wm8776_activate_ctl(struct snd_wm8776 *wm,
        unsigned int index_offset;
 
        memset(&elem_id, 0, sizeof(elem_id));
-       strncpy(elem_id.name, ctl_name, sizeof(elem_id.name));
+       strlcpy(elem_id.name, ctl_name, sizeof(elem_id.name));
        elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
        kctl = snd_ctl_find_id(card, &elem_id);
        if (!kctl)
@@ -526,7 +526,8 @@ static int snd_wm8776_ctl_get(struct snd_kcontrol *kcontrol,
        }
        if (wm->ctl[n].flags & WM8776_FLAG_INVERT) {
                val1 = wm->ctl[n].max - (val1 - wm->ctl[n].min);
-               val2 = wm->ctl[n].max - (val2 - wm->ctl[n].min);
+               if (wm->ctl[n].flags & WM8776_FLAG_STEREO)
+                       val2 = wm->ctl[n].max - (val2 - wm->ctl[n].min);
        }
        ucontrol->value.integer.value[0] = val1;
        if (wm->ctl[n].flags & WM8776_FLAG_STEREO)
index 59c8aaebb91ea4106bf315139603af307f13b25f..08d8733604a2ed981d1c5ac32e095c452e56a431 100644 (file)
@@ -1541,17 +1541,16 @@ static int snd_intel8x0_pcm1(struct intel8x0 *chip, int device,
                                              snd_dma_pci_data(chip->pci),
                                              rec->prealloc_size, rec->prealloc_max_size);
 
-       if (rec->ac97_idx == ICHD_PCMOUT && rec->playback_ops) {
+       if (rec->playback_ops &&
+           rec->playback_ops->open == snd_intel8x0_playback_open) {
                struct snd_pcm_chmap *chmap;
                int chs = 2;
-               if (rec->ac97_idx == ICHD_PCMOUT) {
-                       if (chip->multi8)
-                               chs = 8;
-                       else if (chip->multi6)
-                               chs = 6;
-                       else if (chip->multi4)
-                               chs = 4;
-               }
+               if (chip->multi8)
+                       chs = 8;
+               else if (chip->multi6)
+                       chs = 6;
+               else if (chip->multi4)
+                       chs = 4;
                err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
                                             snd_pcm_alt_chmaps, chs, 0,
                                             &chmap);
index 7307d97186cb45c66f7584f9857c559533038f72..0568540dc8d353995879ba6ab26be56360b164e4 100644 (file)
@@ -463,7 +463,7 @@ static int lola_parse_tree(struct lola *chip)
 
        err = lola_read_param(chip, 1, LOLA_PAR_FUNCTION_TYPE, &val);
        if (err < 0) {
-               printk(KERN_ERR SFX "Can't read FUNCTION_TYPE for 0x%x\n", nid);
+               printk(KERN_ERR SFX "Can't read FUNCTION_TYPE\n");
                return err;
        }
        if (val != 1) {
index 3230e57f246c32c362aeab200200e78100a7f919..5fcaaa6da4a811b1bc1ae3e4a6fe431b90b378de 100644 (file)
@@ -453,8 +453,8 @@ static void lx_trigger_start(struct lx6464es *chip, struct lx_stream *lx_stream)
                                     lower_32_bits(buf), upper_32_bits(buf),
                                     &buffer_index);
 
-               snd_printdd(LXP "starting: buffer index %x on %p (%d bytes)\n",
-                           buffer_index, (void *)buf, period_bytes);
+               snd_printdd(LXP "starting: buffer index %x on 0x%lx (%d bytes)\n",
+                           buffer_index, (unsigned long)buf, period_bytes);
                buf += period_bytes;
        }
 
index 633c8607d05374c7995d4a61af918d9f4b99eca9..626ecad4dae78bb95fd238f97cc299c4c2b03761 100644 (file)
@@ -1191,8 +1191,8 @@ static int lx_interrupt_request_new_buffer(struct lx6464es *chip,
        unpack_pointer(buf, &buf_lo, &buf_hi);
        err = lx_buffer_give(chip, 0, is_capture, period_bytes, buf_lo, buf_hi,
                             &buffer_index);
-       snd_printdd(LXP "interrupt: gave buffer index %x on %p (%d bytes)\n",
-                   buffer_index, (void *)buf, period_bytes);
+       snd_printdd(LXP "interrupt: gave buffer index %x on 0x%lx (%d bytes)\n",
+                   buffer_index, (unsigned long)buf, period_bytes);
 
        lx_stream->frame_pos = next_pos;
        spin_unlock_irqrestore(&chip->lock, flags);
index bb9ebc5543d7a61ff639bbf91d37957d5fdf9086..0236363c301f8e12b7f15a218af5fbcdfb06b25c 100644 (file)
@@ -350,9 +350,8 @@ snd_rme96_playback_copy(struct snd_pcm_substream *substream,
        struct rme96 *rme96 = snd_pcm_substream_chip(substream);
        count <<= rme96->playback_frlog;
        pos <<= rme96->playback_frlog;
-       copy_from_user_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, src,
-                           count);
-       return 0;
+       return copy_from_user_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, src,
+                                  count);
 }
 
 static int
@@ -365,9 +364,8 @@ snd_rme96_capture_copy(struct snd_pcm_substream *substream,
        struct rme96 *rme96 = snd_pcm_substream_chip(substream);
        count <<= rme96->capture_frlog;
        pos <<= rme96->capture_frlog;
-       copy_to_user_fromio(dst, rme96->iobase + RME96_IO_REC_BUFFER + pos,
-                           count);
-        return 0;
+       return copy_to_user_fromio(dst, rme96->iobase + RME96_IO_REC_BUFFER + pos,
+                                  count);
 }
 
 /*
index 3cde55b753e26086c13569dbee92847db02c8a9c..e98dc008de0b91e4b7e73f776081893fafa04084 100644 (file)
@@ -3996,7 +3996,6 @@ static int hdspm_tco_sync_check(struct hdspm *hdspm)
                                        return 1;
                        }
                        return 0;
-                       break;
                case AES32:
                        status = hdspm_read(hdspm, HDSPM_statusRegister);
                        if (status & HDSPM_tcoLockAes) {
@@ -4006,9 +4005,6 @@ static int hdspm_tco_sync_check(struct hdspm *hdspm)
                                        return 1;
                        }
                        return 0;
-
-                       break;
-
                case RayDAT:
                case AIO:
                        status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
@@ -4018,7 +4014,6 @@ static int hdspm_tco_sync_check(struct hdspm *hdspm)
                        if (status & 0x4000000)
                                return 1; /* Lock */
                        return 0; /* No signal */
-                       break;
 
                default:
                        break;
@@ -6405,7 +6400,7 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
                memset(&hdspm_version, 0, sizeof(hdspm_version));
 
                hdspm_version.card_type = hdspm->io_type;
-               strncpy(hdspm_version.cardname, hdspm->card_name,
+               strlcpy(hdspm_version.cardname, hdspm->card_name,
                                sizeof(hdspm_version.cardname));
                hdspm_version.serial = hdspm->serial;
                hdspm_version.firmware_rev = hdspm->firmware_rev;
index 8c7dcbe0118dbce433c0dd7850f5ad467d90a8d4..ebb76f2d90d790175cbb35b93ffa1213f258a299 100644 (file)
@@ -933,8 +933,10 @@ static int snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
        int i, ret;
        u64 lpar_addr, lpar_size;
 
-       BUG_ON(!firmware_has_feature(FW_FEATURE_PS3_LV1));
-       BUG_ON(dev->match_id != PS3_MATCH_ID_SOUND);
+       if (WARN_ON(!firmware_has_feature(FW_FEATURE_PS3_LV1)))
+               return -ENODEV;
+       if (WARN_ON(dev->match_id != PS3_MATCH_ID_SOUND))
+               return -ENODEV;
 
        the_card.ps3_dev = dev;
 
index bb53dea85b17eefc55090dfb4190ee5863337a2d..8697cedccd21240f76b4b5076ba6d0968a394d75 100644 (file)
@@ -777,7 +777,7 @@ static int asoc_ssc_init(struct device *dev)
        if (ret) {
                dev_err(dev, "Could not register PCM: %d\n", ret);
                goto err_unregister_dai;
-       };
+       }
 
        return 0;
 
index 5f9af1fb76e862a1fb2eb4e73176d47cccf4aef8..49cc5f6d6dba41cd1d45712fc15037d8031ddaf5 100644 (file)
@@ -328,7 +328,7 @@ static int ak4641_i2s_hw_params(struct snd_pcm_substream *substream,
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                ak4641->playback_fs = rate;
                ak4641_set_deemph(codec);
-       };
+       }
 
        return 0;
 }
index f5472adee674821435eb46d28c3149658c77a94d..bae60164c7b7355408ab77676daa680bcb5946b0 100644 (file)
@@ -343,7 +343,7 @@ static int mc13783_set_tdm_slot_dac(struct snd_soc_dai *dai,
                break;
        default:
                return -EINVAL;
-       };
+       }
 
        snd_soc_update_bits(codec, MC13783_SSI_NETWORK, mask, val);
 
index fe4d29d885646ab4dd207a167e98a2e2997ade08..a895a5e4bdf207eee6387bc00e8452ae2385233d 100644 (file)
@@ -432,7 +432,7 @@ static int tas5086_hw_params(struct snd_pcm_substream *substream,
        default:
                dev_err(codec->dev, "Invalid bit width\n");
                return -EINVAL;
-       };
+       }
 
        ret = regmap_write(priv->regmap, TAS5086_SERIAL_DATA_IF, val);
        if (ret < 0)
index 5738c19ef147e335905ddb3ef3a426eb3bfcbea8..dcade130157f3433d88ddbf7db7c311c43ea8042 100644 (file)
@@ -59,31 +59,31 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
 /* dapm power sequences - make this per codec in the future */
 static int dapm_up_seq[] = {
        [snd_soc_dapm_pre] = 0,
-       [snd_soc_dapm_supply] = 1,
        [snd_soc_dapm_regulator_supply] = 1,
        [snd_soc_dapm_clock_supply] = 1,
-       [snd_soc_dapm_micbias] = 2,
+       [snd_soc_dapm_supply] = 2,
+       [snd_soc_dapm_micbias] = 3,
        [snd_soc_dapm_dai_link] = 2,
-       [snd_soc_dapm_dai_in] = 3,
-       [snd_soc_dapm_dai_out] = 3,
-       [snd_soc_dapm_aif_in] = 3,
-       [snd_soc_dapm_aif_out] = 3,
-       [snd_soc_dapm_mic] = 4,
-       [snd_soc_dapm_mux] = 5,
-       [snd_soc_dapm_virt_mux] = 5,
-       [snd_soc_dapm_value_mux] = 5,
-       [snd_soc_dapm_dac] = 6,
-       [snd_soc_dapm_switch] = 7,
-       [snd_soc_dapm_mixer] = 7,
-       [snd_soc_dapm_mixer_named_ctl] = 7,
-       [snd_soc_dapm_pga] = 8,
-       [snd_soc_dapm_adc] = 9,
-       [snd_soc_dapm_out_drv] = 10,
-       [snd_soc_dapm_hp] = 10,
-       [snd_soc_dapm_spk] = 10,
-       [snd_soc_dapm_line] = 10,
-       [snd_soc_dapm_kcontrol] = 11,
-       [snd_soc_dapm_post] = 12,
+       [snd_soc_dapm_dai_in] = 4,
+       [snd_soc_dapm_dai_out] = 4,
+       [snd_soc_dapm_aif_in] = 4,
+       [snd_soc_dapm_aif_out] = 4,
+       [snd_soc_dapm_mic] = 5,
+       [snd_soc_dapm_mux] = 6,
+       [snd_soc_dapm_virt_mux] = 6,
+       [snd_soc_dapm_value_mux] = 6,
+       [snd_soc_dapm_dac] = 7,
+       [snd_soc_dapm_switch] = 8,
+       [snd_soc_dapm_mixer] = 8,
+       [snd_soc_dapm_mixer_named_ctl] = 8,
+       [snd_soc_dapm_pga] = 9,
+       [snd_soc_dapm_adc] = 10,
+       [snd_soc_dapm_out_drv] = 11,
+       [snd_soc_dapm_hp] = 11,
+       [snd_soc_dapm_spk] = 11,
+       [snd_soc_dapm_line] = 11,
+       [snd_soc_dapm_kcontrol] = 12,
+       [snd_soc_dapm_post] = 13,
 };
 
 static int dapm_down_seq[] = {
@@ -109,10 +109,10 @@ static int dapm_down_seq[] = {
        [snd_soc_dapm_dai_in] = 10,
        [snd_soc_dapm_dai_out] = 10,
        [snd_soc_dapm_dai_link] = 11,
-       [snd_soc_dapm_clock_supply] = 12,
-       [snd_soc_dapm_regulator_supply] = 12,
        [snd_soc_dapm_supply] = 12,
-       [snd_soc_dapm_post] = 13,
+       [snd_soc_dapm_clock_supply] = 13,
+       [snd_soc_dapm_regulator_supply] = 13,
+       [snd_soc_dapm_post] = 14,
 };
 
 static void pop_wait(u32 pop_time)
@@ -409,6 +409,12 @@ static inline void soc_widget_unlock(struct snd_soc_dapm_widget *w)
                mutex_unlock(&w->platform->mutex);
 }
 
+static void soc_dapm_async_complete(struct snd_soc_dapm_context *dapm)
+{
+       if (dapm->codec && dapm->codec->using_regmap)
+               regmap_async_complete(dapm->codec->control_data);
+}
+
 static int soc_widget_update_bits_locked(struct snd_soc_dapm_widget *w,
        unsigned short reg, unsigned int mask, unsigned int value)
 {
@@ -417,8 +423,9 @@ static int soc_widget_update_bits_locked(struct snd_soc_dapm_widget *w,
        int ret;
 
        if (w->codec && w->codec->using_regmap) {
-               ret = regmap_update_bits_check(w->codec->control_data,
-                                              reg, mask, value, &change);
+               ret = regmap_update_bits_check_async(w->codec->control_data,
+                                                    reg, mask, value,
+                                                    &change);
                if (ret != 0)
                        return ret;
        } else {
@@ -1201,6 +1208,8 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w,
 {
        int ret;
 
+       soc_dapm_async_complete(w->dapm);
+
        if (SND_SOC_DAPM_EVENT_ON(event)) {
                if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) {
                        ret = regulator_allow_bypass(w->regulator, false);
@@ -1234,6 +1243,8 @@ int dapm_clock_event(struct snd_soc_dapm_widget *w,
        if (!w->clk)
                return -EIO;
 
+       soc_dapm_async_complete(w->dapm);
+
 #ifdef CONFIG_HAVE_CLK
        if (SND_SOC_DAPM_EVENT_ON(event)) {
                return clk_prepare_enable(w->clk);
@@ -1426,6 +1437,7 @@ static void dapm_seq_check_event(struct snd_soc_card *card,
        if (w->event && (w->event_flags & event)) {
                pop_dbg(w->dapm->dev, card->pop_time, "pop test : %s %s\n",
                        w->name, ev_name);
+               soc_dapm_async_complete(w->dapm);
                trace_snd_soc_dapm_widget_event_start(w, event);
                ret = w->event(w, NULL, event);
                trace_snd_soc_dapm_widget_event_done(w, event);
@@ -1498,6 +1510,7 @@ static void dapm_seq_run(struct snd_soc_card *card,
        struct list_head *list, int event, bool power_up)
 {
        struct snd_soc_dapm_widget *w, *n;
+       struct snd_soc_dapm_context *d;
        LIST_HEAD(pending);
        int cur_sort = -1;
        int cur_subseq = -1;
@@ -1528,6 +1541,9 @@ static void dapm_seq_run(struct snd_soc_card *card,
                                                                       cur_subseq);
                        }
 
+                       if (cur_dapm && w->dapm != cur_dapm)
+                               soc_dapm_async_complete(cur_dapm);
+
                        INIT_LIST_HEAD(&pending);
                        cur_sort = -1;
                        cur_subseq = INT_MIN;
@@ -1586,6 +1602,10 @@ static void dapm_seq_run(struct snd_soc_card *card,
                                cur_dapm->seq_notifier(cur_dapm,
                                                       i, cur_subseq);
        }
+
+       list_for_each_entry(d, &card->dapm_list, list) {
+               soc_dapm_async_complete(d);
+       }
 }
 
 static void dapm_widget_update(struct snd_soc_card *card)
index 6ad4c7a47f5da0f3513e9c3c962a4435420fa1f5..cbc9c96ce1f412123a1704b171220a2990ad503d 100644 (file)
@@ -230,7 +230,7 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
                }
 
                ret = snd_pcm_lib_preallocate_pages(substream,
-                               SNDRV_DMA_TYPE_DEV,
+                               SNDRV_DMA_TYPE_DEV_IRAM,
                                dmaengine_dma_dev(pcm, substream),
                                prealloc_buffer_size,
                                max_buffer_size);
index 71358e3b54d93ef10c3e402d98f66773b81d27fc..23d43dac91da2c0d9c6ad8bbf792036483da3fab 100644 (file)
@@ -65,6 +65,7 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask)
        struct snd_soc_codec *codec;
        struct snd_soc_dapm_context *dapm;
        struct snd_soc_jack_pin *pin;
+       unsigned int sync = 0;
        int enable;
 
        trace_snd_soc_jack_report(jack, mask, status);
@@ -92,12 +93,16 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask)
                        snd_soc_dapm_enable_pin(dapm, pin->pin);
                else
                        snd_soc_dapm_disable_pin(dapm, pin->pin);
+
+               /* we need to sync for this case only */
+               sync = 1;
        }
 
        /* Report before the DAPM sync to help users updating micbias status */
        blocking_notifier_call_chain(&jack->notifier, jack->status, jack);
 
-       snd_soc_dapm_sync(dapm);
+       if (sync)
+               snd_soc_dapm_sync(dapm);
 
        snd_jack_report(jack->jack, jack->status);
 
index 52af7f6fb37f6938be2845b70cf55d69a7fd12e3..364bf6a907e1c39a36df89ded1d6b2681172940f 100644 (file)
@@ -297,7 +297,7 @@ static bool tegra20_i2s_wr_rd_reg(struct device *dev, unsigned int reg)
                return true;
        default:
                return false;
-       };
+       }
 }
 
 static bool tegra20_i2s_volatile_reg(struct device *dev, unsigned int reg)
@@ -310,7 +310,7 @@ static bool tegra20_i2s_volatile_reg(struct device *dev, unsigned int reg)
                return true;
        default:
                return false;
-       };
+       }
 }
 
 static bool tegra20_i2s_precious_reg(struct device *dev, unsigned int reg)
@@ -321,7 +321,7 @@ static bool tegra20_i2s_precious_reg(struct device *dev, unsigned int reg)
                return true;
        default:
                return false;
-       };
+       }
 }
 
 static const struct regmap_config tegra20_i2s_regmap_config = {
index 551b3c93ce932c77e9e05de4bcf01f5af63d1c94..08bc6931c7c7fc0477703037098a8e7185d7c69d 100644 (file)
@@ -213,7 +213,7 @@ static bool tegra20_spdif_wr_rd_reg(struct device *dev, unsigned int reg)
                return true;
        default:
                return false;
-       };
+       }
 }
 
 static bool tegra20_spdif_volatile_reg(struct device *dev, unsigned int reg)
@@ -234,7 +234,7 @@ static bool tegra20_spdif_volatile_reg(struct device *dev, unsigned int reg)
                return true;
        default:
                return false;
-       };
+       }
 }
 
 static bool tegra20_spdif_precious_reg(struct device *dev, unsigned int reg)
@@ -247,7 +247,7 @@ static bool tegra20_spdif_precious_reg(struct device *dev, unsigned int reg)
                return true;
        default:
                return false;
-       };
+       }
 }
 
 static const struct regmap_config tegra20_spdif_regmap_config = {
index bdd19db4a08b79e630ae81b2d9c1cb3cdca5ec78..31154338c1eb742da6a1342310d81fddb1f9ecd4 100644 (file)
@@ -360,7 +360,7 @@ static bool tegra30_ahub_apbif_wr_rd_reg(struct device *dev, unsigned int reg)
                return true;
        default:
                break;
-       };
+       }
 
        if (REG_IN_ARRAY(reg, CHANNEL_CTRL) ||
            REG_IN_ARRAY(reg, CHANNEL_CLEAR) ||
@@ -395,7 +395,7 @@ static bool tegra30_ahub_apbif_volatile_reg(struct device *dev,
                return true;
        default:
                break;
-       };
+       }
 
        if (REG_IN_ARRAY(reg, CHANNEL_CLEAR) ||
            REG_IN_ARRAY(reg, CHANNEL_STATUS) ||
index 5f20b695eba2d52b7e55917816a19a9b5097ed67..231a785b3921a5bd95d87914a92e7f5f2d266a63 100644 (file)
@@ -376,7 +376,7 @@ static bool tegra30_i2s_wr_rd_reg(struct device *dev, unsigned int reg)
                return true;
        default:
                return false;
-       };
+       }
 }
 
 static bool tegra30_i2s_volatile_reg(struct device *dev, unsigned int reg)
@@ -389,7 +389,7 @@ static bool tegra30_i2s_volatile_reg(struct device *dev, unsigned int reg)
                return true;
        default:
                return false;
-       };
+       }
 }
 
 static const struct regmap_config tegra30_i2s_regmap_config = {
index 54aaad2a10f53aa87a42fb01a1e74491ec01b9b2..b47f6fe6277fd72aadfeadda3eddb623a3215291 100644 (file)
@@ -429,7 +429,8 @@ static void snd_cs4231_advance_dma(struct cs4231_dma_control *dma_cont,
                unsigned int period_size = snd_pcm_lib_period_bytes(substream);
                unsigned int offset = period_size * (*periods_sent);
 
-               BUG_ON(period_size >= (1 << 24));
+               if (WARN_ON(period_size >= (1 << 24)))
+                       return;
 
                if (dma_cont->request(dma_cont,
                                      runtime->dma_addr + offset, period_size))
@@ -912,7 +913,8 @@ static int snd_cs4231_playback_prepare(struct snd_pcm_substream *substream)
        chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE |
                                            CS4231_PLAYBACK_PIO);
 
-       BUG_ON(runtime->period_size > 0xffff + 1);
+       if (WARN_ON(runtime->period_size > 0xffff + 1))
+               return -EINVAL;
 
        chip->p_periods_sent = 0;
        spin_unlock_irqrestore(&chip->lock, flags);
index c39c77978468b9270fd117d6d055ecc2e021fdb3..66edc4a7917f9bd08604f85ed98bc988d8e5d9f3 100644 (file)
@@ -101,7 +101,7 @@ static int usb6fire_chip_probe(struct usb_interface *intf,
                        usb_set_intfdata(intf, chips[i]);
                        mutex_unlock(&register_mutex);
                        return 0;
-               } else if (regidx < 0)
+               } else if (!devices[i] && regidx < 0)
                        regidx = i;
        }
        if (regidx < 0) {
index ae6b50f9ed56a6c2696d38acee7f8f038d3c9a4c..f65fc0987cfb8cc9bdc45ce366a6c3bfa8641f55 100644 (file)
@@ -28,6 +28,7 @@
 #include "control.h"
 
 #define CNT_INTVAL 0x10000
+#define MASCHINE_BANK_SIZE 32
 
 static int control_info(struct snd_kcontrol *kcontrol,
                        struct snd_ctl_elem_info *uinfo)
@@ -105,6 +106,10 @@ static int control_put(struct snd_kcontrol *kcontrol,
                USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1))
                cmd = EP1_CMD_DIMM_LEDS;
 
+       if (cdev->chip.usb_id ==
+               USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER))
+               cmd = EP1_CMD_DIMM_LEDS;
+
        if (pos & CNT_INTVAL) {
                int i = pos & ~CNT_INTVAL;
 
@@ -121,6 +126,20 @@ static int control_put(struct snd_kcontrol *kcontrol,
                                     usb_sndbulkpipe(cdev->chip.dev, 8),
                                     cdev->ep8_out_buf, sizeof(cdev->ep8_out_buf),
                                     &actual_len, 200);
+               } else if (cdev->chip.usb_id ==
+                       USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER)) {
+
+                       int bank = 0;
+                       int offset = 0;
+
+                       if (i >= MASCHINE_BANK_SIZE) {
+                               bank = 0x1e;
+                               offset = MASCHINE_BANK_SIZE;
+                       }
+
+                       snd_usb_caiaq_send_command_bank(cdev, cmd, bank,
+                                       cdev->control_state + offset,
+                                       MASCHINE_BANK_SIZE);
                } else {
                        snd_usb_caiaq_send_command(cdev, cmd,
                                        cdev->control_state, sizeof(cdev->control_state));
@@ -490,6 +509,74 @@ static struct caiaq_controller kontrols4_controller[] = {
        { "LED: FX2: Mode",                     133 | CNT_INTVAL },
 };
 
+static struct caiaq_controller maschine_controller[] = {
+       { "LED: Pad 1",                         3  | CNT_INTVAL },
+       { "LED: Pad 2",                         2  | CNT_INTVAL },
+       { "LED: Pad 3",                         1  | CNT_INTVAL },
+       { "LED: Pad 4",                         0  | CNT_INTVAL },
+       { "LED: Pad 5",                         7  | CNT_INTVAL },
+       { "LED: Pad 6",                         6  | CNT_INTVAL },
+       { "LED: Pad 7",                         5  | CNT_INTVAL },
+       { "LED: Pad 8",                         4  | CNT_INTVAL },
+       { "LED: Pad 9",                         11 | CNT_INTVAL },
+       { "LED: Pad 10",                        10 | CNT_INTVAL },
+       { "LED: Pad 11",                        9  | CNT_INTVAL },
+       { "LED: Pad 12",                        8  | CNT_INTVAL },
+       { "LED: Pad 13",                        15 | CNT_INTVAL },
+       { "LED: Pad 14",                        14 | CNT_INTVAL },
+       { "LED: Pad 15",                        13 | CNT_INTVAL },
+       { "LED: Pad 16",                        12 | CNT_INTVAL },
+
+       { "LED: Mute",                          16 | CNT_INTVAL },
+       { "LED: Solo",                          17 | CNT_INTVAL },
+       { "LED: Select",                        18 | CNT_INTVAL },
+       { "LED: Duplicate",                     19 | CNT_INTVAL },
+       { "LED: Navigate",                      20 | CNT_INTVAL },
+       { "LED: Pad Mode",                      21 | CNT_INTVAL },
+       { "LED: Pattern",                       22 | CNT_INTVAL },
+       { "LED: Scene",                         23 | CNT_INTVAL },
+
+       { "LED: Shift",                         24 | CNT_INTVAL },
+       { "LED: Erase",                         25 | CNT_INTVAL },
+       { "LED: Grid",                          26 | CNT_INTVAL },
+       { "LED: Right Bottom",                  27 | CNT_INTVAL },
+       { "LED: Rec",                           28 | CNT_INTVAL },
+       { "LED: Play",                          29 | CNT_INTVAL },
+       { "LED: Left Bottom",                   32 | CNT_INTVAL },
+       { "LED: Restart",                       33 | CNT_INTVAL },
+
+       { "LED: Group A",                       41 | CNT_INTVAL },
+       { "LED: Group B",                       40 | CNT_INTVAL },
+       { "LED: Group C",                       37 | CNT_INTVAL },
+       { "LED: Group D",                       36 | CNT_INTVAL },
+       { "LED: Group E",                       39 | CNT_INTVAL },
+       { "LED: Group F",                       38 | CNT_INTVAL },
+       { "LED: Group G",                       35 | CNT_INTVAL },
+       { "LED: Group H",                       34 | CNT_INTVAL },
+
+       { "LED: Auto Write",                    42 | CNT_INTVAL },
+       { "LED: Snap",                          43 | CNT_INTVAL },
+       { "LED: Right Top",                     44 | CNT_INTVAL },
+       { "LED: Left Top",                      45 | CNT_INTVAL },
+       { "LED: Sampling",                      46 | CNT_INTVAL },
+       { "LED: Browse",                        47 | CNT_INTVAL },
+       { "LED: Step",                          48 | CNT_INTVAL },
+       { "LED: Control",                       49 | CNT_INTVAL },
+
+       { "LED: Top Button 1",                  57 | CNT_INTVAL },
+       { "LED: Top Button 2",                  56 | CNT_INTVAL },
+       { "LED: Top Button 3",                  55 | CNT_INTVAL },
+       { "LED: Top Button 4",                  54 | CNT_INTVAL },
+       { "LED: Top Button 5",                  53 | CNT_INTVAL },
+       { "LED: Top Button 6",                  52 | CNT_INTVAL },
+       { "LED: Top Button 7",                  51 | CNT_INTVAL },
+       { "LED: Top Button 8",                  50 | CNT_INTVAL },
+
+       { "LED: Note Repeat",                   58 | CNT_INTVAL },
+
+       { "Backlight Display",                  59 | CNT_INTVAL }
+};
+
 static int add_controls(struct caiaq_controller *c, int num,
                        struct snd_usb_caiaqdev *cdev)
 {
@@ -553,6 +640,11 @@ int snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *cdev)
                ret = add_controls(kontrols4_controller,
                        ARRAY_SIZE(kontrols4_controller), cdev);
                break;
+
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER):
+               ret = add_controls(maschine_controller,
+                       ARRAY_SIZE(maschine_controller), cdev);
+               break;
        }
 
        return ret;
index 1a61dd12fe38a9bb881e462f396e92f43a048e36..bc55f708a696d11a3302e87557fa8dfef15ac5f0 100644 (file)
@@ -235,6 +235,31 @@ int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *cdev,
                           cdev->ep1_out_buf, len+1, &actual_len, 200);
 }
 
+int snd_usb_caiaq_send_command_bank(struct snd_usb_caiaqdev *cdev,
+                              unsigned char command,
+                              unsigned char bank,
+                              const unsigned char *buffer,
+                              int len)
+{
+       int actual_len;
+       struct usb_device *usb_dev = cdev->chip.dev;
+
+       if (!usb_dev)
+               return -EIO;
+
+       if (len > EP1_BUFSIZE - 2)
+               len = EP1_BUFSIZE - 2;
+
+       if (buffer && len > 0)
+               memcpy(cdev->ep1_out_buf+2, buffer, len);
+
+       cdev->ep1_out_buf[0] = command;
+       cdev->ep1_out_buf[1] = bank;
+
+       return usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, 1),
+                          cdev->ep1_out_buf, len+2, &actual_len, 200);
+}
+
 int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *cdev,
                                    int rate, int depth, int bpp)
 {
index ad102fac694221b89a7b0d95f49a5aa225ae8ba9..ab0f7520a99be7e6f835817d31951d19084f5ea3 100644 (file)
@@ -128,5 +128,10 @@ int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *cdev,
                               unsigned char command,
                               const unsigned char *buffer,
                               int len);
+int snd_usb_caiaq_send_command_bank(struct snd_usb_caiaqdev *cdev,
+                              unsigned char command,
+                              unsigned char bank,
+                              const unsigned char *buffer,
+                              int len);
 
 #endif /* CAIAQ_DEVICE_H */
index 64952e2d3ed1e94a12ad9e593a15298e8eaceee7..d979050e6a6afbc7daf9b65f666c2de10dbb6269 100644 (file)
@@ -79,7 +79,6 @@ static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card *
 /* Vendor/product IDs for this card */
 static int vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 };
 static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 };
-static int nrpacks = 8;                /* max. number of packets per urb */
 static int device_setup[SNDRV_CARDS]; /* device parameter for this card */
 static bool ignore_ctl_error;
 static bool autoclock = true;
@@ -94,8 +93,6 @@ module_param_array(vid, int, NULL, 0444);
 MODULE_PARM_DESC(vid, "Vendor ID for the USB audio device.");
 module_param_array(pid, int, NULL, 0444);
 MODULE_PARM_DESC(pid, "Product ID for the USB audio device.");
-module_param(nrpacks, int, 0644);
-MODULE_PARM_DESC(nrpacks, "Max. number of packets per URB.");
 module_param_array(device_setup, int, NULL, 0444);
 MODULE_PARM_DESC(device_setup, "Specific device setup (if needed).");
 module_param(ignore_ctl_error, bool, 0444);
@@ -349,6 +346,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
        case USB_SPEED_LOW:
        case USB_SPEED_FULL:
        case USB_SPEED_HIGH:
+       case USB_SPEED_WIRELESS:
        case USB_SPEED_SUPER:
                break;
        default:
@@ -374,7 +372,6 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
        chip->dev = dev;
        chip->card = card;
        chip->setup = device_setup[idx];
-       chip->nrpacks = nrpacks;
        chip->autoclock = autoclock;
        chip->probing = 1;
 
@@ -754,19 +751,4 @@ static struct usb_driver usb_audio_driver = {
        .supports_autosuspend = 1,
 };
 
-static int __init snd_usb_audio_init(void)
-{
-       if (nrpacks < 1 || nrpacks > MAX_PACKS) {
-               printk(KERN_WARNING "invalid nrpacks value.\n");
-               return -EINVAL;
-       }
-       return usb_register(&usb_audio_driver);
-}
-
-static void __exit snd_usb_audio_cleanup(void)
-{
-       usb_deregister(&usb_audio_driver);
-}
-
-module_init(snd_usb_audio_init);
-module_exit(snd_usb_audio_cleanup);
+module_usb_driver(usb_audio_driver);
index 5ecacaa90b53fc163c9383b18100bb1ce97f1dfe..9867ab866857260df9432b4378d5ba87c1d90834 100644 (file)
@@ -2,11 +2,11 @@
 #define __USBAUDIO_CARD_H
 
 #define MAX_NR_RATES   1024
-#define MAX_PACKS      20
+#define MAX_PACKS      6               /* per URB */
 #define MAX_PACKS_HS   (MAX_PACKS * 8) /* in high speed mode */
-#define MAX_URBS       8
+#define MAX_URBS       12
 #define SYNC_URBS      4       /* always four urbs for sync */
-#define MAX_QUEUE      24      /* try not to exceed this queue length, in ms */
+#define MAX_QUEUE      18      /* try not to exceed this queue length, in ms */
 
 struct audioformat {
        struct list_head list;
@@ -87,6 +87,7 @@ struct snd_usb_endpoint {
        unsigned int phase;             /* phase accumulator */
        unsigned int maxpacksize;       /* max packet size in bytes */
        unsigned int maxframesize;      /* max packet size in frames */
+       unsigned int max_urb_frames;    /* max URB size in frames */
        unsigned int curpacksize;       /* current packet size in bytes (for capture) */
        unsigned int curframesize;      /* current packet size in frames (for capture) */
        unsigned int syncmaxsize;       /* sync endpoint packet size */
@@ -95,7 +96,7 @@ struct snd_usb_endpoint {
        unsigned int syncinterval;      /* P for adaptive mode, 0 otherwise */
        unsigned char silence_value;
        unsigned int stride;
-       int iface, alt_idx;
+       int iface, altsetting;
        int skip_packets;               /* quirks for devices to ignore the first n packets
                                           in a stream */
 
@@ -116,6 +117,8 @@ struct snd_usb_substream {
        unsigned int channels_max;      /* max channels in the all audiofmts */
        unsigned int cur_rate;          /* current rate (for hw_params callback) */
        unsigned int period_bytes;      /* current period bytes (for hw_params callback) */
+       unsigned int period_frames;     /* current frames per period */
+       unsigned int buffer_periods;    /* current periods per buffer */
        unsigned int altset_idx;     /* USB data format: index of alternate setting */
        unsigned int txfr_quirk:1;      /* allow sub-frame alignment */
        unsigned int fmt_type;          /* USB audio format type (1-3) */
@@ -125,6 +128,7 @@ struct snd_usb_substream {
 
        unsigned int hwptr_done;        /* processed byte position in the buffer */
        unsigned int transfer_done;             /* processed frames since last period update */
+       unsigned int frame_limit;       /* limits number of packets in URB */
 
        /* data and sync endpoints for this stream */
        unsigned int ep_num;            /* the endpoint number */
index 93e970f2b3c0ad4d58957faed6b76ec2a1fb2991..b9ba0fcc45df10d4151bb39f8a5d6284dc8ec23e 100644 (file)
@@ -33,7 +33,6 @@
 #include "pcm.h"
 #include "quirks.h"
 
-#define EP_FLAG_ACTIVATED      0
 #define EP_FLAG_RUNNING                1
 #define EP_FLAG_STOPPING       2
 
@@ -426,9 +425,9 @@ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
        list_for_each_entry(ep, &chip->ep_list, list) {
                if (ep->ep_num == ep_num &&
                    ep->iface == alts->desc.bInterfaceNumber &&
-                   ep->alt_idx == alts->desc.bAlternateSetting) {
+                   ep->altsetting == alts->desc.bAlternateSetting) {
                        snd_printdd(KERN_DEBUG "Re-using EP %x in iface %d,%d @%p\n",
-                                       ep_num, ep->iface, ep->alt_idx, ep);
+                                       ep_num, ep->iface, ep->altsetting, ep);
                        goto __exit_unlock;
                }
        }
@@ -447,7 +446,7 @@ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
        ep->type = type;
        ep->ep_num = ep_num;
        ep->iface = alts->desc.bInterfaceNumber;
-       ep->alt_idx = alts->desc.bAlternateSetting;
+       ep->altsetting = alts->desc.bAlternateSetting;
        INIT_LIST_HEAD(&ep->ready_playback_urbs);
        ep_num &= USB_ENDPOINT_NUMBER_MASK;
 
@@ -574,11 +573,14 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep,
                              snd_pcm_format_t pcm_format,
                              unsigned int channels,
                              unsigned int period_bytes,
+                             unsigned int frames_per_period,
+                             unsigned int periods_per_buffer,
                              struct audioformat *fmt,
                              struct snd_usb_endpoint *sync_ep)
 {
-       unsigned int maxsize, i, urb_packs, total_packs, packs_per_ms;
-       int is_playback = usb_pipeout(ep->pipe);
+       unsigned int maxsize, minsize, packs_per_ms, max_packs_per_urb;
+       unsigned int max_packs_per_period, urbs_per_period, urb_packs;
+       unsigned int max_urbs, i;
        int frame_bits = snd_pcm_format_physical_width(pcm_format) * channels;
 
        if (pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE && fmt->dsd_dop) {
@@ -611,58 +613,67 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep,
        else
                ep->curpacksize = maxsize;
 
-       if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL)
+       if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL) {
                packs_per_ms = 8 >> ep->datainterval;
-       else
-               packs_per_ms = 1;
-
-       if (is_playback && !snd_usb_endpoint_implicit_feedback_sink(ep)) {
-               urb_packs = max(ep->chip->nrpacks, 1);
-               urb_packs = min(urb_packs, (unsigned int) MAX_PACKS);
+               max_packs_per_urb = MAX_PACKS_HS;
        } else {
-               urb_packs = 1;
+               packs_per_ms = 1;
+               max_packs_per_urb = MAX_PACKS;
        }
+       if (sync_ep && !snd_usb_endpoint_implicit_feedback_sink(ep))
+               max_packs_per_urb = min(max_packs_per_urb,
+                                       1U << sync_ep->syncinterval);
+       max_packs_per_urb = max(1u, max_packs_per_urb >> ep->datainterval);
 
-       urb_packs *= packs_per_ms;
+       /*
+        * Capture endpoints need to use small URBs because there's no way
+        * to tell in advance where the next period will end, and we don't
+        * want the next URB to complete much after the period ends.
+        *
+        * Playback endpoints with implicit sync much use the same parameters
+        * as their corresponding capture endpoint.
+        */
+       if (usb_pipein(ep->pipe) ||
+                       snd_usb_endpoint_implicit_feedback_sink(ep)) {
 
-       if (sync_ep && !snd_usb_endpoint_implicit_feedback_sink(ep))
-               urb_packs = min(urb_packs, 1U << sync_ep->syncinterval);
+               /* make capture URBs <= 1 ms and smaller than a period */
+               urb_packs = min(max_packs_per_urb, packs_per_ms);
+               while (urb_packs > 1 && urb_packs * maxsize >= period_bytes)
+                       urb_packs >>= 1;
+               ep->nurbs = MAX_URBS;
 
-       /* decide how many packets to be used */
-       if (is_playback && !snd_usb_endpoint_implicit_feedback_sink(ep)) {
-               unsigned int minsize, maxpacks;
+       /*
+        * Playback endpoints without implicit sync are adjusted so that
+        * a period fits as evenly as possible in the smallest number of
+        * URBs.  The total number of URBs is adjusted to the size of the
+        * ALSA buffer, subject to the MAX_URBS and MAX_QUEUE limits.
+        */
+       } else {
                /* determine how small a packet can be */
-               minsize = (ep->freqn >> (16 - ep->datainterval))
-                         * (frame_bits >> 3);
+               minsize = (ep->freqn >> (16 - ep->datainterval)) *
+                               (frame_bits >> 3);
                /* with sync from device, assume it can be 12% lower */
                if (sync_ep)
                        minsize -= minsize >> 3;
                minsize = max(minsize, 1u);
-               total_packs = (period_bytes + minsize - 1) / minsize;
-               /* we need at least two URBs for queueing */
-               if (total_packs < 2) {
-                       total_packs = 2;
-               } else {
-                       /* and we don't want too long a queue either */
-                       maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2);
-                       total_packs = min(total_packs, maxpacks);
-               }
-       } else {
-               while (urb_packs > 1 && urb_packs * maxsize >= period_bytes)
-                       urb_packs >>= 1;
-               total_packs = MAX_URBS * urb_packs;
-       }
 
-       ep->nurbs = (total_packs + urb_packs - 1) / urb_packs;
-       if (ep->nurbs > MAX_URBS) {
-               /* too much... */
-               ep->nurbs = MAX_URBS;
-               total_packs = MAX_URBS * urb_packs;
-       } else if (ep->nurbs < 2) {
-               /* too little - we need at least two packets
-                * to ensure contiguous playback/capture
-                */
-               ep->nurbs = 2;
+               /* how many packets will contain an entire ALSA period? */
+               max_packs_per_period = DIV_ROUND_UP(period_bytes, minsize);
+
+               /* how many URBs will contain a period? */
+               urbs_per_period = DIV_ROUND_UP(max_packs_per_period,
+                               max_packs_per_urb);
+               /* how many packets are needed in each URB? */
+               urb_packs = DIV_ROUND_UP(max_packs_per_period, urbs_per_period);
+
+               /* limit the number of frames in a single URB */
+               ep->max_urb_frames = DIV_ROUND_UP(frames_per_period,
+                                       urbs_per_period);
+
+               /* try to use enough URBs to contain an entire ALSA buffer */
+               max_urbs = min((unsigned) MAX_URBS,
+                               MAX_QUEUE * packs_per_ms / urb_packs);
+               ep->nurbs = min(max_urbs, urbs_per_period * periods_per_buffer);
        }
 
        /* allocate and initialize data urbs */
@@ -670,8 +681,7 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep,
                struct snd_urb_ctx *u = &ep->urb[i];
                u->index = i;
                u->ep = ep;
-               u->packets = (i + 1) * total_packs / ep->nurbs
-                       - i * total_packs / ep->nurbs;
+               u->packets = urb_packs;
                u->buffer_size = maxsize * u->packets;
 
                if (fmt->fmt_type == UAC_FORMAT_TYPE_II)
@@ -703,8 +713,7 @@ out_of_memory:
 /*
  * configure a sync endpoint
  */
-static int sync_ep_set_params(struct snd_usb_endpoint *ep,
-                             struct audioformat *fmt)
+static int sync_ep_set_params(struct snd_usb_endpoint *ep)
 {
        int i;
 
@@ -748,6 +757,8 @@ out_of_memory:
  * @pcm_format: the audio fomat.
  * @channels: the number of audio channels.
  * @period_bytes: the number of bytes in one alsa period.
+ * @period_frames: the number of frames in one alsa period.
+ * @buffer_periods: the number of periods in one alsa buffer.
  * @rate: the frame rate.
  * @fmt: the USB audio format information
  * @sync_ep: the sync endpoint to use, if any
@@ -760,6 +771,8 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
                                snd_pcm_format_t pcm_format,
                                unsigned int channels,
                                unsigned int period_bytes,
+                               unsigned int period_frames,
+                               unsigned int buffer_periods,
                                unsigned int rate,
                                struct audioformat *fmt,
                                struct snd_usb_endpoint *sync_ep)
@@ -793,10 +806,11 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
        switch (ep->type) {
        case  SND_USB_ENDPOINT_TYPE_DATA:
                err = data_ep_set_params(ep, pcm_format, channels,
-                                        period_bytes, fmt, sync_ep);
+                                        period_bytes, period_frames,
+                                        buffer_periods, fmt, sync_ep);
                break;
        case  SND_USB_ENDPOINT_TYPE_SYNC:
-               err = sync_ep_set_params(ep, fmt);
+               err = sync_ep_set_params(ep);
                break;
        default:
                err = -EINVAL;
@@ -931,28 +945,21 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep)
  *
  * @ep: the endpoint to deactivate
  *
- * If the endpoint is not currently in use, this functions will select the
- * alternate interface setting 0 for the interface of this endpoint.
+ * If the endpoint is not currently in use, this functions will
+ * deactivate its associated URBs.
  *
  * In case of any active users, this functions does nothing.
- *
- * Returns an error if usb_set_interface() failed, 0 in all other
- * cases.
  */
-int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep)
+void snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep)
 {
        if (!ep)
-               return -EINVAL;
-
-       deactivate_urbs(ep, true);
-       wait_clear_urbs(ep);
+               return;
 
        if (ep->use_count != 0)
-               return 0;
-
-       clear_bit(EP_FLAG_ACTIVATED, &ep->flags);
+               return;
 
-       return 0;
+       deactivate_urbs(ep, true);
+       wait_clear_urbs(ep);
 }
 
 /**
index 2287adf5ca597b65156f1dbbffca609774b84c00..1c7e8ee48abc12e3e61cbda6491f20bdb9628ce5 100644 (file)
@@ -12,6 +12,8 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
                                snd_pcm_format_t pcm_format,
                                unsigned int channels,
                                unsigned int period_bytes,
+                               unsigned int period_frames,
+                               unsigned int buffer_periods,
                                unsigned int rate,
                                struct audioformat *fmt,
                                struct snd_usb_endpoint *sync_ep);
@@ -20,7 +22,7 @@ int  snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep);
 void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep);
 void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep);
 int  snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
-int  snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep);
+void snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep);
 void snd_usb_endpoint_free(struct list_head *head);
 
 int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep);
index 620902463c6ea2d5b9ba8eec496c33b5c8e78c2a..51ed1ac825fdca80744a1351de727bdc4fddbfbe 100644 (file)
@@ -118,6 +118,7 @@ unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip,
 {
        switch (snd_usb_get_speed(chip->dev)) {
        case USB_SPEED_HIGH:
+       case USB_SPEED_WIRELESS:
        case USB_SPEED_SUPER:
                if (get_endpoint(alts, 0)->bInterval >= 1 &&
                    get_endpoint(alts, 0)->bInterval <= 4)
index 95558ef4a7a09c72ed52c4706348d765a40c4314..44b0ba4feab3bd1b43100feb457e82e290ea26dc 100644 (file)
@@ -1151,14 +1151,14 @@ static void check_no_speaker_on_headset(struct snd_kcontrol *kctl,
        const char *names_to_check[] = {
                "Headset", "headset", "Headphone", "headphone", NULL};
        const char **s;
-       bool found = 0;
+       bool found = false;
 
        if (strcmp("Speaker", kctl->id.name))
                return;
 
        for (s = names_to_check; *s; s++)
                if (strstr(card->shortname, *s)) {
-                       found = 1;
+                       found = true;
                        break;
                }
 
index b375d58871e7ce9f7eb0e63a3f73644984c6c5e1..ca3256d6fde3d1206ffe3fc7313305561438f611 100644 (file)
@@ -241,16 +241,17 @@ static int start_endpoints(struct snd_usb_substream *subs, bool can_sleep)
                struct snd_usb_endpoint *ep = subs->sync_endpoint;
 
                if (subs->data_endpoint->iface != subs->sync_endpoint->iface ||
-                   subs->data_endpoint->alt_idx != subs->sync_endpoint->alt_idx) {
+                   subs->data_endpoint->altsetting != subs->sync_endpoint->altsetting) {
                        err = usb_set_interface(subs->dev,
                                                subs->sync_endpoint->iface,
-                                               subs->sync_endpoint->alt_idx);
+                                               subs->sync_endpoint->altsetting);
                        if (err < 0) {
+                               clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags);
                                snd_printk(KERN_ERR
                                           "%d:%d:%d: cannot set interface (%d)\n",
                                           subs->dev->devnum,
                                           subs->sync_endpoint->iface,
-                                          subs->sync_endpoint->alt_idx, err);
+                                          subs->sync_endpoint->altsetting, err);
                                return -EIO;
                        }
                }
@@ -282,22 +283,6 @@ static void stop_endpoints(struct snd_usb_substream *subs, bool wait)
        }
 }
 
-static int deactivate_endpoints(struct snd_usb_substream *subs)
-{
-       int reta, retb;
-
-       reta = snd_usb_endpoint_deactivate(subs->sync_endpoint);
-       retb = snd_usb_endpoint_deactivate(subs->data_endpoint);
-
-       if (reta < 0)
-               return reta;
-
-       if (retb < 0)
-               return retb;
-
-       return 0;
-}
-
 static int search_roland_implicit_fb(struct usb_device *dev, int ifnum,
                                     unsigned int altsetting,
                                     struct usb_host_interface **alts,
@@ -595,6 +580,7 @@ static int configure_sync_endpoint(struct snd_usb_substream *subs)
                                                   subs->pcm_format,
                                                   subs->channels,
                                                   subs->period_bytes,
+                                                  0, 0,
                                                   subs->cur_rate,
                                                   subs->cur_audiofmt,
                                                   NULL);
@@ -631,6 +617,7 @@ static int configure_sync_endpoint(struct snd_usb_substream *subs)
                                          subs->pcm_format,
                                          sync_fp->channels,
                                          sync_period_bytes,
+                                         0, 0,
                                          subs->cur_rate,
                                          sync_fp,
                                          NULL);
@@ -653,6 +640,8 @@ static int configure_endpoint(struct snd_usb_substream *subs)
                                          subs->pcm_format,
                                          subs->channels,
                                          subs->period_bytes,
+                                         subs->period_frames,
+                                         subs->buffer_periods,
                                          subs->cur_rate,
                                          subs->cur_audiofmt,
                                          subs->sync_endpoint);
@@ -689,6 +678,8 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
 
        subs->pcm_format = params_format(hw_params);
        subs->period_bytes = params_period_bytes(hw_params);
+       subs->period_frames = params_period_size(hw_params);
+       subs->buffer_periods = params_periods(hw_params);
        subs->channels = params_channels(hw_params);
        subs->cur_rate = params_rate(hw_params);
 
@@ -730,7 +721,8 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
        down_read(&subs->stream->chip->shutdown_rwsem);
        if (!subs->stream->chip->shutdown) {
                stop_endpoints(subs, true);
-               deactivate_endpoints(subs);
+               snd_usb_endpoint_deactivate(subs->sync_endpoint);
+               snd_usb_endpoint_deactivate(subs->data_endpoint);
        }
        up_read(&subs->stream->chip->shutdown_rwsem);
        return snd_pcm_lib_free_vmalloc_buffer(substream);
@@ -1363,6 +1355,7 @@ static void prepare_playback_urb(struct snd_usb_substream *subs,
        frames = 0;
        urb->number_of_packets = 0;
        spin_lock_irqsave(&subs->lock, flags);
+       subs->frame_limit += ep->max_urb_frames;
        for (i = 0; i < ctx->packets; i++) {
                if (ctx->packet_size[i])
                        counts = ctx->packet_size[i];
@@ -1377,6 +1370,7 @@ static void prepare_playback_urb(struct snd_usb_substream *subs,
                subs->transfer_done += counts;
                if (subs->transfer_done >= runtime->period_size) {
                        subs->transfer_done -= runtime->period_size;
+                       subs->frame_limit = 0;
                        period_elapsed = 1;
                        if (subs->fmt_type == UAC_FORMAT_TYPE_II) {
                                if (subs->transfer_done > 0) {
@@ -1399,8 +1393,10 @@ static void prepare_playback_urb(struct snd_usb_substream *subs,
                                break;
                        }
                }
-               if (period_elapsed &&
-                   !snd_usb_endpoint_implicit_feedback_sink(subs->data_endpoint)) /* finish at the period boundary */
+               /* finish at the period boundary or after enough frames */
+               if ((period_elapsed ||
+                               subs->transfer_done >= subs->frame_limit) &&
+                   !snd_usb_endpoint_implicit_feedback_sink(ep))
                        break;
        }
        bytes = frames * ep->stride;
index c4339f97226bddea9db02c578e6fb5774d1d5a0c..d737d0e6e5580efdec49852b93ed610429c88e31 100644 (file)
@@ -281,8 +281,6 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits,
        const unsigned int *maps;
        int c;
 
-       if (!bits)
-               return NULL;
        if (channels > ARRAY_SIZE(chmap->map))
                return NULL;
 
@@ -293,9 +291,19 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits,
        maps = protocol == UAC_VERSION_2 ? uac2_maps : uac1_maps;
        chmap->channels = channels;
        c = 0;
-       for (; bits && *maps; maps++, bits >>= 1) {
-               if (bits & 1)
-                       chmap->map[c++] = *maps;
+
+       if (bits) {
+               for (; bits && *maps; maps++, bits >>= 1)
+                       if (bits & 1)
+                               chmap->map[c++] = *maps;
+       } else {
+               /* If we're missing wChannelConfig, then guess something
+                   to make sure the channel map is not skipped entirely */
+               if (channels == 1)
+                       chmap->map[c++] = SNDRV_CHMAP_MONO;
+               else
+                       for (; c < channels && *maps; maps++)
+                               chmap->map[c++] = *maps;
        }
 
        for (; c < channels; c++)
@@ -579,6 +587,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
 
                        num_channels = as->bNrChannels;
                        format = le32_to_cpu(as->bmFormats);
+                       chconfig = le32_to_cpu(as->bmChannelConfig);
 
                        /* lookup the terminal associated to this interface
                         * to extract the clock */
@@ -586,7 +595,8 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
                                                                            as->bTerminalLink);
                        if (input_term) {
                                clock = input_term->bCSourceID;
-                               chconfig = le32_to_cpu(input_term->bmChannelConfig);
+                               if (!chconfig && (num_channels == input_term->bNrChannels))
+                                       chconfig = le32_to_cpu(input_term->bmChannelConfig);
                                break;
                        }
 
@@ -652,7 +662,6 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
                                        * (fp->maxpacksize & 0x7ff);
                fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no);
                fp->clock = clock;
-               fp->chmap = convert_chmap(num_channels, chconfig, protocol);
 
                /* some quirks for attributes here */
 
@@ -688,12 +697,16 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
                /* ok, let's parse further... */
                if (snd_usb_parse_audio_format(chip, fp, format, fmt, stream) < 0) {
                        kfree(fp->rate_table);
-                       kfree(fp->chmap);
                        kfree(fp);
                        fp = NULL;
                        continue;
                }
 
+               /* Create chmap */
+               if (fp->channels != num_channels)
+                       chconfig = 0;
+               fp->chmap = convert_chmap(fp->channels, chconfig, protocol);
+
                snd_printdd(KERN_INFO "%d:%u:%d: add audio endpoint %#x\n", dev->devnum, iface_no, altno, fp->endpoint);
                err = snd_usb_add_audio_stream(chip, stream, fp);
                if (err < 0) {
index caabe9b3af49250460d9a3b7e44e5896fea64c9a..5d2fe0530745a175c24a9034416a29477be96062 100644 (file)
@@ -55,7 +55,6 @@ struct snd_usb_audio {
        struct list_head mixer_list;    /* list of mixer interfaces */
 
        int setup;                      /* from the 'device_setup' module param */
-       int nrpacks;                    /* from the 'nrpacks' module param */
        bool autoclock;                 /* from the 'autoclock' module param */
 
        struct usb_host_interface *ctrl_intf;   /* the audio control interface */