Merge tag 'asoc-v3.13-5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie...
authorTakashi Iwai <tiwai@suse.de>
Thu, 21 Nov 2013 14:07:44 +0000 (15:07 +0100)
committerTakashi Iwai <tiwai@suse.de>
Thu, 21 Nov 2013 14:07:44 +0000 (15:07 +0100)
ASoC: Fixes for v3.13

A bunch of device specific fixes, nothing with a general impact here.

150 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/platform/x86/thinkpad_acpi.c
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/aoa/fabrics/layout.c
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/jack.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/msnd/msnd_pinnacle.c
sound/isa/sb/sb16_csp.c
sound/isa/wavefront/wavefront_synth.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/Kconfig
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/blackfin/bf5xx-sport.c
sound/soc/codecs/ak4641.c
sound/soc/codecs/max98088.c
sound/soc/codecs/max98095.c
sound/soc/codecs/mc13783.c
sound/soc/codecs/tas5086.c
sound/soc/codecs/tpa6130a2.c
sound/soc/codecs/wm0010.c
sound/soc/codecs/wm2000.c
sound/soc/codecs/wm5100.c
sound/soc/codecs/wm5102.c
sound/soc/codecs/wm5110.c
sound/soc/codecs/wm8350.c
sound/soc/codecs/wm8580.c
sound/soc/codecs/wm8776.c
sound/soc/codecs/wm8900.c
sound/soc/codecs/wm8904.c
sound/soc/codecs/wm8958-dsp2.c
sound/soc/codecs/wm8962.c
sound/soc/codecs/wm8996.c
sound/soc/codecs/wm8997.c
sound/soc/codecs/wm9713.c
sound/soc/codecs/wm_adsp.c
sound/soc/codecs/wm_hubs.c
sound/soc/fsl/fsl_spdif.c
sound/soc/fsl/imx-pcm-fiq.c
sound/soc/fsl/imx-wm8962.c
sound/soc/mid-x86/sst_platform.c
sound/soc/omap/n810.c
sound/soc/pxa/pxa2xx-i2s.c
sound/soc/s6000/s6000-pcm.c
sound/soc/sh/Kconfig
sound/soc/sh/rcar/scu.c
sound/soc/sh/rcar/ssi.c
sound/soc/sh/siu_dai.c
sound/soc/soc-cache.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/soc-generic-dmaengine-pcm.c
sound/soc/soc-pcm.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/soc/txx9/txx9aclc.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/mixer_quirks.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 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)
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 61ab640e195f3cbf6ee30d574116c797eead5a99..9dc5806d23dd51134b90fb0f8c0966a0588ccd6c 100644 (file)
@@ -644,7 +644,7 @@ static int n##_control_put(struct snd_kcontrol *kcontrol,           \
                           struct snd_ctl_elem_value *ucontrol)         \
 {                                                                      \
        struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol);        \
