浏览代码

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6: (348 commits)
  ALSA: hda - Fix NULL-derefence with a single mic in STAC auto-mic detection
  ALSA: hda - Add missing NID 0x19 fixup for Sony VAIO
  ALSA: hda - Fix ALC275 enable hardware EQ for SONY VAIO
  ALSA: oxygen: fix Xonar DG input
  ALSA: hda - Fix EAPD on Lenovo NB ALC269 to low
  ALSA: hda - Fix missing EAPD for Acer 4930G
  ALSA: hda: Disable 4/6 channels on some NVIDIA GPUs.
  ALSA: hda - Add static_hdmi_pcm option to HDMI codec parser
  ALSA: hda - Don't refer ELD when unplugged
  ASoC: tpa6130a2: Fix compiler warning
  ASoC: tlv320dac33: Add DAPM selection for LOM invert
  ASoC: DMIC codec: Adding a generic DMIC codec
  ALSA: snd-usb-us122l: Fix missing NULL checks
  ALSA: snd-usb-us122l: Fix MIDI output
  ASoC: soc-cache: Fix invalid memory access during snd_soc_lzo_cache_sync()
  ASoC: Fix section mismatch in wm8995.c
  ALSA: oxygen: add S/PDIF source selection for Claro cards
  ALSA: oxygen: fix CD/MIDI for X-Meridian (2G)
  ASoC: fix migor audio build
  ALSA: include delay.h for msleep in Xonar DG support
  ...
Linus Torvalds 14 年之前
父节点
当前提交
66dc918d42
共有 100 个文件被更改,包括 4833 次插入1561 次删除
  1. 9 11
      Documentation/sound/alsa/ALSA-Configuration.txt
  2. 0 1
      Documentation/sound/alsa/HD-Audio-Models.txt
  3. 44 1
      MAINTAINERS
  4. 1 1
      arch/arm/mach-kirkwood/openrd-setup.c
  5. 1 1
      arch/arm/mach-s3c2410/mach-h1940.c
  6. 1 1
      arch/arm/mach-s3c2440/mach-rx1950.c
  7. 1 1
      arch/arm/mach-s3c64xx/clock.c
  8. 35 40
      arch/arm/mach-s3c64xx/dev-audio.c
  9. 1 1
      arch/arm/mach-s3c64xx/mach-smdk6410.c
  10. 37 7
      arch/arm/mach-s5p6442/dev-audio.c
  11. 1 1
      arch/arm/mach-s5p64x0/clock-s5p6440.c
  12. 1 1
      arch/arm/mach-s5p64x0/clock-s5p6450.c
  13. 21 34
      arch/arm/mach-s5p64x0/dev-audio.c
  14. 42 14
      arch/arm/mach-s5pc100/dev-audio.c
  15. 2 0
      arch/arm/mach-s5pc100/mach-smdkc100.c
  16. 5 5
      arch/arm/mach-s5pv210/clock.c
  17. 43 15
      arch/arm/mach-s5pv210/dev-audio.c
  18. 1 0
      arch/arm/mach-s5pv310/Kconfig
  19. 2 1
      arch/arm/mach-s5pv310/Makefile
  20. 364 0
      arch/arm/mach-s5pv310/dev-audio.c
  21. 168 0
      arch/arm/mach-s5pv310/dma.c
  22. 26 0
      arch/arm/mach-s5pv310/include/mach/dma.h
  23. 3 0
      arch/arm/mach-s5pv310/include/mach/irqs.h
  24. 21 0
      arch/arm/mach-s5pv310/include/mach/map.h
  25. 5 0
      arch/arm/mach-shmobile/board-ap4evb.c
  26. 3 16
      arch/arm/plat-s3c24xx/devs.c
  27. 1 0
      arch/arm/plat-samsung/Makefile
  28. 25 0
      arch/arm/plat-samsung/dev-asocdma.c
  29. 24 0
      arch/arm/plat-samsung/include/plat/audio.h
  30. 10 1
      arch/arm/plat-samsung/include/plat/devs.h
  31. 5 0
      arch/sh/boards/mach-se/7724/setup.c
  32. 24 0
      drivers/gpio/wm8994-gpio.c
  33. 74 19
      drivers/mfd/wm8994-core.c
  34. 21 2
      drivers/regulator/wm8994-regulator.c
  35. 1 0
      drivers/video/sh_mobile_hdmi.c
  36. 8 0
      include/linux/mfd/wm8994/core.h
  37. 17 0
      include/linux/mfd/wm8994/pdata.h
  38. 302 0
      include/linux/mfd/wm8994/registers.h
  39. 15 0
      include/sound/alc5623.h
  40. 3 0
      include/sound/asound.h
  41. 3 1
      include/sound/control.h
  42. 1 0
      include/sound/hdsp.h
  43. 2 2
      include/sound/minors.h
  44. 1 0
      include/sound/pcm.h
  45. 1 3
      include/sound/soc-dai.h
  46. 69 30
      include/sound/soc-dapm.h
  47. 92 24
      include/sound/soc.h
  48. 235 0
      include/trace/events/asoc.h
  49. 2 2
      sound/ac97_bus.c
  50. 0 1
      sound/aoa/codecs/onyx.c
  51. 3 4
      sound/aoa/core/gpio-feature.c
  52. 3 4
      sound/aoa/core/gpio-pmf.c
  53. 27 1
      sound/core/control.c
  54. 3 1
      sound/core/oss/pcm_oss.c
  55. 22 0
      sound/core/pcm_lib.c
  56. 3 0
      sound/core/pcm_native.c
  57. 4 0
      sound/core/seq/seq.c
  58. 13 5
      sound/core/sound.c
  59. 5 2
      sound/core/timer.c
  60. 2 2
      sound/drivers/ml403-ac97cr.c
  61. 2 3
      sound/i2c/other/ak4113.c
  62. 2 3
      sound/i2c/other/ak4114.c
  63. 8 15
      sound/pci/Kconfig
  64. 2 4
      sound/pci/ac97/ac97_codec.c
  65. 205 201
      sound/pci/azt3328.c
  66. 2 8
      sound/pci/bt87x.c
  67. 9 16
      sound/pci/cmipci.c
  68. 3 0
      sound/pci/hda/hda_codec.c
  69. 6 3
      sound/pci/hda/hda_intel.c
  70. 36 36
      sound/pci/hda/patch_analog.c
  71. 53 55
      sound/pci/hda/patch_conexant.c
  72. 81 11
      sound/pci/hda/patch_hdmi.c
  73. 213 196
      sound/pci/hda/patch_realtek.c
  74. 13 4
      sound/pci/hda/patch_sigmatel.c
  75. 1 2
      sound/pci/hda/patch_via.c
  76. 48 1
      sound/pci/ice1712/delta.c
  77. 11 0
      sound/pci/ice1712/delta.h
  78. 1 3
      sound/pci/oxygen/Makefile
  79. 107 0
      sound/pci/oxygen/cs4245.h
  80. 0 239
      sound/pci/oxygen/hifier.c
  81. 318 38
      sound/pci/oxygen/oxygen.c
  82. 12 7
      sound/pci/oxygen/oxygen.h
  83. 2 2
      sound/pci/oxygen/oxygen_io.c
  84. 44 27
      sound/pci/oxygen/oxygen_lib.c
  85. 87 23
      sound/pci/oxygen/oxygen_mixer.c
  86. 34 21
      sound/pci/oxygen/oxygen_pcm.c
  87. 9 7
      sound/pci/oxygen/oxygen_regs.h
  88. 2 0
      sound/pci/oxygen/xonar.h
  89. 53 31
      sound/pci/oxygen/xonar_cs43xx.c
  90. 572 0
      sound/pci/oxygen/xonar_dg.c
  91. 8 0
      sound/pci/oxygen/xonar_dg.h
  92. 1 1
      sound/pci/oxygen/xonar_hdmi.c
  93. 4 2
      sound/pci/oxygen/xonar_lib.c
  94. 245 228
      sound/pci/oxygen/xonar_pcm179x.c
  95. 273 44
      sound/pci/oxygen/xonar_wm87x6.c
  96. 481 57
      sound/pci/rme9652/hdsp.c
  97. 3 9
      sound/pci/ymfpci/ymfpci_main.c
  98. 16 1
      sound/soc/Kconfig
  99. 1 1
      sound/soc/Makefile
  100. 6 6
      sound/soc/atmel/playpaq_wm8510.c

+ 9 - 11
Documentation/sound/alsa/ALSA-Configuration.txt

@@ -974,13 +974,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
 
     See hdspm.txt for details.
     See hdspm.txt for details.
 
 
-  Module snd-hifier
-  -----------------
-
-    Module for the MediaTek/TempoTec HiFier Fantasia sound card.
-
-    This module supports autoprobe and multiple cards.
-
   Module snd-ice1712
   Module snd-ice1712
   ------------------
   ------------------
 
 
@@ -1531,15 +1524,20 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
   Module snd-oxygen
   Module snd-oxygen
   -----------------
   -----------------
 
 
-    Module for sound cards based on the C-Media CMI8788 chip:
+    Module for sound cards based on the C-Media CMI8786/8787/8788 chip:
     * Asound A-8788
     * Asound A-8788
+    * Asus Xonar DG
     * AuzenTech X-Meridian
     * AuzenTech X-Meridian
+    * AuzenTech X-Meridian 2G
     * Bgears b-Enspirer
     * Bgears b-Enspirer
     * Club3D Theatron DTS
     * Club3D Theatron DTS
     * HT-Omega Claro (plus)
     * HT-Omega Claro (plus)
     * HT-Omega Claro halo (XT)
     * HT-Omega Claro halo (XT)
+    * Kuroutoshikou CMI8787-HG2PCI
     * Razer Barracuda AC-1
     * Razer Barracuda AC-1
     * Sondigo Inferno
     * Sondigo Inferno
+    * TempoTec HiFier Fantasia
+    * TempoTec HiFier Serenade
 
 
     This module supports autoprobe and multiple cards.
     This module supports autoprobe and multiple cards.
 
 
@@ -2006,9 +2004,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
   Module snd-virtuoso
   Module snd-virtuoso
   -------------------
   -------------------
 
 
-    Module for sound cards based on the Asus AV100/AV200 chips,
-    i.e., Xonar D1, DX, D2, D2X, DS, HDAV1.3 (Deluxe), Essence ST
-    (Deluxe) and Essence STX.
+    Module for sound cards based on the Asus AV66/AV100/AV200 chips,
+    i.e., Xonar D1, DX, D2, D2X, DS, Essence ST (Deluxe), Essence STX,
+    HDAV1.3 (Deluxe), and HDAV1.3 Slim.
 
 
     This module supports autoprobe and multiple cards.
     This module supports autoprobe and multiple cards.
 
 

+ 0 - 1
Documentation/sound/alsa/HD-Audio-Models.txt

@@ -149,7 +149,6 @@ ALC882/883/885/888/889
   acer-aspire-7730g Acer Aspire 7730G
   acer-aspire-7730g Acer Aspire 7730G
   acer-aspire-8930g Acer Aspire 8930G
   acer-aspire-8930g Acer Aspire 8930G
   medion	Medion Laptops
   medion	Medion Laptops
-  medion-md2	Medion MD2
   targa-dig	Targa/MSI
   targa-dig	Targa/MSI
   targa-2ch-dig	Targa/MSI with 2-channel
   targa-2ch-dig	Targa/MSI with 2-channel
   targa-8ch-dig Targa/MSI with 8-channel (MSI GX620)
   targa-8ch-dig Targa/MSI with 8-channel (MSI GX620)

+ 44 - 1
MAINTAINERS

@@ -1523,6 +1523,14 @@ S:	Supported
 F:	block/bsg.c
 F:	block/bsg.c
 F:	include/linux/bsg.h
 F:	include/linux/bsg.h
 
 
+BT87X AUDIO DRIVER
+M:	Clemens Ladisch <clemens@ladisch.de>
+L:	alsa-devel@alsa-project.org (moderated for non-subscribers)
+T:	git git://git.alsa-project.org/alsa-kernel.git
+S:	Maintained
+F:	Documentation/sound/alsa/Bt87x.txt
+F:	sound/pci/bt87x.c
+
 BT8XXGPIO DRIVER
 BT8XXGPIO DRIVER
 M:	Michael Buesch <mb@bu3sch.de>
 M:	Michael Buesch <mb@bu3sch.de>
 W:	http://bu3sch.de/btgpio.php
 W:	http://bu3sch.de/btgpio.php
@@ -1548,6 +1556,13 @@ S:	Maintained
 F:	Documentation/video4linux/bttv/
 F:	Documentation/video4linux/bttv/
 F:	drivers/media/video/bt8xx/bttv*
 F:	drivers/media/video/bt8xx/bttv*
 
 
+C-MEDIA CMI8788 DRIVER
+M:	Clemens Ladisch <clemens@ladisch.de>
+L:	alsa-devel@alsa-project.org (moderated for non-subscribers)
+T:	git git://git.alsa-project.org/alsa-kernel.git
+S:	Maintained
+F:	sound/pci/oxygen/
+
 CACHEFILES: FS-CACHE BACKEND FOR CACHING ON MOUNTED FILESYSTEMS
 CACHEFILES: FS-CACHE BACKEND FOR CACHING ON MOUNTED FILESYSTEMS
 M:	David Howells <dhowells@redhat.com>
 M:	David Howells <dhowells@redhat.com>
 L:	linux-cachefs@redhat.com
 L:	linux-cachefs@redhat.com
@@ -2339,6 +2354,13 @@ W:	bluesmoke.sourceforge.net
 S:	Maintained
 S:	Maintained
 F:	drivers/edac/r82600_edac.c
 F:	drivers/edac/r82600_edac.c
 
 
+EDIROL UA-101/UA-1000 DRIVER
+M:	Clemens Ladisch <clemens@ladisch.de>
+L:	alsa-devel@alsa-project.org (moderated for non-subscribers)
+T:	git git://git.alsa-project.org/alsa-kernel.git
+S:	Maintained
+F:	sound/usb/misc/ua101.c
+
 EEEPC LAPTOP EXTRAS DRIVER
 EEEPC LAPTOP EXTRAS DRIVER
 M:	Corentin Chary <corentincj@iksaif.net>
 M:	Corentin Chary <corentincj@iksaif.net>
 L:	acpi4asus-user@lists.sourceforge.net
 L:	acpi4asus-user@lists.sourceforge.net
@@ -3507,6 +3529,13 @@ L:	linux-serial@vger.kernel.org
 S:	Maintained
 S:	Maintained
 F:	drivers/serial/jsm/
 F:	drivers/serial/jsm/
 
 
+K10TEMP HARDWARE MONITORING DRIVER
+M:	Clemens Ladisch <clemens@ladisch.de>
+L:	lm-sensors@lm-sensors.org
+S:	Maintained
+F:	Documentation/hwmon/k10temp
+F:	drivers/hwmon/k10temp.c
+
 K8TEMP HARDWARE MONITORING DRIVER
 K8TEMP HARDWARE MONITORING DRIVER
 M:	Rudolf Marek <r.marek@assembler.cz>
 M:	Rudolf Marek <r.marek@assembler.cz>
 L:	lm-sensors@lm-sensors.org
 L:	lm-sensors@lm-sensors.org
@@ -4537,6 +4566,13 @@ F:	drivers/of
 F:	include/linux/of*.h
 F:	include/linux/of*.h
 K:	of_get_property
 K:	of_get_property
 
 
+OPL4 DRIVER
+M:	Clemens Ladisch <clemens@ladisch.de>
+L:	alsa-devel@alsa-project.org (moderated for non-subscribers)
+T:	git git://git.alsa-project.org/alsa-kernel.git
+S:	Maintained
+F:	sound/drivers/opl4/
+
 OPROFILE
 OPROFILE
 M:	Robert Richter <robert.richter@amd.com>
 M:	Robert Richter <robert.richter@amd.com>
 L:	oprofile-list@lists.sf.net
 L:	oprofile-list@lists.sf.net
@@ -5283,7 +5319,7 @@ SAMSUNG AUDIO (ASoC) DRIVERS
 M:	Jassi Brar <jassi.brar@samsung.com>
 M:	Jassi Brar <jassi.brar@samsung.com>
 L:	alsa-devel@alsa-project.org (moderated for non-subscribers)
 L:	alsa-devel@alsa-project.org (moderated for non-subscribers)
 S:	Supported
 S:	Supported
-F:	sound/soc/s3c24xx
+F:	sound/soc/samsung
 
 
 TIMEKEEPING, NTP
 TIMEKEEPING, NTP
 M:	John Stultz <johnstul@us.ibm.com>
 M:	John Stultz <johnstul@us.ibm.com>
@@ -6291,6 +6327,13 @@ S:	Maintained
 W:	http://www.one-eyed-alien.net/~mdharm/linux-usb/
 W:	http://www.one-eyed-alien.net/~mdharm/linux-usb/
 F:	drivers/usb/storage/
 F:	drivers/usb/storage/
 
 
+USB MIDI DRIVER
+M:	Clemens Ladisch <clemens@ladisch.de>
+L:	alsa-devel@alsa-project.org (moderated for non-subscribers)
+T:	git git://git.alsa-project.org/alsa-kernel.git
+S:	Maintained
+F:	sound/usb/midi.*
+
 USB OHCI DRIVER
 USB OHCI DRIVER
 M:	David Brownell <dbrownell@users.sourceforge.net>
 M:	David Brownell <dbrownell@users.sourceforge.net>
 L:	linux-usb@vger.kernel.org
 L:	linux-usb@vger.kernel.org

+ 1 - 1
arch/arm/mach-kirkwood/openrd-setup.c

@@ -171,7 +171,7 @@ static void __init openrd_init(void)
 
 
 	kirkwood_i2c_init();
 	kirkwood_i2c_init();
 
 
-	if (machine_is_openrd_client()) {
+	if (machine_is_openrd_client() || machine_is_openrd_ultimate()) {
 		i2c_register_board_info(0, i2c_board_info,
 		i2c_register_board_info(0, i2c_board_info,
 			ARRAY_SIZE(i2c_board_info));
 			ARRAY_SIZE(i2c_board_info));
 		kirkwood_audio_init();
 		kirkwood_audio_init();

+ 1 - 1
arch/arm/mach-s3c2410/mach-h1940.c

@@ -387,7 +387,7 @@ static struct platform_device *h1940_devices[] __initdata = {
 	&s3c_device_wdt,
 	&s3c_device_wdt,
 	&s3c_device_i2c0,
 	&s3c_device_i2c0,
 	&s3c_device_iis,
 	&s3c_device_iis,
-	&s3c_device_pcm,
+	&samsung_asoc_dma,
 	&s3c_device_usbgadget,
 	&s3c_device_usbgadget,
 	&h1940_device_leds,
 	&h1940_device_leds,
 	&h1940_device_bluetooth,
 	&h1940_device_bluetooth,

+ 1 - 1
arch/arm/mach-s3c2440/mach-rx1950.c

@@ -692,7 +692,7 @@ static struct platform_device *rx1950_devices[] __initdata = {
 	&s3c_device_wdt,
 	&s3c_device_wdt,
 	&s3c_device_i2c0,
 	&s3c_device_i2c0,
 	&s3c_device_iis,
 	&s3c_device_iis,
-	&s3c_device_pcm,
+	&samsung_asoc_dma,
 	&s3c_device_usbgadget,
 	&s3c_device_usbgadget,
 	&s3c_device_rtc,
 	&s3c_device_rtc,
 	&s3c_device_nand,
 	&s3c_device_nand,

+ 1 - 1
arch/arm/mach-s3c64xx/clock.c

@@ -695,7 +695,7 @@ static struct clksrc_clk clksrcs[] = {
 	}, {
 	}, {
 		.clk	= {
 		.clk	= {
 			.name		= "audio-bus",
 			.name		= "audio-bus",
-			.id		= -1,  /* There's only one IISv4 port */
+			.id		= 2,
 			.ctrlbit        = S3C6410_CLKCON_SCLK_AUDIO2,
 			.ctrlbit        = S3C6410_CLKCON_SCLK_AUDIO2,
 			.enable		= s3c64xx_sclk_ctrl,
 			.enable		= s3c64xx_sclk_ctrl,
 		},
 		},

+ 35 - 40
arch/arm/mach-s3c64xx/dev-audio.c

@@ -22,7 +22,12 @@
 #include <plat/audio.h>
 #include <plat/audio.h>
 #include <plat/gpio-cfg.h>
 #include <plat/gpio-cfg.h>
 
 
-static int s3c64xx_i2sv3_cfg_gpio(struct platform_device *pdev)
+static const char *rclksrc[] = {
+	[0] = "iis",
+	[1] = "audio-bus",
+};
+
+static int s3c64xx_i2s_cfg_gpio(struct platform_device *pdev)
 {
 {
 	unsigned int base;
 	unsigned int base;
 
 
@@ -33,6 +38,12 @@ static int s3c64xx_i2sv3_cfg_gpio(struct platform_device *pdev)
 	case 1:
 	case 1:
 		base = S3C64XX_GPE(0);
 		base = S3C64XX_GPE(0);
 		break;
 		break;
+	case 2:
+		s3c_gpio_cfgpin(S3C64XX_GPC(4), S3C_GPIO_SFN(5));
+		s3c_gpio_cfgpin(S3C64XX_GPC(5), S3C_GPIO_SFN(5));
+		s3c_gpio_cfgpin(S3C64XX_GPC(7), S3C_GPIO_SFN(5));
+		s3c_gpio_cfgpin_range(S3C64XX_GPH(6), 4, S3C_GPIO_SFN(5));
+		return 0;
 	default:
 	default:
 		printk(KERN_DEBUG "Invalid I2S Controller number: %d\n",
 		printk(KERN_DEBUG "Invalid I2S Controller number: %d\n",
 			pdev->id);
 			pdev->id);
@@ -44,16 +55,6 @@ static int s3c64xx_i2sv3_cfg_gpio(struct platform_device *pdev)
 	return 0;
 	return 0;
 }
 }
 
 
-static int s3c64xx_i2sv4_cfg_gpio(struct platform_device *pdev)
-{
-	s3c_gpio_cfgpin(S3C64XX_GPC(4), S3C_GPIO_SFN(5));
-	s3c_gpio_cfgpin(S3C64XX_GPC(5), S3C_GPIO_SFN(5));
-	s3c_gpio_cfgpin(S3C64XX_GPC(7), S3C_GPIO_SFN(5));
-	s3c_gpio_cfgpin_range(S3C64XX_GPH(6), 4, S3C_GPIO_SFN(5));
-
-	return 0;
-}
-
 static struct resource s3c64xx_iis0_resource[] = {
 static struct resource s3c64xx_iis0_resource[] = {
 	[0] = {
 	[0] = {
 		.start = S3C64XX_PA_IIS0,
 		.start = S3C64XX_PA_IIS0,
@@ -72,17 +73,22 @@ static struct resource s3c64xx_iis0_resource[] = {
 	},
 	},
 };
 };
 
 
-static struct s3c_audio_pdata s3c_i2s0_pdata = {
-	.cfg_gpio = s3c64xx_i2sv3_cfg_gpio,
+static struct s3c_audio_pdata i2sv3_pdata = {
+	.cfg_gpio = s3c64xx_i2s_cfg_gpio,
+	.type = {
+		.i2s = {
+			.src_clk = rclksrc,
+		},
+	},
 };
 };
 
 
 struct platform_device s3c64xx_device_iis0 = {
 struct platform_device s3c64xx_device_iis0 = {
-	.name		  = "s3c64xx-iis",
+	.name		  = "samsung-i2s",
 	.id		  = 0,
 	.id		  = 0,
 	.num_resources	  = ARRAY_SIZE(s3c64xx_iis0_resource),
 	.num_resources	  = ARRAY_SIZE(s3c64xx_iis0_resource),
 	.resource	  = s3c64xx_iis0_resource,
 	.resource	  = s3c64xx_iis0_resource,
 	.dev = {
 	.dev = {
-		.platform_data = &s3c_i2s0_pdata,
+		.platform_data = &i2sv3_pdata,
 	},
 	},
 };
 };
 EXPORT_SYMBOL(s3c64xx_device_iis0);
 EXPORT_SYMBOL(s3c64xx_device_iis0);
@@ -105,17 +111,13 @@ static struct resource s3c64xx_iis1_resource[] = {
 	},
 	},
 };
 };
 
 
-static struct s3c_audio_pdata s3c_i2s1_pdata = {
-	.cfg_gpio = s3c64xx_i2sv3_cfg_gpio,
-};
-
 struct platform_device s3c64xx_device_iis1 = {
 struct platform_device s3c64xx_device_iis1 = {
-	.name		  = "s3c64xx-iis",
+	.name		  = "samsung-i2s",
 	.id		  = 1,
 	.id		  = 1,
 	.num_resources	  = ARRAY_SIZE(s3c64xx_iis1_resource),
 	.num_resources	  = ARRAY_SIZE(s3c64xx_iis1_resource),
 	.resource	  = s3c64xx_iis1_resource,
 	.resource	  = s3c64xx_iis1_resource,
 	.dev = {
 	.dev = {
-		.platform_data = &s3c_i2s1_pdata,
+		.platform_data = &i2sv3_pdata,
 	},
 	},
 };
 };
 EXPORT_SYMBOL(s3c64xx_device_iis1);
 EXPORT_SYMBOL(s3c64xx_device_iis1);
@@ -138,17 +140,23 @@ static struct resource s3c64xx_iisv4_resource[] = {
 	},
 	},
 };
 };
 
 
-static struct s3c_audio_pdata s3c_i2sv4_pdata = {
-	.cfg_gpio = s3c64xx_i2sv4_cfg_gpio,
+static struct s3c_audio_pdata i2sv4_pdata = {
+	.cfg_gpio = s3c64xx_i2s_cfg_gpio,
+	.type = {
+		.i2s = {
+			.quirks = QUIRK_PRI_6CHAN,
+			.src_clk = rclksrc,
+		},
+	},
 };
 };
 
 
 struct platform_device s3c64xx_device_iisv4 = {
 struct platform_device s3c64xx_device_iisv4 = {
-	.name		  = "s3c64xx-iis-v4",
-	.id		  = -1,
+	.name = "samsung-i2s",
+	.id = 2,
 	.num_resources	  = ARRAY_SIZE(s3c64xx_iisv4_resource),
 	.num_resources	  = ARRAY_SIZE(s3c64xx_iisv4_resource),
 	.resource	  = s3c64xx_iisv4_resource,
 	.resource	  = s3c64xx_iisv4_resource,
 	.dev = {
 	.dev = {
-		.platform_data = &s3c_i2sv4_pdata,
+		.platform_data = &i2sv4_pdata,
 	},
 	},
 };
 };
 EXPORT_SYMBOL(s3c64xx_device_iisv4);
 EXPORT_SYMBOL(s3c64xx_device_iisv4);
@@ -288,7 +296,7 @@ static struct s3c_audio_pdata s3c_ac97_pdata;
 static u64 s3c64xx_ac97_dmamask = DMA_BIT_MASK(32);
 static u64 s3c64xx_ac97_dmamask = DMA_BIT_MASK(32);
 
 
 struct platform_device s3c64xx_device_ac97 = {
 struct platform_device s3c64xx_device_ac97 = {
-	.name		  = "s3c-ac97",
+	.name		  = "samsung-ac97",
 	.id		  = -1,
 	.id		  = -1,
 	.num_resources	  = ARRAY_SIZE(s3c64xx_ac97_resource),
 	.num_resources	  = ARRAY_SIZE(s3c64xx_ac97_resource),
 	.resource	  = s3c64xx_ac97_resource,
 	.resource	  = s3c64xx_ac97_resource,
@@ -307,16 +315,3 @@ void __init s3c64xx_ac97_setup_gpio(int num)
 	else
 	else
 		s3c_ac97_pdata.cfg_gpio = s3c64xx_ac97_cfg_gpe;
 		s3c_ac97_pdata.cfg_gpio = s3c64xx_ac97_cfg_gpe;
 }
 }
-
-static u64 s3c_device_audio_dmamask = 0xffffffffUL;
-
-struct platform_device s3c_device_pcm = {
-	.name		  = "s3c24xx-pcm-audio",
-	.id		  = -1,
-	.dev              = {
-		.dma_mask = &s3c_device_audio_dmamask,
-		.coherent_dma_mask = 0xffffffffUL
-	}
-};
-EXPORT_SYMBOL(s3c_device_pcm);
-

+ 1 - 1
arch/arm/mach-s3c64xx/mach-smdk6410.c

@@ -283,7 +283,7 @@ static struct platform_device *smdk6410_devices[] __initdata = {
 	&s3c_device_fb,
 	&s3c_device_fb,
 	&s3c_device_ohci,
 	&s3c_device_ohci,
 	&s3c_device_usb_hsotg,
 	&s3c_device_usb_hsotg,
-	&s3c_device_pcm,
+	&samsung_asoc_dma,
 	&s3c64xx_device_iisv4,
 	&s3c64xx_device_iisv4,
 	&samsung_device_keypad,
 	&samsung_device_keypad,
 
 

+ 37 - 7
arch/arm/mach-s5p6442/dev-audio.c

@@ -29,7 +29,7 @@ static int s5p6442_cfg_i2s(struct platform_device *pdev)
 		base = S5P6442_GPC1(0);
 		base = S5P6442_GPC1(0);
 		break;
 		break;
 
 
-	case -1:
+	case 0:
 		base = S5P6442_GPC0(0);
 		base = S5P6442_GPC0(0);
 		break;
 		break;
 
 
@@ -42,8 +42,19 @@ static int s5p6442_cfg_i2s(struct platform_device *pdev)
 	return 0;
 	return 0;
 }
 }
 
 
-static struct s3c_audio_pdata s3c_i2s_pdata = {
+static const char *rclksrc_v35[] = {
+	[0] = "busclk",
+	[1] = "i2sclk",
+};
+
+static struct s3c_audio_pdata i2sv35_pdata = {
 	.cfg_gpio = s5p6442_cfg_i2s,
 	.cfg_gpio = s5p6442_cfg_i2s,
+	.type = {
+		.i2s = {
+			.quirks = QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR,
+			.src_clk = rclksrc_v35,
+		},
+	},
 };
 };
 
 
 static struct resource s5p6442_iis0_resource[] = {
 static struct resource s5p6442_iis0_resource[] = {
@@ -62,15 +73,34 @@ static struct resource s5p6442_iis0_resource[] = {
 		.end   = DMACH_I2S0_RX,
 		.end   = DMACH_I2S0_RX,
 		.flags = IORESOURCE_DMA,
 		.flags = IORESOURCE_DMA,
 	},
 	},
+	[3] = {
+		.start = DMACH_I2S0S_TX,
+		.end = DMACH_I2S0S_TX,
+		.flags = IORESOURCE_DMA,
+	},
 };
 };
 
 
 struct platform_device s5p6442_device_iis0 = {
 struct platform_device s5p6442_device_iis0 = {
-	.name		  = "s3c64xx-iis-v4",
-	.id		  = -1,
+	.name = "samsung-i2s",
+	.id = 0,
 	.num_resources	  = ARRAY_SIZE(s5p6442_iis0_resource),
 	.num_resources	  = ARRAY_SIZE(s5p6442_iis0_resource),
 	.resource	  = s5p6442_iis0_resource,
 	.resource	  = s5p6442_iis0_resource,
 	.dev = {
 	.dev = {
-		.platform_data = &s3c_i2s_pdata,
+		.platform_data = &i2sv35_pdata,
+	},
+};
+
+static const char *rclksrc_v3[] = {
+	[0] = "iis",
+	[1] = "sclk_audio",
+};
+
+static struct s3c_audio_pdata i2sv3_pdata = {
+	.cfg_gpio = s5p6442_cfg_i2s,
+	.type = {
+		.i2s = {
+			.src_clk = rclksrc_v3,
+		},
 	},
 	},
 };
 };
 
 
@@ -93,12 +123,12 @@ static struct resource s5p6442_iis1_resource[] = {
 };
 };
 
 
 struct platform_device s5p6442_device_iis1 = {
 struct platform_device s5p6442_device_iis1 = {
-	.name		  = "s3c64xx-iis",
+	.name		  = "samsung-i2s",
 	.id		  = 1,
 	.id		  = 1,
 	.num_resources	  = ARRAY_SIZE(s5p6442_iis1_resource),
 	.num_resources	  = ARRAY_SIZE(s5p6442_iis1_resource),
 	.resource	  = s5p6442_iis1_resource,
 	.resource	  = s5p6442_iis1_resource,
 	.dev = {
 	.dev = {
-		.platform_data = &s3c_i2s_pdata,
+		.platform_data = &i2sv3_pdata,
 	},
 	},
 };
 };
 
 

+ 1 - 1
arch/arm/mach-s5p64x0/clock-s5p6440.c

@@ -261,7 +261,7 @@ static struct clk init_clocks_disable[] = {
 		.enable		= s5p64x0_pclk_ctrl,
 		.enable		= s5p64x0_pclk_ctrl,
 		.ctrlbit	= (1 << 25),
 		.ctrlbit	= (1 << 25),
 	}, {
 	}, {
-		.name		= "i2s_v40",
+		.name		= "iis",
 		.id		= 0,
 		.id		= 0,
 		.parent		= &clk_pclk_low.clk,
 		.parent		= &clk_pclk_low.clk,
 		.enable		= s5p64x0_pclk_ctrl,
 		.enable		= s5p64x0_pclk_ctrl,

+ 1 - 1
arch/arm/mach-s5p64x0/clock-s5p6450.c

@@ -256,7 +256,7 @@ static struct clk init_clocks_disable[] = {
 		.ctrlbit	= (1 << 22),
 		.ctrlbit	= (1 << 22),
 	}, {
 	}, {
 		.name		= "iis",
 		.name		= "iis",
-		.id		= -1,
+		.id		= 0,
 		.parent		= &clk_pclk_low.clk,
 		.parent		= &clk_pclk_low.clk,
 		.enable		= s5p64x0_pclk_ctrl,
 		.enable		= s5p64x0_pclk_ctrl,
 		.ctrlbit	= (1 << 26),
 		.ctrlbit	= (1 << 26),

+ 21 - 34
arch/arm/mach-s5p64x0/dev-audio.c

@@ -19,15 +19,19 @@
 #include <mach/dma.h>
 #include <mach/dma.h>
 #include <mach/irqs.h>
 #include <mach/irqs.h>
 
 
-static int s5p6440_cfg_i2s(struct platform_device *pdev)
+static const char *rclksrc[] = {
+	[0] = "iis",
+	[1] = "sclk_audio2",
+};
+
+static int s5p64x0_cfg_i2s(struct platform_device *pdev)
 {
 {
 	/* configure GPIO for i2s port */
 	/* configure GPIO for i2s port */
 	switch (pdev->id) {
 	switch (pdev->id) {
-	case -1:
+	case 0:
 		s3c_gpio_cfgpin_range(S5P6440_GPR(4), 5, S3C_GPIO_SFN(5));
 		s3c_gpio_cfgpin_range(S5P6440_GPR(4), 5, S3C_GPIO_SFN(5));
 		s3c_gpio_cfgpin_range(S5P6440_GPR(13), 2, S3C_GPIO_SFN(5));
 		s3c_gpio_cfgpin_range(S5P6440_GPR(13), 2, S3C_GPIO_SFN(5));
 		break;
 		break;
-
 	default:
 	default:
 		printk(KERN_ERR "Invalid Device %d\n", pdev->id);
 		printk(KERN_ERR "Invalid Device %d\n", pdev->id);
 		return -EINVAL;
 		return -EINVAL;
@@ -36,31 +40,14 @@ static int s5p6440_cfg_i2s(struct platform_device *pdev)
 	return 0;
 	return 0;
 }
 }
 
 
-static int s5p6450_cfg_i2s(struct platform_device *pdev)
-{
-	/* configure GPIO for i2s port */
-	switch (pdev->id) {
-	case -1:
-		s3c_gpio_cfgpin(S5P6450_GPB(4), S3C_GPIO_SFN(5));
-		s3c_gpio_cfgpin_range(S5P6450_GPR(4), 5, S3C_GPIO_SFN(5));
-		s3c_gpio_cfgpin_range(S5P6450_GPR(13), 2, S3C_GPIO_SFN(5));
-
-		break;
-
-	default:
-		printk(KERN_ERR "Invalid Device %d\n", pdev->id);
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-static struct s3c_audio_pdata s5p6440_i2s_pdata = {
-	.cfg_gpio = s5p6440_cfg_i2s,
-};
-
-static struct s3c_audio_pdata s5p6450_i2s_pdata = {
-	.cfg_gpio = s5p6450_cfg_i2s,
+static struct s3c_audio_pdata s5p64x0_i2s_pdata = {
+	.cfg_gpio = s5p64x0_cfg_i2s,
+	.type = {
+		.i2s = {
+			.quirks = QUIRK_PRI_6CHAN,
+			.src_clk = rclksrc,
+		},
+	},
 };
 };
 
 
 static struct resource s5p64x0_iis0_resource[] = {
 static struct resource s5p64x0_iis0_resource[] = {
@@ -82,22 +69,22 @@ static struct resource s5p64x0_iis0_resource[] = {
 };
 };
 
 
 struct platform_device s5p6440_device_iis = {
 struct platform_device s5p6440_device_iis = {
-	.name		= "s3c64xx-iis-v4",
-	.id		= -1,
+	.name		= "samsung-i2s",
+	.id		= 0,
 	.num_resources	= ARRAY_SIZE(s5p64x0_iis0_resource),
 	.num_resources	= ARRAY_SIZE(s5p64x0_iis0_resource),
 	.resource	= s5p64x0_iis0_resource,
 	.resource	= s5p64x0_iis0_resource,
 	.dev = {
 	.dev = {
-		.platform_data = &s5p6440_i2s_pdata,
+		.platform_data = &s5p64x0_i2s_pdata,
 	},
 	},
 };
 };
 
 
 struct platform_device s5p6450_device_iis0 = {
 struct platform_device s5p6450_device_iis0 = {
-	.name		= "s3c64xx-iis-v4",
-	.id		= -1,
+	.name		= "samsung-i2s",
+	.id		= 0,
 	.num_resources	= ARRAY_SIZE(s5p64x0_iis0_resource),
 	.num_resources	= ARRAY_SIZE(s5p64x0_iis0_resource),
 	.resource	= s5p64x0_iis0_resource,
 	.resource	= s5p64x0_iis0_resource,
 	.dev = {
 	.dev = {
-		.platform_data = &s5p6450_i2s_pdata,
+		.platform_data = &s5p64x0_i2s_pdata,
 	},
 	},
 };
 };
 
 

+ 42 - 14
arch/arm/mach-s5pc100/dev-audio.c

@@ -23,17 +23,14 @@ static int s5pc100_cfg_i2s(struct platform_device *pdev)
 {
 {
 	/* configure GPIO for i2s port */
 	/* configure GPIO for i2s port */
 	switch (pdev->id) {
 	switch (pdev->id) {
+	case 0: /* Dedicated pins */
+		break;
 	case 1:
 	case 1:
 		s3c_gpio_cfgpin_range(S5PC100_GPC(0), 5, S3C_GPIO_SFN(2));
 		s3c_gpio_cfgpin_range(S5PC100_GPC(0), 5, S3C_GPIO_SFN(2));
 		break;
 		break;
-
 	case 2:
 	case 2:
 		s3c_gpio_cfgpin_range(S5PC100_GPG3(0), 5, S3C_GPIO_SFN(4));
 		s3c_gpio_cfgpin_range(S5PC100_GPG3(0), 5, S3C_GPIO_SFN(4));
 		break;
 		break;
-
-	case -1: /* Dedicated pins */
-		break;
-
 	default:
 	default:
 		printk(KERN_ERR "Invalid Device %d\n", pdev->id);
 		printk(KERN_ERR "Invalid Device %d\n", pdev->id);
 		return -EINVAL;
 		return -EINVAL;
@@ -42,8 +39,20 @@ static int s5pc100_cfg_i2s(struct platform_device *pdev)
 	return 0;
 	return 0;
 }
 }
 
 
-static struct s3c_audio_pdata s3c_i2s_pdata = {
+static const char *rclksrc_v5[] = {
+	[0] = "iis",
+	[1] = "i2sclkd2",
+};
+
+static struct s3c_audio_pdata i2sv5_pdata = {
 	.cfg_gpio = s5pc100_cfg_i2s,
 	.cfg_gpio = s5pc100_cfg_i2s,
+	.type = {
+		.i2s = {
+			.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI
+					 | QUIRK_NEED_RSTCLR,
+			.src_clk = rclksrc_v5,
+		},
+	},
 };
 };
 
 
 static struct resource s5pc100_iis0_resource[] = {
 static struct resource s5pc100_iis0_resource[] = {
@@ -62,15 +71,34 @@ static struct resource s5pc100_iis0_resource[] = {
 		.end   = DMACH_I2S0_RX,
 		.end   = DMACH_I2S0_RX,
 		.flags = IORESOURCE_DMA,
 		.flags = IORESOURCE_DMA,
 	},
 	},
+	[3] = {
+		.start = DMACH_I2S0S_TX,
+		.end = DMACH_I2S0S_TX,
+		.flags = IORESOURCE_DMA,
+	},
 };
 };
 
 
 struct platform_device s5pc100_device_iis0 = {
 struct platform_device s5pc100_device_iis0 = {
-	.name		  = "s3c64xx-iis-v4",
-	.id		  = -1,
+	.name = "samsung-i2s",
+	.id = 0,
 	.num_resources	  = ARRAY_SIZE(s5pc100_iis0_resource),
 	.num_resources	  = ARRAY_SIZE(s5pc100_iis0_resource),
 	.resource	  = s5pc100_iis0_resource,
 	.resource	  = s5pc100_iis0_resource,
 	.dev = {
 	.dev = {
-		.platform_data = &s3c_i2s_pdata,
+		.platform_data = &i2sv5_pdata,
+	},
+};
+
+static const char *rclksrc_v3[] = {
+	[0] = "iis",
+	[1] = "sclk_audio",
+};
+
+static struct s3c_audio_pdata i2sv3_pdata = {
+	.cfg_gpio = s5pc100_cfg_i2s,
+	.type = {
+		.i2s = {
+			.src_clk = rclksrc_v3,
+		},
 	},
 	},
 };
 };
 
 
@@ -93,12 +121,12 @@ static struct resource s5pc100_iis1_resource[] = {
 };
 };
 
 
 struct platform_device s5pc100_device_iis1 = {
 struct platform_device s5pc100_device_iis1 = {
-	.name		  = "s3c64xx-iis",
+	.name		  = "samsung-i2s",
 	.id		  = 1,
 	.id		  = 1,
 	.num_resources	  = ARRAY_SIZE(s5pc100_iis1_resource),
 	.num_resources	  = ARRAY_SIZE(s5pc100_iis1_resource),
 	.resource	  = s5pc100_iis1_resource,
 	.resource	  = s5pc100_iis1_resource,
 	.dev = {
 	.dev = {
-		.platform_data = &s3c_i2s_pdata,
+		.platform_data = &i2sv3_pdata,
 	},
 	},
 };
 };
 
 
@@ -121,12 +149,12 @@ static struct resource s5pc100_iis2_resource[] = {
 };
 };
 
 
 struct platform_device s5pc100_device_iis2 = {
 struct platform_device s5pc100_device_iis2 = {
-	.name		  = "s3c64xx-iis",
+	.name		  = "samsung-i2s",
 	.id		  = 2,
 	.id		  = 2,
 	.num_resources	  = ARRAY_SIZE(s5pc100_iis2_resource),
 	.num_resources	  = ARRAY_SIZE(s5pc100_iis2_resource),
 	.resource	  = s5pc100_iis2_resource,
 	.resource	  = s5pc100_iis2_resource,
 	.dev = {
 	.dev = {
-		.platform_data = &s3c_i2s_pdata,
+		.platform_data = &i2sv3_pdata,
 	},
 	},
 };
 };
 
 
@@ -253,7 +281,7 @@ static struct s3c_audio_pdata s3c_ac97_pdata = {
 static u64 s5pc100_ac97_dmamask = DMA_BIT_MASK(32);
 static u64 s5pc100_ac97_dmamask = DMA_BIT_MASK(32);
 
 
 struct platform_device s5pc100_device_ac97 = {
 struct platform_device s5pc100_device_ac97 = {
-	.name		  = "s3c-ac97",
+	.name		  = "samsung-ac97",
 	.id		  = -1,
 	.id		  = -1,
 	.num_resources	  = ARRAY_SIZE(s5pc100_ac97_resource),
 	.num_resources	  = ARRAY_SIZE(s5pc100_ac97_resource),
 	.resource	  = s5pc100_ac97_resource,
 	.resource	  = s5pc100_ac97_resource,

+ 2 - 0
arch/arm/mach-s5pc100/mach-smdkc100.c

@@ -96,6 +96,7 @@ static struct s3c2410_uartcfg smdkc100_uartcfgs[] __initdata = {
 
 
 /* I2C0 */
 /* I2C0 */
 static struct i2c_board_info i2c_devs0[] __initdata = {
 static struct i2c_board_info i2c_devs0[] __initdata = {
+	{I2C_BOARD_INFO("wm8580", 0x1b),},
 };
 };
 
 
 /* I2C1 */
 /* I2C1 */
@@ -190,6 +191,7 @@ static struct platform_device *smdkc100_devices[] __initdata = {
 	&s3c_device_ts,
 	&s3c_device_ts,
 	&s3c_device_wdt,
 	&s3c_device_wdt,
 	&smdkc100_lcd_powerdev,
 	&smdkc100_lcd_powerdev,
+	&samsung_asoc_dma,
 	&s5pc100_device_iis0,
 	&s5pc100_device_iis0,
 	&samsung_device_keypad,
 	&samsung_device_keypad,
 	&s5pc100_device_ac97,
 	&s5pc100_device_ac97,

+ 5 - 5
arch/arm/mach-s5pv210/clock.c

@@ -467,20 +467,20 @@ static struct clk init_clocks_disable[] = {
 		.enable		= s5pv210_clk_ip3_ctrl,
 		.enable		= s5pv210_clk_ip3_ctrl,
 		.ctrlbit	= (1<<21),
 		.ctrlbit	= (1<<21),
 	}, {
 	}, {
-		.name		= "i2s_v50",
+		.name		= "iis",
 		.id		= 0,
 		.id		= 0,
 		.parent		= &clk_p,
 		.parent		= &clk_p,
 		.enable		= s5pv210_clk_ip3_ctrl,
 		.enable		= s5pv210_clk_ip3_ctrl,
 		.ctrlbit	= (1<<4),
 		.ctrlbit	= (1<<4),
 	}, {
 	}, {
-		.name		= "i2s_v32",
-		.id		= 0,
+		.name		= "iis",
+		.id		= 1,
 		.parent		= &clk_p,
 		.parent		= &clk_p,
 		.enable		= s5pv210_clk_ip3_ctrl,
 		.enable		= s5pv210_clk_ip3_ctrl,
 		.ctrlbit	= (1 << 5),
 		.ctrlbit	= (1 << 5),
 	}, {
 	}, {
-		.name		= "i2s_v32",
-		.id		= 1,
+		.name		= "iis",
+		.id		= 2,
 		.parent		= &clk_p,
 		.parent		= &clk_p,
 		.enable		= s5pv210_clk_ip3_ctrl,
 		.enable		= s5pv210_clk_ip3_ctrl,
 		.ctrlbit	= (1 << 6),
 		.ctrlbit	= (1 << 6),

+ 43 - 15
arch/arm/mach-s5pv210/dev-audio.c

@@ -19,22 +19,24 @@
 #include <mach/dma.h>
 #include <mach/dma.h>
 #include <mach/irqs.h>
 #include <mach/irqs.h>
 
 
+static const char *rclksrc[] = {
+	[0] = "busclk",
+	[1] = "i2sclk",
+};
+
 static int s5pv210_cfg_i2s(struct platform_device *pdev)
 static int s5pv210_cfg_i2s(struct platform_device *pdev)
 {
 {
 	/* configure GPIO for i2s port */
 	/* configure GPIO for i2s port */
 	switch (pdev->id) {
 	switch (pdev->id) {
+	case 0:
+		s3c_gpio_cfgpin_range(S5PV210_GPI(0), 7, S3C_GPIO_SFN(2));
+		break;
 	case 1:
 	case 1:
 		s3c_gpio_cfgpin_range(S5PV210_GPC0(0), 5, S3C_GPIO_SFN(2));
 		s3c_gpio_cfgpin_range(S5PV210_GPC0(0), 5, S3C_GPIO_SFN(2));
 		break;
 		break;
-
 	case 2:
 	case 2:
 		s3c_gpio_cfgpin_range(S5PV210_GPC1(0), 5, S3C_GPIO_SFN(4));
 		s3c_gpio_cfgpin_range(S5PV210_GPC1(0), 5, S3C_GPIO_SFN(4));
 		break;
 		break;
-
-	case -1:
-		s3c_gpio_cfgpin_range(S5PV210_GPI(0), 7, S3C_GPIO_SFN(2));
-		break;
-
 	default:
 	default:
 		printk(KERN_ERR "Invalid Device %d\n", pdev->id);
 		printk(KERN_ERR "Invalid Device %d\n", pdev->id);
 		return -EINVAL;
 		return -EINVAL;
@@ -43,8 +45,15 @@ static int s5pv210_cfg_i2s(struct platform_device *pdev)
 	return 0;
 	return 0;
 }
 }
 
 
-static struct s3c_audio_pdata s3c_i2s_pdata = {
+static struct s3c_audio_pdata i2sv5_pdata = {
 	.cfg_gpio = s5pv210_cfg_i2s,
 	.cfg_gpio = s5pv210_cfg_i2s,
+	.type = {
+		.i2s = {
+			.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI
+					 | QUIRK_NEED_RSTCLR,
+			.src_clk = rclksrc,
+		},
+	},
 };
 };
 
 
 static struct resource s5pv210_iis0_resource[] = {
 static struct resource s5pv210_iis0_resource[] = {
@@ -63,15 +72,34 @@ static struct resource s5pv210_iis0_resource[] = {
 		.end   = DMACH_I2S0_RX,
 		.end   = DMACH_I2S0_RX,
 		.flags = IORESOURCE_DMA,
 		.flags = IORESOURCE_DMA,
 	},
 	},
+	[3] = {
+		.start = DMACH_I2S0S_TX,
+		.end = DMACH_I2S0S_TX,
+		.flags = IORESOURCE_DMA,
+	},
 };
 };
 
 
 struct platform_device s5pv210_device_iis0 = {
 struct platform_device s5pv210_device_iis0 = {
-	.name		  = "s3c64xx-iis-v4",
-	.id		  = -1,
+	.name = "samsung-i2s",
+	.id = 0,
 	.num_resources	  = ARRAY_SIZE(s5pv210_iis0_resource),
 	.num_resources	  = ARRAY_SIZE(s5pv210_iis0_resource),
 	.resource	  = s5pv210_iis0_resource,
 	.resource	  = s5pv210_iis0_resource,
 	.dev = {
 	.dev = {
-		.platform_data = &s3c_i2s_pdata,
+		.platform_data = &i2sv5_pdata,
+	},
+};
+
+static const char *rclksrc_v3[] = {
+	[0] = "iis",
+	[1] = "audio-bus",
+};
+
+static struct s3c_audio_pdata i2sv3_pdata = {
+	.cfg_gpio = s5pv210_cfg_i2s,
+	.type = {
+		.i2s = {
+			.src_clk = rclksrc_v3,
+		},
 	},
 	},
 };
 };
 
 
@@ -94,12 +122,12 @@ static struct resource s5pv210_iis1_resource[] = {
 };
 };
 
 
 struct platform_device s5pv210_device_iis1 = {
 struct platform_device s5pv210_device_iis1 = {
-	.name		  = "s3c64xx-iis",
+	.name		  = "samsung-i2s",
 	.id		  = 1,
 	.id		  = 1,
 	.num_resources	  = ARRAY_SIZE(s5pv210_iis1_resource),
 	.num_resources	  = ARRAY_SIZE(s5pv210_iis1_resource),
 	.resource	  = s5pv210_iis1_resource,
 	.resource	  = s5pv210_iis1_resource,
 	.dev = {
 	.dev = {
-		.platform_data = &s3c_i2s_pdata,
+		.platform_data = &i2sv3_pdata,
 	},
 	},
 };
 };
 
 
@@ -122,12 +150,12 @@ static struct resource s5pv210_iis2_resource[] = {
 };
 };
 
 
 struct platform_device s5pv210_device_iis2 = {
 struct platform_device s5pv210_device_iis2 = {
-	.name		  = "s3c64xx-iis",
+	.name		  = "samsung-i2s",
 	.id		  = 2,
 	.id		  = 2,
 	.num_resources	  = ARRAY_SIZE(s5pv210_iis2_resource),
 	.num_resources	  = ARRAY_SIZE(s5pv210_iis2_resource),
 	.resource	  = s5pv210_iis2_resource,
 	.resource	  = s5pv210_iis2_resource,
 	.dev = {
 	.dev = {
-		.platform_data = &s3c_i2s_pdata,
+		.platform_data = &i2sv3_pdata,
 	},
 	},
 };
 };
 
 
@@ -283,7 +311,7 @@ static struct s3c_audio_pdata s3c_ac97_pdata = {
 static u64 s5pv210_ac97_dmamask = DMA_BIT_MASK(32);
 static u64 s5pv210_ac97_dmamask = DMA_BIT_MASK(32);
 
 
 struct platform_device s5pv210_device_ac97 = {
 struct platform_device s5pv210_device_ac97 = {
-	.name		  = "s3c-ac97",
+	.name		  = "samsung-ac97",
 	.id		  = -1,
 	.id		  = -1,
 	.num_resources	  = ARRAY_SIZE(s5pv210_ac97_resource),
 	.num_resources	  = ARRAY_SIZE(s5pv210_ac97_resource),
 	.resource	  = s5pv210_ac97_resource,
 	.resource	  = s5pv210_ac97_resource,

+ 1 - 0
arch/arm/mach-s5pv310/Kconfig

@@ -11,6 +11,7 @@ if ARCH_S5PV310
 
 
 config CPU_S5PV310
 config CPU_S5PV310
 	bool
 	bool
+	select S3C_PL330_DMA
 	help
 	help
 	  Enable S5PV310 CPU support
 	  Enable S5PV310 CPU support
 
 

+ 2 - 1
arch/arm/mach-s5pv310/Makefile

@@ -13,7 +13,7 @@ obj-				:=
 # Core support for S5PV310 system
 # Core support for S5PV310 system
 
 
 obj-$(CONFIG_CPU_S5PV310)	+= cpu.o init.o clock.o irq-combiner.o
 obj-$(CONFIG_CPU_S5PV310)	+= cpu.o init.o clock.o irq-combiner.o
-obj-$(CONFIG_CPU_S5PV310)	+= setup-i2c0.o time.o gpiolib.o irq-eint.o
+obj-$(CONFIG_CPU_S5PV310)	+= setup-i2c0.o time.o gpiolib.o irq-eint.o dma.o
 
 
 obj-$(CONFIG_SMP)		+= platsmp.o headsmp.o
 obj-$(CONFIG_SMP)		+= platsmp.o headsmp.o
 obj-$(CONFIG_LOCAL_TIMERS)	+= localtimer.o
 obj-$(CONFIG_LOCAL_TIMERS)	+= localtimer.o
@@ -27,6 +27,7 @@ obj-$(CONFIG_MACH_UNIVERSAL_C210)	+= mach-universal_c210.o
 
 
 # device support
 # device support
 
 
+obj-y += dev-audio.o
 obj-$(CONFIG_S5PV310_SETUP_I2C1)	+= setup-i2c1.o
 obj-$(CONFIG_S5PV310_SETUP_I2C1)	+= setup-i2c1.o
 obj-$(CONFIG_S5PV310_SETUP_I2C2)	+= setup-i2c2.o
 obj-$(CONFIG_S5PV310_SETUP_I2C2)	+= setup-i2c2.o
 obj-$(CONFIG_S5PV310_SETUP_I2C3)	+= setup-i2c3.o
 obj-$(CONFIG_S5PV310_SETUP_I2C3)	+= setup-i2c3.o

+ 364 - 0
arch/arm/mach-s5pv310/dev-audio.c

@@ -0,0 +1,364 @@
+/* linux/arch/arm/mach-s5pv310/dev-audio.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co. Ltd
+ *	Jaswinder Singh <jassi.brar@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/gpio.h>
+
+#include <plat/gpio-cfg.h>
+#include <plat/audio.h>
+
+#include <mach/map.h>
+#include <mach/dma.h>
+#include <mach/irqs.h>
+
+static const char *rclksrc[] = {
+	[0] = "busclk",
+	[1] = "i2sclk",
+};
+
+static int s5pv310_cfg_i2s(struct platform_device *pdev)
+{
+	/* configure GPIO for i2s port */
+	switch (pdev->id) {
+	case 0:
+		s3c_gpio_cfgpin_range(S5PV310_GPZ(0), 7, S3C_GPIO_SFN(2));
+		break;
+	case 1:
+		s3c_gpio_cfgpin_range(S5PV310_GPC0(0), 5, S3C_GPIO_SFN(2));
+		break;
+	case 2:
+		s3c_gpio_cfgpin_range(S5PV310_GPC1(0), 5, S3C_GPIO_SFN(4));
+		break;
+	default:
+		printk(KERN_ERR "Invalid Device %d\n", pdev->id);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct s3c_audio_pdata i2sv5_pdata = {
+	.cfg_gpio = s5pv310_cfg_i2s,
+	.type = {
+		.i2s = {
+			.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI
+					 | QUIRK_NEED_RSTCLR,
+			.src_clk = rclksrc,
+		},
+	},
+};
+
+static struct resource s5pv310_i2s0_resource[] = {
+	[0] = {
+		.start	= S5PV310_PA_I2S0,
+		.end	= S5PV310_PA_I2S0 + 0x100 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= DMACH_I2S0_TX,
+		.end	= DMACH_I2S0_TX,
+		.flags	= IORESOURCE_DMA,
+	},
+	[2] = {
+		.start	= DMACH_I2S0_RX,
+		.end	= DMACH_I2S0_RX,
+		.flags	= IORESOURCE_DMA,
+	},
+	[3] = {
+		.start	= DMACH_I2S0S_TX,
+		.end	= DMACH_I2S0S_TX,
+		.flags	= IORESOURCE_DMA,
+	},
+};
+
+struct platform_device s5pv310_device_i2s0 = {
+	.name = "samsung-i2s",
+	.id = 0,
+	.num_resources = ARRAY_SIZE(s5pv310_i2s0_resource),
+	.resource = s5pv310_i2s0_resource,
+	.dev = {
+		.platform_data = &i2sv5_pdata,
+	},
+};
+
+static const char *rclksrc_v3[] = {
+	[0] = "sclk_i2s",
+	[1] = "no_such_clock",
+};
+
+static struct s3c_audio_pdata i2sv3_pdata = {
+	.cfg_gpio = s5pv310_cfg_i2s,
+	.type = {
+		.i2s = {
+			.quirks = QUIRK_NO_MUXPSR,
+			.src_clk = rclksrc_v3,
+		},
+	},
+};
+
+static struct resource s5pv310_i2s1_resource[] = {
+	[0] = {
+		.start	= S5PV310_PA_I2S1,
+		.end	= S5PV310_PA_I2S1 + 0x100 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= DMACH_I2S1_TX,
+		.end	= DMACH_I2S1_TX,
+		.flags	= IORESOURCE_DMA,
+	},
+	[2] = {
+		.start	= DMACH_I2S1_RX,
+		.end	= DMACH_I2S1_RX,
+		.flags	= IORESOURCE_DMA,
+	},
+};
+
+struct platform_device s5pv310_device_i2s1 = {
+	.name = "samsung-i2s",
+	.id = 1,
+	.num_resources = ARRAY_SIZE(s5pv310_i2s1_resource),
+	.resource = s5pv310_i2s1_resource,
+	.dev = {
+		.platform_data = &i2sv3_pdata,
+	},
+};
+
+static struct resource s5pv310_i2s2_resource[] = {
+	[0] = {
+		.start	= S5PV310_PA_I2S2,
+		.end	= S5PV310_PA_I2S2 + 0x100 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= DMACH_I2S2_TX,
+		.end	= DMACH_I2S2_TX,
+		.flags	= IORESOURCE_DMA,
+	},
+	[2] = {
+		.start	= DMACH_I2S2_RX,
+		.end	= DMACH_I2S2_RX,
+		.flags	= IORESOURCE_DMA,
+	},
+};
+
+struct platform_device s5pv310_device_i2s2 = {
+	.name = "samsung-i2s",
+	.id = 2,
+	.num_resources = ARRAY_SIZE(s5pv310_i2s2_resource),
+	.resource = s5pv310_i2s2_resource,
+	.dev = {
+		.platform_data = &i2sv3_pdata,
+	},
+};
+
+/* PCM Controller platform_devices */
+
+static int s5pv310_pcm_cfg_gpio(struct platform_device *pdev)
+{
+	switch (pdev->id) {
+	case 0:
+		s3c_gpio_cfgpin_range(S5PV310_GPZ(0), 5, S3C_GPIO_SFN(3));
+		break;
+	case 1:
+		s3c_gpio_cfgpin_range(S5PV310_GPC0(0), 5, S3C_GPIO_SFN(3));
+		break;
+	case 2:
+		s3c_gpio_cfgpin_range(S5PV310_GPC1(0), 5, S3C_GPIO_SFN(3));
+		break;
+	default:
+		printk(KERN_DEBUG "Invalid PCM Controller number!");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct s3c_audio_pdata s3c_pcm_pdata = {
+	.cfg_gpio = s5pv310_pcm_cfg_gpio,
+};
+
+static struct resource s5pv310_pcm0_resource[] = {
+	[0] = {
+		.start	= S5PV310_PA_PCM0,
+		.end	= S5PV310_PA_PCM0 + 0x100 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= DMACH_PCM0_TX,
+		.end	= DMACH_PCM0_TX,
+		.flags	= IORESOURCE_DMA,
+	},
+	[2] = {
+		.start	= DMACH_PCM0_RX,
+		.end	= DMACH_PCM0_RX,
+		.flags	= IORESOURCE_DMA,
+	},
+};
+
+struct platform_device s5pv310_device_pcm0 = {
+	.name = "samsung-pcm",
+	.id = 0,
+	.num_resources = ARRAY_SIZE(s5pv310_pcm0_resource),
+	.resource = s5pv310_pcm0_resource,
+	.dev = {
+		.platform_data = &s3c_pcm_pdata,
+	},
+};
+
+static struct resource s5pv310_pcm1_resource[] = {
+	[0] = {
+		.start	= S5PV310_PA_PCM1,
+		.end	= S5PV310_PA_PCM1 + 0x100 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= DMACH_PCM1_TX,
+		.end	= DMACH_PCM1_TX,
+		.flags	= IORESOURCE_DMA,
+	},
+	[2] = {
+		.start	= DMACH_PCM1_RX,
+		.end	= DMACH_PCM1_RX,
+		.flags	= IORESOURCE_DMA,
+	},
+};
+
+struct platform_device s5pv310_device_pcm1 = {
+	.name = "samsung-pcm",
+	.id = 1,
+	.num_resources = ARRAY_SIZE(s5pv310_pcm1_resource),
+	.resource = s5pv310_pcm1_resource,
+	.dev = {
+		.platform_data = &s3c_pcm_pdata,
+	},
+};
+
+static struct resource s5pv310_pcm2_resource[] = {
+	[0] = {
+		.start	= S5PV310_PA_PCM2,
+		.end	= S5PV310_PA_PCM2 + 0x100 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= DMACH_PCM2_TX,
+		.end	= DMACH_PCM2_TX,
+		.flags	= IORESOURCE_DMA,
+	},
+	[2] = {
+		.start	= DMACH_PCM2_RX,
+		.end	= DMACH_PCM2_RX,
+		.flags	= IORESOURCE_DMA,
+	},
+};
+
+struct platform_device s5pv310_device_pcm2 = {
+	.name = "samsung-pcm",
+	.id = 2,
+	.num_resources = ARRAY_SIZE(s5pv310_pcm2_resource),
+	.resource = s5pv310_pcm2_resource,
+	.dev = {
+		.platform_data = &s3c_pcm_pdata,
+	},
+};
+
+/* AC97 Controller platform devices */
+
+static int s5pv310_ac97_cfg_gpio(struct platform_device *pdev)
+{
+	return s3c_gpio_cfgpin_range(S5PV310_GPC0(0), 5, S3C_GPIO_SFN(4));
+}
+
+static struct resource s5pv310_ac97_resource[] = {
+	[0] = {
+		.start	= S5PV310_PA_AC97,
+		.end	= S5PV310_PA_AC97 + 0x100 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= DMACH_AC97_PCMOUT,
+		.end	= DMACH_AC97_PCMOUT,
+		.flags	= IORESOURCE_DMA,
+	},
+	[2] = {
+		.start	= DMACH_AC97_PCMIN,
+		.end	= DMACH_AC97_PCMIN,
+		.flags	= IORESOURCE_DMA,
+	},
+	[3] = {
+		.start	= DMACH_AC97_MICIN,
+		.end	= DMACH_AC97_MICIN,
+		.flags	= IORESOURCE_DMA,
+	},
+	[4] = {
+		.start	= IRQ_AC97,
+		.end	= IRQ_AC97,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct s3c_audio_pdata s3c_ac97_pdata = {
+	.cfg_gpio = s5pv310_ac97_cfg_gpio,
+};
+
+static u64 s5pv310_ac97_dmamask = DMA_BIT_MASK(32);
+
+struct platform_device s5pv310_device_ac97 = {
+	.name = "samsung-ac97",
+	.id = -1,
+	.num_resources = ARRAY_SIZE(s5pv310_ac97_resource),
+	.resource = s5pv310_ac97_resource,
+	.dev = {
+		.platform_data = &s3c_ac97_pdata,
+		.dma_mask = &s5pv310_ac97_dmamask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+};
+
+/* S/PDIF Controller platform_device */
+
+static int s5pv310_spdif_cfg_gpio(struct platform_device *pdev)
+{
+	s3c_gpio_cfgpin_range(S5PV310_GPC1(0), 2, S3C_GPIO_SFN(3));
+
+	return 0;
+}
+
+static struct resource s5pv310_spdif_resource[] = {
+	[0] = {
+		.start	= S5PV310_PA_SPDIF,
+		.end	= S5PV310_PA_SPDIF + 0x100 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= DMACH_SPDIF,
+		.end	= DMACH_SPDIF,
+		.flags	= IORESOURCE_DMA,
+	},
+};
+
+static struct s3c_audio_pdata samsung_spdif_pdata = {
+	.cfg_gpio = s5pv310_spdif_cfg_gpio,
+};
+
+static u64 s5pv310_spdif_dmamask = DMA_BIT_MASK(32);
+
+struct platform_device s5pv310_device_spdif = {
+	.name = "samsung-spdif",
+	.id = -1,
+	.num_resources = ARRAY_SIZE(s5pv310_spdif_resource),
+	.resource = s5pv310_spdif_resource,
+	.dev = {
+		.platform_data = &samsung_spdif_pdata,
+		.dma_mask = &s5pv310_spdif_dmamask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+};

+ 168 - 0
arch/arm/mach-s5pv310/dma.c

@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2010 Samsung Electronics Co. Ltd.
+ *	Jaswinder Singh <jassi.brar@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+
+#include <plat/devs.h>
+#include <plat/irqs.h>
+
+#include <mach/map.h>
+#include <mach/irqs.h>
+
+#include <plat/s3c-pl330-pdata.h>
+
+static u64 dma_dmamask = DMA_BIT_MASK(32);
+
+static struct resource s5pv310_pdma0_resource[] = {
+	[0] = {
+		.start	= S5PV310_PA_PDMA0,
+		.end	= S5PV310_PA_PDMA0 + SZ_4K,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= IRQ_PDMA0,
+		.end	= IRQ_PDMA0,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct s3c_pl330_platdata s5pv310_pdma0_pdata = {
+	.peri = {
+		[0] = DMACH_PCM0_RX,
+		[1] = DMACH_PCM0_TX,
+		[2] = DMACH_PCM2_RX,
+		[3] = DMACH_PCM2_TX,
+		[4] = DMACH_MSM_REQ0,
+		[5] = DMACH_MSM_REQ2,
+		[6] = DMACH_SPI0_RX,
+		[7] = DMACH_SPI0_TX,
+		[8] = DMACH_SPI2_RX,
+		[9] = DMACH_SPI2_TX,
+		[10] = DMACH_I2S0S_TX,
+		[11] = DMACH_I2S0_RX,
+		[12] = DMACH_I2S0_TX,
+		[13] = DMACH_I2S2_RX,
+		[14] = DMACH_I2S2_TX,
+		[15] = DMACH_UART0_RX,
+		[16] = DMACH_UART0_TX,
+		[17] = DMACH_UART2_RX,
+		[18] = DMACH_UART2_TX,
+		[19] = DMACH_UART4_RX,
+		[20] = DMACH_UART4_TX,
+		[21] = DMACH_SLIMBUS0_RX,
+		[22] = DMACH_SLIMBUS0_TX,
+		[23] = DMACH_SLIMBUS2_RX,
+		[24] = DMACH_SLIMBUS2_TX,
+		[25] = DMACH_SLIMBUS4_RX,
+		[26] = DMACH_SLIMBUS4_TX,
+		[27] = DMACH_AC97_MICIN,
+		[28] = DMACH_AC97_PCMIN,
+		[29] = DMACH_AC97_PCMOUT,
+		[30] = DMACH_MAX,
+		[31] = DMACH_MAX,
+	},
+};
+
+static struct platform_device s5pv310_device_pdma0 = {
+	.name		= "s3c-pl330",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(s5pv310_pdma0_resource),
+	.resource	= s5pv310_pdma0_resource,
+	.dev		= {
+		.dma_mask = &dma_dmamask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+		.platform_data = &s5pv310_pdma0_pdata,
+	},
+};
+
+static struct resource s5pv310_pdma1_resource[] = {
+	[0] = {
+		.start	= S5PV310_PA_PDMA1,
+		.end	= S5PV310_PA_PDMA1 + SZ_4K,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= IRQ_PDMA1,
+		.end	= IRQ_PDMA1,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct s3c_pl330_platdata s5pv310_pdma1_pdata = {
+	.peri = {
+		[0] = DMACH_PCM0_RX,
+		[1] = DMACH_PCM0_TX,
+		[2] = DMACH_PCM1_RX,
+		[3] = DMACH_PCM1_TX,
+		[4] = DMACH_MSM_REQ1,
+		[5] = DMACH_MSM_REQ3,
+		[6] = DMACH_SPI1_RX,
+		[7] = DMACH_SPI1_TX,
+		[8] = DMACH_I2S0S_TX,
+		[9] = DMACH_I2S0_RX,
+		[10] = DMACH_I2S0_TX,
+		[11] = DMACH_I2S1_RX,
+		[12] = DMACH_I2S1_TX,
+		[13] = DMACH_UART0_RX,
+		[14] = DMACH_UART0_TX,
+		[15] = DMACH_UART1_RX,
+		[16] = DMACH_UART1_TX,
+		[17] = DMACH_UART3_RX,
+		[18] = DMACH_UART3_TX,
+		[19] = DMACH_SLIMBUS1_RX,
+		[20] = DMACH_SLIMBUS1_TX,
+		[21] = DMACH_SLIMBUS3_RX,
+		[22] = DMACH_SLIMBUS3_TX,
+		[23] = DMACH_SLIMBUS5_RX,
+		[24] = DMACH_SLIMBUS5_TX,
+		[25] = DMACH_SLIMBUS0AUX_RX,
+		[26] = DMACH_SLIMBUS0AUX_TX,
+		[27] = DMACH_SPDIF,
+		[28] = DMACH_MAX,
+		[29] = DMACH_MAX,
+		[30] = DMACH_MAX,
+		[31] = DMACH_MAX,
+	},
+};
+
+static struct platform_device s5pv310_device_pdma1 = {
+	.name		= "s3c-pl330",
+	.id		= 1,
+	.num_resources	= ARRAY_SIZE(s5pv310_pdma1_resource),
+	.resource	= s5pv310_pdma1_resource,
+	.dev		= {
+		.dma_mask = &dma_dmamask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+		.platform_data = &s5pv310_pdma1_pdata,
+	},
+};
+
+static struct platform_device *s5pv310_dmacs[] __initdata = {
+	&s5pv310_device_pdma0,
+	&s5pv310_device_pdma1,
+};
+
+static int __init s5pv310_dma_init(void)
+{
+	platform_add_devices(s5pv310_dmacs, ARRAY_SIZE(s5pv310_dmacs));
+
+	return 0;
+}
+arch_initcall(s5pv310_dma_init);

+ 26 - 0
arch/arm/mach-s5pv310/include/mach/dma.h

@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010 Samsung Electronics Co. Ltd.
+ *	Jaswinder Singh <jassi.brar@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __MACH_DMA_H
+#define __MACH_DMA_H
+
+/* This platform uses the common S3C DMA API driver for PL330 */
+#include <plat/s3c-dma-pl330.h>
+
+#endif /* __MACH_DMA_H */

+ 3 - 0
arch/arm/mach-s5pv310/include/mach/irqs.h

@@ -54,6 +54,9 @@
 #define COMBINER_GROUP(x)	((x) * MAX_IRQ_IN_COMBINER + IRQ_SPI(64))
 #define COMBINER_GROUP(x)	((x) * MAX_IRQ_IN_COMBINER + IRQ_SPI(64))
 #define COMBINER_IRQ(x, y)	(COMBINER_GROUP(x) + y)
 #define COMBINER_IRQ(x, y)	(COMBINER_GROUP(x) + y)
 
 
+#define IRQ_PDMA0		COMBINER_IRQ(21, 0)
+#define IRQ_PDMA1		COMBINER_IRQ(21, 1)
+
 #define IRQ_TIMER0_VIC		COMBINER_IRQ(22, 0)
 #define IRQ_TIMER0_VIC		COMBINER_IRQ(22, 0)
 #define IRQ_TIMER1_VIC		COMBINER_IRQ(22, 1)
 #define IRQ_TIMER1_VIC		COMBINER_IRQ(22, 1)
 #define IRQ_TIMER2_VIC		COMBINER_IRQ(22, 2)
 #define IRQ_TIMER2_VIC		COMBINER_IRQ(22, 2)

+ 21 - 0
arch/arm/mach-s5pv310/include/mach/map.h

@@ -52,6 +52,11 @@
 #define S5PV310_PA_GIC_DIST		(0x10501000)
 #define S5PV310_PA_GIC_DIST		(0x10501000)
 #define S5PV310_PA_L2CC			(0x10502000)
 #define S5PV310_PA_L2CC			(0x10502000)
 
 
+/* DMA */
+#define S5PV310_PA_MDMA		0x10810000
+#define S5PV310_PA_PDMA0	0x12680000
+#define S5PV310_PA_PDMA1	0x12690000
+
 #define S5PV310_PA_GPIO1		(0x11400000)
 #define S5PV310_PA_GPIO1		(0x11400000)
 #define S5PV310_PA_GPIO2		(0x11000000)
 #define S5PV310_PA_GPIO2		(0x11000000)
 #define S5PV310_PA_GPIO3		(0x03860000)
 #define S5PV310_PA_GPIO3		(0x03860000)
@@ -60,6 +65,22 @@
 
 
 #define S5PV310_PA_SROMC		(0x12570000)
 #define S5PV310_PA_SROMC		(0x12570000)
 
 
+/* S/PDIF */
+#define S5PV310_PA_SPDIF	0xE1100000
+
+/* I2S */
+#define S5PV310_PA_I2S0		0x03830000
+#define S5PV310_PA_I2S1		0xE3100000
+#define S5PV310_PA_I2S2		0xE2A00000
+
+/* PCM */
+#define S5PV310_PA_PCM0		0x03840000
+#define S5PV310_PA_PCM1		0x13980000
+#define S5PV310_PA_PCM2		0x13990000
+
+/* AC97 */
+#define S5PV310_PA_AC97		0x139A0000
+
 #define S5PV310_PA_UART			(0x13800000)
 #define S5PV310_PA_UART			(0x13800000)
 
 
 #define S5P_PA_UART(x)			(S5PV310_PA_UART + ((x) * S3C_UART_OFFSET))
 #define S5P_PA_UART(x)			(S5PV310_PA_UART + ((x) * S3C_UART_OFFSET))

+ 5 - 0
arch/arm/mach-shmobile/board-ap4evb.c

@@ -711,6 +711,10 @@ static struct platform_device fsi_device = {
 	},
 	},
 };
 };
 
 
+static struct platform_device fsi_ak4643_device = {
+	.name		= "sh_fsi2_a_ak4643",
+};
+
 static struct sh_mobile_lcdc_info sh_mobile_lcdc1_info = {
 static struct sh_mobile_lcdc_info sh_mobile_lcdc1_info = {
 	.clock_source = LCDC_CLK_EXTERNAL,
 	.clock_source = LCDC_CLK_EXTERNAL,
 	.ch[0] = {
 	.ch[0] = {
@@ -933,6 +937,7 @@ static struct platform_device *ap4evb_devices[] __initdata = {
 	&sdhi1_device,
 	&sdhi1_device,
 	&usb1_host_device,
 	&usb1_host_device,
 	&fsi_device,
 	&fsi_device,
+	&fsi_ak4643_device,
 	&sh_mmcif_device,
 	&sh_mmcif_device,
 	&lcdc1_device,
 	&lcdc1_device,
 	&lcdc_device,
 	&lcdc_device,

+ 3 - 16
arch/arm/plat-s3c24xx/devs.c

@@ -259,21 +259,6 @@ struct platform_device s3c_device_iis = {
 
 
 EXPORT_SYMBOL(s3c_device_iis);
 EXPORT_SYMBOL(s3c_device_iis);
 
 
-/* ASoC PCM DMA */
-
-static u64 s3c_device_audio_dmamask = 0xffffffffUL;
-
-struct platform_device s3c_device_pcm = {
-	.name		  = "s3c24xx-pcm-audio",
-	.id		  = -1,
-	.dev              = {
-		.dma_mask = &s3c_device_audio_dmamask,
-		.coherent_dma_mask = 0xffffffffUL
-	}
-};
-
-EXPORT_SYMBOL(s3c_device_pcm);
-
 /* RTC */
 /* RTC */
 
 
 static struct resource s3c_rtc_resource[] = {
 static struct resource s3c_rtc_resource[] = {
@@ -496,8 +481,10 @@ static struct resource s3c_ac97_resource[] = {
 	},
 	},
 };
 };
 
 
+static u64 s3c_device_audio_dmamask = 0xffffffffUL;
+
 struct platform_device s3c_device_ac97 = {
 struct platform_device s3c_device_ac97 = {
-	.name		  = "s3c-ac97",
+	.name		  = "samsung-ac97",
 	.id		  = -1,
 	.id		  = -1,
 	.num_resources	  = ARRAY_SIZE(s3c_ac97_resource),
 	.num_resources	  = ARRAY_SIZE(s3c_ac97_resource),
 	.resource	  = s3c_ac97_resource,
 	.resource	  = s3c_ac97_resource,

+ 1 - 0
arch/arm/plat-samsung/Makefile

@@ -17,6 +17,7 @@ obj-y				+= clock.o
 obj-y				+= pwm-clock.o
 obj-y				+= pwm-clock.o
 obj-y				+= gpio.o
 obj-y				+= gpio.o
 obj-y				+= gpio-config.o
 obj-y				+= gpio-config.o
+obj-y				+= dev-asocdma.o
 
 
 obj-$(CONFIG_SAMSUNG_GPIOLIB_4BIT)	+= gpiolib.o
 obj-$(CONFIG_SAMSUNG_GPIOLIB_4BIT)	+= gpiolib.o
 obj-$(CONFIG_SAMSUNG_CLKSRC)	+= clock-clksrc.o
 obj-$(CONFIG_SAMSUNG_CLKSRC)	+= clock-clksrc.o

+ 25 - 0
arch/arm/plat-samsung/dev-asocdma.c

@@ -0,0 +1,25 @@
+/* linux/arch/arm/plat-samsung/dev-asocdma.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co. Ltd
+ *	Jaswinder Singh <jassi.brar@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <plat/devs.h>
+
+static u64 audio_dmamask = DMA_BIT_MASK(32);
+
+struct platform_device samsung_asoc_dma = {
+	.name		  = "samsung-audio",
+	.id		  = -1,
+	.dev              = {
+		.dma_mask = &audio_dmamask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	}
+};
+EXPORT_SYMBOL(samsung_asoc_dma);

+ 24 - 0
arch/arm/plat-samsung/include/plat/audio.h

@@ -25,10 +25,34 @@ extern void s3c64xx_ac97_setup_gpio(int);
 #define S5PC100_SPDIF_GPG3 1
 #define S5PC100_SPDIF_GPG3 1
 extern void s5pc100_spdif_setup_gpio(int);
 extern void s5pc100_spdif_setup_gpio(int);
 
 
+struct samsung_i2s {
+/* If the Primary DAI has 5.1 Channels */
+#define QUIRK_PRI_6CHAN		(1 << 0)
+/* If the I2S block has a Stereo Overlay Channel */
+#define QUIRK_SEC_DAI		(1 << 1)
+/*
+ * If the I2S block has no internal prescalar or MUX (I2SMOD[10] bit)
+ * The Machine driver must provide suitably set clock to the I2S block.
+ */
+#define QUIRK_NO_MUXPSR		(1 << 2)
+#define QUIRK_NEED_RSTCLR	(1 << 3)
+	/* Quirks of the I2S controller */
+	u32 quirks;
+
+	/*
+	 * Array of clock names that can be used to generate I2S signals.
+	 * Also corresponds to clocks of I2SMOD[10]
+	 */
+	const char **src_clk;
+};
+
 /**
 /**
  * struct s3c_audio_pdata - common platform data for audio device drivers
  * struct s3c_audio_pdata - common platform data for audio device drivers
  * @cfg_gpio: Callback function to setup mux'ed pins in I2S/PCM/AC97 mode
  * @cfg_gpio: Callback function to setup mux'ed pins in I2S/PCM/AC97 mode
  */
  */
 struct s3c_audio_pdata {
 struct s3c_audio_pdata {
 	int (*cfg_gpio)(struct platform_device *);
 	int (*cfg_gpio)(struct platform_device *);
+	union {
+		struct samsung_i2s i2s;
+	} type;
 };
 };

+ 10 - 1
arch/arm/plat-samsung/include/plat/devs.h

@@ -32,7 +32,7 @@ extern struct platform_device s3c64xx_device_iisv4;
 extern struct platform_device s3c64xx_device_spi0;
 extern struct platform_device s3c64xx_device_spi0;
 extern struct platform_device s3c64xx_device_spi1;
 extern struct platform_device s3c64xx_device_spi1;
 
 
-extern struct platform_device s3c_device_pcm;
+extern struct platform_device samsung_asoc_dma;
 
 
 extern struct platform_device s3c64xx_device_pcm0;
 extern struct platform_device s3c64xx_device_pcm0;
 extern struct platform_device s3c64xx_device_pcm1;
 extern struct platform_device s3c64xx_device_pcm1;
@@ -96,6 +96,15 @@ extern struct platform_device s5pv210_device_iis1;
 extern struct platform_device s5pv210_device_iis2;
 extern struct platform_device s5pv210_device_iis2;
 extern struct platform_device s5pv210_device_spdif;
 extern struct platform_device s5pv210_device_spdif;
 
 
+extern struct platform_device s5pv310_device_ac97;
+extern struct platform_device s5pv310_device_pcm0;
+extern struct platform_device s5pv310_device_pcm1;
+extern struct platform_device s5pv310_device_pcm2;
+extern struct platform_device s5pv310_device_i2s0;
+extern struct platform_device s5pv310_device_i2s1;
+extern struct platform_device s5pv310_device_i2s2;
+extern struct platform_device s5pv310_device_spdif;
+
 extern struct platform_device s5p6442_device_pcm0;
 extern struct platform_device s5p6442_device_pcm0;
 extern struct platform_device s5p6442_device_pcm1;
 extern struct platform_device s5p6442_device_pcm1;
 extern struct platform_device s5p6442_device_iis0;
 extern struct platform_device s5p6442_device_iis0;

+ 5 - 0
arch/sh/boards/mach-se/7724/setup.c

@@ -318,6 +318,10 @@ static struct platform_device fsi_device = {
 	},
 	},
 };
 };
 
 
+static struct platform_device fsi_ak4642_device = {
+	.name		= "sh_fsi_a_ak4642",
+};
+
 /* KEYSC in SoC (Needs SW33-2 set to ON) */
 /* KEYSC in SoC (Needs SW33-2 set to ON) */
 static struct sh_keysc_info keysc_info = {
 static struct sh_keysc_info keysc_info = {
 	.mode = SH_KEYSC_MODE_1,
 	.mode = SH_KEYSC_MODE_1,
@@ -590,6 +594,7 @@ static struct platform_device *ms7724se_devices[] __initdata = {
 	&sh7724_usb0_host_device,
 	&sh7724_usb0_host_device,
 	&sh7724_usb1_gadget_device,
 	&sh7724_usb1_gadget_device,
 	&fsi_device,
 	&fsi_device,
+	&fsi_ak4642_device,
 	&sdhi0_cn7_device,
 	&sdhi0_cn7_device,
 	&sdhi1_cn8_device,
 	&sdhi1_cn8_device,
 	&irda_device,
 	&irda_device,

+ 24 - 0
drivers/gpio/wm8994-gpio.c

@@ -35,6 +35,29 @@ static inline struct wm8994_gpio *to_wm8994_gpio(struct gpio_chip *chip)
 	return container_of(chip, struct wm8994_gpio, gpio_chip);
 	return container_of(chip, struct wm8994_gpio, gpio_chip);
 }
 }
 
 
+static int wm8994_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+	struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip);
+	struct wm8994 *wm8994 = wm8994_gpio->wm8994;
+
+	switch (wm8994->type) {
+	case WM8958:
+		switch (offset) {
+		case 1:
+		case 2:
+		case 3:
+		case 4:
+		case 6:
+			return -EINVAL;
+		}
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
 static int wm8994_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
 static int wm8994_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
 {
 {
 	struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip);
 	struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip);
@@ -136,6 +159,7 @@ static void wm8994_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
 static struct gpio_chip template_chip = {
 static struct gpio_chip template_chip = {
 	.label			= "wm8994",
 	.label			= "wm8994",
 	.owner			= THIS_MODULE,
 	.owner			= THIS_MODULE,
+	.request		= wm8994_gpio_request,
 	.direction_input	= wm8994_gpio_direction_in,
 	.direction_input	= wm8994_gpio_direction_in,
 	.get			= wm8994_gpio_get,
 	.get			= wm8994_gpio_get,
 	.direction_output	= wm8994_gpio_direction_out,
 	.direction_output	= wm8994_gpio_direction_out,

+ 74 - 19
drivers/mfd/wm8994-core.c

@@ -218,6 +218,18 @@ static const char *wm8994_main_supplies[] = {
 	"SPKVDD2",
 	"SPKVDD2",
 };
 };
 
 
+static const char *wm8958_main_supplies[] = {
+	"DBVDD1",
+	"DBVDD2",
+	"DBVDD3",
+	"DCVDD",
+	"AVDD1",
+	"AVDD2",
+	"CPVDD",
+	"SPKVDD1",
+	"SPKVDD2",
+};
+
 #ifdef CONFIG_PM
 #ifdef CONFIG_PM
 static int wm8994_device_suspend(struct device *dev)
 static int wm8994_device_suspend(struct device *dev)
 {
 {
@@ -239,7 +251,7 @@ static int wm8994_device_suspend(struct device *dev)
 	if (ret < 0)
 	if (ret < 0)
 		dev_err(dev, "Failed to save LDO registers: %d\n", ret);
 		dev_err(dev, "Failed to save LDO registers: %d\n", ret);
 
 
-	ret = regulator_bulk_disable(ARRAY_SIZE(wm8994_main_supplies),
+	ret = regulator_bulk_disable(wm8994->num_supplies,
 				     wm8994->supplies);
 				     wm8994->supplies);
 	if (ret != 0) {
 	if (ret != 0) {
 		dev_err(dev, "Failed to disable supplies: %d\n", ret);
 		dev_err(dev, "Failed to disable supplies: %d\n", ret);
@@ -254,7 +266,7 @@ static int wm8994_device_resume(struct device *dev)
 	struct wm8994 *wm8994 = dev_get_drvdata(dev);
 	struct wm8994 *wm8994 = dev_get_drvdata(dev);
 	int ret;
 	int ret;
 
 
-	ret = regulator_bulk_enable(ARRAY_SIZE(wm8994_main_supplies),
+	ret = regulator_bulk_enable(wm8994->num_supplies,
 				    wm8994->supplies);
 				    wm8994->supplies);
 	if (ret != 0) {
 	if (ret != 0) {
 		dev_err(dev, "Failed to enable supplies: %d\n", ret);
 		dev_err(dev, "Failed to enable supplies: %d\n", ret);
@@ -305,9 +317,10 @@ static int wm8994_ldo_in_use(struct wm8994_pdata *pdata, int ldo)
 /*
 /*
  * Instantiate the generic non-control parts of the device.
  * Instantiate the generic non-control parts of the device.
  */
  */
-static int wm8994_device_init(struct wm8994 *wm8994, unsigned long id, int irq)
+static int wm8994_device_init(struct wm8994 *wm8994, int irq)
 {
 {
 	struct wm8994_pdata *pdata = wm8994->dev->platform_data;
 	struct wm8994_pdata *pdata = wm8994->dev->platform_data;
+	const char *devname;
 	int ret, i;
 	int ret, i;
 
 
 	mutex_init(&wm8994->io_lock);
 	mutex_init(&wm8994->io_lock);
@@ -323,25 +336,48 @@ static int wm8994_device_init(struct wm8994 *wm8994, unsigned long id, int irq)
 		goto err;
 		goto err;
 	}
 	}
 
 
+	switch (wm8994->type) {
+	case WM8994:
+		wm8994->num_supplies = ARRAY_SIZE(wm8994_main_supplies);
+		break;
+	case WM8958:
+		wm8994->num_supplies = ARRAY_SIZE(wm8958_main_supplies);
+		break;
+	default:
+		BUG();
+		return -EINVAL;
+	}
+
 	wm8994->supplies = kzalloc(sizeof(struct regulator_bulk_data) *
 	wm8994->supplies = kzalloc(sizeof(struct regulator_bulk_data) *
-				   ARRAY_SIZE(wm8994_main_supplies),
+				   wm8994->num_supplies,
 				   GFP_KERNEL);
 				   GFP_KERNEL);
 	if (!wm8994->supplies) {
 	if (!wm8994->supplies) {
 		ret = -ENOMEM;
 		ret = -ENOMEM;
 		goto err;
 		goto err;
 	}
 	}
 
 
-	for (i = 0; i < ARRAY_SIZE(wm8994_main_supplies); i++)
-		wm8994->supplies[i].supply = wm8994_main_supplies[i];
-
-	ret = regulator_bulk_get(wm8994->dev, ARRAY_SIZE(wm8994_main_supplies),
+	switch (wm8994->type) {
+	case WM8994:
+		for (i = 0; i < ARRAY_SIZE(wm8994_main_supplies); i++)
+			wm8994->supplies[i].supply = wm8994_main_supplies[i];
+		break;
+	case WM8958:
+		for (i = 0; i < ARRAY_SIZE(wm8958_main_supplies); i++)
+			wm8994->supplies[i].supply = wm8958_main_supplies[i];
+		break;
+	default:
+		BUG();
+		return -EINVAL;
+	}
+		
+	ret = regulator_bulk_get(wm8994->dev, wm8994->num_supplies,
 				 wm8994->supplies);
 				 wm8994->supplies);
 	if (ret != 0) {
 	if (ret != 0) {
 		dev_err(wm8994->dev, "Failed to get supplies: %d\n", ret);
 		dev_err(wm8994->dev, "Failed to get supplies: %d\n", ret);
 		goto err_supplies;
 		goto err_supplies;
 	}
 	}
 
 
-	ret = regulator_bulk_enable(ARRAY_SIZE(wm8994_main_supplies),
+	ret = regulator_bulk_enable(wm8994->num_supplies,
 				    wm8994->supplies);
 				    wm8994->supplies);
 	if (ret != 0) {
 	if (ret != 0) {
 		dev_err(wm8994->dev, "Failed to enable supplies: %d\n", ret);
 		dev_err(wm8994->dev, "Failed to enable supplies: %d\n", ret);
@@ -353,7 +389,22 @@ static int wm8994_device_init(struct wm8994 *wm8994, unsigned long id, int irq)
 		dev_err(wm8994->dev, "Failed to read ID register\n");
 		dev_err(wm8994->dev, "Failed to read ID register\n");
 		goto err_enable;
 		goto err_enable;
 	}
 	}
-	if (ret != 0x8994) {
+	switch (ret) {
+	case 0x8994:
+		devname = "WM8994";
+		if (wm8994->type != WM8994)
+			dev_warn(wm8994->dev, "Device registered as type %d\n",
+				 wm8994->type);
+		wm8994->type = WM8994;
+		break;
+	case 0x8958:
+		devname = "WM8958";
+		if (wm8994->type != WM8958)
+			dev_warn(wm8994->dev, "Device registered as type %d\n",
+				 wm8994->type);
+		wm8994->type = WM8958;
+		break;
+	default:
 		dev_err(wm8994->dev, "Device is not a WM8994, ID is %x\n",
 		dev_err(wm8994->dev, "Device is not a WM8994, ID is %x\n",
 			ret);
 			ret);
 		ret = -EINVAL;
 		ret = -EINVAL;
@@ -370,14 +421,16 @@ static int wm8994_device_init(struct wm8994 *wm8994, unsigned long id, int irq)
 	switch (ret) {
 	switch (ret) {
 	case 0:
 	case 0:
 	case 1:
 	case 1:
-		dev_warn(wm8994->dev, "revision %c not fully supported\n",
-			'A' + ret);
+		if (wm8994->type == WM8994)
+			dev_warn(wm8994->dev,
+				 "revision %c not fully supported\n",
+				 'A' + ret);
 		break;
 		break;
 	default:
 	default:
-		dev_info(wm8994->dev, "revision %c\n", 'A' + ret);
 		break;
 		break;
 	}
 	}
 
 
+	dev_info(wm8994->dev, "%s revision %c\n", devname, 'A' + ret);
 
 
 	if (pdata) {
 	if (pdata) {
 		wm8994->irq_base = pdata->irq_base;
 		wm8994->irq_base = pdata->irq_base;
@@ -423,10 +476,10 @@ static int wm8994_device_init(struct wm8994 *wm8994, unsigned long id, int irq)
 err_irq:
 err_irq:
 	wm8994_irq_exit(wm8994);
 	wm8994_irq_exit(wm8994);
 err_enable:
 err_enable:
-	regulator_bulk_disable(ARRAY_SIZE(wm8994_main_supplies),
+	regulator_bulk_disable(wm8994->num_supplies,
 			       wm8994->supplies);
 			       wm8994->supplies);
 err_get:
 err_get:
-	regulator_bulk_free(ARRAY_SIZE(wm8994_main_supplies), wm8994->supplies);
+	regulator_bulk_free(wm8994->num_supplies, wm8994->supplies);
 err_supplies:
 err_supplies:
 	kfree(wm8994->supplies);
 	kfree(wm8994->supplies);
 err:
 err:
@@ -439,9 +492,9 @@ static void wm8994_device_exit(struct wm8994 *wm8994)
 {
 {
 	mfd_remove_devices(wm8994->dev);
 	mfd_remove_devices(wm8994->dev);
 	wm8994_irq_exit(wm8994);
 	wm8994_irq_exit(wm8994);
-	regulator_bulk_disable(ARRAY_SIZE(wm8994_main_supplies),
+	regulator_bulk_disable(wm8994->num_supplies,
 			       wm8994->supplies);
 			       wm8994->supplies);
-	regulator_bulk_free(ARRAY_SIZE(wm8994_main_supplies), wm8994->supplies);
+	regulator_bulk_free(wm8994->num_supplies, wm8994->supplies);
 	kfree(wm8994->supplies);
 	kfree(wm8994->supplies);
 	kfree(wm8994);
 	kfree(wm8994);
 }
 }
@@ -506,8 +559,9 @@ static int wm8994_i2c_probe(struct i2c_client *i2c,
 	wm8994->read_dev = wm8994_i2c_read_device;
 	wm8994->read_dev = wm8994_i2c_read_device;
 	wm8994->write_dev = wm8994_i2c_write_device;
 	wm8994->write_dev = wm8994_i2c_write_device;
 	wm8994->irq = i2c->irq;
 	wm8994->irq = i2c->irq;
+	wm8994->type = id->driver_data;
 
 
-	return wm8994_device_init(wm8994, id->driver_data, i2c->irq);
+	return wm8994_device_init(wm8994, i2c->irq);
 }
 }
 
 
 static int wm8994_i2c_remove(struct i2c_client *i2c)
 static int wm8994_i2c_remove(struct i2c_client *i2c)
@@ -535,7 +589,8 @@ static int wm8994_i2c_resume(struct i2c_client *i2c)
 #endif
 #endif
 
 
 static const struct i2c_device_id wm8994_i2c_id[] = {
 static const struct i2c_device_id wm8994_i2c_id[] = {
-	{ "wm8994", 0 },
+	{ "wm8994", WM8994 },
+	{ "wm8958", WM8958 },
 	{ }
 	{ }
 };
 };
 MODULE_DEVICE_TABLE(i2c, wm8994_i2c_id);
 MODULE_DEVICE_TABLE(i2c, wm8994_i2c_id);

+ 21 - 2
drivers/regulator/wm8994-regulator.c

@@ -130,10 +130,19 @@ static struct regulator_ops wm8994_ldo1_ops = {
 static int wm8994_ldo2_list_voltage(struct regulator_dev *rdev,
 static int wm8994_ldo2_list_voltage(struct regulator_dev *rdev,
 				    unsigned int selector)
 				    unsigned int selector)
 {
 {
+	struct wm8994_ldo *ldo = rdev_get_drvdata(rdev);
+
 	if (selector > WM8994_LDO2_MAX_SELECTOR)
 	if (selector > WM8994_LDO2_MAX_SELECTOR)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	return (selector * 100000) + 900000;
+	switch (ldo->wm8994->type) {
+	case WM8994:
+		return (selector * 100000) + 900000;
+	case WM8958:
+		return (selector * 100000) + 1000000;
+	default:
+		return -EINVAL;
+	}
 }
 }
 
 
 static int wm8994_ldo2_get_voltage_sel(struct regulator_dev *rdev)
 static int wm8994_ldo2_get_voltage_sel(struct regulator_dev *rdev)
@@ -154,7 +163,17 @@ static int wm8994_ldo2_set_voltage(struct regulator_dev *rdev,
 	struct wm8994_ldo *ldo = rdev_get_drvdata(rdev);
 	struct wm8994_ldo *ldo = rdev_get_drvdata(rdev);
 	int selector, v;
 	int selector, v;
 
 
-	selector = (min_uV - 900000) / 100000;
+	switch (ldo->wm8994->type) {
+	case WM8994:
+		selector = (min_uV - 900000) / 100000;
+		break;
+	case WM8958:
+		selector = (min_uV - 1000000) / 100000;
+		break;
+	default:
+		return -EINVAL;
+	}
+
 	v = wm8994_ldo2_list_voltage(rdev, selector);
 	v = wm8994_ldo2_list_voltage(rdev, selector);
 	if (v < 0 || v > max_uV)
 	if (v < 0 || v > max_uV)
 		return -EINVAL;
 		return -EINVAL;

+ 1 - 0
drivers/video/sh_mobile_hdmi.c

@@ -22,6 +22,7 @@
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/types.h>
 #include <linux/types.h>
 #include <linux/workqueue.h>
 #include <linux/workqueue.h>
+#include <sound/soc.h>
 #include <sound/soc-dapm.h>
 #include <sound/soc-dapm.h>
 #include <sound/initval.h>
 #include <sound/initval.h>
 
 

+ 8 - 0
include/linux/mfd/wm8994/core.h

@@ -17,6 +17,11 @@
 
 
 #include <linux/interrupt.h>
 #include <linux/interrupt.h>
 
 
+enum wm8994_type {
+	WM8994 = 0,
+	WM8958 = 1,
+};
+
 struct regulator_dev;
 struct regulator_dev;
 struct regulator_bulk_data;
 struct regulator_bulk_data;
 
 
@@ -48,6 +53,8 @@ struct wm8994 {
 	struct mutex io_lock;
 	struct mutex io_lock;
 	struct mutex irq_lock;
 	struct mutex irq_lock;
 
 
+	enum wm8994_type type;
+
 	struct device *dev;
 	struct device *dev;
 	int (*read_dev)(struct wm8994 *wm8994, unsigned short reg,
 	int (*read_dev)(struct wm8994 *wm8994, unsigned short reg,
 			int bytes, void *dest);
 			int bytes, void *dest);
@@ -68,6 +75,7 @@ struct wm8994 {
 	u16 gpio_regs[WM8994_NUM_GPIO_REGS];
 	u16 gpio_regs[WM8994_NUM_GPIO_REGS];
 
 
 	struct regulator_dev *dbvdd;
 	struct regulator_dev *dbvdd;
+	int num_supplies;
 	struct regulator_bulk_data *supplies;
 	struct regulator_bulk_data *supplies;
 };
 };
 
 

+ 17 - 0
include/linux/mfd/wm8994/pdata.h

@@ -30,6 +30,8 @@ struct wm8994_ldo_pdata {
 
 
 #define WM8994_DRC_REGS 5
 #define WM8994_DRC_REGS 5
 #define WM8994_EQ_REGS  20
 #define WM8994_EQ_REGS  20
+#define WM8958_MBC_CUTOFF_REGS 20
+#define WM8958_MBC_COEFF_REGS  48
 
 
 /**
 /**
  * DRC configurations are specified with a label and a set of register
  * DRC configurations are specified with a label and a set of register
@@ -59,6 +61,18 @@ struct wm8994_retune_mobile_cfg {
         u16 regs[WM8994_EQ_REGS];
         u16 regs[WM8994_EQ_REGS];
 };
 };
 
 
+/**
+ * Multiband compressor configurations are specified with a label and
+ * two sets of values to write.  Configurations are expected to be
+ * generated using the multiband compressor configuration panel in
+ * WISCE - see http://www.wolfsonmicro.com/wisce/
+ */
+struct wm8958_mbc_cfg {
+	const char *name;
+	u16 cutoff_regs[WM8958_MBC_CUTOFF_REGS];
+	u16 coeff_regs[WM8958_MBC_COEFF_REGS];
+};
+
 struct wm8994_pdata {
 struct wm8994_pdata {
 	int gpio_base;
 	int gpio_base;
 
 
@@ -78,6 +92,9 @@ struct wm8994_pdata {
         int num_retune_mobile_cfgs;
         int num_retune_mobile_cfgs;
         struct wm8994_retune_mobile_cfg *retune_mobile_cfgs;
         struct wm8994_retune_mobile_cfg *retune_mobile_cfgs;
 
 
+	int num_mbc_cfgs;
+	struct wm8958_mbc_cfg *mbc_cfgs;
+
         /* LINEOUT can be differential or single ended */
         /* LINEOUT can be differential or single ended */
         unsigned int lineout1_diff:1;
         unsigned int lineout1_diff:1;
         unsigned int lineout2_diff:1;
         unsigned int lineout2_diff:1;

+ 302 - 0
include/linux/mfd/wm8994/registers.h

@@ -64,12 +64,16 @@
 #define WM8994_LDO_1                            0x3B
 #define WM8994_LDO_1                            0x3B
 #define WM8994_LDO_2                            0x3C
 #define WM8994_LDO_2                            0x3C
 #define WM8994_CHARGE_PUMP_1                    0x4C
 #define WM8994_CHARGE_PUMP_1                    0x4C
+#define WM8958_CHARGE_PUMP_2                    0x4D
 #define WM8994_CLASS_W_1                        0x51
 #define WM8994_CLASS_W_1                        0x51
 #define WM8994_DC_SERVO_1                       0x54
 #define WM8994_DC_SERVO_1                       0x54
 #define WM8994_DC_SERVO_2                       0x55
 #define WM8994_DC_SERVO_2                       0x55
 #define WM8994_DC_SERVO_4                       0x57
 #define WM8994_DC_SERVO_4                       0x57
 #define WM8994_DC_SERVO_READBACK                0x58
 #define WM8994_DC_SERVO_READBACK                0x58
 #define WM8994_ANALOGUE_HP_1                    0x60
 #define WM8994_ANALOGUE_HP_1                    0x60
+#define WM8958_MIC_DETECT_1                     0xD0
+#define WM8958_MIC_DETECT_2                     0xD1
+#define WM8958_MIC_DETECT_3                     0xD2
 #define WM8994_CHIP_REVISION                    0x100
 #define WM8994_CHIP_REVISION                    0x100
 #define WM8994_CONTROL_INTERFACE                0x101
 #define WM8994_CONTROL_INTERFACE                0x101
 #define WM8994_WRITE_SEQUENCER_CTRL_1           0x110
 #define WM8994_WRITE_SEQUENCER_CTRL_1           0x110
@@ -109,6 +113,10 @@
 #define WM8994_AIF2DAC_LRCLK                    0x315
 #define WM8994_AIF2DAC_LRCLK                    0x315
 #define WM8994_AIF2DAC_DATA                     0x316
 #define WM8994_AIF2DAC_DATA                     0x316
 #define WM8994_AIF2ADC_DATA                     0x317
 #define WM8994_AIF2ADC_DATA                     0x317
+#define WM8958_AIF3_CONTROL_1                   0x320
+#define WM8958_AIF3_CONTROL_2                   0x321
+#define WM8958_AIF3DAC_DATA                     0x322
+#define WM8958_AIF3ADC_DATA                     0x323
 #define WM8994_AIF1_ADC1_LEFT_VOLUME            0x400
 #define WM8994_AIF1_ADC1_LEFT_VOLUME            0x400
 #define WM8994_AIF1_ADC1_RIGHT_VOLUME           0x401
 #define WM8994_AIF1_ADC1_RIGHT_VOLUME           0x401
 #define WM8994_AIF1_DAC1_LEFT_VOLUME            0x402
 #define WM8994_AIF1_DAC1_LEFT_VOLUME            0x402
@@ -242,6 +250,83 @@
 #define WM8994_INTERRUPT_STATUS_2_MASK          0x739
 #define WM8994_INTERRUPT_STATUS_2_MASK          0x739
 #define WM8994_INTERRUPT_CONTROL                0x740
 #define WM8994_INTERRUPT_CONTROL                0x740
 #define WM8994_IRQ_DEBOUNCE                     0x748
 #define WM8994_IRQ_DEBOUNCE                     0x748
+#define WM8958_DSP2_PROGRAM                     0x900
+#define WM8958_DSP2_CONFIG                      0x901
+#define WM8958_DSP2_MAGICNUM                    0xA00
+#define WM8958_DSP2_RELEASEYEAR                 0xA01
+#define WM8958_DSP2_RELEASEMONTHDAY             0xA02
+#define WM8958_DSP2_RELEASETIME                 0xA03
+#define WM8958_DSP2_VERMAJMIN                   0xA04
+#define WM8958_DSP2_VERBUILD                    0xA05
+#define WM8958_DSP2_EXECCONTROL                 0xA0D
+#define WM8958_MBC_BAND_2_LOWER_CUTOFF_C1_1     0x2200
+#define WM8958_MBC_BAND_2_LOWER_CUTOFF_C1_2     0x2201
+#define WM8958_MBC_BAND_2_LOWER_CUTOFF_C2_1     0x2202
+#define WM8958_MBC_BAND_2_LOWER_CUTOFF_C2_2     0x2203
+#define WM8958_MBC_BAND_2_LOWER_CUTOFF_C3_1     0x2204
+#define WM8958_MBC_BAND_2_LOWER_CUTOFF_C3_2     0x2205
+#define WM8958_MBC_BAND_2_UPPER_CUTOFF_C2_1     0x2206
+#define WM8958_MBC_BAND_2_UPPER_CUTOFF_C2_2     0x2207
+#define WM8958_MBC_BAND_2_UPPER_CUTOFF_C3_1     0x2208
+#define WM8958_MBC_BAND_2_UPPER_CUTOFF_C3_2     0x2209
+#define WM8958_MBC_BAND_2_UPPER_CUTOFF_C1_1     0x220A
+#define WM8958_MBC_BAND_2_UPPER_CUTOFF_C1_2     0x220B
+#define WM8958_MBC_BAND_1_UPPER_CUTOFF_C1_1     0x220C
+#define WM8958_MBC_BAND_1_UPPER_CUTOFF_C1_2     0x220D
+#define WM8958_MBC_BAND_1_UPPER_CUTOFF_C2_1     0x220E
+#define WM8958_MBC_BAND_1_UPPER_CUTOFF_C2_2     0x220F
+#define WM8958_MBC_BAND_1_UPPER_CUTOFF_C3_1     0x2210
+#define WM8958_MBC_BAND_1_UPPER_CUTOFF_C3_2     0x2211
+#define WM8958_MBC_BAND_1_LOWER_CUTOFF_1        0x2212
+#define WM8958_MBC_BAND_1_LOWER_CUTOFF_2        0x2213
+#define WM8958_MBC_BAND_1_K_1                   0x2400
+#define WM8958_MBC_BAND_1_K_2                   0x2401
+#define WM8958_MBC_BAND_1_N1_1                  0x2402
+#define WM8958_MBC_BAND_1_N1_2                  0x2403
+#define WM8958_MBC_BAND_1_N2_1                  0x2404
+#define WM8958_MBC_BAND_1_N2_2                  0x2405
+#define WM8958_MBC_BAND_1_N3_1                  0x2406
+#define WM8958_MBC_BAND_1_N3_2                  0x2407
+#define WM8958_MBC_BAND_1_N4_1                  0x2408
+#define WM8958_MBC_BAND_1_N4_2                  0x2409
+#define WM8958_MBC_BAND_1_N5_1                  0x240A
+#define WM8958_MBC_BAND_1_N5_2                  0x240B
+#define WM8958_MBC_BAND_1_X1_1                  0x240C
+#define WM8958_MBC_BAND_1_X1_2                  0x240D
+#define WM8958_MBC_BAND_1_X2_1                  0x240E
+#define WM8958_MBC_BAND_1_X2_2                  0x240F
+#define WM8958_MBC_BAND_1_X3_1                  0x2410
+#define WM8958_MBC_BAND_1_X3_2                  0x2411
+#define WM8958_MBC_BAND_1_ATTACK_1              0x2412
+#define WM8958_MBC_BAND_1_ATTACK_2              0x2413
+#define WM8958_MBC_BAND_1_DECAY_1               0x2414
+#define WM8958_MBC_BAND_1_DECAY_2               0x2415
+#define WM8958_MBC_BAND_2_K_1                   0x2416
+#define WM8958_MBC_BAND_2_K_2                   0x2417
+#define WM8958_MBC_BAND_2_N1_1                  0x2418
+#define WM8958_MBC_BAND_2_N1_2                  0x2419
+#define WM8958_MBC_BAND_2_N2_1                  0x241A
+#define WM8958_MBC_BAND_2_N2_2                  0x241B
+#define WM8958_MBC_BAND_2_N3_1                  0x241C
+#define WM8958_MBC_BAND_2_N3_2                  0x241D
+#define WM8958_MBC_BAND_2_N4_1                  0x241E
+#define WM8958_MBC_BAND_2_N4_2                  0x241F
+#define WM8958_MBC_BAND_2_N5_1                  0x2420
+#define WM8958_MBC_BAND_2_N5_2                  0x2421
+#define WM8958_MBC_BAND_2_X1_1                  0x2422
+#define WM8958_MBC_BAND_2_X1_2                  0x2423
+#define WM8958_MBC_BAND_2_X2_1                  0x2424
+#define WM8958_MBC_BAND_2_X2_2                  0x2425
+#define WM8958_MBC_BAND_2_X3_1                  0x2426
+#define WM8958_MBC_BAND_2_X3_2                  0x2427
+#define WM8958_MBC_BAND_2_ATTACK_1              0x2428
+#define WM8958_MBC_BAND_2_ATTACK_2              0x2429
+#define WM8958_MBC_BAND_2_DECAY_1               0x242A
+#define WM8958_MBC_BAND_2_DECAY_2               0x242B
+#define WM8958_MBC_B2_PG2_1                     0x242C
+#define WM8958_MBC_B2_PG2_2                     0x242D
+#define WM8958_MBC_B1_PG2_1                     0x242E
+#define WM8958_MBC_B1_PG2_2                     0x242F
 #define WM8994_WRITE_SEQUENCER_0                0x3000
 #define WM8994_WRITE_SEQUENCER_0                0x3000
 #define WM8994_WRITE_SEQUENCER_1                0x3001
 #define WM8994_WRITE_SEQUENCER_1                0x3001
 #define WM8994_WRITE_SEQUENCER_2                0x3002
 #define WM8994_WRITE_SEQUENCER_2                0x3002
@@ -992,6 +1077,12 @@
 /*
 /*
  * R6 (0x06) - Power Management (6)
  * R6 (0x06) - Power Management (6)
  */
  */
+#define WM8958_AIF3ADC_SRC_MASK                 0x0600  /* AIF3ADC_SRC - [10:9] */
+#define WM8958_AIF3ADC_SRC_SHIFT                     9  /* AIF3ADC_SRC - [10:9] */
+#define WM8958_AIF3ADC_SRC_WIDTH                     2  /* AIF3ADC_SRC - [10:9] */
+#define WM8958_AIF2DAC_SRC_MASK                 0x0180  /* AIF2DAC_SRC - [8:7] */
+#define WM8958_AIF2DAC_SRC_SHIFT                     7  /* AIF2DAC_SRC - [8:7] */
+#define WM8958_AIF2DAC_SRC_WIDTH                     2  /* AIF2DAC_SRC - [8:7] */
 #define WM8994_AIF3_TRI                         0x0020  /* AIF3_TRI */
 #define WM8994_AIF3_TRI                         0x0020  /* AIF3_TRI */
 #define WM8994_AIF3_TRI_MASK                    0x0020  /* AIF3_TRI */
 #define WM8994_AIF3_TRI_MASK                    0x0020  /* AIF3_TRI */
 #define WM8994_AIF3_TRI_SHIFT                        5  /* AIF3_TRI */
 #define WM8994_AIF3_TRI_SHIFT                        5  /* AIF3_TRI */
@@ -1835,6 +1926,14 @@
 #define WM8994_CP_ENA_SHIFT                         15  /* CP_ENA */
 #define WM8994_CP_ENA_SHIFT                         15  /* CP_ENA */
 #define WM8994_CP_ENA_WIDTH                          1  /* CP_ENA */
 #define WM8994_CP_ENA_WIDTH                          1  /* CP_ENA */
 
 
+/*
+ * R77 (0x4D) - Charge Pump (2)
+ */
+#define WM8958_CP_DISCH                         0x8000  /* CP_DISCH */
+#define WM8958_CP_DISCH_MASK                    0x8000  /* CP_DISCH */
+#define WM8958_CP_DISCH_SHIFT                       15  /* CP_DISCH */
+#define WM8958_CP_DISCH_WIDTH                        1  /* CP_DISCH */
+
 /*
 /*
  * R81 (0x51) - Class W (1)
  * R81 (0x51) - Class W (1)
  */
  */
@@ -1951,6 +2050,46 @@
 #define WM8994_HPOUT1R_DLY_SHIFT                     1  /* HPOUT1R_DLY */
 #define WM8994_HPOUT1R_DLY_SHIFT                     1  /* HPOUT1R_DLY */
 #define WM8994_HPOUT1R_DLY_WIDTH                     1  /* HPOUT1R_DLY */
 #define WM8994_HPOUT1R_DLY_WIDTH                     1  /* HPOUT1R_DLY */
 
 
+/*
+ * R208 (0xD0) - Mic Detect 1
+ */
+#define WM8958_MICD_BIAS_STARTTIME_MASK         0xF000  /* MICD_BIAS_STARTTIME - [15:12] */
+#define WM8958_MICD_BIAS_STARTTIME_SHIFT            12  /* MICD_BIAS_STARTTIME - [15:12] */
+#define WM8958_MICD_BIAS_STARTTIME_WIDTH             4  /* MICD_BIAS_STARTTIME - [15:12] */
+#define WM8958_MICD_RATE_MASK                   0x0F00  /* MICD_RATE - [11:8] */
+#define WM8958_MICD_RATE_SHIFT                       8  /* MICD_RATE - [11:8] */
+#define WM8958_MICD_RATE_WIDTH                       4  /* MICD_RATE - [11:8] */
+#define WM8958_MICD_DBTIME                      0x0002  /* MICD_DBTIME */
+#define WM8958_MICD_DBTIME_MASK                 0x0002  /* MICD_DBTIME */
+#define WM8958_MICD_DBTIME_SHIFT                     1  /* MICD_DBTIME */
+#define WM8958_MICD_DBTIME_WIDTH                     1  /* MICD_DBTIME */
+#define WM8958_MICD_ENA                         0x0001  /* MICD_ENA */
+#define WM8958_MICD_ENA_MASK                    0x0001  /* MICD_ENA */
+#define WM8958_MICD_ENA_SHIFT                        0  /* MICD_ENA */
+#define WM8958_MICD_ENA_WIDTH                        1  /* MICD_ENA */
+
+/*
+ * R209 (0xD1) - Mic Detect 2
+ */
+#define WM8958_MICD_LVL_SEL_MASK                0x00FF  /* MICD_LVL_SEL - [7:0] */
+#define WM8958_MICD_LVL_SEL_SHIFT                    0  /* MICD_LVL_SEL - [7:0] */
+#define WM8958_MICD_LVL_SEL_WIDTH                    8  /* MICD_LVL_SEL - [7:0] */
+
+/*
+ * R210 (0xD2) - Mic Detect 3
+ */
+#define WM8958_MICD_LVL_MASK                    0x07FC  /* MICD_LVL - [10:2] */
+#define WM8958_MICD_LVL_SHIFT                        2  /* MICD_LVL - [10:2] */
+#define WM8958_MICD_LVL_WIDTH                        9  /* MICD_LVL - [10:2] */
+#define WM8958_MICD_VALID                       0x0002  /* MICD_VALID */
+#define WM8958_MICD_VALID_MASK                  0x0002  /* MICD_VALID */
+#define WM8958_MICD_VALID_SHIFT                      1  /* MICD_VALID */
+#define WM8958_MICD_VALID_WIDTH                      1  /* MICD_VALID */
+#define WM8958_MICD_STS                         0x0001  /* MICD_STS */
+#define WM8958_MICD_STS_MASK                    0x0001  /* MICD_STS */
+#define WM8958_MICD_STS_SHIFT                        0  /* MICD_STS */
+#define WM8958_MICD_STS_WIDTH                        1  /* MICD_STS */
+
 /*
 /*
  * R256 (0x100) - Chip Revision
  * R256 (0x100) - Chip Revision
  */
  */
@@ -2069,6 +2208,14 @@
 /*
 /*
  * R520 (0x208) - Clocking (1)
  * R520 (0x208) - Clocking (1)
  */
  */
+#define WM8958_DSP2CLK_ENA                      0x4000  /* DSP2CLK_ENA */
+#define WM8958_DSP2CLK_ENA_MASK                 0x4000  /* DSP2CLK_ENA */
+#define WM8958_DSP2CLK_ENA_SHIFT                    14  /* DSP2CLK_ENA */
+#define WM8958_DSP2CLK_ENA_WIDTH                     1  /* DSP2CLK_ENA */
+#define WM8958_DSP2CLK_SRC                      0x1000  /* DSP2CLK_SRC */
+#define WM8958_DSP2CLK_SRC_MASK                 0x1000  /* DSP2CLK_SRC */
+#define WM8958_DSP2CLK_SRC_SHIFT                    12  /* DSP2CLK_SRC */
+#define WM8958_DSP2CLK_SRC_WIDTH                     1  /* DSP2CLK_SRC */
 #define WM8994_TOCLK_ENA                        0x0010  /* TOCLK_ENA */
 #define WM8994_TOCLK_ENA                        0x0010  /* TOCLK_ENA */
 #define WM8994_TOCLK_ENA_MASK                   0x0010  /* TOCLK_ENA */
 #define WM8994_TOCLK_ENA_MASK                   0x0010  /* TOCLK_ENA */
 #define WM8994_TOCLK_ENA_SHIFT                       4  /* TOCLK_ENA */
 #define WM8994_TOCLK_ENA_SHIFT                       4  /* TOCLK_ENA */
@@ -2552,6 +2699,63 @@
 #define WM8994_AIF2ADCR_DAT_INV_SHIFT                0  /* AIF2ADCR_DAT_INV */
 #define WM8994_AIF2ADCR_DAT_INV_SHIFT                0  /* AIF2ADCR_DAT_INV */
 #define WM8994_AIF2ADCR_DAT_INV_WIDTH                1  /* AIF2ADCR_DAT_INV */
 #define WM8994_AIF2ADCR_DAT_INV_WIDTH                1  /* AIF2ADCR_DAT_INV */
 
 
+/*
+ * R800 (0x320) - AIF3 Control (1)
+ */
+#define WM8958_AIF3_LRCLK_INV                   0x0080  /* AIF3_LRCLK_INV */
+#define WM8958_AIF3_LRCLK_INV_MASK              0x0080  /* AIF3_LRCLK_INV */
+#define WM8958_AIF3_LRCLK_INV_SHIFT                  7  /* AIF3_LRCLK_INV */
+#define WM8958_AIF3_LRCLK_INV_WIDTH                  1  /* AIF3_LRCLK_INV */
+#define WM8958_AIF3_WL_MASK                     0x0060  /* AIF3_WL - [6:5] */
+#define WM8958_AIF3_WL_SHIFT                         5  /* AIF3_WL - [6:5] */
+#define WM8958_AIF3_WL_WIDTH                         2  /* AIF3_WL - [6:5] */
+#define WM8958_AIF3_FMT_MASK                    0x0018  /* AIF3_FMT - [4:3] */
+#define WM8958_AIF3_FMT_SHIFT                        3  /* AIF3_FMT - [4:3] */
+#define WM8958_AIF3_FMT_WIDTH                        2  /* AIF3_FMT - [4:3] */
+
+/*
+ * R801 (0x321) - AIF3 Control (2)
+ */
+#define WM8958_AIF3DAC_BOOST_MASK               0x0C00  /* AIF3DAC_BOOST - [11:10] */
+#define WM8958_AIF3DAC_BOOST_SHIFT                  10  /* AIF3DAC_BOOST - [11:10] */
+#define WM8958_AIF3DAC_BOOST_WIDTH                   2  /* AIF3DAC_BOOST - [11:10] */
+#define WM8958_AIF3DAC_COMP                     0x0010  /* AIF3DAC_COMP */
+#define WM8958_AIF3DAC_COMP_MASK                0x0010  /* AIF3DAC_COMP */
+#define WM8958_AIF3DAC_COMP_SHIFT                    4  /* AIF3DAC_COMP */
+#define WM8958_AIF3DAC_COMP_WIDTH                    1  /* AIF3DAC_COMP */
+#define WM8958_AIF3DAC_COMPMODE                 0x0008  /* AIF3DAC_COMPMODE */
+#define WM8958_AIF3DAC_COMPMODE_MASK            0x0008  /* AIF3DAC_COMPMODE */
+#define WM8958_AIF3DAC_COMPMODE_SHIFT                3  /* AIF3DAC_COMPMODE */
+#define WM8958_AIF3DAC_COMPMODE_WIDTH                1  /* AIF3DAC_COMPMODE */
+#define WM8958_AIF3ADC_COMP                     0x0004  /* AIF3ADC_COMP */
+#define WM8958_AIF3ADC_COMP_MASK                0x0004  /* AIF3ADC_COMP */
+#define WM8958_AIF3ADC_COMP_SHIFT                    2  /* AIF3ADC_COMP */
+#define WM8958_AIF3ADC_COMP_WIDTH                    1  /* AIF3ADC_COMP */
+#define WM8958_AIF3ADC_COMPMODE                 0x0002  /* AIF3ADC_COMPMODE */
+#define WM8958_AIF3ADC_COMPMODE_MASK            0x0002  /* AIF3ADC_COMPMODE */
+#define WM8958_AIF3ADC_COMPMODE_SHIFT                1  /* AIF3ADC_COMPMODE */
+#define WM8958_AIF3ADC_COMPMODE_WIDTH                1  /* AIF3ADC_COMPMODE */
+#define WM8958_AIF3_LOOPBACK                    0x0001  /* AIF3_LOOPBACK */
+#define WM8958_AIF3_LOOPBACK_MASK               0x0001  /* AIF3_LOOPBACK */
+#define WM8958_AIF3_LOOPBACK_SHIFT                   0  /* AIF3_LOOPBACK */
+#define WM8958_AIF3_LOOPBACK_WIDTH                   1  /* AIF3_LOOPBACK */
+
+/*
+ * R802 (0x322) - AIF3DAC Data
+ */
+#define WM8958_AIF3DAC_DAT_INV                  0x0001  /* AIF3DAC_DAT_INV */
+#define WM8958_AIF3DAC_DAT_INV_MASK             0x0001  /* AIF3DAC_DAT_INV */
+#define WM8958_AIF3DAC_DAT_INV_SHIFT                 0  /* AIF3DAC_DAT_INV */
+#define WM8958_AIF3DAC_DAT_INV_WIDTH                 1  /* AIF3DAC_DAT_INV */
+
+/*
+ * R803 (0x323) - AIF3ADC Data
+ */
+#define WM8958_AIF3ADC_DAT_INV                  0x0001  /* AIF3ADC_DAT_INV */
+#define WM8958_AIF3ADC_DAT_INV_MASK             0x0001  /* AIF3ADC_DAT_INV */
+#define WM8958_AIF3ADC_DAT_INV_SHIFT                 0  /* AIF3ADC_DAT_INV */
+#define WM8958_AIF3ADC_DAT_INV_WIDTH                 1  /* AIF3ADC_DAT_INV */
+
 /*
 /*
  * R1024 (0x400) - AIF1 ADC1 Left Volume
  * R1024 (0x400) - AIF1 ADC1 Left Volume
  */
  */
@@ -4289,4 +4493,102 @@
 #define WM8994_TEMP_SHUT_DB_SHIFT                    0  /* TEMP_SHUT_DB */
 #define WM8994_TEMP_SHUT_DB_SHIFT                    0  /* TEMP_SHUT_DB */
 #define WM8994_TEMP_SHUT_DB_WIDTH                    1  /* TEMP_SHUT_DB */
 #define WM8994_TEMP_SHUT_DB_WIDTH                    1  /* TEMP_SHUT_DB */
 
 
+/*
+ * R2304 (0x900) - DSP2_Program
+ */
+#define WM8958_DSP2_ENA                         0x0001  /* DSP2_ENA */
+#define WM8958_DSP2_ENA_MASK                    0x0001  /* DSP2_ENA */
+#define WM8958_DSP2_ENA_SHIFT                        0  /* DSP2_ENA */
+#define WM8958_DSP2_ENA_WIDTH                        1  /* DSP2_ENA */
+
+/*
+ * R2305 (0x901) - DSP2_Config
+ */
+#define WM8958_MBC_SEL_MASK                     0x0030  /* MBC_SEL - [5:4] */
+#define WM8958_MBC_SEL_SHIFT                         4  /* MBC_SEL - [5:4] */
+#define WM8958_MBC_SEL_WIDTH                         2  /* MBC_SEL - [5:4] */
+#define WM8958_MBC_ENA                          0x0001  /* MBC_ENA */
+#define WM8958_MBC_ENA_MASK                     0x0001  /* MBC_ENA */
+#define WM8958_MBC_ENA_SHIFT                         0  /* MBC_ENA */
+#define WM8958_MBC_ENA_WIDTH                         1  /* MBC_ENA */
+
+/*
+ * R2560 (0xA00) - DSP2_MagicNum
+ */
+#define WM8958_DSP2_MAGIC_NUM_MASK              0xFFFF  /* DSP2_MAGIC_NUM - [15:0] */
+#define WM8958_DSP2_MAGIC_NUM_SHIFT                  0  /* DSP2_MAGIC_NUM - [15:0] */
+#define WM8958_DSP2_MAGIC_NUM_WIDTH                 16  /* DSP2_MAGIC_NUM - [15:0] */
+
+/*
+ * R2561 (0xA01) - DSP2_ReleaseYear
+ */
+#define WM8958_DSP2_RELEASE_YEAR_MASK           0xFFFF  /* DSP2_RELEASE_YEAR - [15:0] */
+#define WM8958_DSP2_RELEASE_YEAR_SHIFT               0  /* DSP2_RELEASE_YEAR - [15:0] */
+#define WM8958_DSP2_RELEASE_YEAR_WIDTH              16  /* DSP2_RELEASE_YEAR - [15:0] */
+
+/*
+ * R2562 (0xA02) - DSP2_ReleaseMonthDay
+ */
+#define WM8958_DSP2_RELEASE_MONTH_MASK          0xFF00  /* DSP2_RELEASE_MONTH - [15:8] */
+#define WM8958_DSP2_RELEASE_MONTH_SHIFT              8  /* DSP2_RELEASE_MONTH - [15:8] */
+#define WM8958_DSP2_RELEASE_MONTH_WIDTH              8  /* DSP2_RELEASE_MONTH - [15:8] */
+#define WM8958_DSP2_RELEASE_DAY_MASK            0x00FF  /* DSP2_RELEASE_DAY - [7:0] */
+#define WM8958_DSP2_RELEASE_DAY_SHIFT                0  /* DSP2_RELEASE_DAY - [7:0] */
+#define WM8958_DSP2_RELEASE_DAY_WIDTH                8  /* DSP2_RELEASE_DAY - [7:0] */
+
+/*
+ * R2563 (0xA03) - DSP2_ReleaseTime
+ */
+#define WM8958_DSP2_RELEASE_HOURS_MASK          0xFF00  /* DSP2_RELEASE_HOURS - [15:8] */
+#define WM8958_DSP2_RELEASE_HOURS_SHIFT              8  /* DSP2_RELEASE_HOURS - [15:8] */
+#define WM8958_DSP2_RELEASE_HOURS_WIDTH              8  /* DSP2_RELEASE_HOURS - [15:8] */
+#define WM8958_DSP2_RELEASE_MINS_MASK           0x00FF  /* DSP2_RELEASE_MINS - [7:0] */
+#define WM8958_DSP2_RELEASE_MINS_SHIFT               0  /* DSP2_RELEASE_MINS - [7:0] */
+#define WM8958_DSP2_RELEASE_MINS_WIDTH               8  /* DSP2_RELEASE_MINS - [7:0] */
+
+/*
+ * R2564 (0xA04) - DSP2_VerMajMin
+ */
+#define WM8958_DSP2_MAJOR_VER_MASK              0xFF00  /* DSP2_MAJOR_VER - [15:8] */
+#define WM8958_DSP2_MAJOR_VER_SHIFT                  8  /* DSP2_MAJOR_VER - [15:8] */
+#define WM8958_DSP2_MAJOR_VER_WIDTH                  8  /* DSP2_MAJOR_VER - [15:8] */
+#define WM8958_DSP2_MINOR_VER_MASK              0x00FF  /* DSP2_MINOR_VER - [7:0] */
+#define WM8958_DSP2_MINOR_VER_SHIFT                  0  /* DSP2_MINOR_VER - [7:0] */
+#define WM8958_DSP2_MINOR_VER_WIDTH                  8  /* DSP2_MINOR_VER - [7:0] */
+
+/*
+ * R2565 (0xA05) - DSP2_VerBuild
+ */
+#define WM8958_DSP2_BUILD_VER_MASK              0xFFFF  /* DSP2_BUILD_VER - [15:0] */
+#define WM8958_DSP2_BUILD_VER_SHIFT                  0  /* DSP2_BUILD_VER - [15:0] */
+#define WM8958_DSP2_BUILD_VER_WIDTH                 16  /* DSP2_BUILD_VER - [15:0] */
+
+/*
+ * R2573 (0xA0D) - DSP2_ExecControl
+ */
+#define WM8958_DSP2_STOPC                       0x0020  /* DSP2_STOPC */
+#define WM8958_DSP2_STOPC_MASK                  0x0020  /* DSP2_STOPC */
+#define WM8958_DSP2_STOPC_SHIFT                      5  /* DSP2_STOPC */
+#define WM8958_DSP2_STOPC_WIDTH                      1  /* DSP2_STOPC */
+#define WM8958_DSP2_STOPS                       0x0010  /* DSP2_STOPS */
+#define WM8958_DSP2_STOPS_MASK                  0x0010  /* DSP2_STOPS */
+#define WM8958_DSP2_STOPS_SHIFT                      4  /* DSP2_STOPS */
+#define WM8958_DSP2_STOPS_WIDTH                      1  /* DSP2_STOPS */
+#define WM8958_DSP2_STOPI                       0x0008  /* DSP2_STOPI */
+#define WM8958_DSP2_STOPI_MASK                  0x0008  /* DSP2_STOPI */
+#define WM8958_DSP2_STOPI_SHIFT                      3  /* DSP2_STOPI */
+#define WM8958_DSP2_STOPI_WIDTH                      1  /* DSP2_STOPI */
+#define WM8958_DSP2_STOP                        0x0004  /* DSP2_STOP */
+#define WM8958_DSP2_STOP_MASK                   0x0004  /* DSP2_STOP */
+#define WM8958_DSP2_STOP_SHIFT                       2  /* DSP2_STOP */
+#define WM8958_DSP2_STOP_WIDTH                       1  /* DSP2_STOP */
+#define WM8958_DSP2_RUNR                        0x0002  /* DSP2_RUNR */
+#define WM8958_DSP2_RUNR_MASK                   0x0002  /* DSP2_RUNR */
+#define WM8958_DSP2_RUNR_SHIFT                       1  /* DSP2_RUNR */
+#define WM8958_DSP2_RUNR_WIDTH                       1  /* DSP2_RUNR */
+#define WM8958_DSP2_RUN                         0x0001  /* DSP2_RUN */
+#define WM8958_DSP2_RUN_MASK                    0x0001  /* DSP2_RUN */
+#define WM8958_DSP2_RUN_SHIFT                        0  /* DSP2_RUN */
+#define WM8958_DSP2_RUN_WIDTH                        1  /* DSP2_RUN */
+
 #endif
 #endif

+ 15 - 0
include/sound/alc5623.h

@@ -0,0 +1,15 @@
+#ifndef _INCLUDE_SOUND_ALC5623_H
+#define _INCLUDE_SOUND_ALC5623_H
+struct alc5623_platform_data {
+	/* configure :                              */
+	/* Lineout/Speaker Amps Vmid ratio control  */
+	/* enable/disable adc/dac high pass filters */
+	unsigned int add_ctrl;
+	/* configure :                              */
+	/* output to enable when jack is low        */
+	/* output to enable when jack is high       */
+	/* jack detect (gpio/nc/jack detect [12]    */
+	unsigned int jack_det_ctrl;
+};
+#endif
+

+ 3 - 0
include/sound/asound.h

@@ -259,6 +259,7 @@ typedef int __bitwise snd_pcm_subformat_t;
 #define SNDRV_PCM_INFO_HALF_DUPLEX	0x00100000	/* only half duplex */
 #define SNDRV_PCM_INFO_HALF_DUPLEX	0x00100000	/* only half duplex */
 #define SNDRV_PCM_INFO_JOINT_DUPLEX	0x00200000	/* playback and capture stream are somewhat correlated */
 #define SNDRV_PCM_INFO_JOINT_DUPLEX	0x00200000	/* playback and capture stream are somewhat correlated */
 #define SNDRV_PCM_INFO_SYNC_START	0x00400000	/* pcm support some kind of sync go */
 #define SNDRV_PCM_INFO_SYNC_START	0x00400000	/* pcm support some kind of sync go */
+#define SNDRV_PCM_INFO_NO_PERIOD_WAKEUP	0x00800000	/* period wakeup can be disabled */
 #define SNDRV_PCM_INFO_FIFO_IN_FRAMES	0x80000000	/* internal kernel flag - FIFO size is in frames */
 #define SNDRV_PCM_INFO_FIFO_IN_FRAMES	0x80000000	/* internal kernel flag - FIFO size is in frames */
 
 
 typedef int __bitwise snd_pcm_state_t;
 typedef int __bitwise snd_pcm_state_t;
@@ -334,6 +335,8 @@ typedef int snd_pcm_hw_param_t;
 #define	SNDRV_PCM_HW_PARAM_LAST_INTERVAL	SNDRV_PCM_HW_PARAM_TICK_TIME
 #define	SNDRV_PCM_HW_PARAM_LAST_INTERVAL	SNDRV_PCM_HW_PARAM_TICK_TIME
 
 
 #define SNDRV_PCM_HW_PARAMS_NORESAMPLE	(1<<0)	/* avoid rate resampling */
 #define SNDRV_PCM_HW_PARAMS_NORESAMPLE	(1<<0)	/* avoid rate resampling */
+#define SNDRV_PCM_HW_PARAMS_EXPORT_BUFFER	(1<<1)	/* export buffer */
+#define SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP	(1<<2)	/* disable period wakeups */
 
 
 struct snd_interval {
 struct snd_interval {
 	unsigned int min, max;
 	unsigned int min, max;

+ 3 - 1
include/sound/control.h

@@ -160,12 +160,14 @@ static inline struct snd_ctl_elem_id *snd_ctl_build_ioff(struct snd_ctl_elem_id
 }
 }
 
 
 /*
 /*
- * Frequently used control callbacks
+ * Frequently used control callbacks/helpers
  */
  */
 int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
 int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
 			      struct snd_ctl_elem_info *uinfo);
 			      struct snd_ctl_elem_info *uinfo);
 int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
 int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
 				struct snd_ctl_elem_info *uinfo);
 				struct snd_ctl_elem_info *uinfo);
+int snd_ctl_enum_info(struct snd_ctl_elem_info *info, unsigned int channels,
+		      unsigned int items, const char *const names[]);
 
 
 /*
 /*
  * virtual master control
  * virtual master control

+ 1 - 0
include/sound/hdsp.h

@@ -28,6 +28,7 @@ enum HDSP_IO_Type {
 	Multiface,
 	Multiface,
 	H9652,
 	H9652,
 	H9632,
 	H9632,
+	RPM,
 	Undefined,
 	Undefined,
 };
 };
 
 

+ 2 - 2
include/sound/minors.h

@@ -31,8 +31,8 @@
 /* these minors can still be used for autoloading devices (/dev/aload*) */
 /* these minors can still be used for autoloading devices (/dev/aload*) */
 #define SNDRV_MINOR_CONTROL		0	/* 0 */
 #define SNDRV_MINOR_CONTROL		0	/* 0 */
 #define SNDRV_MINOR_GLOBAL		1	/* 1 */
 #define SNDRV_MINOR_GLOBAL		1	/* 1 */
-#define SNDRV_MINOR_SEQUENCER		(SNDRV_MINOR_GLOBAL + 0 * 32)
-#define SNDRV_MINOR_TIMER		(SNDRV_MINOR_GLOBAL + 1 * 32)
+#define SNDRV_MINOR_SEQUENCER		1	/* SNDRV_MINOR_GLOBAL + 0 * 32 */
+#define SNDRV_MINOR_TIMER		33	/* SNDRV_MINOR_GLOBAL + 1 * 32 */
 
 
 #ifndef CONFIG_SND_DYNAMIC_MINORS
 #ifndef CONFIG_SND_DYNAMIC_MINORS
 						/* 2 - 3 (reserved) */
 						/* 2 - 3 (reserved) */

+ 1 - 0
include/sound/pcm.h

@@ -297,6 +297,7 @@ struct snd_pcm_runtime {
 	unsigned int info;
 	unsigned int info;
 	unsigned int rate_num;
 	unsigned int rate_num;
 	unsigned int rate_den;
 	unsigned int rate_den;
+	unsigned int no_period_wakeup: 1;
 
 
 	/* -- SW params -- */
 	/* -- SW params -- */
 	int tstamp_mode;		/* mmap timestamp is updated */
 	int tstamp_mode;		/* mmap timestamp is updated */

+ 1 - 3
include/sound/soc-dai.h

@@ -16,8 +16,6 @@
 
 
 #include <linux/list.h>
 #include <linux/list.h>
 
 
-#include <sound/soc.h>
-
 struct snd_pcm_substream;
 struct snd_pcm_substream;
 
 
 /*
 /*
@@ -205,7 +203,7 @@ struct snd_soc_dai_driver {
 	int (*resume)(struct snd_soc_dai *dai);
 	int (*resume)(struct snd_soc_dai *dai);
 
 
 	/* ops */
 	/* ops */
-	struct snd_soc_dai_ops *ops;
+	const struct snd_soc_dai_ops *ops;
 
 
 	/* DAI capabilities */
 	/* DAI capabilities */
 	struct snd_soc_pcm_stream capture;
 	struct snd_soc_pcm_stream capture;

+ 69 - 30
include/sound/soc-dapm.h

@@ -16,7 +16,6 @@
 #include <linux/device.h>
 #include <linux/device.h>
 #include <linux/types.h>
 #include <linux/types.h>
 #include <sound/control.h>
 #include <sound/control.h>
-#include <sound/soc.h>
 
 
 /* widget has no PM register bit */
 /* widget has no PM register bit */
 #define SND_SOC_NOPM	-1
 #define SND_SOC_NOPM	-1
@@ -72,6 +71,10 @@
 	 wcontrols, wncontrols) \
 	 wcontrols, wncontrols) \
 {	.id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
 {	.id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
 	.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols}
 	.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols}
+#define SND_SOC_DAPM_OUT_DRV(wname, wreg, wshift, winvert,\
+	 wcontrols, wncontrols) \
+{	.id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \
+	.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols}
 #define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, \
 #define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, \
 	 wcontrols, wncontrols)\
 	 wcontrols, wncontrols)\
 {	.id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
 {	.id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
@@ -90,6 +93,9 @@
 #define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \
 #define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \
 {	.id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \
 {	.id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \
 	.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1}
 	.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1}
+#define SND_SOC_DAPM_VIRT_MUX(wname, wreg, wshift, winvert, wcontrols) \
+{	.id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \
+	.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1}
 #define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \
 #define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \
 {	.id = snd_soc_dapm_value_mux, .name = wname, .reg = wreg, \
 {	.id = snd_soc_dapm_value_mux, .name = wname, .reg = wreg, \
 	.shift = wshift, .invert = winvert, .kcontrols = wcontrols, \
 	.shift = wshift, .invert = winvert, .kcontrols = wcontrols, \
@@ -116,6 +122,11 @@
 {	.id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
 {	.id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
 	.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \
 	.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \
 	.event = wevent, .event_flags = wflags}
 	.event = wevent, .event_flags = wflags}
+#define SND_SOC_DAPM_OUT_DRV_E(wname, wreg, wshift, winvert, wcontrols, \
+	wncontrols, wevent, wflags) \
+{	.id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \
+	.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \
+	.event = wevent, .event_flags = wflags}
 #define SND_SOC_DAPM_MIXER_E(wname, wreg, wshift, winvert, wcontrols, \
 #define SND_SOC_DAPM_MIXER_E(wname, wreg, wshift, winvert, wcontrols, \
 	wncontrols, wevent, wflags) \
 	wncontrols, wevent, wflags) \
 {	.id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
 {	.id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
@@ -140,6 +151,11 @@
 {	.id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \
 {	.id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \
 	.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \
 	.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \
 	.event = wevent, .event_flags = wflags}
 	.event = wevent, .event_flags = wflags}
+#define SND_SOC_DAPM_VIRT_MUX_E(wname, wreg, wshift, winvert, wcontrols, \
+	wevent, wflags) \
+{	.id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \
+	.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \
+	.event = wevent, .event_flags = wflags}
 
 
 /* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
 /* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
 #define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
 #define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
@@ -219,13 +235,6 @@
 	.info = snd_soc_info_volsw, \
 	.info = snd_soc_info_volsw, \
 	.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
 	.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
 	.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
 	.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
-#define SOC_DAPM_DOUBLE(xname, reg, shift_left, shift_right, max, invert, \
-	power) \
-{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
-	.info = snd_soc_info_volsw, \
- 	.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
- 	.private_value = (reg) | ((shift_left) << 8) | ((shift_right) << 12) |\
-		((max) << 16) | ((invert) << 24) }
 #define SOC_DAPM_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \
 #define SOC_DAPM_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 	.info = snd_soc_info_volsw, \
 	.info = snd_soc_info_volsw, \
@@ -233,15 +242,6 @@
 	.tlv.p = (tlv_array), \
 	.tlv.p = (tlv_array), \
 	.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
 	.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
 	.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
 	.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
-#define SOC_DAPM_DOUBLE_TLV(xname, reg, shift_left, shift_right, max, invert, \
-	power, tlv_array) \
-{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
-	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,\
-	.tlv.p = (tlv_array), \
-	.info = snd_soc_info_volsw, \
-	.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
-	.private_value = (reg) | ((shift_left) << 8) | ((shift_right) << 12) |\
-		((max) << 16) | ((invert) << 24) }
 #define SOC_DAPM_ENUM(xname, xenum) \
 #define SOC_DAPM_ENUM(xname, xenum) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 	.info = snd_soc_info_enum_double, \
 	.info = snd_soc_info_enum_double, \
@@ -297,6 +297,7 @@ enum snd_soc_dapm_type;
 struct snd_soc_dapm_path;
 struct snd_soc_dapm_path;
 struct snd_soc_dapm_pin;
 struct snd_soc_dapm_pin;
 struct snd_soc_dapm_route;
 struct snd_soc_dapm_route;
+struct snd_soc_dapm_context;
 
 
 int dapm_reg_event(struct snd_soc_dapm_widget *w,
 int dapm_reg_event(struct snd_soc_dapm_widget *w,
 		   struct snd_kcontrol *kcontrol, int event);
 		   struct snd_kcontrol *kcontrol, int event);
@@ -324,16 +325,16 @@ int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *uncontrol);
 	struct snd_ctl_elem_value *uncontrol);
 int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol,
 int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *uncontrol);
 	struct snd_ctl_elem_value *uncontrol);
-int snd_soc_dapm_new_control(struct snd_soc_codec *codec,
+int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
 	const struct snd_soc_dapm_widget *widget);
 	const struct snd_soc_dapm_widget *widget);
-int snd_soc_dapm_new_controls(struct snd_soc_codec *codec,
+int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
 	const struct snd_soc_dapm_widget *widget,
 	const struct snd_soc_dapm_widget *widget,
 	int num);
 	int num);
 
 
 /* dapm path setup */
 /* dapm path setup */
-int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec);
-void snd_soc_dapm_free(struct snd_soc_codec *codec);
-int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
+int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm);
+void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm);
+int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
 			    const struct snd_soc_dapm_route *route, int num);
 			    const struct snd_soc_dapm_route *route, int num);
 
 
 /* dapm events */
 /* dapm events */
@@ -343,27 +344,33 @@ void snd_soc_dapm_shutdown(struct snd_soc_card *card);
 
 
 /* dapm sys fs - used by the core */
 /* dapm sys fs - used by the core */
 int snd_soc_dapm_sys_add(struct device *dev);
 int snd_soc_dapm_sys_add(struct device *dev);
-void snd_soc_dapm_debugfs_init(struct snd_soc_codec *codec);
+void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm);
 
 
 /* dapm audio pin control and status */
 /* dapm audio pin control and status */
-int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, const char *pin);
-int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, const char *pin);
-int snd_soc_dapm_nc_pin(struct snd_soc_codec *codec, const char *pin);
-int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, const char *pin);
-int snd_soc_dapm_sync(struct snd_soc_codec *codec);
-int snd_soc_dapm_force_enable_pin(struct snd_soc_codec *codec,
+int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm,
+			    const char *pin);
+int snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm,
+			     const char *pin);
+int snd_soc_dapm_nc_pin(struct snd_soc_dapm_context *dapm, const char *pin);
+int snd_soc_dapm_get_pin_status(struct snd_soc_dapm_context *dapm,
+				const char *pin);
+int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm);
+int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm,
 				  const char *pin);
 				  const char *pin);
-int snd_soc_dapm_ignore_suspend(struct snd_soc_codec *codec, const char *pin);
+int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm,
+				const char *pin);
 
 
 /* dapm widget types */
 /* dapm widget types */
 enum snd_soc_dapm_type {
 enum snd_soc_dapm_type {
 	snd_soc_dapm_input = 0,		/* input pin */
 	snd_soc_dapm_input = 0,		/* input pin */
 	snd_soc_dapm_output,		/* output pin */
 	snd_soc_dapm_output,		/* output pin */
 	snd_soc_dapm_mux,			/* selects 1 analog signal from many inputs */
 	snd_soc_dapm_mux,			/* selects 1 analog signal from many inputs */
+	snd_soc_dapm_virt_mux,			/* virtual version of snd_soc_dapm_mux */
 	snd_soc_dapm_value_mux,			/* selects 1 analog signal from many inputs */
 	snd_soc_dapm_value_mux,			/* selects 1 analog signal from many inputs */
 	snd_soc_dapm_mixer,			/* mixes several analog signals together */
 	snd_soc_dapm_mixer,			/* mixes several analog signals together */
 	snd_soc_dapm_mixer_named_ctl,		/* mixer with named controls */
 	snd_soc_dapm_mixer_named_ctl,		/* mixer with named controls */
 	snd_soc_dapm_pga,			/* programmable gain/attenuation (volume) */
 	snd_soc_dapm_pga,			/* programmable gain/attenuation (volume) */
+	snd_soc_dapm_out_drv,			/* output driver */
 	snd_soc_dapm_adc,			/* analog to digital converter */
 	snd_soc_dapm_adc,			/* analog to digital converter */
 	snd_soc_dapm_dac,			/* digital to analog converter */
 	snd_soc_dapm_dac,			/* digital to analog converter */
 	snd_soc_dapm_micbias,		/* microphone bias (power) */
 	snd_soc_dapm_micbias,		/* microphone bias (power) */
@@ -425,6 +432,7 @@ struct snd_soc_dapm_widget {
 	char *sname;	/* stream name */
 	char *sname;	/* stream name */
 	struct snd_soc_codec *codec;
 	struct snd_soc_codec *codec;
 	struct list_head list;
 	struct list_head list;
+	struct snd_soc_dapm_context *dapm;
 
 
 	/* dapm control */
 	/* dapm control */
 	short reg;						/* negative reg = no direct dapm */
 	short reg;						/* negative reg = no direct dapm */
@@ -461,4 +469,35 @@ struct snd_soc_dapm_widget {
 	struct list_head power_list;
 	struct list_head power_list;
 };
 };
 
 
+struct snd_soc_dapm_update {
+	struct snd_soc_dapm_widget *widget;
+	struct snd_kcontrol *kcontrol;
+	int reg;
+	int mask;
+	int val;
+};
+
+/* DAPM context */
+struct snd_soc_dapm_context {
+	int n_widgets; /* number of widgets in this context */
+	enum snd_soc_bias_level bias_level;
+	enum snd_soc_bias_level suspend_bias_level;
+	struct delayed_work delayed_work;
+	unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */
+
+	struct snd_soc_dapm_update *update;
+
+	struct device *dev; /* from parent - for debug */
+	struct snd_soc_codec *codec; /* parent codec */
+	struct snd_soc_card *card; /* parent card */
+
+	/* used during DAPM updates */
+	int dev_power;
+	struct list_head list;
+
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *debugfs_dapm;
+#endif
+};
+
 #endif
 #endif

+ 92 - 24
include/sound/soc.h

@@ -222,10 +222,8 @@ enum snd_soc_bias_level {
 
 
 struct snd_jack;
 struct snd_jack;
 struct snd_soc_card;
 struct snd_soc_card;
-struct snd_soc_device;
 struct snd_soc_pcm_stream;
 struct snd_soc_pcm_stream;
 struct snd_soc_ops;
 struct snd_soc_ops;
-struct snd_soc_dai_mode;
 struct snd_soc_pcm_runtime;
 struct snd_soc_pcm_runtime;
 struct snd_soc_dai;
 struct snd_soc_dai;
 struct snd_soc_dai_driver;
 struct snd_soc_dai_driver;
@@ -235,9 +233,10 @@ struct snd_soc_platform_driver;
 struct snd_soc_codec;
 struct snd_soc_codec;
 struct snd_soc_codec_driver;
 struct snd_soc_codec_driver;
 struct soc_enum;
 struct soc_enum;
-struct snd_soc_ac97_ops;
 struct snd_soc_jack;
 struct snd_soc_jack;
 struct snd_soc_jack_pin;
 struct snd_soc_jack_pin;
+struct snd_soc_cache_ops;
+#include <sound/soc-dapm.h>
 
 
 #ifdef CONFIG_GPIOLIB
 #ifdef CONFIG_GPIOLIB
 struct snd_soc_jack_gpio;
 struct snd_soc_jack_gpio;
@@ -253,17 +252,30 @@ enum snd_soc_control_type {
 	SND_SOC_SPI,
 	SND_SOC_SPI,
 };
 };
 
 
+enum snd_soc_compress_type {
+	SND_SOC_FLAT_COMPRESSION = 1,
+	SND_SOC_LZO_COMPRESSION,
+	SND_SOC_RBTREE_COMPRESSION
+};
+
 int snd_soc_register_platform(struct device *dev,
 int snd_soc_register_platform(struct device *dev,
 		struct snd_soc_platform_driver *platform_drv);
 		struct snd_soc_platform_driver *platform_drv);
 void snd_soc_unregister_platform(struct device *dev);
 void snd_soc_unregister_platform(struct device *dev);
 int snd_soc_register_codec(struct device *dev,
 int snd_soc_register_codec(struct device *dev,
-		struct snd_soc_codec_driver *codec_drv,
+		const struct snd_soc_codec_driver *codec_drv,
 		struct snd_soc_dai_driver *dai_drv, int num_dai);
 		struct snd_soc_dai_driver *dai_drv, int num_dai);
 void snd_soc_unregister_codec(struct device *dev);
 void snd_soc_unregister_codec(struct device *dev);
 int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg);
 int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg);
 int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
 int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
 			       int addr_bits, int data_bits,
 			       int addr_bits, int data_bits,
 			       enum snd_soc_control_type control);
 			       enum snd_soc_control_type control);
+int snd_soc_cache_sync(struct snd_soc_codec *codec);
+int snd_soc_cache_init(struct snd_soc_codec *codec);
+int snd_soc_cache_exit(struct snd_soc_codec *codec);
+int snd_soc_cache_write(struct snd_soc_codec *codec,
+			unsigned int reg, unsigned int value);
+int snd_soc_cache_read(struct snd_soc_codec *codec,
+		       unsigned int reg, unsigned int *value);
 
 
 /* Utility functions to get clock rates from various things */
 /* Utility functions to get clock rates from various things */
 int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots);
 int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots);
@@ -420,23 +432,37 @@ struct snd_soc_ops {
 	int (*trigger)(struct snd_pcm_substream *, int);
 	int (*trigger)(struct snd_pcm_substream *, int);
 };
 };
 
 
+/* SoC cache ops */
+struct snd_soc_cache_ops {
+	const char *name;
+	enum snd_soc_compress_type id;
+	int (*init)(struct snd_soc_codec *codec);
+	int (*exit)(struct snd_soc_codec *codec);
+	int (*read)(struct snd_soc_codec *codec, unsigned int reg,
+		unsigned int *value);
+	int (*write)(struct snd_soc_codec *codec, unsigned int reg,
+		unsigned int value);
+	int (*sync)(struct snd_soc_codec *codec);
+};
+
 /* SoC Audio Codec device */
 /* SoC Audio Codec device */
 struct snd_soc_codec {
 struct snd_soc_codec {
 	const char *name;
 	const char *name;
+	const char *name_prefix;
 	int id;
 	int id;
 	struct device *dev;
 	struct device *dev;
-	struct snd_soc_codec_driver *driver;
+	const struct snd_soc_codec_driver *driver;
 
 
 	struct mutex mutex;
 	struct mutex mutex;
 	struct snd_soc_card *card;
 	struct snd_soc_card *card;
 	struct list_head list;
 	struct list_head list;
 	struct list_head card_list;
 	struct list_head card_list;
 	int num_dai;
 	int num_dai;
+	enum snd_soc_compress_type compress_type;
 
 
 	/* runtime */
 	/* runtime */
 	struct snd_ac97 *ac97;  /* for ad-hoc ac97 devices */
 	struct snd_ac97 *ac97;  /* for ad-hoc ac97 devices */
 	unsigned int active;
 	unsigned int active;
-	unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */
 	unsigned int cache_only:1;  /* Suppress writes to hardware */
 	unsigned int cache_only:1;  /* Suppress writes to hardware */
 	unsigned int cache_sync:1; /* Cache needs to be synced to hardware */
 	unsigned int cache_sync:1; /* Cache needs to be synced to hardware */
 	unsigned int suspended:1; /* Codec is in suspend PM state */
 	unsigned int suspended:1; /* Codec is in suspend PM state */
@@ -444,25 +470,25 @@ struct snd_soc_codec {
 	unsigned int ac97_registered:1; /* Codec has been AC97 registered */
 	unsigned int ac97_registered:1; /* Codec has been AC97 registered */
 	unsigned int ac97_created:1; /* Codec has been created by SoC */
 	unsigned int ac97_created:1; /* Codec has been created by SoC */
 	unsigned int sysfs_registered:1; /* codec has been sysfs registered */
 	unsigned int sysfs_registered:1; /* codec has been sysfs registered */
+	unsigned int cache_init:1; /* codec cache has been initialized */
 
 
 	/* codec IO */
 	/* codec IO */
 	void *control_data; /* codec control (i2c/3wire) data */
 	void *control_data; /* codec control (i2c/3wire) data */
 	hw_write_t hw_write;
 	hw_write_t hw_write;
 	unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int);
 	unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int);
+	unsigned int (*read)(struct snd_soc_codec *, unsigned int);
+	int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
 	void *reg_cache;
 	void *reg_cache;
+	const void *reg_def_copy;
+	const struct snd_soc_cache_ops *cache_ops;
+	struct mutex cache_rw_mutex;
 
 
 	/* dapm */
 	/* dapm */
-	u32 pop_time;
-	struct list_head dapm_widgets;
-	struct list_head dapm_paths;
-	enum snd_soc_bias_level bias_level;
-	enum snd_soc_bias_level suspend_bias_level;
-	struct delayed_work delayed_work;
+	struct snd_soc_dapm_context dapm;
 
 
 #ifdef CONFIG_DEBUG_FS
 #ifdef CONFIG_DEBUG_FS
 	struct dentry *debugfs_codec_root;
 	struct dentry *debugfs_codec_root;
 	struct dentry *debugfs_reg;
 	struct dentry *debugfs_reg;
-	struct dentry *debugfs_pop_time;
 	struct dentry *debugfs_dapm;
 	struct dentry *debugfs_dapm;
 #endif
 #endif
 };
 };
@@ -488,6 +514,7 @@ struct snd_soc_codec_driver {
 	short reg_cache_step;
 	short reg_cache_step;
 	short reg_word_size;
 	short reg_word_size;
 	const void *reg_cache_default;
 	const void *reg_cache_default;
+	enum snd_soc_compress_type compress_type;
 
 
 	/* codec bias level */
 	/* codec bias level */
 	int (*set_bias_level)(struct snd_soc_codec *,
 	int (*set_bias_level)(struct snd_soc_codec *,
@@ -554,6 +581,30 @@ struct snd_soc_dai_link {
 	struct snd_soc_ops *ops;
 	struct snd_soc_ops *ops;
 };
 };
 
 
+struct snd_soc_codec_conf {
+	const char *dev_name;
+
+	/*
+	 * optional map of kcontrol, widget and path name prefixes that are
+	 * associated per device
+	 */
+	const char *name_prefix;
+
+	/*
+	 * set this to the desired compression type if you want to
+	 * override the one supplied in codec->driver->compress_type
+	 */
+	enum snd_soc_compress_type compress_type;
+};
+
+struct snd_soc_aux_dev {
+	const char *name;		/* Codec name */
+	const char *codec_name;		/* for multi-codec */
+
+	/* codec/machine specific init - e.g. add machine controls */
+	int (*init)(struct snd_soc_dapm_context *dapm);
+};
+
 /* SoC card */
 /* SoC card */
 struct snd_soc_card {
 struct snd_soc_card {
 	const char *name;
 	const char *name;
@@ -579,6 +630,8 @@ struct snd_soc_card {
 	/* callbacks */
 	/* callbacks */
 	int (*set_bias_level)(struct snd_soc_card *,
 	int (*set_bias_level)(struct snd_soc_card *,
 			      enum snd_soc_bias_level level);
 			      enum snd_soc_bias_level level);
+	int (*set_bias_level_post)(struct snd_soc_card *,
+				   enum snd_soc_bias_level level);
 
 
 	long pmdown_time;
 	long pmdown_time;
 
 
@@ -588,12 +641,35 @@ struct snd_soc_card {
 	struct snd_soc_pcm_runtime *rtd;
 	struct snd_soc_pcm_runtime *rtd;
 	int num_rtd;
 	int num_rtd;
 
 
+	/* optional codec specific configuration */
+	struct snd_soc_codec_conf *codec_conf;
+	int num_configs;
+
+	/*
+	 * optional auxiliary devices such as amplifiers or codecs with DAI
+	 * link unused
+	 */
+	struct snd_soc_aux_dev *aux_dev;
+	int num_aux_devs;
+	struct snd_soc_pcm_runtime *rtd_aux;
+	int num_aux_rtd;
+
 	struct work_struct deferred_resume_work;
 	struct work_struct deferred_resume_work;
 
 
 	/* lists of probed devices belonging to this card */
 	/* lists of probed devices belonging to this card */
 	struct list_head codec_dev_list;
 	struct list_head codec_dev_list;
 	struct list_head platform_dev_list;
 	struct list_head platform_dev_list;
 	struct list_head dai_dev_list;
 	struct list_head dai_dev_list;
+
+	struct list_head widgets;
+	struct list_head paths;
+	struct list_head dapm_list;
+
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *debugfs_card_root;
+	struct dentry *debugfs_pop_time;
+#endif
+	u32 pop_time;
 };
 };
 
 
 /* SoC machine DAI configuration, glues a codec and cpu DAI together */
 /* SoC machine DAI configuration, glues a codec and cpu DAI together */
@@ -639,17 +715,9 @@ struct soc_enum {
 };
 };
 
 
 /* codec IO */
 /* codec IO */
-static inline unsigned int snd_soc_read(struct snd_soc_codec *codec,
-					unsigned int reg)
-{
-	return codec->driver->read(codec, reg);
-}
-
-static inline unsigned int snd_soc_write(struct snd_soc_codec *codec,
-					 unsigned int reg, unsigned int val)
-{
-	return codec->driver->write(codec, reg, val);
-}
+unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg);
+unsigned int snd_soc_write(struct snd_soc_codec *codec,
+			   unsigned int reg, unsigned int val);
 
 
 /* device driver data */
 /* device driver data */
 
 

+ 235 - 0
include/trace/events/asoc.h

@@ -0,0 +1,235 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM asoc
+
+#if !defined(_TRACE_ASOC_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_ASOC_H
+
+#include <linux/ktime.h>
+#include <linux/tracepoint.h>
+
+struct snd_soc_jack;
+struct snd_soc_codec;
+struct snd_soc_card;
+struct snd_soc_dapm_widget;
+
+/*
+ * Log register events
+ */
+DECLARE_EVENT_CLASS(snd_soc_reg,
+
+	TP_PROTO(struct snd_soc_codec *codec, unsigned int reg,
+		 unsigned int val),
+
+	TP_ARGS(codec, reg, val),
+
+	TP_STRUCT__entry(
+		__string(	name,		codec->name	)
+		__field(	int,		id		)
+		__field(	unsigned int,	reg		)
+		__field(	unsigned int,	val		)
+	),
+
+	TP_fast_assign(
+		__assign_str(name, codec->name);
+		__entry->id = codec->id;
+		__entry->reg = reg;
+		__entry->val = val;
+	),
+
+	TP_printk("codec=%s.%d reg=%x val=%x", __get_str(name),
+		  (int)__entry->id, (unsigned int)__entry->reg,
+		  (unsigned int)__entry->val)
+);
+
+DEFINE_EVENT(snd_soc_reg, snd_soc_reg_write,
+
+	TP_PROTO(struct snd_soc_codec *codec, unsigned int reg,
+		 unsigned int val),
+
+	TP_ARGS(codec, reg, val)
+
+);
+
+DEFINE_EVENT(snd_soc_reg, snd_soc_reg_read,
+
+	TP_PROTO(struct snd_soc_codec *codec, unsigned int reg,
+		 unsigned int val),
+
+	TP_ARGS(codec, reg, val)
+
+);
+
+DECLARE_EVENT_CLASS(snd_soc_card,
+
+	TP_PROTO(struct snd_soc_card *card, int val),
+
+	TP_ARGS(card, val),
+
+	TP_STRUCT__entry(
+		__string(	name,		card->name	)
+		__field(	int,		val		)
+	),
+
+	TP_fast_assign(
+		__assign_str(name, card->name);
+		__entry->val = val;
+	),
+
+	TP_printk("card=%s val=%d", __get_str(name), (int)__entry->val)
+);
+
+DEFINE_EVENT(snd_soc_card, snd_soc_bias_level_start,
+
+	TP_PROTO(struct snd_soc_card *card, int val),
+
+	TP_ARGS(card, val)
+
+);
+
+DEFINE_EVENT(snd_soc_card, snd_soc_bias_level_done,
+
+	TP_PROTO(struct snd_soc_card *card, int val),
+
+	TP_ARGS(card, val)
+
+);
+
+DECLARE_EVENT_CLASS(snd_soc_dapm_basic,
+
+	TP_PROTO(struct snd_soc_card *card),
+
+	TP_ARGS(card),
+
+	TP_STRUCT__entry(
+		__string(	name,	card->name	)
+	),
+
+	TP_fast_assign(
+		__assign_str(name, card->name);
+	),
+
+	TP_printk("card=%s", __get_str(name))
+);
+
+DEFINE_EVENT(snd_soc_dapm_basic, snd_soc_dapm_start,
+
+	TP_PROTO(struct snd_soc_card *card),
+
+	TP_ARGS(card)
+
+);
+
+DEFINE_EVENT(snd_soc_dapm_basic, snd_soc_dapm_done,
+
+	TP_PROTO(struct snd_soc_card *card),
+
+	TP_ARGS(card)
+
+);
+
+DECLARE_EVENT_CLASS(snd_soc_dapm_widget,
+
+	TP_PROTO(struct snd_soc_dapm_widget *w, int val),
+
+	TP_ARGS(w, val),
+
+	TP_STRUCT__entry(
+		__string(	name,	w->name		)
+		__field(	int,	val		)
+	),
+
+	TP_fast_assign(
+		__assign_str(name, w->name);
+		__entry->val = val;
+	),
+
+	TP_printk("widget=%s val=%d", __get_str(name),
+		  (int)__entry->val)
+);
+
+DEFINE_EVENT(snd_soc_dapm_widget, snd_soc_dapm_widget_power,
+
+	TP_PROTO(struct snd_soc_dapm_widget *w, int val),
+
+	TP_ARGS(w, val)
+
+);
+
+DEFINE_EVENT(snd_soc_dapm_widget, snd_soc_dapm_widget_event_start,
+
+	TP_PROTO(struct snd_soc_dapm_widget *w, int val),
+
+	TP_ARGS(w, val)
+
+);
+
+DEFINE_EVENT(snd_soc_dapm_widget, snd_soc_dapm_widget_event_done,
+
+	TP_PROTO(struct snd_soc_dapm_widget *w, int val),
+
+	TP_ARGS(w, val)
+
+);
+
+TRACE_EVENT(snd_soc_jack_irq,
+
+	TP_PROTO(const char *name),
+
+	TP_ARGS(name),
+
+	TP_STRUCT__entry(
+		__string(	name,	name		)
+	),
+
+	TP_fast_assign(
+		__assign_str(name, name);
+	),
+
+	TP_printk("%s", __get_str(name))
+);
+
+TRACE_EVENT(snd_soc_jack_report,
+
+	TP_PROTO(struct snd_soc_jack *jack, int mask, int val),
+
+	TP_ARGS(jack, mask, val),
+
+	TP_STRUCT__entry(
+		__string(	name,		jack->jack->name	)
+		__field(	int,		mask			)
+		__field(	int,		val			)
+	),
+
+	TP_fast_assign(
+		__assign_str(name, jack->jack->name);
+		__entry->mask = mask;
+		__entry->val = val;
+	),
+
+	TP_printk("jack=%s %x/%x", __get_str(name), (int)__entry->val,
+		  (int)__entry->mask)
+);
+
+TRACE_EVENT(snd_soc_jack_notify,
+
+	TP_PROTO(struct snd_soc_jack *jack, int val),
+
+	TP_ARGS(jack, val),
+
+	TP_STRUCT__entry(
+		__string(	name,		jack->jack->name	)
+		__field(	int,		val			)
+	),
+
+	TP_fast_assign(
+		__assign_str(name, jack->jack->name);
+		__entry->val = val;
+	),
+
+	TP_printk("jack=%s %x", __get_str(name), (int)__entry->val)
+);
+
+#endif /* _TRACE_ASOC_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>

+ 2 - 2
sound/ac97_bus.c

@@ -19,8 +19,8 @@
 
 
 /*
 /*
  * Let drivers decide whether they want to support given codec from their
  * Let drivers decide whether they want to support given codec from their
- * probe method.  Drivers have direct access to the struct snd_ac97 structure and may
- * decide based on the id field amongst other things.
+ * probe method. Drivers have direct access to the struct snd_ac97
+ * structure and may  decide based on the id field amongst other things.
  */
  */
 static int ac97_bus_match(struct device *dev, struct device_driver *drv)
 static int ac97_bus_match(struct device *dev, struct device_driver *drv)
 {
 {

+ 0 - 1
sound/aoa/codecs/onyx.c

@@ -1114,7 +1114,6 @@ static int onyx_i2c_remove(struct i2c_client *client)
 	of_node_put(onyx->codec.node);
 	of_node_put(onyx->codec.node);
 	if (onyx->codec_info)
 	if (onyx->codec_info)
 		kfree(onyx->codec_info);
 		kfree(onyx->codec_info);
-	i2c_set_clientdata(client, onyx);
 	kfree(onyx);
 	kfree(onyx);
 	return 0;
 	return 0;
 }
 }

+ 3 - 4
sound/aoa/core/gpio-feature.c

@@ -287,10 +287,9 @@ static void ftr_gpio_exit(struct gpio_runtime *rt)
 		free_irq(linein_detect_irq, &rt->line_in_notify);
 		free_irq(linein_detect_irq, &rt->line_in_notify);
 	if (rt->line_out_notify.gpio_private)
 	if (rt->line_out_notify.gpio_private)
 		free_irq(lineout_detect_irq, &rt->line_out_notify);
 		free_irq(lineout_detect_irq, &rt->line_out_notify);
-	cancel_delayed_work(&rt->headphone_notify.work);
-	cancel_delayed_work(&rt->line_in_notify.work);
-	cancel_delayed_work(&rt->line_out_notify.work);
-	flush_scheduled_work();
+	cancel_delayed_work_sync(&rt->headphone_notify.work);
+	cancel_delayed_work_sync(&rt->line_in_notify.work);
+	cancel_delayed_work_sync(&rt->line_out_notify.work);
 	mutex_destroy(&rt->headphone_notify.mutex);
 	mutex_destroy(&rt->headphone_notify.mutex);
 	mutex_destroy(&rt->line_in_notify.mutex);
 	mutex_destroy(&rt->line_in_notify.mutex);
 	mutex_destroy(&rt->line_out_notify.mutex);
 	mutex_destroy(&rt->line_out_notify.mutex);

+ 3 - 4
sound/aoa/core/gpio-pmf.c

@@ -107,10 +107,9 @@ static void pmf_gpio_exit(struct gpio_runtime *rt)
 
 
 	/* make sure no work is pending before freeing
 	/* make sure no work is pending before freeing
 	 * all things */
 	 * all things */
-	cancel_delayed_work(&rt->headphone_notify.work);
-	cancel_delayed_work(&rt->line_in_notify.work);
-	cancel_delayed_work(&rt->line_out_notify.work);
-	flush_scheduled_work();
+	cancel_delayed_work_sync(&rt->headphone_notify.work);
+	cancel_delayed_work_sync(&rt->line_in_notify.work);
+	cancel_delayed_work_sync(&rt->line_out_notify.work);
 
 
 	mutex_destroy(&rt->headphone_notify.mutex);
 	mutex_destroy(&rt->headphone_notify.mutex);
 	mutex_destroy(&rt->line_in_notify.mutex);
 	mutex_destroy(&rt->line_in_notify.mutex);

+ 27 - 1
sound/core/control.c

@@ -1488,7 +1488,7 @@ int snd_ctl_create(struct snd_card *card)
 }
 }
 
 
 /*
 /*
- * Frequently used control callbacks
+ * Frequently used control callbacks/helpers
  */
  */
 int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
 int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
 			      struct snd_ctl_elem_info *uinfo)
 			      struct snd_ctl_elem_info *uinfo)
@@ -1513,3 +1513,29 @@ int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
 }
 }
 
 
 EXPORT_SYMBOL(snd_ctl_boolean_stereo_info);
 EXPORT_SYMBOL(snd_ctl_boolean_stereo_info);
+
+/**
+ * snd_ctl_enum_info - fills the info structure for an enumerated control
+ * @info: the structure to be filled
+ * @channels: the number of the control's channels; often one
+ * @items: the number of control values; also the size of @names
+ * @names: an array containing the names of all control values
+ *
+ * Sets all required fields in @info to their appropriate values.
+ * If the control's accessibility is not the default (readable and writable),
+ * the caller has to fill @info->access.
+ */
+int snd_ctl_enum_info(struct snd_ctl_elem_info *info, unsigned int channels,
+		      unsigned int items, const char *const names[])
+{
+	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	info->count = channels;
+	info->value.enumerated.items = items;
+	if (info->value.enumerated.item >= items)
+		info->value.enumerated.item = items - 1;
+	strlcpy(info->value.enumerated.name,
+		names[info->value.enumerated.item],
+		sizeof(info->value.enumerated.name));
+	return 0;
+}
+EXPORT_SYMBOL(snd_ctl_enum_info);

+ 3 - 1
sound/core/oss/pcm_oss.c

@@ -453,8 +453,10 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm,
 	} else {
 	} else {
 		*params = *save;
 		*params = *save;
 		max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir);
 		max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir);
-		if (max < 0)
+		if (max < 0) {
+			kfree(save);
 			return max;
 			return max;
+		}
 		last = 1;
 		last = 1;
 	}
 	}
  _end:
  _end:

+ 22 - 0
sound/core/pcm_lib.c

@@ -373,6 +373,27 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
 			   (unsigned long)new_hw_ptr,
 			   (unsigned long)new_hw_ptr,
 			   (unsigned long)runtime->hw_ptr_base);
 			   (unsigned long)runtime->hw_ptr_base);
 	}
 	}
+
+	if (runtime->no_period_wakeup) {
+		/*
+		 * Without regular period interrupts, we have to check
+		 * the elapsed time to detect xruns.
+		 */
+		jdelta = jiffies - runtime->hw_ptr_jiffies;
+		if (jdelta < runtime->hw_ptr_buffer_jiffies / 2)
+			goto no_delta_check;
+		hdelta = jdelta - delta * HZ / runtime->rate;
+		while (hdelta > runtime->hw_ptr_buffer_jiffies / 2 + 1) {
+			delta += runtime->buffer_size;
+			hw_base += runtime->buffer_size;
+			if (hw_base >= runtime->boundary)
+				hw_base = 0;
+			new_hw_ptr = hw_base + pos;
+			hdelta -= runtime->hw_ptr_buffer_jiffies;
+		}
+		goto no_delta_check;
+	}
+
 	/* something must be really wrong */
 	/* something must be really wrong */
 	if (delta >= runtime->buffer_size + runtime->period_size) {
 	if (delta >= runtime->buffer_size + runtime->period_size) {
 		hw_ptr_error(substream,
 		hw_ptr_error(substream,
@@ -442,6 +463,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
 			     (long)old_hw_ptr);
 			     (long)old_hw_ptr);
 	}
 	}
 
 
+ no_delta_check:
 	if (runtime->status->hw_ptr == new_hw_ptr)
 	if (runtime->status->hw_ptr == new_hw_ptr)
 		return 0;
 		return 0;
 
 

+ 3 - 0
sound/core/pcm_native.c

@@ -422,6 +422,9 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
 	runtime->info = params->info;
 	runtime->info = params->info;
 	runtime->rate_num = params->rate_num;
 	runtime->rate_num = params->rate_num;
 	runtime->rate_den = params->rate_den;
 	runtime->rate_den = params->rate_den;
+	runtime->no_period_wakeup =
+			(params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) &&
+			(params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP);
 
 
 	bits = snd_pcm_format_physical_width(runtime->format);
 	bits = snd_pcm_format_physical_width(runtime->format);
 	runtime->sample_bits = bits;
 	runtime->sample_bits = bits;

+ 4 - 0
sound/core/seq/seq.c

@@ -32,6 +32,7 @@
 #include "seq_timer.h"
 #include "seq_timer.h"
 #include "seq_system.h"
 #include "seq_system.h"
 #include "seq_info.h"
 #include "seq_info.h"
+#include <sound/minors.h>
 #include <sound/seq_device.h>
 #include <sound/seq_device.h>
 
 
 #if defined(CONFIG_SND_SEQ_DUMMY_MODULE)
 #if defined(CONFIG_SND_SEQ_DUMMY_MODULE)
@@ -73,6 +74,9 @@ MODULE_PARM_DESC(seq_default_timer_subdevice, "The default timer subdevice numbe
 module_param(seq_default_timer_resolution, int, 0644);
 module_param(seq_default_timer_resolution, int, 0644);
 MODULE_PARM_DESC(seq_default_timer_resolution, "The default timer resolution in Hz.");
 MODULE_PARM_DESC(seq_default_timer_resolution, "The default timer resolution in Hz.");
 
 
+MODULE_ALIAS_CHARDEV(CONFIG_SND_MAJOR, SNDRV_MINOR_SEQUENCER);
+MODULE_ALIAS("devname:snd/seq");
+
 /*
 /*
  *  INIT PART
  *  INIT PART
  */
  */

+ 13 - 5
sound/core/sound.c

@@ -188,14 +188,22 @@ static const struct file_operations snd_fops =
 };
 };
 
 
 #ifdef CONFIG_SND_DYNAMIC_MINORS
 #ifdef CONFIG_SND_DYNAMIC_MINORS
-static int snd_find_free_minor(void)
+static int snd_find_free_minor(int type)
 {
 {
 	int minor;
 	int minor;
 
 
+	/* static minors for module auto loading */
+	if (type == SNDRV_DEVICE_TYPE_SEQUENCER)
+		return SNDRV_MINOR_SEQUENCER;
+	if (type == SNDRV_DEVICE_TYPE_TIMER)
+		return SNDRV_MINOR_TIMER;
+
 	for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) {
 	for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) {
-		/* skip minors still used statically for autoloading devices */
-		if (SNDRV_MINOR_DEVICE(minor) == SNDRV_MINOR_CONTROL ||
-		    minor == SNDRV_MINOR_SEQUENCER)
+		/* skip static minors still used for module auto loading */
+		if (SNDRV_MINOR_DEVICE(minor) == SNDRV_MINOR_CONTROL)
+			continue;
+		if (minor == SNDRV_MINOR_SEQUENCER ||
+		    minor == SNDRV_MINOR_TIMER)
 			continue;
 			continue;
 		if (!snd_minors[minor])
 		if (!snd_minors[minor])
 			return minor;
 			return minor;
@@ -269,7 +277,7 @@ int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
 	preg->private_data = private_data;
 	preg->private_data = private_data;
 	mutex_lock(&sound_mutex);
 	mutex_lock(&sound_mutex);
 #ifdef CONFIG_SND_DYNAMIC_MINORS
 #ifdef CONFIG_SND_DYNAMIC_MINORS
-	minor = snd_find_free_minor();
+	minor = snd_find_free_minor(type);
 #else
 #else
 	minor = snd_kernel_minor(type, card, dev);
 	minor = snd_kernel_minor(type, card, dev);
 	if (minor >= 0 && snd_minors[minor])
 	if (minor >= 0 && snd_minors[minor])

+ 5 - 2
sound/core/timer.c

@@ -34,8 +34,8 @@
 #include <sound/initval.h>
 #include <sound/initval.h>
 #include <linux/kmod.h>
 #include <linux/kmod.h>
 
 
-#if defined(CONFIG_SND_HPET) || defined(CONFIG_SND_HPET_MODULE)
-#define DEFAULT_TIMER_LIMIT 3
+#if defined(CONFIG_SND_HRTIMER) || defined(CONFIG_SND_HRTIMER_MODULE)
+#define DEFAULT_TIMER_LIMIT 4
 #elif defined(CONFIG_SND_RTCTIMER) || defined(CONFIG_SND_RTCTIMER_MODULE)
 #elif defined(CONFIG_SND_RTCTIMER) || defined(CONFIG_SND_RTCTIMER_MODULE)
 #define DEFAULT_TIMER_LIMIT 2
 #define DEFAULT_TIMER_LIMIT 2
 #else
 #else
@@ -52,6 +52,9 @@ MODULE_PARM_DESC(timer_limit, "Maximum global timers in system.");
 module_param(timer_tstamp_monotonic, int, 0444);
 module_param(timer_tstamp_monotonic, int, 0444);
 MODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for timestamps (default).");
 MODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for timestamps (default).");
 
 
+MODULE_ALIAS_CHARDEV(CONFIG_SND_MAJOR, SNDRV_MINOR_TIMER);
+MODULE_ALIAS("devname:snd/timer");
+
 struct snd_timer_user {
 struct snd_timer_user {
 	struct snd_timer_instance *timeri;
 	struct snd_timer_instance *timeri;
 	int tread;		/* enhanced read with timestamps and events */
 	int tread;		/* enhanced read with timestamps and events */

+ 2 - 2
sound/drivers/ml403-ac97cr.c

@@ -1143,8 +1143,8 @@ snd_ml403_ac97cr_create(struct snd_card *card, struct platform_device *pfdev,
 					     (resource->start) + 1);
 					     (resource->start) + 1);
 	if (ml403_ac97cr->port == NULL) {
 	if (ml403_ac97cr->port == NULL) {
 		snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
 		snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
-			   "unable to remap memory region (%x to %x)\n",
-			   resource->start, resource->end);
+			   "unable to remap memory region (%pR)\n",
+			   resource);
 		snd_ml403_ac97cr_free(ml403_ac97cr);
 		snd_ml403_ac97cr_free(ml403_ac97cr);
 		return -EBUSY;
 		return -EBUSY;
 	}
 	}

+ 2 - 3
sound/i2c/other/ak4113.c

@@ -57,8 +57,7 @@ static void snd_ak4113_free(struct ak4113 *chip)
 {
 {
 	chip->init = 1;	/* don't schedule new work */
 	chip->init = 1;	/* don't schedule new work */
 	mb();
 	mb();
-	cancel_delayed_work(&chip->work);
-	flush_scheduled_work();
+	cancel_delayed_work_sync(&chip->work);
 	kfree(chip);
 	kfree(chip);
 }
 }
 
 
@@ -141,7 +140,7 @@ void snd_ak4113_reinit(struct ak4113 *chip)
 {
 {
 	chip->init = 1;
 	chip->init = 1;
 	mb();
 	mb();
-	flush_scheduled_work();
+	flush_delayed_work_sync(&chip->work);
 	ak4113_init_regs(chip);
 	ak4113_init_regs(chip);
 	/* bring up statistics / event queing */
 	/* bring up statistics / event queing */
 	chip->init = 0;
 	chip->init = 0;

+ 2 - 3
sound/i2c/other/ak4114.c

@@ -67,8 +67,7 @@ static void snd_ak4114_free(struct ak4114 *chip)
 {
 {
 	chip->init = 1;	/* don't schedule new work */
 	chip->init = 1;	/* don't schedule new work */
 	mb();
 	mb();
-	cancel_delayed_work(&chip->work);
-	flush_scheduled_work();
+	cancel_delayed_work_sync(&chip->work);
 	kfree(chip);
 	kfree(chip);
 }
 }
 
 
@@ -154,7 +153,7 @@ void snd_ak4114_reinit(struct ak4114 *chip)
 {
 {
 	chip->init = 1;
 	chip->init = 1;
 	mb();
 	mb();
-	flush_scheduled_work();
+	flush_delayed_work_sync(&chip->work);
 	ak4114_init_regs(chip);
 	ak4114_init_regs(chip);
 	/* bring up statistics / event queing */
 	/* bring up statistics / event queing */
 	chip->init = 0;
 	chip->init = 0;

+ 8 - 15
sound/pci/Kconfig

@@ -209,7 +209,7 @@ config SND_OXYGEN_LIB
         tristate
         tristate
 
 
 config SND_OXYGEN
 config SND_OXYGEN
-	tristate "C-Media 8788 (Oxygen)"
+	tristate "C-Media 8786, 8787, 8788 (Oxygen)"
 	select SND_OXYGEN_LIB
 	select SND_OXYGEN_LIB
 	select SND_PCM
 	select SND_PCM
 	select SND_MPU401_UART
 	select SND_MPU401_UART
@@ -217,13 +217,18 @@ config SND_OXYGEN
 	  Say Y here to include support for sound cards based on the
 	  Say Y here to include support for sound cards based on the
 	  C-Media CMI8788 (Oxygen HD Audio) chip:
 	  C-Media CMI8788 (Oxygen HD Audio) chip:
 	   * Asound A-8788
 	   * Asound A-8788
+	   * Asus Xonar DG
 	   * AuzenTech X-Meridian
 	   * AuzenTech X-Meridian
+	   * AuzenTech X-Meridian 2G
 	   * Bgears b-Enspirer
 	   * Bgears b-Enspirer
 	   * Club3D Theatron DTS
 	   * Club3D Theatron DTS
 	   * HT-Omega Claro (plus)
 	   * HT-Omega Claro (plus)
 	   * HT-Omega Claro halo (XT)
 	   * HT-Omega Claro halo (XT)
+	   * Kuroutoshikou CMI8787-HG2PCI
 	   * Razer Barracuda AC-1
 	   * Razer Barracuda AC-1
 	   * Sondigo Inferno
 	   * Sondigo Inferno
+	   * TempoTec/MediaTek HiFier Fantasia
+	   * TempoTec/MediaTek HiFier Serenade
 
 
 	  To compile this driver as a module, choose M here: the module
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-oxygen.
 	  will be called snd-oxygen.
@@ -578,18 +583,6 @@ config SND_HDSPM
 	  To compile this driver as a module, choose M here: the module
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-hdspm.
 	  will be called snd-hdspm.
 
 
-config SND_HIFIER
-	tristate "TempoTec HiFier Fantasia"
-	select SND_OXYGEN_LIB
-	select SND_PCM
-	select SND_MPU401_UART
-	help
-	  Say Y here to include support for the MediaTek/TempoTec HiFier
-	  Fantasia sound card.
-
-	  To compile this driver as a module, choose M here: the module
-	  will be called snd-hifier.
-
 config SND_ICE1712
 config SND_ICE1712
 	tristate "ICEnsemble ICE1712 (Envy24)"
 	tristate "ICEnsemble ICE1712 (Envy24)"
 	select SND_MPU401_UART
 	select SND_MPU401_UART
@@ -826,8 +819,8 @@ config SND_VIRTUOSO
 	  Say Y here to include support for sound cards based on the
 	  Say Y here to include support for sound cards based on the
 	  Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS,
 	  Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS,
 	  Essence ST (Deluxe), and Essence STX.
 	  Essence ST (Deluxe), and Essence STX.
-	  Support for the HDAV1.3 (Deluxe) is incomplete; for the
-	  HDAV1.3 Slim and Xense, missing.
+	  Support for the HDAV1.3 (Deluxe) and HDAV1.3 Slim is experimental;
+	  for the Xense, missing.
 
 
 	  To compile this driver as a module, choose M here: the module
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-virtuoso.
 	  will be called snd-virtuoso.

+ 2 - 4
sound/pci/ac97/ac97_codec.c

@@ -1014,8 +1014,7 @@ static int snd_ac97_free(struct snd_ac97 *ac97)
 {
 {
 	if (ac97) {
 	if (ac97) {
 #ifdef CONFIG_SND_AC97_POWER_SAVE
 #ifdef CONFIG_SND_AC97_POWER_SAVE
-		cancel_delayed_work(&ac97->power_work);
-		flush_scheduled_work();
+		cancel_delayed_work_sync(&ac97->power_work);
 #endif
 #endif
 		snd_ac97_proc_done(ac97);
 		snd_ac97_proc_done(ac97);
 		if (ac97->bus)
 		if (ac97->bus)
@@ -2456,8 +2455,7 @@ void snd_ac97_suspend(struct snd_ac97 *ac97)
 	if (ac97->build_ops->suspend)
 	if (ac97->build_ops->suspend)
 		ac97->build_ops->suspend(ac97);
 		ac97->build_ops->suspend(ac97);
 #ifdef CONFIG_SND_AC97_POWER_SAVE
 #ifdef CONFIG_SND_AC97_POWER_SAVE
-	cancel_delayed_work(&ac97->power_work);
-	flush_scheduled_work();
+	cancel_delayed_work_sync(&ac97->power_work);
 #endif
 #endif
 	snd_ac97_powerdown(ac97);
 	snd_ac97_powerdown(ac97);
 }
 }

+ 205 - 201
sound/pci/azt3328.c

@@ -1,6 +1,6 @@
 /*
 /*
  *  azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168).
  *  azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168).
- *  Copyright (C) 2002, 2005 - 2009 by Andreas Mohr <andi AT lisas.de>
+ *  Copyright (C) 2002, 2005 - 2010 by Andreas Mohr <andi AT lisas.de>
  *
  *
  *  Framework borrowed from Bart Hartgers's als4000.c.
  *  Framework borrowed from Bart Hartgers's als4000.c.
  *  Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801),
  *  Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801),
@@ -175,6 +175,7 @@
 
 
 #include <asm/io.h>
 #include <asm/io.h>
 #include <linux/init.h>
 #include <linux/init.h>
+#include <linux/bug.h> /* WARN_ONCE */
 #include <linux/pci.h>
 #include <linux/pci.h>
 #include <linux/delay.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
@@ -201,14 +202,15 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
 
 
 /* === Debug settings ===
 /* === Debug settings ===
   Further diagnostic functionality than the settings below
   Further diagnostic functionality than the settings below
-  does not need to be provided, since one can easily write a bash script
+  does not need to be provided, since one can easily write a POSIX shell script
   to dump the card's I/O ports (those listed in lspci -v -v):
   to dump the card's I/O ports (those listed in lspci -v -v):
-  function dump()
+  dump()
   {
   {
     local descr=$1; local addr=$2; local count=$3
     local descr=$1; local addr=$2; local count=$3
 
 
     echo "${descr}: ${count} @ ${addr}:"
     echo "${descr}: ${count} @ ${addr}:"
-    dd if=/dev/port skip=$[${addr}] count=${count} bs=1 2>/dev/null| hexdump -C
+    dd if=/dev/port skip=`printf %d ${addr}` count=${count} bs=1 \
+      2>/dev/null| hexdump -C
   }
   }
   and then use something like
   and then use something like
   "dump joy200 0x200 8", "dump mpu388 0x388 4", "dump joy 0xb400 8",
   "dump joy200 0x200 8", "dump mpu388 0x388 4", "dump joy 0xb400 8",
@@ -216,14 +218,14 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
   possibly within a "while true; do ... sleep 1; done" loop.
   possibly within a "while true; do ... sleep 1; done" loop.
   Tweaking ports could be done using
   Tweaking ports could be done using
   VALSTRING="`printf "%02x" $value`"
   VALSTRING="`printf "%02x" $value`"
-  printf "\x""$VALSTRING"|dd of=/dev/port seek=$[${addr}] bs=1 2>/dev/null
+  printf "\x""$VALSTRING"|dd of=/dev/port seek=`printf %d ${addr}` bs=1 \
+    2>/dev/null
 */
 */
 
 
 #define DEBUG_MISC	0
 #define DEBUG_MISC	0
 #define DEBUG_CALLS	0
 #define DEBUG_CALLS	0
 #define DEBUG_MIXER	0
 #define DEBUG_MIXER	0
 #define DEBUG_CODEC	0
 #define DEBUG_CODEC	0
-#define DEBUG_IO	0
 #define DEBUG_TIMER	0
 #define DEBUG_TIMER	0
 #define DEBUG_GAME	0
 #define DEBUG_GAME	0
 #define DEBUG_PM	0
 #define DEBUG_PM	0
@@ -291,19 +293,23 @@ static int seqtimer_scaling = 128;
 module_param(seqtimer_scaling, int, 0444);
 module_param(seqtimer_scaling, int, 0444);
 MODULE_PARM_DESC(seqtimer_scaling, "Set 1024000Hz sequencer timer scale factor (lockup danger!). Default 128.");
 MODULE_PARM_DESC(seqtimer_scaling, "Set 1024000Hz sequencer timer scale factor (lockup danger!). Default 128.");
 
 
-struct snd_azf3328_codec_data {
-	unsigned long io_base;
-	struct snd_pcm_substream *substream;
-	bool running;
-	const char *name;
-};
-
 enum snd_azf3328_codec_type {
 enum snd_azf3328_codec_type {
+  /* warning: fixed indices (also used for bitmask checks!) */
   AZF_CODEC_PLAYBACK = 0,
   AZF_CODEC_PLAYBACK = 0,
   AZF_CODEC_CAPTURE = 1,
   AZF_CODEC_CAPTURE = 1,
   AZF_CODEC_I2S_OUT = 2,
   AZF_CODEC_I2S_OUT = 2,
 };
 };
 
 
+struct snd_azf3328_codec_data {
+	unsigned long io_base; /* keep first! (avoid offset calc) */
+	unsigned int dma_base; /* helper to avoid an indirection in hotpath */
+	spinlock_t *lock; /* TODO: convert to our own per-codec lock member */
+	struct snd_pcm_substream *substream;
+	bool running;
+	enum snd_azf3328_codec_type type;
+	const char *name;
+};
+
 struct snd_azf3328 {
 struct snd_azf3328 {
 	/* often-used fields towards beginning, then grouped */
 	/* often-used fields towards beginning, then grouped */
 
 
@@ -362,6 +368,9 @@ MODULE_DEVICE_TABLE(pci, snd_azf3328_ids);
 static int
 static int
 snd_azf3328_io_reg_setb(unsigned reg, u8 mask, bool do_set)
 snd_azf3328_io_reg_setb(unsigned reg, u8 mask, bool do_set)
 {
 {
+	/* Well, strictly spoken, the inb/outb sequence isn't atomic
+	   and would need locking. However we currently don't care
+	   since it potentially complicates matters. */
 	u8 prev = inb(reg), new;
 	u8 prev = inb(reg), new;
 
 
 	new = (do_set) ? (prev|mask) : (prev & ~mask);
 	new = (do_set) ? (prev|mask) : (prev & ~mask);
@@ -413,6 +422,21 @@ snd_azf3328_codec_outl(const struct snd_azf3328_codec_data *codec,
 	outl(value, codec->io_base + reg);
 	outl(value, codec->io_base + reg);
 }
 }
 
 
+static inline void
+snd_azf3328_codec_outl_multi(const struct snd_azf3328_codec_data *codec,
+			     unsigned reg, const void *buffer, int count
+)
+{
+	unsigned long addr = codec->io_base + reg;
+	if (count) {
+		const u32 *buf = buffer;
+		do {
+			outl(*buf++, addr);
+			addr += 4;
+		} while (--count);
+	}
+}
+
 static inline u32
 static inline u32
 snd_azf3328_codec_inl(const struct snd_azf3328_codec_data *codec, unsigned reg)
 snd_azf3328_codec_inl(const struct snd_azf3328_codec_data *codec, unsigned reg)
 {
 {
@@ -943,38 +967,43 @@ snd_azf3328_hw_free(struct snd_pcm_substream *substream)
 }
 }
 
 
 static void
 static void
-snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
-			       enum snd_azf3328_codec_type codec_type,
+snd_azf3328_codec_setfmt(struct snd_azf3328_codec_data *codec,
 			       enum azf_freq_t bitrate,
 			       enum azf_freq_t bitrate,
 			       unsigned int format_width,
 			       unsigned int format_width,
 			       unsigned int channels
 			       unsigned int channels
 )
 )
 {
 {
 	unsigned long flags;
 	unsigned long flags;
-	const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
 	u16 val = 0xff00;
 	u16 val = 0xff00;
+	u8 freq = 0;
 
 
 	snd_azf3328_dbgcallenter();
 	snd_azf3328_dbgcallenter();
 	switch (bitrate) {
 	switch (bitrate) {
-	case AZF_FREQ_4000:  val |= SOUNDFORMAT_FREQ_SUSPECTED_4000; break;
-	case AZF_FREQ_4800:  val |= SOUNDFORMAT_FREQ_SUSPECTED_4800; break;
-	case AZF_FREQ_5512:
-		/* the AZF3328 names it "5510" for some strange reason */
-			     val |= SOUNDFORMAT_FREQ_5510; break;
-	case AZF_FREQ_6620:  val |= SOUNDFORMAT_FREQ_6620; break;
-	case AZF_FREQ_8000:  val |= SOUNDFORMAT_FREQ_8000; break;
-	case AZF_FREQ_9600:  val |= SOUNDFORMAT_FREQ_9600; break;
-	case AZF_FREQ_11025: val |= SOUNDFORMAT_FREQ_11025; break;
-	case AZF_FREQ_13240: val |= SOUNDFORMAT_FREQ_SUSPECTED_13240; break;
-	case AZF_FREQ_16000: val |= SOUNDFORMAT_FREQ_16000; break;
-	case AZF_FREQ_22050: val |= SOUNDFORMAT_FREQ_22050; break;
-	case AZF_FREQ_32000: val |= SOUNDFORMAT_FREQ_32000; break;
+#define AZF_FMT_XLATE(in_freq, out_bits) \
+	do { \
+		case AZF_FREQ_ ## in_freq: \
+			freq = SOUNDFORMAT_FREQ_ ## out_bits; \
+			break; \
+	} while (0);
+	AZF_FMT_XLATE(4000, SUSPECTED_4000)
+	AZF_FMT_XLATE(4800, SUSPECTED_4800)
+	/* the AZF3328 names it "5510" for some strange reason: */
+	AZF_FMT_XLATE(5512, 5510)
+	AZF_FMT_XLATE(6620, 6620)
+	AZF_FMT_XLATE(8000, 8000)
+	AZF_FMT_XLATE(9600, 9600)
+	AZF_FMT_XLATE(11025, 11025)
+	AZF_FMT_XLATE(13240, SUSPECTED_13240)
+	AZF_FMT_XLATE(16000, 16000)
+	AZF_FMT_XLATE(22050, 22050)
+	AZF_FMT_XLATE(32000, 32000)
 	default:
 	default:
 		snd_printk(KERN_WARNING "unknown bitrate %d, assuming 44.1kHz!\n", bitrate);
 		snd_printk(KERN_WARNING "unknown bitrate %d, assuming 44.1kHz!\n", bitrate);
 		/* fall-through */
 		/* fall-through */
-	case AZF_FREQ_44100: val |= SOUNDFORMAT_FREQ_44100; break;
-	case AZF_FREQ_48000: val |= SOUNDFORMAT_FREQ_48000; break;
-	case AZF_FREQ_66200: val |= SOUNDFORMAT_FREQ_SUSPECTED_66200; break;
+	AZF_FMT_XLATE(44100, 44100)
+	AZF_FMT_XLATE(48000, 48000)
+	AZF_FMT_XLATE(66200, SUSPECTED_66200)
+#undef AZF_FMT_XLATE
 	}
 	}
 	/* val = 0xff07; 3m27.993s (65301Hz; -> 64000Hz???) hmm, 66120, 65967, 66123 */
 	/* val = 0xff07; 3m27.993s (65301Hz; -> 64000Hz???) hmm, 66120, 65967, 66123 */
 	/* val = 0xff09; 17m15.098s (13123,478Hz; -> 12000Hz???) hmm, 13237.2Hz? */
 	/* val = 0xff09; 17m15.098s (13123,478Hz; -> 12000Hz???) hmm, 13237.2Hz? */
@@ -986,13 +1015,15 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
 	/* val = 0xff0d; 41m23.135s (5523,600Hz; -> 5512Hz???) */
 	/* val = 0xff0d; 41m23.135s (5523,600Hz; -> 5512Hz???) */
 	/* val = 0xff0e; 28m30.777s (8017Hz; -> 8000Hz???) */
 	/* val = 0xff0e; 28m30.777s (8017Hz; -> 8000Hz???) */
 
 
+	val |= freq;
+
 	if (channels == 2)
 	if (channels == 2)
 		val |= SOUNDFORMAT_FLAG_2CHANNELS;
 		val |= SOUNDFORMAT_FLAG_2CHANNELS;
 
 
 	if (format_width == 16)
 	if (format_width == 16)
 		val |= SOUNDFORMAT_FLAG_16BIT;
 		val |= SOUNDFORMAT_FLAG_16BIT;
 
 
-	spin_lock_irqsave(&chip->reg_lock, flags);
+	spin_lock_irqsave(codec->lock, flags);
 
 
 	/* set bitrate/format */
 	/* set bitrate/format */
 	snd_azf3328_codec_outw(codec, IDX_IO_CODEC_SOUNDFORMAT, val);
 	snd_azf3328_codec_outw(codec, IDX_IO_CODEC_SOUNDFORMAT, val);
@@ -1004,7 +1035,8 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
 	 * (FIXME: yes, it works, but what exactly am I doing here?? :)
 	 * (FIXME: yes, it works, but what exactly am I doing here?? :)
 	 * FIXME: does this have some side effects for full-duplex
 	 * FIXME: does this have some side effects for full-duplex
 	 * or other dramatic side effects? */
 	 * or other dramatic side effects? */
-	if (codec_type == AZF_CODEC_PLAYBACK) /* only do it for playback */
+	/* do it for non-capture codecs only */
+	if (codec->type != AZF_CODEC_CAPTURE)
 		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
 		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
 			snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS) |
 			snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS) |
 			DMA_RUN_SOMETHING1 |
 			DMA_RUN_SOMETHING1 |
@@ -1014,20 +1046,19 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
 			DMA_SOMETHING_ELSE
 			DMA_SOMETHING_ELSE
 		);
 		);
 
 
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	spin_unlock_irqrestore(codec->lock, flags);
 	snd_azf3328_dbgcallleave();
 	snd_azf3328_dbgcallleave();
 }
 }
 
 
 static inline void
 static inline void
-snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328 *chip,
-			    enum snd_azf3328_codec_type codec_type
+snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328_codec_data *codec
 )
 )
 {
 {
 	/* choose lowest frequency for low power consumption.
 	/* choose lowest frequency for low power consumption.
 	 * While this will cause louder noise due to rather coarse frequency,
 	 * While this will cause louder noise due to rather coarse frequency,
 	 * it should never matter since output should always
 	 * it should never matter since output should always
 	 * get disabled properly when idle anyway. */
 	 * get disabled properly when idle anyway. */
-	snd_azf3328_codec_setfmt(chip, codec_type, AZF_FREQ_4000, 8, 1);
+	snd_azf3328_codec_setfmt(codec, AZF_FREQ_4000, 8, 1);
 }
 }
 
 
 static void
 static void
@@ -1101,69 +1132,87 @@ snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip,
 		/* ...and adjust clock, too
 		/* ...and adjust clock, too
 		 * (reduce noise and power consumption) */
 		 * (reduce noise and power consumption) */
 		if (!enable)
 		if (!enable)
-			snd_azf3328_codec_setfmt_lowpower(
-				chip,
-				codec_type
-			);
+			snd_azf3328_codec_setfmt_lowpower(codec);
 		codec->running = enable;
 		codec->running = enable;
 	}
 	}
 }
 }
 
 
 static void
 static void
-snd_azf3328_codec_setdmaa(struct snd_azf3328 *chip,
-				enum snd_azf3328_codec_type codec_type,
+snd_azf3328_codec_setdmaa(struct snd_azf3328_codec_data *codec,
 				unsigned long addr,
 				unsigned long addr,
-				unsigned int count,
-				unsigned int size
+				unsigned int period_bytes,
+				unsigned int buffer_bytes
 )
 )
 {
 {
-	const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
 	snd_azf3328_dbgcallenter();
 	snd_azf3328_dbgcallenter();
+	WARN_ONCE(period_bytes & 1, "odd period length!?\n");
+	WARN_ONCE(buffer_bytes != 2 * period_bytes,
+		 "missed our input expectations! %u vs. %u\n",
+		 buffer_bytes, period_bytes);
 	if (!codec->running) {
 	if (!codec->running) {
 		/* AZF3328 uses a two buffer pointer DMA transfer approach */
 		/* AZF3328 uses a two buffer pointer DMA transfer approach */
 
 
-		unsigned long flags, addr_area2;
+		unsigned long flags;
 
 
 		/* width 32bit (prevent overflow): */
 		/* width 32bit (prevent overflow): */
-		u32 count_areas, lengths;
+		u32 area_length;
+		struct codec_setup_io {
+			u32 dma_start_1;
+			u32 dma_start_2;
+			u32 dma_lengths;
+		} __attribute__((packed)) setup_io;
+
+		area_length = buffer_bytes/2;
+
+		setup_io.dma_start_1 = addr;
+		setup_io.dma_start_2 = addr+area_length;
 
 
-		count_areas = size/2;
-		addr_area2 = addr+count_areas;
-		snd_azf3328_dbgcodec("setdma: buffers %08lx[%u] / %08lx[%u]\n",
-				addr, count_areas, addr_area2, count_areas);
+		snd_azf3328_dbgcodec(
+			"setdma: buffers %08x[%u] / %08x[%u], %u, %u\n",
+				setup_io.dma_start_1, area_length,
+				setup_io.dma_start_2, area_length,
+				period_bytes, buffer_bytes);
 
 
-		count_areas--; /* max. index */
+		/* Hmm, are we really supposed to decrement this by 1??
+		   Most definitely certainly not: configuring full length does
+		   work properly (i.e. likely better), and BTW we
+		   violated possibly differing frame sizes with this...
+
+		area_length--; |* max. index *|
+		*/
 
 
 		/* build combined I/O buffer length word */
 		/* build combined I/O buffer length word */
-		lengths = (count_areas << 16) | (count_areas);
-		spin_lock_irqsave(&chip->reg_lock, flags);
-		snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_1, addr);
-		snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_2,
-								addr_area2);
-		snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_LENGTHS,
-								lengths);
-		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		setup_io.dma_lengths = (area_length << 16) | (area_length);
+
+		spin_lock_irqsave(codec->lock, flags);
+		snd_azf3328_codec_outl_multi(
+			codec, IDX_IO_CODEC_DMA_START_1, &setup_io, 3
+		);
+		spin_unlock_irqrestore(codec->lock, flags);
 	}
 	}
 	snd_azf3328_dbgcallleave();
 	snd_azf3328_dbgcallleave();
 }
 }
 
 
 static int
 static int
-snd_azf3328_codec_prepare(struct snd_pcm_substream *substream)
+snd_azf3328_pcm_prepare(struct snd_pcm_substream *substream)
 {
 {
-#if 0
-	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_azf3328_codec_data *codec = runtime->private_data;
+#if 0
         unsigned int size = snd_pcm_lib_buffer_bytes(substream);
         unsigned int size = snd_pcm_lib_buffer_bytes(substream);
 	unsigned int count = snd_pcm_lib_period_bytes(substream);
 	unsigned int count = snd_pcm_lib_period_bytes(substream);
 #endif
 #endif
 
 
 	snd_azf3328_dbgcallenter();
 	snd_azf3328_dbgcallenter();
+
+	codec->dma_base = runtime->dma_addr;
+
 #if 0
 #if 0
-	snd_azf3328_codec_setfmt(chip, AZF_CODEC_...,
+	snd_azf3328_codec_setfmt(codec,
 		runtime->rate,
 		runtime->rate,
 		snd_pcm_format_width(runtime->format),
 		snd_pcm_format_width(runtime->format),
 		runtime->channels);
 		runtime->channels);
-	snd_azf3328_codec_setdmaa(chip, AZF_CODEC_...,
+	snd_azf3328_codec_setdmaa(codec,
 					runtime->dma_addr, count, size);
 					runtime->dma_addr, count, size);
 #endif
 #endif
 	snd_azf3328_dbgcallleave();
 	snd_azf3328_dbgcallleave();
@@ -1171,24 +1220,23 @@ snd_azf3328_codec_prepare(struct snd_pcm_substream *substream)
 }
 }
 
 
 static int
 static int
-snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
-			struct snd_pcm_substream *substream, int cmd)
+snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 {
 {
 	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
 	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
-	const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_azf3328_codec_data *codec = runtime->private_data;
 	int result = 0;
 	int result = 0;
 	u16 flags1;
 	u16 flags1;
 	bool previously_muted = 0;
 	bool previously_muted = 0;
-	bool is_playback_codec = (AZF_CODEC_PLAYBACK == codec_type);
+	bool is_main_mixer_playback_codec = (AZF_CODEC_PLAYBACK == codec->type);
 
 
-	snd_azf3328_dbgcalls("snd_azf3328_codec_trigger cmd %d\n", cmd);
+	snd_azf3328_dbgcalls("snd_azf3328_pcm_trigger cmd %d\n", cmd);
 
 
 	switch (cmd) {
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_START:
 		snd_azf3328_dbgcodec("START %s\n", codec->name);
 		snd_azf3328_dbgcodec("START %s\n", codec->name);
 
 
-		if (is_playback_codec) {
+		if (is_main_mixer_playback_codec) {
 			/* mute WaveOut (avoid clicking during setup) */
 			/* mute WaveOut (avoid clicking during setup) */
 			previously_muted =
 			previously_muted =
 				snd_azf3328_mixer_set_mute(
 				snd_azf3328_mixer_set_mute(
@@ -1196,12 +1244,12 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
 				);
 				);
 		}
 		}
 
 
-		snd_azf3328_codec_setfmt(chip, codec_type,
+		snd_azf3328_codec_setfmt(codec,
 			runtime->rate,
 			runtime->rate,
 			snd_pcm_format_width(runtime->format),
 			snd_pcm_format_width(runtime->format),
 			runtime->channels);
 			runtime->channels);
 
 
-		spin_lock(&chip->reg_lock);
+		spin_lock(codec->lock);
 		/* first, remember current value: */
 		/* first, remember current value: */
 		flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
 		flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
 
 
@@ -1211,14 +1259,14 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
 
 
 		/* FIXME: clear interrupts or what??? */
 		/* FIXME: clear interrupts or what??? */
 		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_IRQTYPE, 0xffff);
 		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_IRQTYPE, 0xffff);
-		spin_unlock(&chip->reg_lock);
+		spin_unlock(codec->lock);
 
 
-		snd_azf3328_codec_setdmaa(chip, codec_type, runtime->dma_addr,
+		snd_azf3328_codec_setdmaa(codec, runtime->dma_addr,
 			snd_pcm_lib_period_bytes(substream),
 			snd_pcm_lib_period_bytes(substream),
 			snd_pcm_lib_buffer_bytes(substream)
 			snd_pcm_lib_buffer_bytes(substream)
 		);
 		);
 
 
-		spin_lock(&chip->reg_lock);
+		spin_lock(codec->lock);
 #ifdef WIN9X
 #ifdef WIN9X
 		/* FIXME: enable playback/recording??? */
 		/* FIXME: enable playback/recording??? */
 		flags1 |= DMA_RUN_SOMETHING1 | DMA_RUN_SOMETHING2;
 		flags1 |= DMA_RUN_SOMETHING1 | DMA_RUN_SOMETHING2;
@@ -1242,10 +1290,10 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
 			DMA_EPILOGUE_SOMETHING |
 			DMA_EPILOGUE_SOMETHING |
 			DMA_SOMETHING_ELSE);
 			DMA_SOMETHING_ELSE);
 #endif
 #endif
-		spin_unlock(&chip->reg_lock);
-		snd_azf3328_ctrl_codec_activity(chip, codec_type, 1);
+		spin_unlock(codec->lock);
+		snd_azf3328_ctrl_codec_activity(chip, codec->type, 1);
 
 
-		if (is_playback_codec) {
+		if (is_main_mixer_playback_codec) {
 			/* now unmute WaveOut */
 			/* now unmute WaveOut */
 			if (!previously_muted)
 			if (!previously_muted)
 				snd_azf3328_mixer_set_mute(
 				snd_azf3328_mixer_set_mute(
@@ -1258,19 +1306,19 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
 	case SNDRV_PCM_TRIGGER_RESUME:
 	case SNDRV_PCM_TRIGGER_RESUME:
 		snd_azf3328_dbgcodec("RESUME %s\n", codec->name);
 		snd_azf3328_dbgcodec("RESUME %s\n", codec->name);
 		/* resume codec if we were active */
 		/* resume codec if we were active */
-		spin_lock(&chip->reg_lock);
+		spin_lock(codec->lock);
 		if (codec->running)
 		if (codec->running)
 			snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
 			snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
 				snd_azf3328_codec_inw(
 				snd_azf3328_codec_inw(
 					codec, IDX_IO_CODEC_DMA_FLAGS
 					codec, IDX_IO_CODEC_DMA_FLAGS
 				) | DMA_RESUME
 				) | DMA_RESUME
 			);
 			);
-		spin_unlock(&chip->reg_lock);
+		spin_unlock(codec->lock);
 		break;
 		break;
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_STOP:
 		snd_azf3328_dbgcodec("STOP %s\n", codec->name);
 		snd_azf3328_dbgcodec("STOP %s\n", codec->name);
 
 
-		if (is_playback_codec) {
+		if (is_main_mixer_playback_codec) {
 			/* mute WaveOut (avoid clicking during setup) */
 			/* mute WaveOut (avoid clicking during setup) */
 			previously_muted =
 			previously_muted =
 				snd_azf3328_mixer_set_mute(
 				snd_azf3328_mixer_set_mute(
@@ -1278,7 +1326,7 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
 				);
 				);
 		}
 		}
 
 
-		spin_lock(&chip->reg_lock);
+		spin_lock(codec->lock);
 		/* first, remember current value: */
 		/* first, remember current value: */
 		flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
 		flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
 
 
@@ -1293,10 +1341,10 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
 
 
 		flags1 &= ~DMA_RUN_SOMETHING1;
 		flags1 &= ~DMA_RUN_SOMETHING1;
 		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
 		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
-		spin_unlock(&chip->reg_lock);
-		snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
+		spin_unlock(codec->lock);
+		snd_azf3328_ctrl_codec_activity(chip, codec->type, 0);
 
 
-		if (is_playback_codec) {
+		if (is_main_mixer_playback_codec) {
 			/* now unmute WaveOut */
 			/* now unmute WaveOut */
 			if (!previously_muted)
 			if (!previously_muted)
 				snd_azf3328_mixer_set_mute(
 				snd_azf3328_mixer_set_mute(
@@ -1330,67 +1378,29 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
 	return result;
 	return result;
 }
 }
 
 
-static int
-snd_azf3328_codec_playback_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-	return snd_azf3328_codec_trigger(AZF_CODEC_PLAYBACK, substream, cmd);
-}
-
-static int
-snd_azf3328_codec_capture_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-	return snd_azf3328_codec_trigger(AZF_CODEC_CAPTURE, substream, cmd);
-}
-
-static int
-snd_azf3328_codec_i2s_out_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-	return snd_azf3328_codec_trigger(AZF_CODEC_I2S_OUT, substream, cmd);
-}
-
 static snd_pcm_uframes_t
 static snd_pcm_uframes_t
-snd_azf3328_codec_pointer(struct snd_pcm_substream *substream,
-			  enum snd_azf3328_codec_type codec_type
+snd_azf3328_pcm_pointer(struct snd_pcm_substream *substream
 )
 )
 {
 {
-	const struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
-	const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
-	unsigned long bufptr, result;
+	const struct snd_azf3328_codec_data *codec =
+		substream->runtime->private_data;
+	unsigned long result;
 	snd_pcm_uframes_t frmres;
 	snd_pcm_uframes_t frmres;
 
 
-#ifdef QUERY_HARDWARE
-	bufptr = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_START_1);
-#else
-	bufptr = substream->runtime->dma_addr;
-#endif
 	result = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_CURRPOS);
 	result = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_CURRPOS);
 
 
 	/* calculate offset */
 	/* calculate offset */
-	result -= bufptr;
+#ifdef QUERY_HARDWARE
+	result -= snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_START_1);
+#else
+	result -= codec->dma_base;
+#endif
 	frmres = bytes_to_frames( substream->runtime, result);
 	frmres = bytes_to_frames( substream->runtime, result);
-	snd_azf3328_dbgcodec("%s @ 0x%8lx, frames %8ld\n",
-				codec->name, result, frmres);
+	snd_azf3328_dbgcodec("%08li %s @ 0x%8lx, frames %8ld\n",
+				jiffies, codec->name, result, frmres);
 	return frmres;
 	return frmres;
 }
 }
 
 
-static snd_pcm_uframes_t
-snd_azf3328_codec_playback_pointer(struct snd_pcm_substream *substream)
-{
-	return snd_azf3328_codec_pointer(substream, AZF_CODEC_PLAYBACK);
-}
-
-static snd_pcm_uframes_t
-snd_azf3328_codec_capture_pointer(struct snd_pcm_substream *substream)
-{
-	return snd_azf3328_codec_pointer(substream, AZF_CODEC_CAPTURE);
-}
-
-static snd_pcm_uframes_t
-snd_azf3328_codec_i2s_out_pointer(struct snd_pcm_substream *substream)
-{
-	return snd_azf3328_codec_pointer(substream, AZF_CODEC_I2S_OUT);
-}
-
 /******************************************************************/
 /******************************************************************/
 
 
 #ifdef SUPPORT_GAMEPORT
 #ifdef SUPPORT_GAMEPORT
@@ -1532,7 +1542,7 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport,
 		}
 		}
 	}
 	}
 
 
-	/* trigger next axes sampling, to be evaluated the next time we
+	/* trigger next sampling of axes, to be evaluated the next time we
 	 * enter this function */
 	 * enter this function */
 
 
 	/* for some very, very strange reason we cannot enable
 	/* for some very, very strange reason we cannot enable
@@ -1624,29 +1634,29 @@ snd_azf3328_irq_log_unknown_type(u8 which)
 }
 }
 
 
 static inline void
 static inline void
-snd_azf3328_codec_interrupt(struct snd_azf3328 *chip, u8 status)
+snd_azf3328_pcm_interrupt(const struct snd_azf3328_codec_data *first_codec,
+			  u8 status
+)
 {
 {
 	u8 which;
 	u8 which;
 	enum snd_azf3328_codec_type codec_type;
 	enum snd_azf3328_codec_type codec_type;
-	const struct snd_azf3328_codec_data *codec;
+	const struct snd_azf3328_codec_data *codec = first_codec;
 
 
 	for (codec_type = AZF_CODEC_PLAYBACK;
 	for (codec_type = AZF_CODEC_PLAYBACK;
 		 codec_type <= AZF_CODEC_I2S_OUT;
 		 codec_type <= AZF_CODEC_I2S_OUT;
-			 ++codec_type) {
+			 ++codec_type, ++codec) {
 
 
 		/* skip codec if there's no interrupt for it */
 		/* skip codec if there's no interrupt for it */
 		if (!(status & (1 << codec_type)))
 		if (!(status & (1 << codec_type)))
 			continue;
 			continue;
 
 
-		codec = &chip->codecs[codec_type];
-
-		spin_lock(&chip->reg_lock);
+		spin_lock(codec->lock);
 		which = snd_azf3328_codec_inb(codec, IDX_IO_CODEC_IRQTYPE);
 		which = snd_azf3328_codec_inb(codec, IDX_IO_CODEC_IRQTYPE);
 		/* ack all IRQ types immediately */
 		/* ack all IRQ types immediately */
 		snd_azf3328_codec_outb(codec, IDX_IO_CODEC_IRQTYPE, which);
 		snd_azf3328_codec_outb(codec, IDX_IO_CODEC_IRQTYPE, which);
-		spin_unlock(&chip->reg_lock);
+		spin_unlock(codec->lock);
 
 
-		if ((chip->pcm[codec_type]) && (codec->substream)) {
+		if (codec->substream) {
 			snd_pcm_period_elapsed(codec->substream);
 			snd_pcm_period_elapsed(codec->substream);
 			snd_azf3328_dbgcodec("%s period done (#%x), @ %x\n",
 			snd_azf3328_dbgcodec("%s period done (#%x), @ %x\n",
 				codec->name,
 				codec->name,
@@ -1701,7 +1711,7 @@ snd_azf3328_interrupt(int irq, void *dev_id)
 	}
 	}
 
 
 	if (status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT))
 	if (status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT))
-		snd_azf3328_codec_interrupt(chip, status);
+		snd_azf3328_pcm_interrupt(chip->codecs, status);
 
 
 	if (status & IRQ_GAMEPORT)
 	if (status & IRQ_GAMEPORT)
 		snd_azf3328_gameport_interrupt(chip);
 		snd_azf3328_gameport_interrupt(chip);
@@ -1789,101 +1799,85 @@ snd_azf3328_pcm_open(struct snd_pcm_substream *substream,
 {
 {
 	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
 	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
 
 
 	snd_azf3328_dbgcallenter();
 	snd_azf3328_dbgcallenter();
-	chip->codecs[codec_type].substream = substream;
+	codec->substream = substream;
 
 
 	/* same parameters for all our codecs - at least we think so... */
 	/* same parameters for all our codecs - at least we think so... */
 	runtime->hw = snd_azf3328_hardware;
 	runtime->hw = snd_azf3328_hardware;
 
 
 	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
 	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
 				   &snd_azf3328_hw_constraints_rates);
 				   &snd_azf3328_hw_constraints_rates);
+	runtime->private_data = codec;
 	snd_azf3328_dbgcallleave();
 	snd_azf3328_dbgcallleave();
 	return 0;
 	return 0;
 }
 }
 
 
 static int
 static int
-snd_azf3328_playback_open(struct snd_pcm_substream *substream)
+snd_azf3328_pcm_playback_open(struct snd_pcm_substream *substream)
 {
 {
 	return snd_azf3328_pcm_open(substream, AZF_CODEC_PLAYBACK);
 	return snd_azf3328_pcm_open(substream, AZF_CODEC_PLAYBACK);
 }
 }
 
 
 static int
 static int
-snd_azf3328_capture_open(struct snd_pcm_substream *substream)
+snd_azf3328_pcm_capture_open(struct snd_pcm_substream *substream)
 {
 {
 	return snd_azf3328_pcm_open(substream, AZF_CODEC_CAPTURE);
 	return snd_azf3328_pcm_open(substream, AZF_CODEC_CAPTURE);
 }
 }
 
 
 static int
 static int
-snd_azf3328_i2s_out_open(struct snd_pcm_substream *substream)
+snd_azf3328_pcm_i2s_out_open(struct snd_pcm_substream *substream)
 {
 {
 	return snd_azf3328_pcm_open(substream, AZF_CODEC_I2S_OUT);
 	return snd_azf3328_pcm_open(substream, AZF_CODEC_I2S_OUT);
 }
 }
 
 
 static int
 static int
-snd_azf3328_pcm_close(struct snd_pcm_substream *substream,
-		      enum snd_azf3328_codec_type codec_type
+snd_azf3328_pcm_close(struct snd_pcm_substream *substream
 )
 )
 {
 {
-	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
+	struct snd_azf3328_codec_data *codec =
+		substream->runtime->private_data;
 
 
 	snd_azf3328_dbgcallenter();
 	snd_azf3328_dbgcallenter();
-	chip->codecs[codec_type].substream = NULL;
+	codec->substream = NULL;
 	snd_azf3328_dbgcallleave();
 	snd_azf3328_dbgcallleave();
 	return 0;
 	return 0;
 }
 }
 
 
-static int
-snd_azf3328_playback_close(struct snd_pcm_substream *substream)
-{
-	return snd_azf3328_pcm_close(substream, AZF_CODEC_PLAYBACK);
-}
-
-static int
-snd_azf3328_capture_close(struct snd_pcm_substream *substream)
-{
-	return snd_azf3328_pcm_close(substream, AZF_CODEC_CAPTURE);
-}
-
-static int
-snd_azf3328_i2s_out_close(struct snd_pcm_substream *substream)
-{
-	return snd_azf3328_pcm_close(substream, AZF_CODEC_I2S_OUT);
-}
-
 /******************************************************************/
 /******************************************************************/
 
 
 static struct snd_pcm_ops snd_azf3328_playback_ops = {
 static struct snd_pcm_ops snd_azf3328_playback_ops = {
-	.open =		snd_azf3328_playback_open,
-	.close =	snd_azf3328_playback_close,
+	.open =		snd_azf3328_pcm_playback_open,
+	.close =	snd_azf3328_pcm_close,
 	.ioctl =	snd_pcm_lib_ioctl,
 	.ioctl =	snd_pcm_lib_ioctl,
 	.hw_params =	snd_azf3328_hw_params,
 	.hw_params =	snd_azf3328_hw_params,
 	.hw_free =	snd_azf3328_hw_free,
 	.hw_free =	snd_azf3328_hw_free,
-	.prepare =	snd_azf3328_codec_prepare,
-	.trigger =	snd_azf3328_codec_playback_trigger,
-	.pointer =	snd_azf3328_codec_playback_pointer
+	.prepare =	snd_azf3328_pcm_prepare,
+	.trigger =	snd_azf3328_pcm_trigger,
+	.pointer =	snd_azf3328_pcm_pointer
 };
 };
 
 
 static struct snd_pcm_ops snd_azf3328_capture_ops = {
 static struct snd_pcm_ops snd_azf3328_capture_ops = {
-	.open =		snd_azf3328_capture_open,
-	.close =	snd_azf3328_capture_close,
+	.open =		snd_azf3328_pcm_capture_open,
+	.close =	snd_azf3328_pcm_close,
 	.ioctl =	snd_pcm_lib_ioctl,
 	.ioctl =	snd_pcm_lib_ioctl,
 	.hw_params =	snd_azf3328_hw_params,
 	.hw_params =	snd_azf3328_hw_params,
 	.hw_free =	snd_azf3328_hw_free,
 	.hw_free =	snd_azf3328_hw_free,
-	.prepare =	snd_azf3328_codec_prepare,
-	.trigger =	snd_azf3328_codec_capture_trigger,
-	.pointer =	snd_azf3328_codec_capture_pointer
+	.prepare =	snd_azf3328_pcm_prepare,
+	.trigger =	snd_azf3328_pcm_trigger,
+	.pointer =	snd_azf3328_pcm_pointer
 };
 };
 
 
 static struct snd_pcm_ops snd_azf3328_i2s_out_ops = {
 static struct snd_pcm_ops snd_azf3328_i2s_out_ops = {
-	.open =		snd_azf3328_i2s_out_open,
-	.close =	snd_azf3328_i2s_out_close,
+	.open =		snd_azf3328_pcm_i2s_out_open,
+	.close =	snd_azf3328_pcm_close,
 	.ioctl =	snd_pcm_lib_ioctl,
 	.ioctl =	snd_pcm_lib_ioctl,
 	.hw_params =	snd_azf3328_hw_params,
 	.hw_params =	snd_azf3328_hw_params,
 	.hw_free =	snd_azf3328_hw_free,
 	.hw_free =	snd_azf3328_hw_free,
-	.prepare =	snd_azf3328_codec_prepare,
-	.trigger =	snd_azf3328_codec_i2s_out_trigger,
-	.pointer =	snd_azf3328_codec_i2s_out_pointer
+	.prepare =	snd_azf3328_pcm_prepare,
+	.trigger =	snd_azf3328_pcm_trigger,
+	.pointer =	snd_azf3328_pcm_pointer
 };
 };
 
 
 static int __devinit
 static int __devinit
@@ -1966,7 +1960,7 @@ snd_azf3328_timer_start(struct snd_timer *timer)
 		snd_azf3328_dbgtimer("delay was too low (%d)!\n", delay);
 		snd_azf3328_dbgtimer("delay was too low (%d)!\n", delay);
 		delay = 49; /* minimum time is 49 ticks */
 		delay = 49; /* minimum time is 49 ticks */
 	}
 	}
-	snd_azf3328_dbgtimer("setting timer countdown value %d, add COUNTDOWN|IRQ\n", delay);
+	snd_azf3328_dbgtimer("setting timer countdown value %d\n", delay);
 	delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE;
 	delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE;
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	snd_azf3328_ctrl_outl(chip, IDX_IO_TIMER_VALUE, delay);
 	snd_azf3328_ctrl_outl(chip, IDX_IO_TIMER_VALUE, delay);
@@ -2180,6 +2174,7 @@ snd_azf3328_create(struct snd_card *card,
 	};
 	};
 	u8 dma_init;
 	u8 dma_init;
 	enum snd_azf3328_codec_type codec_type;
 	enum snd_azf3328_codec_type codec_type;
+	struct snd_azf3328_codec_data *codec_setup;
 
 
 	*rchip = NULL;
 	*rchip = NULL;
 
 
@@ -2217,15 +2212,23 @@ snd_azf3328_create(struct snd_card *card,
 	chip->opl3_io  = pci_resource_start(pci, 3);
 	chip->opl3_io  = pci_resource_start(pci, 3);
 	chip->mixer_io = pci_resource_start(pci, 4);
 	chip->mixer_io = pci_resource_start(pci, 4);
 
 
-	chip->codecs[AZF_CODEC_PLAYBACK].io_base =
-				chip->ctrl_io + AZF_IO_OFFS_CODEC_PLAYBACK;
-	chip->codecs[AZF_CODEC_PLAYBACK].name = "PLAYBACK";
-	chip->codecs[AZF_CODEC_CAPTURE].io_base =
-				chip->ctrl_io + AZF_IO_OFFS_CODEC_CAPTURE;
-	chip->codecs[AZF_CODEC_CAPTURE].name = "CAPTURE";
-	chip->codecs[AZF_CODEC_I2S_OUT].io_base =
-				chip->ctrl_io + AZF_IO_OFFS_CODEC_I2S_OUT;
-	chip->codecs[AZF_CODEC_I2S_OUT].name = "I2S_OUT";
+	codec_setup = &chip->codecs[AZF_CODEC_PLAYBACK];
+	codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_PLAYBACK;
+	codec_setup->lock = &chip->reg_lock;
+	codec_setup->type = AZF_CODEC_PLAYBACK;
+	codec_setup->name = "PLAYBACK";
+
+	codec_setup = &chip->codecs[AZF_CODEC_CAPTURE];
+	codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_CAPTURE;
+	codec_setup->lock = &chip->reg_lock;
+	codec_setup->type = AZF_CODEC_CAPTURE;
+	codec_setup->name = "CAPTURE";
+
+	codec_setup = &chip->codecs[AZF_CODEC_I2S_OUT];
+	codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_I2S_OUT;
+	codec_setup->lock = &chip->reg_lock;
+	codec_setup->type = AZF_CODEC_I2S_OUT;
+	codec_setup->name = "I2S_OUT";
 
 
 	if (request_irq(pci->irq, snd_azf3328_interrupt,
 	if (request_irq(pci->irq, snd_azf3328_interrupt,
 			IRQF_SHARED, card->shortname, chip)) {
 			IRQF_SHARED, card->shortname, chip)) {
@@ -2257,15 +2260,15 @@ snd_azf3328_create(struct snd_card *card,
 		struct snd_azf3328_codec_data *codec =
 		struct snd_azf3328_codec_data *codec =
 			 &chip->codecs[codec_type];
 			 &chip->codecs[codec_type];
 
 
-		/* shutdown codecs to save power */
+		/* shutdown codecs to reduce power / noise */
 			/* have ...ctrl_codec_activity() act properly */
 			/* have ...ctrl_codec_activity() act properly */
 		codec->running = 1;
 		codec->running = 1;
 		snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
 		snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
 
 
-		spin_lock_irq(&chip->reg_lock);
+		spin_lock_irq(codec->lock);
 		snd_azf3328_codec_outb(codec, IDX_IO_CODEC_DMA_FLAGS,
 		snd_azf3328_codec_outb(codec, IDX_IO_CODEC_DMA_FLAGS,
 						 dma_init);
 						 dma_init);
-		spin_unlock_irq(&chip->reg_lock);
+		spin_unlock_irq(codec->lock);
 	}
 	}
 
 
 	snd_card_set_dev(card, &pci->dev);
 	snd_card_set_dev(card, &pci->dev);
@@ -2419,6 +2422,7 @@ snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
 
 
 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
 
 
+	/* same pcm object for playback/capture */
 	snd_pcm_suspend_all(chip->pcm[AZF_CODEC_PLAYBACK]);
 	snd_pcm_suspend_all(chip->pcm[AZF_CODEC_PLAYBACK]);
 	snd_pcm_suspend_all(chip->pcm[AZF_CODEC_I2S_OUT]);
 	snd_pcm_suspend_all(chip->pcm[AZF_CODEC_I2S_OUT]);
 
 

+ 2 - 8
sound/pci/bt87x.c

@@ -637,15 +637,9 @@ static struct snd_kcontrol_new snd_bt87x_capture_boost = {
 static int snd_bt87x_capture_source_info(struct snd_kcontrol *kcontrol,
 static int snd_bt87x_capture_source_info(struct snd_kcontrol *kcontrol,
 					 struct snd_ctl_elem_info *info)
 					 struct snd_ctl_elem_info *info)
 {
 {
-	static char *texts[3] = {"TV Tuner", "FM", "Mic/Line"};
+	static const char *const texts[3] = {"TV Tuner", "FM", "Mic/Line"};
 
 
-	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-	info->count = 1;
-	info->value.enumerated.items = 3;
-	if (info->value.enumerated.item > 2)
-		info->value.enumerated.item = 2;
-	strcpy(info->value.enumerated.name, texts[info->value.enumerated.item]);
-	return 0;
+	return snd_ctl_enum_info(info, 1, 3, texts);
 }
 }
 
 
 static int snd_bt87x_capture_source_get(struct snd_kcontrol *kcontrol,
 static int snd_bt87x_capture_source_get(struct snd_kcontrol *kcontrol,

+ 9 - 16
sound/pci/cmipci.c

@@ -2507,14 +2507,12 @@ static int snd_cmipci_line_in_mode_info(struct snd_kcontrol *kcontrol,
 					struct snd_ctl_elem_info *uinfo)
 					struct snd_ctl_elem_info *uinfo)
 {
 {
 	struct cmipci *cm = snd_kcontrol_chip(kcontrol);
 	struct cmipci *cm = snd_kcontrol_chip(kcontrol);
-	static char *texts[3] = { "Line-In", "Rear Output", "Bass Output" };
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-	uinfo->count = 1;
-	uinfo->value.enumerated.items = cm->chip_version >= 39 ? 3 : 2;
-	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
-		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
-	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
-	return 0;
+	static const char *const texts[3] = {
+		"Line-In", "Rear Output", "Bass Output"
+	};
+
+	return snd_ctl_enum_info(uinfo, 1,
+				 cm->chip_version >= 39 ? 3 : 2, texts);
 }
 }
 
 
 static inline unsigned int get_line_in_mode(struct cmipci *cm)
 static inline unsigned int get_line_in_mode(struct cmipci *cm)
@@ -2564,14 +2562,9 @@ static int snd_cmipci_line_in_mode_put(struct snd_kcontrol *kcontrol,
 static int snd_cmipci_mic_in_mode_info(struct snd_kcontrol *kcontrol,
 static int snd_cmipci_mic_in_mode_info(struct snd_kcontrol *kcontrol,
 				       struct snd_ctl_elem_info *uinfo)
 				       struct snd_ctl_elem_info *uinfo)
 {
 {
-	static char *texts[2] = { "Mic-In", "Center/LFE Output" };
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-	uinfo->count = 1;
-	uinfo->value.enumerated.items = 2;
-	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
-		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
-	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
-	return 0;
+	static const char *const texts[2] = { "Mic-In", "Center/LFE Output" };
+
+	return snd_ctl_enum_info(uinfo, 1, 2, texts);
 }
 }
 
 
 static int snd_cmipci_mic_in_mode_get(struct snd_kcontrol *kcontrol,
 static int snd_cmipci_mic_in_mode_get(struct snd_kcontrol *kcontrol,

+ 3 - 0
sound/pci/hda/hda_codec.c

@@ -4571,6 +4571,9 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
 		}
 		}
 		memset(cfg->hp_pins + cfg->hp_outs, 0,
 		memset(cfg->hp_pins + cfg->hp_outs, 0,
 		       sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs));
 		       sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs));
+		if (!cfg->hp_outs)
+			cfg->line_out_type = AUTO_PIN_HP_OUT;
+
 	}
 	}
 
 
 	/* sort by sequence */
 	/* sort by sequence */

+ 6 - 3
sound/pci/hda/hda_intel.c

@@ -1235,7 +1235,8 @@ static int azx_setup_periods(struct azx *chip,
 			pos_adj = 0;
 			pos_adj = 0;
 		} else {
 		} else {
 			ofs = setup_bdle(substream, azx_dev,
 			ofs = setup_bdle(substream, azx_dev,
-					 &bdl, ofs, pos_adj, 1);
+					 &bdl, ofs, pos_adj,
+					 !substream->runtime->no_period_wakeup);
 			if (ofs < 0)
 			if (ofs < 0)
 				goto error;
 				goto error;
 		}
 		}
@@ -1247,7 +1248,8 @@ static int azx_setup_periods(struct azx *chip,
 					 period_bytes - pos_adj, 0);
 					 period_bytes - pos_adj, 0);
 		else
 		else
 			ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
 			ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
-					 period_bytes, 1);
+					 period_bytes,
+					 !substream->runtime->no_period_wakeup);
 		if (ofs < 0)
 		if (ofs < 0)
 			goto error;
 			goto error;
 	}
 	}
@@ -1515,7 +1517,8 @@ static struct snd_pcm_hardware azx_pcm_hw = {
 				 /* No full-resume yet implemented */
 				 /* No full-resume yet implemented */
 				 /* SNDRV_PCM_INFO_RESUME |*/
 				 /* SNDRV_PCM_INFO_RESUME |*/
 				 SNDRV_PCM_INFO_PAUSE |
 				 SNDRV_PCM_INFO_PAUSE |
-				 SNDRV_PCM_INFO_SYNC_START),
+				 SNDRV_PCM_INFO_SYNC_START |
+				 SNDRV_PCM_INFO_NO_PERIOD_WAKEUP),
 	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
 	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
 	.rates =		SNDRV_PCM_RATE_48000,
 	.rates =		SNDRV_PCM_RATE_48000,
 	.rate_min =		48000,
 	.rate_min =		48000,

+ 36 - 36
sound/pci/hda/patch_analog.c

@@ -666,7 +666,7 @@ static struct snd_kcontrol_new ad1986a_mixers[] = {
 	HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
@@ -729,7 +729,7 @@ static struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
 	HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
 	/* 
 	/* 
 	   HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
 	   HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
 	   HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
 	   HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
@@ -775,7 +775,7 @@ static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
 	HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
 	{
 	{
@@ -1358,7 +1358,7 @@ static struct snd_kcontrol_new ad1983_mixers[] = {
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Boost Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
 	{
 	{
@@ -1515,8 +1515,8 @@ static struct snd_kcontrol_new ad1981_mixers[] = {
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
 	{
 	{
@@ -1726,8 +1726,8 @@ static struct snd_kcontrol_new ad1981_hp_mixers[] = {
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
 #endif
 #endif
-	HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME("Internal Mic Boost", 0x18, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x18, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
 	{
 	{
@@ -1774,7 +1774,7 @@ static struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
 	{
 	{
@@ -2160,8 +2160,8 @@ static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
 	HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
 
 
-	HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
 
 
 	{ } /* end */
 	{ } /* end */
 };
 };
@@ -2203,8 +2203,8 @@ static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
 	HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
 
 
-	HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
 	{
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.name = "Channel Mode",
 		.name = "Channel Mode",
@@ -2232,7 +2232,7 @@ static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
 	HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
 
 
-	HDA_CODEC_VOLUME("Mic Boost", 0x39, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
 
 
 	{
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -2902,7 +2902,7 @@ static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
 		idx = ad1988_pin_idx(pin);
 		idx = ad1988_pin_idx(pin);
 		bnid = ad1988_boost_nids[idx];
 		bnid = ad1988_boost_nids[idx];
 		if (bnid) {
 		if (bnid) {
-			sprintf(name, "%s Boost", ctlname);
+			sprintf(name, "%s Boost Volume", ctlname);
 			return add_control(spec, AD_CTL_WIDGET_VOL, name,
 			return add_control(spec, AD_CTL_WIDGET_VOL, name,
 					   HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
 					   HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
 
 
@@ -3300,8 +3300,8 @@ static struct snd_kcontrol_new ad1884_base_mixers[] = {
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
-	HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
@@ -3499,9 +3499,9 @@ static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
 	HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
 	HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
 	HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
 	HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
 	HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
 	HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
-	HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
@@ -3560,8 +3560,8 @@ static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
 	HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
 	HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
 	HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
 	HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
-	HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line-In Boost Volume", 0x15, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
@@ -3745,9 +3745,9 @@ static struct snd_kcontrol_new ad1884a_base_mixers[] = {
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
-	HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Boost Volume", 0x15, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
@@ -3888,9 +3888,9 @@ static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
 	HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
 	HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
 	HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
 	HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
 	HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
 	HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
-	HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
 	{ } /* end */
 	{ } /* end */
@@ -4126,8 +4126,8 @@ static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
 	HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
 	HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
-	HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
 	{
 	{
@@ -4255,8 +4255,8 @@ static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
 	HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
 	HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT),
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -4494,9 +4494,9 @@ static struct snd_kcontrol_new ad1882_base_mixers[] = {
 	HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
 
 
-	HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Line-In Boost Volume", 0x3a, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
@@ -4547,7 +4547,7 @@ static struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
 	HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
 	HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
-	HDA_CODEC_VOLUME("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Digital Mic Boost Volume", 0x1f, 0x0, HDA_INPUT),
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 

+ 53 - 55
sound/pci/hda/patch_conexant.c

@@ -869,16 +869,16 @@ static void cxt5045_hp_unsol_event(struct hda_codec *codec,
 }
 }
 
 
 static struct snd_kcontrol_new cxt5045_mixers[] = {
 static struct snd_kcontrol_new cxt5045_mixers[] = {
-	HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x01, HDA_INPUT),
-	HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x01, HDA_INPUT),
-	HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x02, HDA_INPUT),
-	HDA_CODEC_MUTE("Ext Mic Capture Switch", 0x1a, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x1a, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Internal Mic Capture Switch", 0x1a, 0x01, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Capture Volume", 0x1a, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Capture Switch", 0x1a, 0x02, HDA_INPUT),
 	HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x17, 0x1, HDA_INPUT),
-	HDA_CODEC_MUTE("Int Mic Playback Switch", 0x17, 0x1, HDA_INPUT),
-	HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x17, 0x2, HDA_INPUT),
-	HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x17, 0x2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x17, 0x2, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x17, 0x2, HDA_INPUT),
 	HDA_BIND_VOL("Master Playback Volume", &cxt5045_hp_bind_master_vol),
 	HDA_BIND_VOL("Master Playback Volume", &cxt5045_hp_bind_master_vol),
 	{
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -910,16 +910,16 @@ static struct snd_kcontrol_new cxt5045_benq_mixers[] = {
 };
 };
 
 
 static struct snd_kcontrol_new cxt5045_mixers_hp530[] = {
 static struct snd_kcontrol_new cxt5045_mixers_hp530[] = {
-	HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x02, HDA_INPUT),
-	HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x02, HDA_INPUT),
-	HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x01, HDA_INPUT),
-	HDA_CODEC_MUTE("Ext Mic Capture Switch", 0x1a, 0x01, HDA_INPUT),
+	HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x1a, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Internal Mic Capture Switch", 0x1a, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Capture Volume", 0x1a, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Capture Switch", 0x1a, 0x01, HDA_INPUT),
 	HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x17, 0x2, HDA_INPUT),
-	HDA_CODEC_MUTE("Int Mic Playback Switch", 0x17, 0x2, HDA_INPUT),
-	HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x17, 0x1, HDA_INPUT),
-	HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x17, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x2, HDA_INPUT),
+	HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x17, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x17, 0x1, HDA_INPUT),
 	HDA_BIND_VOL("Master Playback Volume", &cxt5045_hp_bind_master_vol),
 	HDA_BIND_VOL("Master Playback Volume", &cxt5045_hp_bind_master_vol),
 	{
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -947,7 +947,7 @@ static struct hda_verb cxt5045_init_verbs[] = {
 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-	/* Record selector: Int mic */
+	/* Record selector: Internal mic */
 	{0x1a, AC_VERB_SET_CONNECT_SEL,0x1},
 	{0x1a, AC_VERB_SET_CONNECT_SEL,0x1},
 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE,
 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE,
 	 AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17},
 	 AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17},
@@ -960,7 +960,7 @@ static struct hda_verb cxt5045_init_verbs[] = {
 };
 };
 
 
 static struct hda_verb cxt5045_benq_init_verbs[] = {
 static struct hda_verb cxt5045_benq_init_verbs[] = {
-	/* Int Mic, Mic */
+	/* Internal Mic, Mic */
 	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
 	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
 	/* Line In,HP, Amp  */
 	/* Line In,HP, Amp  */
@@ -973,7 +973,7 @@ static struct hda_verb cxt5045_benq_init_verbs[] = {
 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-	/* Record selector: Int mic */
+	/* Record selector: Internal mic */
 	{0x1a, AC_VERB_SET_CONNECT_SEL, 0x1},
 	{0x1a, AC_VERB_SET_CONNECT_SEL, 0x1},
 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE,
 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE,
 	 AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17},
 	 AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17},
@@ -1376,7 +1376,7 @@ static void cxt5047_hp_unsol_event(struct hda_codec *codec,
 static struct snd_kcontrol_new cxt5047_base_mixers[] = {
 static struct snd_kcontrol_new cxt5047_base_mixers[] = {
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x19, 0x02, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x19, 0x02, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x19, 0x02, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x19, 0x02, HDA_INPUT),
-	HDA_CODEC_VOLUME("Mic Boost", 0x1a, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Boost Volume", 0x1a, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT),
 	HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT),
@@ -1796,8 +1796,8 @@ static struct snd_kcontrol_new cxt5051_playback_mixers[] = {
 static struct snd_kcontrol_new cxt5051_capture_mixers[] = {
 static struct snd_kcontrol_new cxt5051_capture_mixers[] = {
 	HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
 	HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
 	HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
 	HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
-	HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT),
-	HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Volume", 0x14, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Switch", 0x14, 0x01, HDA_INPUT),
 	HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT),
 	HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT),
 	HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT),
 	HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT),
 	{}
 	{}
@@ -1806,8 +1806,8 @@ static struct snd_kcontrol_new cxt5051_capture_mixers[] = {
 static struct snd_kcontrol_new cxt5051_hp_mixers[] = {
 static struct snd_kcontrol_new cxt5051_hp_mixers[] = {
 	HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
 	HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
 	HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
 	HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
-	HDA_CODEC_VOLUME("External Mic Volume", 0x15, 0x00, HDA_INPUT),
-	HDA_CODEC_MUTE("External Mic Switch", 0x15, 0x00, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Volume", 0x15, 0x00, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Switch", 0x15, 0x00, HDA_INPUT),
 	{}
 	{}
 };
 };
 
 
@@ -1826,8 +1826,8 @@ static struct snd_kcontrol_new cxt5051_f700_mixers[] = {
 static struct snd_kcontrol_new cxt5051_toshiba_mixers[] = {
 static struct snd_kcontrol_new cxt5051_toshiba_mixers[] = {
 	HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
 	HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
 	HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
 	HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
-	HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT),
-	HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Volume", 0x14, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Switch", 0x14, 0x01, HDA_INPUT),
 	{}
 	{}
 };
 };
 
 
@@ -1847,7 +1847,7 @@ static struct hda_verb cxt5051_init_verbs[] = {
 	{0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
 	{0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
 	/* DAC1 */	
 	/* DAC1 */	
 	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-	/* Record selector: Int mic */
+	/* Record selector: Internal mic */
 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
@@ -1874,7 +1874,7 @@ static struct hda_verb cxt5051_hp_dv6736_init_verbs[] = {
 	{0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
 	{0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
 	/* DAC1 */
 	/* DAC1 */
 	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-	/* Record selector: Int mic */
+	/* Record selector: Internal mic */
 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
 	{0x14, AC_VERB_SET_CONNECT_SEL, 0x1},
 	{0x14, AC_VERB_SET_CONNECT_SEL, 0x1},
 	/* SPDIF route: PCM */
 	/* SPDIF route: PCM */
@@ -1904,7 +1904,7 @@ static struct hda_verb cxt5051_lenovo_x200_init_verbs[] = {
 	{0x19, AC_VERB_SET_CONNECT_SEL, 0x00},
 	{0x19, AC_VERB_SET_CONNECT_SEL, 0x00},
 	/* DAC1 */
 	/* DAC1 */
 	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-	/* Record selector: Int mic */
+	/* Record selector: Internal mic */
 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
@@ -1932,7 +1932,7 @@ static struct hda_verb cxt5051_f700_init_verbs[] = {
 	{0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
 	{0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
 	/* DAC1 */
 	/* DAC1 */
 	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-	/* Record selector: Int mic */
+	/* Record selector: Internal mic */
 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
 	{0x14, AC_VERB_SET_CONNECT_SEL, 0x1},
 	{0x14, AC_VERB_SET_CONNECT_SEL, 0x1},
 	/* SPDIF route: PCM */
 	/* SPDIF route: PCM */
@@ -2111,6 +2111,11 @@ static struct hda_channel_mode cxt5066_modes[1] = {
 	{ 2, NULL },
 	{ 2, NULL },
 };
 };
 
 
+#define HP_PRESENT_PORT_A	(1 << 0)
+#define HP_PRESENT_PORT_D	(1 << 1)
+#define hp_port_a_present(spec)	((spec)->hp_present & HP_PRESENT_PORT_A)
+#define hp_port_d_present(spec)	((spec)->hp_present & HP_PRESENT_PORT_D)
+
 static void cxt5066_update_speaker(struct hda_codec *codec)
 static void cxt5066_update_speaker(struct hda_codec *codec)
 {
 {
 	struct conexant_spec *spec = codec->spec;
 	struct conexant_spec *spec = codec->spec;
@@ -2120,24 +2125,20 @@ static void cxt5066_update_speaker(struct hda_codec *codec)
 		    spec->hp_present, spec->cur_eapd);
 		    spec->hp_present, spec->cur_eapd);
 
 
 	/* Port A (HP) */
 	/* Port A (HP) */
-	pinctl = ((spec->hp_present & 1) && spec->cur_eapd) ? PIN_HP : 0;
+	pinctl = (hp_port_a_present(spec) && spec->cur_eapd) ? PIN_HP : 0;
 	snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
 	snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
 			pinctl);
 			pinctl);
 
 
 	/* Port D (HP/LO) */
 	/* Port D (HP/LO) */
-	if (spec->dell_automute) {
-		/* DELL AIO Port Rule: PortA>  PortD>  IntSpk */
-		pinctl = (!(spec->hp_present & 1) && spec->cur_eapd)
-			? PIN_OUT : 0;
-	} else if (spec->thinkpad) {
-		if (spec->cur_eapd)
-			pinctl = spec->port_d_mode;
-		/* Mute dock line-out if Port A (laptop HP) is present */
-		if (spec->hp_present&  1)
+	pinctl = spec->cur_eapd ? spec->port_d_mode : 0;
+	if (spec->dell_automute || spec->thinkpad) {
+		/* Mute if Port A is connected */
+		if (hp_port_a_present(spec))
 			pinctl = 0;
 			pinctl = 0;
 	} else {
 	} else {
-		pinctl = ((spec->hp_present & 2) && spec->cur_eapd)
-			? spec->port_d_mode : 0;
+		/* Thinkpad/Dell doesn't give pin-D status */
+		if (!hp_port_d_present(spec))
+			pinctl = 0;
 	}
 	}
 	snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
 	snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
 			pinctl);
 			pinctl);
@@ -2379,8 +2380,8 @@ static void cxt5066_hp_automute(struct hda_codec *codec)
 	/* Port D */
 	/* Port D */
 	portD = snd_hda_jack_detect(codec, 0x1c);
 	portD = snd_hda_jack_detect(codec, 0x1c);
 
 
-	spec->hp_present = !!(portA);
-	spec->hp_present |= portD ? 2 : 0;
+	spec->hp_present = portA ? HP_PRESENT_PORT_A : 0;
+	spec->hp_present |= portD ? HP_PRESENT_PORT_D : 0;
 	snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n",
 	snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n",
 		portA, portD, spec->hp_present);
 		portA, portD, spec->hp_present);
 	cxt5066_update_speaker(codec);
 	cxt5066_update_speaker(codec);
@@ -2728,7 +2729,7 @@ static struct snd_kcontrol_new cxt5066_mixers[] = {
 static struct snd_kcontrol_new cxt5066_vostro_mixers[] = {
 static struct snd_kcontrol_new cxt5066_vostro_mixers[] = {
 	{
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Int Mic Boost Capture Enum",
+		.name = "Internal Mic Boost Capture Enum",
 		.info = cxt5066_mic_boost_mux_enum_info,
 		.info = cxt5066_mic_boost_mux_enum_info,
 		.get = cxt5066_mic_boost_mux_enum_get,
 		.get = cxt5066_mic_boost_mux_enum_get,
 		.put = cxt5066_mic_boost_mux_enum_put,
 		.put = cxt5066_mic_boost_mux_enum_put,
@@ -2954,7 +2955,7 @@ static struct hda_verb cxt5066_init_verbs_ideapad[] = {
 	{0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
 	{0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
 
 
 	/* internal microphone */
 	/* internal microphone */
-	{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* enable int mic */
+	{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* enable internal mic */
 
 
 	/* EAPD */
 	/* EAPD */
 	{0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
 	{0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
@@ -3009,7 +3010,7 @@ static struct hda_verb cxt5066_init_verbs_thinkpad[] = {
 	{0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
 	{0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
 
 
 	/* internal microphone */
 	/* internal microphone */
-	{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* enable int mic */
+	{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* enable internal mic */
 
 
 	/* EAPD */
 	/* EAPD */
 	{0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
 	{0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
@@ -3097,6 +3098,7 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
 	SND_PCI_QUIRK_MASK(0x1025, 0xff00, 0x0400, "Acer", CXT5066_IDEAPAD),
 	SND_PCI_QUIRK_MASK(0x1025, 0xff00, 0x0400, "Acer", CXT5066_IDEAPAD),
 	SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTRO),
 	SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTRO),
 	SND_PCI_QUIRK(0x1028, 0x02f5, "Dell Vostro 320", CXT5066_IDEAPAD),
 	SND_PCI_QUIRK(0x1028, 0x02f5, "Dell Vostro 320", CXT5066_IDEAPAD),
+	SND_PCI_QUIRK(0x1028, 0x0401, "Dell Vostro 1014", CXT5066_DELL_VOSTRO),
 	SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTRO),
 	SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTRO),
 	SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD),
 	SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD),
 	SND_PCI_QUIRK(0x103c, 0x360b, "HP G60", CXT5066_HP_LAPTOP),
 	SND_PCI_QUIRK(0x103c, 0x360b, "HP G60", CXT5066_HP_LAPTOP),
@@ -3108,16 +3110,9 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
 		      CXT5066_LAPTOP),
 		      CXT5066_LAPTOP),
 	SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5),
 	SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5),
 	SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD),
 	SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD),
-	SND_PCI_QUIRK(0x17aa, 0x21b2, "Thinkpad X100e", CXT5066_IDEAPAD),
 	SND_PCI_QUIRK(0x17aa, 0x21c5, "Thinkpad Edge 13", CXT5066_THINKPAD),
 	SND_PCI_QUIRK(0x17aa, 0x21c5, "Thinkpad Edge 13", CXT5066_THINKPAD),
-	SND_PCI_QUIRK(0x17aa, 0x21b3, "Thinkpad Edge 13 (197)", CXT5066_IDEAPAD),
-	SND_PCI_QUIRK(0x17aa, 0x21b4, "Thinkpad Edge", CXT5066_IDEAPAD),
-	SND_PCI_QUIRK(0x17aa, 0x21c8, "Thinkpad Edge 11", CXT5066_IDEAPAD),
  	SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD),
  	SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD),
- 	SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G series", CXT5066_IDEAPAD),
-	SND_PCI_QUIRK(0x17aa, 0x390a, "Lenovo S10-3t", CXT5066_IDEAPAD),
-	SND_PCI_QUIRK(0x17aa, 0x3938, "Lenovo G series (AMD)", CXT5066_IDEAPAD),
-	SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD),
+	SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", CXT5066_IDEAPAD), /* Fallback for Lenovos without dock mic */
 	{}
 	{}
 };
 };
 
 
@@ -3422,6 +3417,9 @@ static void cx_auto_hp_automute(struct hda_codec *codec)
 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
 				    present ? 0 : PIN_OUT);
 				    present ? 0 : PIN_OUT);
 	}
 	}
+	for (i = 0; !present && i < cfg->line_outs; i++)
+		if (snd_hda_jack_detect(codec, cfg->line_out_pins[i]))
+			present = 1;
 	for (i = 0; i < cfg->speaker_outs; i++) {
 	for (i = 0; i < cfg->speaker_outs; i++) {
 		snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
 		snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
 				    AC_VERB_SET_PIN_WIDGET_CONTROL,

+ 81 - 11
sound/pci/hda/patch_hdmi.c

@@ -31,10 +31,15 @@
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
+#include <linux/moduleparam.h>
 #include <sound/core.h>
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_local.h"
 
 
+static bool static_hdmi_pcm;
+module_param(static_hdmi_pcm, bool, 0644);
+MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
+
 /*
 /*
  * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
  * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
  * could support two independent pipes, each of them can be connected to one or
  * could support two independent pipes, each of them can be connected to one or
@@ -827,7 +832,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
 		*codec_pars = *hinfo;
 		*codec_pars = *hinfo;
 
 
 	eld = &spec->sink_eld[idx];
 	eld = &spec->sink_eld[idx];
-	if (eld->sad_count > 0) {
+	if (!static_hdmi_pcm && eld->eld_valid && eld->sad_count > 0) {
 		hdmi_eld_update_pcm_info(eld, hinfo, codec_pars);
 		hdmi_eld_update_pcm_info(eld, hinfo, codec_pars);
 		if (hinfo->channels_min > hinfo->channels_max ||
 		if (hinfo->channels_min > hinfo->channels_max ||
 		    !hinfo->rates || !hinfo->formats)
 		    !hinfo->rates || !hinfo->formats)
@@ -904,23 +909,28 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
 	spec->pin[spec->num_pins] = pin_nid;
 	spec->pin[spec->num_pins] = pin_nid;
 	spec->num_pins++;
 	spec->num_pins++;
 
 
-	/*
-	 * It is assumed that converter nodes come first in the node list and
-	 * hence have been registered and usable now.
-	 */
 	return hdmi_read_pin_conn(codec, pin_nid);
 	return hdmi_read_pin_conn(codec, pin_nid);
 }
 }
 
 
 static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid)
 static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid)
 {
 {
+	int i, found_pin = 0;
 	struct hdmi_spec *spec = codec->spec;
 	struct hdmi_spec *spec = codec->spec;
 
 
-	if (spec->num_cvts >= MAX_HDMI_CVTS) {
-		snd_printk(KERN_WARNING
-			   "HDMI: no space for converter %d\n", nid);
-		return -E2BIG;
+	for (i = 0; i < spec->num_pins; i++)
+		if (nid == spec->pin_cvt[i]) {
+			found_pin = 1;
+			break;
+		}
+
+	if (!found_pin) {
+		snd_printdd("HDMI: Skipping node %d (no connection)\n", nid);
+		return -EINVAL;
 	}
 	}
 
 
+	if (snd_BUG_ON(spec->num_cvts >= MAX_HDMI_CVTS))
+		return -E2BIG;
+
 	spec->cvt[spec->num_cvts] = nid;
 	spec->cvt[spec->num_cvts] = nid;
 	spec->num_cvts++;
 	spec->num_cvts++;
 
 
@@ -931,6 +941,8 @@ static int hdmi_parse_codec(struct hda_codec *codec)
 {
 {
 	hda_nid_t nid;
 	hda_nid_t nid;
 	int i, nodes;
 	int i, nodes;
+	int num_tmp_cvts = 0;
+	hda_nid_t tmp_cvt[MAX_HDMI_CVTS];
 
 
 	nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
 	nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
 	if (!nid || nodes < 0) {
 	if (!nid || nodes < 0) {
@@ -941,6 +953,7 @@ static int hdmi_parse_codec(struct hda_codec *codec)
 	for (i = 0; i < nodes; i++, nid++) {
 	for (i = 0; i < nodes; i++, nid++) {
 		unsigned int caps;
 		unsigned int caps;
 		unsigned int type;
 		unsigned int type;
+		unsigned int config;
 
 
 		caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
 		caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
 		type = get_wcaps_type(caps);
 		type = get_wcaps_type(caps);
@@ -950,17 +963,32 @@ static int hdmi_parse_codec(struct hda_codec *codec)
 
 
 		switch (type) {
 		switch (type) {
 		case AC_WID_AUD_OUT:
 		case AC_WID_AUD_OUT:
-			hdmi_add_cvt(codec, nid);
+			if (num_tmp_cvts >= MAX_HDMI_CVTS) {
+				snd_printk(KERN_WARNING
+					   "HDMI: no space for converter %d\n", nid);
+				continue;
+			}
+			tmp_cvt[num_tmp_cvts] = nid;
+			num_tmp_cvts++;
 			break;
 			break;
 		case AC_WID_PIN:
 		case AC_WID_PIN:
 			caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
 			caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
 			if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
 			if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
 				continue;
 				continue;
+
+			config = snd_hda_codec_read(codec, nid, 0,
+					     AC_VERB_GET_CONFIG_DEFAULT, 0);
+			if (get_defcfg_connect(config) == AC_JACK_PORT_NONE)
+				continue;
+
 			hdmi_add_pin(codec, nid);
 			hdmi_add_pin(codec, nid);
 			break;
 			break;
 		}
 		}
 	}
 	}
 
 
+	for (i = 0; i < num_tmp_cvts; i++)
+		hdmi_add_cvt(codec, tmp_cvt[i]);
+
 	/*
 	/*
 	 * G45/IbexPeak don't support EPSS: the unsolicited pin hot plug event
 	 * G45/IbexPeak don't support EPSS: the unsolicited pin hot plug event
 	 * can be lost and presence sense verb will become inaccurate if the
 	 * can be lost and presence sense verb will become inaccurate if the
@@ -1165,11 +1193,53 @@ static int nvhdmi_7x_init(struct hda_codec *codec)
 	return 0;
 	return 0;
 }
 }
 
 
+static unsigned int channels_2_6_8[] = {
+	2, 6, 8
+};
+
+static unsigned int channels_2_8[] = {
+	2, 8
+};
+
+static struct snd_pcm_hw_constraint_list hw_constraints_2_6_8_channels = {
+	.count = ARRAY_SIZE(channels_2_6_8),
+	.list = channels_2_6_8,
+	.mask = 0,
+};
+
+static struct snd_pcm_hw_constraint_list hw_constraints_2_8_channels = {
+	.count = ARRAY_SIZE(channels_2_8),
+	.list = channels_2_8,
+	.mask = 0,
+};
+
 static int simple_playback_pcm_open(struct hda_pcm_stream *hinfo,
 static int simple_playback_pcm_open(struct hda_pcm_stream *hinfo,
 				    struct hda_codec *codec,
 				    struct hda_codec *codec,
 				    struct snd_pcm_substream *substream)
 				    struct snd_pcm_substream *substream)
 {
 {
 	struct hdmi_spec *spec = codec->spec;
 	struct hdmi_spec *spec = codec->spec;
+	struct snd_pcm_hw_constraint_list *hw_constraints_channels = NULL;
+
+	switch (codec->preset->id) {
+	case 0x10de0002:
+	case 0x10de0003:
+	case 0x10de0005:
+	case 0x10de0006:
+		hw_constraints_channels = &hw_constraints_2_8_channels;
+		break;
+	case 0x10de0007:
+		hw_constraints_channels = &hw_constraints_2_6_8_channels;
+		break;
+	default:
+		break;
+	}
+
+	if (hw_constraints_channels != NULL) {
+		snd_pcm_hw_constraint_list(substream->runtime, 0,
+				SNDRV_PCM_HW_PARAM_CHANNELS,
+				hw_constraints_channels);
+	}
+
 	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
 	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
 }
 }
 
 
@@ -1532,7 +1602,7 @@ static struct hda_codec_preset snd_hda_preset_hdmi[] = {
 { .id = 0x1002793c, .name = "RS600 HDMI",	.patch = patch_atihdmi },
 { .id = 0x1002793c, .name = "RS600 HDMI",	.patch = patch_atihdmi },
 { .id = 0x10027919, .name = "RS600 HDMI",	.patch = patch_atihdmi },
 { .id = 0x10027919, .name = "RS600 HDMI",	.patch = patch_atihdmi },
 { .id = 0x1002791a, .name = "RS690/780 HDMI",	.patch = patch_atihdmi },
 { .id = 0x1002791a, .name = "RS690/780 HDMI",	.patch = patch_atihdmi },
-{ .id = 0x1002aa01, .name = "R6xx HDMI",	.patch = patch_atihdmi },
+{ .id = 0x1002aa01, .name = "R6xx HDMI",	.patch = patch_generic_hdmi },
 { .id = 0x10951390, .name = "SiI1390 HDMI",	.patch = patch_generic_hdmi },
 { .id = 0x10951390, .name = "SiI1390 HDMI",	.patch = patch_generic_hdmi },
 { .id = 0x10951392, .name = "SiI1392 HDMI",	.patch = patch_generic_hdmi },
 { .id = 0x10951392, .name = "SiI1392 HDMI",	.patch = patch_generic_hdmi },
 { .id = 0x17e80047, .name = "Chrontel HDMI",	.patch = patch_generic_hdmi },
 { .id = 0x17e80047, .name = "Chrontel HDMI",	.patch = patch_generic_hdmi },

文件差异内容过多而无法显示
+ 213 - 196
sound/pci/hda/patch_realtek.c


+ 13 - 4
sound/pci/hda/patch_sigmatel.c

@@ -389,6 +389,9 @@ static hda_nid_t stac92hd83xxx_dmic_nids[STAC92HD83XXX_NUM_DMICS + 1] = {
 	0x11, 0x20, 0
 	0x11, 0x20, 0
 };
 };
 
 
+#define STAC92HD88XXX_NUM_DMICS	STAC92HD83XXX_NUM_DMICS
+#define stac92hd88xxx_dmic_nids	stac92hd83xxx_dmic_nids
+
 #define STAC92HD87B_NUM_DMICS	 1
 #define STAC92HD87B_NUM_DMICS	 1
 static hda_nid_t stac92hd87b_dmic_nids[STAC92HD87B_NUM_DMICS + 1] = {
 static hda_nid_t stac92hd87b_dmic_nids[STAC92HD87B_NUM_DMICS + 1] = {
 	0x11, 0
 	0x11, 0
@@ -3591,7 +3594,7 @@ static int stac_check_auto_mic(struct hda_codec *codec)
 		if (check_mic_pin(codec, spec->dmic_nids[i],
 		if (check_mic_pin(codec, spec->dmic_nids[i],
 		    &fixed, &ext, &dock))
 		    &fixed, &ext, &dock))
 			return 0;
 			return 0;
-	if (!fixed && !ext && !dock)
+	if (!fixed || (!ext && !dock))
 		return 0; /* no input to switch */
 		return 0; /* no input to switch */
 	if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP))
 	if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP))
 		return 0; /* no unsol support */
 		return 0; /* no unsol support */
@@ -5422,7 +5425,7 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
 	snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7ED, 0);
 	snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7ED, 0);
 	codec->no_trigger_sense = 1;
 	codec->no_trigger_sense = 1;
 	codec->spec = spec;
 	codec->spec = spec;
-	spec->linear_tone_beep = 1;
+	spec->linear_tone_beep = 0;
 	codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs;
 	codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs;
 	spec->digbeep_nid = 0x21;
 	spec->digbeep_nid = 0x21;
 	spec->dmic_nids = stac92hd83xxx_dmic_nids;
 	spec->dmic_nids = stac92hd83xxx_dmic_nids;
@@ -5462,15 +5465,21 @@ again:
 		spec->num_dmics = stac92xx_connected_ports(codec,
 		spec->num_dmics = stac92xx_connected_ports(codec,
 				stac92hd87b_dmic_nids,
 				stac92hd87b_dmic_nids,
 				STAC92HD87B_NUM_DMICS);
 				STAC92HD87B_NUM_DMICS);
-		/* Fall through */
+		spec->num_pins = ARRAY_SIZE(stac92hd88xxx_pin_nids);
+		spec->pin_nids = stac92hd88xxx_pin_nids;
+		spec->mono_nid = 0;
+		spec->num_pwrs = 0;
+		break;
 	case 0x111d7666:
 	case 0x111d7666:
 	case 0x111d7667:
 	case 0x111d7667:
 	case 0x111d7668:
 	case 0x111d7668:
 	case 0x111d7669:
 	case 0x111d7669:
+		spec->num_dmics = stac92xx_connected_ports(codec,
+				stac92hd88xxx_dmic_nids,
+				STAC92HD88XXX_NUM_DMICS);
 		spec->num_pins = ARRAY_SIZE(stac92hd88xxx_pin_nids);
 		spec->num_pins = ARRAY_SIZE(stac92hd88xxx_pin_nids);
 		spec->pin_nids = stac92hd88xxx_pin_nids;
 		spec->pin_nids = stac92hd88xxx_pin_nids;
 		spec->mono_nid = 0;
 		spec->mono_nid = 0;
-		spec->digbeep_nid = 0;
 		spec->num_pwrs = 0;
 		spec->num_pwrs = 0;
 		break;
 		break;
 	case 0x111d7604:
 	case 0x111d7604:

+ 1 - 2
sound/pci/hda/patch_via.c

@@ -263,8 +263,7 @@ static void vt1708_stop_hp_work(struct via_spec *spec)
 		return;
 		return;
 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
 			    !spec->vt1708_jack_detectect);
 			    !spec->vt1708_jack_detectect);
-	cancel_delayed_work(&spec->vt1708_hp_work);
-	flush_scheduled_work();
+	cancel_delayed_work_sync(&spec->vt1708_hp_work);
 }
 }
 
 
 
 

+ 48 - 1
sound/pci/ice1712/delta.c

@@ -96,6 +96,11 @@ static unsigned char ap_cs8427_codec_select(struct snd_ice1712 *ice)
 		tmp |= ICE1712_DELTA_AP_CCLK | ICE1712_DELTA_AP_CS_CODEC;
 		tmp |= ICE1712_DELTA_AP_CCLK | ICE1712_DELTA_AP_CS_CODEC;
 		tmp &= ~ICE1712_DELTA_AP_CS_DIGITAL;
 		tmp &= ~ICE1712_DELTA_AP_CS_DIGITAL;
 		break;
 		break;
+	case ICE1712_SUBDEVICE_DELTA66E:
+		tmp |= ICE1712_DELTA_66E_CCLK | ICE1712_DELTA_66E_CS_CHIP_A |
+		       ICE1712_DELTA_66E_CS_CHIP_B;
+		tmp &= ~ICE1712_DELTA_66E_CS_CS8427;
+		break;
 	case ICE1712_SUBDEVICE_VX442:
 	case ICE1712_SUBDEVICE_VX442:
 		tmp |= ICE1712_VX442_CCLK | ICE1712_VX442_CODEC_CHIP_A | ICE1712_VX442_CODEC_CHIP_B;
 		tmp |= ICE1712_VX442_CCLK | ICE1712_VX442_CODEC_CHIP_A | ICE1712_VX442_CODEC_CHIP_B;
 		tmp &= ~ICE1712_VX442_CS_DIGITAL;
 		tmp &= ~ICE1712_VX442_CS_DIGITAL;
@@ -119,6 +124,9 @@ static void ap_cs8427_codec_deassert(struct snd_ice1712 *ice, unsigned char tmp)
 	case ICE1712_SUBDEVICE_DELTA410:
 	case ICE1712_SUBDEVICE_DELTA410:
 		tmp |= ICE1712_DELTA_AP_CS_DIGITAL;
 		tmp |= ICE1712_DELTA_AP_CS_DIGITAL;
 		break;
 		break;
+	case ICE1712_SUBDEVICE_DELTA66E:
+		tmp |= ICE1712_DELTA_66E_CS_CS8427;
+		break;
 	case ICE1712_SUBDEVICE_VX442:
 	case ICE1712_SUBDEVICE_VX442:
 		tmp |= ICE1712_VX442_CS_DIGITAL;
 		tmp |= ICE1712_VX442_CS_DIGITAL;
 		break;
 		break;
@@ -275,6 +283,20 @@ static void delta1010lt_ak4524_lock(struct snd_akm4xxx *ak, int chip)
 	priv->cs_addr = chip << 4;
 	priv->cs_addr = chip << 4;
 }
 }
 
 
+/*
+ * AK4524 on Delta66 rev E to choose the chip address
+ */
+static void delta66e_ak4524_lock(struct snd_akm4xxx *ak, int chip)
+{
+	struct snd_ak4xxx_private *priv = (void *)ak->private_value[0];
+	struct snd_ice1712 *ice = ak->private_data[0];
+
+	snd_ice1712_save_gpio_status(ice);
+	priv->cs_mask =
+	priv->cs_addr = chip == 0 ? ICE1712_DELTA_66E_CS_CHIP_A :
+				    ICE1712_DELTA_66E_CS_CHIP_B;
+}
+
 /*
 /*
  * AK4528 on VX442 to choose the chip mask
  * AK4528 on VX442 to choose the chip mask
  */
  */
@@ -487,6 +509,29 @@ static struct snd_ak4xxx_private akm_delta1010lt_priv __devinitdata = {
 	.mask_flags = 0,
 	.mask_flags = 0,
 };
 };
 
 
+static struct snd_akm4xxx akm_delta66e __devinitdata = {
+	.type = SND_AK4524,
+	.num_adcs = 4,
+	.num_dacs = 4,
+	.ops = {
+		.lock = delta66e_ak4524_lock,
+		.set_rate_val = delta_ak4524_set_rate_val
+	}
+};
+
+static struct snd_ak4xxx_private akm_delta66e_priv __devinitdata = {
+	.caddr = 2,
+	.cif = 0, /* the default level of the CIF pin from AK4524 */
+	.data_mask = ICE1712_DELTA_66E_DOUT,
+	.clk_mask = ICE1712_DELTA_66E_CCLK,
+	.cs_mask = 0,
+	.cs_addr = 0, /* set later */
+	.cs_none = 0,
+	.add_flags = 0,
+	.mask_flags = 0,
+};
+
+
 static struct snd_akm4xxx akm_delta44 __devinitdata = {
 static struct snd_akm4xxx akm_delta44 __devinitdata = {
 	.type = SND_AK4524,
 	.type = SND_AK4524,
 	.num_adcs = 4,
 	.num_adcs = 4,
@@ -644,9 +689,11 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
 		err = snd_ice1712_akm4xxx_init(ak, &akm_delta44, &akm_delta44_priv, ice);
 		err = snd_ice1712_akm4xxx_init(ak, &akm_delta44, &akm_delta44_priv, ice);
 		break;
 		break;
 	case ICE1712_SUBDEVICE_VX442:
 	case ICE1712_SUBDEVICE_VX442:
-	case ICE1712_SUBDEVICE_DELTA66E:
 		err = snd_ice1712_akm4xxx_init(ak, &akm_vx442, &akm_vx442_priv, ice);
 		err = snd_ice1712_akm4xxx_init(ak, &akm_vx442, &akm_vx442_priv, ice);
 		break;
 		break;
+	case ICE1712_SUBDEVICE_DELTA66E:
+		err = snd_ice1712_akm4xxx_init(ak, &akm_delta66e, &akm_delta66e_priv, ice);
+		break;
 	default:
 	default:
 		snd_BUG();
 		snd_BUG();
 		return -EINVAL;
 		return -EINVAL;

+ 11 - 0
sound/pci/ice1712/delta.h

@@ -144,6 +144,17 @@ extern struct snd_ice1712_card_info snd_ice1712_delta_cards[];
 #define ICE1712_DELTA_1010LT_CS_NONE	0x50	/* nothing */
 #define ICE1712_DELTA_1010LT_CS_NONE	0x50	/* nothing */
 #define ICE1712_DELTA_1010LT_WORDCLOCK 0x80	/* sample clock source: 0 = Word Clock Input, 1 = S/PDIF Input ??? */
 #define ICE1712_DELTA_1010LT_WORDCLOCK 0x80	/* sample clock source: 0 = Word Clock Input, 1 = S/PDIF Input ??? */
 
 
+/* M-Audio Delta 66 rev. E definitions.
+ * Newer revisions of Delta 66 have CS8427 over SPI for
+ * S/PDIF transceiver instead of CS8404/CS8414. */
+/* 0x01 = DFS */
+#define ICE1712_DELTA_66E_CCLK		0x02	/* SPI clock */
+#define ICE1712_DELTA_66E_DIN		0x04	/* data input */
+#define ICE1712_DELTA_66E_DOUT		0x08	/* data output */
+#define ICE1712_DELTA_66E_CS_CS8427	0x10	/* chip select, low = CS8427 */
+#define ICE1712_DELTA_66E_CS_CHIP_A	0x20	/* AK4524 #0 */
+#define ICE1712_DELTA_66E_CS_CHIP_B	0x40	/* AK4524 #1 */
+
 /* Digigram VX442 definitions */
 /* Digigram VX442 definitions */
 #define ICE1712_VX442_CCLK		0x02	/* SPI clock */
 #define ICE1712_VX442_CCLK		0x02	/* SPI clock */
 #define ICE1712_VX442_DIN		0x04	/* data input */
 #define ICE1712_VX442_DIN		0x04	/* data input */

+ 1 - 3
sound/pci/oxygen/Makefile

@@ -1,10 +1,8 @@
 snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o
 snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o
-snd-hifier-objs := hifier.o
-snd-oxygen-objs := oxygen.o
+snd-oxygen-objs := oxygen.o xonar_dg.o
 snd-virtuoso-objs := virtuoso.o xonar_lib.o \
 snd-virtuoso-objs := virtuoso.o xonar_lib.o \
 	xonar_pcm179x.o xonar_cs43xx.o xonar_wm87x6.o xonar_hdmi.o
 	xonar_pcm179x.o xonar_cs43xx.o xonar_wm87x6.o xonar_hdmi.o
 
 
 obj-$(CONFIG_SND_OXYGEN_LIB) += snd-oxygen-lib.o
 obj-$(CONFIG_SND_OXYGEN_LIB) += snd-oxygen-lib.o
-obj-$(CONFIG_SND_HIFIER) += snd-hifier.o
 obj-$(CONFIG_SND_OXYGEN) += snd-oxygen.o
 obj-$(CONFIG_SND_OXYGEN) += snd-oxygen.o
 obj-$(CONFIG_SND_VIRTUOSO) += snd-virtuoso.o
 obj-$(CONFIG_SND_VIRTUOSO) += snd-virtuoso.o

+ 107 - 0
sound/pci/oxygen/cs4245.h

@@ -0,0 +1,107 @@
+#define CS4245_CHIP_ID		0x01
+#define CS4245_POWER_CTRL	0x02
+#define CS4245_DAC_CTRL_1	0x03
+#define CS4245_ADC_CTRL		0x04
+#define CS4245_MCLK_FREQ	0x05
+#define CS4245_SIGNAL_SEL	0x06
+#define CS4245_PGA_B_CTRL	0x07
+#define CS4245_PGA_A_CTRL	0x08
+#define CS4245_ANALOG_IN	0x09
+#define CS4245_DAC_A_CTRL	0x0a
+#define CS4245_DAC_B_CTRL	0x0b
+#define CS4245_DAC_CTRL_2	0x0c
+#define CS4245_INT_STATUS	0x0d
+#define CS4245_INT_MASK		0x0e
+#define CS4245_INT_MODE_MSB	0x0f
+#define CS4245_INT_MODE_LSB	0x10
+
+/* Chip ID */
+#define CS4245_CHIP_PART_MASK	0xf0
+#define CS4245_CHIP_REV_MASK	0x0f
+
+/* Power Control */
+#define CS4245_FREEZE		0x80
+#define CS4245_PDN_MIC		0x08
+#define CS4245_PDN_ADC		0x04
+#define CS4245_PDN_DAC		0x02
+#define CS4245_PDN		0x01
+
+/* DAC Control */
+#define CS4245_DAC_FM_MASK	0xc0
+#define CS4245_DAC_FM_SINGLE	0x00
+#define CS4245_DAC_FM_DOUBLE	0x40
+#define CS4245_DAC_FM_QUAD	0x80
+#define CS4245_DAC_DIF_MASK	0x30
+#define CS4245_DAC_DIF_LJUST	0x00
+#define CS4245_DAC_DIF_I2S	0x10
+#define CS4245_DAC_DIF_RJUST_16	0x20
+#define CS4245_DAC_DIF_RJUST_24	0x30
+#define CS4245_RESERVED_1	0x08
+#define CS4245_MUTE_DAC		0x04
+#define CS4245_DEEMPH		0x02
+#define CS4245_DAC_MASTER	0x01
+
+/* ADC Control */
+#define CS4245_ADC_FM_MASK	0xc0
+#define CS4245_ADC_FM_SINGLE	0x00
+#define CS4245_ADC_FM_DOUBLE	0x40
+#define CS4245_ADC_FM_QUAD	0x80
+#define CS4245_ADC_DIF_MASK	0x10
+#define CS4245_ADC_DIF_LJUST	0x00
+#define CS4245_ADC_DIF_I2S	0x10
+#define CS4245_MUTE_ADC		0x04
+#define CS4245_HPF_FREEZE	0x02
+#define CS4245_ADC_MASTER	0x01
+
+/* MCLK Frequency */
+#define CS4245_MCLK1_MASK	0x70
+#define CS4245_MCLK1_SHIFT	4
+#define CS4245_MCLK2_MASK	0x07
+#define CS4245_MCLK2_SHIFT	0
+#define CS4245_MCLK_1		0
+#define CS4245_MCLK_1_5		1
+#define CS4245_MCLK_2		2
+#define CS4245_MCLK_3		3
+#define CS4245_MCLK_4		4
+
+/* Signal Selection */
+#define CS4245_A_OUT_SEL_MASK	0x60
+#define CS4245_A_OUT_SEL_HIZ	0x00
+#define CS4245_A_OUT_SEL_DAC	0x20
+#define CS4245_A_OUT_SEL_PGA	0x40
+#define CS4245_LOOP		0x02
+#define CS4245_ASYNCH		0x01
+
+/* Channel B/A PGA Control */
+#define CS4245_PGA_GAIN_MASK	0x3f
+
+/* ADC Input Control */
+#define CS4245_PGA_SOFT		0x10
+#define CS4245_PGA_ZERO		0x08
+#define CS4245_SEL_MASK		0x07
+#define CS4245_SEL_MIC		0x00
+#define CS4245_SEL_INPUT_1	0x01
+#define CS4245_SEL_INPUT_2	0x02
+#define CS4245_SEL_INPUT_3	0x03
+#define CS4245_SEL_INPUT_4	0x04
+#define CS4245_SEL_INPUT_5	0x05
+#define CS4245_SEL_INPUT_6	0x06
+
+/* DAC Channel A/B Volume Control */
+#define CS4245_VOL_MASK		0xff
+
+/* DAC Control 2 */
+#define CS4245_DAC_SOFT		0x80
+#define CS4245_DAC_ZERO		0x40
+#define CS4245_INVERT_DAC	0x20
+#define CS4245_INT_ACTIVE_HIGH	0x01
+
+/* Interrupt Status/Mask/Mode */
+#define CS4245_ADC_CLK_ERR	0x08
+#define CS4245_DAC_CLK_ERR	0x04
+#define CS4245_ADC_OVFL		0x02
+#define CS4245_ADC_UNDRFL	0x01
+
+
+#define CS4245_SPI_ADDRESS	(0x9e << 16)
+#define CS4245_SPI_WRITE	(0 << 16)

+ 0 - 239
sound/pci/oxygen/hifier.c

@@ -1,239 +0,0 @@
-/*
- * C-Media CMI8788 driver for the MediaTek/TempoTec HiFier Fantasia
- *
- * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
- *
- *
- *  This driver is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License, version 2.
- *
- *  This driver is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this driver; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- */
-
-/*
- * CMI8788:
- *
- * SPI 0 -> AK4396
- */
-
-#include <linux/delay.h>
-#include <linux/pci.h>
-#include <sound/control.h>
-#include <sound/core.h>
-#include <sound/initval.h>
-#include <sound/pcm.h>
-#include <sound/tlv.h>
-#include "oxygen.h"
-#include "ak4396.h"
-
-MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
-MODULE_DESCRIPTION("TempoTec HiFier driver");
-MODULE_LICENSE("GPL v2");
-
-static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
-static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
-
-module_param_array(index, int, NULL, 0444);
-MODULE_PARM_DESC(index, "card index");
-module_param_array(id, charp, NULL, 0444);
-MODULE_PARM_DESC(id, "ID string");
-module_param_array(enable, bool, NULL, 0444);
-MODULE_PARM_DESC(enable, "enable card");
-
-static DEFINE_PCI_DEVICE_TABLE(hifier_ids) = {
-	{ OXYGEN_PCI_SUBID(0x14c3, 0x1710) },
-	{ OXYGEN_PCI_SUBID(0x14c3, 0x1711) },
-	{ OXYGEN_PCI_SUBID_BROKEN_EEPROM },
-	{ }
-};
-MODULE_DEVICE_TABLE(pci, hifier_ids);
-
-struct hifier_data {
-	u8 ak4396_regs[5];
-};
-
-static void ak4396_write(struct oxygen *chip, u8 reg, u8 value)
-{
-	struct hifier_data *data = chip->model_data;
-
-	oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER  |
-			 OXYGEN_SPI_DATA_LENGTH_2 |
-			 OXYGEN_SPI_CLOCK_160 |
-			 (0 << OXYGEN_SPI_CODEC_SHIFT) |
-			 OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
-			 AK4396_WRITE | (reg << 8) | value);
-	data->ak4396_regs[reg] = value;
-}
-
-static void ak4396_write_cached(struct oxygen *chip, u8 reg, u8 value)
-{
-	struct hifier_data *data = chip->model_data;
-
-	if (value != data->ak4396_regs[reg])
-		ak4396_write(chip, reg, value);
-}
-
-static void hifier_registers_init(struct oxygen *chip)
-{
-	struct hifier_data *data = chip->model_data;
-
-	ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
-	ak4396_write(chip, AK4396_CONTROL_2,
-		     data->ak4396_regs[AK4396_CONTROL_2]);
-	ak4396_write(chip, AK4396_CONTROL_3, AK4396_PCM);
-	ak4396_write(chip, AK4396_LCH_ATT, chip->dac_volume[0]);
-	ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
-}
-
-static void hifier_init(struct oxygen *chip)
-{
-	struct hifier_data *data = chip->model_data;
-
-	data->ak4396_regs[AK4396_CONTROL_2] =
-		AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
-	hifier_registers_init(chip);
-
-	snd_component_add(chip->card, "AK4396");
-	snd_component_add(chip->card, "CS5340");
-}
-
-static void hifier_cleanup(struct oxygen *chip)
-{
-}
-
-static void hifier_resume(struct oxygen *chip)
-{
-	hifier_registers_init(chip);
-}
-
-static void set_ak4396_params(struct oxygen *chip,
-			       struct snd_pcm_hw_params *params)
-{
-	struct hifier_data *data = chip->model_data;
-	u8 value;
-
-	value = data->ak4396_regs[AK4396_CONTROL_2] & ~AK4396_DFS_MASK;
-	if (params_rate(params) <= 54000)
-		value |= AK4396_DFS_NORMAL;
-	else if (params_rate(params) <= 108000)
-		value |= AK4396_DFS_DOUBLE;
-	else
-		value |= AK4396_DFS_QUAD;
-
-	msleep(1); /* wait for the new MCLK to become stable */
-
-	if (value != data->ak4396_regs[AK4396_CONTROL_2]) {
-		ak4396_write(chip, AK4396_CONTROL_1,
-			     AK4396_DIF_24_MSB);
-		ak4396_write(chip, AK4396_CONTROL_2, value);
-		ak4396_write(chip, AK4396_CONTROL_1,
-			     AK4396_DIF_24_MSB | AK4396_RSTN);
-	}
-}
-
-static void update_ak4396_volume(struct oxygen *chip)
-{
-	ak4396_write_cached(chip, AK4396_LCH_ATT, chip->dac_volume[0]);
-	ak4396_write_cached(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
-}
-
-static void update_ak4396_mute(struct oxygen *chip)
-{
-	struct hifier_data *data = chip->model_data;
-	u8 value;
-
-	value = data->ak4396_regs[AK4396_CONTROL_2] & ~AK4396_SMUTE;
-	if (chip->dac_mute)
-		value |= AK4396_SMUTE;
-	ak4396_write_cached(chip, AK4396_CONTROL_2, value);
-}
-
-static void set_cs5340_params(struct oxygen *chip,
-			      struct snd_pcm_hw_params *params)
-{
-}
-
-static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
-
-static const struct oxygen_model model_hifier = {
-	.shortname = "C-Media CMI8787",
-	.longname = "C-Media Oxygen HD Audio",
-	.chip = "CMI8788",
-	.init = hifier_init,
-	.cleanup = hifier_cleanup,
-	.resume = hifier_resume,
-	.get_i2s_mclk = oxygen_default_i2s_mclk,
-	.set_dac_params = set_ak4396_params,
-	.set_adc_params = set_cs5340_params,
-	.update_dac_volume = update_ak4396_volume,
-	.update_dac_mute = update_ak4396_mute,
-	.dac_tlv = ak4396_db_scale,
-	.model_data_size = sizeof(struct hifier_data),
-	.device_config = PLAYBACK_0_TO_I2S |
-			 PLAYBACK_1_TO_SPDIF |
-			 CAPTURE_0_FROM_I2S_1,
-	.dac_channels = 2,
-	.dac_volume_min = 0,
-	.dac_volume_max = 255,
-	.function_flags = OXYGEN_FUNCTION_SPI,
-	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-};
-
-static int __devinit get_hifier_model(struct oxygen *chip,
-				      const struct pci_device_id *id)
-{
-	chip->model = model_hifier;
-	return 0;
-}
-
-static int __devinit hifier_probe(struct pci_dev *pci,
-				  const struct pci_device_id *pci_id)
-{
-	static int dev;
-	int err;
-
-	if (dev >= SNDRV_CARDS)
-		return -ENODEV;
-	if (!enable[dev]) {
-		++dev;
-		return -ENOENT;
-	}
-	err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE,
-			       hifier_ids, get_hifier_model);
-	if (err >= 0)
-		++dev;
-	return err;
-}
-
-static struct pci_driver hifier_driver = {
-	.name = "CMI8787HiFier",
-	.id_table = hifier_ids,
-	.probe = hifier_probe,
-	.remove = __devexit_p(oxygen_pci_remove),
-#ifdef CONFIG_PM
-	.suspend = oxygen_pci_suspend,
-	.resume = oxygen_pci_resume,
-#endif
-};
-
-static int __init alsa_card_hifier_init(void)
-{
-	return pci_register_driver(&hifier_driver);
-}
-
-static void __exit alsa_card_hifier_exit(void)
-{
-	pci_unregister_driver(&hifier_driver);
-}
-
-module_init(alsa_card_hifier_init)
-module_exit(alsa_card_hifier_exit)

+ 318 - 38
sound/pci/oxygen/oxygen.c

@@ -20,19 +20,32 @@
 /*
 /*
  * CMI8788:
  * CMI8788:
  *
  *
- * SPI 0 -> 1st AK4396 (front)
- * SPI 1 -> 2nd AK4396 (surround)
- * SPI 2 -> 3rd AK4396 (center/LFE)
- * SPI 3 -> WM8785
- * SPI 4 -> 4th AK4396 (back)
+ *   SPI 0 -> 1st AK4396 (front)
+ *   SPI 1 -> 2nd AK4396 (surround)
+ *   SPI 2 -> 3rd AK4396 (center/LFE)
+ *   SPI 3 -> WM8785
+ *   SPI 4 -> 4th AK4396 (back)
  *
  *
- * GPIO 0 -> DFS0 of AK5385
- * GPIO 1 -> DFS1 of AK5385
- * GPIO 8 -> enable headphone amplifier on HT-Omega models
+ *   GPIO 0 -> DFS0 of AK5385
+ *   GPIO 1 -> DFS1 of AK5385
+ *
+ * X-Meridian models:
+ *   GPIO 4 -> enable extension S/PDIF input
+ *   GPIO 6 -> enable on-board S/PDIF input
+ *
+ * Claro models:
+ *   GPIO 6 -> S/PDIF from optical (0) or coaxial (1) input
+ *   GPIO 8 -> enable headphone amplifier
  *
  *
  * CM9780:
  * CM9780:
  *
  *
- * GPO 0 -> route line-in (0) or AC97 output (1) to ADC input
+ *   LINE_OUT -> input of ADC
+ *
+ *   AUX_IN <- aux
+ *   CD_IN  <- CD
+ *   MIC_IN <- mic
+ *
+ *   GPO 0 -> route line-in (0) or AC97 output (1) to ADC input
  */
  */
 
 
 #include <linux/delay.h>
 #include <linux/delay.h>
@@ -41,18 +54,22 @@
 #include <sound/ac97_codec.h>
 #include <sound/ac97_codec.h>
 #include <sound/control.h>
 #include <sound/control.h>
 #include <sound/core.h>
 #include <sound/core.h>
+#include <sound/info.h>
 #include <sound/initval.h>
 #include <sound/initval.h>
 #include <sound/pcm.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/pcm_params.h>
 #include <sound/tlv.h>
 #include <sound/tlv.h>
 #include "oxygen.h"
 #include "oxygen.h"
+#include "xonar_dg.h"
 #include "ak4396.h"
 #include "ak4396.h"
 #include "wm8785.h"
 #include "wm8785.h"
 
 
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_DESCRIPTION("C-Media CMI8788 driver");
 MODULE_DESCRIPTION("C-Media CMI8788 driver");
 MODULE_LICENSE("GPL v2");
 MODULE_LICENSE("GPL v2");
-MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8788}}");
+MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8786}"
+			",{C-Media,CMI8787}"
+			",{C-Media,CMI8788}}");
 
 
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
@@ -66,24 +83,46 @@ module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "enable card");
 MODULE_PARM_DESC(enable, "enable card");
 
 
 enum {
 enum {
-	MODEL_CMEDIA_REF,	/* C-Media's reference design */
-	MODEL_MERIDIAN,		/* AuzenTech X-Meridian */
-	MODEL_CLARO,		/* HT-Omega Claro */
-	MODEL_CLARO_HALO,	/* HT-Omega Claro halo */
+	MODEL_CMEDIA_REF,
+	MODEL_MERIDIAN,
+	MODEL_MERIDIAN_2G,
+	MODEL_CLARO,
+	MODEL_CLARO_HALO,
+	MODEL_FANTASIA,
+	MODEL_SERENADE,
+	MODEL_2CH_OUTPUT,
+	MODEL_HG2PCI,
+	MODEL_XONAR_DG,
 };
 };
 
 
 static DEFINE_PCI_DEVICE_TABLE(oxygen_ids) = {
 static DEFINE_PCI_DEVICE_TABLE(oxygen_ids) = {
+	/* C-Media's reference design */
 	{ OXYGEN_PCI_SUBID(0x10b0, 0x0216), .driver_data = MODEL_CMEDIA_REF },
 	{ OXYGEN_PCI_SUBID(0x10b0, 0x0216), .driver_data = MODEL_CMEDIA_REF },
+	{ OXYGEN_PCI_SUBID(0x10b0, 0x0217), .driver_data = MODEL_CMEDIA_REF },
 	{ OXYGEN_PCI_SUBID(0x10b0, 0x0218), .driver_data = MODEL_CMEDIA_REF },
 	{ OXYGEN_PCI_SUBID(0x10b0, 0x0218), .driver_data = MODEL_CMEDIA_REF },
 	{ OXYGEN_PCI_SUBID(0x10b0, 0x0219), .driver_data = MODEL_CMEDIA_REF },
 	{ OXYGEN_PCI_SUBID(0x10b0, 0x0219), .driver_data = MODEL_CMEDIA_REF },
 	{ OXYGEN_PCI_SUBID(0x13f6, 0x0001), .driver_data = MODEL_CMEDIA_REF },
 	{ OXYGEN_PCI_SUBID(0x13f6, 0x0001), .driver_data = MODEL_CMEDIA_REF },
 	{ OXYGEN_PCI_SUBID(0x13f6, 0x0010), .driver_data = MODEL_CMEDIA_REF },
 	{ OXYGEN_PCI_SUBID(0x13f6, 0x0010), .driver_data = MODEL_CMEDIA_REF },
 	{ OXYGEN_PCI_SUBID(0x13f6, 0x8788), .driver_data = MODEL_CMEDIA_REF },
 	{ OXYGEN_PCI_SUBID(0x13f6, 0x8788), .driver_data = MODEL_CMEDIA_REF },
-	{ OXYGEN_PCI_SUBID(0x13f6, 0xffff), .driver_data = MODEL_CMEDIA_REF },
 	{ OXYGEN_PCI_SUBID(0x147a, 0xa017), .driver_data = MODEL_CMEDIA_REF },
 	{ OXYGEN_PCI_SUBID(0x147a, 0xa017), .driver_data = MODEL_CMEDIA_REF },
 	{ OXYGEN_PCI_SUBID(0x1a58, 0x0910), .driver_data = MODEL_CMEDIA_REF },
 	{ OXYGEN_PCI_SUBID(0x1a58, 0x0910), .driver_data = MODEL_CMEDIA_REF },
+	/* Asus Xonar DG */
+	{ OXYGEN_PCI_SUBID(0x1043, 0x8467), .driver_data = MODEL_XONAR_DG },
+	/* PCI 2.0 HD Audio */
+	{ OXYGEN_PCI_SUBID(0x13f6, 0x8782), .driver_data = MODEL_2CH_OUTPUT },
+	/* Kuroutoshikou CMI8787-HG2PCI */
+	{ OXYGEN_PCI_SUBID(0x13f6, 0xffff), .driver_data = MODEL_HG2PCI },
+	/* TempoTec HiFier Fantasia */
+	{ OXYGEN_PCI_SUBID(0x14c3, 0x1710), .driver_data = MODEL_FANTASIA },
+	/* TempoTec HiFier Serenade */
+	{ OXYGEN_PCI_SUBID(0x14c3, 0x1711), .driver_data = MODEL_SERENADE },
+	/* AuzenTech X-Meridian */
 	{ OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = MODEL_MERIDIAN },
 	{ OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = MODEL_MERIDIAN },
+	/* AuzenTech X-Meridian 2G */
+	{ OXYGEN_PCI_SUBID(0x5431, 0x017a), .driver_data = MODEL_MERIDIAN_2G },
+	/* HT-Omega Claro */
 	{ OXYGEN_PCI_SUBID(0x7284, 0x9761), .driver_data = MODEL_CLARO },
 	{ OXYGEN_PCI_SUBID(0x7284, 0x9761), .driver_data = MODEL_CLARO },
+	/* HT-Omega Claro halo */
 	{ OXYGEN_PCI_SUBID(0x7284, 0x9781), .driver_data = MODEL_CLARO_HALO },
 	{ OXYGEN_PCI_SUBID(0x7284, 0x9781), .driver_data = MODEL_CLARO_HALO },
 	{ }
 	{ }
 };
 };
@@ -95,9 +134,15 @@ MODULE_DEVICE_TABLE(pci, oxygen_ids);
 #define GPIO_AK5385_DFS_DOUBLE	0x0001
 #define GPIO_AK5385_DFS_DOUBLE	0x0001
 #define GPIO_AK5385_DFS_QUAD	0x0002
 #define GPIO_AK5385_DFS_QUAD	0x0002
 
 
+#define GPIO_MERIDIAN_DIG_MASK	0x0050
+#define GPIO_MERIDIAN_DIG_EXT	0x0010
+#define GPIO_MERIDIAN_DIG_BOARD	0x0040
+
+#define GPIO_CLARO_DIG_COAX	0x0040
 #define GPIO_CLARO_HP		0x0100
 #define GPIO_CLARO_HP		0x0100
 
 
 struct generic_data {
 struct generic_data {
+	unsigned int dacs;
 	u8 ak4396_regs[4][5];
 	u8 ak4396_regs[4][5];
 	u16 wm8785_regs[3];
 	u16 wm8785_regs[3];
 };
 };
@@ -148,7 +193,7 @@ static void ak4396_registers_init(struct oxygen *chip)
 	struct generic_data *data = chip->model_data;
 	struct generic_data *data = chip->model_data;
 	unsigned int i;
 	unsigned int i;
 
 
-	for (i = 0; i < 4; ++i) {
+	for (i = 0; i < data->dacs; ++i) {
 		ak4396_write(chip, i, AK4396_CONTROL_1,
 		ak4396_write(chip, i, AK4396_CONTROL_1,
 			     AK4396_DIF_24_MSB | AK4396_RSTN);
 			     AK4396_DIF_24_MSB | AK4396_RSTN);
 		ak4396_write(chip, i, AK4396_CONTROL_2,
 		ak4396_write(chip, i, AK4396_CONTROL_2,
@@ -166,6 +211,7 @@ static void ak4396_init(struct oxygen *chip)
 {
 {
 	struct generic_data *data = chip->model_data;
 	struct generic_data *data = chip->model_data;
 
 
+	data->dacs = chip->model.dac_channels_pcm / 2;
 	data->ak4396_regs[0][AK4396_CONTROL_2] =
 	data->ak4396_regs[0][AK4396_CONTROL_2] =
 		AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
 		AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
 	ak4396_registers_init(chip);
 	ak4396_registers_init(chip);
@@ -207,6 +253,10 @@ static void generic_init(struct oxygen *chip)
 
 
 static void meridian_init(struct oxygen *chip)
 static void meridian_init(struct oxygen *chip)
 {
 {
+	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+			  GPIO_MERIDIAN_DIG_MASK);
+	oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+			      GPIO_MERIDIAN_DIG_BOARD, GPIO_MERIDIAN_DIG_MASK);
 	ak4396_init(chip);
 	ak4396_init(chip);
 	ak5385_init(chip);
 	ak5385_init(chip);
 }
 }
@@ -220,6 +270,8 @@ static void claro_enable_hp(struct oxygen *chip)
 
 
 static void claro_init(struct oxygen *chip)
 static void claro_init(struct oxygen *chip)
 {
 {
+	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CLARO_DIG_COAX);
+	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_DIG_COAX);
 	ak4396_init(chip);
 	ak4396_init(chip);
 	wm8785_init(chip);
 	wm8785_init(chip);
 	claro_enable_hp(chip);
 	claro_enable_hp(chip);
@@ -227,11 +279,24 @@ static void claro_init(struct oxygen *chip)
 
 
 static void claro_halo_init(struct oxygen *chip)
 static void claro_halo_init(struct oxygen *chip)
 {
 {
+	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CLARO_DIG_COAX);
+	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_DIG_COAX);
 	ak4396_init(chip);
 	ak4396_init(chip);
 	ak5385_init(chip);
 	ak5385_init(chip);
 	claro_enable_hp(chip);
 	claro_enable_hp(chip);
 }
 }
 
 
+static void fantasia_init(struct oxygen *chip)
+{
+	ak4396_init(chip);
+	snd_component_add(chip->card, "CS5340");
+}
+
+static void stereo_output_init(struct oxygen *chip)
+{
+	ak4396_init(chip);
+}
+
 static void generic_cleanup(struct oxygen *chip)
 static void generic_cleanup(struct oxygen *chip)
 {
 {
 }
 }
@@ -268,6 +333,11 @@ static void claro_resume(struct oxygen *chip)
 	claro_enable_hp(chip);
 	claro_enable_hp(chip);
 }
 }
 
 
+static void stereo_resume(struct oxygen *chip)
+{
+	ak4396_registers_init(chip);
+}
+
 static void set_ak4396_params(struct oxygen *chip,
 static void set_ak4396_params(struct oxygen *chip,
 			      struct snd_pcm_hw_params *params)
 			      struct snd_pcm_hw_params *params)
 {
 {
@@ -286,7 +356,7 @@ static void set_ak4396_params(struct oxygen *chip,
 	msleep(1); /* wait for the new MCLK to become stable */
 	msleep(1); /* wait for the new MCLK to become stable */
 
 
 	if (value != data->ak4396_regs[0][AK4396_CONTROL_2]) {
 	if (value != data->ak4396_regs[0][AK4396_CONTROL_2]) {
-		for (i = 0; i < 4; ++i) {
+		for (i = 0; i < data->dacs; ++i) {
 			ak4396_write(chip, i, AK4396_CONTROL_1,
 			ak4396_write(chip, i, AK4396_CONTROL_1,
 				     AK4396_DIF_24_MSB);
 				     AK4396_DIF_24_MSB);
 			ak4396_write(chip, i, AK4396_CONTROL_2, value);
 			ak4396_write(chip, i, AK4396_CONTROL_2, value);
@@ -298,9 +368,10 @@ static void set_ak4396_params(struct oxygen *chip,
 
 
 static void update_ak4396_volume(struct oxygen *chip)
 static void update_ak4396_volume(struct oxygen *chip)
 {
 {
+	struct generic_data *data = chip->model_data;
 	unsigned int i;
 	unsigned int i;
 
 
-	for (i = 0; i < 4; ++i) {
+	for (i = 0; i < data->dacs; ++i) {
 		ak4396_write_cached(chip, i, AK4396_LCH_ATT,
 		ak4396_write_cached(chip, i, AK4396_LCH_ATT,
 				    chip->dac_volume[i * 2]);
 				    chip->dac_volume[i * 2]);
 		ak4396_write_cached(chip, i, AK4396_RCH_ATT,
 		ak4396_write_cached(chip, i, AK4396_RCH_ATT,
@@ -317,7 +388,7 @@ static void update_ak4396_mute(struct oxygen *chip)
 	value = data->ak4396_regs[0][AK4396_CONTROL_2] & ~AK4396_SMUTE;
 	value = data->ak4396_regs[0][AK4396_CONTROL_2] & ~AK4396_SMUTE;
 	if (chip->dac_mute)
 	if (chip->dac_mute)
 		value |= AK4396_SMUTE;
 		value |= AK4396_SMUTE;
-	for (i = 0; i < 4; ++i)
+	for (i = 0; i < data->dacs; ++i)
 		ak4396_write_cached(chip, i, AK4396_CONTROL_2, value);
 		ak4396_write_cached(chip, i, AK4396_CONTROL_2, value);
 }
 }
 
 
@@ -356,6 +427,10 @@ static void set_ak5385_params(struct oxygen *chip,
 			      value, GPIO_AK5385_DFS_MASK);
 			      value, GPIO_AK5385_DFS_MASK);
 }
 }
 
 
+static void set_no_params(struct oxygen *chip, struct snd_pcm_hw_params *params)
+{
+}
+
 static int rolloff_info(struct snd_kcontrol *ctl,
 static int rolloff_info(struct snd_kcontrol *ctl,
 			struct snd_ctl_elem_info *info)
 			struct snd_ctl_elem_info *info)
 {
 {
@@ -363,13 +438,7 @@ static int rolloff_info(struct snd_kcontrol *ctl,
 		"Sharp Roll-off", "Slow Roll-off"
 		"Sharp Roll-off", "Slow Roll-off"
 	};
 	};
 
 
-	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-	info->count = 1;
-	info->value.enumerated.items = 2;
-	if (info->value.enumerated.item >= 2)
-		info->value.enumerated.item = 1;
-	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
-	return 0;
+	return snd_ctl_enum_info(info, 1, 2, names);
 }
 }
 
 
 static int rolloff_get(struct snd_kcontrol *ctl,
 static int rolloff_get(struct snd_kcontrol *ctl,
@@ -400,7 +469,7 @@ static int rolloff_put(struct snd_kcontrol *ctl,
 		reg &= ~AK4396_SLOW;
 		reg &= ~AK4396_SLOW;
 	changed = reg != data->ak4396_regs[0][AK4396_CONTROL_2];
 	changed = reg != data->ak4396_regs[0][AK4396_CONTROL_2];
 	if (changed) {
 	if (changed) {
-		for (i = 0; i < 4; ++i)
+		for (i = 0; i < data->dacs; ++i)
 			ak4396_write(chip, i, AK4396_CONTROL_2, reg);
 			ak4396_write(chip, i, AK4396_CONTROL_2, reg);
 	}
 	}
 	mutex_unlock(&chip->mutex);
 	mutex_unlock(&chip->mutex);
@@ -421,13 +490,7 @@ static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
 		"None", "High-pass Filter"
 		"None", "High-pass Filter"
 	};
 	};
 
 
-	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-	info->count = 1;
-	info->value.enumerated.items = 2;
-	if (info->value.enumerated.item >= 2)
-		info->value.enumerated.item = 1;
-	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
-	return 0;
+	return snd_ctl_enum_info(info, 1, 2, names);
 }
 }
 
 
 static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
 static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
@@ -466,6 +529,100 @@ static const struct snd_kcontrol_new hpf_control = {
 	.put = hpf_put,
 	.put = hpf_put,
 };
 };
 
 
+static int meridian_dig_source_info(struct snd_kcontrol *ctl,
+				    struct snd_ctl_elem_info *info)
+{
+	static const char *const names[2] = { "On-board", "Extension" };
+
+	return snd_ctl_enum_info(info, 1, 2, names);
+}
+
+static int claro_dig_source_info(struct snd_kcontrol *ctl,
+				 struct snd_ctl_elem_info *info)
+{
+	static const char *const names[2] = { "Optical", "Coaxial" };
+
+	return snd_ctl_enum_info(info, 1, 2, names);
+}
+
+static int meridian_dig_source_get(struct snd_kcontrol *ctl,
+				   struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+
+	value->value.enumerated.item[0] =
+		!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) &
+		   GPIO_MERIDIAN_DIG_EXT);
+	return 0;
+}
+
+static int claro_dig_source_get(struct snd_kcontrol *ctl,
+				struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+
+	value->value.enumerated.item[0] =
+		!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) &
+		   GPIO_CLARO_DIG_COAX);
+	return 0;
+}
+
+static int meridian_dig_source_put(struct snd_kcontrol *ctl,
+				   struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	u16 old_reg, new_reg;
+	int changed;
+
+	mutex_lock(&chip->mutex);
+	old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA);
+	new_reg = old_reg & ~GPIO_MERIDIAN_DIG_MASK;
+	if (value->value.enumerated.item[0] == 0)
+		new_reg |= GPIO_MERIDIAN_DIG_BOARD;
+	else
+		new_reg |= GPIO_MERIDIAN_DIG_EXT;
+	changed = new_reg != old_reg;
+	if (changed)
+		oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg);
+	mutex_unlock(&chip->mutex);
+	return changed;
+}
+
+static int claro_dig_source_put(struct snd_kcontrol *ctl,
+				struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	u16 old_reg, new_reg;
+	int changed;
+
+	mutex_lock(&chip->mutex);
+	old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA);
+	new_reg = old_reg & ~GPIO_CLARO_DIG_COAX;
+	if (value->value.enumerated.item[0])
+		new_reg |= GPIO_CLARO_DIG_COAX;
+	changed = new_reg != old_reg;
+	if (changed)
+		oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg);
+	mutex_unlock(&chip->mutex);
+	return changed;
+}
+
+static const struct snd_kcontrol_new meridian_dig_source_control = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "IEC958 Source Capture Enum",
+	.info = meridian_dig_source_info,
+	.get = meridian_dig_source_get,
+	.put = meridian_dig_source_put,
+};
+
+static const struct snd_kcontrol_new claro_dig_source_control = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "IEC958 Source Capture Enum",
+	.info = claro_dig_source_info,
+	.get = claro_dig_source_get,
+	.put = claro_dig_source_put,
+};
+
 static int generic_mixer_init(struct oxygen *chip)
 static int generic_mixer_init(struct oxygen *chip)
 {
 {
 	return snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip));
 	return snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip));
@@ -484,6 +641,81 @@ static int generic_wm8785_mixer_init(struct oxygen *chip)
 	return 0;
 	return 0;
 }
 }
 
 
+static int meridian_mixer_init(struct oxygen *chip)
+{
+	int err;
+
+	err = generic_mixer_init(chip);
+	if (err < 0)
+		return err;
+	err = snd_ctl_add(chip->card,
+			  snd_ctl_new1(&meridian_dig_source_control, chip));
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+static int claro_mixer_init(struct oxygen *chip)
+{
+	int err;
+
+	err = generic_wm8785_mixer_init(chip);
+	if (err < 0)
+		return err;
+	err = snd_ctl_add(chip->card,
+			  snd_ctl_new1(&claro_dig_source_control, chip));
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+static int claro_halo_mixer_init(struct oxygen *chip)
+{
+	int err;
+
+	err = generic_mixer_init(chip);
+	if (err < 0)
+		return err;
+	err = snd_ctl_add(chip->card,
+			  snd_ctl_new1(&claro_dig_source_control, chip));
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+static void dump_ak4396_registers(struct oxygen *chip,
+				  struct snd_info_buffer *buffer)
+{
+	struct generic_data *data = chip->model_data;
+	unsigned int dac, i;
+
+	for (dac = 0; dac < data->dacs; ++dac) {
+		snd_iprintf(buffer, "\nAK4396 %u:", dac + 1);
+		for (i = 0; i < 5; ++i)
+			snd_iprintf(buffer, " %02x", data->ak4396_regs[dac][i]);
+	}
+	snd_iprintf(buffer, "\n");
+}
+
+static void dump_wm8785_registers(struct oxygen *chip,
+				  struct snd_info_buffer *buffer)
+{
+	struct generic_data *data = chip->model_data;
+	unsigned int i;
+
+	snd_iprintf(buffer, "\nWM8785:");
+	for (i = 0; i < 3; ++i)
+		snd_iprintf(buffer, " %03x", data->wm8785_regs[i]);
+	snd_iprintf(buffer, "\n");
+}
+
+static void dump_oxygen_registers(struct oxygen *chip,
+				  struct snd_info_buffer *buffer)
+{
+	dump_ak4396_registers(chip, buffer);
+	dump_wm8785_registers(chip, buffer);
+}
+
 static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
 static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
 
 
 static const struct oxygen_model model_generic = {
 static const struct oxygen_model model_generic = {
@@ -494,11 +726,11 @@ static const struct oxygen_model model_generic = {
 	.mixer_init = generic_wm8785_mixer_init,
 	.mixer_init = generic_wm8785_mixer_init,
 	.cleanup = generic_cleanup,
 	.cleanup = generic_cleanup,
 	.resume = generic_resume,
 	.resume = generic_resume,
-	.get_i2s_mclk = oxygen_default_i2s_mclk,
 	.set_dac_params = set_ak4396_params,
 	.set_dac_params = set_ak4396_params,
 	.set_adc_params = set_wm8785_params,
 	.set_adc_params = set_wm8785_params,
 	.update_dac_volume = update_ak4396_volume,
 	.update_dac_volume = update_ak4396_volume,
 	.update_dac_mute = update_ak4396_mute,
 	.update_dac_mute = update_ak4396_mute,
+	.dump_registers = dump_oxygen_registers,
 	.dac_tlv = ak4396_db_scale,
 	.dac_tlv = ak4396_db_scale,
 	.model_data_size = sizeof(struct generic_data),
 	.model_data_size = sizeof(struct generic_data),
 	.device_config = PLAYBACK_0_TO_I2S |
 	.device_config = PLAYBACK_0_TO_I2S |
@@ -508,11 +740,14 @@ static const struct oxygen_model model_generic = {
 			 CAPTURE_1_FROM_SPDIF |
 			 CAPTURE_1_FROM_SPDIF |
 			 CAPTURE_2_FROM_AC97_1 |
 			 CAPTURE_2_FROM_AC97_1 |
 			 AC97_CD_INPUT,
 			 AC97_CD_INPUT,
-	.dac_channels = 8,
+	.dac_channels_pcm = 8,
+	.dac_channels_mixer = 8,
 	.dac_volume_min = 0,
 	.dac_volume_min = 0,
 	.dac_volume_max = 255,
 	.dac_volume_max = 255,
 	.function_flags = OXYGEN_FUNCTION_SPI |
 	.function_flags = OXYGEN_FUNCTION_SPI |
 			  OXYGEN_FUNCTION_ENABLE_SPI_4_5,
 			  OXYGEN_FUNCTION_ENABLE_SPI_4_5,
+	.dac_mclks = OXYGEN_MCLKS(256, 128, 128),
+	.adc_mclks = OXYGEN_MCLKS(256, 256, 128),
 	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 };
 };
@@ -520,42 +755,87 @@ static const struct oxygen_model model_generic = {
 static int __devinit get_oxygen_model(struct oxygen *chip,
 static int __devinit get_oxygen_model(struct oxygen *chip,
 				      const struct pci_device_id *id)
 				      const struct pci_device_id *id)
 {
 {
+	static const char *const names[] = {
+		[MODEL_MERIDIAN]	= "AuzenTech X-Meridian",
+		[MODEL_MERIDIAN_2G]	= "AuzenTech X-Meridian 2G",
+		[MODEL_CLARO]		= "HT-Omega Claro",
+		[MODEL_CLARO_HALO]	= "HT-Omega Claro halo",
+		[MODEL_FANTASIA]	= "TempoTec HiFier Fantasia",
+		[MODEL_SERENADE]	= "TempoTec HiFier Serenade",
+		[MODEL_HG2PCI]		= "CMI8787-HG2PCI",
+	};
+
 	chip->model = model_generic;
 	chip->model = model_generic;
 	switch (id->driver_data) {
 	switch (id->driver_data) {
 	case MODEL_MERIDIAN:
 	case MODEL_MERIDIAN:
+	case MODEL_MERIDIAN_2G:
 		chip->model.init = meridian_init;
 		chip->model.init = meridian_init;
-		chip->model.mixer_init = generic_mixer_init;
+		chip->model.mixer_init = meridian_mixer_init;
 		chip->model.resume = meridian_resume;
 		chip->model.resume = meridian_resume;
 		chip->model.set_adc_params = set_ak5385_params;
 		chip->model.set_adc_params = set_ak5385_params;
+		chip->model.dump_registers = dump_ak4396_registers;
 		chip->model.device_config = PLAYBACK_0_TO_I2S |
 		chip->model.device_config = PLAYBACK_0_TO_I2S |
 					    PLAYBACK_1_TO_SPDIF |
 					    PLAYBACK_1_TO_SPDIF |
 					    CAPTURE_0_FROM_I2S_2 |
 					    CAPTURE_0_FROM_I2S_2 |
 					    CAPTURE_1_FROM_SPDIF;
 					    CAPTURE_1_FROM_SPDIF;
+		if (id->driver_data == MODEL_MERIDIAN)
+			chip->model.device_config |= AC97_CD_INPUT;
 		break;
 		break;
 	case MODEL_CLARO:
 	case MODEL_CLARO:
 		chip->model.init = claro_init;
 		chip->model.init = claro_init;
+		chip->model.mixer_init = claro_mixer_init;
 		chip->model.cleanup = claro_cleanup;
 		chip->model.cleanup = claro_cleanup;
 		chip->model.suspend = claro_suspend;
 		chip->model.suspend = claro_suspend;
 		chip->model.resume = claro_resume;
 		chip->model.resume = claro_resume;
 		break;
 		break;
 	case MODEL_CLARO_HALO:
 	case MODEL_CLARO_HALO:
 		chip->model.init = claro_halo_init;
 		chip->model.init = claro_halo_init;
-		chip->model.mixer_init = generic_mixer_init;
+		chip->model.mixer_init = claro_halo_mixer_init;
 		chip->model.cleanup = claro_cleanup;
 		chip->model.cleanup = claro_cleanup;
 		chip->model.suspend = claro_suspend;
 		chip->model.suspend = claro_suspend;
 		chip->model.resume = claro_resume;
 		chip->model.resume = claro_resume;
 		chip->model.set_adc_params = set_ak5385_params;
 		chip->model.set_adc_params = set_ak5385_params;
+		chip->model.dump_registers = dump_ak4396_registers;
 		chip->model.device_config = PLAYBACK_0_TO_I2S |
 		chip->model.device_config = PLAYBACK_0_TO_I2S |
 					    PLAYBACK_1_TO_SPDIF |
 					    PLAYBACK_1_TO_SPDIF |
 					    CAPTURE_0_FROM_I2S_2 |
 					    CAPTURE_0_FROM_I2S_2 |
 					    CAPTURE_1_FROM_SPDIF;
 					    CAPTURE_1_FROM_SPDIF;
 		break;
 		break;
+	case MODEL_FANTASIA:
+	case MODEL_SERENADE:
+	case MODEL_2CH_OUTPUT:
+	case MODEL_HG2PCI:
+		chip->model.shortname = "C-Media CMI8787";
+		chip->model.chip = "CMI8787";
+		if (id->driver_data == MODEL_FANTASIA)
+			chip->model.init = fantasia_init;
+		else
+			chip->model.init = stereo_output_init;
+		chip->model.resume = stereo_resume;
+		chip->model.mixer_init = generic_mixer_init;
+		chip->model.set_adc_params = set_no_params;
+		chip->model.dump_registers = dump_ak4396_registers;
+		chip->model.device_config = PLAYBACK_0_TO_I2S |
+					    PLAYBACK_1_TO_SPDIF;
+		if (id->driver_data == MODEL_FANTASIA) {
+			chip->model.device_config |= CAPTURE_0_FROM_I2S_1;
+			chip->model.adc_mclks = OXYGEN_MCLKS(256, 128, 128);
+		}
+		chip->model.dac_channels_pcm = 2;
+		chip->model.dac_channels_mixer = 2;
+		break;
+	case MODEL_XONAR_DG:
+		chip->model = model_xonar_dg;
+		break;
 	}
 	}
 	if (id->driver_data == MODEL_MERIDIAN ||
 	if (id->driver_data == MODEL_MERIDIAN ||
+	    id->driver_data == MODEL_MERIDIAN_2G ||
 	    id->driver_data == MODEL_CLARO_HALO) {
 	    id->driver_data == MODEL_CLARO_HALO) {
 		chip->model.misc_flags = OXYGEN_MISC_MIDI;
 		chip->model.misc_flags = OXYGEN_MISC_MIDI;
 		chip->model.device_config |= MIDI_OUTPUT | MIDI_INPUT;
 		chip->model.device_config |= MIDI_OUTPUT | MIDI_INPUT;
 	}
 	}
+	if (id->driver_data < ARRAY_SIZE(names) && names[id->driver_data])
+		chip->model.shortname = names[id->driver_data];
 	return 0;
 	return 0;
 }
 }
 
 

+ 12 - 7
sound/pci/oxygen/oxygen.h

@@ -16,6 +16,10 @@
 #define PCM_AC97	5
 #define PCM_AC97	5
 #define PCM_COUNT	6
 #define PCM_COUNT	6
 
 
+#define OXYGEN_MCLKS(f_single, f_double, f_quad) ((MCLK_##f_single << 0) | \
+						  (MCLK_##f_double << 2) | \
+						  (MCLK_##f_quad   << 4))
+
 #define OXYGEN_IO_SIZE	0x100
 #define OXYGEN_IO_SIZE	0x100
 
 
 #define OXYGEN_EEPROM_ID	0x434d	/* "CM" */
 #define OXYGEN_EEPROM_ID	0x434d	/* "CM" */
@@ -35,6 +39,7 @@
 #define MIDI_OUTPUT		0x0800
 #define MIDI_OUTPUT		0x0800
 #define MIDI_INPUT		0x1000
 #define MIDI_INPUT		0x1000
 #define AC97_CD_INPUT		0x2000
 #define AC97_CD_INPUT		0x2000
+#define AC97_FMIC_SWITCH	0x4000
 
 
 enum {
 enum {
 	CONTROL_SPDIF_PCM,
 	CONTROL_SPDIF_PCM,
@@ -65,6 +70,7 @@ struct snd_pcm_hardware;
 struct snd_pcm_hw_params;
 struct snd_pcm_hw_params;
 struct snd_kcontrol_new;
 struct snd_kcontrol_new;
 struct snd_rawmidi;
 struct snd_rawmidi;
+struct snd_info_buffer;
 struct oxygen;
 struct oxygen;
 
 
 struct oxygen_model {
 struct oxygen_model {
@@ -79,8 +85,6 @@ struct oxygen_model {
 	void (*resume)(struct oxygen *chip);
 	void (*resume)(struct oxygen *chip);
 	void (*pcm_hardware_filter)(unsigned int channel,
 	void (*pcm_hardware_filter)(unsigned int channel,
 				    struct snd_pcm_hardware *hardware);
 				    struct snd_pcm_hardware *hardware);
-	unsigned int (*get_i2s_mclk)(struct oxygen *chip, unsigned int channel,
-				     struct snd_pcm_hw_params *hw_params);
 	void (*set_dac_params)(struct oxygen *chip,
 	void (*set_dac_params)(struct oxygen *chip,
 			       struct snd_pcm_hw_params *params);
 			       struct snd_pcm_hw_params *params);
 	void (*set_adc_params)(struct oxygen *chip,
 	void (*set_adc_params)(struct oxygen *chip,
@@ -92,15 +96,19 @@ struct oxygen_model {
 	void (*uart_input)(struct oxygen *chip);
 	void (*uart_input)(struct oxygen *chip);
 	void (*ac97_switch)(struct oxygen *chip,
 	void (*ac97_switch)(struct oxygen *chip,
 			    unsigned int reg, unsigned int mute);
 			    unsigned int reg, unsigned int mute);
+	void (*dump_registers)(struct oxygen *chip,
+			       struct snd_info_buffer *buffer);
 	const unsigned int *dac_tlv;
 	const unsigned int *dac_tlv;
-	unsigned long private_data;
 	size_t model_data_size;
 	size_t model_data_size;
 	unsigned int device_config;
 	unsigned int device_config;
-	u8 dac_channels;
+	u8 dac_channels_pcm;
+	u8 dac_channels_mixer;
 	u8 dac_volume_min;
 	u8 dac_volume_min;
 	u8 dac_volume_max;
 	u8 dac_volume_max;
 	u8 misc_flags;
 	u8 misc_flags;
 	u8 function_flags;
 	u8 function_flags;
+	u8 dac_mclks;
+	u8 adc_mclks;
 	u16 dac_i2s_format;
 	u16 dac_i2s_format;
 	u16 adc_i2s_format;
 	u16 adc_i2s_format;
 };
 };
@@ -121,7 +129,6 @@ struct oxygen {
 	u8 pcm_running;
 	u8 pcm_running;
 	u8 dac_routing;
 	u8 dac_routing;
 	u8 spdif_playback_enable;
 	u8 spdif_playback_enable;
-	u8 revision;
 	u8 has_ac97_0;
 	u8 has_ac97_0;
 	u8 has_ac97_1;
 	u8 has_ac97_1;
 	u32 spdif_bits;
 	u32 spdif_bits;
@@ -167,8 +174,6 @@ void oxygen_update_spdif_source(struct oxygen *chip);
 /* oxygen_pcm.c */
 /* oxygen_pcm.c */
 
 
 int oxygen_pcm_init(struct oxygen *chip);
 int oxygen_pcm_init(struct oxygen *chip);
-unsigned int oxygen_default_i2s_mclk(struct oxygen *chip, unsigned int channel,
-				     struct snd_pcm_hw_params *hw_params);
 
 
 /* oxygen_io.c */
 /* oxygen_io.c */
 
 

+ 2 - 2
sound/pci/oxygen/oxygen_io.c

@@ -197,11 +197,11 @@ void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data)
 {
 {
 	unsigned int count;
 	unsigned int count;
 
 
-	/* should not need more than 7.68 us (24 * 320 ns) */
+	/* should not need more than 30.72 us (24 * 1.28 us) */
 	count = 10;
 	count = 10;
 	while ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & OXYGEN_SPI_BUSY)
 	while ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & OXYGEN_SPI_BUSY)
 	       && count > 0) {
 	       && count > 0) {
-		udelay(1);
+		udelay(4);
 		--count;
 		--count;
 	}
 	}
 
 

+ 44 - 27
sound/pci/oxygen/oxygen_lib.c

@@ -202,7 +202,13 @@ static void oxygen_proc_read(struct snd_info_entry *entry,
 	struct oxygen *chip = entry->private_data;
 	struct oxygen *chip = entry->private_data;
 	int i, j;
 	int i, j;
 
 
-	snd_iprintf(buffer, "CMI8788\n\n");
+	switch (oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_PACKAGE_ID_MASK) {
+	case OXYGEN_PACKAGE_ID_8786: i = '6'; break;
+	case OXYGEN_PACKAGE_ID_8787: i = '7'; break;
+	case OXYGEN_PACKAGE_ID_8788: i = '8'; break;
+	default:                     i = '?'; break;
+	}
+	snd_iprintf(buffer, "CMI878%c:\n", i);
 	for (i = 0; i < OXYGEN_IO_SIZE; i += 0x10) {
 	for (i = 0; i < OXYGEN_IO_SIZE; i += 0x10) {
 		snd_iprintf(buffer, "%02x:", i);
 		snd_iprintf(buffer, "%02x:", i);
 		for (j = 0; j < 0x10; ++j)
 		for (j = 0; j < 0x10; ++j)
@@ -212,7 +218,7 @@ static void oxygen_proc_read(struct snd_info_entry *entry,
 	if (mutex_lock_interruptible(&chip->mutex) < 0)
 	if (mutex_lock_interruptible(&chip->mutex) < 0)
 		return;
 		return;
 	if (chip->has_ac97_0) {
 	if (chip->has_ac97_0) {
-		snd_iprintf(buffer, "\nAC97\n");
+		snd_iprintf(buffer, "\nAC97:\n");
 		for (i = 0; i < 0x80; i += 0x10) {
 		for (i = 0; i < 0x80; i += 0x10) {
 			snd_iprintf(buffer, "%02x:", i);
 			snd_iprintf(buffer, "%02x:", i);
 			for (j = 0; j < 0x10; j += 2)
 			for (j = 0; j < 0x10; j += 2)
@@ -222,7 +228,7 @@ static void oxygen_proc_read(struct snd_info_entry *entry,
 		}
 		}
 	}
 	}
 	if (chip->has_ac97_1) {
 	if (chip->has_ac97_1) {
-		snd_iprintf(buffer, "\nAC97 2\n");
+		snd_iprintf(buffer, "\nAC97 2:\n");
 		for (i = 0; i < 0x80; i += 0x10) {
 		for (i = 0; i < 0x80; i += 0x10) {
 			snd_iprintf(buffer, "%02x:", i);
 			snd_iprintf(buffer, "%02x:", i);
 			for (j = 0; j < 0x10; j += 2)
 			for (j = 0; j < 0x10; j += 2)
@@ -232,13 +238,15 @@ static void oxygen_proc_read(struct snd_info_entry *entry,
 		}
 		}
 	}
 	}
 	mutex_unlock(&chip->mutex);
 	mutex_unlock(&chip->mutex);
+	if (chip->model.dump_registers)
+		chip->model.dump_registers(chip, buffer);
 }
 }
 
 
 static void oxygen_proc_init(struct oxygen *chip)
 static void oxygen_proc_init(struct oxygen *chip)
 {
 {
 	struct snd_info_entry *entry;
 	struct snd_info_entry *entry;
 
 
-	if (!snd_card_proc_new(chip->card, "cmi8788", &entry))
+	if (!snd_card_proc_new(chip->card, "oxygen", &entry))
 		snd_info_set_text_ops(entry, chip, oxygen_proc_read);
 		snd_info_set_text_ops(entry, chip, oxygen_proc_read);
 }
 }
 #else
 #else
@@ -262,7 +270,7 @@ oxygen_search_pci_id(struct oxygen *chip, const struct pci_device_id ids[])
 	 */
 	 */
 	subdevice = oxygen_read_eeprom(chip, 2);
 	subdevice = oxygen_read_eeprom(chip, 2);
 	/* use default ID if EEPROM is missing */
 	/* use default ID if EEPROM is missing */
-	if (subdevice == 0xffff)
+	if (subdevice == 0xffff && oxygen_read_eeprom(chip, 1) == 0xffff)
 		subdevice = 0x8788;
 		subdevice = 0x8788;
 	/*
 	/*
 	 * We use only the subsystem device ID for searching because it is
 	 * We use only the subsystem device ID for searching because it is
@@ -364,12 +372,7 @@ static void oxygen_init(struct oxygen *chip)
 		(IEC958_AES1_CON_PCM_CODER << OXYGEN_SPDIF_CATEGORY_SHIFT);
 		(IEC958_AES1_CON_PCM_CODER << OXYGEN_SPDIF_CATEGORY_SHIFT);
 	chip->spdif_pcm_bits = chip->spdif_bits;
 	chip->spdif_pcm_bits = chip->spdif_bits;
 
 
-	if (oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_REVISION_2)
-		chip->revision = 2;
-	else
-		chip->revision = 1;
-
-	if (chip->revision == 1)
+	if (!(oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_REVISION_2))
 		oxygen_set_bits8(chip, OXYGEN_MISC,
 		oxygen_set_bits8(chip, OXYGEN_MISC,
 				 OXYGEN_MISC_PCI_MEM_W_1_CLOCK);
 				 OXYGEN_MISC_PCI_MEM_W_1_CLOCK);
 
 
@@ -406,28 +409,40 @@ static void oxygen_init(struct oxygen *chip)
 		      (OXYGEN_FORMAT_16 << OXYGEN_MULTICH_FORMAT_SHIFT));
 		      (OXYGEN_FORMAT_16 << OXYGEN_MULTICH_FORMAT_SHIFT));
 	oxygen_write8(chip, OXYGEN_REC_CHANNELS, OXYGEN_REC_CHANNELS_2_2_2);
 	oxygen_write8(chip, OXYGEN_REC_CHANNELS, OXYGEN_REC_CHANNELS_2_2_2);
 	oxygen_write16(chip, OXYGEN_I2S_MULTICH_FORMAT,
 	oxygen_write16(chip, OXYGEN_I2S_MULTICH_FORMAT,
-		       OXYGEN_RATE_48000 | chip->model.dac_i2s_format |
-		       OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
-		       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
+		       OXYGEN_RATE_48000 |
+		       chip->model.dac_i2s_format |
+		       OXYGEN_I2S_MCLK(chip->model.dac_mclks) |
+		       OXYGEN_I2S_BITS_16 |
+		       OXYGEN_I2S_MASTER |
+		       OXYGEN_I2S_BCLK_64);
 	if (chip->model.device_config & CAPTURE_0_FROM_I2S_1)
 	if (chip->model.device_config & CAPTURE_0_FROM_I2S_1)
 		oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
 		oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
-			       OXYGEN_RATE_48000 | chip->model.adc_i2s_format |
-			       OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
-			       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
+			       OXYGEN_RATE_48000 |
+			       chip->model.adc_i2s_format |
+			       OXYGEN_I2S_MCLK(chip->model.adc_mclks) |
+			       OXYGEN_I2S_BITS_16 |
+			       OXYGEN_I2S_MASTER |
+			       OXYGEN_I2S_BCLK_64);
 	else
 	else
 		oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
 		oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
-			       OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK);
+			       OXYGEN_I2S_MASTER |
+			       OXYGEN_I2S_MUTE_MCLK);
 	if (chip->model.device_config & (CAPTURE_0_FROM_I2S_2 |
 	if (chip->model.device_config & (CAPTURE_0_FROM_I2S_2 |
 					 CAPTURE_2_FROM_I2S_2))
 					 CAPTURE_2_FROM_I2S_2))
 		oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
 		oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
-			       OXYGEN_RATE_48000 | chip->model.adc_i2s_format |
-			       OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
-			       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
+			       OXYGEN_RATE_48000 |
+			       chip->model.adc_i2s_format |
+			       OXYGEN_I2S_MCLK(chip->model.adc_mclks) |
+			       OXYGEN_I2S_BITS_16 |
+			       OXYGEN_I2S_MASTER |
+			       OXYGEN_I2S_BCLK_64);
 	else
 	else
 		oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
 		oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
-			       OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK);
+			       OXYGEN_I2S_MASTER |
+			       OXYGEN_I2S_MUTE_MCLK);
 	oxygen_write16(chip, OXYGEN_I2S_C_FORMAT,
 	oxygen_write16(chip, OXYGEN_I2S_C_FORMAT,
-		       OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK);
+		       OXYGEN_I2S_MASTER |
+		       OXYGEN_I2S_MUTE_MCLK);
 	oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
 	oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
 			    OXYGEN_SPDIF_OUT_ENABLE |
 			    OXYGEN_SPDIF_OUT_ENABLE |
 			    OXYGEN_SPDIF_LOOPBACK);
 			    OXYGEN_SPDIF_LOOPBACK);
@@ -557,7 +572,8 @@ static void oxygen_card_free(struct snd_card *card)
 	oxygen_shutdown(chip);
 	oxygen_shutdown(chip);
 	if (chip->irq >= 0)
 	if (chip->irq >= 0)
 		free_irq(chip->irq, chip);
 		free_irq(chip->irq, chip);
-	flush_scheduled_work();
+	flush_work_sync(&chip->spdif_input_bits_work);
+	flush_work_sync(&chip->gpio_work);
 	chip->model.cleanup(chip);
 	chip->model.cleanup(chip);
 	kfree(chip->model_data);
 	kfree(chip->model_data);
 	mutex_destroy(&chip->mutex);
 	mutex_destroy(&chip->mutex);
@@ -648,8 +664,8 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
 
 
 	strcpy(card->driver, chip->model.chip);
 	strcpy(card->driver, chip->model.chip);
 	strcpy(card->shortname, chip->model.shortname);
 	strcpy(card->shortname, chip->model.shortname);
-	sprintf(card->longname, "%s (rev %u) at %#lx, irq %i",
-		chip->model.longname, chip->revision, chip->addr, chip->irq);
+	sprintf(card->longname, "%s at %#lx, irq %i",
+		chip->model.longname, chip->addr, chip->irq);
 	strcpy(card->mixername, chip->model.chip);
 	strcpy(card->mixername, chip->model.chip);
 	snd_component_add(card, chip->model.chip);
 	snd_component_add(card, chip->model.chip);
 
 
@@ -733,7 +749,8 @@ int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state)
 	spin_unlock_irq(&chip->reg_lock);
 	spin_unlock_irq(&chip->reg_lock);
 
 
 	synchronize_irq(chip->irq);
 	synchronize_irq(chip->irq);
-	flush_scheduled_work();
+	flush_work_sync(&chip->spdif_input_bits_work);
+	flush_work_sync(&chip->gpio_work);
 	chip->interrupt_mask = saved_interrupt_mask;
 	chip->interrupt_mask = saved_interrupt_mask;
 
 
 	pci_disable_device(pci);
 	pci_disable_device(pci);

+ 87 - 23
sound/pci/oxygen/oxygen_mixer.c

@@ -31,7 +31,7 @@ static int dac_volume_info(struct snd_kcontrol *ctl,
 	struct oxygen *chip = ctl->private_data;
 	struct oxygen *chip = ctl->private_data;
 
 
 	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-	info->count = chip->model.dac_channels;
+	info->count = chip->model.dac_channels_mixer;
 	info->value.integer.min = chip->model.dac_volume_min;
 	info->value.integer.min = chip->model.dac_volume_min;
 	info->value.integer.max = chip->model.dac_volume_max;
 	info->value.integer.max = chip->model.dac_volume_max;
 	return 0;
 	return 0;
@@ -44,7 +44,7 @@ static int dac_volume_get(struct snd_kcontrol *ctl,
 	unsigned int i;
 	unsigned int i;
 
 
 	mutex_lock(&chip->mutex);
 	mutex_lock(&chip->mutex);
-	for (i = 0; i < chip->model.dac_channels; ++i)
+	for (i = 0; i < chip->model.dac_channels_mixer; ++i)
 		value->value.integer.value[i] = chip->dac_volume[i];
 		value->value.integer.value[i] = chip->dac_volume[i];
 	mutex_unlock(&chip->mutex);
 	mutex_unlock(&chip->mutex);
 	return 0;
 	return 0;
@@ -59,7 +59,7 @@ static int dac_volume_put(struct snd_kcontrol *ctl,
 
 
 	changed = 0;
 	changed = 0;
 	mutex_lock(&chip->mutex);
 	mutex_lock(&chip->mutex);
-	for (i = 0; i < chip->model.dac_channels; ++i)
+	for (i = 0; i < chip->model.dac_channels_mixer; ++i)
 		if (value->value.integer.value[i] != chip->dac_volume[i]) {
 		if (value->value.integer.value[i] != chip->dac_volume[i]) {
 			chip->dac_volume[i] = value->value.integer.value[i];
 			chip->dac_volume[i] = value->value.integer.value[i];
 			changed = 1;
 			changed = 1;
@@ -97,6 +97,16 @@ static int dac_mute_put(struct snd_kcontrol *ctl,
 	return changed;
 	return changed;
 }
 }
 
 
+static unsigned int upmix_item_count(struct oxygen *chip)
+{
+	if (chip->model.dac_channels_pcm < 8)
+		return 2;
+	else if (chip->model.update_center_lfe_mix)
+		return 5;
+	else
+		return 3;
+}
+
 static int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
 static int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
 {
 {
 	static const char *const names[5] = {
 	static const char *const names[5] = {
@@ -107,15 +117,9 @@ static int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
 		"Front+Surround+Center/LFE+Back",
 		"Front+Surround+Center/LFE+Back",
 	};
 	};
 	struct oxygen *chip = ctl->private_data;
 	struct oxygen *chip = ctl->private_data;
-	unsigned int count = chip->model.update_center_lfe_mix ? 5 : 3;
+	unsigned int count = upmix_item_count(chip);
 
 
-	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-	info->count = 1;
-	info->value.enumerated.items = count;
-	if (info->value.enumerated.item >= count)
-		info->value.enumerated.item = count - 1;
-	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
-	return 0;
+	return snd_ctl_enum_info(info, 1, count, names);
 }
 }
 
 
 static int upmix_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
 static int upmix_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
@@ -188,7 +192,7 @@ void oxygen_update_dac_routing(struct oxygen *chip)
 static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
 static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
 {
 {
 	struct oxygen *chip = ctl->private_data;
 	struct oxygen *chip = ctl->private_data;
-	unsigned int count = chip->model.update_center_lfe_mix ? 5 : 3;
+	unsigned int count = upmix_item_count(chip);
 	int changed;
 	int changed;
 
 
 	if (value->value.enumerated.item[0] >= count)
 	if (value->value.enumerated.item[0] >= count)
@@ -430,30 +434,31 @@ static int spdif_input_default_get(struct snd_kcontrol *ctl,
 	return 0;
 	return 0;
 }
 }
 
 
-static int spdif_loopback_get(struct snd_kcontrol *ctl,
-			      struct snd_ctl_elem_value *value)
+static int spdif_bit_switch_get(struct snd_kcontrol *ctl,
+				struct snd_ctl_elem_value *value)
 {
 {
 	struct oxygen *chip = ctl->private_data;
 	struct oxygen *chip = ctl->private_data;
+	u32 bit = ctl->private_value;
 
 
 	value->value.integer.value[0] =
 	value->value.integer.value[0] =
-		!!(oxygen_read32(chip, OXYGEN_SPDIF_CONTROL)
-		   & OXYGEN_SPDIF_LOOPBACK);
+		!!(oxygen_read32(chip, OXYGEN_SPDIF_CONTROL) & bit);
 	return 0;
 	return 0;
 }
 }
 
 
-static int spdif_loopback_put(struct snd_kcontrol *ctl,
-			      struct snd_ctl_elem_value *value)
+static int spdif_bit_switch_put(struct snd_kcontrol *ctl,
+				struct snd_ctl_elem_value *value)
 {
 {
 	struct oxygen *chip = ctl->private_data;
 	struct oxygen *chip = ctl->private_data;
+	u32 bit = ctl->private_value;
 	u32 oldreg, newreg;
 	u32 oldreg, newreg;
 	int changed;
 	int changed;
 
 
 	spin_lock_irq(&chip->reg_lock);
 	spin_lock_irq(&chip->reg_lock);
 	oldreg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
 	oldreg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
 	if (value->value.integer.value[0])
 	if (value->value.integer.value[0])
-		newreg = oldreg | OXYGEN_SPDIF_LOOPBACK;
+		newreg = oldreg | bit;
 	else
 	else
-		newreg = oldreg & ~OXYGEN_SPDIF_LOOPBACK;
+		newreg = oldreg & ~bit;
 	changed = newreg != oldreg;
 	changed = newreg != oldreg;
 	if (changed)
 	if (changed)
 		oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, newreg);
 		oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, newreg);
@@ -644,6 +649,46 @@ static int ac97_volume_put(struct snd_kcontrol *ctl,
 	return change;
 	return change;
 }
 }
 
 
+static int mic_fmic_source_info(struct snd_kcontrol *ctl,
+			   struct snd_ctl_elem_info *info)
+{
+	static const char *const names[] = { "Mic Jack", "Front Panel" };
+
+	return snd_ctl_enum_info(info, 1, 2, names);
+}
+
+static int mic_fmic_source_get(struct snd_kcontrol *ctl,
+			       struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+
+	mutex_lock(&chip->mutex);
+	value->value.enumerated.item[0] =
+		!!(oxygen_read_ac97(chip, 0, CM9780_JACK) & CM9780_FMIC2MIC);
+	mutex_unlock(&chip->mutex);
+	return 0;
+}
+
+static int mic_fmic_source_put(struct snd_kcontrol *ctl,
+			       struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	u16 oldreg, newreg;
+	int change;
+
+	mutex_lock(&chip->mutex);
+	oldreg = oxygen_read_ac97(chip, 0, CM9780_JACK);
+	if (value->value.enumerated.item[0])
+		newreg = oldreg | CM9780_FMIC2MIC;
+	else
+		newreg = oldreg & ~CM9780_FMIC2MIC;
+	change = newreg != oldreg;
+	if (change)
+		oxygen_write_ac97(chip, 0, CM9780_JACK, newreg);
+	mutex_unlock(&chip->mutex);
+	return change;
+}
+
 static int ac97_fp_rec_volume_info(struct snd_kcontrol *ctl,
 static int ac97_fp_rec_volume_info(struct snd_kcontrol *ctl,
 				   struct snd_ctl_elem_info *info)
 				   struct snd_ctl_elem_info *info)
 {
 {
@@ -791,8 +836,17 @@ static const struct snd_kcontrol_new spdif_input_controls[] = {
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.name = SNDRV_CTL_NAME_IEC958("Loopback ", NONE, SWITCH),
 		.name = SNDRV_CTL_NAME_IEC958("Loopback ", NONE, SWITCH),
 		.info = snd_ctl_boolean_mono_info,
 		.info = snd_ctl_boolean_mono_info,
-		.get = spdif_loopback_get,
-		.put = spdif_loopback_put,
+		.get = spdif_bit_switch_get,
+		.put = spdif_bit_switch_put,
+		.private_value = OXYGEN_SPDIF_LOOPBACK,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = SNDRV_CTL_NAME_IEC958("Validity Check ",CAPTURE,SWITCH),
+		.info = snd_ctl_boolean_mono_info,
+		.get = spdif_bit_switch_get,
+		.put = spdif_bit_switch_put,
+		.private_value = OXYGEN_SPDIF_SPDVALID,
 	},
 	},
 };
 };
 
 
@@ -908,6 +962,13 @@ static const struct snd_kcontrol_new ac97_controls[] = {
 	AC97_VOLUME("Mic Capture Volume", 0, AC97_MIC, 0),
 	AC97_VOLUME("Mic Capture Volume", 0, AC97_MIC, 0),
 	AC97_SWITCH("Mic Capture Switch", 0, AC97_MIC, 15, 1),
 	AC97_SWITCH("Mic Capture Switch", 0, AC97_MIC, 15, 1),
 	AC97_SWITCH("Mic Boost (+20dB)", 0, AC97_MIC, 6, 0),
 	AC97_SWITCH("Mic Boost (+20dB)", 0, AC97_MIC, 6, 0),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Mic Source Capture Enum",
+		.info = mic_fmic_source_info,
+		.get = mic_fmic_source_get,
+		.put = mic_fmic_source_put,
+	},
 	AC97_SWITCH("Line Capture Switch", 0, AC97_LINE, 15, 1),
 	AC97_SWITCH("Line Capture Switch", 0, AC97_LINE, 15, 1),
 	AC97_VOLUME("CD Capture Volume", 0, AC97_CD, 1),
 	AC97_VOLUME("CD Capture Volume", 0, AC97_CD, 1),
 	AC97_SWITCH("CD Capture Switch", 0, AC97_CD, 15, 1),
 	AC97_SWITCH("CD Capture Switch", 0, AC97_CD, 15, 1),
@@ -970,7 +1031,10 @@ static int add_controls(struct oxygen *chip,
 				continue;
 				continue;
 		}
 		}
 		if (!strcmp(template.name, "Stereo Upmixing") &&
 		if (!strcmp(template.name, "Stereo Upmixing") &&
-		    chip->model.dac_channels == 2)
+		    chip->model.dac_channels_pcm == 2)
+			continue;
+		if (!strcmp(template.name, "Mic Source Capture Enum") &&
+		    !(chip->model.device_config & AC97_FMIC_SWITCH))
 			continue;
 			continue;
 		if (!strncmp(template.name, "CD Capture ", 11) &&
 		if (!strncmp(template.name, "CD Capture ", 11) &&
 		    !(chip->model.device_config & AC97_CD_INPUT))
 		    !(chip->model.device_config & AC97_CD_INPUT))

+ 34 - 21
sound/pci/oxygen/oxygen_pcm.c

@@ -39,7 +39,8 @@ static const struct snd_pcm_hardware oxygen_stereo_hardware = {
 		SNDRV_PCM_INFO_MMAP_VALID |
 		SNDRV_PCM_INFO_MMAP_VALID |
 		SNDRV_PCM_INFO_INTERLEAVED |
 		SNDRV_PCM_INFO_INTERLEAVED |
 		SNDRV_PCM_INFO_PAUSE |
 		SNDRV_PCM_INFO_PAUSE |
-		SNDRV_PCM_INFO_SYNC_START,
+		SNDRV_PCM_INFO_SYNC_START |
+		SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
 	.formats = SNDRV_PCM_FMTBIT_S16_LE |
 	.formats = SNDRV_PCM_FMTBIT_S16_LE |
 		   SNDRV_PCM_FMTBIT_S32_LE,
 		   SNDRV_PCM_FMTBIT_S32_LE,
 	.rates = SNDRV_PCM_RATE_32000 |
 	.rates = SNDRV_PCM_RATE_32000 |
@@ -65,7 +66,8 @@ static const struct snd_pcm_hardware oxygen_multichannel_hardware = {
 		SNDRV_PCM_INFO_MMAP_VALID |
 		SNDRV_PCM_INFO_MMAP_VALID |
 		SNDRV_PCM_INFO_INTERLEAVED |
 		SNDRV_PCM_INFO_INTERLEAVED |
 		SNDRV_PCM_INFO_PAUSE |
 		SNDRV_PCM_INFO_PAUSE |
-		SNDRV_PCM_INFO_SYNC_START,
+		SNDRV_PCM_INFO_SYNC_START |
+		SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
 	.formats = SNDRV_PCM_FMTBIT_S16_LE |
 	.formats = SNDRV_PCM_FMTBIT_S16_LE |
 		   SNDRV_PCM_FMTBIT_S32_LE,
 		   SNDRV_PCM_FMTBIT_S32_LE,
 	.rates = SNDRV_PCM_RATE_32000 |
 	.rates = SNDRV_PCM_RATE_32000 |
@@ -91,7 +93,8 @@ static const struct snd_pcm_hardware oxygen_ac97_hardware = {
 		SNDRV_PCM_INFO_MMAP_VALID |
 		SNDRV_PCM_INFO_MMAP_VALID |
 		SNDRV_PCM_INFO_INTERLEAVED |
 		SNDRV_PCM_INFO_INTERLEAVED |
 		SNDRV_PCM_INFO_PAUSE |
 		SNDRV_PCM_INFO_PAUSE |
-		SNDRV_PCM_INFO_SYNC_START,
+		SNDRV_PCM_INFO_SYNC_START |
+		SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
 	.rates = SNDRV_PCM_RATE_48000,
 	.rates = SNDRV_PCM_RATE_48000,
 	.rate_min = 48000,
 	.rate_min = 48000,
@@ -140,7 +143,7 @@ static int oxygen_open(struct snd_pcm_substream *substream,
 		runtime->hw.rate_min = 44100;
 		runtime->hw.rate_min = 44100;
 		break;
 		break;
 	case PCM_MULTICH:
 	case PCM_MULTICH:
-		runtime->hw.channels_max = chip->model.dac_channels;
+		runtime->hw.channels_max = chip->model.dac_channels_pcm;
 		break;
 		break;
 	}
 	}
 	if (chip->model.pcm_hardware_filter)
 	if (chip->model.pcm_hardware_filter)
@@ -271,17 +274,6 @@ static unsigned int oxygen_rate(struct snd_pcm_hw_params *hw_params)
 	}
 	}
 }
 }
 
 
-unsigned int oxygen_default_i2s_mclk(struct oxygen *chip,
-				     unsigned int channel,
-				     struct snd_pcm_hw_params *hw_params)
-{
-	if (params_rate(hw_params) <= 96000)
-		return OXYGEN_I2S_MCLK_256;
-	else
-		return OXYGEN_I2S_MCLK_128;
-}
-EXPORT_SYMBOL(oxygen_default_i2s_mclk);
-
 static unsigned int oxygen_i2s_bits(struct snd_pcm_hw_params *hw_params)
 static unsigned int oxygen_i2s_bits(struct snd_pcm_hw_params *hw_params)
 {
 {
 	if (params_format(hw_params) == SNDRV_PCM_FORMAT_S32_LE)
 	if (params_format(hw_params) == SNDRV_PCM_FORMAT_S32_LE)
@@ -341,6 +333,26 @@ static int oxygen_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 	return 0;
 }
 }
 
 
+static u16 get_mclk(struct oxygen *chip, unsigned int channel,
+		    struct snd_pcm_hw_params *params)
+{
+	unsigned int mclks, shift;
+
+	if (channel == PCM_MULTICH)
+		mclks = chip->model.dac_mclks;
+	else
+		mclks = chip->model.adc_mclks;
+
+	if (params_rate(params) <= 48000)
+		shift = 0;
+	else if (params_rate(params) <= 96000)
+		shift = 2;
+	else
+		shift = 4;
+
+	return OXYGEN_I2S_MCLK(mclks >> shift);
+}
+
 static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,
 static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,
 				  struct snd_pcm_hw_params *hw_params)
 				  struct snd_pcm_hw_params *hw_params)
 {
 {
@@ -357,8 +369,8 @@ static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,
 			     OXYGEN_REC_FORMAT_A_MASK);
 			     OXYGEN_REC_FORMAT_A_MASK);
 	oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT,
 	oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT,
 			      oxygen_rate(hw_params) |
 			      oxygen_rate(hw_params) |
-			      chip->model.get_i2s_mclk(chip, PCM_A, hw_params) |
 			      chip->model.adc_i2s_format |
 			      chip->model.adc_i2s_format |
+			      get_mclk(chip, PCM_A, hw_params) |
 			      oxygen_i2s_bits(hw_params),
 			      oxygen_i2s_bits(hw_params),
 			      OXYGEN_I2S_RATE_MASK |
 			      OXYGEN_I2S_RATE_MASK |
 			      OXYGEN_I2S_FORMAT_MASK |
 			      OXYGEN_I2S_FORMAT_MASK |
@@ -393,9 +405,8 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream,
 	if (!is_ac97)
 	if (!is_ac97)
 		oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT,
 		oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT,
 				      oxygen_rate(hw_params) |
 				      oxygen_rate(hw_params) |
-				      chip->model.get_i2s_mclk(chip, PCM_B,
-							       hw_params) |
 				      chip->model.adc_i2s_format |
 				      chip->model.adc_i2s_format |
+				      get_mclk(chip, PCM_B, hw_params) |
 				      oxygen_i2s_bits(hw_params),
 				      oxygen_i2s_bits(hw_params),
 				      OXYGEN_I2S_RATE_MASK |
 				      OXYGEN_I2S_RATE_MASK |
 				      OXYGEN_I2S_FORMAT_MASK |
 				      OXYGEN_I2S_FORMAT_MASK |
@@ -476,8 +487,7 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,
 	oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
 	oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
 			      oxygen_rate(hw_params) |
 			      oxygen_rate(hw_params) |
 			      chip->model.dac_i2s_format |
 			      chip->model.dac_i2s_format |
-			      chip->model.get_i2s_mclk(chip, PCM_MULTICH,
-						       hw_params) |
+			      get_mclk(chip, PCM_MULTICH, hw_params) |
 			      oxygen_i2s_bits(hw_params),
 			      oxygen_i2s_bits(hw_params),
 			      OXYGEN_I2S_RATE_MASK |
 			      OXYGEN_I2S_RATE_MASK |
 			      OXYGEN_I2S_FORMAT_MASK |
 			      OXYGEN_I2S_FORMAT_MASK |
@@ -530,7 +540,10 @@ static int oxygen_prepare(struct snd_pcm_substream *substream)
 	oxygen_set_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
 	oxygen_set_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
 	oxygen_clear_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
 	oxygen_clear_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
 
 
-	chip->interrupt_mask |= channel_mask;
+	if (substream->runtime->no_period_wakeup)
+		chip->interrupt_mask &= ~channel_mask;
+	else
+		chip->interrupt_mask |= channel_mask;
 	oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
 	oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
 	spin_unlock_irq(&chip->reg_lock);
 	spin_unlock_irq(&chip->reg_lock);
 	return 0;
 	return 0;

+ 9 - 7
sound/pci/oxygen/oxygen_regs.h

@@ -139,9 +139,11 @@
 #define  OXYGEN_I2S_FORMAT_I2S		0x0000
 #define  OXYGEN_I2S_FORMAT_I2S		0x0000
 #define  OXYGEN_I2S_FORMAT_LJUST	0x0008
 #define  OXYGEN_I2S_FORMAT_LJUST	0x0008
 #define  OXYGEN_I2S_MCLK_MASK		0x0030	/* MCLK/LRCK */
 #define  OXYGEN_I2S_MCLK_MASK		0x0030	/* MCLK/LRCK */
-#define  OXYGEN_I2S_MCLK_128		0x0000
-#define  OXYGEN_I2S_MCLK_256		0x0010
-#define  OXYGEN_I2S_MCLK_512		0x0020
+#define  OXYGEN_I2S_MCLK_SHIFT		4
+#define  MCLK_128			0
+#define  MCLK_256			1
+#define  MCLK_512			2
+#define  OXYGEN_I2S_MCLK(f)		(((f) & 3) << OXYGEN_I2S_MCLK_SHIFT)
 #define  OXYGEN_I2S_BITS_MASK		0x00c0
 #define  OXYGEN_I2S_BITS_MASK		0x00c0
 #define  OXYGEN_I2S_BITS_16		0x0000
 #define  OXYGEN_I2S_BITS_16		0x0000
 #define  OXYGEN_I2S_BITS_20		0x0040
 #define  OXYGEN_I2S_BITS_20		0x0040
@@ -238,11 +240,11 @@
 #define  OXYGEN_SPI_DATA_LENGTH_MASK	0x02
 #define  OXYGEN_SPI_DATA_LENGTH_MASK	0x02
 #define  OXYGEN_SPI_DATA_LENGTH_2	0x00
 #define  OXYGEN_SPI_DATA_LENGTH_2	0x00
 #define  OXYGEN_SPI_DATA_LENGTH_3	0x02
 #define  OXYGEN_SPI_DATA_LENGTH_3	0x02
-#define  OXYGEN_SPI_CLOCK_MASK		0xc0
+#define  OXYGEN_SPI_CLOCK_MASK		0x0c
 #define  OXYGEN_SPI_CLOCK_160		0x00	/* ns */
 #define  OXYGEN_SPI_CLOCK_160		0x00	/* ns */
-#define  OXYGEN_SPI_CLOCK_320		0x40
-#define  OXYGEN_SPI_CLOCK_640		0x80
-#define  OXYGEN_SPI_CLOCK_1280		0xc0
+#define  OXYGEN_SPI_CLOCK_320		0x04
+#define  OXYGEN_SPI_CLOCK_640		0x08
+#define  OXYGEN_SPI_CLOCK_1280		0x0c
 #define  OXYGEN_SPI_CODEC_MASK		0x70	/* 0..5 */
 #define  OXYGEN_SPI_CODEC_MASK		0x70	/* 0..5 */
 #define  OXYGEN_SPI_CODEC_SHIFT		4
 #define  OXYGEN_SPI_CODEC_SHIFT		4
 #define  OXYGEN_SPI_CEN_MASK		0x80
 #define  OXYGEN_SPI_CEN_MASK		0x80

+ 2 - 0
sound/pci/oxygen/xonar.h

@@ -24,6 +24,8 @@ void xonar_init_ext_power(struct oxygen *chip);
 void xonar_init_cs53x1(struct oxygen *chip);
 void xonar_init_cs53x1(struct oxygen *chip);
 void xonar_set_cs53x1_params(struct oxygen *chip,
 void xonar_set_cs53x1_params(struct oxygen *chip,
 			     struct snd_pcm_hw_params *params);
 			     struct snd_pcm_hw_params *params);
+
+#define XONAR_GPIO_BIT_INVERT	(1 << 16)
 int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl,
 int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl,
 			      struct snd_ctl_elem_value *value);
 			      struct snd_ctl_elem_value *value);
 int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,
 int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,

+ 53 - 31
sound/pci/oxygen/xonar_cs43xx.c

@@ -22,29 +22,28 @@
  *
  *
  * CMI8788:
  * CMI8788:
  *
  *
- * I²C <-> CS4398 (front)
- *     <-> CS4362A (surround, center/LFE, back)
+ *   I²C <-> CS4398 (addr 1001111) (front)
+ *       <-> CS4362A (addr 0011000) (surround, center/LFE, back)
  *
  *
- * GPI 0 <- external power present (DX only)
+ *   GPI 0 <- external power present (DX only)
  *
  *
- * GPIO 0 -> enable output to speakers
- * GPIO 1 -> enable front panel I/O
- * GPIO 2 -> M0 of CS5361
- * GPIO 3 -> M1 of CS5361
- * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
+ *   GPIO 0 -> enable output to speakers
+ *   GPIO 1 -> route output to front panel
+ *   GPIO 2 -> M0 of CS5361
+ *   GPIO 3 -> M1 of CS5361
+ *   GPIO 6 -> ?
+ *   GPIO 7 -> ?
+ *   GPIO 8 -> route input jack to line-in (0) or mic-in (1)
  *
  *
- * CS4398:
- *
- * AD0 <- 1
- * AD1 <- 1
+ * CM9780:
  *
  *
- * CS4362A:
+ *   LINE_OUT -> input of ADC
  *
  *
- * AD0 <- 0
+ *   AUX_IN  <- aux
+ *   MIC_IN  <- mic
+ *   FMIC_IN <- front mic
  *
  *
- * CM9780:
- *
- * GPO 0 -> route line-in (0) or AC97 output (1) to CS5361 input
+ *   GPO 0 -> route line-in (0) or AC97 output (1) to CS5361 input
  */
  */
 
 
 #include <linux/pci.h>
 #include <linux/pci.h>
@@ -63,6 +62,7 @@
 #define GPI_EXT_POWER		0x01
 #define GPI_EXT_POWER		0x01
 #define GPIO_D1_OUTPUT_ENABLE	0x0001
 #define GPIO_D1_OUTPUT_ENABLE	0x0001
 #define GPIO_D1_FRONT_PANEL	0x0002
 #define GPIO_D1_FRONT_PANEL	0x0002
+#define GPIO_D1_MAGIC		0x00c0
 #define GPIO_D1_INPUT_ROUTE	0x0100
 #define GPIO_D1_INPUT_ROUTE	0x0100
 
 
 #define I2C_DEVICE_CS4398	0x9e	/* 10011, AD1=1, AD0=1, /W=0 */
 #define I2C_DEVICE_CS4398	0x9e	/* 10011, AD1=1, AD0=1, /W=0 */
@@ -169,12 +169,12 @@ static void xonar_d1_init(struct oxygen *chip)
 	cs43xx_registers_init(chip);
 	cs43xx_registers_init(chip);
 
 
 	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
 	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
-			  GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE);
+			  GPIO_D1_FRONT_PANEL |
+			  GPIO_D1_MAGIC |
+			  GPIO_D1_INPUT_ROUTE);
 	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
 	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
 			    GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE);
 			    GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE);
 
 
-	oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC);
-
 	xonar_init_cs53x1(chip);
 	xonar_init_cs53x1(chip);
 	xonar_enable_output(chip);
 	xonar_enable_output(chip);
 
 
@@ -284,7 +284,7 @@ static void update_cs43xx_center_lfe_mix(struct oxygen *chip, bool mixed)
 
 
 static const struct snd_kcontrol_new front_panel_switch = {
 static const struct snd_kcontrol_new front_panel_switch = {
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name = "Front Panel Switch",
+	.name = "Front Panel Playback Switch",
 	.info = snd_ctl_boolean_mono_info,
 	.info = snd_ctl_boolean_mono_info,
 	.get = xonar_gpio_bit_switch_get,
 	.get = xonar_gpio_bit_switch_get,
 	.put = xonar_gpio_bit_switch_put,
 	.put = xonar_gpio_bit_switch_put,
@@ -298,13 +298,7 @@ static int rolloff_info(struct snd_kcontrol *ctl,
 		"Fast Roll-off", "Slow Roll-off"
 		"Fast Roll-off", "Slow Roll-off"
 	};
 	};
 
 
-	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-	info->count = 1;
-	info->value.enumerated.items = 2;
-	if (info->value.enumerated.item >= 2)
-		info->value.enumerated.item = 1;
-	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
-	return 0;
+	return snd_ctl_enum_info(info, 1, 2, names);
 }
 }
 
 
 static int rolloff_get(struct snd_kcontrol *ctl,
 static int rolloff_get(struct snd_kcontrol *ctl,
@@ -380,6 +374,30 @@ static int xonar_d1_mixer_init(struct oxygen *chip)
 	return 0;
 	return 0;
 }
 }
 
 
+static void dump_cs4362a_registers(struct xonar_cs43xx *data,
+				   struct snd_info_buffer *buffer)
+{
+	unsigned int i;
+
+	snd_iprintf(buffer, "\nCS4362A:");
+	for (i = 1; i <= 14; ++i)
+		snd_iprintf(buffer, " %02x", data->cs4362a_regs[i]);
+	snd_iprintf(buffer, "\n");
+}
+
+static void dump_d1_registers(struct oxygen *chip,
+			      struct snd_info_buffer *buffer)
+{
+	struct xonar_cs43xx *data = chip->model_data;
+	unsigned int i;
+
+	snd_iprintf(buffer, "\nCS4398: 7?");
+	for (i = 2; i <= 8; ++i)
+		snd_iprintf(buffer, " %02x", data->cs4398_regs[i]);
+	snd_iprintf(buffer, "\n");
+	dump_cs4362a_registers(data, buffer);
+}
+
 static const struct oxygen_model model_xonar_d1 = {
 static const struct oxygen_model model_xonar_d1 = {
 	.longname = "Asus Virtuoso 100",
 	.longname = "Asus Virtuoso 100",
 	.chip = "AV200",
 	.chip = "AV200",
@@ -388,22 +406,26 @@ static const struct oxygen_model model_xonar_d1 = {
 	.cleanup = xonar_d1_cleanup,
 	.cleanup = xonar_d1_cleanup,
 	.suspend = xonar_d1_suspend,
 	.suspend = xonar_d1_suspend,
 	.resume = xonar_d1_resume,
 	.resume = xonar_d1_resume,
-	.get_i2s_mclk = oxygen_default_i2s_mclk,
 	.set_dac_params = set_cs43xx_params,
 	.set_dac_params = set_cs43xx_params,
 	.set_adc_params = xonar_set_cs53x1_params,
 	.set_adc_params = xonar_set_cs53x1_params,
 	.update_dac_volume = update_cs43xx_volume,
 	.update_dac_volume = update_cs43xx_volume,
 	.update_dac_mute = update_cs43xx_mute,
 	.update_dac_mute = update_cs43xx_mute,
 	.update_center_lfe_mix = update_cs43xx_center_lfe_mix,
 	.update_center_lfe_mix = update_cs43xx_center_lfe_mix,
 	.ac97_switch = xonar_d1_line_mic_ac97_switch,
 	.ac97_switch = xonar_d1_line_mic_ac97_switch,
+	.dump_registers = dump_d1_registers,
 	.dac_tlv = cs4362a_db_scale,
 	.dac_tlv = cs4362a_db_scale,
 	.model_data_size = sizeof(struct xonar_cs43xx),
 	.model_data_size = sizeof(struct xonar_cs43xx),
 	.device_config = PLAYBACK_0_TO_I2S |
 	.device_config = PLAYBACK_0_TO_I2S |
 			 PLAYBACK_1_TO_SPDIF |
 			 PLAYBACK_1_TO_SPDIF |
-			 CAPTURE_0_FROM_I2S_2,
-	.dac_channels = 8,
+			 CAPTURE_0_FROM_I2S_2 |
+			 AC97_FMIC_SWITCH,
+	.dac_channels_pcm = 8,
+	.dac_channels_mixer = 8,
 	.dac_volume_min = 127 - 60,
 	.dac_volume_min = 127 - 60,
 	.dac_volume_max = 127,
 	.dac_volume_max = 127,
 	.function_flags = OXYGEN_FUNCTION_2WIRE,
 	.function_flags = OXYGEN_FUNCTION_2WIRE,
+	.dac_mclks = OXYGEN_MCLKS(256, 128, 128),
+	.adc_mclks = OXYGEN_MCLKS(256, 128, 128),
 	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 };
 };

+ 572 - 0
sound/pci/oxygen/xonar_dg.c

@@ -0,0 +1,572 @@
+/*
+ * card driver for the Xonar DG
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ *
+ *
+ *  This driver is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License, version 2.
+ *
+ *  This driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this driver; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Xonar DG
+ * --------
+ *
+ * CMI8788:
+ *
+ *   SPI 0 -> CS4245
+ *
+ *   GPIO 3 <- ?
+ *   GPIO 4 <- headphone detect
+ *   GPIO 5 -> route input jack to line-in (0) or mic-in (1)
+ *   GPIO 6 -> route input jack to line-in (0) or mic-in (1)
+ *   GPIO 7 -> enable rear headphone amp
+ *   GPIO 8 -> enable output to speakers
+ *
+ * CS4245:
+ *
+ *   input 1 <- aux
+ *   input 2 <- front mic
+ *   input 4 <- line/mic
+ *   aux out -> front panel headphones
+ */
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+#include "oxygen.h"
+#include "xonar_dg.h"
+#include "cs4245.h"
+
+#define GPIO_MAGIC		0x0008
+#define GPIO_HP_DETECT		0x0010
+#define GPIO_INPUT_ROUTE	0x0060
+#define GPIO_HP_REAR		0x0080
+#define GPIO_OUTPUT_ENABLE	0x0100
+
+struct dg {
+	unsigned int output_sel;
+	s8 input_vol[4][2];
+	unsigned int input_sel;
+	u8 hp_vol_att;
+	u8 cs4245_regs[0x11];
+};
+
+static void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value)
+{
+	struct dg *data = chip->model_data;
+
+	oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
+			 OXYGEN_SPI_DATA_LENGTH_3 |
+			 OXYGEN_SPI_CLOCK_1280 |
+			 (0 << OXYGEN_SPI_CODEC_SHIFT) |
+			 OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
+			 CS4245_SPI_ADDRESS |
+			 CS4245_SPI_WRITE |
+			 (value << 8) | reg);
+	data->cs4245_regs[reg] = value;
+}
+
+static void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value)
+{
+	struct dg *data = chip->model_data;
+
+	if (value != data->cs4245_regs[reg])
+		cs4245_write(chip, reg, value);
+}
+
+static void cs4245_registers_init(struct oxygen *chip)
+{
+	struct dg *data = chip->model_data;
+
+	cs4245_write(chip, CS4245_POWER_CTRL, CS4245_PDN);
+	cs4245_write(chip, CS4245_DAC_CTRL_1,
+		     data->cs4245_regs[CS4245_DAC_CTRL_1]);
+	cs4245_write(chip, CS4245_ADC_CTRL,
+		     data->cs4245_regs[CS4245_ADC_CTRL]);
+	cs4245_write(chip, CS4245_SIGNAL_SEL,
+		     data->cs4245_regs[CS4245_SIGNAL_SEL]);
+	cs4245_write(chip, CS4245_PGA_B_CTRL,
+		     data->cs4245_regs[CS4245_PGA_B_CTRL]);
+	cs4245_write(chip, CS4245_PGA_A_CTRL,
+		     data->cs4245_regs[CS4245_PGA_A_CTRL]);
+	cs4245_write(chip, CS4245_ANALOG_IN,
+		     data->cs4245_regs[CS4245_ANALOG_IN]);
+	cs4245_write(chip, CS4245_DAC_A_CTRL,
+		     data->cs4245_regs[CS4245_DAC_A_CTRL]);
+	cs4245_write(chip, CS4245_DAC_B_CTRL,
+		     data->cs4245_regs[CS4245_DAC_B_CTRL]);
+	cs4245_write(chip, CS4245_DAC_CTRL_2,
+		     CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC);
+	cs4245_write(chip, CS4245_INT_MASK, 0);
+	cs4245_write(chip, CS4245_POWER_CTRL, 0);
+}
+
+static void cs4245_init(struct oxygen *chip)
+{
+	struct dg *data = chip->model_data;
+
+	data->cs4245_regs[CS4245_DAC_CTRL_1] =
+		CS4245_DAC_FM_SINGLE | CS4245_DAC_DIF_LJUST;
+	data->cs4245_regs[CS4245_ADC_CTRL] =
+		CS4245_ADC_FM_SINGLE | CS4245_ADC_DIF_LJUST;
+	data->cs4245_regs[CS4245_SIGNAL_SEL] =
+		CS4245_A_OUT_SEL_HIZ | CS4245_ASYNCH;
+	data->cs4245_regs[CS4245_PGA_B_CTRL] = 0;
+	data->cs4245_regs[CS4245_PGA_A_CTRL] = 0;
+	data->cs4245_regs[CS4245_ANALOG_IN] =
+		CS4245_PGA_SOFT | CS4245_PGA_ZERO | CS4245_SEL_INPUT_4;
+	data->cs4245_regs[CS4245_DAC_A_CTRL] = 0;
+	data->cs4245_regs[CS4245_DAC_B_CTRL] = 0;
+	cs4245_registers_init(chip);
+	snd_component_add(chip->card, "CS4245");
+}
+
+static void dg_output_enable(struct oxygen *chip)
+{
+	msleep(2500);
+	oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
+}
+
+static void dg_init(struct oxygen *chip)
+{
+	struct dg *data = chip->model_data;
+
+	data->output_sel = 0;
+	data->input_sel = 3;
+	data->hp_vol_att = 2 * 16;
+
+	cs4245_init(chip);
+
+	oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
+			    GPIO_MAGIC | GPIO_HP_DETECT);
+	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+			  GPIO_INPUT_ROUTE | GPIO_HP_REAR | GPIO_OUTPUT_ENABLE);
+	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
+			    GPIO_INPUT_ROUTE | GPIO_HP_REAR);
+	dg_output_enable(chip);
+}
+
+static void dg_cleanup(struct oxygen *chip)
+{
+	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
+}
+
+static void dg_suspend(struct oxygen *chip)
+{
+	dg_cleanup(chip);
+}
+
+static void dg_resume(struct oxygen *chip)
+{
+	cs4245_registers_init(chip);
+	dg_output_enable(chip);
+}
+
+static void set_cs4245_dac_params(struct oxygen *chip,
+				  struct snd_pcm_hw_params *params)
+{
+	struct dg *data = chip->model_data;
+	u8 value;
+
+	value = data->cs4245_regs[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK;
+	if (params_rate(params) <= 50000)
+		value |= CS4245_DAC_FM_SINGLE;
+	else if (params_rate(params) <= 100000)
+		value |= CS4245_DAC_FM_DOUBLE;
+	else
+		value |= CS4245_DAC_FM_QUAD;
+	cs4245_write_cached(chip, CS4245_DAC_CTRL_1, value);
+}
+
+static void set_cs4245_adc_params(struct oxygen *chip,
+				  struct snd_pcm_hw_params *params)
+{
+	struct dg *data = chip->model_data;
+	u8 value;
+
+	value = data->cs4245_regs[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK;
+	if (params_rate(params) <= 50000)
+		value |= CS4245_ADC_FM_SINGLE;
+	else if (params_rate(params) <= 100000)
+		value |= CS4245_ADC_FM_DOUBLE;
+	else
+		value |= CS4245_ADC_FM_QUAD;
+	cs4245_write_cached(chip, CS4245_ADC_CTRL, value);
+}
+
+static int output_switch_info(struct snd_kcontrol *ctl,
+			      struct snd_ctl_elem_info *info)
+{
+	static const char *const names[3] = {
+		"Speakers", "Headphones", "FP Headphones"
+	};
+
+	return snd_ctl_enum_info(info, 1, 3, names);
+}
+
+static int output_switch_get(struct snd_kcontrol *ctl,
+			     struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	struct dg *data = chip->model_data;
+
+	mutex_lock(&chip->mutex);
+	value->value.enumerated.item[0] = data->output_sel;
+	mutex_unlock(&chip->mutex);
+	return 0;
+}
+
+static int output_switch_put(struct snd_kcontrol *ctl,
+			     struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	struct dg *data = chip->model_data;
+	u8 reg;
+	int changed;
+
+	if (value->value.enumerated.item[0] > 2)
+		return -EINVAL;
+
+	mutex_lock(&chip->mutex);
+	changed = value->value.enumerated.item[0] != data->output_sel;
+	if (changed) {
+		data->output_sel = value->value.enumerated.item[0];
+
+		reg = data->cs4245_regs[CS4245_SIGNAL_SEL] &
+						~CS4245_A_OUT_SEL_MASK;
+		reg |= data->output_sel == 2 ?
+				CS4245_A_OUT_SEL_DAC : CS4245_A_OUT_SEL_HIZ;
+		cs4245_write_cached(chip, CS4245_SIGNAL_SEL, reg);
+
+		cs4245_write_cached(chip, CS4245_DAC_A_CTRL,
+				    data->output_sel ? data->hp_vol_att : 0);
+		cs4245_write_cached(chip, CS4245_DAC_B_CTRL,
+				    data->output_sel ? data->hp_vol_att : 0);
+
+		oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+				      data->output_sel == 1 ? GPIO_HP_REAR : 0,
+				      GPIO_HP_REAR);
+	}
+	mutex_unlock(&chip->mutex);
+	return changed;
+}
+
+static int hp_volume_offset_info(struct snd_kcontrol *ctl,
+				 struct snd_ctl_elem_info *info)
+{
+	static const char *const names[3] = {
+		"< 64 ohms", "64-150 ohms", "150-300 ohms"
+	};
+
+	return snd_ctl_enum_info(info, 1, 3, names);
+}
+
+static int hp_volume_offset_get(struct snd_kcontrol *ctl,
+				struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	struct dg *data = chip->model_data;
+
+	mutex_lock(&chip->mutex);
+	if (data->hp_vol_att > 2 * 7)
+		value->value.enumerated.item[0] = 0;
+	else if (data->hp_vol_att > 0)
+		value->value.enumerated.item[0] = 1;
+	else
+		value->value.enumerated.item[0] = 2;
+	mutex_unlock(&chip->mutex);
+	return 0;
+}
+
+static int hp_volume_offset_put(struct snd_kcontrol *ctl,
+				struct snd_ctl_elem_value *value)
+{
+	static const s8 atts[3] = { 2 * 16, 2 * 7, 0 };
+	struct oxygen *chip = ctl->private_data;
+	struct dg *data = chip->model_data;
+	s8 att;
+	int changed;
+
+	if (value->value.enumerated.item[0] > 2)
+		return -EINVAL;
+	att = atts[value->value.enumerated.item[0]];
+	mutex_lock(&chip->mutex);
+	changed = att != data->hp_vol_att;
+	if (changed) {
+		data->hp_vol_att = att;
+		if (data->output_sel) {
+			cs4245_write_cached(chip, CS4245_DAC_A_CTRL, att);
+			cs4245_write_cached(chip, CS4245_DAC_B_CTRL, att);
+		}
+	}
+	mutex_unlock(&chip->mutex);
+	return changed;
+}
+
+static int input_vol_info(struct snd_kcontrol *ctl,
+			  struct snd_ctl_elem_info *info)
+{
+	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	info->count = 2;
+	info->value.integer.min = 2 * -12;
+	info->value.integer.max = 2 * 12;
+	return 0;
+}
+
+static int input_vol_get(struct snd_kcontrol *ctl,
+			 struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	struct dg *data = chip->model_data;
+	unsigned int idx = ctl->private_value;
+
+	mutex_lock(&chip->mutex);
+	value->value.integer.value[0] = data->input_vol[idx][0];
+	value->value.integer.value[1] = data->input_vol[idx][1];
+	mutex_unlock(&chip->mutex);
+	return 0;
+}
+
+static int input_vol_put(struct snd_kcontrol *ctl,
+			 struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	struct dg *data = chip->model_data;
+	unsigned int idx = ctl->private_value;
+	int changed = 0;
+
+	if (value->value.integer.value[0] < 2 * -12 ||
+	    value->value.integer.value[0] > 2 * 12 ||
+	    value->value.integer.value[1] < 2 * -12 ||
+	    value->value.integer.value[1] > 2 * 12)
+		return -EINVAL;
+	mutex_lock(&chip->mutex);
+	changed = data->input_vol[idx][0] != value->value.integer.value[0] ||
+		  data->input_vol[idx][1] != value->value.integer.value[1];
+	if (changed) {
+		data->input_vol[idx][0] = value->value.integer.value[0];
+		data->input_vol[idx][1] = value->value.integer.value[1];
+		if (idx == data->input_sel) {
+			cs4245_write_cached(chip, CS4245_PGA_A_CTRL,
+					    data->input_vol[idx][0]);
+			cs4245_write_cached(chip, CS4245_PGA_B_CTRL,
+					    data->input_vol[idx][1]);
+		}
+	}
+	mutex_unlock(&chip->mutex);
+	return changed;
+}
+
+static DECLARE_TLV_DB_SCALE(cs4245_pga_db_scale, -1200, 50, 0);
+
+static int input_sel_info(struct snd_kcontrol *ctl,
+			  struct snd_ctl_elem_info *info)
+{
+	static const char *const names[4] = {
+		"Mic", "Aux", "Front Mic", "Line"
+	};
+
+	return snd_ctl_enum_info(info, 1, 4, names);
+}
+
+static int input_sel_get(struct snd_kcontrol *ctl,
+			 struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	struct dg *data = chip->model_data;
+
+	mutex_lock(&chip->mutex);
+	value->value.enumerated.item[0] = data->input_sel;
+	mutex_unlock(&chip->mutex);
+	return 0;
+}
+
+static int input_sel_put(struct snd_kcontrol *ctl,
+			 struct snd_ctl_elem_value *value)
+{
+	static const u8 sel_values[4] = {
+		CS4245_SEL_MIC,
+		CS4245_SEL_INPUT_1,
+		CS4245_SEL_INPUT_2,
+		CS4245_SEL_INPUT_4
+	};
+	struct oxygen *chip = ctl->private_data;
+	struct dg *data = chip->model_data;
+	int changed;
+
+	if (value->value.enumerated.item[0] > 3)
+		return -EINVAL;
+
+	mutex_lock(&chip->mutex);
+	changed = value->value.enumerated.item[0] != data->input_sel;
+	if (changed) {
+		data->input_sel = value->value.enumerated.item[0];
+
+		cs4245_write(chip, CS4245_ANALOG_IN,
+			     (data->cs4245_regs[CS4245_ANALOG_IN] &
+							~CS4245_SEL_MASK) |
+			     sel_values[data->input_sel]);
+
+		cs4245_write_cached(chip, CS4245_PGA_A_CTRL,
+				    data->input_vol[data->input_sel][0]);
+		cs4245_write_cached(chip, CS4245_PGA_B_CTRL,
+				    data->input_vol[data->input_sel][1]);
+
+		oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+				      data->input_sel ? 0 : GPIO_INPUT_ROUTE,
+				      GPIO_INPUT_ROUTE);
+	}
+	mutex_unlock(&chip->mutex);
+	return changed;
+}
+
+static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
+{
+	static const char *const names[2] = { "Active", "Frozen" };
+
+	return snd_ctl_enum_info(info, 1, 2, names);
+}
+
+static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	struct dg *data = chip->model_data;
+
+	value->value.enumerated.item[0] =
+		!!(data->cs4245_regs[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE);
+	return 0;
+}
+
+static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	struct dg *data = chip->model_data;
+	u8 reg;
+	int changed;
+
+	mutex_lock(&chip->mutex);
+	reg = data->cs4245_regs[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE;
+	if (value->value.enumerated.item[0])
+		reg |= CS4245_HPF_FREEZE;
+	changed = reg != data->cs4245_regs[CS4245_ADC_CTRL];
+	if (changed)
+		cs4245_write(chip, CS4245_ADC_CTRL, reg);
+	mutex_unlock(&chip->mutex);
+	return changed;
+}
+
+#define INPUT_VOLUME(xname, index) { \
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+	.name = xname, \
+	.info = input_vol_info, \
+	.get = input_vol_get, \
+	.put = input_vol_put, \
+	.tlv = { .p = cs4245_pga_db_scale }, \
+	.private_value = index, \
+}
+static const struct snd_kcontrol_new dg_controls[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Analog Output Playback Enum",
+		.info = output_switch_info,
+		.get = output_switch_get,
+		.put = output_switch_put,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Headphones Impedance Playback Enum",
+		.info = hp_volume_offset_info,
+		.get = hp_volume_offset_get,
+		.put = hp_volume_offset_put,
+	},
+	INPUT_VOLUME("Mic Capture Volume", 0),
+	INPUT_VOLUME("Aux Capture Volume", 1),
+	INPUT_VOLUME("Front Mic Capture Volume", 2),
+	INPUT_VOLUME("Line Capture Volume", 3),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Capture Source",
+		.info = input_sel_info,
+		.get = input_sel_get,
+		.put = input_sel_put,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "ADC High-pass Filter Capture Enum",
+		.info = hpf_info,
+		.get = hpf_get,
+		.put = hpf_put,
+	},
+};
+
+static int dg_control_filter(struct snd_kcontrol_new *template)
+{
+	if (!strncmp(template->name, "Master Playback ", 16))
+		return 1;
+	return 0;
+}
+
+static int dg_mixer_init(struct oxygen *chip)
+{
+	unsigned int i;
+	int err;
+
+	for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) {
+		err = snd_ctl_add(chip->card,
+				  snd_ctl_new1(&dg_controls[i], chip));
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+static void dump_cs4245_registers(struct oxygen *chip,
+				  struct snd_info_buffer *buffer)
+{
+	struct dg *data = chip->model_data;
+	unsigned int i;
+
+	snd_iprintf(buffer, "\nCS4245:");
+	for (i = 1; i <= 0x10; ++i)
+		snd_iprintf(buffer, " %02x", data->cs4245_regs[i]);
+	snd_iprintf(buffer, "\n");
+}
+
+struct oxygen_model model_xonar_dg = {
+	.shortname = "Xonar DG",
+	.longname = "C-Media Oxygen HD Audio",
+	.chip = "CMI8786",
+	.init = dg_init,
+	.control_filter = dg_control_filter,
+	.mixer_init = dg_mixer_init,
+	.cleanup = dg_cleanup,
+	.suspend = dg_suspend,
+	.resume = dg_resume,
+	.set_dac_params = set_cs4245_dac_params,
+	.set_adc_params = set_cs4245_adc_params,
+	.dump_registers = dump_cs4245_registers,
+	.model_data_size = sizeof(struct dg),
+	.device_config = PLAYBACK_0_TO_I2S |
+			 PLAYBACK_1_TO_SPDIF |
+			 CAPTURE_0_FROM_I2S_2,
+	.dac_channels_pcm = 6,
+	.dac_channels_mixer = 0,
+	.function_flags = OXYGEN_FUNCTION_SPI,
+	.dac_mclks = OXYGEN_MCLKS(256, 128, 128),
+	.adc_mclks = OXYGEN_MCLKS(256, 128, 128),
+	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};

+ 8 - 0
sound/pci/oxygen/xonar_dg.h

@@ -0,0 +1,8 @@
+#ifndef XONAR_DG_H_INCLUDED
+#define XONAR_DG_H_INCLUDED
+
+#include "oxygen.h"
+
+extern struct oxygen_model model_xonar_dg;
+
+#endif

+ 1 - 1
sound/pci/oxygen/xonar_hdmi.c

@@ -1,5 +1,5 @@
 /*
 /*
- * helper functions for HDMI models (Xonar HDAV1.3)
+ * helper functions for HDMI models (Xonar HDAV1.3/HDAV1.3 Slim)
  *
  *
  * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
  * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
  *
  *

+ 4 - 2
sound/pci/oxygen/xonar_lib.c

@@ -104,9 +104,10 @@ int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl,
 {
 {
 	struct oxygen *chip = ctl->private_data;
 	struct oxygen *chip = ctl->private_data;
 	u16 bit = ctl->private_value;
 	u16 bit = ctl->private_value;
+	bool invert = ctl->private_value & XONAR_GPIO_BIT_INVERT;
 
 
 	value->value.integer.value[0] =
 	value->value.integer.value[0] =
-		!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit);
+		!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit) ^ invert;
 	return 0;
 	return 0;
 }
 }
 
 
@@ -115,12 +116,13 @@ int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,
 {
 {
 	struct oxygen *chip = ctl->private_data;
 	struct oxygen *chip = ctl->private_data;
 	u16 bit = ctl->private_value;
 	u16 bit = ctl->private_value;
+	bool invert = ctl->private_value & XONAR_GPIO_BIT_INVERT;
 	u16 old_bits, new_bits;
 	u16 old_bits, new_bits;
 	int changed;
 	int changed;
 
 
 	spin_lock_irq(&chip->reg_lock);
 	spin_lock_irq(&chip->reg_lock);
 	old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
 	old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
-	if (value->value.integer.value[0])
+	if (!!value->value.integer.value[0] ^ invert)
 		new_bits = old_bits | bit;
 		new_bits = old_bits | bit;
 	else
 	else
 		new_bits = old_bits & ~bit;
 		new_bits = old_bits & ~bit;

+ 245 - 228
sound/pci/oxygen/xonar_pcm179x.c

@@ -22,20 +22,26 @@
  *
  *
  * CMI8788:
  * CMI8788:
  *
  *
- * SPI 0 -> 1st PCM1796 (front)
- * SPI 1 -> 2nd PCM1796 (surround)
- * SPI 2 -> 3rd PCM1796 (center/LFE)
- * SPI 4 -> 4th PCM1796 (back)
+ *   SPI 0 -> 1st PCM1796 (front)
+ *   SPI 1 -> 2nd PCM1796 (surround)
+ *   SPI 2 -> 3rd PCM1796 (center/LFE)
+ *   SPI 4 -> 4th PCM1796 (back)
  *
  *
- * GPIO 2 -> M0 of CS5381
- * GPIO 3 -> M1 of CS5381
- * GPIO 5 <- external power present (D2X only)
- * GPIO 7 -> ALT
- * GPIO 8 -> enable output to speakers
+ *   GPIO 2 -> M0 of CS5381
+ *   GPIO 3 -> M1 of CS5381
+ *   GPIO 5 <- external power present (D2X only)
+ *   GPIO 7 -> ALT
+ *   GPIO 8 -> enable output to speakers
  *
  *
  * CM9780:
  * CM9780:
  *
  *
- * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
+ *   LINE_OUT -> input of ADC
+ *
+ *   AUX_IN   <- aux
+ *   VIDEO_IN <- CD
+ *   FMIC_IN  <- mic
+ *
+ *   GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
  */
  */
 
 
 /*
 /*
@@ -44,52 +50,53 @@
  *
  *
  * CMI8788:
  * CMI8788:
  *
  *
- * I²C <-> PCM1796 (front)
+ *   I²C <-> PCM1796 (addr 1001100) (front)
  *
  *
- * GPI 0 <- external power present
+ *   GPI 0 <- external power present
  *
  *
- * GPIO 0 -> enable output to speakers
- * GPIO 2 -> M0 of CS5381
- * GPIO 3 -> M1 of CS5381
- * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
+ *   GPIO 0 -> enable HDMI (0) or speaker (1) output
+ *   GPIO 2 -> M0 of CS5381
+ *   GPIO 3 -> M1 of CS5381
+ *   GPIO 4 <- daughterboard detection
+ *   GPIO 5 <- daughterboard detection
+ *   GPIO 6 -> ?
+ *   GPIO 7 -> ?
+ *   GPIO 8 -> route input jack to line-in (0) or mic-in (1)
  *
  *
- * TXD -> HDMI controller
- * RXD <- HDMI controller
- *
- * PCM1796 front: AD1,0 <- 0,0
+ *   UART <-> HDMI controller
  *
  *
  * CM9780:
  * CM9780:
  *
  *
- * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
+ *   LINE_OUT -> input of ADC
+ *
+ *   AUX_IN <- aux
+ *   CD_IN  <- CD
+ *   MIC_IN <- mic
+ *
+ *   GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
  *
  *
  * no daughterboard
  * no daughterboard
  * ----------------
  * ----------------
  *
  *
- * GPIO 4 <- 1
+ *   GPIO 4 <- 1
  *
  *
  * H6 daughterboard
  * H6 daughterboard
  * ----------------
  * ----------------
  *
  *
- * GPIO 4 <- 0
- * GPIO 5 <- 0
- *
- * I²C <-> PCM1796 (surround)
- *     <-> PCM1796 (center/LFE)
- *     <-> PCM1796 (back)
+ *   GPIO 4 <- 0
+ *   GPIO 5 <- 0
  *
  *
- * PCM1796 surround:   AD1,0 <- 0,1
- * PCM1796 center/LFE: AD1,0 <- 1,0
- * PCM1796 back:       AD1,0 <- 1,1
+ *   I²C <-> PCM1796 (addr 1001101) (surround)
+ *       <-> PCM1796 (addr 1001110) (center/LFE)
+ *       <-> PCM1796 (addr 1001111) (back)
  *
  *
  * unknown daughterboard
  * unknown daughterboard
  * ---------------------
  * ---------------------
  *
  *
- * GPIO 4 <- 0
- * GPIO 5 <- 1
- *
- * I²C <-> CS4362A (surround, center/LFE, back)
+ *   GPIO 4 <- 0
+ *   GPIO 5 <- 1
  *
  *
- * CS4362A: AD0 <- 0
+ *   I²C <-> CS4362A (addr 0011000) (surround, center/LFE, back)
  */
  */
 
 
 /*
 /*
@@ -98,32 +105,35 @@
  *
  *
  * CMI8788:
  * CMI8788:
  *
  *
- * I²C <-> PCM1792A
- *     <-> CS2000 (ST only)
+ *   I²C <-> PCM1792A (addr 1001100)
+ *       <-> CS2000 (addr 1001110) (ST only)
  *
  *
- * ADC1 MCLK -> REF_CLK of CS2000 (ST only)
+ *   ADC1 MCLK -> REF_CLK of CS2000 (ST only)
  *
  *
- * GPI 0 <- external power present (STX only)
+ *   GPI 0 <- external power present (STX only)
  *
  *
- * GPIO 0 -> enable output to speakers
- * GPIO 1 -> route HP to front panel (0) or rear jack (1)
- * GPIO 2 -> M0 of CS5381
- * GPIO 3 -> M1 of CS5381
- * GPIO 7 -> route output to speaker jacks (0) or HP (1)
- * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
+ *   GPIO 0 -> enable output to speakers
+ *   GPIO 1 -> route HP to front panel (0) or rear jack (1)
+ *   GPIO 2 -> M0 of CS5381
+ *   GPIO 3 -> M1 of CS5381
+ *   GPIO 4 <- daughterboard detection
+ *   GPIO 5 <- daughterboard detection
+ *   GPIO 6 -> ?
+ *   GPIO 7 -> route output to speaker jacks (0) or HP (1)
+ *   GPIO 8 -> route input jack to line-in (0) or mic-in (1)
  *
  *
  * PCM1792A:
  * PCM1792A:
  *
  *
- * AD1,0 <- 0,0
- * SCK <- CLK_OUT of CS2000 (ST only)
+ *   SCK <- CLK_OUT of CS2000 (ST only)
  *
  *
- * CS2000:
+ * CM9780:
  *
  *
- * AD0 <- 0
+ *   LINE_OUT -> input of ADC
  *
  *
- * CM9780:
+ *   AUX_IN <- aux
+ *   MIC_IN <- mic
  *
  *
- * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
+ *   GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
  *
  *
  * H6 daughterboard
  * H6 daughterboard
  * ----------------
  * ----------------
@@ -133,15 +143,39 @@
  */
  */
 
 
 /*
 /*
- * Xonar HDAV1.3 Slim
- * ------------------
+ * Xonar Xense
+ * -----------
  *
  *
  * CMI8788:
  * CMI8788:
  *
  *
- * GPIO 1 -> enable output
+ *   I²C <-> PCM1796 (addr 1001100) (front)
+ *       <-> CS4362A (addr 0011000) (surround, center/LFE, back)
+ *       <-> CS2000 (addr 1001110)
+ *
+ *   ADC1 MCLK -> REF_CLK of CS2000
+ *
+ *   GPI 0 <- external power present
+ *
+ *   GPIO 0 -> enable output
+ *   GPIO 1 -> route HP to front panel (0) or rear jack (1)
+ *   GPIO 2 -> M0 of CS5381
+ *   GPIO 3 -> M1 of CS5381
+ *   GPIO 4 -> enable output
+ *   GPIO 5 -> enable output
+ *   GPIO 6 -> ?
+ *   GPIO 7 -> route output to HP (0) or speaker (1)
+ *   GPIO 8 -> route input jack to mic-in (0) or line-in (1)
  *
  *
- * TXD -> HDMI controller
- * RXD <- HDMI controller
+ * CM9780:
+ *
+ *   LINE_OUT -> input of ADC
+ *
+ *   AUX_IN   <- aux
+ *   VIDEO_IN <- ?
+ *   FMIC_IN  <- mic
+ *
+ *   GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
+ *   GPO 1 -> route mic-in from input jack (0) or front panel header (1)
  */
  */
 
 
 #include <linux/pci.h>
 #include <linux/pci.h>
@@ -150,6 +184,7 @@
 #include <sound/ac97_codec.h>
 #include <sound/ac97_codec.h>
 #include <sound/control.h>
 #include <sound/control.h>
 #include <sound/core.h>
 #include <sound/core.h>
+#include <sound/info.h>
 #include <sound/pcm.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/pcm_params.h>
 #include <sound/tlv.h>
 #include <sound/tlv.h>
@@ -167,12 +202,14 @@
 #define GPIO_INPUT_ROUTE	0x0100
 #define GPIO_INPUT_ROUTE	0x0100
 
 
 #define GPIO_HDAV_OUTPUT_ENABLE	0x0001
 #define GPIO_HDAV_OUTPUT_ENABLE	0x0001
+#define GPIO_HDAV_MAGIC		0x00c0
 
 
 #define GPIO_DB_MASK		0x0030
 #define GPIO_DB_MASK		0x0030
 #define GPIO_DB_H6		0x0000
 #define GPIO_DB_H6		0x0000
 
 
 #define GPIO_ST_OUTPUT_ENABLE	0x0001
 #define GPIO_ST_OUTPUT_ENABLE	0x0001
 #define GPIO_ST_HP_REAR		0x0002
 #define GPIO_ST_HP_REAR		0x0002
+#define GPIO_ST_MAGIC		0x0040
 #define GPIO_ST_HP		0x0080
 #define GPIO_ST_HP		0x0080
 
 
 #define I2C_DEVICE_PCM1796(i)	(0x98 + ((i) << 1))	/* 10011, ii, /W=0 */
 #define I2C_DEVICE_PCM1796(i)	(0x98 + ((i) << 1))	/* 10011, ii, /W=0 */
@@ -186,11 +223,12 @@ struct xonar_pcm179x {
 	unsigned int dacs;
 	unsigned int dacs;
 	u8 pcm1796_regs[4][5];
 	u8 pcm1796_regs[4][5];
 	unsigned int current_rate;
 	unsigned int current_rate;
-	bool os_128;
+	bool h6;
 	bool hp_active;
 	bool hp_active;
 	s8 hp_gain_offset;
 	s8 hp_gain_offset;
 	bool has_cs2000;
 	bool has_cs2000;
-	u8 cs2000_fun_cfg_1;
+	u8 cs2000_regs[0x1f];
+	bool broken_i2c;
 };
 };
 
 
 struct xonar_hdav {
 struct xonar_hdav {
@@ -249,16 +287,14 @@ static void cs2000_write(struct oxygen *chip, u8 reg, u8 value)
 	struct xonar_pcm179x *data = chip->model_data;
 	struct xonar_pcm179x *data = chip->model_data;
 
 
 	oxygen_write_i2c(chip, I2C_DEVICE_CS2000, reg, value);
 	oxygen_write_i2c(chip, I2C_DEVICE_CS2000, reg, value);
-	if (reg == CS2000_FUN_CFG_1)
-		data->cs2000_fun_cfg_1 = value;
+	data->cs2000_regs[reg] = value;
 }
 }
 
 
 static void cs2000_write_cached(struct oxygen *chip, u8 reg, u8 value)
 static void cs2000_write_cached(struct oxygen *chip, u8 reg, u8 value)
 {
 {
 	struct xonar_pcm179x *data = chip->model_data;
 	struct xonar_pcm179x *data = chip->model_data;
 
 
-	if (reg != CS2000_FUN_CFG_1 ||
-	    value != data->cs2000_fun_cfg_1)
+	if (value != data->cs2000_regs[reg])
 		cs2000_write(chip, reg, value);
 		cs2000_write(chip, reg, value);
 }
 }
 
 
@@ -268,6 +304,7 @@ static void pcm1796_registers_init(struct oxygen *chip)
 	unsigned int i;
 	unsigned int i;
 	s8 gain_offset;
 	s8 gain_offset;
 
 
+	msleep(1);
 	gain_offset = data->hp_active ? data->hp_gain_offset : 0;
 	gain_offset = data->hp_active ? data->hp_gain_offset : 0;
 	for (i = 0; i < data->dacs; ++i) {
 	for (i = 0; i < data->dacs; ++i) {
 		/* set ATLD before ATL/ATR */
 		/* set ATLD before ATL/ATR */
@@ -282,6 +319,7 @@ static void pcm1796_registers_init(struct oxygen *chip)
 		pcm1796_write(chip, i, 20,
 		pcm1796_write(chip, i, 20,
 			      data->pcm1796_regs[0][20 - PCM1796_REG_BASE]);
 			      data->pcm1796_regs[0][20 - PCM1796_REG_BASE]);
 		pcm1796_write(chip, i, 21, 0);
 		pcm1796_write(chip, i, 21, 0);
+		gain_offset = 0;
 	}
 	}
 }
 }
 
 
@@ -290,10 +328,11 @@ static void pcm1796_init(struct oxygen *chip)
 	struct xonar_pcm179x *data = chip->model_data;
 	struct xonar_pcm179x *data = chip->model_data;
 
 
 	data->pcm1796_regs[0][18 - PCM1796_REG_BASE] = PCM1796_MUTE |
 	data->pcm1796_regs[0][18 - PCM1796_REG_BASE] = PCM1796_MUTE |
-		PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD;
+		PCM1796_DMF_DISABLED | PCM1796_FMT_24_I2S | PCM1796_ATLD;
 	data->pcm1796_regs[0][19 - PCM1796_REG_BASE] =
 	data->pcm1796_regs[0][19 - PCM1796_REG_BASE] =
 		PCM1796_FLT_SHARP | PCM1796_ATS_1;
 		PCM1796_FLT_SHARP | PCM1796_ATS_1;
-	data->pcm1796_regs[0][20 - PCM1796_REG_BASE] = PCM1796_OS_64;
+	data->pcm1796_regs[0][20 - PCM1796_REG_BASE] =
+		data->h6 ? PCM1796_OS_64 : PCM1796_OS_128;
 	pcm1796_registers_init(chip);
 	pcm1796_registers_init(chip);
 	data->current_rate = 48000;
 	data->current_rate = 48000;
 }
 }
@@ -339,18 +378,20 @@ static void xonar_hdav_init(struct oxygen *chip)
 	oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
 	oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
 		       OXYGEN_2WIRE_LENGTH_8 |
 		       OXYGEN_2WIRE_LENGTH_8 |
 		       OXYGEN_2WIRE_INTERRUPT_MASK |
 		       OXYGEN_2WIRE_INTERRUPT_MASK |
-		       OXYGEN_2WIRE_SPEED_FAST);
+		       OXYGEN_2WIRE_SPEED_STANDARD);
 
 
 	data->pcm179x.generic.anti_pop_delay = 100;
 	data->pcm179x.generic.anti_pop_delay = 100;
 	data->pcm179x.generic.output_enable_bit = GPIO_HDAV_OUTPUT_ENABLE;
 	data->pcm179x.generic.output_enable_bit = GPIO_HDAV_OUTPUT_ENABLE;
 	data->pcm179x.generic.ext_power_reg = OXYGEN_GPI_DATA;
 	data->pcm179x.generic.ext_power_reg = OXYGEN_GPI_DATA;
 	data->pcm179x.generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
 	data->pcm179x.generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
 	data->pcm179x.generic.ext_power_bit = GPI_EXT_POWER;
 	data->pcm179x.generic.ext_power_bit = GPI_EXT_POWER;
-	data->pcm179x.dacs = chip->model.private_data ? 4 : 1;
+	data->pcm179x.dacs = chip->model.dac_channels_mixer / 2;
+	data->pcm179x.h6 = chip->model.dac_channels_mixer > 2;
 
 
 	pcm1796_init(chip);
 	pcm1796_init(chip);
 
 
-	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_INPUT_ROUTE);
+	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+			  GPIO_HDAV_MAGIC | GPIO_INPUT_ROUTE);
 	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_INPUT_ROUTE);
 	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_INPUT_ROUTE);
 
 
 	xonar_init_cs53x1(chip);
 	xonar_init_cs53x1(chip);
@@ -367,7 +408,7 @@ static void xonar_st_init_i2c(struct oxygen *chip)
 	oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
 	oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
 		       OXYGEN_2WIRE_LENGTH_8 |
 		       OXYGEN_2WIRE_LENGTH_8 |
 		       OXYGEN_2WIRE_INTERRUPT_MASK |
 		       OXYGEN_2WIRE_INTERRUPT_MASK |
-		       OXYGEN_2WIRE_SPEED_FAST);
+		       OXYGEN_2WIRE_SPEED_STANDARD);
 }
 }
 
 
 static void xonar_st_init_common(struct oxygen *chip)
 static void xonar_st_init_common(struct oxygen *chip)
@@ -375,13 +416,14 @@ static void xonar_st_init_common(struct oxygen *chip)
 	struct xonar_pcm179x *data = chip->model_data;
 	struct xonar_pcm179x *data = chip->model_data;
 
 
 	data->generic.output_enable_bit = GPIO_ST_OUTPUT_ENABLE;
 	data->generic.output_enable_bit = GPIO_ST_OUTPUT_ENABLE;
-	data->dacs = chip->model.private_data ? 4 : 1;
+	data->dacs = chip->model.dac_channels_mixer / 2;
 	data->hp_gain_offset = 2*-18;
 	data->hp_gain_offset = 2*-18;
 
 
 	pcm1796_init(chip);
 	pcm1796_init(chip);
 
 
 	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
 	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
-			  GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP);
+			  GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR |
+			  GPIO_ST_MAGIC | GPIO_ST_HP);
 	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
 	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
 			    GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP);
 			    GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP);
 
 
@@ -410,9 +452,11 @@ static void cs2000_registers_init(struct oxygen *chip)
 	cs2000_write(chip, CS2000_RATIO_0 + 1, 0x10);
 	cs2000_write(chip, CS2000_RATIO_0 + 1, 0x10);
 	cs2000_write(chip, CS2000_RATIO_0 + 2, 0x00);
 	cs2000_write(chip, CS2000_RATIO_0 + 2, 0x00);
 	cs2000_write(chip, CS2000_RATIO_0 + 3, 0x00);
 	cs2000_write(chip, CS2000_RATIO_0 + 3, 0x00);
-	cs2000_write(chip, CS2000_FUN_CFG_1, data->cs2000_fun_cfg_1);
+	cs2000_write(chip, CS2000_FUN_CFG_1,
+		     data->cs2000_regs[CS2000_FUN_CFG_1]);
 	cs2000_write(chip, CS2000_FUN_CFG_2, 0);
 	cs2000_write(chip, CS2000_FUN_CFG_2, 0);
 	cs2000_write(chip, CS2000_GLOBAL_CFG, CS2000_EN_DEV_CFG_2);
 	cs2000_write(chip, CS2000_GLOBAL_CFG, CS2000_EN_DEV_CFG_2);
+	msleep(3); /* PLL lock delay */
 }
 }
 
 
 static void xonar_st_init(struct oxygen *chip)
 static void xonar_st_init(struct oxygen *chip)
@@ -420,13 +464,18 @@ static void xonar_st_init(struct oxygen *chip)
 	struct xonar_pcm179x *data = chip->model_data;
 	struct xonar_pcm179x *data = chip->model_data;
 
 
 	data->generic.anti_pop_delay = 100;
 	data->generic.anti_pop_delay = 100;
+	data->h6 = chip->model.dac_channels_mixer > 2;
 	data->has_cs2000 = 1;
 	data->has_cs2000 = 1;
-	data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_1;
+	data->cs2000_regs[CS2000_FUN_CFG_1] = CS2000_REF_CLK_DIV_1;
+	data->broken_i2c = true;
 
 
 	oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
 	oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
-		       OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_I2S |
-		       OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
-		       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
+		       OXYGEN_RATE_48000 |
+		       OXYGEN_I2S_FORMAT_I2S |
+		       OXYGEN_I2S_MCLK(data->h6 ? MCLK_256 : MCLK_512) |
+		       OXYGEN_I2S_BITS_16 |
+		       OXYGEN_I2S_MASTER |
+		       OXYGEN_I2S_BCLK_64);
 
 
 	xonar_st_init_i2c(chip);
 	xonar_st_init_i2c(chip);
 	cs2000_registers_init(chip);
 	cs2000_registers_init(chip);
@@ -507,44 +556,16 @@ static void xonar_st_resume(struct oxygen *chip)
 	xonar_stx_resume(chip);
 	xonar_stx_resume(chip);
 }
 }
 
 
-static unsigned int mclk_from_rate(struct oxygen *chip, unsigned int rate)
-{
-	struct xonar_pcm179x *data = chip->model_data;
-
-	if (rate <= 32000)
-		return OXYGEN_I2S_MCLK_512;
-	else if (rate <= 48000 && data->os_128)
-		return OXYGEN_I2S_MCLK_512;
-	else if (rate <= 96000)
-		return OXYGEN_I2S_MCLK_256;
-	else
-		return OXYGEN_I2S_MCLK_128;
-}
-
-static unsigned int get_pcm1796_i2s_mclk(struct oxygen *chip,
-					 unsigned int channel,
-					 struct snd_pcm_hw_params *params)
-{
-	if (channel == PCM_MULTICH)
-		return mclk_from_rate(chip, params_rate(params));
-	else
-		return oxygen_default_i2s_mclk(chip, channel, params);
-}
-
 static void update_pcm1796_oversampling(struct oxygen *chip)
 static void update_pcm1796_oversampling(struct oxygen *chip)
 {
 {
 	struct xonar_pcm179x *data = chip->model_data;
 	struct xonar_pcm179x *data = chip->model_data;
 	unsigned int i;
 	unsigned int i;
 	u8 reg;
 	u8 reg;
 
 
-	if (data->current_rate <= 32000)
+	if (data->current_rate <= 48000 && !data->h6)
 		reg = PCM1796_OS_128;
 		reg = PCM1796_OS_128;
-	else if (data->current_rate <= 48000 && data->os_128)
-		reg = PCM1796_OS_128;
-	else if (data->current_rate <= 96000 || data->os_128)
-		reg = PCM1796_OS_64;
 	else
 	else
-		reg = PCM1796_OS_32;
+		reg = PCM1796_OS_64;
 	for (i = 0; i < data->dacs; ++i)
 	for (i = 0; i < data->dacs; ++i)
 		pcm1796_write_cached(chip, i, 20, reg);
 		pcm1796_write_cached(chip, i, 20, reg);
 }
 }
@@ -554,6 +575,7 @@ static void set_pcm1796_params(struct oxygen *chip,
 {
 {
 	struct xonar_pcm179x *data = chip->model_data;
 	struct xonar_pcm179x *data = chip->model_data;
 
 
+	msleep(1);
 	data->current_rate = params_rate(params);
 	data->current_rate = params_rate(params);
 	update_pcm1796_oversampling(chip);
 	update_pcm1796_oversampling(chip);
 }
 }
@@ -570,6 +592,7 @@ static void update_pcm1796_volume(struct oxygen *chip)
 				     + gain_offset);
 				     + gain_offset);
 		pcm1796_write_cached(chip, i, 17, chip->dac_volume[i * 2 + 1]
 		pcm1796_write_cached(chip, i, 17, chip->dac_volume[i * 2 + 1]
 				     + gain_offset);
 				     + gain_offset);
+		gain_offset = 0;
 	}
 	}
 }
 }
 
 
@@ -579,7 +602,7 @@ static void update_pcm1796_mute(struct oxygen *chip)
 	unsigned int i;
 	unsigned int i;
 	u8 value;
 	u8 value;
 
 
-	value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD;
+	value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_I2S | PCM1796_ATLD;
 	if (chip->dac_mute)
 	if (chip->dac_mute)
 		value |= PCM1796_MUTE;
 		value |= PCM1796_MUTE;
 	for (i = 0; i < data->dacs; ++i)
 	for (i = 0; i < data->dacs; ++i)
@@ -592,45 +615,35 @@ static void update_cs2000_rate(struct oxygen *chip, unsigned int rate)
 	u8 rate_mclk, reg;
 	u8 rate_mclk, reg;
 
 
 	switch (rate) {
 	switch (rate) {
-		/* XXX Why is the I2S A MCLK half the actual I2S MCLK? */
 	case 32000:
 	case 32000:
-		rate_mclk = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256;
-		break;
-	case 44100:
-		if (data->os_128)
-			rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256;
-		else
-			rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_128;
-		break;
-	default: /* 48000 */
-		if (data->os_128)
-			rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256;
-		else
-			rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_128;
-		break;
 	case 64000:
 	case 64000:
-		rate_mclk = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256;
+		rate_mclk = OXYGEN_RATE_32000;
 		break;
 		break;
+	case 44100:
 	case 88200:
 	case 88200:
-		rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256;
-		break;
-	case 96000:
-		rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256;
-		break;
 	case 176400:
 	case 176400:
-		rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256;
+		rate_mclk = OXYGEN_RATE_44100;
 		break;
 		break;
+	default:
+	case 48000:
+	case 96000:
 	case 192000:
 	case 192000:
-		rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256;
+		rate_mclk = OXYGEN_RATE_48000;
 		break;
 		break;
 	}
 	}
-	oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, rate_mclk,
-			      OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_MCLK_MASK);
-	if ((rate_mclk & OXYGEN_I2S_MCLK_MASK) <= OXYGEN_I2S_MCLK_128)
+
+	if (rate <= 96000 && (rate > 48000 || data->h6)) {
+		rate_mclk |= OXYGEN_I2S_MCLK(MCLK_256);
 		reg = CS2000_REF_CLK_DIV_1;
 		reg = CS2000_REF_CLK_DIV_1;
-	else
+	} else {
+		rate_mclk |= OXYGEN_I2S_MCLK(MCLK_512);
 		reg = CS2000_REF_CLK_DIV_2;
 		reg = CS2000_REF_CLK_DIV_2;
+	}
+
+	oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, rate_mclk,
+			      OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_MCLK_MASK);
 	cs2000_write_cached(chip, CS2000_FUN_CFG_1, reg);
 	cs2000_write_cached(chip, CS2000_FUN_CFG_1, reg);
+	msleep(3); /* PLL lock delay */
 }
 }
 
 
 static void set_st_params(struct oxygen *chip,
 static void set_st_params(struct oxygen *chip,
@@ -665,13 +678,7 @@ static int rolloff_info(struct snd_kcontrol *ctl,
 		"Sharp Roll-off", "Slow Roll-off"
 		"Sharp Roll-off", "Slow Roll-off"
 	};
 	};
 
 
-	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-	info->count = 1;
-	info->value.enumerated.items = 2;
-	if (info->value.enumerated.item >= 2)
-		info->value.enumerated.item = 1;
-	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
-	return 0;
+	return snd_ctl_enum_info(info, 1, 2, names);
 }
 }
 
 
 static int rolloff_get(struct snd_kcontrol *ctl,
 static int rolloff_get(struct snd_kcontrol *ctl,
@@ -719,57 +726,13 @@ static const struct snd_kcontrol_new rolloff_control = {
 	.put = rolloff_put,
 	.put = rolloff_put,
 };
 };
 
 
-static int os_128_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
-{
-	static const char *const names[2] = { "64x", "128x" };
-
-	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-	info->count = 1;
-	info->value.enumerated.items = 2;
-	if (info->value.enumerated.item >= 2)
-		info->value.enumerated.item = 1;
-	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
-	return 0;
-}
-
-static int os_128_get(struct snd_kcontrol *ctl,
-		      struct snd_ctl_elem_value *value)
-{
-	struct oxygen *chip = ctl->private_data;
-	struct xonar_pcm179x *data = chip->model_data;
-
-	value->value.enumerated.item[0] = data->os_128;
-	return 0;
-}
-
-static int os_128_put(struct snd_kcontrol *ctl,
-		      struct snd_ctl_elem_value *value)
-{
-	struct oxygen *chip = ctl->private_data;
-	struct xonar_pcm179x *data = chip->model_data;
-	int changed;
-
-	mutex_lock(&chip->mutex);
-	changed = value->value.enumerated.item[0] != data->os_128;
-	if (changed) {
-		data->os_128 = value->value.enumerated.item[0];
-		if (data->has_cs2000)
-			update_cs2000_rate(chip, data->current_rate);
-		oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
-				      mclk_from_rate(chip, data->current_rate),
-				      OXYGEN_I2S_MCLK_MASK);
-		update_pcm1796_oversampling(chip);
-	}
-	mutex_unlock(&chip->mutex);
-	return changed;
-}
-
-static const struct snd_kcontrol_new os_128_control = {
+static const struct snd_kcontrol_new hdav_hdmi_control = {
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name = "DAC Oversampling Playback Enum",
-	.info = os_128_info,
-	.get = os_128_get,
-	.put = os_128_put,
+	.name = "HDMI Playback Switch",
+	.info = snd_ctl_boolean_mono_info,
+	.get = xonar_gpio_bit_switch_get,
+	.put = xonar_gpio_bit_switch_put,
+	.private_value = GPIO_HDAV_OUTPUT_ENABLE | XONAR_GPIO_BIT_INVERT,
 };
 };
 
 
 static int st_output_switch_info(struct snd_kcontrol *ctl,
 static int st_output_switch_info(struct snd_kcontrol *ctl,
@@ -779,13 +742,7 @@ static int st_output_switch_info(struct snd_kcontrol *ctl,
 		"Speakers", "Headphones", "FP Headphones"
 		"Speakers", "Headphones", "FP Headphones"
 	};
 	};
 
 
-	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-	info->count = 1;
-	info->value.enumerated.items = 3;
-	if (info->value.enumerated.item >= 3)
-		info->value.enumerated.item = 2;
-	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
-	return 0;
+	return snd_ctl_enum_info(info, 1, 3, names);
 }
 }
 
 
 static int st_output_switch_get(struct snd_kcontrol *ctl,
 static int st_output_switch_get(struct snd_kcontrol *ctl,
@@ -840,13 +797,7 @@ static int st_hp_volume_offset_info(struct snd_kcontrol *ctl,
 		"< 64 ohms", "64-300 ohms", "300-600 ohms"
 		"< 64 ohms", "64-300 ohms", "300-600 ohms"
 	};
 	};
 
 
-	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-	info->count = 1;
-	info->value.enumerated.items = 3;
-	if (info->value.enumerated.item > 2)
-		info->value.enumerated.item = 2;
-	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
-	return 0;
+	return snd_ctl_enum_info(info, 1, 3, names);
 }
 }
 
 
 static int st_hp_volume_offset_get(struct snd_kcontrol *ctl,
 static int st_hp_volume_offset_get(struct snd_kcontrol *ctl,
@@ -928,16 +879,25 @@ static int xonar_d2_control_filter(struct snd_kcontrol_new *template)
 	return 0;
 	return 0;
 }
 }
 
 
+static int xonar_st_h6_control_filter(struct snd_kcontrol_new *template)
+{
+	if (!strncmp(template->name, "Master Playback ", 16))
+		/* no volume/mute, as I²C to the third DAC does not work */
+		return 1;
+	return 0;
+}
+
 static int add_pcm1796_controls(struct oxygen *chip)
 static int add_pcm1796_controls(struct oxygen *chip)
 {
 {
+	struct xonar_pcm179x *data = chip->model_data;
 	int err;
 	int err;
 
 
-	err = snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip));
-	if (err < 0)
-		return err;
-	err = snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip));
-	if (err < 0)
-		return err;
+	if (!data->broken_i2c) {
+		err = snd_ctl_add(chip->card,
+				  snd_ctl_new1(&rolloff_control, chip));
+		if (err < 0)
+			return err;
+	}
 	return 0;
 	return 0;
 }
 }
 
 
@@ -956,7 +916,15 @@ static int xonar_d2_mixer_init(struct oxygen *chip)
 
 
 static int xonar_hdav_mixer_init(struct oxygen *chip)
 static int xonar_hdav_mixer_init(struct oxygen *chip)
 {
 {
-	return add_pcm1796_controls(chip);
+	int err;
+
+	err = snd_ctl_add(chip->card, snd_ctl_new1(&hdav_hdmi_control, chip));
+	if (err < 0)
+		return err;
+	err = add_pcm1796_controls(chip);
+	if (err < 0)
+		return err;
+	return 0;
 }
 }
 
 
 static int xonar_st_mixer_init(struct oxygen *chip)
 static int xonar_st_mixer_init(struct oxygen *chip)
@@ -976,6 +944,45 @@ static int xonar_st_mixer_init(struct oxygen *chip)
 	return 0;
 	return 0;
 }
 }
 
 
+static void dump_pcm1796_registers(struct oxygen *chip,
+				   struct snd_info_buffer *buffer)
+{
+	struct xonar_pcm179x *data = chip->model_data;
+	unsigned int dac, i;
+
+	for (dac = 0; dac < data->dacs; ++dac) {
+		snd_iprintf(buffer, "\nPCM1796 %u:", dac + 1);
+		for (i = 0; i < 5; ++i)
+			snd_iprintf(buffer, " %02x",
+				    data->pcm1796_regs[dac][i]);
+	}
+	snd_iprintf(buffer, "\n");
+}
+
+static void dump_cs2000_registers(struct oxygen *chip,
+				  struct snd_info_buffer *buffer)
+{
+	struct xonar_pcm179x *data = chip->model_data;
+	unsigned int i;
+
+	if (data->has_cs2000) {
+		snd_iprintf(buffer, "\nCS2000:\n00:   ");
+		for (i = 1; i < 0x10; ++i)
+			snd_iprintf(buffer, " %02x", data->cs2000_regs[i]);
+		snd_iprintf(buffer, "\n10:");
+		for (i = 0x10; i < 0x1f; ++i)
+			snd_iprintf(buffer, " %02x", data->cs2000_regs[i]);
+		snd_iprintf(buffer, "\n");
+	}
+}
+
+static void dump_st_registers(struct oxygen *chip,
+			      struct snd_info_buffer *buffer)
+{
+	dump_pcm1796_registers(chip, buffer);
+	dump_cs2000_registers(chip, buffer);
+}
+
 static const struct oxygen_model model_xonar_d2 = {
 static const struct oxygen_model model_xonar_d2 = {
 	.longname = "Asus Virtuoso 200",
 	.longname = "Asus Virtuoso 200",
 	.chip = "AV200",
 	.chip = "AV200",
@@ -985,11 +992,11 @@ static const struct oxygen_model model_xonar_d2 = {
 	.cleanup = xonar_d2_cleanup,
 	.cleanup = xonar_d2_cleanup,
 	.suspend = xonar_d2_suspend,
 	.suspend = xonar_d2_suspend,
 	.resume = xonar_d2_resume,
 	.resume = xonar_d2_resume,
-	.get_i2s_mclk = get_pcm1796_i2s_mclk,
 	.set_dac_params = set_pcm1796_params,
 	.set_dac_params = set_pcm1796_params,
 	.set_adc_params = xonar_set_cs53x1_params,
 	.set_adc_params = xonar_set_cs53x1_params,
 	.update_dac_volume = update_pcm1796_volume,
 	.update_dac_volume = update_pcm1796_volume,
 	.update_dac_mute = update_pcm1796_mute,
 	.update_dac_mute = update_pcm1796_mute,
+	.dump_registers = dump_pcm1796_registers,
 	.dac_tlv = pcm1796_db_scale,
 	.dac_tlv = pcm1796_db_scale,
 	.model_data_size = sizeof(struct xonar_pcm179x),
 	.model_data_size = sizeof(struct xonar_pcm179x),
 	.device_config = PLAYBACK_0_TO_I2S |
 	.device_config = PLAYBACK_0_TO_I2S |
@@ -999,13 +1006,16 @@ static const struct oxygen_model model_xonar_d2 = {
 			 MIDI_OUTPUT |
 			 MIDI_OUTPUT |
 			 MIDI_INPUT |
 			 MIDI_INPUT |
 			 AC97_CD_INPUT,
 			 AC97_CD_INPUT,
-	.dac_channels = 8,
+	.dac_channels_pcm = 8,
+	.dac_channels_mixer = 8,
 	.dac_volume_min = 255 - 2*60,
 	.dac_volume_min = 255 - 2*60,
 	.dac_volume_max = 255,
 	.dac_volume_max = 255,
 	.misc_flags = OXYGEN_MISC_MIDI,
 	.misc_flags = OXYGEN_MISC_MIDI,
 	.function_flags = OXYGEN_FUNCTION_SPI |
 	.function_flags = OXYGEN_FUNCTION_SPI |
 			  OXYGEN_FUNCTION_ENABLE_SPI_4_5,
 			  OXYGEN_FUNCTION_ENABLE_SPI_4_5,
-	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+	.dac_mclks = OXYGEN_MCLKS(512, 128, 128),
+	.adc_mclks = OXYGEN_MCLKS(256, 128, 128),
+	.dac_i2s_format = OXYGEN_I2S_FORMAT_I2S,
 	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 };
 };
 
 
@@ -1018,25 +1028,28 @@ static const struct oxygen_model model_xonar_hdav = {
 	.suspend = xonar_hdav_suspend,
 	.suspend = xonar_hdav_suspend,
 	.resume = xonar_hdav_resume,
 	.resume = xonar_hdav_resume,
 	.pcm_hardware_filter = xonar_hdmi_pcm_hardware_filter,
 	.pcm_hardware_filter = xonar_hdmi_pcm_hardware_filter,
-	.get_i2s_mclk = get_pcm1796_i2s_mclk,
 	.set_dac_params = set_hdav_params,
 	.set_dac_params = set_hdav_params,
 	.set_adc_params = xonar_set_cs53x1_params,
 	.set_adc_params = xonar_set_cs53x1_params,
 	.update_dac_volume = update_pcm1796_volume,
 	.update_dac_volume = update_pcm1796_volume,
 	.update_dac_mute = update_pcm1796_mute,
 	.update_dac_mute = update_pcm1796_mute,
 	.uart_input = xonar_hdmi_uart_input,
 	.uart_input = xonar_hdmi_uart_input,
 	.ac97_switch = xonar_line_mic_ac97_switch,
 	.ac97_switch = xonar_line_mic_ac97_switch,
+	.dump_registers = dump_pcm1796_registers,
 	.dac_tlv = pcm1796_db_scale,
 	.dac_tlv = pcm1796_db_scale,
 	.model_data_size = sizeof(struct xonar_hdav),
 	.model_data_size = sizeof(struct xonar_hdav),
 	.device_config = PLAYBACK_0_TO_I2S |
 	.device_config = PLAYBACK_0_TO_I2S |
 			 PLAYBACK_1_TO_SPDIF |
 			 PLAYBACK_1_TO_SPDIF |
 			 CAPTURE_0_FROM_I2S_2 |
 			 CAPTURE_0_FROM_I2S_2 |
 			 CAPTURE_1_FROM_SPDIF,
 			 CAPTURE_1_FROM_SPDIF,
-	.dac_channels = 8,
+	.dac_channels_pcm = 8,
+	.dac_channels_mixer = 2,
 	.dac_volume_min = 255 - 2*60,
 	.dac_volume_min = 255 - 2*60,
 	.dac_volume_max = 255,
 	.dac_volume_max = 255,
 	.misc_flags = OXYGEN_MISC_MIDI,
 	.misc_flags = OXYGEN_MISC_MIDI,
 	.function_flags = OXYGEN_FUNCTION_2WIRE,
 	.function_flags = OXYGEN_FUNCTION_2WIRE,
-	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+	.dac_mclks = OXYGEN_MCLKS(512, 128, 128),
+	.adc_mclks = OXYGEN_MCLKS(256, 128, 128),
+	.dac_i2s_format = OXYGEN_I2S_FORMAT_I2S,
 	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 };
 };
 
 
@@ -1048,22 +1061,26 @@ static const struct oxygen_model model_xonar_st = {
 	.cleanup = xonar_st_cleanup,
 	.cleanup = xonar_st_cleanup,
 	.suspend = xonar_st_suspend,
 	.suspend = xonar_st_suspend,
 	.resume = xonar_st_resume,
 	.resume = xonar_st_resume,
-	.get_i2s_mclk = get_pcm1796_i2s_mclk,
 	.set_dac_params = set_st_params,
 	.set_dac_params = set_st_params,
 	.set_adc_params = xonar_set_cs53x1_params,
 	.set_adc_params = xonar_set_cs53x1_params,
 	.update_dac_volume = update_pcm1796_volume,
 	.update_dac_volume = update_pcm1796_volume,
 	.update_dac_mute = update_pcm1796_mute,
 	.update_dac_mute = update_pcm1796_mute,
 	.ac97_switch = xonar_line_mic_ac97_switch,
 	.ac97_switch = xonar_line_mic_ac97_switch,
+	.dump_registers = dump_st_registers,
 	.dac_tlv = pcm1796_db_scale,
 	.dac_tlv = pcm1796_db_scale,
 	.model_data_size = sizeof(struct xonar_pcm179x),
 	.model_data_size = sizeof(struct xonar_pcm179x),
 	.device_config = PLAYBACK_0_TO_I2S |
 	.device_config = PLAYBACK_0_TO_I2S |
 			 PLAYBACK_1_TO_SPDIF |
 			 PLAYBACK_1_TO_SPDIF |
-			 CAPTURE_0_FROM_I2S_2,
-	.dac_channels = 2,
+			 CAPTURE_0_FROM_I2S_2 |
+			 AC97_FMIC_SWITCH,
+	.dac_channels_pcm = 2,
+	.dac_channels_mixer = 2,
 	.dac_volume_min = 255 - 2*60,
 	.dac_volume_min = 255 - 2*60,
 	.dac_volume_max = 255,
 	.dac_volume_max = 255,
 	.function_flags = OXYGEN_FUNCTION_2WIRE,
 	.function_flags = OXYGEN_FUNCTION_2WIRE,
-	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+	.dac_mclks = OXYGEN_MCLKS(512, 128, 128),
+	.adc_mclks = OXYGEN_MCLKS(256, 128, 128),
+	.dac_i2s_format = OXYGEN_I2S_FORMAT_I2S,
 	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 };
 };
 
 
@@ -1089,7 +1106,8 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip,
 			break;
 			break;
 		case GPIO_DB_H6:
 		case GPIO_DB_H6:
 			chip->model.shortname = "Xonar HDAV1.3+H6";
 			chip->model.shortname = "Xonar HDAV1.3+H6";
-			chip->model.private_data = 1;
+			chip->model.dac_channels_mixer = 8;
+			chip->model.dac_mclks = OXYGEN_MCLKS(256, 128, 128);
 			break;
 			break;
 		}
 		}
 		break;
 		break;
@@ -1102,8 +1120,10 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip,
 			break;
 			break;
 		case GPIO_DB_H6:
 		case GPIO_DB_H6:
 			chip->model.shortname = "Xonar ST+H6";
 			chip->model.shortname = "Xonar ST+H6";
-			chip->model.dac_channels = 8;
-			chip->model.private_data = 1;
+			chip->model.control_filter = xonar_st_h6_control_filter;
+			chip->model.dac_channels_pcm = 8;
+			chip->model.dac_channels_mixer = 8;
+			chip->model.dac_mclks = OXYGEN_MCLKS(256, 128, 128);
 			break;
 			break;
 		}
 		}
 		break;
 		break;
@@ -1114,9 +1134,6 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip,
 		chip->model.resume = xonar_stx_resume;
 		chip->model.resume = xonar_stx_resume;
 		chip->model.set_dac_params = set_pcm1796_params;
 		chip->model.set_dac_params = set_pcm1796_params;
 		break;
 		break;
-	case 0x835e:
-		snd_printk(KERN_ERR "the HDAV1.3 Slim is not supported\n");
-		return -ENODEV;
 	default:
 	default:
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}

+ 273 - 44
sound/pci/oxygen/xonar_wm87x6.c

@@ -1,5 +1,5 @@
 /*
 /*
- * card driver for models with WM8776/WM8766 DACs (Xonar DS)
+ * card driver for models with WM8776/WM8766 DACs (Xonar DS/HDAV1.3 Slim)
  *
  *
  * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
  * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
  *
  *
@@ -22,26 +22,48 @@
  *
  *
  * CMI8788:
  * CMI8788:
  *
  *
- * SPI 0 -> WM8766 (surround, center/LFE, back)
- * SPI 1 -> WM8776 (front, input)
+ *   SPI 0 -> WM8766 (surround, center/LFE, back)
+ *   SPI 1 -> WM8776 (front, input)
  *
  *
- * GPIO 4 <- headphone detect, 0 = plugged
- * GPIO 6 -> route input jack to mic-in (0) or line-in (1)
- * GPIO 7 -> enable output to front L/R speaker channels
- * GPIO 8 -> enable output to other speaker channels and front panel headphone
+ *   GPIO 4 <- headphone detect, 0 = plugged
+ *   GPIO 6 -> route input jack to mic-in (0) or line-in (1)
+ *   GPIO 7 -> enable output to front L/R speaker channels
+ *   GPIO 8 -> enable output to other speaker channels and front panel headphone
  *
  *
- * WM8766:
+ * WM8776:
  *
  *
- * input 1 <- line
- * input 2 <- mic
- * input 3 <- front mic
- * input 4 <- aux
+ *   input 1 <- line
+ *   input 2 <- mic
+ *   input 3 <- front mic
+ *   input 4 <- aux
+ */
+
+/*
+ * Xonar HDAV1.3 Slim
+ * ------------------
+ *
+ * CMI8788:
+ *
+ *   I²C <-> WM8776 (addr 0011010)
+ *
+ *   GPIO 0  -> disable HDMI output
+ *   GPIO 1  -> enable HP output
+ *   GPIO 6  -> firmware EEPROM I²C clock
+ *   GPIO 7 <-> firmware EEPROM I²C data
+ *
+ *   UART <-> HDMI controller
+ *
+ * WM8776:
+ *
+ *   input 1 <- mic
+ *   input 2 <- aux
  */
  */
 
 
 #include <linux/pci.h>
 #include <linux/pci.h>
 #include <linux/delay.h>
 #include <linux/delay.h>
 #include <sound/control.h>
 #include <sound/control.h>
 #include <sound/core.h>
 #include <sound/core.h>
+#include <sound/info.h>
 #include <sound/jack.h>
 #include <sound/jack.h>
 #include <sound/pcm.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/pcm_params.h>
@@ -55,6 +77,13 @@
 #define GPIO_DS_OUTPUT_FRONTLR	0x0080
 #define GPIO_DS_OUTPUT_FRONTLR	0x0080
 #define GPIO_DS_OUTPUT_ENABLE	0x0100
 #define GPIO_DS_OUTPUT_ENABLE	0x0100
 
 
+#define GPIO_SLIM_HDMI_DISABLE	0x0001
+#define GPIO_SLIM_OUTPUT_ENABLE	0x0002
+#define GPIO_SLIM_FIRMWARE_CLK	0x0040
+#define GPIO_SLIM_FIRMWARE_DATA	0x0080
+
+#define I2C_DEVICE_WM8776	0x34	/* 001101, 0, /W=0 */
+
 #define LC_CONTROL_LIMITER	0x40000000
 #define LC_CONTROL_LIMITER	0x40000000
 #define LC_CONTROL_ALC		0x20000000
 #define LC_CONTROL_ALC		0x20000000
 
 
@@ -66,19 +95,37 @@ struct xonar_wm87x6 {
 	struct snd_kcontrol *mic_adcmux_control;
 	struct snd_kcontrol *mic_adcmux_control;
 	struct snd_kcontrol *lc_controls[13];
 	struct snd_kcontrol *lc_controls[13];
 	struct snd_jack *hp_jack;
 	struct snd_jack *hp_jack;
+	struct xonar_hdmi hdmi;
 };
 };
 
 
-static void wm8776_write(struct oxygen *chip,
-			 unsigned int reg, unsigned int value)
+static void wm8776_write_spi(struct oxygen *chip,
+			     unsigned int reg, unsigned int value)
 {
 {
-	struct xonar_wm87x6 *data = chip->model_data;
-
 	oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
 	oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
 			 OXYGEN_SPI_DATA_LENGTH_2 |
 			 OXYGEN_SPI_DATA_LENGTH_2 |
 			 OXYGEN_SPI_CLOCK_160 |
 			 OXYGEN_SPI_CLOCK_160 |
 			 (1 << OXYGEN_SPI_CODEC_SHIFT) |
 			 (1 << OXYGEN_SPI_CODEC_SHIFT) |
 			 OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
 			 OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
 			 (reg << 9) | value);
 			 (reg << 9) | value);
+}
+
+static void wm8776_write_i2c(struct oxygen *chip,
+			     unsigned int reg, unsigned int value)
+{
+	oxygen_write_i2c(chip, I2C_DEVICE_WM8776,
+			 (reg << 1) | (value >> 8), value);
+}
+
+static void wm8776_write(struct oxygen *chip,
+			 unsigned int reg, unsigned int value)
+{
+	struct xonar_wm87x6 *data = chip->model_data;
+
+	if ((chip->model.function_flags & OXYGEN_FUNCTION_2WIRE_SPI_MASK) ==
+	    OXYGEN_FUNCTION_SPI)
+		wm8776_write_spi(chip, reg, value);
+	else
+		wm8776_write_i2c(chip, reg, value);
 	if (reg < ARRAY_SIZE(data->wm8776_regs)) {
 	if (reg < ARRAY_SIZE(data->wm8776_regs)) {
 		if (reg >= WM8776_HPLVOL && reg <= WM8776_DACMASTER)
 		if (reg >= WM8776_HPLVOL && reg <= WM8776_DACMASTER)
 			value &= ~WM8776_UPDATE;
 			value &= ~WM8776_UPDATE;
@@ -245,17 +292,50 @@ static void xonar_ds_init(struct oxygen *chip)
 	snd_component_add(chip->card, "WM8766");
 	snd_component_add(chip->card, "WM8766");
 }
 }
 
 
+static void xonar_hdav_slim_init(struct oxygen *chip)
+{
+	struct xonar_wm87x6 *data = chip->model_data;
+
+	data->generic.anti_pop_delay = 300;
+	data->generic.output_enable_bit = GPIO_SLIM_OUTPUT_ENABLE;
+
+	wm8776_init(chip);
+
+	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+			  GPIO_SLIM_HDMI_DISABLE |
+			  GPIO_SLIM_FIRMWARE_CLK |
+			  GPIO_SLIM_FIRMWARE_DATA);
+
+	xonar_hdmi_init(chip, &data->hdmi);
+	xonar_enable_output(chip);
+
+	snd_component_add(chip->card, "WM8776");
+}
+
 static void xonar_ds_cleanup(struct oxygen *chip)
 static void xonar_ds_cleanup(struct oxygen *chip)
 {
 {
 	xonar_disable_output(chip);
 	xonar_disable_output(chip);
 	wm8776_write(chip, WM8776_RESET, 0);
 	wm8776_write(chip, WM8776_RESET, 0);
 }
 }
 
 
+static void xonar_hdav_slim_cleanup(struct oxygen *chip)
+{
+	xonar_hdmi_cleanup(chip);
+	xonar_disable_output(chip);
+	wm8776_write(chip, WM8776_RESET, 0);
+	msleep(2);
+}
+
 static void xonar_ds_suspend(struct oxygen *chip)
 static void xonar_ds_suspend(struct oxygen *chip)
 {
 {
 	xonar_ds_cleanup(chip);
 	xonar_ds_cleanup(chip);
 }
 }
 
 
+static void xonar_hdav_slim_suspend(struct oxygen *chip)
+{
+	xonar_hdav_slim_cleanup(chip);
+}
+
 static void xonar_ds_resume(struct oxygen *chip)
 static void xonar_ds_resume(struct oxygen *chip)
 {
 {
 	wm8776_registers_init(chip);
 	wm8776_registers_init(chip);
@@ -264,6 +344,15 @@ static void xonar_ds_resume(struct oxygen *chip)
 	xonar_ds_handle_hp_jack(chip);
 	xonar_ds_handle_hp_jack(chip);
 }
 }
 
 
+static void xonar_hdav_slim_resume(struct oxygen *chip)
+{
+	struct xonar_wm87x6 *data = chip->model_data;
+
+	wm8776_registers_init(chip);
+	xonar_hdmi_resume(chip, &data->hdmi);
+	xonar_enable_output(chip);
+}
+
 static void wm8776_adc_hardware_filter(unsigned int channel,
 static void wm8776_adc_hardware_filter(unsigned int channel,
 				       struct snd_pcm_hardware *hardware)
 				       struct snd_pcm_hardware *hardware)
 {
 {
@@ -278,6 +367,13 @@ static void wm8776_adc_hardware_filter(unsigned int channel,
 	}
 	}
 }
 }
 
 
+static void xonar_hdav_slim_hardware_filter(unsigned int channel,
+					    struct snd_pcm_hardware *hardware)
+{
+	wm8776_adc_hardware_filter(channel, hardware);
+	xonar_hdmi_pcm_hardware_filter(channel, hardware);
+}
+
 static void set_wm87x6_dac_params(struct oxygen *chip,
 static void set_wm87x6_dac_params(struct oxygen *chip,
 				  struct snd_pcm_hw_params *params)
 				  struct snd_pcm_hw_params *params)
 {
 {
@@ -294,6 +390,14 @@ static void set_wm8776_adc_params(struct oxygen *chip,
 	wm8776_write_cached(chip, WM8776_MSTRCTRL, reg);
 	wm8776_write_cached(chip, WM8776_MSTRCTRL, reg);
 }
 }
 
 
+static void set_hdav_slim_dac_params(struct oxygen *chip,
+				     struct snd_pcm_hw_params *params)
+{
+	struct xonar_wm87x6 *data = chip->model_data;
+
+	xonar_set_hdmi_params(chip, &data->hdmi, params);
+}
+
 static void update_wm8776_volume(struct oxygen *chip)
 static void update_wm8776_volume(struct oxygen *chip)
 {
 {
 	struct xonar_wm87x6 *data = chip->model_data;
 	struct xonar_wm87x6 *data = chip->model_data;
@@ -473,11 +577,6 @@ static int wm8776_field_enum_info(struct snd_kcontrol *ctl,
 	const char *const *names;
 	const char *const *names;
 
 
 	max = (ctl->private_value >> 12) & 0xf;
 	max = (ctl->private_value >> 12) & 0xf;
-	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-	info->count = 1;
-	info->value.enumerated.items = max + 1;
-	if (info->value.enumerated.item > max)
-		info->value.enumerated.item = max;
 	switch ((ctl->private_value >> 24) & 0x1f) {
 	switch ((ctl->private_value >> 24) & 0x1f) {
 	case WM8776_ALCCTRL2:
 	case WM8776_ALCCTRL2:
 		names = hld;
 		names = hld;
@@ -501,8 +600,7 @@ static int wm8776_field_enum_info(struct snd_kcontrol *ctl,
 	default:
 	default:
 		return -ENXIO;
 		return -ENXIO;
 	}
 	}
-	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
-	return 0;
+	return snd_ctl_enum_info(info, 1, max + 1, names);
 }
 }
 
 
 static int wm8776_field_volume_info(struct snd_kcontrol *ctl,
 static int wm8776_field_volume_info(struct snd_kcontrol *ctl,
@@ -759,13 +857,8 @@ static int wm8776_level_control_info(struct snd_kcontrol *ctl,
 	static const char *const names[3] = {
 	static const char *const names[3] = {
 		"None", "Peak Limiter", "Automatic Level Control"
 		"None", "Peak Limiter", "Automatic Level Control"
 	};
 	};
-	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-	info->count = 1;
-	info->value.enumerated.items = 3;
-	if (info->value.enumerated.item >= 3)
-		info->value.enumerated.item = 2;
-	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
-	return 0;
+
+	return snd_ctl_enum_info(info, 1, 3, names);
 }
 }
 
 
 static int wm8776_level_control_get(struct snd_kcontrol *ctl,
 static int wm8776_level_control_get(struct snd_kcontrol *ctl,
@@ -851,13 +944,7 @@ static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
 		"None", "High-pass Filter"
 		"None", "High-pass Filter"
 	};
 	};
 
 
-	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-	info->count = 1;
-	info->value.enumerated.items = 2;
-	if (info->value.enumerated.item >= 2)
-		info->value.enumerated.item = 1;
-	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
-	return 0;
+	return snd_ctl_enum_info(info, 1, 2, names);
 }
 }
 
 
 static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
 static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
@@ -985,6 +1072,53 @@ static const struct snd_kcontrol_new ds_controls[] = {
 		.private_value = 0,
 		.private_value = 0,
 	},
 	},
 };
 };
+static const struct snd_kcontrol_new hdav_slim_controls[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "HDMI Playback Switch",
+		.info = snd_ctl_boolean_mono_info,
+		.get = xonar_gpio_bit_switch_get,
+		.put = xonar_gpio_bit_switch_put,
+		.private_value = GPIO_SLIM_HDMI_DISABLE | XONAR_GPIO_BIT_INVERT,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Headphone Playback Volume",
+		.info = wm8776_hp_vol_info,
+		.get = wm8776_hp_vol_get,
+		.put = wm8776_hp_vol_put,
+		.tlv = { .p = wm8776_hp_db_scale },
+	},
+	WM8776_BIT_SWITCH("Headphone Playback Switch",
+			  WM8776_PWRDOWN, WM8776_HPPD, 1, 0),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Input Capture Volume",
+		.info = wm8776_input_vol_info,
+		.get = wm8776_input_vol_get,
+		.put = wm8776_input_vol_put,
+		.tlv = { .p = wm8776_adc_db_scale },
+	},
+	WM8776_BIT_SWITCH("Mic Capture Switch",
+			  WM8776_ADCMUX, 1 << 0, 0, 0),
+	WM8776_BIT_SWITCH("Aux Capture Switch",
+			  WM8776_ADCMUX, 1 << 1, 0, 0),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "ADC Filter Capture Enum",
+		.info = hpf_info,
+		.get = hpf_get,
+		.put = hpf_put,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Level Control Capture Enum",
+		.info = wm8776_level_control_info,
+		.get = wm8776_level_control_get,
+		.put = wm8776_level_control_put,
+		.private_value = 0,
+	},
+};
 static const struct snd_kcontrol_new lc_controls[] = {
 static const struct snd_kcontrol_new lc_controls[] = {
 	WM8776_FIELD_CTL_VOLUME("Limiter Threshold",
 	WM8776_FIELD_CTL_VOLUME("Limiter Threshold",
 				WM8776_ALCCTRL1, 0, 11, 0, 15, 0xf,
 				WM8776_ALCCTRL1, 0, 11, 0, 15, 0xf,
@@ -1028,6 +1162,26 @@ static const struct snd_kcontrol_new lc_controls[] = {
 				LC_CONTROL_ALC, wm8776_ngth_db_scale),
 				LC_CONTROL_ALC, wm8776_ngth_db_scale),
 };
 };
 
 
+static int add_lc_controls(struct oxygen *chip)
+{
+	struct xonar_wm87x6 *data = chip->model_data;
+	unsigned int i;
+	struct snd_kcontrol *ctl;
+	int err;
+
+	BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls));
+	for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) {
+		ctl = snd_ctl_new1(&lc_controls[i], chip);
+		if (!ctl)
+			return -ENOMEM;
+		err = snd_ctl_add(chip->card, ctl);
+		if (err < 0)
+			return err;
+		data->lc_controls[i] = ctl;
+	}
+	return 0;
+}
+
 static int xonar_ds_mixer_init(struct oxygen *chip)
 static int xonar_ds_mixer_init(struct oxygen *chip)
 {
 {
 	struct xonar_wm87x6 *data = chip->model_data;
 	struct xonar_wm87x6 *data = chip->model_data;
@@ -1049,17 +1203,54 @@ static int xonar_ds_mixer_init(struct oxygen *chip)
 	}
 	}
 	if (!data->line_adcmux_control || !data->mic_adcmux_control)
 	if (!data->line_adcmux_control || !data->mic_adcmux_control)
 		return -ENXIO;
 		return -ENXIO;
-	BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls));
-	for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) {
-		ctl = snd_ctl_new1(&lc_controls[i], chip);
+
+	return add_lc_controls(chip);
+}
+
+static int xonar_hdav_slim_mixer_init(struct oxygen *chip)
+{
+	unsigned int i;
+	struct snd_kcontrol *ctl;
+	int err;
+
+	for (i = 0; i < ARRAY_SIZE(hdav_slim_controls); ++i) {
+		ctl = snd_ctl_new1(&hdav_slim_controls[i], chip);
 		if (!ctl)
 		if (!ctl)
 			return -ENOMEM;
 			return -ENOMEM;
 		err = snd_ctl_add(chip->card, ctl);
 		err = snd_ctl_add(chip->card, ctl);
 		if (err < 0)
 		if (err < 0)
 			return err;
 			return err;
-		data->lc_controls[i] = ctl;
 	}
 	}
-	return 0;
+
+	return add_lc_controls(chip);
+}
+
+static void dump_wm8776_registers(struct oxygen *chip,
+				  struct snd_info_buffer *buffer)
+{
+	struct xonar_wm87x6 *data = chip->model_data;
+	unsigned int i;
+
+	snd_iprintf(buffer, "\nWM8776:\n00:");
+	for (i = 0; i < 0x10; ++i)
+		snd_iprintf(buffer, " %03x", data->wm8776_regs[i]);
+	snd_iprintf(buffer, "\n10:");
+	for (i = 0x10; i < 0x17; ++i)
+		snd_iprintf(buffer, " %03x", data->wm8776_regs[i]);
+	snd_iprintf(buffer, "\n");
+}
+
+static void dump_wm87x6_registers(struct oxygen *chip,
+				  struct snd_info_buffer *buffer)
+{
+	struct xonar_wm87x6 *data = chip->model_data;
+	unsigned int i;
+
+	dump_wm8776_registers(chip, buffer);
+	snd_iprintf(buffer, "\nWM8766:\n00:");
+	for (i = 0; i < 0x10; ++i)
+		snd_iprintf(buffer, " %03x", data->wm8766_regs[i]);
+	snd_iprintf(buffer, "\n");
 }
 }
 
 
 static const struct oxygen_model model_xonar_ds = {
 static const struct oxygen_model model_xonar_ds = {
@@ -1072,22 +1263,57 @@ static const struct oxygen_model model_xonar_ds = {
 	.suspend = xonar_ds_suspend,
 	.suspend = xonar_ds_suspend,
 	.resume = xonar_ds_resume,
 	.resume = xonar_ds_resume,
 	.pcm_hardware_filter = wm8776_adc_hardware_filter,
 	.pcm_hardware_filter = wm8776_adc_hardware_filter,
-	.get_i2s_mclk = oxygen_default_i2s_mclk,
 	.set_dac_params = set_wm87x6_dac_params,
 	.set_dac_params = set_wm87x6_dac_params,
 	.set_adc_params = set_wm8776_adc_params,
 	.set_adc_params = set_wm8776_adc_params,
 	.update_dac_volume = update_wm87x6_volume,
 	.update_dac_volume = update_wm87x6_volume,
 	.update_dac_mute = update_wm87x6_mute,
 	.update_dac_mute = update_wm87x6_mute,
 	.update_center_lfe_mix = update_wm8766_center_lfe_mix,
 	.update_center_lfe_mix = update_wm8766_center_lfe_mix,
 	.gpio_changed = xonar_ds_gpio_changed,
 	.gpio_changed = xonar_ds_gpio_changed,
+	.dump_registers = dump_wm87x6_registers,
 	.dac_tlv = wm87x6_dac_db_scale,
 	.dac_tlv = wm87x6_dac_db_scale,
 	.model_data_size = sizeof(struct xonar_wm87x6),
 	.model_data_size = sizeof(struct xonar_wm87x6),
 	.device_config = PLAYBACK_0_TO_I2S |
 	.device_config = PLAYBACK_0_TO_I2S |
 			 PLAYBACK_1_TO_SPDIF |
 			 PLAYBACK_1_TO_SPDIF |
 			 CAPTURE_0_FROM_I2S_1,
 			 CAPTURE_0_FROM_I2S_1,
-	.dac_channels = 8,
+	.dac_channels_pcm = 8,
+	.dac_channels_mixer = 8,
 	.dac_volume_min = 255 - 2*60,
 	.dac_volume_min = 255 - 2*60,
 	.dac_volume_max = 255,
 	.dac_volume_max = 255,
 	.function_flags = OXYGEN_FUNCTION_SPI,
 	.function_flags = OXYGEN_FUNCTION_SPI,
+	.dac_mclks = OXYGEN_MCLKS(256, 256, 128),
+	.adc_mclks = OXYGEN_MCLKS(256, 256, 128),
+	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
+
+static const struct oxygen_model model_xonar_hdav_slim = {
+	.shortname = "Xonar HDAV1.3 Slim",
+	.longname = "Asus Virtuoso 200",
+	.chip = "AV200",
+	.init = xonar_hdav_slim_init,
+	.mixer_init = xonar_hdav_slim_mixer_init,
+	.cleanup = xonar_hdav_slim_cleanup,
+	.suspend = xonar_hdav_slim_suspend,
+	.resume = xonar_hdav_slim_resume,
+	.pcm_hardware_filter = xonar_hdav_slim_hardware_filter,
+	.set_dac_params = set_hdav_slim_dac_params,
+	.set_adc_params = set_wm8776_adc_params,
+	.update_dac_volume = update_wm8776_volume,
+	.update_dac_mute = update_wm8776_mute,
+	.uart_input = xonar_hdmi_uart_input,
+	.dump_registers = dump_wm8776_registers,
+	.dac_tlv = wm87x6_dac_db_scale,
+	.model_data_size = sizeof(struct xonar_wm87x6),
+	.device_config = PLAYBACK_0_TO_I2S |
+			 PLAYBACK_1_TO_SPDIF |
+			 CAPTURE_0_FROM_I2S_1,
+	.dac_channels_pcm = 8,
+	.dac_channels_mixer = 2,
+	.dac_volume_min = 255 - 2*60,
+	.dac_volume_max = 255,
+	.function_flags = OXYGEN_FUNCTION_2WIRE,
+	.dac_mclks = OXYGEN_MCLKS(256, 256, 128),
+	.adc_mclks = OXYGEN_MCLKS(256, 256, 128),
 	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 };
 };
@@ -1099,6 +1325,9 @@ int __devinit get_xonar_wm87x6_model(struct oxygen *chip,
 	case 0x838e:
 	case 0x838e:
 		chip->model = model_xonar_ds;
 		chip->model = model_xonar_ds;
 		break;
 		break;
+	case 0x835e:
+		chip->model = model_xonar_hdav_slim;
+		break;
 	default:
 	default:
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}

+ 481 - 57
sound/pci/rme9652/hdsp.c

@@ -60,6 +60,7 @@ MODULE_SUPPORTED_DEVICE("{{RME Hammerfall-DSP},"
 	        "{RME HDSP-9652},"
 	        "{RME HDSP-9652},"
 		"{RME HDSP-9632}}");
 		"{RME HDSP-9632}}");
 #ifdef HDSP_FW_LOADER
 #ifdef HDSP_FW_LOADER
+MODULE_FIRMWARE("rpm_firmware.bin");
 MODULE_FIRMWARE("multiface_firmware.bin");
 MODULE_FIRMWARE("multiface_firmware.bin");
 MODULE_FIRMWARE("multiface_firmware_rev11.bin");
 MODULE_FIRMWARE("multiface_firmware_rev11.bin");
 MODULE_FIRMWARE("digiface_firmware.bin");
 MODULE_FIRMWARE("digiface_firmware.bin");
@@ -81,6 +82,7 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin");
 #define H9632_SS_CHANNELS	 12
 #define H9632_SS_CHANNELS	 12
 #define H9632_DS_CHANNELS	 8
 #define H9632_DS_CHANNELS	 8
 #define H9632_QS_CHANNELS	 4
 #define H9632_QS_CHANNELS	 4
+#define RPM_CHANNELS             6
 
 
 /* Write registers. These are defined as byte-offsets from the iobase value.
 /* Write registers. These are defined as byte-offsets from the iobase value.
  */
  */
@@ -191,6 +193,25 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin");
 #define HDSP_PhoneGain1		  (1<<30)
 #define HDSP_PhoneGain1		  (1<<30)
 #define HDSP_QuadSpeed	  	  (1<<31)
 #define HDSP_QuadSpeed	  	  (1<<31)
 
 
+/* RPM uses some of the registers for special purposes */
+#define HDSP_RPM_Inp12            0x04A00
+#define HDSP_RPM_Inp12_Phon_6dB   0x00800  /* Dolby */
+#define HDSP_RPM_Inp12_Phon_0dB   0x00000  /* .. */
+#define HDSP_RPM_Inp12_Phon_n6dB  0x04000  /* inp_0 */
+#define HDSP_RPM_Inp12_Line_0dB   0x04200  /* Dolby+PRO */
+#define HDSP_RPM_Inp12_Line_n6dB  0x00200  /* PRO */
+
+#define HDSP_RPM_Inp34            0x32000
+#define HDSP_RPM_Inp34_Phon_6dB   0x20000  /* SyncRef1 */
+#define HDSP_RPM_Inp34_Phon_0dB   0x00000  /* .. */
+#define HDSP_RPM_Inp34_Phon_n6dB  0x02000  /* SyncRef2 */
+#define HDSP_RPM_Inp34_Line_0dB   0x30000  /* SyncRef1+SyncRef0 */
+#define HDSP_RPM_Inp34_Line_n6dB  0x10000  /* SyncRef0 */
+
+#define HDSP_RPM_Bypass           0x01000
+
+#define HDSP_RPM_Disconnect       0x00001
+
 #define HDSP_ADGainMask       (HDSP_ADGain0|HDSP_ADGain1)
 #define HDSP_ADGainMask       (HDSP_ADGain0|HDSP_ADGain1)
 #define HDSP_ADGainMinus10dBV  HDSP_ADGainMask
 #define HDSP_ADGainMinus10dBV  HDSP_ADGainMask
 #define HDSP_ADGainPlus4dBu   (HDSP_ADGain0)
 #define HDSP_ADGainPlus4dBu   (HDSP_ADGain0)
@@ -450,7 +471,7 @@ struct hdsp {
 	u32                   creg_spdif;
 	u32                   creg_spdif;
 	u32                   creg_spdif_stream;
 	u32                   creg_spdif_stream;
 	int                   clock_source_locked;
 	int                   clock_source_locked;
-	char                 *card_name;	     /* digiface/multiface */
+	char                 *card_name;	 /* digiface/multiface/rpm */
 	enum HDSP_IO_Type     io_type;               /* ditto, but for code use */
 	enum HDSP_IO_Type     io_type;               /* ditto, but for code use */
         unsigned short        firmware_rev;
         unsigned short        firmware_rev;
 	unsigned short	      state;		     /* stores state bits */
 	unsigned short	      state;		     /* stores state bits */
@@ -612,6 +633,7 @@ static int hdsp_playback_to_output_key (struct hdsp *hdsp, int in, int out)
 	switch (hdsp->io_type) {
 	switch (hdsp->io_type) {
 	case Multiface:
 	case Multiface:
 	case Digiface:
 	case Digiface:
+	case RPM:
 	default:
 	default:
 		if (hdsp->firmware_rev == 0xa)
 		if (hdsp->firmware_rev == 0xa)
 			return (64 * out) + (32 + (in));
 			return (64 * out) + (32 + (in));
@@ -629,6 +651,7 @@ static int hdsp_input_to_output_key (struct hdsp *hdsp, int in, int out)
 	switch (hdsp->io_type) {
 	switch (hdsp->io_type) {
 	case Multiface:
 	case Multiface:
 	case Digiface:
 	case Digiface:
+	case RPM:
 	default:
 	default:
 		if (hdsp->firmware_rev == 0xa)
 		if (hdsp->firmware_rev == 0xa)
 			return (64 * out) + in;
 			return (64 * out) + in;
@@ -655,7 +678,7 @@ static int hdsp_check_for_iobox (struct hdsp *hdsp)
 {
 {
 	if (hdsp->io_type == H9652 || hdsp->io_type == H9632) return 0;
 	if (hdsp->io_type == H9652 || hdsp->io_type == H9632) return 0;
 	if (hdsp_read (hdsp, HDSP_statusRegister) & HDSP_ConfigError) {
 	if (hdsp_read (hdsp, HDSP_statusRegister) & HDSP_ConfigError) {
-		snd_printk ("Hammerfall-DSP: no Digiface or Multiface connected!\n");
+		snd_printk("Hammerfall-DSP: no IO box connected!\n");
 		hdsp->state &= ~HDSP_FirmwareLoaded;
 		hdsp->state &= ~HDSP_FirmwareLoaded;
 		return -EIO;
 		return -EIO;
 	}
 	}
@@ -680,7 +703,7 @@ static int hdsp_wait_for_iobox(struct hdsp *hdsp, unsigned int loops,
 		}
 		}
 	}
 	}
 
 
-	snd_printk("Hammerfall-DSP: no Digiface or Multiface connected!\n");
+	snd_printk("Hammerfall-DSP: no IO box connected!\n");
 	hdsp->state &= ~HDSP_FirmwareLoaded;
 	hdsp->state &= ~HDSP_FirmwareLoaded;
 	return -EIO;
 	return -EIO;
 }
 }
@@ -752,17 +775,21 @@ static int hdsp_get_iobox_version (struct hdsp *hdsp)
 		hdsp_write (hdsp, HDSP_control2Reg, HDSP_S_LOAD);
 		hdsp_write (hdsp, HDSP_control2Reg, HDSP_S_LOAD);
 		hdsp_write (hdsp, HDSP_fifoData, 0);
 		hdsp_write (hdsp, HDSP_fifoData, 0);
 
 
-		if (hdsp_fifo_wait (hdsp, 0, HDSP_SHORT_WAIT)) {
-			hdsp->io_type = Multiface;
-			hdsp_write (hdsp, HDSP_control2Reg, HDSP_VERSION_BIT);
-			hdsp_write (hdsp, HDSP_control2Reg, HDSP_S_LOAD);
-			hdsp_fifo_wait (hdsp, 0, HDSP_SHORT_WAIT);
+		if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT)) {
+			hdsp_write(hdsp, HDSP_control2Reg, HDSP_VERSION_BIT);
+			hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD);
+			if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT))
+				hdsp->io_type = RPM;
+			else
+				hdsp->io_type = Multiface;
 		} else {
 		} else {
 			hdsp->io_type = Digiface;
 			hdsp->io_type = Digiface;
 		}
 		}
 	} else {
 	} else {
 		/* firmware was already loaded, get iobox type */
 		/* firmware was already loaded, get iobox type */
-		if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version1)
+		if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version2)
+			hdsp->io_type = RPM;
+		else if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version1)
 			hdsp->io_type = Multiface;
 			hdsp->io_type = Multiface;
 		else
 		else
 			hdsp->io_type = Digiface;
 			hdsp->io_type = Digiface;
@@ -1184,6 +1211,7 @@ static int hdsp_set_rate(struct hdsp *hdsp, int rate, int called_internally)
 			hdsp->channel_map = channel_map_ds;
 			hdsp->channel_map = channel_map_ds;
 	} else {
 	} else {
 		switch (hdsp->io_type) {
 		switch (hdsp->io_type) {
+		case RPM:
 		case Multiface:
 		case Multiface:
 			hdsp->channel_map = channel_map_mf_ss;
 			hdsp->channel_map = channel_map_mf_ss;
 			break;
 			break;
@@ -3231,6 +3259,318 @@ HDSP_PRECISE_POINTER("Precise Pointer", 0),
 HDSP_USE_MIDI_TASKLET("Use Midi Tasklet", 0),
 HDSP_USE_MIDI_TASKLET("Use Midi Tasklet", 0),
 };
 };
 
 
+
+static int hdsp_rpm_input12(struct hdsp *hdsp)
+{
+	switch (hdsp->control_register & HDSP_RPM_Inp12) {
+	case HDSP_RPM_Inp12_Phon_6dB:
+		return 0;
+	case HDSP_RPM_Inp12_Phon_n6dB:
+		return 2;
+	case HDSP_RPM_Inp12_Line_0dB:
+		return 3;
+	case HDSP_RPM_Inp12_Line_n6dB:
+		return 4;
+	}
+	return 1;
+}
+
+
+static int snd_hdsp_get_rpm_input12(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.enumerated.item[0] = hdsp_rpm_input12(hdsp);
+	return 0;
+}
+
+
+static int hdsp_set_rpm_input12(struct hdsp *hdsp, int mode)
+{
+	hdsp->control_register &= ~HDSP_RPM_Inp12;
+	switch (mode) {
+	case 0:
+		hdsp->control_register |= HDSP_RPM_Inp12_Phon_6dB;
+		break;
+	case 1:
+		break;
+	case 2:
+		hdsp->control_register |= HDSP_RPM_Inp12_Phon_n6dB;
+		break;
+	case 3:
+		hdsp->control_register |= HDSP_RPM_Inp12_Line_0dB;
+		break;
+	case 4:
+		hdsp->control_register |= HDSP_RPM_Inp12_Line_n6dB;
+		break;
+	default:
+		return -1;
+	}
+
+	hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+	return 0;
+}
+
+
+static int snd_hdsp_put_rpm_input12(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+	int change;
+	int val;
+
+	if (!snd_hdsp_use_is_exclusive(hdsp))
+		return -EBUSY;
+	val = ucontrol->value.enumerated.item[0];
+	if (val < 0)
+		val = 0;
+	if (val > 4)
+		val = 4;
+	spin_lock_irq(&hdsp->lock);
+	if (val != hdsp_rpm_input12(hdsp))
+		change = (hdsp_set_rpm_input12(hdsp, val) == 0) ? 1 : 0;
+	else
+		change = 0;
+	spin_unlock_irq(&hdsp->lock);
+	return change;
+}
+
+
+static int snd_hdsp_info_rpm_input(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[] = {"Phono +6dB", "Phono 0dB", "Phono -6dB", "Line 0dB", "Line -6dB"};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 5;
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+
+static int hdsp_rpm_input34(struct hdsp *hdsp)
+{
+	switch (hdsp->control_register & HDSP_RPM_Inp34) {
+	case HDSP_RPM_Inp34_Phon_6dB:
+		return 0;
+	case HDSP_RPM_Inp34_Phon_n6dB:
+		return 2;
+	case HDSP_RPM_Inp34_Line_0dB:
+		return 3;
+	case HDSP_RPM_Inp34_Line_n6dB:
+		return 4;
+	}
+	return 1;
+}
+
+
+static int snd_hdsp_get_rpm_input34(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.enumerated.item[0] = hdsp_rpm_input34(hdsp);
+	return 0;
+}
+
+
+static int hdsp_set_rpm_input34(struct hdsp *hdsp, int mode)
+{
+	hdsp->control_register &= ~HDSP_RPM_Inp34;
+	switch (mode) {
+	case 0:
+		hdsp->control_register |= HDSP_RPM_Inp34_Phon_6dB;
+		break;
+	case 1:
+		break;
+	case 2:
+		hdsp->control_register |= HDSP_RPM_Inp34_Phon_n6dB;
+		break;
+	case 3:
+		hdsp->control_register |= HDSP_RPM_Inp34_Line_0dB;
+		break;
+	case 4:
+		hdsp->control_register |= HDSP_RPM_Inp34_Line_n6dB;
+		break;
+	default:
+		return -1;
+	}
+
+	hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+	return 0;
+}
+
+
+static int snd_hdsp_put_rpm_input34(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+	int change;
+	int val;
+
+	if (!snd_hdsp_use_is_exclusive(hdsp))
+		return -EBUSY;
+	val = ucontrol->value.enumerated.item[0];
+	if (val < 0)
+		val = 0;
+	if (val > 4)
+		val = 4;
+	spin_lock_irq(&hdsp->lock);
+	if (val != hdsp_rpm_input34(hdsp))
+		change = (hdsp_set_rpm_input34(hdsp, val) == 0) ? 1 : 0;
+	else
+		change = 0;
+	spin_unlock_irq(&hdsp->lock);
+	return change;
+}
+
+
+/* RPM Bypass switch */
+static int hdsp_rpm_bypass(struct hdsp *hdsp)
+{
+	return (hdsp->control_register & HDSP_RPM_Bypass) ? 1 : 0;
+}
+
+
+static int snd_hdsp_get_rpm_bypass(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] = hdsp_rpm_bypass(hdsp);
+	return 0;
+}
+
+
+static int hdsp_set_rpm_bypass(struct hdsp *hdsp, int on)
+{
+	if (on)
+		hdsp->control_register |= HDSP_RPM_Bypass;
+	else
+		hdsp->control_register &= ~HDSP_RPM_Bypass;
+	hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+	return 0;
+}
+
+
+static int snd_hdsp_put_rpm_bypass(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+	int change;
+	unsigned int val;
+
+	if (!snd_hdsp_use_is_exclusive(hdsp))
+		return -EBUSY;
+	val = ucontrol->value.integer.value[0] & 1;
+	spin_lock_irq(&hdsp->lock);
+	change = (int)val != hdsp_rpm_bypass(hdsp);
+	hdsp_set_rpm_bypass(hdsp, val);
+	spin_unlock_irq(&hdsp->lock);
+	return change;
+}
+
+
+static int snd_hdsp_info_rpm_bypass(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[] = {"On", "Off"};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 2;
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+
+/* RPM Disconnect switch */
+static int hdsp_rpm_disconnect(struct hdsp *hdsp)
+{
+	return (hdsp->control_register & HDSP_RPM_Disconnect) ? 1 : 0;
+}
+
+
+static int snd_hdsp_get_rpm_disconnect(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] = hdsp_rpm_disconnect(hdsp);
+	return 0;
+}
+
+
+static int hdsp_set_rpm_disconnect(struct hdsp *hdsp, int on)
+{
+	if (on)
+		hdsp->control_register |= HDSP_RPM_Disconnect;
+	else
+		hdsp->control_register &= ~HDSP_RPM_Disconnect;
+	hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+	return 0;
+}
+
+
+static int snd_hdsp_put_rpm_disconnect(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+	int change;
+	unsigned int val;
+
+	if (!snd_hdsp_use_is_exclusive(hdsp))
+		return -EBUSY;
+	val = ucontrol->value.integer.value[0] & 1;
+	spin_lock_irq(&hdsp->lock);
+	change = (int)val != hdsp_rpm_disconnect(hdsp);
+	hdsp_set_rpm_disconnect(hdsp, val);
+	spin_unlock_irq(&hdsp->lock);
+	return change;
+}
+
+static int snd_hdsp_info_rpm_disconnect(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[] = {"On", "Off"};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 2;
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static struct snd_kcontrol_new snd_hdsp_rpm_controls[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "RPM Bypass",
+		.get = snd_hdsp_get_rpm_bypass,
+		.put = snd_hdsp_put_rpm_bypass,
+		.info = snd_hdsp_info_rpm_bypass
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "RPM Disconnect",
+		.get = snd_hdsp_get_rpm_disconnect,
+		.put = snd_hdsp_put_rpm_disconnect,
+		.info = snd_hdsp_info_rpm_disconnect
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Input 1/2",
+		.get = snd_hdsp_get_rpm_input12,
+		.put = snd_hdsp_put_rpm_input12,
+		.info = snd_hdsp_info_rpm_input
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Input 3/4",
+		.get = snd_hdsp_get_rpm_input34,
+		.put = snd_hdsp_put_rpm_input34,
+		.info = snd_hdsp_info_rpm_input
+	},
+	HDSP_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
+	HDSP_MIXER("Mixer", 0)
+};
+
 static struct snd_kcontrol_new snd_hdsp_96xx_aeb = HDSP_AEB("Analog Extension Board", 0);
 static struct snd_kcontrol_new snd_hdsp_96xx_aeb = HDSP_AEB("Analog Extension Board", 0);
 static struct snd_kcontrol_new snd_hdsp_adat_sync_check = HDSP_ADAT_SYNC_CHECK;
 static struct snd_kcontrol_new snd_hdsp_adat_sync_check = HDSP_ADAT_SYNC_CHECK;
 
 
@@ -3240,6 +3580,16 @@ static int snd_hdsp_create_controls(struct snd_card *card, struct hdsp *hdsp)
 	int err;
 	int err;
 	struct snd_kcontrol *kctl;
 	struct snd_kcontrol *kctl;
 
 
+	if (hdsp->io_type == RPM) {
+		/* RPM Bypass, Disconnect and Input switches */
+		for (idx = 0; idx < ARRAY_SIZE(snd_hdsp_rpm_controls); idx++) {
+			err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_hdsp_rpm_controls[idx], hdsp));
+			if (err < 0)
+				return err;
+		}
+		return 0;
+	}
+
 	for (idx = 0; idx < ARRAY_SIZE(snd_hdsp_controls); idx++) {
 	for (idx = 0; idx < ARRAY_SIZE(snd_hdsp_controls); idx++) {
 		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_hdsp_controls[idx], hdsp))) < 0)
 		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_hdsp_controls[idx], hdsp))) < 0)
 			return err;
 			return err;
@@ -3459,48 +3809,102 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
 
 
 	snd_iprintf(buffer, "\n");
 	snd_iprintf(buffer, "\n");
 
 
-	switch (hdsp_spdif_in(hdsp)) {
-	case HDSP_SPDIFIN_OPTICAL:
-		snd_iprintf(buffer, "IEC958 input: Optical\n");
-		break;
-	case HDSP_SPDIFIN_COAXIAL:
-		snd_iprintf(buffer, "IEC958 input: Coaxial\n");
-		break;
-	case HDSP_SPDIFIN_INTERNAL:
-		snd_iprintf(buffer, "IEC958 input: Internal\n");
-		break;
-	case HDSP_SPDIFIN_AES:
-		snd_iprintf(buffer, "IEC958 input: AES\n");
-		break;
-	default:
-		snd_iprintf(buffer, "IEC958 input: ???\n");
-		break;
+	if (hdsp->io_type != RPM) {
+		switch (hdsp_spdif_in(hdsp)) {
+		case HDSP_SPDIFIN_OPTICAL:
+			snd_iprintf(buffer, "IEC958 input: Optical\n");
+			break;
+		case HDSP_SPDIFIN_COAXIAL:
+			snd_iprintf(buffer, "IEC958 input: Coaxial\n");
+			break;
+		case HDSP_SPDIFIN_INTERNAL:
+			snd_iprintf(buffer, "IEC958 input: Internal\n");
+			break;
+		case HDSP_SPDIFIN_AES:
+			snd_iprintf(buffer, "IEC958 input: AES\n");
+			break;
+		default:
+			snd_iprintf(buffer, "IEC958 input: ???\n");
+			break;
+		}
 	}
 	}
 
 
-	if (hdsp->control_register & HDSP_SPDIFOpticalOut)
-		snd_iprintf(buffer, "IEC958 output: Coaxial & ADAT1\n");
-	else
-		snd_iprintf(buffer, "IEC958 output: Coaxial only\n");
+	if (RPM == hdsp->io_type) {
+		if (hdsp->control_register & HDSP_RPM_Bypass)
+			snd_iprintf(buffer, "RPM Bypass: disabled\n");
+		else
+			snd_iprintf(buffer, "RPM Bypass: enabled\n");
+		if (hdsp->control_register & HDSP_RPM_Disconnect)
+			snd_iprintf(buffer, "RPM disconnected\n");
+		else
+			snd_iprintf(buffer, "RPM connected\n");
 
 
-	if (hdsp->control_register & HDSP_SPDIFProfessional)
-		snd_iprintf(buffer, "IEC958 quality: Professional\n");
-	else
-		snd_iprintf(buffer, "IEC958 quality: Consumer\n");
+		switch (hdsp->control_register & HDSP_RPM_Inp12) {
+		case HDSP_RPM_Inp12_Phon_6dB:
+			snd_iprintf(buffer, "Input 1/2: Phono, 6dB\n");
+			break;
+		case HDSP_RPM_Inp12_Phon_0dB:
+			snd_iprintf(buffer, "Input 1/2: Phono, 0dB\n");
+			break;
+		case HDSP_RPM_Inp12_Phon_n6dB:
+			snd_iprintf(buffer, "Input 1/2: Phono, -6dB\n");
+			break;
+		case HDSP_RPM_Inp12_Line_0dB:
+			snd_iprintf(buffer, "Input 1/2: Line, 0dB\n");
+			break;
+		case HDSP_RPM_Inp12_Line_n6dB:
+			snd_iprintf(buffer, "Input 1/2: Line, -6dB\n");
+			break;
+		default:
+			snd_iprintf(buffer, "Input 1/2: ???\n");
+		}
 
 
-	if (hdsp->control_register & HDSP_SPDIFEmphasis)
-		snd_iprintf(buffer, "IEC958 emphasis: on\n");
-	else
-		snd_iprintf(buffer, "IEC958 emphasis: off\n");
+		switch (hdsp->control_register & HDSP_RPM_Inp34) {
+		case HDSP_RPM_Inp34_Phon_6dB:
+			snd_iprintf(buffer, "Input 3/4: Phono, 6dB\n");
+			break;
+		case HDSP_RPM_Inp34_Phon_0dB:
+			snd_iprintf(buffer, "Input 3/4: Phono, 0dB\n");
+			break;
+		case HDSP_RPM_Inp34_Phon_n6dB:
+			snd_iprintf(buffer, "Input 3/4: Phono, -6dB\n");
+			break;
+		case HDSP_RPM_Inp34_Line_0dB:
+			snd_iprintf(buffer, "Input 3/4: Line, 0dB\n");
+			break;
+		case HDSP_RPM_Inp34_Line_n6dB:
+			snd_iprintf(buffer, "Input 3/4: Line, -6dB\n");
+			break;
+		default:
+			snd_iprintf(buffer, "Input 3/4: ???\n");
+		}
 
 
-	if (hdsp->control_register & HDSP_SPDIFNonAudio)
-		snd_iprintf(buffer, "IEC958 NonAudio: on\n");
-	else
-		snd_iprintf(buffer, "IEC958 NonAudio: off\n");
-	if ((x = hdsp_spdif_sample_rate (hdsp)) != 0)
-		snd_iprintf (buffer, "IEC958 sample rate: %d\n", x);
-	else
-		snd_iprintf (buffer, "IEC958 sample rate: Error flag set\n");
+	} else {
+		if (hdsp->control_register & HDSP_SPDIFOpticalOut)
+			snd_iprintf(buffer, "IEC958 output: Coaxial & ADAT1\n");
+		else
+			snd_iprintf(buffer, "IEC958 output: Coaxial only\n");
+
+		if (hdsp->control_register & HDSP_SPDIFProfessional)
+			snd_iprintf(buffer, "IEC958 quality: Professional\n");
+		else
+			snd_iprintf(buffer, "IEC958 quality: Consumer\n");
+
+		if (hdsp->control_register & HDSP_SPDIFEmphasis)
+			snd_iprintf(buffer, "IEC958 emphasis: on\n");
+		else
+			snd_iprintf(buffer, "IEC958 emphasis: off\n");
 
 
+		if (hdsp->control_register & HDSP_SPDIFNonAudio)
+			snd_iprintf(buffer, "IEC958 NonAudio: on\n");
+		else
+			snd_iprintf(buffer, "IEC958 NonAudio: off\n");
+		x = hdsp_spdif_sample_rate(hdsp);
+		if (x != 0)
+			snd_iprintf(buffer, "IEC958 sample rate: %d\n", x);
+		else
+			snd_iprintf(buffer, "IEC958 sample rate: Error flag set\n");
+	}
 	snd_iprintf(buffer, "\n");
 	snd_iprintf(buffer, "\n");
 
 
 	/* Sync Check */
 	/* Sync Check */
@@ -3765,7 +4169,7 @@ static irqreturn_t snd_hdsp_interrupt(int irq, void *dev_id)
 			snd_hdsp_midi_input_read (&hdsp->midi[0]);
 			snd_hdsp_midi_input_read (&hdsp->midi[0]);
 		}
 		}
 	}
 	}
-	if (hdsp->io_type != Multiface && hdsp->io_type != H9632 && midi1 && midi1status) {
+	if (hdsp->io_type != Multiface && hdsp->io_type != RPM && hdsp->io_type != H9632 && midi1 && midi1status) {
 		if (hdsp->use_midi_tasklet) {
 		if (hdsp->use_midi_tasklet) {
 			/* we disable interrupts for this input until processing is done */
 			/* we disable interrupts for this input until processing is done */
 			hdsp->control_register &= ~HDSP_Midi1InterruptEnable;
 			hdsp->control_register &= ~HDSP_Midi1InterruptEnable;
@@ -4093,7 +4497,7 @@ static struct snd_pcm_hardware snd_hdsp_playback_subinfo =
 				 SNDRV_PCM_RATE_96000),
 				 SNDRV_PCM_RATE_96000),
 	.rate_min =		32000,
 	.rate_min =		32000,
 	.rate_max =		96000,
 	.rate_max =		96000,
-	.channels_min =		14,
+	.channels_min =		6,
 	.channels_max =		HDSP_MAX_CHANNELS,
 	.channels_max =		HDSP_MAX_CHANNELS,
 	.buffer_bytes_max =	HDSP_CHANNEL_BUFFER_BYTES * HDSP_MAX_CHANNELS,
 	.buffer_bytes_max =	HDSP_CHANNEL_BUFFER_BYTES * HDSP_MAX_CHANNELS,
 	.period_bytes_min =	(64 * 4) * 10,
 	.period_bytes_min =	(64 * 4) * 10,
@@ -4122,7 +4526,7 @@ static struct snd_pcm_hardware snd_hdsp_capture_subinfo =
 				 SNDRV_PCM_RATE_96000),
 				 SNDRV_PCM_RATE_96000),
 	.rate_min =		32000,
 	.rate_min =		32000,
 	.rate_max =		96000,
 	.rate_max =		96000,
-	.channels_min =		14,
+	.channels_min =		5,
 	.channels_max =		HDSP_MAX_CHANNELS,
 	.channels_max =		HDSP_MAX_CHANNELS,
 	.buffer_bytes_max =	HDSP_CHANNEL_BUFFER_BYTES * HDSP_MAX_CHANNELS,
 	.buffer_bytes_max =	HDSP_CHANNEL_BUFFER_BYTES * HDSP_MAX_CHANNELS,
 	.period_bytes_min =	(64 * 4) * 10,
 	.period_bytes_min =	(64 * 4) * 10,
@@ -4357,10 +4761,12 @@ static int snd_hdsp_playback_open(struct snd_pcm_substream *substream)
 			     snd_hdsp_hw_rule_rate_out_channels, hdsp,
 			     snd_hdsp_hw_rule_rate_out_channels, hdsp,
 			     SNDRV_PCM_HW_PARAM_CHANNELS, -1);
 			     SNDRV_PCM_HW_PARAM_CHANNELS, -1);
 
 
-	hdsp->creg_spdif_stream = hdsp->creg_spdif;
-	hdsp->spdif_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
-	snd_ctl_notify(hdsp->card, SNDRV_CTL_EVENT_MASK_VALUE |
-		       SNDRV_CTL_EVENT_MASK_INFO, &hdsp->spdif_ctl->id);
+	if (RPM != hdsp->io_type) {
+		hdsp->creg_spdif_stream = hdsp->creg_spdif;
+		hdsp->spdif_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+		snd_ctl_notify(hdsp->card, SNDRV_CTL_EVENT_MASK_VALUE |
+			SNDRV_CTL_EVENT_MASK_INFO, &hdsp->spdif_ctl->id);
+	}
 	return 0;
 	return 0;
 }
 }
 
 
@@ -4375,9 +4781,11 @@ static int snd_hdsp_playback_release(struct snd_pcm_substream *substream)
 
 
 	spin_unlock_irq(&hdsp->lock);
 	spin_unlock_irq(&hdsp->lock);
 
 
-	hdsp->spdif_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
-	snd_ctl_notify(hdsp->card, SNDRV_CTL_EVENT_MASK_VALUE |
-		       SNDRV_CTL_EVENT_MASK_INFO, &hdsp->spdif_ctl->id);
+	if (RPM != hdsp->io_type) {
+		hdsp->spdif_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+		snd_ctl_notify(hdsp->card, SNDRV_CTL_EVENT_MASK_VALUE |
+			SNDRV_CTL_EVENT_MASK_INFO, &hdsp->spdif_ctl->id);
+	}
 	return 0;
 	return 0;
 }
 }
 
 
@@ -4616,7 +5024,7 @@ static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigne
 		if (hdsp->io_type != H9632)
 		if (hdsp->io_type != H9632)
 		    info.adatsync_sync_check = (unsigned char)hdsp_adatsync_sync_check(hdsp);
 		    info.adatsync_sync_check = (unsigned char)hdsp_adatsync_sync_check(hdsp);
 		info.spdif_sync_check = (unsigned char)hdsp_spdif_sync_check(hdsp);
 		info.spdif_sync_check = (unsigned char)hdsp_spdif_sync_check(hdsp);
-		for (i = 0; i < ((hdsp->io_type != Multiface && hdsp->io_type != H9632) ? 3 : 1); ++i)
+		for (i = 0; i < ((hdsp->io_type != Multiface && hdsp->io_type != RPM && hdsp->io_type != H9632) ? 3 : 1); ++i)
 			info.adat_sync_check[i] = (unsigned char)hdsp_adat_sync_check(hdsp, i);
 			info.adat_sync_check[i] = (unsigned char)hdsp_adat_sync_check(hdsp, i);
 		info.spdif_in = (unsigned char)hdsp_spdif_in(hdsp);
 		info.spdif_in = (unsigned char)hdsp_spdif_in(hdsp);
 		info.spdif_out = (unsigned char)hdsp_spdif_out(hdsp);
 		info.spdif_out = (unsigned char)hdsp_spdif_out(hdsp);
@@ -4636,6 +5044,9 @@ static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigne
 			info.phone_gain = (unsigned char)hdsp_phone_gain(hdsp);
 			info.phone_gain = (unsigned char)hdsp_phone_gain(hdsp);
 			info.xlr_breakout_cable = (unsigned char)hdsp_xlr_breakout_cable(hdsp);
 			info.xlr_breakout_cable = (unsigned char)hdsp_xlr_breakout_cable(hdsp);
 
 
+		} else if (hdsp->io_type == RPM) {
+			info.da_gain = (unsigned char) hdsp_rpm_input12(hdsp);
+			info.ad_gain = (unsigned char) hdsp_rpm_input34(hdsp);
 		}
 		}
 		if (hdsp->io_type == H9632 || hdsp->io_type == H9652)
 		if (hdsp->io_type == H9632 || hdsp->io_type == H9652)
 			info.analog_extension_board = (unsigned char)hdsp_aeb(hdsp);
 			info.analog_extension_board = (unsigned char)hdsp_aeb(hdsp);
@@ -4844,6 +5255,14 @@ static void snd_hdsp_initialize_channels(struct hdsp *hdsp)
 		hdsp->ds_in_channels = hdsp->ds_out_channels = MULTIFACE_DS_CHANNELS;
 		hdsp->ds_in_channels = hdsp->ds_out_channels = MULTIFACE_DS_CHANNELS;
 		break;
 		break;
 
 
+	case RPM:
+		hdsp->card_name = "RME Hammerfall DSP + RPM";
+		hdsp->ss_in_channels = RPM_CHANNELS-1;
+		hdsp->ss_out_channels = RPM_CHANNELS;
+		hdsp->ds_in_channels = RPM_CHANNELS-1;
+		hdsp->ds_out_channels = RPM_CHANNELS;
+		break;
+
 	default:
 	default:
  		/* should never get here */
  		/* should never get here */
 		break;
 		break;
@@ -4930,6 +5349,9 @@ static int hdsp_request_fw_loader(struct hdsp *hdsp)
 
 
 	/* caution: max length of firmware filename is 30! */
 	/* caution: max length of firmware filename is 30! */
 	switch (hdsp->io_type) {
 	switch (hdsp->io_type) {
+	case RPM:
+		fwfile = "rpm_firmware.bin";
+		break;
 	case Multiface:
 	case Multiface:
 		if (hdsp->firmware_rev == 0xa)
 		if (hdsp->firmware_rev == 0xa)
 			fwfile = "multiface_firmware.bin";
 			fwfile = "multiface_firmware.bin";
@@ -5100,7 +5522,9 @@ static int __devinit snd_hdsp_create(struct snd_card *card,
 			return 0;
 			return 0;
 		} else {
 		} else {
 			snd_printk(KERN_INFO "Hammerfall-DSP: Firmware already present, initializing card.\n");
 			snd_printk(KERN_INFO "Hammerfall-DSP: Firmware already present, initializing card.\n");
-			if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version1)
+			if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version2)
+				hdsp->io_type = RPM;
+			else if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version1)
 				hdsp->io_type = Multiface;
 				hdsp->io_type = Multiface;
 			else
 			else
 				hdsp->io_type = Digiface;
 				hdsp->io_type = Digiface;

+ 3 - 9
sound/pci/ymfpci/ymfpci_main.c

@@ -1389,15 +1389,9 @@ static struct snd_kcontrol_new snd_ymfpci_spdif_stream __devinitdata =
 
 
 static int snd_ymfpci_drec_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *info)
 static int snd_ymfpci_drec_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *info)
 {
 {
-	static char *texts[3] = {"AC'97", "IEC958", "ZV Port"};
-
-	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-	info->count = 1;
-	info->value.enumerated.items = 3;
-	if (info->value.enumerated.item > 2)
-		info->value.enumerated.item = 2;
-	strcpy(info->value.enumerated.name, texts[info->value.enumerated.item]);
-	return 0;
+	static const char *const texts[3] = {"AC'97", "IEC958", "ZV Port"};
+
+	return snd_ctl_enum_info(info, 1, 3, texts);
 }
 }
 
 
 static int snd_ymfpci_drec_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value)
 static int snd_ymfpci_drec_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value)

+ 16 - 1
sound/soc/Kconfig

@@ -20,6 +20,21 @@ menuconfig SND_SOC
 
 
 if SND_SOC
 if SND_SOC
 
 
+config SND_SOC_CACHE_LZO
+	bool "Support LZO compression for register caches"
+	select LZO_COMPRESS
+	select LZO_DECOMPRESS
+	---help---
+	   Select this to enable LZO compression for register caches.
+	   This will allow machine or CODEC drivers to compress register
+	   caches in memory, reducing the memory consumption at the
+	   expense of performance.  If this is not present and is used
+	   the system will fall back to uncompressed caches.
+
+	   Usually it is safe to disable this option, where cache
+	   compression in used the rbtree option will typically perform
+	   better.
+
 config SND_SOC_AC97_BUS
 config SND_SOC_AC97_BUS
 	bool
 	bool
 
 
@@ -36,7 +51,7 @@ source "sound/soc/nuc900/Kconfig"
 source "sound/soc/omap/Kconfig"
 source "sound/soc/omap/Kconfig"
 source "sound/soc/kirkwood/Kconfig"
 source "sound/soc/kirkwood/Kconfig"
 source "sound/soc/pxa/Kconfig"
 source "sound/soc/pxa/Kconfig"
-source "sound/soc/s3c24xx/Kconfig"
+source "sound/soc/samsung/Kconfig"
 source "sound/soc/s6000/Kconfig"
 source "sound/soc/s6000/Kconfig"
 source "sound/soc/sh/Kconfig"
 source "sound/soc/sh/Kconfig"
 source "sound/soc/txx9/Kconfig"
 source "sound/soc/txx9/Kconfig"

+ 1 - 1
sound/soc/Makefile

@@ -14,7 +14,7 @@ obj-$(CONFIG_SND_SOC)	+= nuc900/
 obj-$(CONFIG_SND_SOC)	+= omap/
 obj-$(CONFIG_SND_SOC)	+= omap/
 obj-$(CONFIG_SND_SOC)	+= kirkwood/
 obj-$(CONFIG_SND_SOC)	+= kirkwood/
 obj-$(CONFIG_SND_SOC)	+= pxa/
 obj-$(CONFIG_SND_SOC)	+= pxa/
-obj-$(CONFIG_SND_SOC)	+= s3c24xx/
+obj-$(CONFIG_SND_SOC)	+= samsung/
 obj-$(CONFIG_SND_SOC)	+= s6000/
 obj-$(CONFIG_SND_SOC)	+= s6000/
 obj-$(CONFIG_SND_SOC)	+= sh/
 obj-$(CONFIG_SND_SOC)	+= sh/
 obj-$(CONFIG_SND_SOC)	+= txx9/
 obj-$(CONFIG_SND_SOC)	+= txx9/

+ 6 - 6
sound/soc/atmel/playpaq_wm8510.c

@@ -33,7 +33,6 @@
 #include <sound/pcm.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/soc.h>
-#include <sound/soc-dapm.h>
 
 
 #include <mach/at32ap700x.h>
 #include <mach/at32ap700x.h>
 #include <mach/portmux.h>
 #include <mach/portmux.h>
@@ -318,27 +317,28 @@ static const struct snd_soc_dapm_route intercon[] = {
 static int playpaq_wm8510_init(struct snd_soc_pcm_runtime *rtd)
 static int playpaq_wm8510_init(struct snd_soc_pcm_runtime *rtd)
 {
 {
 	struct snd_soc_codec *codec = rtd->codec;
 	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	int i;
 	int i;
 
 
 	/*
 	/*
 	 * Add DAPM widgets
 	 * Add DAPM widgets
 	 */
 	 */
 	for (i = 0; i < ARRAY_SIZE(playpaq_dapm_widgets); i++)
 	for (i = 0; i < ARRAY_SIZE(playpaq_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &playpaq_dapm_widgets[i]);
+		snd_soc_dapm_new_control(dapm, &playpaq_dapm_widgets[i]);
 
 
 
 
 
 
 	/*
 	/*
 	 * Setup audio path interconnects
 	 * Setup audio path interconnects
 	 */
 	 */
-	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+	snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
 
 
 
 
 
 
 	/* always connected pins */
 	/* always connected pins */
-	snd_soc_dapm_enable_pin(codec, "Int Mic");
-	snd_soc_dapm_enable_pin(codec, "Ext Spk");
-	snd_soc_dapm_sync(codec);
+	snd_soc_dapm_enable_pin(dapm, "Int Mic");
+	snd_soc_dapm_enable_pin(dapm, "Ext Spk");
+	snd_soc_dapm_sync(dapm);
 
 
 
 
 
 

部分文件因为文件数量过多而无法显示