-       if (gpio->methods && gpio->methods->get_##n)                    \
+       if (gpio->methods && gpio->methods->set_##n)                    \
                gpio->methods->set_##n(gpio,                            \
                        !!ucontrol->value.integer.value[0]);            \
        return 1;                                                       \
@@ -1135,7 +1135,7 @@ static int aoa_fabric_layout_resume(struct soundbus_dev *sdev)
 {
        struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
 
-       if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
+       if (ldev->gpio.methods && ldev->gpio.methods->all_amps_restore)
                ldev->gpio.methods->all_amps_restore(&ldev->gpio);
 
        return 0;
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..9d518ac73eea064a6b4d918d3dba072cb50102f5 100644 (file)
@@ -384,8 +384,7 @@ static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
                return -EFAULT;
 
        mutex_lock(&stream->device->lock);
-       if (stream->runtime->state == SNDRV_PCM_STATE_PAUSED ||
-                       stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+       if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
                retval = -EBADFD;
                goto out;
        }
@@ -680,14 +679,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 +728,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 +772,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 b35fe7345c203c557d2baa5d0cbb778afac2dfb9..8658578eb584997ec867a864a4e31a124519f828 100644 (file)
@@ -34,12 +34,12 @@ static int jack_switch_types[SND_JACK_SWITCH_TYPES] = {
        SW_LINEIN_INSERT,
 };
 
-static int snd_jack_dev_free(struct snd_device *device)
+static int snd_jack_dev_disconnect(struct snd_device *device)
 {
        struct snd_jack *jack = device->device_data;
 
-       if (jack->private_free)
-               jack->private_free(jack);
+       if (!jack->input_dev)
+               return 0;
 
        /* If the input device is registered with the input subsystem
         * then we need to use a different deallocator. */
@@ -47,6 +47,18 @@ static int snd_jack_dev_free(struct snd_device *device)
                input_unregister_device(jack->input_dev);
        else
                input_free_device(jack->input_dev);
+       jack->input_dev = NULL;
+       return 0;
+}
+
+static int snd_jack_dev_free(struct snd_device *device)
+{
+       struct snd_jack *jack = device->device_data;
+
+       if (jack->private_free)
+               jack->private_free(jack);
+
+       snd_jack_dev_disconnect(device);
 
        kfree(jack->id);
        kfree(jack);
@@ -110,6 +122,7 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
        static struct snd_device_ops ops = {
                .dev_free = snd_jack_dev_free,
                .dev_register = snd_jack_dev_register,
+               .dev_disconnect = snd_jack_dev_disconnect,
        };
 
        jack = kzalloc(sizeof(struct snd_jack), GFP_KERNEL);
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..328bd29264ce555e048da881af2dcc7e60e289d6 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);
@@ -187,8 +188,8 @@ static int pcsp_probe(struct platform_device *dev)
 static int pcsp_remove(struct platform_device *dev)
 {
        struct snd_pcsp *chip = platform_get_drvdata(dev);
-       alsa_card_pcsp_exit(chip);
        pcspkr_input_remove(chip->input_dev);
+       alsa_card_pcsp_exit(chip);
        return 0;
 }
 
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..2746ecd291af057bf778379cecd7530e353d5fc2 100644 (file)
@@ -1,8 +1,10 @@
 #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 <sound/asound.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 +38,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 +55,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 +64,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 +88,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 +104,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 81aeb934261a26d2f2fca18060f21517b4650b32..0a90bd6ae2325d296f12f8d01a730e6d98fb0606 100644 (file)
 #ifdef MSND_CLASSIC
 #  include "msnd_classic.h"
 #  define LOGNAME                      "msnd_classic"
+#  define DEV_NAME                     "msnd-classic"
 #else
 #  include "msnd_pinnacle.h"
 #  define LOGNAME                      "snd_msnd_pinnacle"
+#  define DEV_NAME                     "msnd-pinnacle"
 #endif
 
 static void set_default_audio_parameters(struct snd_msnd *chip)
@@ -1067,8 +1069,6 @@ static int snd_msnd_isa_remove(struct device *pdev, unsigned int dev)
        return 0;
 }
 
-#define DEV_NAME "msnd-pinnacle"
-
 static struct isa_driver snd_msnd_driver = {
        .match          = snd_msnd_isa_match,
        .probe          = snd_msnd_isa_probe,
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 a2f87f9488ee163e336350c3c1b7cdebb48dca24..e5db001363eee9376d23e79f8a94483f62cd42d1 100644 (file)
@@ -1196,7 +1196,7 @@ wavefront_send_multisample (snd_wavefront_t *dev, wavefront_patch_info *header)
        int num_samples;
        unsigned char *msample_hdr;
 
-       msample_hdr = kmalloc(sizeof(WF_MSAMPLE_BYTES), GFP_KERNEL);
+       msample_hdr = kmalloc(WF_MSAMPLE_BYTES, GFP_KERNEL);
        if (! msample_hdr)
                return -ENOMEM;
 
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 8de66ccd7279d682d84f1718d8cd2f5788ac9e06..4cdd9ded4563247318795b3b5f259dd828ed54b3 100644 (file)
@@ -209,8 +209,9 @@ config SND_HDA_CODEC_CA0132
 
 config SND_HDA_CODEC_CA0132_DSP
        bool "Support new DSP code for CA0132 codec"
-       depends on SND_HDA_CODEC_CA0132 && FW_LOADER
+       depends on SND_HDA_CODEC_CA0132
        select SND_HDA_DSP_LOADER
+       select FW_LOADER
        help
          Say Y here to enable the DSP for Creative CA0132 for extended
          features like equalizer or echo cancellation.
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..69178c4f4113fad7fb3111b57d49247c8f2ebe5c 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;
@@ -2579,9 +2579,6 @@ int snd_hda_codec_reset(struct hda_codec *codec)
        cancel_delayed_work_sync(&codec->jackpoll_work);
 #ifdef CONFIG_PM
        cancel_delayed_work_sync(&codec->power_work);
-       codec->power_on = 0;
-       codec->power_transition = 0;
-       codec->power_jiffies = jiffies;
        flush_workqueue(bus->workq);
 #endif
        snd_hda_ctls_clear(codec);
@@ -2634,8 +2631,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 +2658,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 +2673,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 +2706,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 +2767,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;
@@ -3991,6 +4000,10 @@ static void hda_call_codec_resume(struct hda_codec *codec)
         * in the resume / power-save sequence
         */
        hda_keep_power_on(codec);
+       if (codec->pm_down_notified) {
+               codec->pm_down_notified = 0;
+               hda_call_pm_notify(codec->bus, true);
+       }
        hda_set_power_state(codec, AC_PWRST_D0);
        restore_shutup_pins(codec);
        hda_exec_init_verbs(codec);
@@ -5395,11 +5408,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 +5418,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..79ca80f6c77ada6d182a86b9880f3ce9125726f5 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,174 @@ 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 <= 0) {
+               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 <= 0)
+                       continue;
+
+               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;
+       }
+
+       /*
+        * HDMI VSDB latency format:
+        * separately for both audio and video:
+        *  0          field not valid or unknown latency
+        *  [1..251]   msecs = (x-1)*2  (max 500ms with x = 251 = 0xfb)
+        *  255        audio/video not supported
+        *
+        * HDA latency format:
+        * single value indicating video latency relative to audio:
+        *  0          unknown or 0ms
+        *  [1..250]   msecs = x*2  (max 500ms with x = 250 = 0xfa)
+        *  [251..255] reserved
+        */
+       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_hdmi = (aud_synch & ATI_DELAY_VIDEO_LATENCY);
+               int audio_latency_hdmi = (aud_synch & ATI_DELAY_AUDIO_LATENCY) >> 8;
+
+               if (video_latency_hdmi <= 0xfb && audio_latency_hdmi <= 0xfb &&
+                   video_latency_hdmi > audio_latency_hdmi)
+                       buf[6] = video_latency_hdmi - audio_latency_hdmi;
+               /* else unknown/invalid or 0ms or video ahead of audio, so use zero */
+       }
+
+       /* SAD count */
+       buf[5] |= ((pos - ELD_FIXED_BYTES - sink_desc_len) / 3) << 4;
+
+       /* Baseline ELD block length is 4-byte aligned */
+       pos = round_up(pos, 4);
+
+       /* Baseline ELD length (4-byte header is not counted in) */
+       buf[2] = (pos - 4) / 4;
+
+       *eld_size = pos;
+
+       return 0;
+}
index b7c89dff7066758e950feec6842dab58f2de29ed..3067ed4fe3b2f7fdb7edfca13e2b14575b9707a1 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;
 }
@@ -792,10 +796,10 @@ static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable)
        if (spec->own_eapd_ctl ||
            !(snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD))
                return;
-       if (codec->inv_eapd)
-               enable = !enable;
        if (spec->keep_eapd_on && !enable)
                return;
+       if (codec->inv_eapd)
+               enable = !enable;
        snd_hda_codec_update_cache(codec, pin, 0,
                                   AC_VERB_SET_EAPD_BTLENABLE,
                                   enable ? 0x02 : 0x00);
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..1a83559f4cbd27e455a941adaddc6d0513d9dd81 100644 (file)
@@ -139,6 +139,18 @@ static int ad198x_suspend(struct hda_codec *codec)
 }
 #endif
 
+/* follow EAPD via vmaster hook */
+static void ad_vmaster_eapd_hook(void *private_data, int enabled)
+{
+       struct hda_codec *codec = private_data;
+       struct ad198x_spec *spec = codec->spec;
+
+       if (!spec->eapd_nid)
+               return;
+       snd_hda_codec_update_cache(codec, spec->eapd_nid, 0,
+                                  AC_VERB_SET_EAPD_BTLENABLE,
+                                  enabled ? 0x02 : 0x00);
+}
 
 /*
  * Automatic parse of I/O pins from the BIOS configuration
@@ -219,8 +231,14 @@ static int alloc_ad_spec(struct hda_codec *codec)
 static void ad_fixup_inv_jack_detect(struct hda_codec *codec,
                                     const struct hda_fixup *fix, int action)
 {
-       if (action == HDA_FIXUP_ACT_PRE_PROBE)
+       struct ad198x_spec *spec = codec->spec;
+
+       if (action == HDA_FIXUP_ACT_PRE_PROBE) {
                codec->inv_jack_detect = 1;
+               spec->gen.keep_eapd_on = 1;
+               spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
+               spec->eapd_nid = 0x1b;
+       }
 }
 
 enum {
@@ -465,19 +483,6 @@ static int patch_ad1983(struct hda_codec *codec)
  * AD1981 HD specific
  */
 
-/* follow EAPD via vmaster hook */
-static void ad_vmaster_eapd_hook(void *private_data, int enabled)
-{
-       struct hda_codec *codec = private_data;
-       struct ad198x_spec *spec = codec->spec;
-
-       if (!spec->eapd_nid)
-               return;
-       snd_hda_codec_update_cache(codec, spec->eapd_nid, 0,
-                                  AC_VERB_SET_EAPD_BTLENABLE,
-                                  enabled ? 0x02 : 0x00);
-}
-
 static void ad1981_fixup_hp_eapd(struct hda_codec *codec,
                                 const struct hda_fixup *fix, int action)
 {
@@ -973,8 +978,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..fc492ac24caa917c9d3670a37f83cb1d3e0bca95 100644 (file)
@@ -47,6 +47,10 @@ struct cs_spec {
        unsigned int spdif_present:1;
        unsigned int sense_b:1;
        hda_nid_t vendor_nid;
+
+       /* for MBP SPDIF control */
+       int (*spdif_sw_put)(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol);
 };
 
 /* available models with CS420x */
@@ -331,10 +335,21 @@ static int cs_init(struct hda_codec *codec)
        return 0;
 }
 
+static int cs_build_controls(struct hda_codec *codec)
+{
+       int err;
+
+       err = snd_hda_gen_build_controls(codec);
+       if (err < 0)
+               return err;
+       snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD);
+       return 0;
+}
+
 #define cs_free                snd_hda_gen_free
 
 static const struct hda_codec_ops cs_patch_ops = {
-       .build_controls = snd_hda_gen_build_controls,
+       .build_controls = cs_build_controls,
        .build_pcms = snd_hda_gen_build_pcms,
        .init = cs_init,
        .free = cs_free,
@@ -597,18 +612,27 @@ static int patch_cs420x(struct hda_codec *codec)
  * Its layout is no longer compatible with CS4206/CS4207
  */
 enum {
+       CS4208_MAC_AUTO,
        CS4208_MBA6,
+       CS4208_MBP11,
        CS4208_GPIO0,
 };
 
 static const struct hda_model_fixup cs4208_models[] = {
        { .id = CS4208_GPIO0, .name = "gpio0" },
        { .id = CS4208_MBA6, .name = "mba6" },
+       { .id = CS4208_MBP11, .name = "mbp11" },
        {}
 };
 
 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, 0x5e00, "MacBookPro 11,2", CS4208_MBP11),
        SND_PCI_QUIRK(0x106b, 0x7100, "MacBookAir 6,1", CS4208_MBA6),
        SND_PCI_QUIRK(0x106b, 0x7200, "MacBookAir 6,2", CS4208_MBA6),
        {} /* terminator */
@@ -626,6 +650,50 @@ 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 int cs4208_spdif_sw_put(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct cs_spec *spec = codec->spec;
+       hda_nid_t pin = spec->gen.autocfg.dig_out_pins[0];
+       int pinctl = ucontrol->value.integer.value[0] ? PIN_OUT : 0;
+
+       snd_hda_set_pin_ctl_cache(codec, pin, pinctl);
+       return spec->spdif_sw_put(kcontrol, ucontrol);
+}
+
+/* hook the SPDIF switch */
+static void cs4208_fixup_spdif_switch(struct hda_codec *codec,
+                                     const struct hda_fixup *fix, int action)
+{
+       if (action == HDA_FIXUP_ACT_BUILD) {
+               struct cs_spec *spec = codec->spec;
+               struct snd_kcontrol *kctl;
+
+               if (!spec->gen.autocfg.dig_out_pins[0])
+                       return;
+               kctl = snd_hda_find_mixer_ctl(codec, "IEC958 Playback Switch");
+               if (!kctl)
+                       return;
+               spec->spdif_sw_put = kctl->put;
+               kctl->put = cs4208_spdif_sw_put;
+       }
+}
+
 static const struct hda_fixup cs4208_fixups[] = {
        [CS4208_MBA6] = {
                .type = HDA_FIXUP_PINS,
@@ -633,10 +701,20 @@ static const struct hda_fixup cs4208_fixups[] = {
                .chained = true,
                .chain_id = CS4208_GPIO0,
        },
+       [CS4208_MBP11] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = cs4208_fixup_spdif_switch,
+               .chained = true,
+               .chain_id = CS4208_GPIO0,
+       },
        [CS4208_GPIO0] = {
                .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 +738,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..08407bed093e5a64d4b659f90bf236dead9ec002 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_TFL,      FLH },
+       { SNDRV_CHMAP_TFR,      FRH },
+       { SNDRV_CHMAP_FLW,      FLW },
+       { SNDRV_CHMAP_FRW,      FRW },
+       { SNDRV_CHMAP_TC,       TC },
+       { SNDRV_CHMAP_TFC,      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,25 @@ 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);
 
+               if (pinctl < 0)
+                       return hbr ? -EINVAL : 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 +1266,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 +1331,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 +1411,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 +1419,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 +1478,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 +1497,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 +1511,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 +1529,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 +1549,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 +1582,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 +1743,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 +1786,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 +1822,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 +1866,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 +1888,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 +1925,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 +1949,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 +2076,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 +2121,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 +2160,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_c