Browse Source

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: (294 commits)
  S3C64XX: Staticise platform data for PCM devices
  ASoC: Rename controls with a / in wm_hubs
  snd-fm801: autodetect SF64-PCR (tuner-only) card
  ALSA: tea575x-tuner: fix mute
  ASoC: au1x: dbdma2: plug memleak in pcm device creation error path
  ASoC: au1x: dbdma2: fix oops on soc device removal.
  ALSA: hda - Fix memory leaks in the previous patch
  ALSA: hda - Add ALC661/259, ALC892/888VD support
  ALSA: opti9xx: remove snd_opti9xx fields
  ALSA: aaci - Clean up duplicate code
  ALSA: usb - Fix mixer map for Hercules Gamesurround Muse Pocket LT
  ALSA: hda - Add position_fix quirk for HP dv3
  ALSA: hda - Add a pin-fix for FSC Amilo Pi1505
  ALSA: hda - Fix Cxt5047 test mode
  ASoC: pxa/raumfeld: adopt new snd_soc_dai_set_pll() API
  ASoC: sh: fsi: Add runtime PM support
  sh: ms7724se: Add runtime PM support for FSI
  ALSA: hda - Add a position_fix quirk for MSI Wind U115
  ALSA: opti-miro: add PnP detection
  ALSA: opti-miro: separate comon probing code
  ...
Linus Torvalds 16 years ago
parent
commit
a421018e8c
100 changed files with 4390 additions and 2990 deletions
  1. 9 4
      Documentation/sound/alsa/ALSA-Configuration.txt
  2. 2 1
      Documentation/sound/alsa/ControlNames.txt
  3. 1 0
      Documentation/sound/alsa/HD-Audio-Models.txt
  4. 8 0
      arch/arm/mach-davinci/include/mach/asp.h
  5. 10 0
      arch/arm/mach-omap2/board-3430sdp.c
  6. 10 0
      arch/arm/mach-omap2/board-omap3beagle.c
  7. 10 0
      arch/arm/mach-omap2/board-omap3evm.c
  8. 10 0
      arch/arm/mach-omap2/board-omap3pandora.c
  9. 10 0
      arch/arm/mach-omap2/board-overo.c
  10. 10 0
      arch/arm/mach-omap2/board-zoom2.c
  11. 2 0
      arch/arm/mach-s3c6400/include/mach/map.h
  12. 10 38
      arch/arm/plat-s3c/include/plat/audio.h
  13. 3 0
      arch/arm/plat-s3c/include/plat/devs.h
  14. 2 0
      arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h
  15. 100 1
      arch/arm/plat-s3c64xx/dev-audio.c
  16. 55 0
      arch/sh/boards/mach-hp6xx/setup.c
  17. 3 0
      arch/sh/boards/mach-se/7724/setup.c
  18. 4 0
      arch/sh/include/mach-common/mach/hp6xx.h
  19. 18 0
      drivers/media/radio/Kconfig
  20. 1 0
      drivers/media/radio/Makefile
  21. 270 0
      drivers/media/radio/radio-miropcm20.c
  22. 6 0
      drivers/mfd/Kconfig
  23. 1 0
      drivers/mfd/Makefile
  24. 276 0
      drivers/mfd/twl4030-codec.c
  25. 15 1
      drivers/mfd/twl4030-core.c
  26. 19 0
      include/linux/i2c/twl4030.h
  27. 272 0
      include/linux/mfd/twl4030-codec.h
  28. 0 1
      include/sound/Kbuild
  29. 20 3
      include/sound/aci.h
  30. 321 0
      include/sound/ak4113.h
  31. 6 6
      include/sound/ak4114.h
  32. 4 1
      include/sound/ak4xxx-adda.h
  33. 3 2
      include/sound/control.h
  34. 0 1
      include/sound/cs4231-regs.h
  35. 3 0
      include/sound/pcm.h
  36. 2 0
      include/sound/rawmidi.h
  37. 21 0
      include/sound/sh_dac_audio.h
  38. 11 3
      include/sound/soc-dai.h
  39. 17 0
      include/sound/soc-dapm.h
  40. 9 6
      include/sound/soc.h
  41. 0 21
      include/sound/sscape_ioctl.h
  42. 20 0
      include/sound/tlv320dac33-plat.h
  43. 30 0
      include/sound/tpa6130a2-plat.h
  44. 0 1
      include/sound/wss.h
  45. 1 1
      sound/Kconfig
  46. 1 1
      sound/arm/Makefile
  47. 10 25
      sound/arm/aaci.c
  48. 0 80
      sound/arm/devdma.c
  49. 0 3
      sound/arm/devdma.h
  50. 4 5
      sound/core/control.c
  51. 9 1
      sound/core/isadma.c
  52. 3 1
      sound/core/oss/mixer_oss.c
  53. 5 1
      sound/core/pcm.c
  54. 59 24
      sound/core/pcm_native.c
  55. 12 5
      sound/core/rawmidi.c
  56. 20 12
      sound/drivers/pcsp/pcsp.c
  57. 1 1
      sound/drivers/pcsp/pcsp.h
  58. 27 8
      sound/drivers/pcsp/pcsp_mixer.c
  59. 2 13
      sound/i2c/cs8427.c
  60. 2 1
      sound/i2c/other/Makefile
  61. 639 0
      sound/i2c/other/ak4113.c
  62. 104 32
      sound/i2c/other/ak4xxx-adda.c
  63. 1 1
      sound/i2c/other/tea575x-tuner.c
  64. 9 3
      sound/isa/Kconfig
  65. 2 2
      sound/isa/cmi8330.c
  66. 3 10
      sound/isa/cs423x/cs4236.c
  67. 155 86
      sound/isa/cs423x/cs4236_lib.c
  68. 1 1
      sound/isa/es1688/es1688_lib.c
  69. 102 119
      sound/isa/es18xx.c
  70. 510 273
      sound/isa/opti9xx/miro.c
  71. 43 67
      sound/isa/opti9xx/opti92x-ad1848.c
  72. 2 2
      sound/isa/sb/sb_mixer.c
  73. 319 408
      sound/isa/sscape.c
  74. 40 65
      sound/isa/wss/wss_lib.c
  75. 0 12
      sound/oss/Kconfig
  76. 0 1
      sound/oss/Makefile
  77. 1 1
      sound/oss/audio.c
  78. 1 1
      sound/oss/midi_synth.c
  79. 1 1
      sound/oss/mpu401.c
  80. 0 3
      sound/oss/sh_dac_audio.c
  81. 0 1480
      sound/oss/sscape.c
  82. 1 0
      sound/pci/Kconfig
  83. 3 3
      sound/pci/ac97/ac97_codec.c
  84. 6 6
      sound/pci/ac97/ac97_patch.c
  85. 2 2
      sound/pci/azt3328.c
  86. 2 2
      sound/pci/ca0106/ca0106_mixer.c
  87. 2 2
      sound/pci/ca0106/ca0106_proc.c
  88. 2 2
      sound/pci/cmipci.c
  89. 1 1
      sound/pci/ctxfi/ctatc.c
  90. 1 2
      sound/pci/emu10k1/emu10k1x.c
  91. 2 2
      sound/pci/emu10k1/emumixer.c
  92. 2 2
      sound/pci/emu10k1/emuproc.c
  93. 1 1
      sound/pci/emu10k1/io.c
  94. 1 1
      sound/pci/es1938.c
  95. 27 13
      sound/pci/fm801.c
  96. 12 1
      sound/pci/hda/Kconfig
  97. 96 18
      sound/pci/hda/hda_beep.c
  98. 10 0
      sound/pci/hda/hda_beep.h
  99. 515 92
      sound/pci/hda/hda_codec.c
  100. 11 0
      sound/pci/hda/hda_codec.h

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

@@ -798,6 +798,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     		setup before initializing the codecs.  This option is
     		setup before initializing the codecs.  This option is
 		available only when CONFIG_SND_HDA_PATCH_LOADER=y is set.
 		available only when CONFIG_SND_HDA_PATCH_LOADER=y is set.
 		See HD-Audio.txt for details.
 		See HD-Audio.txt for details.
+    beep_mode	- Selects the beep registration mode (0=off, 1=on, 2=
+		dynamic registration via mute switch on/off); the default
+		value is set via CONFIG_SND_HDA_INPUT_BEEP_MODE kconfig.
     
     
     [Single (global) options]
     [Single (global) options]
     single_cmd  - Use single immediate commands to communicate with
     single_cmd  - Use single immediate commands to communicate with
@@ -1454,6 +1457,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
 
     Module for internal PC-Speaker.
     Module for internal PC-Speaker.
 
 
+    nopcm	- Disable PC-Speaker PCM sound. Only beeps remain.
     nforce_wa	- enable NForce chipset workaround. Expect bad sound.
     nforce_wa	- enable NForce chipset workaround. Expect bad sound.
 
 
     This module supports system beeps, some kind of PCM playback and
     This module supports system beeps, some kind of PCM playback and
@@ -1631,7 +1635,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
   Module snd-sscape
   Module snd-sscape
   -----------------
   -----------------
 
 
-    Module for ENSONIQ SoundScape PnP cards.
+    Module for ENSONIQ SoundScape cards.
 
 
     port	- Port # (PnP setup)
     port	- Port # (PnP setup)
     wss_port	- WSS Port # (PnP setup)
     wss_port	- WSS Port # (PnP setup)
@@ -1639,10 +1643,11 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     mpu_irq	- MPU-401 IRQ # (PnP setup)
     mpu_irq	- MPU-401 IRQ # (PnP setup)
     dma		- DMA # (PnP setup)
     dma		- DMA # (PnP setup)
     dma2	- 2nd DMA # (PnP setup, -1 to disable)
     dma2	- 2nd DMA # (PnP setup, -1 to disable)
+    joystick	- Enable gameport - 0 = disable (default), 1 = enable
+
+    This module supports multiple cards.
 
 
-    This module supports multiple cards.  ISA PnP must be enabled.
-    You need sscape_ctl tool in alsa-tools package for loading
-    the microcode.
+    The driver requires the firmware loader support on kernel.
 
 
   Module snd-sun-amd7930 (on sparc only)
   Module snd-sun-amd7930 (on sparc only)
   --------------------------------------
   --------------------------------------

+ 2 - 1
Documentation/sound/alsa/ControlNames.txt

@@ -18,8 +18,9 @@ SOURCE:
   Master
   Master
   Master Mono
   Master Mono
   Hardware Master
   Hardware Master
+  Speaker	(internal speaker)
   Headphone
   Headphone
-  PC Speaker
+  Beep		(beep generator)
   Phone
   Phone
   Phone Input
   Phone Input
   Phone Output
   Phone Output

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

@@ -391,6 +391,7 @@ STAC92HD83*
   ref		Reference board
   ref		Reference board
   mic-ref	Reference board with power management for ports
   mic-ref	Reference board with power management for ports
   dell-s14	Dell laptop
   dell-s14	Dell laptop
+  hp		HP laptops with (inverted) mute-LED
   auto		BIOS setup (default)
   auto		BIOS setup (default)
 
 
 STAC9872
 STAC9872

+ 8 - 0
arch/arm/mach-davinci/include/mach/asp.h

@@ -51,6 +51,14 @@ struct snd_platform_data {
 	u32 rx_dma_offset;
 	u32 rx_dma_offset;
 	enum dma_event_q eventq_no;	/* event queue number */
 	enum dma_event_q eventq_no;	/* event queue number */
 	unsigned int codec_fmt;
 	unsigned int codec_fmt;
+	/*
+	 * Allowing this is more efficient and eliminates left and right swaps
+	 * caused by underruns, but will swap the left and right channels
+	 * when compared to previous behavior.
+	 */
+	unsigned enable_channel_combine:1;
+	unsigned sram_size_playback;
+	unsigned sram_size_capture;
 
 
 	/* McASP specific fields */
 	/* McASP specific fields */
 	int tdm_slots;
 	int tdm_slots;

+ 10 - 0
arch/arm/mach-omap2/board-3430sdp.c

@@ -410,6 +410,15 @@ static struct regulator_init_data sdp3430_vpll2 = {
 	.consumer_supplies	= &sdp3430_vdvi_supply,
 	.consumer_supplies	= &sdp3430_vdvi_supply,
 };
 };
 
 
+static struct twl4030_codec_audio_data sdp3430_audio = {
+	.audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data sdp3430_codec = {
+	.audio_mclk = 26000000,
+	.audio = &sdp3430_audio,
+};
+
 static struct twl4030_platform_data sdp3430_twldata = {
 static struct twl4030_platform_data sdp3430_twldata = {
 	.irq_base	= TWL4030_IRQ_BASE,
 	.irq_base	= TWL4030_IRQ_BASE,
 	.irq_end	= TWL4030_IRQ_END,
 	.irq_end	= TWL4030_IRQ_END,
@@ -420,6 +429,7 @@ static struct twl4030_platform_data sdp3430_twldata = {
 	.madc		= &sdp3430_madc_data,
 	.madc		= &sdp3430_madc_data,
 	.keypad		= &sdp3430_kp_data,
 	.keypad		= &sdp3430_kp_data,
 	.usb		= &sdp3430_usb_data,
 	.usb		= &sdp3430_usb_data,
+	.codec		= &sdp3430_codec,
 
 
 	.vaux1		= &sdp3430_vaux1,
 	.vaux1		= &sdp3430_vaux1,
 	.vaux2		= &sdp3430_vaux2,
 	.vaux2		= &sdp3430_vaux2,

+ 10 - 0
arch/arm/mach-omap2/board-omap3beagle.c

@@ -254,6 +254,15 @@ static struct twl4030_usb_data beagle_usb_data = {
 	.usb_mode	= T2_USB_MODE_ULPI,
 	.usb_mode	= T2_USB_MODE_ULPI,
 };
 };
 
 
+static struct twl4030_codec_audio_data beagle_audio_data = {
+	.audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data beagle_codec_data = {
+	.audio_mclk = 26000000,
+	.audio = &beagle_audio_data,
+};
+
 static struct twl4030_platform_data beagle_twldata = {
 static struct twl4030_platform_data beagle_twldata = {
 	.irq_base	= TWL4030_IRQ_BASE,
 	.irq_base	= TWL4030_IRQ_BASE,
 	.irq_end	= TWL4030_IRQ_END,
 	.irq_end	= TWL4030_IRQ_END,
@@ -261,6 +270,7 @@ static struct twl4030_platform_data beagle_twldata = {
 	/* platform_data for children goes here */
 	/* platform_data for children goes here */
 	.usb		= &beagle_usb_data,
 	.usb		= &beagle_usb_data,
 	.gpio		= &beagle_gpio_data,
 	.gpio		= &beagle_gpio_data,
+	.codec		= &beagle_codec_data,
 	.vmmc1		= &beagle_vmmc1,
 	.vmmc1		= &beagle_vmmc1,
 	.vsim		= &beagle_vsim,
 	.vsim		= &beagle_vsim,
 	.vdac		= &beagle_vdac,
 	.vdac		= &beagle_vdac,

+ 10 - 0
arch/arm/mach-omap2/board-omap3evm.c

@@ -194,6 +194,15 @@ static struct twl4030_madc_platform_data omap3evm_madc_data = {
 	.irq_line	= 1,
 	.irq_line	= 1,
 };
 };
 
 
+static struct twl4030_codec_audio_data omap3evm_audio_data = {
+	.audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data omap3evm_codec_data = {
+	.audio_mclk = 26000000,
+	.audio = &omap3evm_audio_data,
+};
+
 static struct twl4030_platform_data omap3evm_twldata = {
 static struct twl4030_platform_data omap3evm_twldata = {
 	.irq_base	= TWL4030_IRQ_BASE,
 	.irq_base	= TWL4030_IRQ_BASE,
 	.irq_end	= TWL4030_IRQ_END,
 	.irq_end	= TWL4030_IRQ_END,
@@ -203,6 +212,7 @@ static struct twl4030_platform_data omap3evm_twldata = {
 	.madc		= &omap3evm_madc_data,
 	.madc		= &omap3evm_madc_data,
 	.usb		= &omap3evm_usb_data,
 	.usb		= &omap3evm_usb_data,
 	.gpio		= &omap3evm_gpio_data,
 	.gpio		= &omap3evm_gpio_data,
+	.codec		= &omap3evm_codec_data,
 };
 };
 
 
 static struct i2c_board_info __initdata omap3evm_i2c_boardinfo[] = {
 static struct i2c_board_info __initdata omap3evm_i2c_boardinfo[] = {

+ 10 - 0
arch/arm/mach-omap2/board-omap3pandora.c

@@ -281,11 +281,21 @@ static struct twl4030_usb_data omap3pandora_usb_data = {
 	.usb_mode	= T2_USB_MODE_ULPI,
 	.usb_mode	= T2_USB_MODE_ULPI,
 };
 };
 
 
+static struct twl4030_codec_audio_data omap3pandora_audio_data = {
+	.audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data omap3pandora_codec_data = {
+	.audio_mclk = 26000000,
+	.audio = &omap3pandora_audio_data,
+};
+
 static struct twl4030_platform_data omap3pandora_twldata = {
 static struct twl4030_platform_data omap3pandora_twldata = {
 	.irq_base	= TWL4030_IRQ_BASE,
 	.irq_base	= TWL4030_IRQ_BASE,
 	.irq_end	= TWL4030_IRQ_END,
 	.irq_end	= TWL4030_IRQ_END,
 	.gpio		= &omap3pandora_gpio_data,
 	.gpio		= &omap3pandora_gpio_data,
 	.usb		= &omap3pandora_usb_data,
 	.usb		= &omap3pandora_usb_data,
+	.codec		= &omap3pandora_codec_data,
 	.vmmc1		= &pandora_vmmc1,
 	.vmmc1		= &pandora_vmmc1,
 	.vmmc2		= &pandora_vmmc2,
 	.vmmc2		= &pandora_vmmc2,
 	.keypad		= &pandora_kp_data,
 	.keypad		= &pandora_kp_data,

+ 10 - 0
arch/arm/mach-omap2/board-overo.c

@@ -329,6 +329,15 @@ static struct regulator_init_data overo_vmmc1 = {
 	.consumer_supplies	= &overo_vmmc1_supply,
 	.consumer_supplies	= &overo_vmmc1_supply,
 };
 };
 
 
+static struct twl4030_codec_audio_data overo_audio_data = {
+	.audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data overo_codec_data = {
+	.audio_mclk = 26000000,
+	.audio = &overo_audio_data,
+};
+
 /* mmc2 (WLAN) and Bluetooth don't use twl4030 regulators */
 /* mmc2 (WLAN) and Bluetooth don't use twl4030 regulators */
 
 
 static struct twl4030_platform_data overo_twldata = {
 static struct twl4030_platform_data overo_twldata = {
@@ -336,6 +345,7 @@ static struct twl4030_platform_data overo_twldata = {
 	.irq_end	= TWL4030_IRQ_END,
 	.irq_end	= TWL4030_IRQ_END,
 	.gpio		= &overo_gpio_data,
 	.gpio		= &overo_gpio_data,
 	.usb		= &overo_usb_data,
 	.usb		= &overo_usb_data,
+	.codec		= &overo_codec_data,
 	.vmmc1		= &overo_vmmc1,
 	.vmmc1		= &overo_vmmc1,
 };
 };
 
 

+ 10 - 0
arch/arm/mach-omap2/board-zoom2.c

@@ -230,6 +230,15 @@ static struct twl4030_madc_platform_data zoom2_madc_data = {
 	.irq_line	= 1,
 	.irq_line	= 1,
 };
 };
 
 
+static struct twl4030_codec_audio_data zoom2_audio_data = {
+	.audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data zoom2_codec_data = {
+	.audio_mclk = 26000000,
+	.audio = &zoom2_audio_data,
+};
+
 static struct twl4030_platform_data zoom2_twldata = {
 static struct twl4030_platform_data zoom2_twldata = {
 	.irq_base	= TWL4030_IRQ_BASE,
 	.irq_base	= TWL4030_IRQ_BASE,
 	.irq_end	= TWL4030_IRQ_END,
 	.irq_end	= TWL4030_IRQ_END,
@@ -240,6 +249,7 @@ static struct twl4030_platform_data zoom2_twldata = {
 	.usb		= &zoom2_usb_data,
 	.usb		= &zoom2_usb_data,
 	.gpio		= &zoom2_gpio_data,
 	.gpio		= &zoom2_gpio_data,
 	.keypad		= &zoom2_kp_twl4030_data,
 	.keypad		= &zoom2_kp_twl4030_data,
+	.codec		= &zoom2_codec_data,
 	.vmmc1          = &zoom2_vmmc1,
 	.vmmc1          = &zoom2_vmmc1,
 	.vmmc2          = &zoom2_vmmc2,
 	.vmmc2          = &zoom2_vmmc2,
 	.vsim           = &zoom2_vsim,
 	.vsim           = &zoom2_vsim,

+ 2 - 0
arch/arm/mach-s3c6400/include/mach/map.h

@@ -48,6 +48,8 @@
 #define S3C64XX_PA_IIS1		(0x7F003000)
 #define S3C64XX_PA_IIS1		(0x7F003000)
 #define S3C64XX_PA_TIMER	(0x7F006000)
 #define S3C64XX_PA_TIMER	(0x7F006000)
 #define S3C64XX_PA_IIC0		(0x7F004000)
 #define S3C64XX_PA_IIC0		(0x7F004000)
+#define S3C64XX_PA_PCM0		(0x7F009000)
+#define S3C64XX_PA_PCM1		(0x7F00A000)
 #define S3C64XX_PA_IISV4	(0x7F00D000)
 #define S3C64XX_PA_IISV4	(0x7F00D000)
 #define S3C64XX_PA_IIC1		(0x7F00F000)
 #define S3C64XX_PA_IIC1		(0x7F00F000)
 
 

+ 10 - 38
arch/arm/plat-s3c/include/plat/audio.h

@@ -1,45 +1,17 @@
-/* arch/arm/mach-s3c2410/include/mach/audio.h
+/* arch/arm/plat-s3c/include/plat/audio.h
  *
  *
- * Copyright (c) 2004-2005 Simtec Electronics
- *	http://www.simtec.co.uk/products/SWLINUX/
- *	Ben Dooks <ben@simtec.co.uk>
- *
- * S3C24XX - Audio platfrom_device info
+ * Copyright (c) 2009 Samsung Electronics Co. Ltd
+ * Author: Jaswinder Singh <jassi.brar@samsung.com>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  * published by the Free Software Foundation.
-*/
-
-#ifndef __ASM_ARCH_AUDIO_H
-#define __ASM_ARCH_AUDIO_H __FILE__
-
-/* struct s3c24xx_iis_ops
- *
- * called from the s3c24xx audio core to deal with the architecture
- * or the codec's setup and control.
- *
- * the pointer to itself is passed through in case the caller wants to
- * embed this in an larger structure for easy reference to it's context.
-*/
+ */
 
 
-struct s3c24xx_iis_ops {
-	struct module *owner;
-
-	int	(*startup)(struct s3c24xx_iis_ops *me);
-	void	(*shutdown)(struct s3c24xx_iis_ops *me);
-	int	(*suspend)(struct s3c24xx_iis_ops *me);
-	int	(*resume)(struct s3c24xx_iis_ops *me);
-
-	int	(*open)(struct s3c24xx_iis_ops *me, struct snd_pcm_substream *strm);
-	int	(*close)(struct s3c24xx_iis_ops *me, struct snd_pcm_substream *strm);
-	int	(*prepare)(struct s3c24xx_iis_ops *me, struct snd_pcm_substream *strm, struct snd_pcm_runtime *rt);
+/**
+ * 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
+ */
+struct s3c_audio_pdata {
+	int (*cfg_gpio)(struct platform_device *);
 };
 };
-
-struct s3c24xx_platdata_iis {
-	const char		*codec_clk;
-	struct s3c24xx_iis_ops	*ops;
-	int			(*match_dev)(struct device *dev);
-};
-
-#endif /* __ASM_ARCH_AUDIO_H */

+ 3 - 0
arch/arm/plat-s3c/include/plat/devs.h

@@ -28,6 +28,9 @@ extern struct platform_device s3c64xx_device_iis0;
 extern struct platform_device s3c64xx_device_iis1;
 extern struct platform_device s3c64xx_device_iis1;
 extern struct platform_device s3c64xx_device_iisv4;
 extern struct platform_device s3c64xx_device_iisv4;
 
 
+extern struct platform_device s3c64xx_device_pcm0;
+extern struct platform_device s3c64xx_device_pcm1;
+
 extern struct platform_device s3c_device_fb;
 extern struct platform_device s3c_device_fb;
 extern struct platform_device s3c_device_usb;
 extern struct platform_device s3c_device_usb;
 extern struct platform_device s3c_device_lcd;
 extern struct platform_device s3c_device_lcd;

+ 2 - 0
arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h

@@ -67,6 +67,8 @@
 #define S3C2412_IISMOD_BCLK_MASK	(3 << 1)
 #define S3C2412_IISMOD_BCLK_MASK	(3 << 1)
 #define S3C2412_IISMOD_8BIT		(1 << 0)
 #define S3C2412_IISMOD_8BIT		(1 << 0)
 
 
+#define S3C64XX_IISMOD_CDCLKCON		(1 << 12)
+
 #define S3C2412_IISPSR_PSREN		(1 << 15)
 #define S3C2412_IISPSR_PSREN		(1 << 15)
 
 
 #define S3C2412_IISFIC_TXFLUSH		(1 << 15)
 #define S3C2412_IISFIC_TXFLUSH		(1 << 15)

+ 100 - 1
arch/arm/plat-s3c64xx/dev-audio.c

@@ -15,9 +15,14 @@
 
 
 #include <mach/irqs.h>
 #include <mach/irqs.h>
 #include <mach/map.h>
 #include <mach/map.h>
+#include <mach/dma.h>
+#include <mach/gpio.h>
 
 
 #include <plat/devs.h>
 #include <plat/devs.h>
-
+#include <plat/audio.h>
+#include <plat/gpio-bank-d.h>
+#include <plat/gpio-bank-e.h>
+#include <plat/gpio-cfg.h>
 
 
 static struct resource s3c64xx_iis0_resource[] = {
 static struct resource s3c64xx_iis0_resource[] = {
 	[0] = {
 	[0] = {
@@ -66,3 +71,97 @@ struct platform_device s3c64xx_device_iisv4 = {
 	.resource	  = s3c64xx_iisv4_resource,
 	.resource	  = s3c64xx_iisv4_resource,
 };
 };
 EXPORT_SYMBOL(s3c64xx_device_iisv4);
 EXPORT_SYMBOL(s3c64xx_device_iisv4);
+
+
+/* PCM Controller platform_devices */
+
+static int s3c64xx_pcm_cfg_gpio(struct platform_device *pdev)
+{
+	switch (pdev->id) {
+	case 0:
+		s3c_gpio_cfgpin(S3C64XX_GPD(0), S3C64XX_GPD0_PCM0_SCLK);
+		s3c_gpio_cfgpin(S3C64XX_GPD(1), S3C64XX_GPD1_PCM0_EXTCLK);
+		s3c_gpio_cfgpin(S3C64XX_GPD(2), S3C64XX_GPD2_PCM0_FSYNC);
+		s3c_gpio_cfgpin(S3C64XX_GPD(3), S3C64XX_GPD3_PCM0_SIN);
+		s3c_gpio_cfgpin(S3C64XX_GPD(4), S3C64XX_GPD4_PCM0_SOUT);
+		break;
+	case 1:
+		s3c_gpio_cfgpin(S3C64XX_GPE(0), S3C64XX_GPE0_PCM1_SCLK);
+		s3c_gpio_cfgpin(S3C64XX_GPE(1), S3C64XX_GPE1_PCM1_EXTCLK);
+		s3c_gpio_cfgpin(S3C64XX_GPE(2), S3C64XX_GPE2_PCM1_FSYNC);
+		s3c_gpio_cfgpin(S3C64XX_GPE(3), S3C64XX_GPE3_PCM1_SIN);
+		s3c_gpio_cfgpin(S3C64XX_GPE(4), S3C64XX_GPE4_PCM1_SOUT);
+		break;
+	default:
+		printk(KERN_DEBUG "Invalid PCM Controller number!");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct resource s3c64xx_pcm0_resource[] = {
+	[0] = {
+		.start = S3C64XX_PA_PCM0,
+		.end   = S3C64XX_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,
+	},
+};
+
+static struct s3c_audio_pdata s3c_pcm0_pdata = {
+	.cfg_gpio = s3c64xx_pcm_cfg_gpio,
+};
+
+struct platform_device s3c64xx_device_pcm0 = {
+	.name		  = "samsung-pcm",
+	.id		  = 0,
+	.num_resources	  = ARRAY_SIZE(s3c64xx_pcm0_resource),
+	.resource	  = s3c64xx_pcm0_resource,
+	.dev = {
+		.platform_data = &s3c_pcm0_pdata,
+	},
+};
+EXPORT_SYMBOL(s3c64xx_device_pcm0);
+
+static struct resource s3c64xx_pcm1_resource[] = {
+	[0] = {
+		.start = S3C64XX_PA_PCM1,
+		.end   = S3C64XX_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,
+	},
+};
+
+static struct s3c_audio_pdata s3c_pcm1_pdata = {
+	.cfg_gpio = s3c64xx_pcm_cfg_gpio,
+};
+
+struct platform_device s3c64xx_device_pcm1 = {
+	.name		  = "samsung-pcm",
+	.id		  = 1,
+	.num_resources	  = ARRAY_SIZE(s3c64xx_pcm1_resource),
+	.resource	  = s3c64xx_pcm1_resource,
+	.dev = {
+		.platform_data = &s3c_pcm1_pdata,
+	},
+};
+EXPORT_SYMBOL(s3c64xx_device_pcm1);

+ 55 - 0
arch/sh/boards/mach-hp6xx/setup.c

@@ -13,6 +13,7 @@
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/platform_device.h>
 #include <linux/irq.h>
 #include <linux/irq.h>
+#include <sound/sh_dac_audio.h>
 #include <asm/hd64461.h>
 #include <asm/hd64461.h>
 #include <asm/io.h>
 #include <asm/io.h>
 #include <mach/hp6xx.h>
 #include <mach/hp6xx.h>
@@ -51,9 +52,63 @@ static struct platform_device jornadakbd_device = {
 	.id		= -1,
 	.id		= -1,
 };
 };
 
 
+static void dac_audio_start(struct dac_audio_pdata *pdata)
+{
+	u16 v;
+	u8 v8;
+
+	/* HP Jornada 680/690 speaker on */
+	v = inw(HD64461_GPADR);
+	v &= ~HD64461_GPADR_SPEAKER;
+	outw(v, HD64461_GPADR);
+
+	/* HP Palmtop 620lx/660lx speaker on */
+	v8 = inb(PKDR);
+	v8 &= ~PKDR_SPEAKER;
+	outb(v8, PKDR);
+
+	sh_dac_enable(pdata->channel);
+}
+
+static void dac_audio_stop(struct dac_audio_pdata *pdata)
+{
+	u16 v;
+	u8 v8;
+
+	/* HP Jornada 680/690 speaker off */
+	v = inw(HD64461_GPADR);
+	v |= HD64461_GPADR_SPEAKER;
+	outw(v, HD64461_GPADR);
+
+	/* HP Palmtop 620lx/660lx speaker off */
+	v8 = inb(PKDR);
+	v8 |= PKDR_SPEAKER;
+	outb(v8, PKDR);
+
+	sh_dac_output(0, pdata->channel);
+	sh_dac_disable(pdata->channel);
+}
+
+static struct dac_audio_pdata dac_audio_platform_data = {
+	.buffer_size		= 64000,
+	.channel		= 1,
+	.start			= dac_audio_start,
+	.stop			= dac_audio_stop,
+};
+
+static struct platform_device dac_audio_device = {
+	.name		= "dac_audio",
+	.id		= -1,
+	.dev		= {
+		.platform_data	= &dac_audio_platform_data,
+	}
+
+};
+
 static struct platform_device *hp6xx_devices[] __initdata = {
 static struct platform_device *hp6xx_devices[] __initdata = {
 	&cf_ide_device,
 	&cf_ide_device,
 	&jornadakbd_device,
 	&jornadakbd_device,
+	&dac_audio_device,
 };
 };
 
 
 static void __init hp6xx_init_irq(void)
 static void __init hp6xx_init_irq(void)

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

@@ -313,6 +313,9 @@ static struct platform_device fsi_device = {
 	.dev	= {
 	.dev	= {
 		.platform_data	= &fsi_info,
 		.platform_data	= &fsi_info,
 	},
 	},
+	.archdata = {
+		.hwblk_id = HWBLK_SPU, /* FSI needs SPU hwblk */
+	},
 };
 };
 
 
 /* KEYSC in SoC (Needs SW33-2 set to ON) */
 /* KEYSC in SoC (Needs SW33-2 set to ON) */

+ 4 - 0
arch/sh/include/mach-common/mach/hp6xx.h

@@ -29,6 +29,9 @@
 
 
 #define PKDR_LED_GREEN		0x10
 #define PKDR_LED_GREEN		0x10
 
 
+/* HP Palmtop 620lx/660lx speaker on/off */
+#define PKDR_SPEAKER		0x20
+
 #define SCPDR_TS_SCAN_ENABLE	0x20
 #define SCPDR_TS_SCAN_ENABLE	0x20
 #define SCPDR_TS_SCAN_Y		0x02
 #define SCPDR_TS_SCAN_Y		0x02
 #define SCPDR_TS_SCAN_X		0x01
 #define SCPDR_TS_SCAN_X		0x01
@@ -42,6 +45,7 @@
 #define ADC_CHANNEL_BACKUP	4
 #define ADC_CHANNEL_BACKUP	4
 #define ADC_CHANNEL_CHARGE	5
 #define ADC_CHANNEL_CHARGE	5
 
 
+/* HP Jornada 680/690 speaker on/off */
 #define HD64461_GPADR_SPEAKER	0x01
 #define HD64461_GPADR_SPEAKER	0x01
 #define HD64461_GPADR_PCMCIA0	(0x02|0x08)
 #define HD64461_GPADR_PCMCIA0	(0x02|0x08)
 
 

+ 18 - 0
drivers/media/radio/Kconfig

@@ -195,6 +195,24 @@ config RADIO_MAESTRO
 	  To compile this driver as a module, choose M here: the
 	  To compile this driver as a module, choose M here: the
 	  module will be called radio-maestro.
 	  module will be called radio-maestro.
 
 
+config RADIO_MIROPCM20
+	tristate "miroSOUND PCM20 radio"
+	depends on ISA && VIDEO_V4L2
+	select SND_MIRO
+	---help---
+	  Choose Y here if you have this FM radio card. You also need to enable
+	  the ALSA sound system. This choice automatically selects the ALSA
+	  sound card driver "Miro miroSOUND PCM1pro/PCM12/PCM20radio" as this
+	  is required for the radio-miropcm20.
+
+	  In order to control your radio card, you will need to use programs
+	  that are compatible with the Video For Linux API.  Information on
+	  this API and pointers to "v4l" programs may be found at
+	  <file:Documentation/video4linux/API.html>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called radio-miropcm20.
+
 config RADIO_SF16FMI
 config RADIO_SF16FMI
 	tristate "SF16FMI Radio"
 	tristate "SF16FMI Radio"
 	depends on ISA && VIDEO_V4L2
 	depends on ISA && VIDEO_V4L2

+ 1 - 0
drivers/media/radio/Makefile

@@ -18,6 +18,7 @@ obj-$(CONFIG_RADIO_TRUST) += radio-trust.o
 obj-$(CONFIG_I2C_SI4713) += si4713-i2c.o
 obj-$(CONFIG_I2C_SI4713) += si4713-i2c.o
 obj-$(CONFIG_RADIO_SI4713) += radio-si4713.o
 obj-$(CONFIG_RADIO_SI4713) += radio-si4713.o
 obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o
 obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o
+obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o
 obj-$(CONFIG_USB_DSBR) += dsbr100.o
 obj-$(CONFIG_USB_DSBR) += dsbr100.o
 obj-$(CONFIG_RADIO_SI470X) += si470x/
 obj-$(CONFIG_RADIO_SI470X) += si470x/
 obj-$(CONFIG_USB_MR800) += radio-mr800.o
 obj-$(CONFIG_USB_MR800) += radio-mr800.o

+ 270 - 0
drivers/media/radio/radio-miropcm20.c

@@ -0,0 +1,270 @@
+/* Miro PCM20 radio driver for Linux radio support
+ * (c) 1998 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl>
+ * Thanks to Norberto Pellici for the ACI device interface specification
+ * The API part is based on the radiotrack driver by M. Kirkwood
+ * This driver relies on the aci mixer provided by the snd-miro
+ * ALSA driver.
+ * Look there for further info...
+ */
+
+/* What ever you think about the ACI, version 0x07 is not very well!
+ * I can't get frequency, 'tuner status', 'tuner flags' or mute/mono
+ * conditions...                Robert
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <sound/aci.h>
+
+static int radio_nr = -1;
+module_param(radio_nr, int, 0);
+MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX).  Default: -1 (autodetect)");
+
+static int mono;
+module_param(mono, bool, 0);
+MODULE_PARM_DESC(mono, "Force tuner into mono mode.");
+
+struct pcm20 {
+	struct v4l2_device v4l2_dev;
+	struct video_device vdev;
+	unsigned long freq;
+	int muted;
+	struct snd_miro_aci *aci;
+};
+
+static struct pcm20 pcm20_card = {
+	.freq   = 87*16000,
+	.muted  = 1,
+};
+
+static int pcm20_mute(struct pcm20 *dev, unsigned char mute)
+{
+	dev->muted = mute;
+	return snd_aci_cmd(dev->aci, ACI_SET_TUNERMUTE, mute, -1);
+}
+
+static int pcm20_stereo(struct pcm20 *dev, unsigned char stereo)
+{
+	return snd_aci_cmd(dev->aci, ACI_SET_TUNERMONO, !stereo, -1);
+}
+
+static int pcm20_setfreq(struct pcm20 *dev, unsigned long freq)
+{
+	unsigned char freql;
+	unsigned char freqh;
+	struct snd_miro_aci *aci = dev->aci;
+
+	dev->freq = freq;
+
+	freq /= 160;
+	if (!(aci->aci_version == 0x07 || aci->aci_version >= 0xb0))
+		freq /= 10;  /* I don't know exactly which version
+			      * needs this hack */
+	freql = freq & 0xff;
+	freqh = freq >> 8;
+
+	pcm20_stereo(dev, !mono);
+	return snd_aci_cmd(aci, ACI_WRITE_TUNE, freql, freqh);
+}
+
+static const struct v4l2_file_operations pcm20_fops = {
+	.owner		= THIS_MODULE,
+	.ioctl		= video_ioctl2,
+};
+
+static int vidioc_querycap(struct file *file, void *priv,
+				struct v4l2_capability *v)
+{
+	strlcpy(v->driver, "Miro PCM20", sizeof(v->driver));
+	strlcpy(v->card, "Miro PCM20", sizeof(v->card));
+	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
+	v->version = 0x1;
+	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+	return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+				struct v4l2_tuner *v)
+{
+	if (v->index)	/* Only 1 tuner */
+		return -EINVAL;
+	strlcpy(v->name, "FM", sizeof(v->name));
+	v->type = V4L2_TUNER_RADIO;
+	v->rangelow = 87*16000;
+	v->rangehigh = 108*16000;
+	v->signal = 0xffff;
+	v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+	v->capability = V4L2_TUNER_CAP_LOW;
+	v->audmode = V4L2_TUNER_MODE_MONO;
+	return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+				struct v4l2_tuner *v)
+{
+	return v->index ? -EINVAL : 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+				struct v4l2_frequency *f)
+{
+	struct pcm20 *dev = video_drvdata(file);
+
+	if (f->tuner != 0)
+		return -EINVAL;
+
+	f->type = V4L2_TUNER_RADIO;
+	f->frequency = dev->freq;
+	return 0;
+}
+
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+				struct v4l2_frequency *f)
+{
+	struct pcm20 *dev = video_drvdata(file);
+
+	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+		return -EINVAL;
+
+	dev->freq = f->frequency;
+	pcm20_setfreq(dev, f->frequency);
+	return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+				struct v4l2_queryctrl *qc)
+{
+	switch (qc->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
+	}
+	return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+				struct v4l2_control *ctrl)
+{
+	struct pcm20 *dev = video_drvdata(file);
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		ctrl->value = dev->muted;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+				struct v4l2_control *ctrl)
+{
+	struct pcm20 *dev = video_drvdata(file);
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		pcm20_mute(dev, ctrl->value);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+	*i = 0;
+	return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+	return i ? -EINVAL : 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv,
+				struct v4l2_audio *a)
+{
+	a->index = 0;
+	strlcpy(a->name, "Radio", sizeof(a->name));
+	a->capability = V4L2_AUDCAP_STEREO;
+	return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv,
+				struct v4l2_audio *a)
+{
+	return a->index ? -EINVAL : 0;
+}
+
+static const struct v4l2_ioctl_ops pcm20_ioctl_ops = {
+	.vidioc_querycap    = vidioc_querycap,
+	.vidioc_g_tuner     = vidioc_g_tuner,
+	.vidioc_s_tuner     = vidioc_s_tuner,
+	.vidioc_g_frequency = vidioc_g_frequency,
+	.vidioc_s_frequency = vidioc_s_frequency,
+	.vidioc_queryctrl   = vidioc_queryctrl,
+	.vidioc_g_ctrl      = vidioc_g_ctrl,
+	.vidioc_s_ctrl      = vidioc_s_ctrl,
+	.vidioc_g_audio     = vidioc_g_audio,
+	.vidioc_s_audio     = vidioc_s_audio,
+	.vidioc_g_input     = vidioc_g_input,
+	.vidioc_s_input     = vidioc_s_input,
+};
+
+static int __init pcm20_init(void)
+{
+	struct pcm20 *dev = &pcm20_card;
+	struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
+	int res;
+
+	dev->aci = snd_aci_get_aci();
+	if (dev->aci == NULL) {
+		v4l2_err(v4l2_dev,
+			 "you must load the snd-miro driver first!\n");
+		return -ENODEV;
+	}
+	strlcpy(v4l2_dev->name, "miropcm20", sizeof(v4l2_dev->name));
+
+
+	res = v4l2_device_register(NULL, v4l2_dev);
+	if (res < 0) {
+		v4l2_err(v4l2_dev, "could not register v4l2_device\n");
+		return -EINVAL;
+	}
+
+	strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
+	dev->vdev.v4l2_dev = v4l2_dev;
+	dev->vdev.fops = &pcm20_fops;
+	dev->vdev.ioctl_ops = &pcm20_ioctl_ops;
+	dev->vdev.release = video_device_release_empty;
+	video_set_drvdata(&dev->vdev, dev);
+
+	if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0)
+		goto fail;
+
+	v4l2_info(v4l2_dev, "Mirosound PCM20 Radio tuner\n");
+	return 0;
+fail:
+	v4l2_device_unregister(v4l2_dev);
+	return -EINVAL;
+}
+
+MODULE_AUTHOR("Ruurd Reitsma, Krzysztof Helt");
+MODULE_DESCRIPTION("A driver for the Miro PCM20 radio card.");
+MODULE_LICENSE("GPL");
+
+static void __exit pcm20_cleanup(void)
+{
+	struct pcm20 *dev = &pcm20_card;
+
+	video_unregister_device(&dev->vdev);
+	v4l2_device_unregister(&dev->v4l2_dev);
+}
+
+module_init(pcm20_init);
+module_exit(pcm20_cleanup);

+ 6 - 0
drivers/mfd/Kconfig

@@ -121,6 +121,12 @@ config TWL4030_POWER
 	  and load scripts controling which resources are switched off/on
 	  and load scripts controling which resources are switched off/on
 	  or reset when a sleep, wakeup or warm reset event occurs.
 	  or reset when a sleep, wakeup or warm reset event occurs.
 
 
+config TWL4030_CODEC
+	bool
+	depends on TWL4030_CORE
+	select MFD_CORE
+	default n
+
 config MFD_TMIO
 config MFD_TMIO
 	bool
 	bool
 	default n
 	default n

+ 1 - 0
drivers/mfd/Makefile

@@ -26,6 +26,7 @@ obj-$(CONFIG_MENELAUS)		+= menelaus.o
 
 
 obj-$(CONFIG_TWL4030_CORE)	+= twl4030-core.o twl4030-irq.o
 obj-$(CONFIG_TWL4030_CORE)	+= twl4030-core.o twl4030-irq.o
 obj-$(CONFIG_TWL4030_POWER)    += twl4030-power.o
 obj-$(CONFIG_TWL4030_POWER)    += twl4030-power.o
+obj-$(CONFIG_TWL4030_CODEC)	+= twl4030-codec.o
 
 
 obj-$(CONFIG_MFD_MC13783)	+= mc13783-core.o
 obj-$(CONFIG_MFD_MC13783)	+= mc13783-core.o
 
 

+ 276 - 0
drivers/mfd/twl4030-codec.c

@@ -0,0 +1,276 @@
+/*
+ * MFD driver for twl4030 codec submodule
+ *
+ * Author:	Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * Copyright:   (C) 2009 Nokia Corporation
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl4030.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/twl4030-codec.h>
+
+#define TWL4030_CODEC_CELLS	2
+
+static struct platform_device *twl4030_codec_dev;
+
+struct twl4030_codec_resource {
+	int request_count;
+	u8 reg;
+	u8 mask;
+};
+
+struct twl4030_codec {
+	unsigned int audio_mclk;
+	struct mutex mutex;
+	struct twl4030_codec_resource resource[TWL4030_CODEC_RES_MAX];
+	struct mfd_cell cells[TWL4030_CODEC_CELLS];
+};
+
+/*
+ * Modify the resource, the function returns the content of the register
+ * after the modification.
+ */
+static int twl4030_codec_set_resource(enum twl4030_codec_res id, int enable)
+{
+	struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+	u8 val;
+
+	twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
+			codec->resource[id].reg);
+
+	if (enable)
+		val |= codec->resource[id].mask;
+	else
+		val &= ~codec->resource[id].mask;
+
+	twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+					val, codec->resource[id].reg);
+
+	return val;
+}
+
+static inline int twl4030_codec_get_resource(enum twl4030_codec_res id)
+{
+	struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+	u8 val;
+
+	twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
+			codec->resource[id].reg);
+
+	return val;
+}
+
+/*
+ * Enable the resource.
+ * The function returns with error or the content of the register
+ */
+int twl4030_codec_enable_resource(enum twl4030_codec_res id)
+{
+	struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+	int val;
+
+	if (id >= TWL4030_CODEC_RES_MAX) {
+		dev_err(&twl4030_codec_dev->dev,
+				"Invalid resource ID (%u)\n", id);
+		return -EINVAL;
+	}
+
+	mutex_lock(&codec->mutex);
+	if (!codec->resource[id].request_count)
+		/* Resource was disabled, enable it */
+		val = twl4030_codec_set_resource(id, 1);
+	else
+		val = twl4030_codec_get_resource(id);
+
+	codec->resource[id].request_count++;
+	mutex_unlock(&codec->mutex);
+
+	return val;
+}
+EXPORT_SYMBOL_GPL(twl4030_codec_enable_resource);
+
+/*
+ * Disable the resource.
+ * The function returns with error or the content of the register
+ */
+int twl4030_codec_disable_resource(unsigned id)
+{
+	struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+	int val;
+
+	if (id >= TWL4030_CODEC_RES_MAX) {
+		dev_err(&twl4030_codec_dev->dev,
+				"Invalid resource ID (%u)\n", id);
+		return -EINVAL;
+	}
+
+	mutex_lock(&codec->mutex);
+	if (!codec->resource[id].request_count) {
+		dev_err(&twl4030_codec_dev->dev,
+			"Resource has been disabled already (%u)\n", id);
+		mutex_unlock(&codec->mutex);
+		return -EPERM;
+	}
+	codec->resource[id].request_count--;
+
+	if (!codec->resource[id].request_count)
+		/* Resource can be disabled now */
+		val = twl4030_codec_set_resource(id, 0);
+	else
+		val = twl4030_codec_get_resource(id);
+
+	mutex_unlock(&codec->mutex);
+
+	return val;
+}
+EXPORT_SYMBOL_GPL(twl4030_codec_disable_resource);
+
+unsigned int twl4030_codec_get_mclk(void)
+{
+	struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+
+	return codec->audio_mclk;
+}
+EXPORT_SYMBOL_GPL(twl4030_codec_get_mclk);
+
+static int __devinit twl4030_codec_probe(struct platform_device *pdev)
+{
+	struct twl4030_codec *codec;
+	struct twl4030_codec_data *pdata = pdev->dev.platform_data;
+	struct mfd_cell *cell = NULL;
+	int ret, childs = 0;
+	u8 val;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "Platform data is missing\n");
+		return -EINVAL;
+	}
+
+	/* Configure APLL_INFREQ and disable APLL if enabled */
+	val = 0;
+	switch (pdata->audio_mclk) {
+	case 19200000:
+		val |= TWL4030_APLL_INFREQ_19200KHZ;
+		break;
+	case 26000000:
+		val |= TWL4030_APLL_INFREQ_26000KHZ;
+		break;
+	case 38400000:
+		val |= TWL4030_APLL_INFREQ_38400KHZ;
+		break;
+	default:
+		dev_err(&pdev->dev, "Invalid audio_mclk\n");
+		return -EINVAL;
+	}
+	twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+					val, TWL4030_REG_APLL_CTL);
+
+	codec = kzalloc(sizeof(struct twl4030_codec), GFP_KERNEL);
+	if (!codec)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, codec);
+
+	twl4030_codec_dev = pdev;
+	mutex_init(&codec->mutex);
+	codec->audio_mclk = pdata->audio_mclk;
+
+	/* Codec power */
+	codec->resource[TWL4030_CODEC_RES_POWER].reg = TWL4030_REG_CODEC_MODE;
+	codec->resource[TWL4030_CODEC_RES_POWER].mask = TWL4030_CODECPDZ;
+
+	/* PLL */
+	codec->resource[TWL4030_CODEC_RES_APLL].reg = TWL4030_REG_APLL_CTL;
+	codec->resource[TWL4030_CODEC_RES_APLL].mask = TWL4030_APLL_EN;
+
+	if (pdata->audio) {
+		cell = &codec->cells[childs];
+		cell->name = "twl4030_codec_audio";
+		cell->platform_data = pdata->audio;
+		cell->data_size = sizeof(*pdata->audio);
+		childs++;
+	}
+	if (pdata->vibra) {
+		cell = &codec->cells[childs];
+		cell->name = "twl4030_codec_vibra";
+		cell->platform_data = pdata->vibra;
+		cell->data_size = sizeof(*pdata->vibra);
+		childs++;
+	}
+
+	if (childs)
+		ret = mfd_add_devices(&pdev->dev, pdev->id, codec->cells,
+				      childs, NULL, 0);
+	else {
+		dev_err(&pdev->dev, "No platform data found for childs\n");
+		ret = -ENODEV;
+	}
+
+	if (!ret)
+		return 0;
+
+	platform_set_drvdata(pdev, NULL);
+	kfree(codec);
+	twl4030_codec_dev = NULL;
+	return ret;
+}
+
+static int __devexit twl4030_codec_remove(struct platform_device *pdev)
+{
+	struct twl4030_codec *codec = platform_get_drvdata(pdev);
+
+	mfd_remove_devices(&pdev->dev);
+	platform_set_drvdata(pdev, NULL);
+	kfree(codec);
+	twl4030_codec_dev = NULL;
+
+	return 0;
+}
+
+MODULE_ALIAS("platform:twl4030_codec");
+
+static struct platform_driver twl4030_codec_driver = {
+	.probe		= twl4030_codec_probe,
+	.remove		= __devexit_p(twl4030_codec_remove),
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "twl4030_codec",
+	},
+};
+
+static int __devinit twl4030_codec_init(void)
+{
+	return platform_driver_register(&twl4030_codec_driver);
+}
+module_init(twl4030_codec_init);
+
+static void __devexit twl4030_codec_exit(void)
+{
+	platform_driver_unregister(&twl4030_codec_driver);
+}
+module_exit(twl4030_codec_exit);
+
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>");
+MODULE_LICENSE("GPL");
+

+ 15 - 1
drivers/mfd/twl4030-core.c

@@ -114,6 +114,12 @@
 #define twl_has_watchdog()        false
 #define twl_has_watchdog()        false
 #endif
 #endif
 
 
+#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE)
+#define twl_has_codec()	true
+#else
+#define twl_has_codec()	false
+#endif
+
 /* Triton Core internal information (BEGIN) */
 /* Triton Core internal information (BEGIN) */
 
 
 /* Last - for index max*/
 /* Last - for index max*/
@@ -601,6 +607,14 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
 			return PTR_ERR(child);
 			return PTR_ERR(child);
 	}
 	}
 
 
+	if (twl_has_codec() && pdata->codec) {
+		child = add_child(1, "twl4030_codec",
+				pdata->codec, sizeof(*pdata->codec),
+				false, 0, 0);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
+
 	if (twl_has_regulator()) {
 	if (twl_has_regulator()) {
 		/*
 		/*
 		child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1);
 		child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1);
@@ -763,7 +777,7 @@ static int twl4030_remove(struct i2c_client *client)
 }
 }
 
 
 /* NOTE:  this driver only handles a single twl4030/tps659x0 chip */
 /* NOTE:  this driver only handles a single twl4030/tps659x0 chip */
-static int
+static int __init
 twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id)
 twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id)
 {
 {
 	int				status;
 	int				status;

+ 19 - 0
include/linux/i2c/twl4030.h

@@ -401,6 +401,24 @@ struct twl4030_power_data {
 
 
 extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts);
 extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts);
 
 
+struct twl4030_codec_audio_data {
+	unsigned int	audio_mclk;
+	unsigned int ramp_delay_value;
+	unsigned int hs_extmute:1;
+	void (*set_hs_extmute)(int mute);
+};
+
+struct twl4030_codec_vibra_data {
+	unsigned int	audio_mclk;
+	unsigned int	coexist;
+};
+
+struct twl4030_codec_data {
+	unsigned int	audio_mclk;
+	struct twl4030_codec_audio_data		*audio;
+	struct twl4030_codec_vibra_data		*vibra;
+};
+
 struct twl4030_platform_data {
 struct twl4030_platform_data {
 	unsigned				irq_base, irq_end;
 	unsigned				irq_base, irq_end;
 	struct twl4030_bci_platform_data	*bci;
 	struct twl4030_bci_platform_data	*bci;
@@ -409,6 +427,7 @@ struct twl4030_platform_data {
 	struct twl4030_keypad_data		*keypad;
 	struct twl4030_keypad_data		*keypad;
 	struct twl4030_usb_data			*usb;
 	struct twl4030_usb_data			*usb;
 	struct twl4030_power_data		*power;
 	struct twl4030_power_data		*power;
+	struct twl4030_codec_data		*codec;
 
 
 	/* LDO regulators */
 	/* LDO regulators */
 	struct regulator_init_data		*vdac;
 	struct regulator_init_data		*vdac;

+ 272 - 0
include/linux/mfd/twl4030-codec.h

@@ -0,0 +1,272 @@
+/*
+ * MFD driver for twl4030 codec submodule
+ *
+ * Author:	Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * Copyright:   (C) 2009 Nokia Corporation
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TWL4030_CODEC_H__
+#define __TWL4030_CODEC_H__
+
+/* Codec registers */
+#define TWL4030_REG_CODEC_MODE		0x01
+#define TWL4030_REG_OPTION		0x02
+#define TWL4030_REG_UNKNOWN		0x03
+#define TWL4030_REG_MICBIAS_CTL		0x04
+#define TWL4030_REG_ANAMICL		0x05
+#define TWL4030_REG_ANAMICR		0x06
+#define TWL4030_REG_AVADC_CTL		0x07
+#define TWL4030_REG_ADCMICSEL		0x08
+#define TWL4030_REG_DIGMIXING		0x09
+#define TWL4030_REG_ATXL1PGA		0x0A
+#define TWL4030_REG_ATXR1PGA		0x0B
+#define TWL4030_REG_AVTXL2PGA		0x0C
+#define TWL4030_REG_AVTXR2PGA		0x0D
+#define TWL4030_REG_AUDIO_IF		0x0E
+#define TWL4030_REG_VOICE_IF		0x0F
+#define TWL4030_REG_ARXR1PGA		0x10
+#define TWL4030_REG_ARXL1PGA		0x11
+#define TWL4030_REG_ARXR2PGA		0x12
+#define TWL4030_REG_ARXL2PGA		0x13
+#define TWL4030_REG_VRXPGA		0x14
+#define TWL4030_REG_VSTPGA		0x15
+#define TWL4030_REG_VRX2ARXPGA		0x16
+#define TWL4030_REG_AVDAC_CTL		0x17
+#define TWL4030_REG_ARX2VTXPGA		0x18
+#define TWL4030_REG_ARXL1_APGA_CTL	0x19
+#define TWL4030_REG_ARXR1_APGA_CTL	0x1A
+#define TWL4030_REG_ARXL2_APGA_CTL	0x1B
+#define TWL4030_REG_ARXR2_APGA_CTL	0x1C
+#define TWL4030_REG_ATX2ARXPGA		0x1D
+#define TWL4030_REG_BT_IF		0x1E
+#define TWL4030_REG_BTPGA		0x1F
+#define TWL4030_REG_BTSTPGA		0x20
+#define TWL4030_REG_EAR_CTL		0x21
+#define TWL4030_REG_HS_SEL		0x22
+#define TWL4030_REG_HS_GAIN_SET		0x23
+#define TWL4030_REG_HS_POPN_SET		0x24
+#define TWL4030_REG_PREDL_CTL		0x25
+#define TWL4030_REG_PREDR_CTL		0x26
+#define TWL4030_REG_PRECKL_CTL		0x27
+#define TWL4030_REG_PRECKR_CTL		0x28
+#define TWL4030_REG_HFL_CTL		0x29
+#define TWL4030_REG_HFR_CTL		0x2A
+#define TWL4030_REG_ALC_CTL		0x2B
+#define TWL4030_REG_ALC_SET1		0x2C
+#define TWL4030_REG_ALC_SET2		0x2D
+#define TWL4030_REG_BOOST_CTL		0x2E
+#define TWL4030_REG_SOFTVOL_CTL		0x2F
+#define TWL4030_REG_DTMF_FREQSEL	0x30
+#define TWL4030_REG_DTMF_TONEXT1H	0x31
+#define TWL4030_REG_DTMF_TONEXT1L	0x32
+#define TWL4030_REG_DTMF_TONEXT2H	0x33
+#define TWL4030_REG_DTMF_TONEXT2L	0x34
+#define TWL4030_REG_DTMF_TONOFF		0x35
+#define TWL4030_REG_DTMF_WANONOFF	0x36
+#define TWL4030_REG_I2S_RX_SCRAMBLE_H	0x37
+#define TWL4030_REG_I2S_RX_SCRAMBLE_M	0x38
+#define TWL4030_REG_I2S_RX_SCRAMBLE_L	0x39
+#define TWL4030_REG_APLL_CTL		0x3A
+#define TWL4030_REG_DTMF_CTL		0x3B
+#define TWL4030_REG_DTMF_PGA_CTL2	0x3C
+#define TWL4030_REG_DTMF_PGA_CTL1	0x3D
+#define TWL4030_REG_MISC_SET_1		0x3E
+#define TWL4030_REG_PCMBTMUX		0x3F
+#define TWL4030_REG_RX_PATH_SEL		0x43
+#define TWL4030_REG_VDL_APGA_CTL	0x44
+#define TWL4030_REG_VIBRA_CTL		0x45
+#define TWL4030_REG_VIBRA_SET		0x46
+#define TWL4030_REG_VIBRA_PWM_SET	0x47
+#define TWL4030_REG_ANAMIC_GAIN		0x48
+#define TWL4030_REG_MISC_SET_2		0x49
+
+/* Bitfield Definitions */
+
+/* TWL4030_CODEC_MODE (0x01) Fields */
+#define TWL4030_APLL_RATE		0xF0
+#define TWL4030_APLL_RATE_8000		0x00
+#define TWL4030_APLL_RATE_11025		0x10
+#define TWL4030_APLL_RATE_12000		0x20
+#define TWL4030_APLL_RATE_16000		0x40
+#define TWL4030_APLL_RATE_22050		0x50
+#define TWL4030_APLL_RATE_24000		0x60
+#define TWL4030_APLL_RATE_32000		0x80
+#define TWL4030_APLL_RATE_44100		0x90
+#define TWL4030_APLL_RATE_48000		0xA0
+#define TWL4030_APLL_RATE_96000		0xE0
+#define TWL4030_SEL_16K			0x08
+#define TWL4030_CODECPDZ		0x02
+#define TWL4030_OPT_MODE		0x01
+#define TWL4030_OPTION_1		(1 << 0)
+#define TWL4030_OPTION_2		(0 << 0)
+
+/* TWL4030_OPTION (0x02) Fields */
+#define TWL4030_ATXL1_EN		(1 << 0)
+#define TWL4030_ATXR1_EN		(1 << 1)
+#define TWL4030_ATXL2_VTXL_EN		(1 << 2)
+#define TWL4030_ATXR2_VTXR_EN		(1 << 3)
+#define TWL4030_ARXL1_VRX_EN		(1 << 4)
+#define TWL4030_ARXR1_EN		(1 << 5)
+#define TWL4030_ARXL2_EN		(1 << 6)
+#define TWL4030_ARXR2_EN		(1 << 7)
+
+/* TWL4030_REG_MICBIAS_CTL (0x04) Fields */
+#define TWL4030_MICBIAS2_CTL		0x40
+#define TWL4030_MICBIAS1_CTL		0x20
+#define TWL4030_HSMICBIAS_EN		0x04
+#define TWL4030_MICBIAS2_EN		0x02
+#define TWL4030_MICBIAS1_EN		0x01
+
+/* ANAMICL (0x05) Fields */
+#define TWL4030_CNCL_OFFSET_START	0x80
+#define TWL4030_OFFSET_CNCL_SEL		0x60
+#define TWL4030_OFFSET_CNCL_SEL_ARX1	0x00
+#define TWL4030_OFFSET_CNCL_SEL_ARX2	0x20
+#define TWL4030_OFFSET_CNCL_SEL_VRX	0x40
+#define TWL4030_OFFSET_CNCL_SEL_ALL	0x60
+#define TWL4030_MICAMPL_EN		0x10
+#define TWL4030_CKMIC_EN		0x08
+#define TWL4030_AUXL_EN			0x04
+#define TWL4030_HSMIC_EN		0x02
+#define TWL4030_MAINMIC_EN		0x01
+
+/* ANAMICR (0x06) Fields */
+#define TWL4030_MICAMPR_EN		0x10
+#define TWL4030_AUXR_EN			0x04
+#define TWL4030_SUBMIC_EN		0x01
+
+/* AVADC_CTL (0x07) Fields */
+#define TWL4030_ADCL_EN			0x08
+#define TWL4030_AVADC_CLK_PRIORITY	0x04
+#define TWL4030_ADCR_EN			0x02
+
+/* TWL4030_REG_ADCMICSEL (0x08) Fields */
+#define TWL4030_DIGMIC1_EN		0x08
+#define TWL4030_TX2IN_SEL		0x04
+#define TWL4030_DIGMIC0_EN		0x02
+#define TWL4030_TX1IN_SEL		0x01
+
+/* AUDIO_IF (0x0E) Fields */
+#define TWL4030_AIF_SLAVE_EN		0x80
+#define TWL4030_DATA_WIDTH		0x60
+#define TWL4030_DATA_WIDTH_16S_16W	0x00
+#define TWL4030_DATA_WIDTH_32S_16W	0x40
+#define TWL4030_DATA_WIDTH_32S_24W	0x60
+#define TWL4030_AIF_FORMAT		0x18
+#define TWL4030_AIF_FORMAT_CODEC	0x00
+#define TWL4030_AIF_FORMAT_LEFT		0x08
+#define TWL4030_AIF_FORMAT_RIGHT	0x10
+#define TWL4030_AIF_FORMAT_TDM		0x18
+#define TWL4030_AIF_TRI_EN		0x04
+#define TWL4030_CLK256FS_EN		0x02
+#define TWL4030_AIF_EN			0x01
+
+/* VOICE_IF (0x0F) Fields */
+#define TWL4030_VIF_SLAVE_EN		0x80
+#define TWL4030_VIF_DIN_EN		0x40
+#define TWL4030_VIF_DOUT_EN		0x20
+#define TWL4030_VIF_SWAP		0x10
+#define TWL4030_VIF_FORMAT		0x08
+#define TWL4030_VIF_TRI_EN		0x04
+#define TWL4030_VIF_SUB_EN		0x02
+#define TWL4030_VIF_EN			0x01
+
+/* EAR_CTL (0x21) */
+#define TWL4030_EAR_GAIN		0x30
+
+/* HS_GAIN_SET (0x23) Fields */
+#define TWL4030_HSR_GAIN		0x0C
+#define TWL4030_HSR_GAIN_PWR_DOWN	0x00
+#define TWL4030_HSR_GAIN_PLUS_6DB	0x04
+#define TWL4030_HSR_GAIN_0DB		0x08
+#define TWL4030_HSR_GAIN_MINUS_6DB	0x0C
+#define TWL4030_HSL_GAIN		0x03
+#define TWL4030_HSL_GAIN_PWR_DOWN	0x00
+#define TWL4030_HSL_GAIN_PLUS_6DB	0x01
+#define TWL4030_HSL_GAIN_0DB		0x02
+#define TWL4030_HSL_GAIN_MINUS_6DB	0x03
+
+/* HS_POPN_SET (0x24) Fields */
+#define TWL4030_VMID_EN			0x40
+#define	TWL4030_EXTMUTE			0x20
+#define TWL4030_RAMP_DELAY		0x1C
+#define TWL4030_RAMP_DELAY_20MS		0x00
+#define TWL4030_RAMP_DELAY_40MS		0x04
+#define TWL4030_RAMP_DELAY_81MS		0x08
+#define TWL4030_RAMP_DELAY_161MS	0x0C
+#define TWL4030_RAMP_DELAY_323MS	0x10
+#define TWL4030_RAMP_DELAY_645MS	0x14
+#define TWL4030_RAMP_DELAY_1291MS	0x18
+#define TWL4030_RAMP_DELAY_2581MS	0x1C
+#define TWL4030_RAMP_EN			0x02
+
+/* PREDL_CTL (0x25) */
+#define TWL4030_PREDL_GAIN		0x30
+
+/* PREDR_CTL (0x26) */
+#define TWL4030_PREDR_GAIN		0x30
+
+/* PRECKL_CTL (0x27) */
+#define TWL4030_PRECKL_GAIN		0x30
+
+/* PRECKR_CTL (0x28) */
+#define TWL4030_PRECKR_GAIN		0x30
+
+/* HFL_CTL (0x29, 0x2A) Fields */
+#define TWL4030_HF_CTL_HB_EN		0x04
+#define TWL4030_HF_CTL_LOOP_EN		0x08
+#define TWL4030_HF_CTL_RAMP_EN		0x10
+#define TWL4030_HF_CTL_REF_EN		0x20
+
+/* APLL_CTL (0x3A) Fields */
+#define TWL4030_APLL_EN			0x10
+#define TWL4030_APLL_INFREQ		0x0F
+#define TWL4030_APLL_INFREQ_19200KHZ	0x05
+#define TWL4030_APLL_INFREQ_26000KHZ	0x06
+#define TWL4030_APLL_INFREQ_38400KHZ	0x0F
+
+/* REG_MISC_SET_1 (0x3E) Fields */
+#define TWL4030_CLK64_EN		0x80
+#define TWL4030_SCRAMBLE_EN		0x40
+#define TWL4030_FMLOOP_EN		0x20
+#define TWL4030_SMOOTH_ANAVOL_EN	0x02
+#define TWL4030_DIGMIC_LR_SWAP_EN	0x01
+
+/* VIBRA_CTL (0x45) */
+#define TWL4030_VIBRA_EN		0x01
+#define TWL4030_VIBRA_DIR		0x02
+#define TWL4030_VIBRA_AUDIO_SEL_L1	(0x00 << 2)
+#define TWL4030_VIBRA_AUDIO_SEL_R1	(0x01 << 2)
+#define TWL4030_VIBRA_AUDIO_SEL_L2	(0x02 << 2)
+#define TWL4030_VIBRA_AUDIO_SEL_R2	(0x03 << 2)
+#define TWL4030_VIBRA_SEL		0x10
+#define TWL4030_VIBRA_DIR_SEL		0x20
+
+/* TWL4030 codec resource IDs */
+enum twl4030_codec_res {
+	TWL4030_CODEC_RES_POWER = 0,
+	TWL4030_CODEC_RES_APLL,
+	TWL4030_CODEC_RES_MAX,
+};
+
+int twl4030_codec_disable_resource(enum twl4030_codec_res id);
+int twl4030_codec_enable_resource(enum twl4030_codec_res id);
+unsigned int twl4030_codec_get_mclk(void);
+
+#endif	/* End of __TWL4030_CODEC_H__ */

+ 0 - 1
include/sound/Kbuild

@@ -2,7 +2,6 @@ header-y += asound_fm.h
 header-y += hdsp.h
 header-y += hdsp.h
 header-y += hdspm.h
 header-y += hdspm.h
 header-y += sfnt_info.h
 header-y += sfnt_info.h
-header-y += sscape_ioctl.h
 
 
 unifdef-y += asequencer.h
 unifdef-y += asequencer.h
 unifdef-y += asound.h
 unifdef-y += asound.h

+ 20 - 3
sound/isa/opti9xx/miro.h → include/sound/aci.h

@@ -1,5 +1,5 @@
-#ifndef _MIRO_H_
-#define _MIRO_H_
+#ifndef _ACI_H_
+#define _ACI_H_
 
 
 #define ACI_REG_COMMAND		0	/* write register offset */
 #define ACI_REG_COMMAND		0	/* write register offset */
 #define ACI_REG_STATUS		1	/* read register offset */
 #define ACI_REG_STATUS		1	/* read register offset */
@@ -70,4 +70,21 @@
 #define ACI_SET_EQ6		0x45
 #define ACI_SET_EQ6		0x45
 #define ACI_SET_EQ7		0x46	/* ... to Treble */
 #define ACI_SET_EQ7		0x46	/* ... to Treble */
 
 
-#endif  /* _MIRO_H_ */
+struct snd_miro_aci {
+	unsigned long aci_port;
+	int aci_vendor;
+	int aci_product;
+	int aci_version;
+	int aci_amp;
+	int aci_preamp;
+	int aci_solomode;
+
+	struct mutex aci_mutex;
+};
+
+int snd_aci_cmd(struct snd_miro_aci *aci, int write1, int write2, int write3);
+
+struct snd_miro_aci *snd_aci_get_aci(void);
+
+#endif  /* _ACI_H_ */
+

+ 321 - 0
include/sound/ak4113.h

@@ -0,0 +1,321 @@
+#ifndef __SOUND_AK4113_H
+#define __SOUND_AK4113_H
+
+/*
+ *  Routines for Asahi Kasei AK4113
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
+ *  Copyright (c) by Pavel Hofman <pavel.hofman@ivitera.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+/* AK4113 registers */
+/* power down */
+#define AK4113_REG_PWRDN	0x00
+/* format control */
+#define AK4113_REG_FORMAT	0x01
+/* input/output control */
+#define AK4113_REG_IO0		0x02
+/* input/output control */
+#define AK4113_REG_IO1		0x03
+/* interrupt0 mask */
+#define AK4113_REG_INT0_MASK	0x04
+/* interrupt1 mask */
+#define AK4113_REG_INT1_MASK	0x05
+/* DAT mask & DTS select */
+#define AK4113_REG_DATDTS	0x06
+/* receiver status 0 */
+#define AK4113_REG_RCS0		0x07
+/* receiver status 1 */
+#define AK4113_REG_RCS1		0x08
+/* receiver status 2 */
+#define AK4113_REG_RCS2		0x09
+/* RX channel status byte 0 */
+#define AK4113_REG_RXCSB0	0x0a
+/* RX channel status byte 1 */
+#define AK4113_REG_RXCSB1	0x0b
+/* RX channel status byte 2 */
+#define AK4113_REG_RXCSB2	0x0c
+/* RX channel status byte 3 */
+#define AK4113_REG_RXCSB3	0x0d
+/* RX channel status byte 4 */
+#define AK4113_REG_RXCSB4	0x0e
+/* burst preamble Pc byte 0 */
+#define AK4113_REG_Pc0		0x0f
+/* burst preamble Pc byte 1 */
+#define AK4113_REG_Pc1		0x10
+/* burst preamble Pd byte 0 */
+#define AK4113_REG_Pd0		0x11
+/* burst preamble Pd byte 1 */
+#define AK4113_REG_Pd1		0x12
+/* Q-subcode address + control */
+#define AK4113_REG_QSUB_ADDR	0x13
+/* Q-subcode track */
+#define AK4113_REG_QSUB_TRACK	0x14
+/* Q-subcode index */
+#define AK4113_REG_QSUB_INDEX	0x15
+/* Q-subcode minute */
+#define AK4113_REG_QSUB_MINUTE	0x16
+/* Q-subcode second */
+#define AK4113_REG_QSUB_SECOND	0x17
+/* Q-subcode frame */
+#define AK4113_REG_QSUB_FRAME	0x18
+/* Q-subcode zero */
+#define AK4113_REG_QSUB_ZERO	0x19
+/* Q-subcode absolute minute */
+#define AK4113_REG_QSUB_ABSMIN	0x1a
+/* Q-subcode absolute second */
+#define AK4113_REG_QSUB_ABSSEC	0x1b
+/* Q-subcode absolute frame */
+#define AK4113_REG_QSUB_ABSFRM	0x1c
+
+/* sizes */
+#define AK4113_REG_RXCSB_SIZE	((AK4113_REG_RXCSB4-AK4113_REG_RXCSB0)+1)
+#define AK4113_REG_QSUB_SIZE	((AK4113_REG_QSUB_ABSFRM-AK4113_REG_QSUB_ADDR)\
+		+1)
+
+#define AK4113_WRITABLE_REGS	(AK4113_REG_DATDTS + 1)
+
+/* AK4113_REG_PWRDN bits */
+/* Channel Status Select */
+#define AK4113_CS12		(1<<7)
+/* Block Start & C/U Output Mode */
+#define AK4113_BCU		(1<<6)
+/* Master Clock Operation Select */
+#define AK4113_CM1		(1<<5)
+/* Master Clock Operation Select */
+#define AK4113_CM0		(1<<4)
+/* Master Clock Frequency Select */
+#define AK4113_OCKS1		(1<<3)
+/* Master Clock Frequency Select */
+#define AK4113_OCKS0		(1<<2)
+/* 0 = power down, 1 = normal operation */
+#define AK4113_PWN		(1<<1)
+/* 0 = reset & initialize (except thisregister), 1 = normal operation */
+#define AK4113_RST		(1<<0)
+
+/* AK4113_REQ_FORMAT bits */
+/* V/TX Output select: 0 = Validity Flag Output, 1 = TX */
+#define AK4113_VTX		(1<<7)
+/* Audio Data Control */
+#define AK4113_DIF2		(1<<6)
+/* Audio Data Control */
+#define AK4113_DIF1		(1<<5)
+/* Audio Data Control */
+#define AK4113_DIF0		(1<<4)
+/* Deemphasis Autodetect Enable (1 = enable) */
+#define AK4113_DEAU		(1<<3)
+/* 32kHz-48kHz Deemphasis Control */
+#define AK4113_DEM1		(1<<2)
+/* 32kHz-48kHz Deemphasis Control */
+#define AK4113_DEM0		(1<<1)
+#define AK4113_DEM_OFF		(AK4113_DEM0)
+#define AK4113_DEM_44KHZ	(0)
+#define AK4113_DEM_48KHZ	(AK4113_DEM1)
+#define AK4113_DEM_32KHZ	(AK4113_DEM0|AK4113_DEM1)
+/* STDO: 16-bit, right justified */
+#define AK4113_DIF_16R		(0)
+/* STDO: 18-bit, right justified */
+#define AK4113_DIF_18R		(AK4113_DIF0)
+/* STDO: 20-bit, right justified */
+#define AK4113_DIF_20R		(AK4113_DIF1)
+/* STDO: 24-bit, right justified */
+#define AK4113_DIF_24R		(AK4113_DIF1|AK4113_DIF0)
+/* STDO: 24-bit, left justified */
+#define AK4113_DIF_24L		(AK4113_DIF2)
+/* STDO: I2S */
+#define AK4113_DIF_24I2S	(AK4113_DIF2|AK4113_DIF0)
+/* STDO: 24-bit, left justified; LRCLK, BICK = Input */
+#define AK4113_DIF_I24L		(AK4113_DIF2|AK4113_DIF1)
+/* STDO: I2S;  LRCLK, BICK = Input */
+#define AK4113_DIF_I24I2S	(AK4113_DIF2|AK4113_DIF1|AK4113_DIF0)
+
+/* AK4113_REG_IO0 */
+/* XTL1=0,XTL0=0 -> 11.2896Mhz; XTL1=0,XTL0=1 -> 12.288Mhz */
+#define AK4113_XTL1		(1<<6)
+/* XTL1=1,XTL0=0 -> 24.576Mhz; XTL1=1,XTL0=1 -> use channel status */
+#define AK4113_XTL0		(1<<5)
+/* Block Start Signal Output: 0 = U-bit, 1 = C-bit (req. BCU = 1) */
+#define AK4113_UCE		(1<<4)
+/* TX Output Enable (1 = enable) */
+#define AK4113_TXE		(1<<3)
+/* Output Through Data Selector for TX pin */
+#define AK4113_OPS2		(1<<2)
+/* Output Through Data Selector for TX pin */
+#define AK4113_OPS1		(1<<1)
+/* Output Through Data Selector for TX pin */
+#define AK4113_OPS0		(1<<0)
+/* 11.2896 MHz ref. Xtal freq. */
+#define AK4113_XTL_11_2896M	(0)
+/* 12.288 MHz ref. Xtal freq. */
+#define AK4113_XTL_12_288M	(AK4113_XTL0)
+/* 24.576 MHz ref. Xtal freq. */
+#define AK4113_XTL_24_576M	(AK4113_XTL1)
+
+/* AK4113_REG_IO1 */
+/* Interrupt 0 pin Hold */
+#define AK4113_EFH1		(1<<7)
+/* Interrupt 0 pin Hold */
+#define AK4113_EFH0		(1<<6)
+#define AK4113_EFH_512LRCLK	(0)
+#define AK4113_EFH_1024LRCLK	(AK4113_EFH0)
+#define AK4113_EFH_2048LRCLK	(AK4113_EFH1)
+#define AK4113_EFH_4096LRCLK	(AK4113_EFH1|AK4113_EFH0)
+/* PLL Lock Time: 0 = 384/fs, 1 = 1/fs */
+#define AK4113_FAST		(1<<5)
+/* MCKO2 Output Select: 0 = CMx/OCKSx, 1 = Xtal */
+#define AK4113_XMCK		(1<<4)
+/* MCKO2 Output Freq. Select: 0 = x1, 1 = x0.5  (req. XMCK = 1) */
+#define AK4113_DIV		(1<<3)
+/* Input Recovery Data Select */
+#define AK4113_IPS2		(1<<2)
+/* Input Recovery Data Select */
+#define AK4113_IPS1		(1<<1)
+/* Input Recovery Data Select */
+#define AK4113_IPS0		(1<<0)
+#define AK4113_IPS(x)		((x)&7)
+
+/* AK4113_REG_INT0_MASK && AK4113_REG_INT1_MASK*/
+/* mask enable for QINT bit */
+#define AK4113_MQI		(1<<7)
+/* mask enable for AUTO bit */
+#define AK4113_MAUT		(1<<6)
+/* mask enable for CINT bit */
+#define AK4113_MCIT		(1<<5)
+/* mask enable for UNLOCK bit */
+#define AK4113_MULK		(1<<4)
+/* mask enable for V bit */
+#define AK4113_V		(1<<3)
+/* mask enable for STC bit */
+#define AK4113_STC		(1<<2)
+/* mask enable for AUDN bit */
+#define AK4113_MAN		(1<<1)
+/* mask enable for PAR bit */
+#define AK4113_MPR		(1<<0)
+
+/* AK4113_REG_DATDTS */
+/* DAT Start ID Counter */
+#define AK4113_DCNT		(1<<4)
+/* DTS-CD 16-bit Sync Word Detect */
+#define AK4113_DTS16		(1<<3)
+/* DTS-CD 14-bit Sync Word Detect */
+#define AK4113_DTS14		(1<<2)
+/* mask enable for DAT bit (if 1, no INT1 effect */
+#define AK4113_MDAT1		(1<<1)
+/* mask enable for DAT bit (if 1, no INT0 effect */
+#define AK4113_MDAT0		(1<<0)
+
+/* AK4113_REG_RCS0 */
+/* Q-subcode buffer interrupt, 0 = no change, 1 = changed */
+#define AK4113_QINT		(1<<7)
+/* Non-PCM or DTS stream auto detection, 0 = no detect, 1 = detect */
+#define AK4113_AUTO		(1<<6)
+/* channel status buffer interrupt, 0 = no change, 1 = change */
+#define AK4113_CINT		(1<<5)
+/* PLL lock status, 0 = lock, 1 = unlock */
+#define AK4113_UNLCK		(1<<4)
+/* Validity bit, 0 = valid, 1 = invalid */
+#define AK4113_V		(1<<3)
+/* sampling frequency or Pre-emphasis change, 0 = no detect, 1 = detect */
+#define AK4113_STC		(1<<2)
+/* audio bit output, 0 = audio, 1 = non-audio */
+#define AK4113_AUDION		(1<<1)
+/* parity error or biphase error status, 0 = no error, 1 = error */
+#define AK4113_PAR		(1<<0)
+
+/* AK4113_REG_RCS1 */
+/* sampling frequency detection */
+#define AK4113_FS3		(1<<7)
+#define AK4113_FS2		(1<<6)
+#define AK4113_FS1		(1<<5)
+#define AK4113_FS0		(1<<4)
+/* Pre-emphasis detect, 0 = OFF, 1 = ON */
+#define AK4113_PEM		(1<<3)
+/* DAT Start ID Detect, 0 = no detect, 1 = detect */
+#define AK4113_DAT		(1<<2)
+/* DTS-CD bit audio stream detect, 0 = no detect, 1 = detect */
+#define AK4113_DTSCD		(1<<1)
+/* Non-PCM bit stream detection, 0 = no detect, 1 = detect */
+#define AK4113_NPCM		(1<<0)
+#define AK4113_FS_8000HZ	(AK4113_FS3|AK4113_FS0)
+#define AK4113_FS_11025HZ	(AK4113_FS2|AK4113_FS0)
+#define AK4113_FS_16000HZ	(AK4113_FS2|AK4113_FS1|AK4113_FS0)
+#define AK4113_FS_22050HZ	(AK4113_FS2)
+#define AK4113_FS_24000HZ	(AK4113_FS2|AK4113_FS1)
+#define AK4113_FS_32000HZ	(AK4113_FS1|AK4113_FS0)
+#define AK4113_FS_44100HZ	(0)
+#define AK4113_FS_48000HZ	(AK4113_FS1)
+#define AK4113_FS_64000HZ	(AK4113_FS3|AK4113_FS1|AK4113_FS0)
+#define AK4113_FS_88200HZ	(AK4113_FS3)
+#define AK4113_FS_96000HZ	(AK4113_FS3|AK4113_FS1)
+#define AK4113_FS_176400HZ	(AK4113_FS3|AK4113_FS2)
+#define AK4113_FS_192000HZ	(AK4113_FS3|AK4113_FS2|AK4113_FS1)
+
+/* AK4113_REG_RCS2 */
+/* CRC for Q-subcode, 0 = no error, 1 = error */
+#define AK4113_QCRC		(1<<1)
+/* CRC for channel status, 0 = no error, 1 = error */
+#define AK4113_CCRC		(1<<0)
+
+/* flags for snd_ak4113_check_rate_and_errors() */
+#define AK4113_CHECK_NO_STAT	(1<<0)	/* no statistics */
+#define AK4113_CHECK_NO_RATE	(1<<1)	/* no rate check */
+
+#define AK4113_CONTROLS		13
+
+typedef void (ak4113_write_t)(void *private_data, unsigned char addr,
+		unsigned char data);
+typedef unsigned char (ak4113_read_t)(void *private_data, unsigned char addr);
+
+struct ak4113 {
+	struct snd_card *card;
+	ak4113_write_t *write;
+	ak4113_read_t *read;
+	void *private_data;
+	unsigned int init:1;
+	spinlock_t lock;
+	unsigned char regmap[AK4113_WRITABLE_REGS];
+	struct snd_kcontrol *kctls[AK4113_CONTROLS];
+	struct snd_pcm_substream *substream;
+	unsigned long parity_errors;
+	unsigned long v_bit_errors;
+	unsigned long qcrc_errors;
+	unsigned long ccrc_errors;
+	unsigned char rcs0;
+	unsigned char rcs1;
+	unsigned char rcs2;
+	struct delayed_work work;
+	unsigned int check_flags;
+	void *change_callback_private;
+	void (*change_callback)(struct ak4113 *ak4113, unsigned char c0,
+			unsigned char c1);
+};
+
+int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read,
+		ak4113_write_t *write,
+		const unsigned char pgm[AK4113_WRITABLE_REGS],
+		void *private_data, struct ak4113 **r_ak4113);
+void snd_ak4113_reg_write(struct ak4113 *ak4113, unsigned char reg,
+		unsigned char mask, unsigned char val);
+void snd_ak4113_reinit(struct ak4113 *ak4113);
+int snd_ak4113_build(struct ak4113 *ak4113,
+		struct snd_pcm_substream *capture_substream);
+int snd_ak4113_external_rate(struct ak4113 *ak4113);
+int snd_ak4113_check_rate_and_errors(struct ak4113 *ak4113, unsigned int flags);
+
+#endif /* __SOUND_AK4113_H */
+

+ 6 - 6
include/sound/ak4114.h

@@ -95,13 +95,13 @@
 
 
 /* AK4114_REG_IO0 */
 /* AK4114_REG_IO0 */
 #define AK4114_TX1E		(1<<7)	/* TX1 Output Enable (1 = enable) */
 #define AK4114_TX1E		(1<<7)	/* TX1 Output Enable (1 = enable) */
-#define AK4114_OPS12		(1<<2)	/* Output Though Data Selector for TX1 pin */
-#define AK4114_OPS11		(1<<1)	/* Output Though Data Selector for TX1 pin */
-#define AK4114_OPS10		(1<<0)	/* Output Though Data Selector for TX1 pin */
+#define AK4114_OPS12		(1<<6)	/* Output Data Selector for TX1 pin */
+#define AK4114_OPS11		(1<<5)	/* Output Data Selector for TX1 pin */
+#define AK4114_OPS10		(1<<4)	/* Output Data Selector for TX1 pin */
 #define AK4114_TX0E		(1<<3)	/* TX0 Output Enable (1 = enable) */
 #define AK4114_TX0E		(1<<3)	/* TX0 Output Enable (1 = enable) */
-#define AK4114_OPS02		(1<<2)	/* Output Though Data Selector for TX0 pin */
-#define AK4114_OPS01		(1<<1)	/* Output Though Data Selector for TX0 pin */
-#define AK4114_OPS00		(1<<0)	/* Output Though Data Selector for TX0 pin */
+#define AK4114_OPS02		(1<<2)	/* Output Data Selector for TX0 pin */
+#define AK4114_OPS01		(1<<1)	/* Output Data Selector for TX0 pin */
+#define AK4114_OPS00		(1<<0)	/* Output Data Selector for TX0 pin */
 
 
 /* AK4114_REG_IO1 */
 /* AK4114_REG_IO1 */
 #define AK4114_EFH1		(1<<7)	/* Interrupt 0 pin Hold */
 #define AK4114_EFH1		(1<<7)	/* Interrupt 0 pin Hold */

+ 4 - 1
include/sound/ak4xxx-adda.h

@@ -68,7 +68,7 @@ struct snd_akm4xxx {
 	enum {
 	enum {
 		SND_AK4524, SND_AK4528, SND_AK4529,
 		SND_AK4524, SND_AK4528, SND_AK4529,
 		SND_AK4355, SND_AK4358, SND_AK4381,
 		SND_AK4355, SND_AK4358, SND_AK4381,
-		SND_AK5365
+		SND_AK5365, SND_AK4620,
 	} type;
 	} type;
 
 
 	/* (array) information of combined codecs */
 	/* (array) information of combined codecs */
@@ -76,6 +76,9 @@ struct snd_akm4xxx {
 	const struct snd_akm4xxx_adc_channel *adc_info;
 	const struct snd_akm4xxx_adc_channel *adc_info;
 
 
 	struct snd_ak4xxx_ops ops;
 	struct snd_ak4xxx_ops ops;
+	unsigned int num_chips;
+	unsigned int total_regs;
+	const char *name;
 };
 };
 
 
 void snd_akm4xxx_write(struct snd_akm4xxx *ak, int chip, unsigned char reg,
 void snd_akm4xxx_write(struct snd_akm4xxx *ak, int chip, unsigned char reg,

+ 3 - 2
include/sound/control.h

@@ -56,7 +56,6 @@ struct snd_kcontrol_new {
 
 
 struct snd_kcontrol_volatile {
 struct snd_kcontrol_volatile {
 	struct snd_ctl_file *owner;	/* locked */
 	struct snd_ctl_file *owner;	/* locked */
-	pid_t owner_pid;
 	unsigned int access;	/* access rights */
 	unsigned int access;	/* access rights */
 };
 };
 
 
@@ -87,10 +86,12 @@ struct snd_kctl_event {
 
 
 #define snd_kctl_event(n) list_entry(n, struct snd_kctl_event, list)
 #define snd_kctl_event(n) list_entry(n, struct snd_kctl_event, list)
 
 
+struct pid;
+
 struct snd_ctl_file {
 struct snd_ctl_file {
 	struct list_head list;		/* list of all control files */
 	struct list_head list;		/* list of all control files */
 	struct snd_card *card;
 	struct snd_card *card;
-	pid_t pid;
+	struct pid *pid;
 	int prefer_pcm_subdevice;
 	int prefer_pcm_subdevice;
 	int prefer_rawmidi_subdevice;
 	int prefer_rawmidi_subdevice;
 	wait_queue_head_t change_sleep;
 	wait_queue_head_t change_sleep;

+ 0 - 1
include/sound/cs4231-regs.h

@@ -70,7 +70,6 @@
 #define AD1845_PWR_DOWN		0x1b	/* power down control */
 #define AD1845_PWR_DOWN		0x1b	/* power down control */
 #define CS4235_LEFT_MASTER	0x1b	/* left master output control */
 #define CS4235_LEFT_MASTER	0x1b	/* left master output control */
 #define CS4231_REC_FORMAT	0x1c	/* clock and data format - record - bits 7-0 MCE */
 #define CS4231_REC_FORMAT	0x1c	/* clock and data format - record - bits 7-0 MCE */
-#define CS4231_PLY_VAR_FREQ	0x1d	/* playback variable frequency */
 #define AD1845_CLOCK		0x1d	/* crystal clock select and total power down */
 #define AD1845_CLOCK		0x1d	/* crystal clock select and total power down */
 #define CS4235_RIGHT_MASTER	0x1d	/* right master output control */
 #define CS4235_RIGHT_MASTER	0x1d	/* right master output control */
 #define CS4231_REC_UPR_CNT	0x1e	/* record upper count */
 #define CS4231_REC_UPR_CNT	0x1e	/* record upper count */

+ 3 - 0
include/sound/pcm.h

@@ -348,6 +348,8 @@ struct snd_pcm_group {		/* keep linked substreams */
 	int count;
 	int count;
 };
 };
 
 
+struct pid;
+
 struct snd_pcm_substream {
 struct snd_pcm_substream {
 	struct snd_pcm *pcm;
 	struct snd_pcm *pcm;
 	struct snd_pcm_str *pstr;
 	struct snd_pcm_str *pstr;
@@ -379,6 +381,7 @@ struct snd_pcm_substream {
 	atomic_t mmap_count;
 	atomic_t mmap_count;
 	unsigned int f_flags;
 	unsigned int f_flags;
 	void (*pcm_release)(struct snd_pcm_substream *);
 	void (*pcm_release)(struct snd_pcm_substream *);
+	struct pid *pid;
 #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
 #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
 	/* -- OSS things -- */
 	/* -- OSS things -- */
 	struct snd_pcm_oss_substream oss;
 	struct snd_pcm_oss_substream oss;

+ 2 - 0
include/sound/rawmidi.h

@@ -46,6 +46,7 @@
 struct snd_rawmidi;
 struct snd_rawmidi;
 struct snd_rawmidi_substream;
 struct snd_rawmidi_substream;
 struct snd_seq_port_info;
 struct snd_seq_port_info;
+struct pid;
 
 
 struct snd_rawmidi_ops {
 struct snd_rawmidi_ops {
 	int (*open) (struct snd_rawmidi_substream * substream);
 	int (*open) (struct snd_rawmidi_substream * substream);
@@ -97,6 +98,7 @@ struct snd_rawmidi_substream {
 	struct snd_rawmidi_str *pstr;
 	struct snd_rawmidi_str *pstr;
 	char name[32];
 	char name[32];
 	struct snd_rawmidi_runtime *runtime;
 	struct snd_rawmidi_runtime *runtime;
+	struct pid *pid;
 	/* hardware layer */
 	/* hardware layer */
 	struct snd_rawmidi_ops *ops;
 	struct snd_rawmidi_ops *ops;
 };
 };

+ 21 - 0
include/sound/sh_dac_audio.h

@@ -0,0 +1,21 @@
+/*
+ * SH_DAC specific configuration, for the dac_audio platform_device
+ *
+ * Copyright (C) 2009 Rafael Ignacio Zurita <rizurita@yahoo.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.
+ */
+
+#ifndef __INCLUDE_SH_DAC_AUDIO_H
+#define __INCLUDE_SH_DAC_AUDIO_H
+
+struct dac_audio_pdata {
+	int buffer_size;
+	int channel;
+	void (*start)(struct dac_audio_pdata *pd);
+	void (*stop)(struct dac_audio_pdata *pd);
+};
+
+#endif /* __INCLUDE_SH_DAC_AUDIO_H */

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

@@ -30,6 +30,7 @@ struct snd_pcm_substream;
 #define SND_SOC_DAIFMT_DSP_A		3 /* L data MSB after FRM LRC */
 #define SND_SOC_DAIFMT_DSP_A		3 /* L data MSB after FRM LRC */
 #define SND_SOC_DAIFMT_DSP_B		4 /* L data MSB during FRM LRC */
 #define SND_SOC_DAIFMT_DSP_B		4 /* L data MSB during FRM LRC */
 #define SND_SOC_DAIFMT_AC97		5 /* AC97 */
 #define SND_SOC_DAIFMT_AC97		5 /* AC97 */
+#define SND_SOC_DAIFMT_PDM		6 /* Pulse density modulation */
 
 
 /* left and right justified also known as MSB and LSB respectively */
 /* left and right justified also known as MSB and LSB respectively */
 #define SND_SOC_DAIFMT_MSB		SND_SOC_DAIFMT_LEFT_J
 #define SND_SOC_DAIFMT_MSB		SND_SOC_DAIFMT_LEFT_J
@@ -106,7 +107,7 @@ int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
 	int div_id, int div);
 	int div_id, int div);
 
 
 int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
 int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
-	int pll_id, unsigned int freq_in, unsigned int freq_out);
+	int pll_id, int source, unsigned int freq_in, unsigned int freq_out);
 
 
 /* Digital Audio interface formatting */
 /* Digital Audio interface formatting */
 int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
 int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
@@ -114,6 +115,10 @@ int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
 int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
 int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
 	unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width);
 	unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width);
 
 
+int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai,
+	unsigned int tx_num, unsigned int *tx_slot,
+	unsigned int rx_num, unsigned int *rx_slot);
+
 int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
 int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
 
 
 /* Digital Audio Interface mute */
 /* Digital Audio Interface mute */
@@ -136,8 +141,8 @@ struct snd_soc_dai_ops {
 	 */
 	 */
 	int (*set_sysclk)(struct snd_soc_dai *dai,
 	int (*set_sysclk)(struct snd_soc_dai *dai,
 		int clk_id, unsigned int freq, int dir);
 		int clk_id, unsigned int freq, int dir);
-	int (*set_pll)(struct snd_soc_dai *dai,
-		int pll_id, unsigned int freq_in, unsigned int freq_out);
+	int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source,
+		unsigned int freq_in, unsigned int freq_out);
 	int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
 	int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
 
 
 	/*
 	/*
@@ -148,6 +153,9 @@ struct snd_soc_dai_ops {
 	int (*set_tdm_slot)(struct snd_soc_dai *dai,
 	int (*set_tdm_slot)(struct snd_soc_dai *dai,
 		unsigned int tx_mask, unsigned int rx_mask,
 		unsigned int tx_mask, unsigned int rx_mask,
 		int slots, int slot_width);
 		int slots, int slot_width);
+	int (*set_channel_map)(struct snd_soc_dai *dai,
+		unsigned int tx_num, unsigned int *tx_slot,
+		unsigned int rx_num, unsigned int *rx_slot);
 	int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
 	int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
 
 
 	/*
 	/*

+ 17 - 0
include/sound/soc-dapm.h

@@ -206,6 +206,12 @@
  	.get = snd_soc_dapm_get_enum_double, \
  	.get = snd_soc_dapm_get_enum_double, \
  	.put = snd_soc_dapm_put_enum_double, \
  	.put = snd_soc_dapm_put_enum_double, \
   	.private_value = (unsigned long)&xenum }
   	.private_value = (unsigned long)&xenum }
+#define SOC_DAPM_ENUM_VIRT(xname, xenum)		    \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = snd_soc_info_enum_double, \
+	.get = snd_soc_dapm_get_enum_virt, \
+	.put = snd_soc_dapm_put_enum_virt, \
+	.private_value = (unsigned long)&xenum }
 #define SOC_DAPM_VALUE_ENUM(xname, xenum) \
 #define SOC_DAPM_VALUE_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, \
@@ -260,6 +266,10 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol);
 	struct snd_ctl_elem_value *ucontrol);
 int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
 int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol);
 	struct snd_ctl_elem_value *ucontrol);
+int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol);
+int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol);
 int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol,
 int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol);
 	struct snd_ctl_elem_value *ucontrol);
 int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
 int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
@@ -333,6 +343,10 @@ struct snd_soc_dapm_route {
 	const char *sink;
 	const char *sink;
 	const char *control;
 	const char *control;
 	const char *source;
 	const char *source;
+
+	/* Note: currently only supported for links where source is a supply */
+	int (*connected)(struct snd_soc_dapm_widget *source,
+			 struct snd_soc_dapm_widget *sink);
 };
 };
 
 
 /* dapm audio path between two widgets */
 /* dapm audio path between two widgets */
@@ -349,6 +363,9 @@ struct snd_soc_dapm_path {
 	u32 connect:1;	/* source and sink widgets are connected */
 	u32 connect:1;	/* source and sink widgets are connected */
 	u32 walked:1;	/* path has been walked */
 	u32 walked:1;	/* path has been walked */
 
 
+	int (*connected)(struct snd_soc_dapm_widget *source,
+			 struct snd_soc_dapm_widget *sink);
+
 	struct list_head list_source;
 	struct list_head list_source;
 	struct list_head list_sink;
 	struct list_head list_sink;
 	struct list_head list;
 	struct list_head list;

+ 9 - 6
include/sound/soc.h

@@ -223,15 +223,15 @@ 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);
 
 
-#ifdef CONFIG_PM
-int snd_soc_suspend_device(struct device *dev);
-int snd_soc_resume_device(struct device *dev);
-#endif
-
 /* pcm <-> DAI connect */
 /* pcm <-> DAI connect */
 void snd_soc_free_pcms(struct snd_soc_device *socdev);
 void snd_soc_free_pcms(struct snd_soc_device *socdev);
 int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid);
 int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid);
-int snd_soc_init_card(struct snd_soc_device *socdev);
+
+/* 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_params_to_frame_size(struct snd_pcm_hw_params *params);
+int snd_soc_calc_bclk(int fs, int sample_size, int channels, int tdm_slots);
+int snd_soc_params_to_bclk(struct snd_pcm_hw_params *parms);
 
 
 /* set runtime hw params */
 /* set runtime hw params */
 int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
 int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
@@ -333,6 +333,8 @@ struct snd_soc_jack_gpio {
 	int debounce_time;
 	int debounce_time;
 	struct snd_soc_jack *jack;
 	struct snd_soc_jack *jack;
 	struct work_struct work;
 	struct work_struct work;
+
+	int (*jack_status_check)(void);
 };
 };
 #endif
 #endif
 
 
@@ -413,6 +415,7 @@ struct snd_soc_codec {
 	unsigned int num_dai;
 	unsigned int num_dai;
 
 
 #ifdef CONFIG_DEBUG_FS
 #ifdef CONFIG_DEBUG_FS
+	struct dentry *debugfs_codec_root;
 	struct dentry *debugfs_reg;
 	struct dentry *debugfs_reg;
 	struct dentry *debugfs_pop_time;
 	struct dentry *debugfs_pop_time;
 	struct dentry *debugfs_dapm;
 	struct dentry *debugfs_dapm;

+ 0 - 21
include/sound/sscape_ioctl.h

@@ -1,21 +0,0 @@
-#ifndef SSCAPE_IOCTL_H
-#define SSCAPE_IOCTL_H
-
-
-struct sscape_bootblock
-{
-  unsigned char code[256];
-  unsigned version;
-};
-
-#define SSCAPE_MICROCODE_SIZE  65536
-
-struct sscape_microcode
-{
-  unsigned char __user *code;
-};
-
-#define SND_SSCAPE_LOAD_BOOTB  _IOWR('P', 100, struct sscape_bootblock)
-#define SND_SSCAPE_LOAD_MCODE  _IOW ('P', 101, struct sscape_microcode)
-
-#endif

+ 20 - 0
include/sound/tlv320dac33-plat.h

@@ -0,0 +1,20 @@
+/*
+ * Platform header for Texas Instruments TLV320DAC33 codec driver
+ *
+ * Author:	Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * Copyright:   (C) 2009 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __TLV320DAC33_PLAT_H
+#define __TLV320DAC33_PLAT_H
+
+struct tlv320dac33_platform_data {
+	int power_gpio;
+};
+
+#endif /* __TLV320DAC33_PLAT_H */

+ 30 - 0
include/sound/tpa6130a2-plat.h

@@ -0,0 +1,30 @@
+/*
+ * TPA6130A2 driver platform header
+ *
+ * Copyright (C) Nokia Corporation
+ *
+ * Written by Peter Ujfalusi <peter.ujfalusi@nokia.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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef TPA6130A2_PLAT_H
+#define TPA6130A2_PLAT_H
+
+struct tpa6130a2_platform_data {
+	int power_gpio;
+};
+
+#endif

+ 0 - 1
include/sound/wss.h

@@ -154,7 +154,6 @@ int snd_wss_create(struct snd_card *card,
 		      unsigned short hardware,
 		      unsigned short hardware,
 		      unsigned short hwshare,
 		      unsigned short hwshare,
 		      struct snd_wss **rchip);
 		      struct snd_wss **rchip);
-int snd_wss_free(struct snd_wss *chip);
 int snd_wss_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm);
 int snd_wss_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm);
 int snd_wss_timer(struct snd_wss *chip, int device, struct snd_timer **rtimer);
 int snd_wss_timer(struct snd_wss *chip, int device, struct snd_timer **rtimer);
 int snd_wss_mixer(struct snd_wss *chip);
 int snd_wss_mixer(struct snd_wss *chip);

+ 1 - 1
sound/Kconfig

@@ -58,7 +58,7 @@ config SOUND_OSS_CORE_PRECLAIM
 	  Please read Documentation/feature-removal-schedule.txt for
 	  Please read Documentation/feature-removal-schedule.txt for
 	  details.
 	  details.
 
 
-	  If unusre, say Y.
+	  If unsure, say Y.
 
 
 source "sound/oss/dmasound/Kconfig"
 source "sound/oss/dmasound/Kconfig"
 
 

+ 1 - 1
sound/arm/Makefile

@@ -3,7 +3,7 @@
 #
 #
 
 
 obj-$(CONFIG_SND_ARMAACI)	+= snd-aaci.o
 obj-$(CONFIG_SND_ARMAACI)	+= snd-aaci.o
-snd-aaci-objs			:= aaci.o devdma.o
+snd-aaci-objs			:= aaci.o
 
 
 obj-$(CONFIG_SND_PXA2XX_PCM)	+= snd-pxa2xx-pcm.o
 obj-$(CONFIG_SND_PXA2XX_PCM)	+= snd-pxa2xx-pcm.o
 snd-pxa2xx-pcm-objs		:= pxa2xx-pcm.o
 snd-pxa2xx-pcm-objs		:= pxa2xx-pcm.o

+ 10 - 25
sound/arm/aaci.c

@@ -18,10 +18,7 @@
 #include <linux/interrupt.h>
 #include <linux/interrupt.h>
 #include <linux/err.h>
 #include <linux/err.h>
 #include <linux/amba/bus.h>
 #include <linux/amba/bus.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/sizes.h>
+#include <linux/io.h>
 
 
 #include <sound/core.h>
 #include <sound/core.h>
 #include <sound/initval.h>
 #include <sound/initval.h>
@@ -30,7 +27,6 @@
 #include <sound/pcm_params.h>
 #include <sound/pcm_params.h>
 
 
 #include "aaci.h"
 #include "aaci.h"
-#include "devdma.h"
 
 
 #define DRIVER_NAME	"aaci-pl041"
 #define DRIVER_NAME	"aaci-pl041"
 
 
@@ -492,7 +488,7 @@ static int aaci_pcm_hw_free(struct snd_pcm_substream *substream)
 	/*
 	/*
 	 * Clear out the DMA and any allocated buffers.
 	 * Clear out the DMA and any allocated buffers.
 	 */
 	 */
-	devdma_hw_free(NULL, substream);
+	snd_pcm_lib_free_pages(substream);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -509,20 +505,14 @@ static int aaci_pcm_hw_params(struct snd_pcm_substream *substream,
 		aacirun->pcm_open = 0;
 		aacirun->pcm_open = 0;
 	}
 	}
 
 
-	err = devdma_hw_alloc(NULL, substream,
-			      params_buffer_bytes(params));
+	err = snd_pcm_lib_malloc_pages(substream,
+				       params_buffer_bytes(params));
 	if (err < 0)
 	if (err < 0)
 		goto out;
 		goto out;
 
 
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		err = snd_ac97_pcm_open(aacirun->pcm, params_rate(params),
-					params_channels(params),
-					aacirun->pcm->r[0].slots);
-	else
-		err = snd_ac97_pcm_open(aacirun->pcm, params_rate(params),
-					params_channels(params),
-					aacirun->pcm->r[0].slots);
-
+	err = snd_ac97_pcm_open(aacirun->pcm, params_rate(params),
+				params_channels(params),
+				aacirun->pcm->r[0].slots);
 	if (err)
 	if (err)
 		goto out;
 		goto out;
 
 
@@ -538,7 +528,7 @@ static int aaci_pcm_prepare(struct snd_pcm_substream *substream)
 	struct aaci_runtime *aacirun = runtime->private_data;
 	struct aaci_runtime *aacirun = runtime->private_data;
 
 
 	aacirun->start	= (void *)runtime->dma_area;
 	aacirun->start	= (void *)runtime->dma_area;
-	aacirun->end	= aacirun->start + runtime->dma_bytes;
+	aacirun->end	= aacirun->start + snd_pcm_lib_buffer_bytes(substream);
 	aacirun->ptr	= aacirun->start;
 	aacirun->ptr	= aacirun->start;
 	aacirun->period	=
 	aacirun->period	=
 	aacirun->bytes	= frames_to_bytes(runtime, runtime->period_size);
 	aacirun->bytes	= frames_to_bytes(runtime, runtime->period_size);
@@ -555,11 +545,6 @@ static snd_pcm_uframes_t aaci_pcm_pointer(struct snd_pcm_substream *substream)
 	return bytes_to_frames(runtime, bytes);
 	return bytes_to_frames(runtime, bytes);
 }
 }
 
 
-static int aaci_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma)
-{
-	return devdma_mmap(NULL, substream, vma);
-}
-
 
 
 /*
 /*
  * Playback specific ALSA stuff
  * Playback specific ALSA stuff
@@ -726,7 +711,6 @@ static struct snd_pcm_ops aaci_playback_ops = {
 	.prepare	= aaci_pcm_prepare,
 	.prepare	= aaci_pcm_prepare,
 	.trigger	= aaci_pcm_playback_trigger,
 	.trigger	= aaci_pcm_playback_trigger,
 	.pointer	= aaci_pcm_pointer,
 	.pointer	= aaci_pcm_pointer,
-	.mmap		= aaci_pcm_mmap,
 };
 };
 
 
 static int aaci_pcm_capture_hw_params(struct snd_pcm_substream *substream,
 static int aaci_pcm_capture_hw_params(struct snd_pcm_substream *substream,
@@ -854,7 +838,6 @@ static struct snd_pcm_ops aaci_capture_ops = {
 	.prepare	= aaci_pcm_capture_prepare,
 	.prepare	= aaci_pcm_capture_prepare,
 	.trigger	= aaci_pcm_capture_trigger,
 	.trigger	= aaci_pcm_capture_trigger,
 	.pointer	= aaci_pcm_pointer,
 	.pointer	= aaci_pcm_pointer,
-	.mmap		= aaci_pcm_mmap,
 };
 };
 
 
 /*
 /*
@@ -1044,6 +1027,8 @@ static int __devinit aaci_init_pcm(struct aaci *aaci)
 
 
 		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &aaci_playback_ops);
 		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &aaci_playback_ops);
 		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &aaci_capture_ops);
 		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &aaci_capture_ops);
+		snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+						      NULL, 0, 64 * 104);
 	}
 	}
 
 
 	return ret;
 	return ret;

+ 0 - 80
sound/arm/devdma.c

@@ -1,80 +0,0 @@
-/*
- *  linux/sound/arm/devdma.c
- *
- *  Copyright (C) 2003-2004 Russell King, All rights reserved.
- *
- * 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.
- *
- *  ARM DMA shim for ALSA.
- */
-#include <linux/device.h>
-#include <linux/dma-mapping.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-
-#include "devdma.h"
-
-void devdma_hw_free(struct device *dev, struct snd_pcm_substream *substream)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct snd_dma_buffer *buf = runtime->dma_buffer_p;
-
-	if (runtime->dma_area == NULL)
-		return;
-
-	if (buf != &substream->dma_buffer) {
-		dma_free_coherent(buf->dev.dev, buf->bytes, buf->area, buf->addr);
-		kfree(runtime->dma_buffer_p);
-	}
-
-	snd_pcm_set_runtime_buffer(substream, NULL);
-}
-
-int devdma_hw_alloc(struct device *dev, struct snd_pcm_substream *substream, size_t size)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct snd_dma_buffer *buf = runtime->dma_buffer_p;
-	int ret = 0;
-
-	if (buf) {
-		if (buf->bytes >= size)
-			goto out;
-		devdma_hw_free(dev, substream);
-	}
-
-	if (substream->dma_buffer.area != NULL && substream->dma_buffer.bytes >= size) {
-		buf = &substream->dma_buffer;
-	} else {
-		buf = kmalloc(sizeof(struct snd_dma_buffer), GFP_KERNEL);
-		if (!buf)
-			goto nomem;
-
-		buf->dev.type = SNDRV_DMA_TYPE_DEV;
-		buf->dev.dev = dev;
-		buf->area = dma_alloc_coherent(dev, size, &buf->addr, GFP_KERNEL);
-		buf->bytes = size;
-		buf->private_data = NULL;
-
-		if (!buf->area)
-			goto free;
-	}
-	snd_pcm_set_runtime_buffer(substream, buf);
-	ret = 1;
- out:
-	runtime->dma_bytes = size;
-	return ret;
-
- free:
-	kfree(buf);
- nomem:
-	return -ENOMEM;
-}
-
-int devdma_mmap(struct device *dev, struct snd_pcm_substream *substream, struct vm_area_struct *vma)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	return dma_mmap_coherent(dev, vma, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes);
-}

+ 0 - 3
sound/arm/devdma.h

@@ -1,3 +0,0 @@
-void devdma_hw_free(struct device *dev, struct snd_pcm_substream *substream);
-int devdma_hw_alloc(struct device *dev, struct snd_pcm_substream *substream, size_t size);
-int devdma_mmap(struct device *dev, struct snd_pcm_substream *substream, struct vm_area_struct *vma);

+ 4 - 5
sound/core/control.c

@@ -75,7 +75,7 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
 	ctl->card = card;
 	ctl->card = card;
 	ctl->prefer_pcm_subdevice = -1;
 	ctl->prefer_pcm_subdevice = -1;
 	ctl->prefer_rawmidi_subdevice = -1;
 	ctl->prefer_rawmidi_subdevice = -1;
-	ctl->pid = current->pid;
+	ctl->pid = get_pid(task_pid(current));
 	file->private_data = ctl;
 	file->private_data = ctl;
 	write_lock_irqsave(&card->ctl_files_rwlock, flags);
 	write_lock_irqsave(&card->ctl_files_rwlock, flags);
 	list_add_tail(&ctl->list, &card->ctl_files);
 	list_add_tail(&ctl->list, &card->ctl_files);
@@ -125,6 +125,7 @@ static int snd_ctl_release(struct inode *inode, struct file *file)
 				control->vd[idx].owner = NULL;
 				control->vd[idx].owner = NULL;
 	up_write(&card->controls_rwsem);
 	up_write(&card->controls_rwsem);
 	snd_ctl_empty_read_queue(ctl);
 	snd_ctl_empty_read_queue(ctl);
+	put_pid(ctl->pid);
 	kfree(ctl);
 	kfree(ctl);
 	module_put(card->module);
 	module_put(card->module);
 	snd_card_file_remove(card, file);
 	snd_card_file_remove(card, file);
@@ -672,7 +673,7 @@ static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
 			info->access |= SNDRV_CTL_ELEM_ACCESS_LOCK;
 			info->access |= SNDRV_CTL_ELEM_ACCESS_LOCK;
 			if (vd->owner == ctl)
 			if (vd->owner == ctl)
 				info->access |= SNDRV_CTL_ELEM_ACCESS_OWNER;
 				info->access |= SNDRV_CTL_ELEM_ACCESS_OWNER;
-			info->owner = vd->owner_pid;
+			info->owner = pid_vnr(vd->owner->pid);
 		} else {
 		} else {
 			info->owner = -1;
 			info->owner = -1;
 		}
 		}
@@ -827,7 +828,6 @@ static int snd_ctl_elem_lock(struct snd_ctl_file *file,
 			result = -EBUSY;
 			result = -EBUSY;
 		else {
 		else {
 			vd->owner = file;
 			vd->owner = file;
-			vd->owner_pid = current->pid;
 			result = 0;
 			result = 0;
 		}
 		}
 	}
 	}
@@ -858,7 +858,6 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
 			result = -EPERM;
 			result = -EPERM;
 		else {
 		else {
 			vd->owner = NULL;
 			vd->owner = NULL;
-			vd->owner_pid = 0;
 			result = 0;
 			result = 0;
 		}
 		}
 	}
 	}
@@ -1120,7 +1119,7 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
 	    	goto __kctl_end;
 	    	goto __kctl_end;
 	}
 	}
 	if (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
 	if (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
-		if (file && vd->owner != NULL && vd->owner != file) {
+		if (vd->owner != NULL && vd->owner != file) {
 			err = -EPERM;
 			err = -EPERM;
 			goto __kctl_end;
 			goto __kctl_end;
 		}
 		}

+ 9 - 1
sound/core/isadma.c

@@ -85,16 +85,24 @@ EXPORT_SYMBOL(snd_dma_disable);
 unsigned int snd_dma_pointer(unsigned long dma, unsigned int size)
 unsigned int snd_dma_pointer(unsigned long dma, unsigned int size)
 {
 {
 	unsigned long flags;
 	unsigned long flags;
-	unsigned int result;
+	unsigned int result, result1;
 
 
 	flags = claim_dma_lock();
 	flags = claim_dma_lock();
 	clear_dma_ff(dma);
 	clear_dma_ff(dma);
 	if (!isa_dma_bridge_buggy)
 	if (!isa_dma_bridge_buggy)
 		disable_dma(dma);
 		disable_dma(dma);
 	result = get_dma_residue(dma);
 	result = get_dma_residue(dma);
+	/*
+	 * HACK - read the counter again and choose higher value in order to
+	 * avoid reading during counter lower byte roll over if the
+	 * isa_dma_bridge_buggy is set.
+	 */
+	result1 = get_dma_residue(dma);
 	if (!isa_dma_bridge_buggy)
 	if (!isa_dma_bridge_buggy)
 		enable_dma(dma);
 		enable_dma(dma);
 	release_dma_lock(flags);
 	release_dma_lock(flags);
+	if (unlikely(result < result1))
+		result = result1;
 #ifdef CONFIG_SND_DEBUG
 #ifdef CONFIG_SND_DEBUG
 	if (result > size)
 	if (result > size)
 		snd_printk(KERN_ERR "pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size);
 		snd_printk(KERN_ERR "pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size);

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

@@ -1251,7 +1251,9 @@ static void snd_mixer_oss_build(struct snd_mixer_oss *mixer)
 		{ SOUND_MIXER_SYNTH,	"FM",			0 }, /* fallback */
 		{ SOUND_MIXER_SYNTH,	"FM",			0 }, /* fallback */
 		{ SOUND_MIXER_SYNTH,	"Music",		0 }, /* fallback */
 		{ SOUND_MIXER_SYNTH,	"Music",		0 }, /* fallback */
 		{ SOUND_MIXER_PCM,	"PCM",			0 },
 		{ SOUND_MIXER_PCM,	"PCM",			0 },
-		{ SOUND_MIXER_SPEAKER,	"PC Speaker", 		0 },
+		{ SOUND_MIXER_SPEAKER,	"Beep", 		0 },
+		{ SOUND_MIXER_SPEAKER,	"PC Speaker", 		0 }, /* fallback */
+		{ SOUND_MIXER_SPEAKER,	"Speaker", 		0 }, /* fallback */
 		{ SOUND_MIXER_LINE,	"Line", 		0 },
 		{ SOUND_MIXER_LINE,	"Line", 		0 },
 		{ SOUND_MIXER_MIC,	"Mic", 			0 },
 		{ SOUND_MIXER_MIC,	"Mic", 			0 },
 		{ SOUND_MIXER_CD,	"CD", 			0 },
 		{ SOUND_MIXER_CD,	"CD", 			0 },

+ 5 - 1
sound/core/pcm.c

@@ -435,6 +435,7 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry,
 		return;
 		return;
 	}
 	}
 	snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state));
 	snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state));
+	snd_iprintf(buffer, "owner_pid   : %d\n", pid_vnr(substream->pid));
 	snd_iprintf(buffer, "trigger_time: %ld.%09ld\n",
 	snd_iprintf(buffer, "trigger_time: %ld.%09ld\n",
 		status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_nsec);
 		status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_nsec);
 	snd_iprintf(buffer, "tstamp      : %ld.%09ld\n",
 	snd_iprintf(buffer, "tstamp      : %ld.%09ld\n",
@@ -809,7 +810,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
 	card = pcm->card;
 	card = pcm->card;
 	read_lock(&card->ctl_files_rwlock);
 	read_lock(&card->ctl_files_rwlock);
 	list_for_each_entry(kctl, &card->ctl_files, list) {
 	list_for_each_entry(kctl, &card->ctl_files, list) {
-		if (kctl->pid == current->pid) {
+		if (kctl->pid == task_pid(current)) {
 			prefer_subdevice = kctl->prefer_pcm_subdevice;
 			prefer_subdevice = kctl->prefer_pcm_subdevice;
 			if (prefer_subdevice != -1)
 			if (prefer_subdevice != -1)
 				break;
 				break;
@@ -900,6 +901,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
 	substream->private_data = pcm->private_data;
 	substream->private_data = pcm->private_data;
 	substream->ref_count = 1;
 	substream->ref_count = 1;
 	substream->f_flags = file->f_flags;
 	substream->f_flags = file->f_flags;
+	substream->pid = get_pid(task_pid(current));
 	pstr->substream_opened++;
 	pstr->substream_opened++;
 	*rsubstream = substream;
 	*rsubstream = substream;
 	return 0;
 	return 0;
@@ -921,6 +923,8 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
 	kfree(runtime->hw_constraints.rules);
 	kfree(runtime->hw_constraints.rules);
 	kfree(runtime);
 	kfree(runtime);
 	substream->runtime = NULL;
 	substream->runtime = NULL;
+	put_pid(substream->pid);
+	substream->pid = NULL;
 	substream->pstr->substream_opened--;
 	substream->pstr->substream_opened--;
 }
 }
 
 

+ 59 - 24
sound/core/pcm_native.c

@@ -26,6 +26,7 @@
 #include <linux/time.h>
 #include <linux/time.h>
 #include <linux/pm_qos_params.h>
 #include <linux/pm_qos_params.h>
 #include <linux/uio.h>
 #include <linux/uio.h>
+#include <linux/dma-mapping.h>
 #include <sound/core.h>
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/control.h>
 #include <sound/info.h>
 #include <sound/info.h>
@@ -3061,6 +3062,27 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file
 }
 }
 #endif /* coherent mmap */
 #endif /* coherent mmap */
 
 
+static inline struct page *
+snd_pcm_default_page_ops(struct snd_pcm_substream *substream, unsigned long ofs)
+{
+	void *vaddr = substream->runtime->dma_area + ofs;
+#if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT)
+	if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
+		return virt_to_page(CAC_ADDR(vaddr));
+#endif
+#if defined(CONFIG_PPC32) && defined(CONFIG_NOT_COHERENT_CACHE)
+	if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) {
+		dma_addr_t addr = substream->runtime->dma_addr + ofs;
+		addr -= get_dma_offset(substream->dma_buffer.dev.dev);
+		/* assume dma_handle set via pfn_to_phys() in
+		 * mm/dma-noncoherent.c
+		 */
+		return pfn_to_page(addr >> PAGE_SHIFT);
+	}
+#endif
+	return virt_to_page(vaddr);
+}
+
 /*
 /*
  * fault callback for mmapping a RAM page
  * fault callback for mmapping a RAM page
  */
  */
@@ -3071,7 +3093,6 @@ static int snd_pcm_mmap_data_fault(struct vm_area_struct *area,
 	struct snd_pcm_runtime *runtime;
 	struct snd_pcm_runtime *runtime;
 	unsigned long offset;
 	unsigned long offset;
 	struct page * page;
 	struct page * page;
-	void *vaddr;
 	size_t dma_bytes;
 	size_t dma_bytes;
 	
 	
 	if (substream == NULL)
 	if (substream == NULL)
@@ -3081,36 +3102,53 @@ static int snd_pcm_mmap_data_fault(struct vm_area_struct *area,
 	dma_bytes = PAGE_ALIGN(runtime->dma_bytes);
 	dma_bytes = PAGE_ALIGN(runtime->dma_bytes);
 	if (offset > dma_bytes - PAGE_SIZE)
 	if (offset > dma_bytes - PAGE_SIZE)
 		return VM_FAULT_SIGBUS;
 		return VM_FAULT_SIGBUS;
-	if (substream->ops->page) {
+	if (substream->ops->page)
 		page = substream->ops->page(substream, offset);
 		page = substream->ops->page(substream, offset);
-		if (!page)
-			return VM_FAULT_SIGBUS;
-	} else {
-		vaddr = runtime->dma_area + offset;
-		page = virt_to_page(vaddr);
-	}
+	else
+		page = snd_pcm_default_page_ops(substream, offset);
+	if (!page)
+		return VM_FAULT_SIGBUS;
 	get_page(page);
 	get_page(page);
 	vmf->page = page;
 	vmf->page = page;
 	return 0;
 	return 0;
 }
 }
 
 
-static const struct vm_operations_struct snd_pcm_vm_ops_data =
-{
+static const struct vm_operations_struct snd_pcm_vm_ops_data = {
+	.open =		snd_pcm_mmap_data_open,
+	.close =	snd_pcm_mmap_data_close,
+};
+
+static const struct vm_operations_struct snd_pcm_vm_ops_data_fault = {
 	.open =		snd_pcm_mmap_data_open,
 	.open =		snd_pcm_mmap_data_open,
 	.close =	snd_pcm_mmap_data_close,
 	.close =	snd_pcm_mmap_data_close,
 	.fault =	snd_pcm_mmap_data_fault,
 	.fault =	snd_pcm_mmap_data_fault,
 };
 };
 
 
+#ifndef ARCH_HAS_DMA_MMAP_COHERENT
+/* This should be defined / handled globally! */
+#ifdef CONFIG_ARM
+#define ARCH_HAS_DMA_MMAP_COHERENT
+#endif
+#endif
+
 /*
 /*
  * mmap the DMA buffer on RAM
  * mmap the DMA buffer on RAM
  */
  */
 static int snd_pcm_default_mmap(struct snd_pcm_substream *substream,
 static int snd_pcm_default_mmap(struct snd_pcm_substream *substream,
 				struct vm_area_struct *area)
 				struct vm_area_struct *area)
 {
 {
-	area->vm_ops = &snd_pcm_vm_ops_data;
-	area->vm_private_data = substream;
 	area->vm_flags |= VM_RESERVED;
 	area->vm_flags |= VM_RESERVED;
-	atomic_inc(&substream->mmap_count);
+#ifdef ARCH_HAS_DMA_MMAP_COHERENT
+	if (!substream->ops->page &&
+	    substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
+		return dma_mmap_coherent(substream->dma_buffer.dev.dev,
+					 area,
+					 substream->runtime->dma_area,
+					 substream->runtime->dma_addr,
+					 area->vm_end - area->vm_start);
+#endif /* ARCH_HAS_DMA_MMAP_COHERENT */
+	/* mmap with fault handler */
+	area->vm_ops = &snd_pcm_vm_ops_data_fault;
 	return 0;
 	return 0;
 }
 }
 
 
@@ -3118,12 +3156,6 @@ static int snd_pcm_default_mmap(struct snd_pcm_substream *substream,
  * mmap the DMA buffer on I/O memory area
  * mmap the DMA buffer on I/O memory area
  */
  */
 #if SNDRV_PCM_INFO_MMAP_IOMEM
 #if SNDRV_PCM_INFO_MMAP_IOMEM
-static const struct vm_operations_struct snd_pcm_vm_ops_data_mmio =
-{
-	.open =		snd_pcm_mmap_data_open,
-	.close =	snd_pcm_mmap_data_close,
-};
-
 int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,
 int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,
 			   struct vm_area_struct *area)
 			   struct vm_area_struct *area)
 {
 {
@@ -3133,8 +3165,6 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,
 #ifdef pgprot_noncached
 #ifdef pgprot_noncached
 	area->vm_page_prot = pgprot_noncached(area->vm_page_prot);
 	area->vm_page_prot = pgprot_noncached(area->vm_page_prot);
 #endif
 #endif
-	area->vm_ops = &snd_pcm_vm_ops_data_mmio;
-	area->vm_private_data = substream;
 	area->vm_flags |= VM_IO;
 	area->vm_flags |= VM_IO;
 	size = area->vm_end - area->vm_start;
 	size = area->vm_end - area->vm_start;
 	offset = area->vm_pgoff << PAGE_SHIFT;
 	offset = area->vm_pgoff << PAGE_SHIFT;
@@ -3142,7 +3172,6 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,
 				(substream->runtime->dma_addr + offset) >> PAGE_SHIFT,
 				(substream->runtime->dma_addr + offset) >> PAGE_SHIFT,
 				size, area->vm_page_prot))
 				size, area->vm_page_prot))
 		return -EAGAIN;
 		return -EAGAIN;
-	atomic_inc(&substream->mmap_count);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -3159,6 +3188,7 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file,
 	long size;
 	long size;
 	unsigned long offset;
 	unsigned long offset;
 	size_t dma_bytes;
 	size_t dma_bytes;
+	int err;
 
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		if (!(area->vm_flags & (VM_WRITE|VM_READ)))
 		if (!(area->vm_flags & (VM_WRITE|VM_READ)))
@@ -3183,10 +3213,15 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file,
 	if (offset > dma_bytes - size)
 	if (offset > dma_bytes - size)
 		return -EINVAL;
 		return -EINVAL;
 
 
+	area->vm_ops = &snd_pcm_vm_ops_data;
+	area->vm_private_data = substream;
 	if (substream->ops->mmap)
 	if (substream->ops->mmap)
-		return substream->ops->mmap(substream, area);
+		err = substream->ops->mmap(substream, area);
 	else
 	else
-		return snd_pcm_default_mmap(substream, area);
+		err = snd_pcm_default_mmap(substream, area);
+	if (!err)
+		atomic_inc(&substream->mmap_count);
+	return err;
 }
 }
 
 
 EXPORT_SYMBOL(snd_pcm_mmap_data);
 EXPORT_SYMBOL(snd_pcm_mmap_data);

+ 12 - 5
sound/core/rawmidi.c

@@ -242,8 +242,6 @@ static int assign_substream(struct snd_rawmidi *rmidi, int subdevice,
 		return -ENXIO;
 		return -ENXIO;
 	if (subdevice >= 0 && subdevice >= s->substream_count)
 	if (subdevice >= 0 && subdevice >= s->substream_count)
 		return -ENODEV;
 		return -ENODEV;
-	if (s->substream_opened >= s->substream_count)
-		return -EAGAIN;
 
 
 	list_for_each_entry(substream, &s->substreams, list) {
 	list_for_each_entry(substream, &s->substreams, list) {
 		if (substream->opened) {
 		if (substream->opened) {
@@ -280,9 +278,10 @@ static int open_substream(struct snd_rawmidi *rmidi,
 		substream->active_sensing = 0;
 		substream->active_sensing = 0;
 		if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
 		if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
 			substream->append = 1;
 			substream->append = 1;
+		substream->pid = get_pid(task_pid(current));
+		rmidi->streams[substream->stream].substream_opened++;
 	}
 	}
 	substream->use_count++;
 	substream->use_count++;
-	rmidi->streams[substream->stream].substream_opened++;
 	return 0;
 	return 0;
 }
 }
 
 
@@ -413,7 +412,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
 		subdevice = -1;
 		subdevice = -1;
 		read_lock(&card->ctl_files_rwlock);
 		read_lock(&card->ctl_files_rwlock);
 		list_for_each_entry(kctl, &card->ctl_files, list) {
 		list_for_each_entry(kctl, &card->ctl_files, list) {
-			if (kctl->pid == current->pid) {
+			if (kctl->pid == task_pid(current)) {
 				subdevice = kctl->prefer_rawmidi_subdevice;
 				subdevice = kctl->prefer_rawmidi_subdevice;
 				if (subdevice != -1)
 				if (subdevice != -1)
 					break;
 					break;
@@ -466,7 +465,6 @@ static void close_substream(struct snd_rawmidi *rmidi,
 			    struct snd_rawmidi_substream *substream,
 			    struct snd_rawmidi_substream *substream,
 			    int cleanup)
 			    int cleanup)
 {
 {
-	rmidi->streams[substream->stream].substream_opened--;
 	if (--substream->use_count)
 	if (--substream->use_count)
 		return;
 		return;
 
 
@@ -491,6 +489,9 @@ static void close_substream(struct snd_rawmidi *rmidi,
 	snd_rawmidi_runtime_free(substream);
 	snd_rawmidi_runtime_free(substream);
 	substream->opened = 0;
 	substream->opened = 0;
 	substream->append = 0;
 	substream->append = 0;
+	put_pid(substream->pid);
+	substream->pid = NULL;
+	rmidi->streams[substream->stream].substream_opened--;
 }
 }
 
 
 static void rawmidi_release_priv(struct snd_rawmidi_file *rfile)
 static void rawmidi_release_priv(struct snd_rawmidi_file *rfile)
@@ -1338,6 +1339,9 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
 				    substream->number,
 				    substream->number,
 				    (unsigned long) substream->bytes);
 				    (unsigned long) substream->bytes);
 			if (substream->opened) {
 			if (substream->opened) {
+				snd_iprintf(buffer,
+				    "  Owner PID    : %d\n",
+				    pid_vnr(substream->pid));
 				runtime = substream->runtime;
 				runtime = substream->runtime;
 				snd_iprintf(buffer,
 				snd_iprintf(buffer,
 				    "  Mode         : %s\n"
 				    "  Mode         : %s\n"
@@ -1359,6 +1363,9 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
 				    substream->number,
 				    substream->number,
 				    (unsigned long) substream->bytes);
 				    (unsigned long) substream->bytes);
 			if (substream->opened) {
 			if (substream->opened) {
+				snd_iprintf(buffer,
+					    "  Owner PID    : %d\n",
+					    pid_vnr(substream->pid));
 				runtime = substream->runtime;
 				runtime = substream->runtime;
 				snd_iprintf(buffer,
 				snd_iprintf(buffer,
 					    "  Buffer size  : %lu\n"
 					    "  Buffer size  : %lu\n"

+ 20 - 12
sound/drivers/pcsp/pcsp.c

@@ -26,6 +26,7 @@ MODULE_ALIAS("platform:pcspkr");
 static int index = SNDRV_DEFAULT_IDX1;	/* Index 0-MAX */
 static int index = SNDRV_DEFAULT_IDX1;	/* Index 0-MAX */
 static char *id = SNDRV_DEFAULT_STR1;	/* ID for this card */
 static char *id = SNDRV_DEFAULT_STR1;	/* ID for this card */
 static int enable = SNDRV_DEFAULT_ENABLE1;	/* Enable this card */
 static int enable = SNDRV_DEFAULT_ENABLE1;	/* Enable this card */
+static int nopcm;	/* Disable PCM capability of the driver */
 
 
 module_param(index, int, 0444);
 module_param(index, int, 0444);
 MODULE_PARM_DESC(index, "Index value for pcsp soundcard.");
 MODULE_PARM_DESC(index, "Index value for pcsp soundcard.");
@@ -33,6 +34,8 @@ module_param(id, charp, 0444);
 MODULE_PARM_DESC(id, "ID string for pcsp soundcard.");
 MODULE_PARM_DESC(id, "ID string for pcsp soundcard.");
 module_param(enable, bool, 0444);
 module_param(enable, bool, 0444);
 MODULE_PARM_DESC(enable, "Enable PC-Speaker sound.");
 MODULE_PARM_DESC(enable, "Enable PC-Speaker sound.");
+module_param(nopcm, bool, 0444);
+MODULE_PARM_DESC(nopcm, "Disable PC-Speaker PCM sound. Only beeps remain.");
 
 
 struct snd_pcsp pcsp_chip;
 struct snd_pcsp pcsp_chip;
 
 
@@ -43,13 +46,16 @@ static int __devinit snd_pcsp_create(struct snd_card *card)
 	int err;
 	int err;
 	int div, min_div, order;
 	int div, min_div, order;
 
 
-	hrtimer_get_res(CLOCK_MONOTONIC, &tp);
-	if (tp.tv_sec || tp.tv_nsec > PCSP_MAX_PERIOD_NS) {
-		printk(KERN_ERR "PCSP: Timer resolution is not sufficient "
-		       "(%linS)\n", tp.tv_nsec);
-		printk(KERN_ERR "PCSP: Make sure you have HPET and ACPI "
-		       "enabled.\n");
-		return -EIO;
+	if (!nopcm) {
+		hrtimer_get_res(CLOCK_MONOTONIC, &tp);
+		if (tp.tv_sec || tp.tv_nsec > PCSP_MAX_PERIOD_NS) {
+			printk(KERN_ERR "PCSP: Timer resolution is not sufficient "
+				"(%linS)\n", tp.tv_nsec);
+			printk(KERN_ERR "PCSP: Make sure you have HPET and ACPI "
+				"enabled.\n");
+			printk(KERN_ERR "PCSP: Turned into nopcm mode.\n");
+			nopcm = 1;
+		}
 	}
 	}
 
 
 	if (loops_per_jiffy >= PCSP_MIN_LPJ && tp.tv_nsec <= PCSP_MIN_PERIOD_NS)
 	if (loops_per_jiffy >= PCSP_MIN_LPJ && tp.tv_nsec <= PCSP_MIN_PERIOD_NS)
@@ -107,12 +113,14 @@ static int __devinit snd_card_pcsp_probe(int devnum, struct device *dev)
 		snd_card_free(card);
 		snd_card_free(card);
 		return err;
 		return err;
 	}
 	}
-	err = snd_pcsp_new_pcm(&pcsp_chip);
-	if (err < 0) {
-		snd_card_free(card);
-		return err;
+	if (!nopcm) {
+		err = snd_pcsp_new_pcm(&pcsp_chip);
+		if (err < 0) {
+			snd_card_free(card);
+			return err;
+		}
 	}
 	}
-	err = snd_pcsp_new_mixer(&pcsp_chip);
+	err = snd_pcsp_new_mixer(&pcsp_chip, nopcm);
 	if (err < 0) {
 	if (err < 0) {
 		snd_card_free(card);
 		snd_card_free(card);
 		return err;
 		return err;

+ 1 - 1
sound/drivers/pcsp/pcsp.h

@@ -83,6 +83,6 @@ extern enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle);
 extern void pcsp_sync_stop(struct snd_pcsp *chip);
 extern void pcsp_sync_stop(struct snd_pcsp *chip);
 
 
 extern int snd_pcsp_new_pcm(struct snd_pcsp *chip);
 extern int snd_pcsp_new_pcm(struct snd_pcsp *chip);
-extern int snd_pcsp_new_mixer(struct snd_pcsp *chip);
+extern int snd_pcsp_new_mixer(struct snd_pcsp *chip, int nopcm);
 
 
 #endif
 #endif

+ 27 - 8
sound/drivers/pcsp/pcsp_mixer.c

@@ -119,24 +119,43 @@ static int pcsp_pcspkr_put(struct snd_kcontrol *kcontrol,
 	.put =		pcsp_##ctl_type##_put, \
 	.put =		pcsp_##ctl_type##_put, \
 }
 }
 
 
-static struct snd_kcontrol_new __devinitdata snd_pcsp_controls[] = {
+static struct snd_kcontrol_new __devinitdata snd_pcsp_controls_pcm[] = {
 	PCSP_MIXER_CONTROL(enable, "Master Playback Switch"),
 	PCSP_MIXER_CONTROL(enable, "Master Playback Switch"),
 	PCSP_MIXER_CONTROL(treble, "BaseFRQ Playback Volume"),
 	PCSP_MIXER_CONTROL(treble, "BaseFRQ Playback Volume"),
-	PCSP_MIXER_CONTROL(pcspkr, "PC Speaker Playback Switch"),
 };
 };
 
 
-int __devinit snd_pcsp_new_mixer(struct snd_pcsp *chip)
+static struct snd_kcontrol_new __devinitdata snd_pcsp_controls_spkr[] = {
+	PCSP_MIXER_CONTROL(pcspkr, "Beep Playback Switch"),
+};
+
+static int __devinit snd_pcsp_ctls_add(struct snd_pcsp *chip,
+	struct snd_kcontrol_new *ctls, int num)
 {
 {
-	struct snd_card *card = chip->card;
 	int i, err;
 	int i, err;
+	struct snd_card *card = chip->card;
+	for (i = 0; i < num; i++) {
+		err = snd_ctl_add(card, snd_ctl_new1(ctls + i, chip));
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+int __devinit snd_pcsp_new_mixer(struct snd_pcsp *chip, int nopcm)
+{
+	int err;
+	struct snd_card *card = chip->card;
 
 
-	for (i = 0; i < ARRAY_SIZE(snd_pcsp_controls); i++) {
-		err = snd_ctl_add(card,
-				 snd_ctl_new1(snd_pcsp_controls + i,
-					      chip));
+	if (!nopcm) {
+		err = snd_pcsp_ctls_add(chip, snd_pcsp_controls_pcm,
+			ARRAY_SIZE(snd_pcsp_controls_pcm));
 		if (err < 0)
 		if (err < 0)
 			return err;
 			return err;
 	}
 	}
+	err = snd_pcsp_ctls_add(chip, snd_pcsp_controls_spkr,
+		ARRAY_SIZE(snd_pcsp_controls_spkr));
+	if (err < 0)
+		return err;
 
 
 	strcpy(card->mixername, "PC-Speaker");
 	strcpy(card->mixername, "PC-Speaker");
 
 

+ 2 - 13
sound/i2c/cs8427.c

@@ -23,6 +23,7 @@
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/init.h>
+#include <linux/bitrev.h>
 #include <asm/unaligned.h>
 #include <asm/unaligned.h>
 #include <sound/core.h>
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/control.h>
@@ -55,18 +56,6 @@ struct cs8427 {
 	struct cs8427_stream capture;
 	struct cs8427_stream capture;
 };
 };
 
 
-static unsigned char swapbits(unsigned char val)
-{
-	int bit;
-	unsigned char res = 0;
-	for (bit = 0; bit < 8; bit++) {
-		res <<= 1;
-		res |= val & 1;
-		val >>= 1;
-	}
-	return res;
-}
-
 int snd_cs8427_reg_write(struct snd_i2c_device *device, unsigned char reg,
 int snd_cs8427_reg_write(struct snd_i2c_device *device, unsigned char reg,
 			 unsigned char val)
 			 unsigned char val)
 {
 {
@@ -149,7 +138,7 @@ static int snd_cs8427_send_corudata(struct snd_i2c_device *device,
 	}
 	}
 	data[0] = CS8427_REG_AUTOINC | CS8427_REG_CORU_DATABUF;
 	data[0] = CS8427_REG_AUTOINC | CS8427_REG_CORU_DATABUF;
 	for (idx = 0; idx < count; idx++)
 	for (idx = 0; idx < count; idx++)
-		data[idx + 1] = swapbits(ndata[idx]);
+		data[idx + 1] = bitrev8(ndata[idx]);
 	if (snd_i2c_sendbytes(device, data, count + 1) != count + 1)
 	if (snd_i2c_sendbytes(device, data, count + 1) != count + 1)
 		return -EIO;
 		return -EIO;
 	return 1;
 	return 1;

+ 2 - 1
sound/i2c/other/Makefile

@@ -5,6 +5,7 @@
 
 
 snd-ak4114-objs := ak4114.o
 snd-ak4114-objs := ak4114.o
 snd-ak4117-objs := ak4117.o
 snd-ak4117-objs := ak4117.o
+snd-ak4113-objs := ak4113.o
 snd-ak4xxx-adda-objs := ak4xxx-adda.o
 snd-ak4xxx-adda-objs := ak4xxx-adda.o
 snd-pt2258-objs := pt2258.o
 snd-pt2258-objs := pt2258.o
 snd-tea575x-tuner-objs := tea575x-tuner.o
 snd-tea575x-tuner-objs := tea575x-tuner.o
@@ -12,5 +13,5 @@ snd-tea575x-tuner-objs := tea575x-tuner.o
 # Module Dependency
 # Module Dependency
 obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o
 obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o
 obj-$(CONFIG_SND_ICE1712) += snd-ak4xxx-adda.o
 obj-$(CONFIG_SND_ICE1712) += snd-ak4xxx-adda.o
-obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4xxx-adda.o snd-pt2258.o
+obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4113.o snd-ak4xxx-adda.o snd-pt2258.o
 obj-$(CONFIG_SND_FM801_TEA575X) += snd-tea575x-tuner.o
 obj-$(CONFIG_SND_FM801_TEA575X) += snd-tea575x-tuner.o

+ 639 - 0
sound/i2c/other/ak4113.c

@@ -0,0 +1,639 @@
+/*
+ *  Routines for control of the AK4113 via I2C/4-wire serial interface
+ *  IEC958 (S/PDIF) receiver by Asahi Kasei
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *  Copyright (c) by Pavel Hofman <pavel.hofman@ivitera.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/ak4113.h>
+#include <sound/asoundef.h>
+#include <sound/info.h>
+
+MODULE_AUTHOR("Pavel Hofman <pavel.hofman@ivitera.com>");
+MODULE_DESCRIPTION("AK4113 IEC958 (S/PDIF) receiver by Asahi Kasei");
+MODULE_LICENSE("GPL");
+
+#define AK4113_ADDR			0x00 /* fixed address */
+
+static void ak4113_stats(struct work_struct *work);
+static void ak4113_init_regs(struct ak4113 *chip);
+
+
+static void reg_write(struct ak4113 *ak4113, unsigned char reg,
+		unsigned char val)
+{
+	ak4113->write(ak4113->private_data, reg, val);
+	if (reg < sizeof(ak4113->regmap))
+		ak4113->regmap[reg] = val;
+}
+
+static inline unsigned char reg_read(struct ak4113 *ak4113, unsigned char reg)
+{
+	return ak4113->read(ak4113->private_data, reg);
+}
+
+static void snd_ak4113_free(struct ak4113 *chip)
+{
+	chip->init = 1;	/* don't schedule new work */
+	mb();
+	cancel_delayed_work(&chip->work);
+	flush_scheduled_work();
+	kfree(chip);
+}
+
+static int snd_ak4113_dev_free(struct snd_device *device)
+{
+	struct ak4113 *chip = device->device_data;
+	snd_ak4113_free(chip);
+	return 0;
+}
+
+int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read,
+		ak4113_write_t *write, const unsigned char pgm[5],
+		void *private_data, struct ak4113 **r_ak4113)
+{
+	struct ak4113 *chip;
+	int err = 0;
+	unsigned char reg;
+	static struct snd_device_ops ops = {
+		.dev_free =     snd_ak4113_dev_free,
+	};
+
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+	spin_lock_init(&chip->lock);
+	chip->card = card;
+	chip->read = read;
+	chip->write = write;
+	chip->private_data = private_data;
+	INIT_DELAYED_WORK(&chip->work, ak4113_stats);
+
+	for (reg = 0; reg < AK4113_WRITABLE_REGS ; reg++)
+		chip->regmap[reg] = pgm[reg];
+	ak4113_init_regs(chip);
+
+	chip->rcs0 = reg_read(chip, AK4113_REG_RCS0) & ~(AK4113_QINT |
+			AK4113_CINT | AK4113_STC);
+	chip->rcs1 = reg_read(chip, AK4113_REG_RCS1);
+	chip->rcs2 = reg_read(chip, AK4113_REG_RCS2);
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+	if (err < 0)
+		goto __fail;
+
+	if (r_ak4113)
+		*r_ak4113 = chip;
+	return 0;
+
+__fail:
+	snd_ak4113_free(chip);
+	return err < 0 ? err : -EIO;
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_create);
+
+void snd_ak4113_reg_write(struct ak4113 *chip, unsigned char reg,
+		unsigned char mask, unsigned char val)
+{
+	if (reg >= AK4113_WRITABLE_REGS)
+		return;
+	reg_write(chip, reg, (chip->regmap[reg] & ~mask) | val);
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_reg_write);
+
+static void ak4113_init_regs(struct ak4113 *chip)
+{
+	unsigned char old = chip->regmap[AK4113_REG_PWRDN], reg;
+
+	/* bring the chip to reset state and powerdown state */
+	reg_write(chip, AK4113_REG_PWRDN, old & ~(AK4113_RST|AK4113_PWN));
+	udelay(200);
+	/* release reset, but leave powerdown */
+	reg_write(chip, AK4113_REG_PWRDN, (old | AK4113_RST) & ~AK4113_PWN);
+	udelay(200);
+	for (reg = 1; reg < AK4113_WRITABLE_REGS; reg++)
+		reg_write(chip, reg, chip->regmap[reg]);
+	/* release powerdown, everything is initialized now */
+	reg_write(chip, AK4113_REG_PWRDN, old | AK4113_RST | AK4113_PWN);
+}
+
+void snd_ak4113_reinit(struct ak4113 *chip)
+{
+	chip->init = 1;
+	mb();
+	flush_scheduled_work();
+	ak4113_init_regs(chip);
+	/* bring up statistics / event queing */
+	chip->init = 0;
+	if (chip->kctls[0])
+		schedule_delayed_work(&chip->work, HZ / 10);
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_reinit);
+
+static unsigned int external_rate(unsigned char rcs1)
+{
+	switch (rcs1 & (AK4113_FS0|AK4113_FS1|AK4113_FS2|AK4113_FS3)) {
+	case AK4113_FS_8000HZ:
+		return 8000;
+	case AK4113_FS_11025HZ:
+		return 11025;
+	case AK4113_FS_16000HZ:
+		return 16000;
+	case AK4113_FS_22050HZ:
+		return 22050;
+	case AK4113_FS_24000HZ:
+		return 24000;
+	case AK4113_FS_32000HZ:
+		return 32000;
+	case AK4113_FS_44100HZ:
+		return 44100;
+	case AK4113_FS_48000HZ:
+		return 48000;
+	case AK4113_FS_64000HZ:
+		return 64000;
+	case AK4113_FS_88200HZ:
+		return 88200;
+	case AK4113_FS_96000HZ:
+		return 96000;
+	case AK4113_FS_176400HZ:
+		return 176400;
+	case AK4113_FS_192000HZ:
+		return 192000;
+	default:
+		return 0;
+	}
+}
+
+static int snd_ak4113_in_error_info(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = LONG_MAX;
+	return 0;
+}
+
+static int snd_ak4113_in_error_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+	long *ptr;
+
+	spin_lock_irq(&chip->lock);
+	ptr = (long *)(((char *)chip) + kcontrol->private_value);
+	ucontrol->value.integer.value[0] = *ptr;
+	*ptr = 0;
+	spin_unlock_irq(&chip->lock);
+	return 0;
+}
+
+#define snd_ak4113_in_bit_info		snd_ctl_boolean_mono_info
+
+static int snd_ak4113_in_bit_get(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+	unsigned char reg = kcontrol->private_value & 0xff;
+	unsigned char bit = (kcontrol->private_value >> 8) & 0xff;
+	unsigned char inv = (kcontrol->private_value >> 31) & 1;
+
+	ucontrol->value.integer.value[0] =
+		((reg_read(chip, reg) & (1 << bit)) ? 1 : 0) ^ inv;
+	return 0;
+}
+
+static int snd_ak4113_rx_info(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 5;
+	return 0;
+}
+
+static int snd_ak4113_rx_get(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] =
+		(AK4113_IPS(chip->regmap[AK4113_REG_IO1]));
+	return 0;
+}
+
+static int snd_ak4113_rx_put(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+	int change;
+	u8 old_val;
+
+	spin_lock_irq(&chip->lock);
+	old_val = chip->regmap[AK4113_REG_IO1];
+	change = ucontrol->value.integer.value[0] != AK4113_IPS(old_val);
+	if (change)
+		reg_write(chip, AK4113_REG_IO1,
+				(old_val & (~AK4113_IPS(0xff))) |
+				(AK4113_IPS(ucontrol->value.integer.value[0])));
+	spin_unlock_irq(&chip->lock);
+	return change;
+}
+
+static int snd_ak4113_rate_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 192000;
+	return 0;
+}
+
+static int snd_ak4113_rate_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] = external_rate(reg_read(chip,
+				AK4113_REG_RCS1));
+	return 0;
+}
+
+static int snd_ak4113_spdif_info(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_ak4113_spdif_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+	unsigned i;
+
+	for (i = 0; i < AK4113_REG_RXCSB_SIZE; i++)
+		ucontrol->value.iec958.status[i] = reg_read(chip,
+				AK4113_REG_RXCSB0 + i);
+	return 0;
+}
+
+static int snd_ak4113_spdif_mask_info(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_ak4113_spdif_mask_get(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	memset(ucontrol->value.iec958.status, 0xff, AK4113_REG_RXCSB_SIZE);
+	return 0;
+}
+
+static int snd_ak4113_spdif_pinfo(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 0xffff;
+	uinfo->count = 4;
+	return 0;
+}
+
+static int snd_ak4113_spdif_pget(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+	unsigned short tmp;
+
+	ucontrol->value.integer.value[0] = 0xf8f2;
+	ucontrol->value.integer.value[1] = 0x4e1f;
+	tmp = reg_read(chip, AK4113_REG_Pc0) |
+		(reg_read(chip, AK4113_REG_Pc1) << 8);
+	ucontrol->value.integer.value[2] = tmp;
+	tmp = reg_read(chip, AK4113_REG_Pd0) |
+		(reg_read(chip, AK4113_REG_Pd1) << 8);
+	ucontrol->value.integer.value[3] = tmp;
+	return 0;
+}
+
+static int snd_ak4113_spdif_qinfo(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+	uinfo->count = AK4113_REG_QSUB_SIZE;
+	return 0;
+}
+
+static int snd_ak4113_spdif_qget(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+	unsigned i;
+
+	for (i = 0; i < AK4113_REG_QSUB_SIZE; i++)
+		ucontrol->value.bytes.data[i] = reg_read(chip,
+				AK4113_REG_QSUB_ADDR + i);
+	return 0;
+}
+
+/* Don't forget to change AK4113_CONTROLS define!!! */
+static struct snd_kcontrol_new snd_ak4113_iec958_controls[] = {
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		"IEC958 Parity Errors",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ |
+		SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info =		snd_ak4113_in_error_info,
+	.get =		snd_ak4113_in_error_get,
+	.private_value = offsetof(struct ak4113, parity_errors),
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		"IEC958 V-Bit Errors",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ |
+		SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info =		snd_ak4113_in_error_info,
+	.get =		snd_ak4113_in_error_get,
+	.private_value = offsetof(struct ak4113, v_bit_errors),
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		"IEC958 C-CRC Errors",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ |
+		SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info =		snd_ak4113_in_error_info,
+	.get =		snd_ak4113_in_error_get,
+	.private_value = offsetof(struct ak4113, ccrc_errors),
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		"IEC958 Q-CRC Errors",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ |
+		SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info =		snd_ak4113_in_error_info,
+	.get =		snd_ak4113_in_error_get,
+	.private_value = offsetof(struct ak4113, qcrc_errors),
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		"IEC958 External Rate",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ |
+		SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info =		snd_ak4113_rate_info,
+	.get =		snd_ak4113_rate_get,
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK),
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+	.info =		snd_ak4113_spdif_mask_info,
+	.get =		snd_ak4113_spdif_mask_get,
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ |
+		SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info =		snd_ak4113_spdif_info,
+	.get =		snd_ak4113_spdif_get,
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		"IEC958 Preample Capture Default",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ |
+		SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info =		snd_ak4113_spdif_pinfo,
+	.get =		snd_ak4113_spdif_pget,
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		"IEC958 Q-subcode Capture Default",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ |
+		SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info =		snd_ak4113_spdif_qinfo,
+	.get =		snd_ak4113_spdif_qget,
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		"IEC958 Audio",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ |
+		SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info =		snd_ak4113_in_bit_info,
+	.get =		snd_ak4113_in_bit_get,
+	.private_value = (1<<31) | (1<<8) | AK4113_REG_RCS0,
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		"IEC958 Non-PCM Bitstream",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ |
+		SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info =		snd_ak4113_in_bit_info,
+	.get =		snd_ak4113_in_bit_get,
+	.private_value = (0<<8) | AK4113_REG_RCS1,
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		"IEC958 DTS Bitstream",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ |
+		SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info =		snd_ak4113_in_bit_info,
+	.get =		snd_ak4113_in_bit_get,
+	.private_value = (1<<8) | AK4113_REG_RCS1,
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		"AK4113 Input Select",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ |
+		SNDRV_CTL_ELEM_ACCESS_WRITE,
+	.info =		snd_ak4113_rx_info,
+	.get =		snd_ak4113_rx_get,
+	.put =		snd_ak4113_rx_put,
+}
+};
+
+static void snd_ak4113_proc_regs_read(struct snd_info_entry *entry,
+		struct snd_info_buffer *buffer)
+{
+	struct ak4113 *ak4113 = entry->private_data;
+	int reg, val;
+	/* all ak4113 registers 0x00 - 0x1c */
+	for (reg = 0; reg < 0x1d; reg++) {
+		val = reg_read(ak4113, reg);
+		snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val);
+	}
+}
+
+static void snd_ak4113_proc_init(struct ak4113 *ak4113)
+{
+	struct snd_info_entry *entry;
+	if (!snd_card_proc_new(ak4113->card, "ak4113", &entry))
+		snd_info_set_text_ops(entry, ak4113, snd_ak4113_proc_regs_read);
+}
+
+int snd_ak4113_build(struct ak4113 *ak4113,
+		struct snd_pcm_substream *cap_substream)
+{
+	struct snd_kcontrol *kctl;
+	unsigned int idx;
+	int err;
+
+	if (snd_BUG_ON(!cap_substream))
+		return -EINVAL;
+	ak4113->substream = cap_substream;
+	for (idx = 0; idx < AK4113_CONTROLS; idx++) {
+		kctl = snd_ctl_new1(&snd_ak4113_iec958_controls[idx], ak4113);
+		if (kctl == NULL)
+			return -ENOMEM;
+		kctl->id.device = cap_substream->pcm->device;
+		kctl->id.subdevice = cap_substream->number;
+		err = snd_ctl_add(ak4113->card, kctl);
+		if (err < 0)
+			return err;
+		ak4113->kctls[idx] = kctl;
+	}
+	snd_ak4113_proc_init(ak4113);
+	/* trigger workq */
+	schedule_delayed_work(&ak4113->work, HZ / 10);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_build);
+
+int snd_ak4113_external_rate(struct ak4113 *ak4113)
+{
+	unsigned char rcs1;
+
+	rcs1 = reg_read(ak4113, AK4113_REG_RCS1);
+	return external_rate(rcs1);
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_external_rate);
+
+int snd_ak4113_check_rate_and_errors(struct ak4113 *ak4113, unsigned int flags)
+{
+	struct snd_pcm_runtime *runtime =
+		ak4113->substream ? ak4113->substream->runtime : NULL;
+	unsigned long _flags;
+	int res = 0;
+	unsigned char rcs0, rcs1, rcs2;
+	unsigned char c0, c1;
+
+	rcs1 = reg_read(ak4113, AK4113_REG_RCS1);
+	if (flags & AK4113_CHECK_NO_STAT)
+		goto __rate;
+	rcs0 = reg_read(ak4113, AK4113_REG_RCS0);
+	rcs2 = reg_read(ak4113, AK4113_REG_RCS2);
+	spin_lock_irqsave(&ak4113->lock, _flags);
+	if (rcs0 & AK4113_PAR)
+		ak4113->parity_errors++;
+	if (rcs0 & AK4113_V)
+		ak4113->v_bit_errors++;
+	if (rcs2 & AK4113_CCRC)
+		ak4113->ccrc_errors++;
+	if (rcs2 & AK4113_QCRC)
+		ak4113->qcrc_errors++;
+	c0 = (ak4113->rcs0 & (AK4113_QINT | AK4113_CINT | AK4113_STC |
+				AK4113_AUDION | AK4113_AUTO | AK4113_UNLCK)) ^
+		(rcs0 & (AK4113_QINT | AK4113_CINT | AK4113_STC |
+			 AK4113_AUDION | AK4113_AUTO | AK4113_UNLCK));
+	c1 = (ak4113->rcs1 & (AK4113_DTSCD | AK4113_NPCM | AK4113_PEM |
+				AK4113_DAT | 0xf0)) ^
+		(rcs1 & (AK4113_DTSCD | AK4113_NPCM | AK4113_PEM |
+			 AK4113_DAT | 0xf0));
+	ak4113->rcs0 = rcs0 & ~(AK4113_QINT | AK4113_CINT | AK4113_STC);
+	ak4113->rcs1 = rcs1;
+	ak4113->rcs2 = rcs2;
+	spin_unlock_irqrestore(&ak4113->lock, _flags);
+
+	if (rcs0 & AK4113_PAR)
+		snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+				&ak4113->kctls[0]->id);
+	if (rcs0 & AK4113_V)
+		snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+				&ak4113->kctls[1]->id);
+	if (rcs2 & AK4113_CCRC)
+		snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+				&ak4113->kctls[2]->id);
+	if (rcs2 & AK4113_QCRC)
+		snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+				&ak4113->kctls[3]->id);
+
+	/* rate change */
+	if (c1 & 0xf0)
+		snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+				&ak4113->kctls[4]->id);
+
+	if ((c1 & AK4113_PEM) | (c0 & AK4113_CINT))
+		snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+				&ak4113->kctls[6]->id);
+	if (c0 & AK4113_QINT)
+		snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+				&ak4113->kctls[8]->id);
+
+	if (c0 & AK4113_AUDION)
+		snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+				&ak4113->kctls[9]->id);
+	if (c1 & AK4113_NPCM)
+		snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+				&ak4113->kctls[10]->id);
+	if (c1 & AK4113_DTSCD)
+		snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+				&ak4113->kctls[11]->id);
+
+	if (ak4113->change_callback && (c0 | c1) != 0)
+		ak4113->change_callback(ak4113, c0, c1);
+
+__rate:
+	/* compare rate */
+	res = external_rate(rcs1);
+	if (!(flags & AK4113_CHECK_NO_RATE) && runtime &&
+			(runtime->rate != res)) {
+		snd_pcm_stream_lock_irqsave(ak4113->substream, _flags);
+		if (snd_pcm_running(ak4113->substream)) {
+			/*printk(KERN_DEBUG "rate changed (%i <- %i)\n",
+			 * runtime->rate, res); */
+			snd_pcm_stop(ak4113->substream,
+					SNDRV_PCM_STATE_DRAINING);
+			wake_up(&runtime->sleep);
+			res = 1;
+		}
+		snd_pcm_stream_unlock_irqrestore(ak4113->substream, _flags);
+	}
+	return res;
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_check_rate_and_errors);
+
+static void ak4113_stats(struct work_struct *work)
+{
+	struct ak4113 *chip = container_of(work, struct ak4113, work.work);
+
+	if (!chip->init)
+		snd_ak4113_check_rate_and_errors(chip, chip->check_flags);
+
+	schedule_delayed_work(&chip->work, HZ / 10);
+}

+ 104 - 32
sound/i2c/other/ak4xxx-adda.c

@@ -19,7 +19,7 @@
  *   along with this program; if not, write to the Free Software
  *   along with this program; if not, write to the Free Software
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  *
  *
- */      
+ */
 
 
 #include <asm/io.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/delay.h>
@@ -29,6 +29,7 @@
 #include <sound/control.h>
 #include <sound/control.h>
 #include <sound/tlv.h>
 #include <sound/tlv.h>
 #include <sound/ak4xxx-adda.h>
 #include <sound/ak4xxx-adda.h>
+#include <sound/info.h>
 
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>");
 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>");
 MODULE_DESCRIPTION("Routines for control of AK452x / AK43xx  AD/DA converters");
 MODULE_DESCRIPTION("Routines for control of AK452x / AK43xx  AD/DA converters");
@@ -52,26 +53,21 @@ EXPORT_SYMBOL(snd_akm4xxx_write);
 static void ak4524_reset(struct snd_akm4xxx *ak, int state)
 static void ak4524_reset(struct snd_akm4xxx *ak, int state)
 {
 {
 	unsigned int chip;
 	unsigned int chip;
-	unsigned char reg, maxreg;
+	unsigned char reg;
 
 
-	if (ak->type == SND_AK4528)
-		maxreg = 0x06;
-	else
-		maxreg = 0x08;
 	for (chip = 0; chip < ak->num_dacs/2; chip++) {
 	for (chip = 0; chip < ak->num_dacs/2; chip++) {
 		snd_akm4xxx_write(ak, chip, 0x01, state ? 0x00 : 0x03);
 		snd_akm4xxx_write(ak, chip, 0x01, state ? 0x00 : 0x03);
 		if (state)
 		if (state)
 			continue;
 			continue;
 		/* DAC volumes */
 		/* DAC volumes */
-		for (reg = 0x04; reg < maxreg; reg++)
+		for (reg = 0x04; reg < ak->total_regs; reg++)
 			snd_akm4xxx_write(ak, chip, reg,
 			snd_akm4xxx_write(ak, chip, reg,
 					  snd_akm4xxx_get(ak, chip, reg));
 					  snd_akm4xxx_get(ak, chip, reg));
 	}
 	}
 }
 }
 
 
 /* reset procedure for AK4355 and AK4358 */
 /* reset procedure for AK4355 and AK4358 */
-static void ak435X_reset(struct snd_akm4xxx *ak, int state,
-		unsigned char total_regs)
+static void ak435X_reset(struct snd_akm4xxx *ak, int state)
 {
 {
 	unsigned char reg;
 	unsigned char reg;
 
 
@@ -79,7 +75,7 @@ static void ak435X_reset(struct snd_akm4xxx *ak, int state,
 		snd_akm4xxx_write(ak, 0, 0x01, 0x02); /* reset and soft-mute */
 		snd_akm4xxx_write(ak, 0, 0x01, 0x02); /* reset and soft-mute */
 		return;
 		return;
 	}
 	}
-	for (reg = 0x00; reg < total_regs; reg++)
+	for (reg = 0x00; reg < ak->total_regs; reg++)
 		if (reg != 0x01)
 		if (reg != 0x01)
 			snd_akm4xxx_write(ak, 0, reg,
 			snd_akm4xxx_write(ak, 0, reg,
 					  snd_akm4xxx_get(ak, 0, reg));
 					  snd_akm4xxx_get(ak, 0, reg));
@@ -91,12 +87,11 @@ static void ak4381_reset(struct snd_akm4xxx *ak, int state)
 {
 {
 	unsigned int chip;
 	unsigned int chip;
 	unsigned char reg;
 	unsigned char reg;
-
 	for (chip = 0; chip < ak->num_dacs/2; chip++) {
 	for (chip = 0; chip < ak->num_dacs/2; chip++) {
 		snd_akm4xxx_write(ak, chip, 0x00, state ? 0x0c : 0x0f);
 		snd_akm4xxx_write(ak, chip, 0x00, state ? 0x0c : 0x0f);
 		if (state)
 		if (state)
 			continue;
 			continue;
-		for (reg = 0x01; reg < 0x05; reg++)
+		for (reg = 0x01; reg < ak->total_regs; reg++)
 			snd_akm4xxx_write(ak, chip, reg,
 			snd_akm4xxx_write(ak, chip, reg,
 					  snd_akm4xxx_get(ak, chip, reg));
 					  snd_akm4xxx_get(ak, chip, reg));
 	}
 	}
@@ -113,16 +108,17 @@ void snd_akm4xxx_reset(struct snd_akm4xxx *ak, int state)
 	switch (ak->type) {
 	switch (ak->type) {
 	case SND_AK4524:
 	case SND_AK4524:
 	case SND_AK4528:
 	case SND_AK4528:
+	case SND_AK4620:
 		ak4524_reset(ak, state);
 		ak4524_reset(ak, state);
 		break;
 		break;
 	case SND_AK4529:
 	case SND_AK4529:
 		/* FIXME: needed for ak4529? */
 		/* FIXME: needed for ak4529? */
 		break;
 		break;
 	case SND_AK4355:
 	case SND_AK4355:
-		ak435X_reset(ak, state, 0x0b);
+		ak435X_reset(ak, state);
 		break;
 		break;
 	case SND_AK4358:
 	case SND_AK4358:
-		ak435X_reset(ak, state, 0x10);
+		ak435X_reset(ak, state);
 		break;
 		break;
 	case SND_AK4381:
 	case SND_AK4381:
 		ak4381_reset(ak, state);
 		ak4381_reset(ak, state);
@@ -139,7 +135,7 @@ EXPORT_SYMBOL(snd_akm4xxx_reset);
  * Volume conversion table for non-linear volumes
  * Volume conversion table for non-linear volumes
  * from -63.5dB (mute) to 0dB step 0.5dB
  * from -63.5dB (mute) to 0dB step 0.5dB
  *
  *
- * Used for AK4524 input/ouput attenuation, AK4528, and
+ * Used for AK4524/AK4620 input/ouput attenuation, AK4528, and
  * AK5365 input attenuation
  * AK5365 input attenuation
  */
  */
 static const unsigned char vol_cvt_datt[128] = {
 static const unsigned char vol_cvt_datt[128] = {
@@ -259,8 +255,22 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
 		0x00, 0x0f, /* 0: power-up, un-reset */
 		0x00, 0x0f, /* 0: power-up, un-reset */
 		0xff, 0xff
 		0xff, 0xff
 	};
 	};
+	static const unsigned char inits_ak4620[] = {
+		0x00, 0x07, /* 0: normal */
+		0x01, 0x00, /* 0: reset */
+		0x01, 0x02, /* 1: RSTAD */
+		0x01, 0x03, /* 1: RSTDA */
+		0x01, 0x0f, /* 1: normal */
+		0x02, 0x60, /* 2: 24bit I2S */
+		0x03, 0x01, /* 3: deemphasis off */
+		0x04, 0x00, /* 4: LIN muted */
+		0x05, 0x00, /* 5: RIN muted */
+		0x06, 0x00, /* 6: LOUT muted */
+		0x07, 0x00, /* 7: ROUT muted */
+		0xff, 0xff
+	};
 
 
-	int chip, num_chips;
+	int chip;
 	const unsigned char *ptr, *inits;
 	const unsigned char *ptr, *inits;
 	unsigned char reg, data;
 	unsigned char reg, data;
 
 
@@ -270,42 +280,64 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
 	switch (ak->type) {
 	switch (ak->type) {
 	case SND_AK4524:
 	case SND_AK4524:
 		inits = inits_ak4524;
 		inits = inits_ak4524;
-		num_chips = ak->num_dacs / 2;
+		ak->num_chips = ak->num_dacs / 2;
+		ak->name = "ak4524";
+		ak->total_regs = 0x08;
 		break;
 		break;
 	case SND_AK4528:
 	case SND_AK4528:
 		inits = inits_ak4528;
 		inits = inits_ak4528;
-		num_chips = ak->num_dacs / 2;
+		ak->num_chips = ak->num_dacs / 2;
+		ak->name = "ak4528";
+		ak->total_regs = 0x06;
 		break;
 		break;
 	case SND_AK4529:
 	case SND_AK4529:
 		inits = inits_ak4529;
 		inits = inits_ak4529;
-		num_chips = 1;
+		ak->num_chips = 1;
+		ak->name = "ak4529";
+		ak->total_regs = 0x0d;
 		break;
 		break;
 	case SND_AK4355:
 	case SND_AK4355:
 		inits = inits_ak4355;
 		inits = inits_ak4355;
-		num_chips = 1;
+		ak->num_chips = 1;
+		ak->name = "ak4355";
+		ak->total_regs = 0x0b;
 		break;
 		break;
 	case SND_AK4358:
 	case SND_AK4358:
 		inits = inits_ak4358;
 		inits = inits_ak4358;
-		num_chips = 1;
+		ak->num_chips = 1;
+		ak->name = "ak4358";
+		ak->total_regs = 0x10;
 		break;
 		break;
 	case SND_AK4381:
 	case SND_AK4381:
 		inits = inits_ak4381;
 		inits = inits_ak4381;
-		num_chips = ak->num_dacs / 2;
+		ak->num_chips = ak->num_dacs / 2;
+		ak->name = "ak4381";
+		ak->total_regs = 0x05;
 		break;
 		break;
 	case SND_AK5365:
 	case SND_AK5365:
 		/* FIXME: any init sequence? */
 		/* FIXME: any init sequence? */
+		ak->num_chips = 1;
+		ak->name = "ak5365";
+		ak->total_regs = 0x08;
 		return;
 		return;
+	case SND_AK4620:
+		inits = inits_ak4620;
+		ak->num_chips = ak->num_dacs / 2;
+		ak->name = "ak4620";
+		ak->total_regs = 0x08;
+		break;
 	default:
 	default:
 		snd_BUG();
 		snd_BUG();
 		return;
 		return;
 	}
 	}
 
 
-	for (chip = 0; chip < num_chips; chip++) {
+	for (chip = 0; chip < ak->num_chips; chip++) {
 		ptr = inits;
 		ptr = inits;
 		while (*ptr != 0xff) {
 		while (*ptr != 0xff) {
 			reg = *ptr++;
 			reg = *ptr++;
 			data = *ptr++;
 			data = *ptr++;
 			snd_akm4xxx_write(ak, chip, reg, data);
 			snd_akm4xxx_write(ak, chip, reg, data);
+			udelay(10);
 		}
 		}
 	}
 	}
 }
 }
@@ -688,6 +720,12 @@ static int build_dac_controls(struct snd_akm4xxx *ak)
 				AK_COMPOSE(idx/2, (idx%2) + 3, 0, 255);
 				AK_COMPOSE(idx/2, (idx%2) + 3, 0, 255);
 			knew.tlv.p = db_scale_linear;
 			knew.tlv.p = db_scale_linear;
 			break;
 			break;
+		case SND_AK4620:
+			/* register 6 & 7 */
+			knew.private_value =
+				AK_COMPOSE(idx/2, (idx%2) + 6, 0, 255);
+			knew.tlv.p = db_scale_linear;
+			break;
 		default:
 		default:
 			return -EINVAL;
 			return -EINVAL;
 		}
 		}
@@ -704,10 +742,12 @@ static int build_dac_controls(struct snd_akm4xxx *ak)
 
 
 static int build_adc_controls(struct snd_akm4xxx *ak)
 static int build_adc_controls(struct snd_akm4xxx *ak)
 {
 {
-	int idx, err, mixer_ch, num_stereo;
+	int idx, err, mixer_ch, num_stereo, max_steps;
 	struct snd_kcontrol_new knew;
 	struct snd_kcontrol_new knew;
 
 
 	mixer_ch = 0;
 	mixer_ch = 0;
+	if (ak->type == SND_AK4528)
+		return 0;	/* no controls */
 	for (idx = 0; idx < ak->num_adcs;) {
 	for (idx = 0; idx < ak->num_adcs;) {
 		memset(&knew, 0, sizeof(knew));
 		memset(&knew, 0, sizeof(knew));
 		if (! ak->adc_info || ! ak->adc_info[mixer_ch].name) {
 		if (! ak->adc_info || ! ak->adc_info[mixer_ch].name) {
@@ -733,13 +773,12 @@ static int build_adc_controls(struct snd_akm4xxx *ak)
 		}
 		}
 		/* register 4 & 5 */
 		/* register 4 & 5 */
 		if (ak->type == SND_AK5365)
 		if (ak->type == SND_AK5365)
-			knew.private_value =
-				AK_COMPOSE(idx/2, (idx%2) + 4, 0, 151) |
-				AK_VOL_CVT | AK_IPGA;
+			max_steps = 152;
 		else
 		else
-			knew.private_value =
-				AK_COMPOSE(idx/2, (idx%2) + 4, 0, 163) |
-				AK_VOL_CVT | AK_IPGA;
+			max_steps = 164;
+		knew.private_value =
+			AK_COMPOSE(idx/2, (idx%2) + 4, 0, max_steps) |
+			AK_VOL_CVT | AK_IPGA;
 		knew.tlv.p = db_scale_vol_datt;
 		knew.tlv.p = db_scale_vol_datt;
 		err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
 		err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
 		if (err < 0)
 		if (err < 0)
@@ -808,6 +847,7 @@ static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs)
 		switch (ak->type) {
 		switch (ak->type) {
 		case SND_AK4524:
 		case SND_AK4524:
 		case SND_AK4528:
 		case SND_AK4528:
+		case SND_AK4620:
 			/* register 3 */
 			/* register 3 */
 			knew.private_value = AK_COMPOSE(idx, 3, 0, 0);
 			knew.private_value = AK_COMPOSE(idx, 3, 0, 0);
 			break;
 			break;
@@ -834,6 +874,35 @@ static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs)
 	return 0;
 	return 0;
 }
 }
 
 
+#ifdef CONFIG_PROC_FS
+static void proc_regs_read(struct snd_info_entry *entry,
+		struct snd_info_buffer *buffer)
+{
+	struct snd_akm4xxx *ak = (struct snd_akm4xxx *)entry->private_data;
+	int reg, val, chip;
+	for (chip = 0; chip < ak->num_chips; chip++) {
+		for (reg = 0; reg < ak->total_regs; reg++) {
+			val =  snd_akm4xxx_get(ak, chip, reg);
+			snd_iprintf(buffer, "chip %d: 0x%02x = 0x%02x\n", chip,
+					reg, val);
+		}
+	}
+}
+
+static int proc_init(struct snd_akm4xxx *ak)
+{
+	struct snd_info_entry *entry;
+	int err;
+	err = snd_card_proc_new(ak->card, ak->name, &entry);
+	if (err < 0)
+		return err;
+	snd_info_set_text_ops(entry, ak, proc_regs_read);
+	return 0;
+}
+#else /* !CONFIG_PROC_FS */
+static int proc_init(struct snd_akm4xxx *ak) {}
+#endif
+
 int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
 int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
 {
 {
 	int err, num_emphs;
 	int err, num_emphs;
@@ -845,18 +914,21 @@ int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
 	err = build_adc_controls(ak);
 	err = build_adc_controls(ak);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
-
 	if (ak->type == SND_AK4355 || ak->type == SND_AK4358)
 	if (ak->type == SND_AK4355 || ak->type == SND_AK4358)
 		num_emphs = 1;
 		num_emphs = 1;
+	else if (ak->type == SND_AK4620)
+		num_emphs = 0;
 	else
 	else
 		num_emphs = ak->num_dacs / 2;
 		num_emphs = ak->num_dacs / 2;
 	err = build_deemphasis(ak, num_emphs);
 	err = build_deemphasis(ak, num_emphs);
+	if (err < 0)
+		return err;
+	err = proc_init(ak);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
 
 
 	return 0;
 	return 0;
 }
 }
-	
 EXPORT_SYMBOL(snd_akm4xxx_build_controls);
 EXPORT_SYMBOL(snd_akm4xxx_build_controls);
 
 
 static int __init alsa_akm4xxx_module_init(void)
 static int __init alsa_akm4xxx_module_init(void)

+ 1 - 1
sound/i2c/other/tea575x-tuner.c

@@ -225,7 +225,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
 	case V4L2_CID_AUDIO_MUTE:
 	case V4L2_CID_AUDIO_MUTE:
 		if (tea->ops->mute) {
 		if (tea->ops->mute) {
 			tea->ops->mute(tea, ctrl->value);
 			tea->ops->mute(tea, ctrl->value);
-			tea->mute = 1;
+			tea->mute = ctrl->value;
 			return 0;
 			return 0;
 		}
 		}
 	}
 	}

+ 9 - 3
sound/isa/Kconfig

@@ -372,15 +372,21 @@ config SND_SGALAXY
 
 
 config SND_SSCAPE
 config SND_SSCAPE
 	tristate "Ensoniq SoundScape driver"
 	tristate "Ensoniq SoundScape driver"
-	select SND_HWDEP
 	select SND_MPU401_UART
 	select SND_MPU401_UART
 	select SND_WSS_LIB
 	select SND_WSS_LIB
+	select FW_LOADER
 	help
 	help
 	  Say Y here to include support for Ensoniq SoundScape 
 	  Say Y here to include support for Ensoniq SoundScape 
-	  soundcards.
+	  and Ensoniq OEM soundcards.
 
 
 	  The PCM audio is supported on SoundScape Classic, Elite, PnP
 	  The PCM audio is supported on SoundScape Classic, Elite, PnP
-	  and VIVO cards. The MIDI support is very experimental.
+	  and VIVO cards. The supported OEM cards are SPEA Media FX and
+	  Reveal SC-600.
+	  The MIDI support is very experimental and requires binary
+	  firmware files called "scope.cod" and "sndscape.co?" where the
+	  ? is digit 0, 1, 2, 3 or 4. The firmware files can be found
+	  in DOS or Windows driver packages. One has to put the firmware
+	  files into the /lib/firmware directory.
 
 
 	  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-sscape.
 	  will be called snd-sscape.

+ 2 - 2
sound/isa/cmi8330.c

@@ -237,7 +237,7 @@ WSS_DOUBLE("Wavetable Capture Volume", 0,
 		CMI8330_WAVGAIN, CMI8330_WAVGAIN, 4, 0, 15, 0),
 		CMI8330_WAVGAIN, CMI8330_WAVGAIN, 4, 0, 15, 0),
 WSS_SINGLE("3D Control - Switch", 0,
 WSS_SINGLE("3D Control - Switch", 0,
 		CMI8330_RMUX3D, 5, 1, 1),
 		CMI8330_RMUX3D, 5, 1, 1),
-WSS_SINGLE("PC Speaker Playback Volume", 0,
+WSS_SINGLE("Beep Playback Volume", 0,
 		CMI8330_OUTPUTVOL, 3, 3, 0),
 		CMI8330_OUTPUTVOL, 3, 3, 0),
 WSS_DOUBLE("FM Playback Switch", 0,
 WSS_DOUBLE("FM Playback Switch", 0,
 		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
 		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
@@ -262,7 +262,7 @@ SB_DOUBLE("SB Line Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3,
 SB_DOUBLE("SB Line Playback Volume", SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31),
 SB_DOUBLE("SB Line Playback Volume", SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31),
 SB_SINGLE("SB Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1),
 SB_SINGLE("SB Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1),
 SB_SINGLE("SB Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31),
 SB_SINGLE("SB Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31),
-SB_SINGLE("SB PC Speaker Volume", SB_DSP4_SPEAKER_DEV, 6, 3),
+SB_SINGLE("SB Beep Volume", SB_DSP4_SPEAKER_DEV, 6, 3),
 SB_DOUBLE("SB Capture Volume", SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3),
 SB_DOUBLE("SB Capture Volume", SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3),
 SB_DOUBLE("SB Playback Volume", SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3),
 SB_DOUBLE("SB Playback Volume", SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3),
 SB_SINGLE("SB Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1),
 SB_SINGLE("SB Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1),

+ 3 - 10
sound/isa/cs423x/cs4236.c

@@ -394,21 +394,15 @@ static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)
 			return -EBUSY;
 			return -EBUSY;
 		}
 		}
 
 
-	err = snd_wss_create(card, port[dev], cport[dev],
+	err = snd_cs4236_create(card, port[dev], cport[dev],
 			     irq[dev],
 			     irq[dev],
 			     dma1[dev], dma2[dev],
 			     dma1[dev], dma2[dev],
 			     WSS_HW_DETECT3, 0, &chip);
 			     WSS_HW_DETECT3, 0, &chip);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
+
+	acard->chip = chip;
 	if (chip->hardware & WSS_HW_CS4236B_MASK) {
 	if (chip->hardware & WSS_HW_CS4236B_MASK) {
-		snd_wss_free(chip);
-		err = snd_cs4236_create(card,
-					port[dev], cport[dev],
-					irq[dev], dma1[dev], dma2[dev],
-					WSS_HW_DETECT, 0, &chip);
-		if (err < 0)
-			return err;
-		acard->chip = chip;
 
 
 		err = snd_cs4236_pcm(chip, 0, &pcm);
 		err = snd_cs4236_pcm(chip, 0, &pcm);
 		if (err < 0)
 		if (err < 0)
@@ -418,7 +412,6 @@ static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)
 		if (err < 0)
 		if (err < 0)
 			return err;
 			return err;
 	} else {
 	} else {
-		acard->chip = chip;
 		err = snd_wss_pcm(chip, 0, &pcm);
 		err = snd_wss_pcm(chip, 0, &pcm);
 		if (err < 0)
 		if (err < 0)
 			return err;
 			return err;

+ 155 - 86
sound/isa/cs423x/cs4236_lib.c

@@ -87,6 +87,8 @@
 #include <sound/core.h>
 #include <sound/core.h>
 #include <sound/wss.h>
 #include <sound/wss.h>
 #include <sound/asoundef.h>
 #include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
 
 
 /*
 /*
  *
  *
@@ -264,7 +266,10 @@ static void snd_cs4236_resume(struct snd_wss *chip)
 }
 }
 
 
 #endif /* CONFIG_PM */
 #endif /* CONFIG_PM */
-
+/*
+ * This function does no fail if the chip is not CS4236B or compatible.
+ * It just an equivalent to the snd_wss_create() then.
+ */
 int snd_cs4236_create(struct snd_card *card,
 int snd_cs4236_create(struct snd_card *card,
 		      unsigned long port,
 		      unsigned long port,
 		      unsigned long cport,
 		      unsigned long cport,
@@ -281,21 +286,17 @@ int snd_cs4236_create(struct snd_card *card,
 	*rchip = NULL;
 	*rchip = NULL;
 	if (hardware == WSS_HW_DETECT)
 	if (hardware == WSS_HW_DETECT)
 		hardware = WSS_HW_DETECT3;
 		hardware = WSS_HW_DETECT3;
-	if (cport < 0x100) {
-		snd_printk(KERN_ERR "please, specify control port "
-			   "for CS4236+ chips\n");
-		return -ENODEV;
-	}
+
 	err = snd_wss_create(card, port, cport,
 	err = snd_wss_create(card, port, cport,
 			     irq, dma1, dma2, hardware, hwshare, &chip);
 			     irq, dma1, dma2, hardware, hwshare, &chip);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
 
 
-	if (!(chip->hardware & WSS_HW_CS4236B_MASK)) {
-		snd_printk(KERN_ERR "CS4236+: MODE3 and extended registers "
-			   "not available, hardware=0x%x\n", chip->hardware);
-		snd_device_free(card, chip);
-		return -ENODEV;
+	if ((chip->hardware & WSS_HW_CS4236B_MASK) == 0) {
+		snd_printd("chip is not CS4236+, hardware=0x%x\n",
+			   chip->hardware);
+		*rchip = chip;
+		return 0;
 	}
 	}
 #if 0
 #if 0
 	{
 	{
@@ -308,9 +309,16 @@ int snd_cs4236_create(struct snd_card *card,
 				   idx, snd_cs4236_ctrl_in(chip, idx));
 				   idx, snd_cs4236_ctrl_in(chip, idx));
 	}
 	}
 #endif
 #endif
+	if (cport < 0x100 || cport == SNDRV_AUTO_PORT) {
+		snd_printk(KERN_ERR "please, specify control port "
+			   "for CS4236+ chips\n");
+		snd_device_free(card, chip);
+		return -ENODEV;
+	}
 	ver1 = snd_cs4236_ctrl_in(chip, 1);
 	ver1 = snd_cs4236_ctrl_in(chip, 1);
 	ver2 = snd_cs4236_ext_in(chip, CS4236_VERSION);
 	ver2 = snd_cs4236_ext_in(chip, CS4236_VERSION);
-	snd_printdd("CS4236: [0x%lx] C1 (version) = 0x%x, ext = 0x%x\n", cport, ver1, ver2);
+	snd_printdd("CS4236: [0x%lx] C1 (version) = 0x%x, ext = 0x%x\n",
+			cport, ver1, ver2);
 	if (ver1 != ver2) {
 	if (ver1 != ver2) {
 		snd_printk(KERN_ERR "CS4236+ chip detected, but "
 		snd_printk(KERN_ERR "CS4236+ chip detected, but "
 			   "control port 0x%lx is not valid\n", cport);
 			   "control port 0x%lx is not valid\n", cport);
@@ -321,13 +329,17 @@ int snd_cs4236_create(struct snd_card *card,
 	snd_cs4236_ctrl_out(chip, 2, 0xff);
 	snd_cs4236_ctrl_out(chip, 2, 0xff);
 	snd_cs4236_ctrl_out(chip, 3, 0x00);
 	snd_cs4236_ctrl_out(chip, 3, 0x00);
 	snd_cs4236_ctrl_out(chip, 4, 0x80);
 	snd_cs4236_ctrl_out(chip, 4, 0x80);
-	snd_cs4236_ctrl_out(chip, 5, ((IEC958_AES1_CON_PCM_CODER & 3) << 6) | IEC958_AES0_CON_EMPHASIS_NONE);
+	reg = ((IEC958_AES1_CON_PCM_CODER & 3) << 6) |
+	      IEC958_AES0_CON_EMPHASIS_NONE;
+	snd_cs4236_ctrl_out(chip, 5, reg);
 	snd_cs4236_ctrl_out(chip, 6, IEC958_AES1_CON_PCM_CODER >> 2);
 	snd_cs4236_ctrl_out(chip, 6, IEC958_AES1_CON_PCM_CODER >> 2);
 	snd_cs4236_ctrl_out(chip, 7, 0x00);
 	snd_cs4236_ctrl_out(chip, 7, 0x00);
-	/* 0x8c for C8 is valid for Turtle Beach Malibu - the IEC-958 output */
-	/* is working with this setup, other hardware should have */
-	/* different signal paths and this value should be selectable */
-	/* in the future */
+	/*
+	 * 0x8c for C8 is valid for Turtle Beach Malibu - the IEC-958
+	 * output is working with this setup, other hardware should
+	 * have different signal paths and this value should be
+	 * selectable in the future
+	 */
 	snd_cs4236_ctrl_out(chip, 8, 0x8c);
 	snd_cs4236_ctrl_out(chip, 8, 0x8c);
 	chip->rate_constraint = snd_cs4236_xrate;
 	chip->rate_constraint = snd_cs4236_xrate;
 	chip->set_playback_format = snd_cs4236_playback_format;
 	chip->set_playback_format = snd_cs4236_playback_format;
@@ -339,9 +351,10 @@ int snd_cs4236_create(struct snd_card *card,
 
 
 	/* initialize extended registers */
 	/* initialize extended registers */
 	for (reg = 0; reg < sizeof(snd_cs4236_ext_map); reg++)
 	for (reg = 0; reg < sizeof(snd_cs4236_ext_map); reg++)
-		snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), snd_cs4236_ext_map[reg]);
+		snd_cs4236_ext_out(chip, CS4236_I23VAL(reg),
+				   snd_cs4236_ext_map[reg]);
 
 
-        /* initialize compatible but more featured registers */
+	/* initialize compatible but more featured registers */
 	snd_wss_out(chip, CS4231_LEFT_INPUT, 0x40);
 	snd_wss_out(chip, CS4231_LEFT_INPUT, 0x40);
 	snd_wss_out(chip, CS4231_RIGHT_INPUT, 0x40);
 	snd_wss_out(chip, CS4231_RIGHT_INPUT, 0x40);
 	snd_wss_out(chip, CS4231_AUX1_LEFT_INPUT, 0xff);
 	snd_wss_out(chip, CS4231_AUX1_LEFT_INPUT, 0xff);
@@ -387,6 +400,14 @@ int snd_cs4236_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm)
   .get = snd_cs4236_get_single, .put = snd_cs4236_put_single, \
   .get = snd_cs4236_get_single, .put = snd_cs4236_put_single, \
   .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
   .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
 
 
+#define CS4236_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+  .info = snd_cs4236_info_single, \
+  .get = snd_cs4236_get_single, .put = snd_cs4236_put_single, \
+  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \
+  .tlv = { .p = (xtlv) } }
+
 static int snd_cs4236_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 static int snd_cs4236_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 {
 {
 	int mask = (kcontrol->private_value >> 16) & 0xff;
 	int mask = (kcontrol->private_value >> 16) & 0xff;
@@ -490,6 +511,16 @@ static int snd_cs4236_put_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_
   .get = snd_cs4236_get_double, .put = snd_cs4236_put_double, \
   .get = snd_cs4236_get_double, .put = snd_cs4236_put_double, \
   .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
   .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
 
 
+#define CS4236_DOUBLE_TLV(xname, xindex, left_reg, right_reg, shift_left, \
+			  shift_right, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+  .info = snd_cs4236_info_double, \
+  .get = snd_cs4236_get_double, .put = snd_cs4236_put_double, \
+  .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | \
+		   (shift_right << 19) | (mask << 24) | (invert << 22), \
+  .tlv = { .p = (xtlv) } }
+
 static int snd_cs4236_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 static int snd_cs4236_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 {
 {
 	int mask = (kcontrol->private_value >> 24) & 0xff;
 	int mask = (kcontrol->private_value >> 24) & 0xff;
@@ -560,12 +591,23 @@ static int snd_cs4236_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
 	return change;
 	return change;
 }
 }
 
 
-#define CS4236_DOUBLE1(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+#define CS4236_DOUBLE1(xname, xindex, left_reg, right_reg, shift_left, \
+			shift_right, mask, invert) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
   .info = snd_cs4236_info_double, \
   .info = snd_cs4236_info_double, \
   .get = snd_cs4236_get_double1, .put = snd_cs4236_put_double1, \
   .get = snd_cs4236_get_double1, .put = snd_cs4236_put_double1, \
   .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
   .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
 
 
+#define CS4236_DOUBLE1_TLV(xname, xindex, left_reg, right_reg, shift_left, \
+			   shift_right, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+  .info = snd_cs4236_info_double, \
+  .get = snd_cs4236_get_double1, .put = snd_cs4236_put_double1, \
+  .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | \
+		   (shift_right << 19) | (mask << 24) | (invert << 22), \
+  .tlv = { .p = (xtlv) } }
+
 static int snd_cs4236_get_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 static int snd_cs4236_get_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
 {
 	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
@@ -619,16 +661,18 @@ static int snd_cs4236_put_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_
 	return change;
 	return change;
 }
 }
 
 
-#define CS4236_MASTER_DIGITAL(xname, xindex) \
+#define CS4236_MASTER_DIGITAL(xname, xindex, xtlv) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
   .info = snd_cs4236_info_double, \
   .info = snd_cs4236_info_double, \
   .get = snd_cs4236_get_master_digital, .put = snd_cs4236_put_master_digital, \
   .get = snd_cs4236_get_master_digital, .put = snd_cs4236_put_master_digital, \
-  .private_value = 71 << 24 }
+  .private_value = 71 << 24, \
+  .tlv = { .p = (xtlv) } }
 
 
 static inline int snd_cs4236_mixer_master_digital_invert_volume(int vol)
 static inline int snd_cs4236_mixer_master_digital_invert_volume(int vol)
 {
 {
 	return (vol < 64) ? 63 - vol : 64 + (71 - vol);
 	return (vol < 64) ? 63 - vol : 64 + (71 - vol);
-}        
+}
 
 
 static int snd_cs4236_get_master_digital(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 static int snd_cs4236_get_master_digital(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
 {
@@ -661,11 +705,13 @@ static int snd_cs4236_put_master_digital(struct snd_kcontrol *kcontrol, struct s
 	return change;
 	return change;
 }
 }
 
 
-#define CS4235_OUTPUT_ACCU(xname, xindex) \
+#define CS4235_OUTPUT_ACCU(xname, xindex, xtlv) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
   .info = snd_cs4236_info_double, \
   .info = snd_cs4236_info_double, \
   .get = snd_cs4235_get_output_accu, .put = snd_cs4235_put_output_accu, \
   .get = snd_cs4235_get_output_accu, .put = snd_cs4235_put_output_accu, \
-  .private_value = 3 << 24 }
+  .private_value = 3 << 24, \
+  .tlv = { .p = (xtlv) } }
 
 
 static inline int snd_cs4235_mixer_output_accu_get_volume(int vol)
 static inline int snd_cs4235_mixer_output_accu_get_volume(int vol)
 {
 {
@@ -720,41 +766,56 @@ static int snd_cs4235_put_output_accu(struct snd_kcontrol *kcontrol, struct snd_
 	return change;
 	return change;
 }
 }
 
 
+static const DECLARE_TLV_DB_SCALE(db_scale_7bit, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_6bit_12db_max, -8250, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_5bit_22db_max, -2400, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_2bit, -1800, 600, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
+
 static struct snd_kcontrol_new snd_cs4236_controls[] = {
 static struct snd_kcontrol_new snd_cs4236_controls[] = {
 
 
 CS4236_DOUBLE("Master Digital Playback Switch", 0,
 CS4236_DOUBLE("Master Digital Playback Switch", 0,
 		CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1),
 		CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1),
 CS4236_DOUBLE("Master Digital Capture Switch", 0,
 CS4236_DOUBLE("Master Digital Capture Switch", 0,
 		CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),
 		CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),
-CS4236_MASTER_DIGITAL("Master Digital Volume", 0),
+CS4236_MASTER_DIGITAL("Master Digital Volume", 0, db_scale_7bit),
 
 
-CS4236_DOUBLE("Capture Boost Volume", 0,
-		CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1),
+CS4236_DOUBLE_TLV("Capture Boost Volume", 0,
+		  CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1,
+		  db_scale_2bit),
 
 
 WSS_DOUBLE("PCM Playback Switch", 0,
 WSS_DOUBLE("PCM Playback Switch", 0,
 		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
 		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
-WSS_DOUBLE("PCM Playback Volume", 0,
-		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
+WSS_DOUBLE_TLV("PCM Playback Volume", 0,
+		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1,
+		db_scale_6bit),
 
 
 CS4236_DOUBLE("DSP Playback Switch", 0,
 CS4236_DOUBLE("DSP Playback Switch", 0,
 		CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1),
 		CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1),
-CS4236_DOUBLE("DSP Playback Volume", 0,
-		CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 0, 0, 63, 1),
+CS4236_DOUBLE_TLV("DSP Playback Volume", 0,
+		  CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 0, 0, 63, 1,
+		  db_scale_6bit),
 
 
 CS4236_DOUBLE("FM Playback Switch", 0,
 CS4236_DOUBLE("FM Playback Switch", 0,
 		CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1),
 		CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1),
-CS4236_DOUBLE("FM Playback Volume", 0,
-		CS4236_LEFT_FM, CS4236_RIGHT_FM, 0, 0, 63, 1),
+CS4236_DOUBLE_TLV("FM Playback Volume", 0,
+		  CS4236_LEFT_FM, CS4236_RIGHT_FM, 0, 0, 63, 1,
+		  db_scale_6bit),
 
 
 CS4236_DOUBLE("Wavetable Playback Switch", 0,
 CS4236_DOUBLE("Wavetable Playback Switch", 0,
 		CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1),
 		CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1),
-CS4236_DOUBLE("Wavetable Playback Volume", 0,
-		CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 0, 0, 63, 1),
+CS4236_DOUBLE_TLV("Wavetable Playback Volume", 0,
+		  CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 0, 0, 63, 1,
+		  db_scale_6bit_12db_max),
 
 
 WSS_DOUBLE("Synth Playback Switch", 0,
 WSS_DOUBLE("Synth Playback Switch", 0,
 		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
 		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
-WSS_DOUBLE("Synth Volume", 0,
-		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
+WSS_DOUBLE_TLV("Synth Volume", 0,
+		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1,
+		db_scale_5bit_12db_max),
 WSS_DOUBLE("Synth Capture Switch", 0,
 WSS_DOUBLE("Synth Capture Switch", 0,
 		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1),
 		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1),
 WSS_DOUBLE("Synth Capture Bypass", 0,
 WSS_DOUBLE("Synth Capture Bypass", 0,
@@ -764,14 +825,16 @@ CS4236_DOUBLE("Mic Playback Switch", 0,
 		CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1),
 		CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1),
 CS4236_DOUBLE("Mic Capture Switch", 0,
 CS4236_DOUBLE("Mic Capture Switch", 0,
 		CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1),
 		CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1),
-CS4236_DOUBLE("Mic Volume", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 0, 0, 31, 1),
-CS4236_DOUBLE("Mic Playback Boost", 0,
+CS4236_DOUBLE_TLV("Mic Volume", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC,
+		  0, 0, 31, 1, db_scale_5bit_22db_max),
+CS4236_DOUBLE("Mic Playback Boost (+20dB)", 0,
 		CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 5, 5, 1, 0),
 		CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 5, 5, 1, 0),
 
 
 WSS_DOUBLE("Line Playback Switch", 0,
 WSS_DOUBLE("Line Playback Switch", 0,
 		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
 		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
-WSS_DOUBLE("Line Volume", 0,
-		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
+WSS_DOUBLE_TLV("Line Volume", 0,
+		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1,
+		db_scale_5bit_12db_max),
 WSS_DOUBLE("Line Capture Switch", 0,
 WSS_DOUBLE("Line Capture Switch", 0,
 		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1),
 		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1),
 WSS_DOUBLE("Line Capture Bypass", 0,
 WSS_DOUBLE("Line Capture Bypass", 0,
@@ -779,57 +842,63 @@ WSS_DOUBLE("Line Capture Bypass", 0,
 
 
 WSS_DOUBLE("CD Playback Switch", 0,
 WSS_DOUBLE("CD Playback Switch", 0,
 		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
 		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
-WSS_DOUBLE("CD Volume", 0,
-		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
+WSS_DOUBLE_TLV("CD Volume", 0,
+		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1,
+		db_scale_5bit_12db_max),
 WSS_DOUBLE("CD Capture Switch", 0,
 WSS_DOUBLE("CD Capture Switch", 0,
 		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1),
 		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1),
 
 
 CS4236_DOUBLE1("Mono Output Playback Switch", 0,
 CS4236_DOUBLE1("Mono Output Playback Switch", 0,
 		CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1),
 		CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1),
-CS4236_DOUBLE1("Mono Playback Switch", 0,
+CS4236_DOUBLE1("Beep Playback Switch", 0,
 		CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1),
 		CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1),
-WSS_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
-WSS_SINGLE("Mono Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0),
+WSS_SINGLE_TLV("Beep Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1,
+		db_scale_4bit),
+WSS_SINGLE("Beep Bypass Playback Switch", 0, CS4231_MONO_CTRL, 5, 1, 0),
 
 
-WSS_DOUBLE("Capture Volume", 0,
-		CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
+WSS_DOUBLE_TLV("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT,
+		0, 0, 15, 0, db_scale_rec_gain),
 WSS_DOUBLE("Analog Loopback Capture Switch", 0,
 WSS_DOUBLE("Analog Loopback Capture Switch", 0,
 		CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0),
 		CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0),
 
 
-WSS_SINGLE("Digital Loopback Playback Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
-CS4236_DOUBLE1("Digital Loopback Playback Volume", 0,
-		CS4231_LOOPBACK, CS4236_RIGHT_LOOPBACK, 2, 0, 63, 1)
+WSS_SINGLE("Loopback Digital Playback Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
+CS4236_DOUBLE1_TLV("Loopback Digital Playback Volume", 0,
+		   CS4231_LOOPBACK, CS4236_RIGHT_LOOPBACK, 2, 0, 63, 1,
+		   db_scale_6bit),
 };
 };
 
 
+static const DECLARE_TLV_DB_SCALE(db_scale_5bit_6db_max, -5600, 200, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_2bit_16db_max, -2400, 800, 0);
+
 static struct snd_kcontrol_new snd_cs4235_controls[] = {
 static struct snd_kcontrol_new snd_cs4235_controls[] = {
 
 
-WSS_DOUBLE("Master Switch", 0,
+WSS_DOUBLE("Master Playback Switch", 0,
 		CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 7, 7, 1, 1),
 		CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 7, 7, 1, 1),
-WSS_DOUBLE("Master Volume", 0,
-		CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 0, 0, 31, 1),
-
-CS4235_OUTPUT_ACCU("Playback Volume", 0),
+WSS_DOUBLE_TLV("Master Playback Volume", 0,
+		CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 0, 0, 31, 1,
+		db_scale_5bit_6db_max),
 
 
-CS4236_DOUBLE("Master Digital Playback Switch", 0,
-		CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1),
-CS4236_DOUBLE("Master Digital Capture Switch", 0,
-		CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),
-CS4236_MASTER_DIGITAL("Master Digital Volume", 0),
+CS4235_OUTPUT_ACCU("Playback Volume", 0, db_scale_2bit_16db_max),
 
 
-WSS_DOUBLE("Master Digital Playback Switch", 1,
+WSS_DOUBLE("Synth Playback Switch", 1,
 		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
 		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
-WSS_DOUBLE("Master Digital Capture Switch", 1,
+WSS_DOUBLE("Synth Capture Switch", 1,
 		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1),
 		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1),
-WSS_DOUBLE("Master Digital Volume", 1,
-		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
+WSS_DOUBLE_TLV("Synth Volume", 1,
+		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1,
+		db_scale_5bit_12db_max),
 
 
-CS4236_DOUBLE("Capture Volume", 0,
-		CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1),
+CS4236_DOUBLE_TLV("Capture Volume", 0,
+		  CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1,
+		  db_scale_2bit),
 
 
-WSS_DOUBLE("PCM Switch", 0,
+WSS_DOUBLE("PCM Playback Switch", 0,
 		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
 		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
-WSS_DOUBLE("PCM Volume", 0,
-		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
+WSS_DOUBLE("PCM Capture Switch", 0,
+		CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),
+WSS_DOUBLE_TLV("PCM Volume", 0,
+		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1,
+		db_scale_6bit),
 
 
 CS4236_DOUBLE("DSP Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1),
 CS4236_DOUBLE("DSP Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1),
 
 
@@ -842,29 +911,29 @@ CS4236_DOUBLE("Mic Capture Switch", 0,
 		CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1),
 		CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1),
 CS4236_DOUBLE("Mic Playback Switch", 0,
 CS4236_DOUBLE("Mic Playback Switch", 0,
 		CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1),
 		CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1),
-CS4236_SINGLE("Mic Volume", 0, CS4236_LEFT_MIC, 0, 31, 1),
-CS4236_SINGLE("Mic Playback Boost", 0, CS4236_LEFT_MIC, 5, 1, 0),
+CS4236_SINGLE_TLV("Mic Volume", 0, CS4236_LEFT_MIC, 0, 31, 1,
+		  db_scale_5bit_22db_max),
+CS4236_SINGLE("Mic Boost (+20dB)", 0, CS4236_LEFT_MIC, 5, 1, 0),
 
 
-WSS_DOUBLE("Aux Playback Switch", 0,
+WSS_DOUBLE("Line Playback Switch", 0,
 		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
 		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
-WSS_DOUBLE("Aux Capture Switch", 0,
+WSS_DOUBLE("Line Capture Switch", 0,
 		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1),
 		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1),
-WSS_DOUBLE("Aux Volume", 0,
-		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
+WSS_DOUBLE_TLV("Line Volume", 0,
+		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1,
+		db_scale_5bit_12db_max),
 
 
-WSS_DOUBLE("Aux Playback Switch", 1,
+WSS_DOUBLE("CD Playback Switch", 1,
 		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
 		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
-WSS_DOUBLE("Aux Capture Switch", 1,
+WSS_DOUBLE("CD Capture Switch", 1,
 		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1),
 		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1),
-WSS_DOUBLE("Aux Volume", 1,
-		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
-
-CS4236_DOUBLE1("Master Mono Switch", 0,
-		CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1),
+WSS_DOUBLE_TLV("CD Volume", 1,
+		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1,
+		db_scale_5bit_12db_max),
 
 
-CS4236_DOUBLE1("Mono Switch", 0,
+CS4236_DOUBLE1("Beep Playback Switch", 0,
 		CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1),
 		CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1),
-WSS_SINGLE("Mono Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
+WSS_SINGLE("Beep Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
 
 
 WSS_DOUBLE("Analog Loopback Switch", 0,
 WSS_DOUBLE("Analog Loopback Switch", 0,
 		CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0),
 		CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0),

+ 1 - 1
sound/isa/es1688/es1688_lib.c

@@ -982,7 +982,7 @@ ES1688_DOUBLE("CD Playback Volume", 0, ES1688_CD_DEV, ES1688_CD_DEV, 4, 0, 15, 0
 ES1688_DOUBLE("FM Playback Volume", 0, ES1688_FM_DEV, ES1688_FM_DEV, 4, 0, 15, 0),
 ES1688_DOUBLE("FM Playback Volume", 0, ES1688_FM_DEV, ES1688_FM_DEV, 4, 0, 15, 0),
 ES1688_DOUBLE("Mic Playback Volume", 0, ES1688_MIC_DEV, ES1688_MIC_DEV, 4, 0, 15, 0),
 ES1688_DOUBLE("Mic Playback Volume", 0, ES1688_MIC_DEV, ES1688_MIC_DEV, 4, 0, 15, 0),
 ES1688_DOUBLE("Aux Playback Volume", 0, ES1688_AUX_DEV, ES1688_AUX_DEV, 4, 0, 15, 0),
 ES1688_DOUBLE("Aux Playback Volume", 0, ES1688_AUX_DEV, ES1688_AUX_DEV, 4, 0, 15, 0),
-ES1688_SINGLE("PC Speaker Playback Volume", 0, ES1688_SPEAKER_DEV, 0, 7, 0),
+ES1688_SINGLE("Beep Playback Volume", 0, ES1688_SPEAKER_DEV, 0, 7, 0),
 ES1688_DOUBLE("Capture Volume", 0, ES1688_RECLEV_DEV, ES1688_RECLEV_DEV, 4, 0, 15, 0),
 ES1688_DOUBLE("Capture Volume", 0, ES1688_RECLEV_DEV, ES1688_RECLEV_DEV, 4, 0, 15, 0),
 ES1688_SINGLE("Capture Switch", 0, ES1688_REC_DEV, 4, 1, 1),
 ES1688_SINGLE("Capture Switch", 0, ES1688_REC_DEV, 4, 1, 1),
 {
 {

+ 102 - 119
sound/isa/es18xx.c

@@ -102,8 +102,6 @@
 
 
 struct snd_es18xx {
 struct snd_es18xx {
 	unsigned long port;		/* port of ESS chip */
 	unsigned long port;		/* port of ESS chip */
-	unsigned long mpu_port;		/* MPU-401 port of ESS chip */
-	unsigned long fm_port;		/* FM port */
 	unsigned long ctrl_port;	/* Control port of ESS chip */
 	unsigned long ctrl_port;	/* Control port of ESS chip */
 	struct resource *res_port;
 	struct resource *res_port;
 	struct resource *res_mpu_port;
 	struct resource *res_mpu_port;
@@ -116,12 +114,9 @@ struct snd_es18xx {
 	unsigned short audio2_vol;	/* volume level of audio2 */
 	unsigned short audio2_vol;	/* volume level of audio2 */
 
 
 	unsigned short active;		/* active channel mask */
 	unsigned short active;		/* active channel mask */
-	unsigned int dma1_size;
-	unsigned int dma2_size;
 	unsigned int dma1_shift;
 	unsigned int dma1_shift;
 	unsigned int dma2_shift;
 	unsigned int dma2_shift;
 
 
-	struct snd_card *card;
 	struct snd_pcm *pcm;
 	struct snd_pcm *pcm;
 	struct snd_pcm_substream *playback_a_substream;
 	struct snd_pcm_substream *playback_a_substream;
 	struct snd_pcm_substream *capture_a_substream;
 	struct snd_pcm_substream *capture_a_substream;
@@ -136,14 +131,9 @@ struct snd_es18xx {
 
 
 	spinlock_t reg_lock;
 	spinlock_t reg_lock;
 	spinlock_t mixer_lock;
 	spinlock_t mixer_lock;
-	spinlock_t ctrl_lock;
 #ifdef CONFIG_PM
 #ifdef CONFIG_PM
 	unsigned char pm_reg;
 	unsigned char pm_reg;
 #endif
 #endif
-};
-
-struct snd_audiodrive {
-	struct snd_es18xx *chip;
 #ifdef CONFIG_PNP
 #ifdef CONFIG_PNP
 	struct pnp_dev *dev;
 	struct pnp_dev *dev;
 	struct pnp_dev *devc;
 	struct pnp_dev *devc;
@@ -359,7 +349,7 @@ static inline int snd_es18xx_mixer_writable(struct snd_es18xx *chip, unsigned ch
 }
 }
 
 
 
 
-static int snd_es18xx_reset(struct snd_es18xx *chip)
+static int __devinit snd_es18xx_reset(struct snd_es18xx *chip)
 {
 {
 	int i;
 	int i;
         outb(0x03, chip->port + 0x06);
         outb(0x03, chip->port + 0x06);
@@ -495,8 +485,6 @@ static int snd_es18xx_playback1_prepare(struct snd_es18xx *chip,
 	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);
 
 
-	chip->dma2_size = size;
-
         snd_es18xx_rate_set(chip, substream, DAC2);
         snd_es18xx_rate_set(chip, substream, DAC2);
 
 
         /* Transfer Count Reload */
         /* Transfer Count Reload */
@@ -596,8 +584,6 @@ static int snd_es18xx_capture_prepare(struct snd_pcm_substream *substream)
 	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);
 
 
-	chip->dma1_size = size;
-
 	snd_es18xx_reset_fifo(chip);
 	snd_es18xx_reset_fifo(chip);
 
 
         /* Set stereo/mono */
         /* Set stereo/mono */
@@ -664,8 +650,6 @@ static int snd_es18xx_playback2_prepare(struct snd_es18xx *chip,
 	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);
 
 
-	chip->dma1_size = size;
-
 	snd_es18xx_reset_fifo(chip);
 	snd_es18xx_reset_fifo(chip);
 
 
         /* Set stereo/mono */
         /* Set stereo/mono */
@@ -755,7 +739,8 @@ static int snd_es18xx_playback_trigger(struct snd_pcm_substream *substream,
 
 
 static irqreturn_t snd_es18xx_interrupt(int irq, void *dev_id)
 static irqreturn_t snd_es18xx_interrupt(int irq, void *dev_id)
 {
 {
-	struct snd_es18xx *chip = dev_id;
+	struct snd_card *card = dev_id;
+	struct snd_es18xx *chip = card->private_data;
 	unsigned char status;
 	unsigned char status;
 
 
 	if (chip->caps & ES18XX_CONTROL) {
 	if (chip->caps & ES18XX_CONTROL) {
@@ -805,12 +790,16 @@ static irqreturn_t snd_es18xx_interrupt(int irq, void *dev_id)
 		int split = 0;
 		int split = 0;
 		if (chip->caps & ES18XX_HWV) {
 		if (chip->caps & ES18XX_HWV) {
 			split = snd_es18xx_mixer_read(chip, 0x64) & 0x80;
 			split = snd_es18xx_mixer_read(chip, 0x64) & 0x80;
-			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_switch->id);
-			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_volume->id);
+			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+					&chip->hw_switch->id);
+			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+					&chip->hw_volume->id);
 		}
 		}
 		if (!split) {
 		if (!split) {
-			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id);
-			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id);
+			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+					&chip->master_switch->id);
+			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+					&chip->master_volume->id);
 		}
 		}
 		/* ack interrupt */
 		/* ack interrupt */
 		snd_es18xx_mixer_write(chip, 0x66, 0x00);
 		snd_es18xx_mixer_write(chip, 0x66, 0x00);
@@ -821,17 +810,18 @@ static irqreturn_t snd_es18xx_interrupt(int irq, void *dev_id)
 static snd_pcm_uframes_t snd_es18xx_playback_pointer(struct snd_pcm_substream *substream)
 static snd_pcm_uframes_t snd_es18xx_playback_pointer(struct snd_pcm_substream *substream)
 {
 {
         struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
         struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
 	int pos;
 	int pos;
 
 
 	if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) {
 	if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) {
 		if (!(chip->active & DAC2))
 		if (!(chip->active & DAC2))
 			return 0;
 			return 0;
-		pos = snd_dma_pointer(chip->dma2, chip->dma2_size);
+		pos = snd_dma_pointer(chip->dma2, size);
 		return pos >> chip->dma2_shift;
 		return pos >> chip->dma2_shift;
 	} else {
 	} else {
 		if (!(chip->active & DAC1))
 		if (!(chip->active & DAC1))
 			return 0;
 			return 0;
-		pos = snd_dma_pointer(chip->dma1, chip->dma1_size);
+		pos = snd_dma_pointer(chip->dma1, size);
 		return pos >> chip->dma1_shift;
 		return pos >> chip->dma1_shift;
 	}
 	}
 }
 }
@@ -839,11 +829,12 @@ static snd_pcm_uframes_t snd_es18xx_playback_pointer(struct snd_pcm_substream *s
 static snd_pcm_uframes_t snd_es18xx_capture_pointer(struct snd_pcm_substream *substream)
 static snd_pcm_uframes_t snd_es18xx_capture_pointer(struct snd_pcm_substream *substream)
 {
 {
         struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
         struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
 	int pos;
 	int pos;
 
 
         if (!(chip->active & ADC1))
         if (!(chip->active & ADC1))
                 return 0;
                 return 0;
-	pos = snd_dma_pointer(chip->dma1, chip->dma1_size);
+	pos = snd_dma_pointer(chip->dma1, size);
 	return pos >> chip->dma1_shift;
 	return pos >> chip->dma1_shift;
 }
 }
 
 
@@ -974,9 +965,6 @@ static int snd_es18xx_capture_close(struct snd_pcm_substream *substream)
 
 
 static int snd_es18xx_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 static int snd_es18xx_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 {
 {
-	static char *texts4Source[4] = {
-		"Mic", "CD", "Line", "Master"
-	};
 	static char *texts5Source[5] = {
 	static char *texts5Source[5] = {
 		"Mic", "CD", "Line", "Master", "Mix"
 		"Mic", "CD", "Line", "Master", "Mix"
 	};
 	};
@@ -994,7 +982,8 @@ static int snd_es18xx_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
 		uinfo->value.enumerated.items = 4;
 		uinfo->value.enumerated.items = 4;
 		if (uinfo->value.enumerated.item > 3)
 		if (uinfo->value.enumerated.item > 3)
 			uinfo->value.enumerated.item = 3;
 			uinfo->value.enumerated.item = 3;
-		strcpy(uinfo->value.enumerated.name, texts4Source[uinfo->value.enumerated.item]);
+		strcpy(uinfo->value.enumerated.name,
+			texts5Source[uinfo->value.enumerated.item]);
 		break;
 		break;
 	case 0x1887:
 	case 0x1887:
 	case 0x1888:
 	case 0x1888:
@@ -1313,7 +1302,7 @@ ES18XX_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0)
  * The chipset specific mixer controls
  * The chipset specific mixer controls
  */
  */
 static struct snd_kcontrol_new snd_es18xx_opt_speaker =
 static struct snd_kcontrol_new snd_es18xx_opt_speaker =
-	ES18XX_SINGLE("PC Speaker Playback Volume", 0, 0x3c, 0, 7, 0);
+	ES18XX_SINGLE("Beep Playback Volume", 0, 0x3c, 0, 7, 0);
 
 
 static struct snd_kcontrol_new snd_es18xx_opt_1869[] = {
 static struct snd_kcontrol_new snd_es18xx_opt_1869[] = {
 ES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
 ES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
@@ -1378,11 +1367,9 @@ ES18XX_SINGLE("Hardware Master Volume Split", 0, 0x64, 7, 1, 0),
 static int __devinit snd_es18xx_config_read(struct snd_es18xx *chip, unsigned char reg)
 static int __devinit snd_es18xx_config_read(struct snd_es18xx *chip, unsigned char reg)
 {
 {
 	int data;
 	int data;
-	unsigned long flags;
-        spin_lock_irqsave(&chip->ctrl_lock, flags);
+
 	outb(reg, chip->ctrl_port);
 	outb(reg, chip->ctrl_port);
 	data = inb(chip->ctrl_port + 1);
 	data = inb(chip->ctrl_port + 1);
-        spin_unlock_irqrestore(&chip->ctrl_lock, flags);
 	return data;
 	return data;
 }
 }
 
 
@@ -1398,7 +1385,9 @@ static void __devinit snd_es18xx_config_write(struct snd_es18xx *chip,
 #endif
 #endif
 }
 }
 
 
-static int __devinit snd_es18xx_initialize(struct snd_es18xx *chip)
+static int __devinit snd_es18xx_initialize(struct snd_es18xx *chip,
+					   unsigned long mpu_port,
+					   unsigned long fm_port)
 {
 {
 	int mask = 0;
 	int mask = 0;
 
 
@@ -1412,15 +1401,15 @@ static int __devinit snd_es18xx_initialize(struct snd_es18xx *chip)
 	if (chip->caps & ES18XX_CONTROL) {
 	if (chip->caps & ES18XX_CONTROL) {
 		/* Hardware volume IRQ */
 		/* Hardware volume IRQ */
 		snd_es18xx_config_write(chip, 0x27, chip->irq);
 		snd_es18xx_config_write(chip, 0x27, chip->irq);
-		if (chip->fm_port > 0 && chip->fm_port != SNDRV_AUTO_PORT) {
+		if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) {
 			/* FM I/O */
 			/* FM I/O */
-			snd_es18xx_config_write(chip, 0x62, chip->fm_port >> 8);
-			snd_es18xx_config_write(chip, 0x63, chip->fm_port & 0xff);
+			snd_es18xx_config_write(chip, 0x62, fm_port >> 8);
+			snd_es18xx_config_write(chip, 0x63, fm_port & 0xff);
 		}
 		}
-		if (chip->mpu_port > 0 && chip->mpu_port != SNDRV_AUTO_PORT) {
+		if (mpu_port > 0 && mpu_port != SNDRV_AUTO_PORT) {
 			/* MPU-401 I/O */
 			/* MPU-401 I/O */
-			snd_es18xx_config_write(chip, 0x64, chip->mpu_port >> 8);
-			snd_es18xx_config_write(chip, 0x65, chip->mpu_port & 0xff);
+			snd_es18xx_config_write(chip, 0x64, mpu_port >> 8);
+			snd_es18xx_config_write(chip, 0x65, mpu_port & 0xff);
 			/* MPU-401 IRQ */
 			/* MPU-401 IRQ */
 			snd_es18xx_config_write(chip, 0x28, chip->irq);
 			snd_es18xx_config_write(chip, 0x28, chip->irq);
 		}
 		}
@@ -1507,11 +1496,12 @@ static int __devinit snd_es18xx_initialize(struct snd_es18xx *chip)
 		snd_es18xx_mixer_write(chip, 0x7A, 0x68);
 		snd_es18xx_mixer_write(chip, 0x7A, 0x68);
 		/* Enable and set hardware volume interrupt */
 		/* Enable and set hardware volume interrupt */
 		snd_es18xx_mixer_write(chip, 0x64, 0x06);
 		snd_es18xx_mixer_write(chip, 0x64, 0x06);
-		if (chip->mpu_port > 0 && chip->mpu_port != SNDRV_AUTO_PORT) {
+		if (mpu_port > 0 && mpu_port != SNDRV_AUTO_PORT) {
 			/* MPU401 share irq with audio
 			/* MPU401 share irq with audio
 			   Joystick enabled
 			   Joystick enabled
 			   FM enabled */
 			   FM enabled */
-			snd_es18xx_mixer_write(chip, 0x40, 0x43 | (chip->mpu_port & 0xf0) >> 1);
+			snd_es18xx_mixer_write(chip, 0x40,
+					       0x43 | (mpu_port & 0xf0) >> 1);
 		}
 		}
 		snd_es18xx_mixer_write(chip, 0x7f, ((irqmask + 1) << 1) | 0x01);
 		snd_es18xx_mixer_write(chip, 0x7f, ((irqmask + 1) << 1) | 0x01);
 	}
 	}
@@ -1629,7 +1619,9 @@ static int __devinit snd_es18xx_identify(struct snd_es18xx *chip)
 	return 0;
 	return 0;
 }
 }
 
 
-static int __devinit snd_es18xx_probe(struct snd_es18xx *chip)
+static int __devinit snd_es18xx_probe(struct snd_es18xx *chip,
+					unsigned long mpu_port,
+					unsigned long fm_port)
 {
 {
 	if (snd_es18xx_identify(chip) < 0) {
 	if (snd_es18xx_identify(chip) < 0) {
 		snd_printk(KERN_ERR PFX "[0x%lx] ESS chip not found\n", chip->port);
 		snd_printk(KERN_ERR PFX "[0x%lx] ESS chip not found\n", chip->port);
@@ -1650,8 +1642,6 @@ static int __devinit snd_es18xx_probe(struct snd_es18xx *chip)
 		chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_I2S | ES18XX_CONTROL | ES18XX_HWV;
 		chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_I2S | ES18XX_CONTROL | ES18XX_HWV;
 		break;
 		break;
 	case 0x1887:
 	case 0x1887:
-		chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME;
-		break;
 	case 0x1888:
 	case 0x1888:
 		chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME;
 		chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME;
 		break;
 		break;
@@ -1666,7 +1656,7 @@ static int __devinit snd_es18xx_probe(struct snd_es18xx *chip)
 	if (chip->dma1 == chip->dma2)
 	if (chip->dma1 == chip->dma2)
 		chip->caps &= ~(ES18XX_PCM2 | ES18XX_DUPLEX_SAME);
 		chip->caps &= ~(ES18XX_PCM2 | ES18XX_DUPLEX_SAME);
 
 
-        return snd_es18xx_initialize(chip);
+	return snd_es18xx_initialize(chip, mpu_port, fm_port);
 }
 }
 
 
 static struct snd_pcm_ops snd_es18xx_playback_ops = {
 static struct snd_pcm_ops snd_es18xx_playback_ops = {
@@ -1691,8 +1681,10 @@ static struct snd_pcm_ops snd_es18xx_capture_ops = {
 	.pointer =	snd_es18xx_capture_pointer,
 	.pointer =	snd_es18xx_capture_pointer,
 };
 };
 
 
-static int __devinit snd_es18xx_pcm(struct snd_es18xx *chip, int device, struct snd_pcm ** rpcm)
+static int __devinit snd_es18xx_pcm(struct snd_card *card, int device,
+				    struct snd_pcm **rpcm)
 {
 {
+	struct snd_es18xx *chip = card->private_data;
         struct snd_pcm *pcm;
         struct snd_pcm *pcm;
 	char str[16];
 	char str[16];
 	int err;
 	int err;
@@ -1701,9 +1693,9 @@ static int __devinit snd_es18xx_pcm(struct snd_es18xx *chip, int device, struct
 		*rpcm = NULL;
 		*rpcm = NULL;
 	sprintf(str, "ES%x", chip->version);
 	sprintf(str, "ES%x", chip->version);
 	if (chip->caps & ES18XX_PCM2)
 	if (chip->caps & ES18XX_PCM2)
-		err = snd_pcm_new(chip->card, str, device, 2, 1, &pcm);
+		err = snd_pcm_new(card, str, device, 2, 1, &pcm);
 	else
 	else
-		err = snd_pcm_new(chip->card, str, device, 1, 1, &pcm);
+		err = snd_pcm_new(card, str, device, 1, 1, &pcm);
         if (err < 0)
         if (err < 0)
                 return err;
                 return err;
 
 
@@ -1734,10 +1726,9 @@ static int __devinit snd_es18xx_pcm(struct snd_es18xx *chip, int device, struct
 #ifdef CONFIG_PM
 #ifdef CONFIG_PM
 static int snd_es18xx_suspend(struct snd_card *card, pm_message_t state)
 static int snd_es18xx_suspend(struct snd_card *card, pm_message_t state)
 {
 {
-	struct snd_audiodrive *acard = card->private_data;
-	struct snd_es18xx *chip = acard->chip;
+	struct snd_es18xx *chip = card->private_data;
 
 
-	snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
 
 
 	snd_pcm_suspend_all(chip->pcm);
 	snd_pcm_suspend_all(chip->pcm);
 
 
@@ -1752,24 +1743,25 @@ static int snd_es18xx_suspend(struct snd_card *card, pm_message_t state)
 
 
 static int snd_es18xx_resume(struct snd_card *card)
 static int snd_es18xx_resume(struct snd_card *card)
 {
 {
-	struct snd_audiodrive *acard = card->private_data;
-	struct snd_es18xx *chip = acard->chip;
+	struct snd_es18xx *chip = card->private_data;
 
 
 	/* restore PM register, we won't wake till (not 0x07) i/o activity though */
 	/* restore PM register, we won't wake till (not 0x07) i/o activity though */
 	snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_FM);
 	snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_FM);
 
 
-	snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
 	return 0;
 	return 0;
 }
 }
 #endif /* CONFIG_PM */
 #endif /* CONFIG_PM */
 
 
-static int snd_es18xx_free(struct snd_es18xx *chip)
+static int snd_es18xx_free(struct snd_card *card)
 {
 {
+	struct snd_es18xx *chip = card->private_data;
+
 	release_and_free_resource(chip->res_port);
 	release_and_free_resource(chip->res_port);
 	release_and_free_resource(chip->res_ctrl_port);
 	release_and_free_resource(chip->res_ctrl_port);
 	release_and_free_resource(chip->res_mpu_port);
 	release_and_free_resource(chip->res_mpu_port);
 	if (chip->irq >= 0)
 	if (chip->irq >= 0)
-		free_irq(chip->irq, (void *) chip);
+		free_irq(chip->irq, (void *) card);
 	if (chip->dma1 >= 0) {
 	if (chip->dma1 >= 0) {
 		disable_dma(chip->dma1);
 		disable_dma(chip->dma1);
 		free_dma(chip->dma1);
 		free_dma(chip->dma1);
@@ -1778,93 +1770,82 @@ static int snd_es18xx_free(struct snd_es18xx *chip)
 		disable_dma(chip->dma2);
 		disable_dma(chip->dma2);
 		free_dma(chip->dma2);
 		free_dma(chip->dma2);
 	}
 	}
-	kfree(chip);
 	return 0;
 	return 0;
 }
 }
 
 
 static int snd_es18xx_dev_free(struct snd_device *device)
 static int snd_es18xx_dev_free(struct snd_device *device)
 {
 {
-	struct snd_es18xx *chip = device->device_data;
-	return snd_es18xx_free(chip);
+	return snd_es18xx_free(device->card);
 }
 }
 
 
 static int __devinit snd_es18xx_new_device(struct snd_card *card,
 static int __devinit snd_es18xx_new_device(struct snd_card *card,
 					   unsigned long port,
 					   unsigned long port,
 					   unsigned long mpu_port,
 					   unsigned long mpu_port,
 					   unsigned long fm_port,
 					   unsigned long fm_port,
-					   int irq, int dma1, int dma2,
-					   struct snd_es18xx ** rchip)
+					   int irq, int dma1, int dma2)
 {
 {
-        struct snd_es18xx *chip;
+	struct snd_es18xx *chip = card->private_data;
 	static struct snd_device_ops ops = {
 	static struct snd_device_ops ops = {
 		.dev_free =	snd_es18xx_dev_free,
 		.dev_free =	snd_es18xx_dev_free,
         };
         };
 	int err;
 	int err;
 
 
-	*rchip = NULL;
-        chip = kzalloc(sizeof(*chip), GFP_KERNEL);
-	if (chip == NULL)
-		return -ENOMEM;
 	spin_lock_init(&chip->reg_lock);
 	spin_lock_init(&chip->reg_lock);
  	spin_lock_init(&chip->mixer_lock);
  	spin_lock_init(&chip->mixer_lock);
- 	spin_lock_init(&chip->ctrl_lock);
-        chip->card = card;
         chip->port = port;
         chip->port = port;
-        chip->mpu_port = mpu_port;
-        chip->fm_port = fm_port;
         chip->irq = -1;
         chip->irq = -1;
         chip->dma1 = -1;
         chip->dma1 = -1;
         chip->dma2 = -1;
         chip->dma2 = -1;
         chip->audio2_vol = 0x00;
         chip->audio2_vol = 0x00;
 	chip->active = 0;
 	chip->active = 0;
 
 
-	if ((chip->res_port = request_region(port, 16, "ES18xx")) == NULL) {
-		snd_es18xx_free(chip);
+	chip->res_port = request_region(port, 16, "ES18xx");
+	if (chip->res_port == NULL) {
+		snd_es18xx_free(card);
 		snd_printk(KERN_ERR PFX "unable to grap ports 0x%lx-0x%lx\n", port, port + 16 - 1);
 		snd_printk(KERN_ERR PFX "unable to grap ports 0x%lx-0x%lx\n", port, port + 16 - 1);
 		return -EBUSY;
 		return -EBUSY;
 	}
 	}
 
 
-	if (request_irq(irq, snd_es18xx_interrupt, IRQF_DISABLED, "ES18xx", (void *) chip)) {
-		snd_es18xx_free(chip);
+	if (request_irq(irq, snd_es18xx_interrupt, IRQF_DISABLED, "ES18xx",
+			(void *) card)) {
+		snd_es18xx_free(card);
 		snd_printk(KERN_ERR PFX "unable to grap IRQ %d\n", irq);
 		snd_printk(KERN_ERR PFX "unable to grap IRQ %d\n", irq);
 		return -EBUSY;
 		return -EBUSY;
 	}
 	}
 	chip->irq = irq;
 	chip->irq = irq;
 
 
 	if (request_dma(dma1, "ES18xx DMA 1")) {
 	if (request_dma(dma1, "ES18xx DMA 1")) {
-		snd_es18xx_free(chip);
+		snd_es18xx_free(card);
 		snd_printk(KERN_ERR PFX "unable to grap DMA1 %d\n", dma1);
 		snd_printk(KERN_ERR PFX "unable to grap DMA1 %d\n", dma1);
 		return -EBUSY;
 		return -EBUSY;
 	}
 	}
 	chip->dma1 = dma1;
 	chip->dma1 = dma1;
 
 
 	if (dma2 != dma1 && request_dma(dma2, "ES18xx DMA 2")) {
 	if (dma2 != dma1 && request_dma(dma2, "ES18xx DMA 2")) {
-		snd_es18xx_free(chip);
+		snd_es18xx_free(card);
 		snd_printk(KERN_ERR PFX "unable to grap DMA2 %d\n", dma2);
 		snd_printk(KERN_ERR PFX "unable to grap DMA2 %d\n", dma2);
 		return -EBUSY;
 		return -EBUSY;
 	}
 	}
 	chip->dma2 = dma2;
 	chip->dma2 = dma2;
 
 
-        if (snd_es18xx_probe(chip) < 0) {
-                snd_es18xx_free(chip);
-                return -ENODEV;
-        }
-	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
-		snd_es18xx_free(chip);
+	if (snd_es18xx_probe(chip, mpu_port, fm_port) < 0) {
+		snd_es18xx_free(card);
+		return -ENODEV;
+	}
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+	if (err < 0) {
+		snd_es18xx_free(card);
 		return err;
 		return err;
 	}
 	}
-        *rchip = chip;
         return 0;
         return 0;
 }
 }
 
 
-static int __devinit snd_es18xx_mixer(struct snd_es18xx *chip)
+static int __devinit snd_es18xx_mixer(struct snd_card *card)
 {
 {
-	struct snd_card *card;
+	struct snd_es18xx *chip = card->private_data;
 	int err;
 	int err;
 	unsigned int idx;
 	unsigned int idx;
 
 
-	card = chip->card;
-
 	strcpy(card->mixername, chip->pcm->name);
 	strcpy(card->mixername, chip->pcm->name);
 
 
 	for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_base_controls); idx++) {
 	for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_base_controls); idx++) {
@@ -1986,7 +1967,7 @@ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
 static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
 static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
 #ifdef CONFIG_PNP
 #ifdef CONFIG_PNP
-static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static int isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP;
 #endif
 #endif
 static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x240,0x260,0x280 */
 static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x240,0x260,0x280 */
 #ifndef CONFIG_PNP
 #ifndef CONFIG_PNP
@@ -2063,11 +2044,11 @@ static int __devinit snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev)
 	return 0;
 	return 0;
 }
 }
 
 
-static int __devinit snd_audiodrive_pnp(int dev, struct snd_audiodrive *acard,
+static int __devinit snd_audiodrive_pnp(int dev, struct snd_es18xx *chip,
 					struct pnp_dev *pdev)
 					struct pnp_dev *pdev)
 {
 {
-	acard->dev = pdev;
-	if (snd_audiodrive_pnp_init_main(dev, acard->dev) < 0)
+	chip->dev = pdev;
+	if (snd_audiodrive_pnp_init_main(dev, chip->dev) < 0)
 		return -EBUSY;
 		return -EBUSY;
 	return 0;
 	return 0;
 }
 }
@@ -2093,26 +2074,26 @@ static struct pnp_card_device_id snd_audiodrive_pnpids[] = {
 
 
 MODULE_DEVICE_TABLE(pnp_card, snd_audiodrive_pnpids);
 MODULE_DEVICE_TABLE(pnp_card, snd_audiodrive_pnpids);
 
 
-static int __devinit snd_audiodrive_pnpc(int dev, struct snd_audiodrive *acard,
+static int __devinit snd_audiodrive_pnpc(int dev, struct snd_es18xx *chip,
 					struct pnp_card_link *card,
 					struct pnp_card_link *card,
 					const struct pnp_card_device_id *id)
 					const struct pnp_card_device_id *id)
 {
 {
-	acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
-	if (acard->dev == NULL)
+	chip->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
+	if (chip->dev == NULL)
 		return -EBUSY;
 		return -EBUSY;
 
 
-	acard->devc = pnp_request_card_device(card, id->devs[1].id, NULL);
-	if (acard->devc == NULL)
+	chip->devc = pnp_request_card_device(card, id->devs[1].id, NULL);
+	if (chip->devc == NULL)
 		return -EBUSY;
 		return -EBUSY;
 
 
 	/* Control port initialization */
 	/* Control port initialization */
-	if (pnp_activate_dev(acard->devc) < 0) {
+	if (pnp_activate_dev(chip->devc) < 0) {
 		snd_printk(KERN_ERR PFX "PnP control configure failure (out of resources?)\n");
 		snd_printk(KERN_ERR PFX "PnP control configure failure (out of resources?)\n");
 		return -EAGAIN;
 		return -EAGAIN;
 	}
 	}
 	snd_printdd("pnp: port=0x%llx\n",
 	snd_printdd("pnp: port=0x%llx\n",
-			(unsigned long long)pnp_port_start(acard->devc, 0));
-	if (snd_audiodrive_pnp_init_main(dev, acard->dev) < 0)
+			(unsigned long long)pnp_port_start(chip->devc, 0));
+	if (snd_audiodrive_pnp_init_main(dev, chip->dev) < 0)
 		return -EBUSY;
 		return -EBUSY;
 
 
 	return 0;
 	return 0;
@@ -2128,24 +2109,20 @@ static int __devinit snd_audiodrive_pnpc(int dev, struct snd_audiodrive *acard,
 static int snd_es18xx_card_new(int dev, struct snd_card **cardp)
 static int snd_es18xx_card_new(int dev, struct snd_card **cardp)
 {
 {
 	return snd_card_create(index[dev], id[dev], THIS_MODULE,
 	return snd_card_create(index[dev], id[dev], THIS_MODULE,
-			       sizeof(struct snd_audiodrive), cardp);
+			       sizeof(struct snd_es18xx), cardp);
 }
 }
 
 
 static int __devinit snd_audiodrive_probe(struct snd_card *card, int dev)
 static int __devinit snd_audiodrive_probe(struct snd_card *card, int dev)
 {
 {
-	struct snd_audiodrive *acard = card->private_data;
-	struct snd_es18xx *chip;
+	struct snd_es18xx *chip = card->private_data;
 	struct snd_opl3 *opl3;
 	struct snd_opl3 *opl3;
 	int err;
 	int err;
 
 
-	if ((err = snd_es18xx_new_device(card,
-					 port[dev],
-					 mpu_port[dev],
-					 fm_port[dev],
-					 irq[dev], dma1[dev], dma2[dev],
-					 &chip)) < 0)
+	err = snd_es18xx_new_device(card,
+				    port[dev], mpu_port[dev], fm_port[dev],
+				    irq[dev], dma1[dev], dma2[dev]);
+	if (err < 0)
 		return err;
 		return err;
-	acard->chip = chip;
 
 
 	sprintf(card->driver, "ES%x", chip->version);
 	sprintf(card->driver, "ES%x", chip->version);
 	
 	
@@ -2161,26 +2138,32 @@ static int __devinit snd_audiodrive_probe(struct snd_card *card, int dev)
 			chip->port,
 			chip->port,
 			irq[dev], dma1[dev]);
 			irq[dev], dma1[dev]);
 
 
-	if ((err = snd_es18xx_pcm(chip, 0, NULL)) < 0)
+	err = snd_es18xx_pcm(card, 0, NULL);
+	if (err < 0)
 		return err;
 		return err;
 
 
-	if ((err = snd_es18xx_mixer(chip)) < 0)
+	err = snd_es18xx_mixer(card);
+	if (err < 0)
 		return err;
 		return err;
 
 
 	if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
 	if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
-		if (snd_opl3_create(card, chip->fm_port, chip->fm_port + 2, OPL3_HW_OPL3, 0, &opl3) < 0) {
-			snd_printk(KERN_WARNING PFX "opl3 not detected at 0x%lx\n", chip->fm_port);
+		if (snd_opl3_create(card, fm_port[dev], fm_port[dev] + 2,
+				    OPL3_HW_OPL3, 0, &opl3) < 0) {
+			snd_printk(KERN_WARNING PFX
+				   "opl3 not detected at 0x%lx\n",
+				   fm_port[dev]);
 		} else {
 		} else {
-			if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0)
+			err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+			if (err < 0)
 				return err;
 				return err;
 		}
 		}
 	}
 	}
 
 
 	if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
 	if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
-		if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES18XX,
-					       chip->mpu_port, 0,
-					       irq[dev], 0,
-					       &chip->rmidi)) < 0)
+		err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES18XX,
+					  mpu_port[dev], 0,
+					  irq[dev], 0, &chip->rmidi);
+		if (err < 0)
 			return err;
 			return err;
 	}
 	}
 
 

+ 510 - 273
sound/isa/opti9xx/miro.c

@@ -25,6 +25,7 @@
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/err.h>
 #include <linux/isa.h>
 #include <linux/isa.h>
+#include <linux/pnp.h>
 #include <linux/delay.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/ioport.h>
 #include <linux/ioport.h>
@@ -40,7 +41,7 @@
 #define SNDRV_LEGACY_FIND_FREE_IRQ
 #define SNDRV_LEGACY_FIND_FREE_IRQ
 #define SNDRV_LEGACY_FIND_FREE_DMA
 #define SNDRV_LEGACY_FIND_FREE_DMA
 #include <sound/initval.h>
 #include <sound/initval.h>
-#include "miro.h"
+#include <sound/aci.h>
 
 
 MODULE_AUTHOR("Martin Langer <martin-langer@gmx.de>");
 MODULE_AUTHOR("Martin Langer <martin-langer@gmx.de>");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
@@ -60,6 +61,9 @@ static int dma1 = SNDRV_DEFAULT_DMA1;		/* 0,1,3 */
 static int dma2 = SNDRV_DEFAULT_DMA1;		/* 0,1,3 */
 static int dma2 = SNDRV_DEFAULT_DMA1;		/* 0,1,3 */
 static int wss;
 static int wss;
 static int ide;
 static int ide;
+#ifdef CONFIG_PNP
+static int isapnp = 1;				/* Enable ISA PnP detection */
+#endif
 
 
 module_param(index, int, 0444);
 module_param(index, int, 0444);
 MODULE_PARM_DESC(index, "Index value for miro soundcard.");
 MODULE_PARM_DESC(index, "Index value for miro soundcard.");
@@ -83,6 +87,10 @@ module_param(wss, int, 0444);
 MODULE_PARM_DESC(wss, "wss mode");
 MODULE_PARM_DESC(wss, "wss mode");
 module_param(ide, int, 0444);
 module_param(ide, int, 0444);
 MODULE_PARM_DESC(ide, "enable ide port");
 MODULE_PARM_DESC(ide, "enable ide port");
+#ifdef CONFIG_PNP
+module_param(isapnp, bool, 0444);
+MODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard.");
+#endif
 
 
 #define OPTi9XX_HW_DETECT	0
 #define OPTi9XX_HW_DETECT	0
 #define OPTi9XX_HW_82C928	1
 #define OPTi9XX_HW_82C928	1
@@ -96,7 +104,6 @@ MODULE_PARM_DESC(ide, "enable ide port");
 
 
 #define OPTi9XX_MC_REG(n)	n
 #define OPTi9XX_MC_REG(n)	n
 
 
-
 struct snd_miro {
 struct snd_miro {
 	unsigned short hardware;
 	unsigned short hardware;
 	unsigned char password;
 	unsigned char password;
@@ -110,7 +117,6 @@ struct snd_miro {
 	unsigned long pwd_reg;
 	unsigned long pwd_reg;
 
 
 	spinlock_t lock;
 	spinlock_t lock;
-	struct snd_card *card;
 	struct snd_pcm *pcm;
 	struct snd_pcm *pcm;
 
 
 	long wss_base;
 	long wss_base;
@@ -118,23 +124,13 @@ struct snd_miro {
 	int dma1;
 	int dma1;
 	int dma2;
 	int dma2;
 
 
-	long fm_port;
-
 	long mpu_port;
 	long mpu_port;
 	int mpu_irq;
 	int mpu_irq;
 
 
-	unsigned long aci_port;
-	int aci_vendor;
-	int aci_product;
-	int aci_version;
-	int aci_amp;
-	int aci_preamp;
-	int aci_solomode;
-
-	struct mutex aci_mutex;
+	struct snd_miro_aci *aci;
 };
 };
 
 
-static void snd_miro_proc_init(struct snd_miro * miro);
+static struct snd_miro_aci aci_device;
 
 
 static char * snd_opti9xx_names[] = {
 static char * snd_opti9xx_names[] = {
 	"unkown",
 	"unkown",
@@ -143,17 +139,33 @@ static char * snd_opti9xx_names[] = {
 	"82C930", "82C931", "82C933"
 	"82C930", "82C931", "82C933"
 };
 };
 
 
+static int snd_miro_pnp_is_probed;
+
+#ifdef CONFIG_PNP
+
+static struct pnp_card_device_id snd_miro_pnpids[] = {
+	/* PCM20 and PCM12 in PnP mode */
+	{ .id = "MIR0924",
+	  .devs = { { "MIR0000" }, { "MIR0002" }, { "MIR0005" } }, },
+	{ .id = "" }
+};
+
+MODULE_DEVICE_TABLE(pnp_card, snd_miro_pnpids);
+
+#endif	/* CONFIG_PNP */
+
 /* 
 /* 
  *  ACI control
  *  ACI control
  */
  */
 
 
-static int aci_busy_wait(struct snd_miro * miro)
+static int aci_busy_wait(struct snd_miro_aci *aci)
 {
 {
 	long timeout;
 	long timeout;
 	unsigned char byte;
 	unsigned char byte;
 
 
-	for (timeout = 1; timeout <= ACI_MINTIME+30; timeout++) {
-		if (((byte=inb(miro->aci_port + ACI_REG_BUSY)) & 1) == 0) {
+	for (timeout = 1; timeout <= ACI_MINTIME + 30; timeout++) {
+		byte = inb(aci->aci_port + ACI_REG_BUSY);
+		if ((byte & 1) == 0) {
 			if (timeout >= ACI_MINTIME)
 			if (timeout >= ACI_MINTIME)
 				snd_printd("aci ready in round %ld.\n",
 				snd_printd("aci ready in round %ld.\n",
 					   timeout-ACI_MINTIME);
 					   timeout-ACI_MINTIME);
@@ -179,10 +191,10 @@ static int aci_busy_wait(struct snd_miro * miro)
 	return -EBUSY;
 	return -EBUSY;
 }
 }
 
 
-static inline int aci_write(struct snd_miro * miro, unsigned char byte)
+static inline int aci_write(struct snd_miro_aci *aci, unsigned char byte)
 {
 {
-	if (aci_busy_wait(miro) >= 0) {
-		outb(byte, miro->aci_port + ACI_REG_COMMAND);
+	if (aci_busy_wait(aci) >= 0) {
+		outb(byte, aci->aci_port + ACI_REG_COMMAND);
 		return 0;
 		return 0;
 	} else {
 	} else {
 		snd_printk(KERN_ERR "aci busy, aci_write(0x%x) stopped.\n", byte);
 		snd_printk(KERN_ERR "aci busy, aci_write(0x%x) stopped.\n", byte);
@@ -190,12 +202,12 @@ static inline int aci_write(struct snd_miro * miro, unsigned char byte)
 	}
 	}
 }
 }
 
 
-static inline int aci_read(struct snd_miro * miro)
+static inline int aci_read(struct snd_miro_aci *aci)
 {
 {
 	unsigned char byte;
 	unsigned char byte;
 
 
-	if (aci_busy_wait(miro) >= 0) {
-		byte=inb(miro->aci_port + ACI_REG_STATUS);
+	if (aci_busy_wait(aci) >= 0) {
+		byte = inb(aci->aci_port + ACI_REG_STATUS);
 		return byte;
 		return byte;
 	} else {
 	} else {
 		snd_printk(KERN_ERR "aci busy, aci_read() stopped.\n");
 		snd_printk(KERN_ERR "aci busy, aci_read() stopped.\n");
@@ -203,39 +215,49 @@ static inline int aci_read(struct snd_miro * miro)
 	}
 	}
 }
 }
 
 
-static int aci_cmd(struct snd_miro * miro, int write1, int write2, int write3)
+int snd_aci_cmd(struct snd_miro_aci *aci, int write1, int write2, int write3)
 {
 {
 	int write[] = {write1, write2, write3};
 	int write[] = {write1, write2, write3};
 	int value, i;
 	int value, i;
 
 
-	if (mutex_lock_interruptible(&miro->aci_mutex))
+	if (mutex_lock_interruptible(&aci->aci_mutex))
 		return -EINTR;
 		return -EINTR;
 
 
 	for (i=0; i<3; i++) {
 	for (i=0; i<3; i++) {
 		if (write[i]< 0 || write[i] > 255)
 		if (write[i]< 0 || write[i] > 255)
 			break;
 			break;
 		else {
 		else {
-			value = aci_write(miro, write[i]);
+			value = aci_write(aci, write[i]);
 			if (value < 0)
 			if (value < 0)
 				goto out;
 				goto out;
 		}
 		}
 	}
 	}
 
 
-	value = aci_read(miro);
+	value = aci_read(aci);
 
 
-out:	mutex_unlock(&miro->aci_mutex);
+out:	mutex_unlock(&aci->aci_mutex);
 	return value;
 	return value;
 }
 }
+EXPORT_SYMBOL(snd_aci_cmd);
+
+static int aci_getvalue(struct snd_miro_aci *aci, unsigned char index)
+{
+	return snd_aci_cmd(aci, ACI_STATUS, index, -1);
+}
 
 
-static int aci_getvalue(struct snd_miro * miro, unsigned char index)
+static int aci_setvalue(struct snd_miro_aci *aci, unsigned char index,
+			int value)
 {
 {
-	return aci_cmd(miro, ACI_STATUS, index, -1);
+	return snd_aci_cmd(aci, index, value, -1);
 }
 }
 
 
-static int aci_setvalue(struct snd_miro * miro, unsigned char index, int value)
+struct snd_miro_aci *snd_aci_get_aci(void)
 {
 {
-	return aci_cmd(miro, index, value, -1);
+	if (aci_device.aci_port == 0)
+		return NULL;
+	return &aci_device;
 }
 }
+EXPORT_SYMBOL(snd_aci_get_aci);
 
 
 /*
 /*
  *  MIXER part
  *  MIXER part
@@ -249,8 +271,10 @@ static int snd_miro_get_capture(struct snd_kcontrol *kcontrol,
 	struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
 	struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
 	int value;
 	int value;
 
 
-	if ((value = aci_getvalue(miro, ACI_S_GENERAL)) < 0) {
-		snd_printk(KERN_ERR "snd_miro_get_capture() failed: %d\n", value);
+	value = aci_getvalue(miro->aci, ACI_S_GENERAL);
+	if (value < 0) {
+		snd_printk(KERN_ERR "snd_miro_get_capture() failed: %d\n",
+			   value);
 		return value;
 		return value;
 	}
 	}
 
 
@@ -267,13 +291,15 @@ static int snd_miro_put_capture(struct snd_kcontrol *kcontrol,
 
 
 	value = !(ucontrol->value.integer.value[0]);
 	value = !(ucontrol->value.integer.value[0]);
 
 
-	if ((error = aci_setvalue(miro, ACI_SET_SOLOMODE, value)) < 0) {
-		snd_printk(KERN_ERR "snd_miro_put_capture() failed: %d\n", error);
+	error = aci_setvalue(miro->aci, ACI_SET_SOLOMODE, value);
+	if (error < 0) {
+		snd_printk(KERN_ERR "snd_miro_put_capture() failed: %d\n",
+			   error);
 		return error;
 		return error;
 	}
 	}
 
 
-	change = (value != miro->aci_solomode);
-	miro->aci_solomode = value;
+	change = (value != miro->aci->aci_solomode);
+	miro->aci->aci_solomode = value;
 	
 	
 	return change;
 	return change;
 }
 }
@@ -295,7 +321,7 @@ static int snd_miro_get_preamp(struct snd_kcontrol *kcontrol,
 	struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
 	struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
 	int value;
 	int value;
 
 
-	if (miro->aci_version <= 176) {
+	if (miro->aci->aci_version <= 176) {
 
 
 		/* 
 		/* 
 		   OSS says it's not readable with versions < 176.
 		   OSS says it's not readable with versions < 176.
@@ -303,12 +329,14 @@ static int snd_miro_get_preamp(struct snd_kcontrol *kcontrol,
 		   which is a PCM12 with aci_version = 176.
 		   which is a PCM12 with aci_version = 176.
 		*/
 		*/
 
 
-		ucontrol->value.integer.value[0] = miro->aci_preamp;
+		ucontrol->value.integer.value[0] = miro->aci->aci_preamp;
 		return 0;
 		return 0;
 	}
 	}
 
 
-	if ((value = aci_getvalue(miro, ACI_GET_PREAMP)) < 0) {
-		snd_printk(KERN_ERR "snd_miro_get_preamp() failed: %d\n", value);
+	value = aci_getvalue(miro->aci, ACI_GET_PREAMP);
+	if (value < 0) {
+		snd_printk(KERN_ERR "snd_miro_get_preamp() failed: %d\n",
+			   value);
 		return value;
 		return value;
 	}
 	}
 	
 	
@@ -325,13 +353,15 @@ static int snd_miro_put_preamp(struct snd_kcontrol *kcontrol,
 
 
 	value = ucontrol->value.integer.value[0];
 	value = ucontrol->value.integer.value[0];
 
 
-	if ((error = aci_setvalue(miro, ACI_SET_PREAMP, value)) < 0) {
-		snd_printk(KERN_ERR "snd_miro_put_preamp() failed: %d\n", error);
+	error = aci_setvalue(miro->aci, ACI_SET_PREAMP, value);
+	if (error < 0) {
+		snd_printk(KERN_ERR "snd_miro_put_preamp() failed: %d\n",
+			   error);
 		return error;
 		return error;
 	}
 	}
 
 
-	change = (value != miro->aci_preamp);
-	miro->aci_preamp = value;
+	change = (value != miro->aci->aci_preamp);
+	miro->aci->aci_preamp = value;
 
 
 	return change;
 	return change;
 }
 }
@@ -342,7 +372,7 @@ static int snd_miro_get_amp(struct snd_kcontrol *kcontrol,
 			    struct snd_ctl_elem_value *ucontrol)
 			    struct snd_ctl_elem_value *ucontrol)
 {
 {
 	struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
 	struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
-	ucontrol->value.integer.value[0] = miro->aci_amp;
+	ucontrol->value.integer.value[0] = miro->aci->aci_amp;
 
 
 	return 0;
 	return 0;
 }
 }
@@ -355,13 +385,14 @@ static int snd_miro_put_amp(struct snd_kcontrol *kcontrol,
 
 
 	value = ucontrol->value.integer.value[0];
 	value = ucontrol->value.integer.value[0];
 
 
-	if ((error = aci_setvalue(miro, ACI_SET_POWERAMP, value)) < 0) {
+	error = aci_setvalue(miro->aci, ACI_SET_POWERAMP, value);
+	if (error < 0) {
 		snd_printk(KERN_ERR "snd_miro_put_amp() to %d failed: %d\n", value, error);
 		snd_printk(KERN_ERR "snd_miro_put_amp() to %d failed: %d\n", value, error);
 		return error;
 		return error;
 	}
 	}
 
 
-	change = (value != miro->aci_amp);
-	miro->aci_amp = value;
+	change = (value != miro->aci->aci_amp);
+	miro->aci->aci_amp = value;
 
 
 	return change;
 	return change;
 }
 }
@@ -410,12 +441,14 @@ static int snd_miro_get_double(struct snd_kcontrol *kcontrol,
 	int right_reg = kcontrol->private_value & 0xff;
 	int right_reg = kcontrol->private_value & 0xff;
 	int left_reg = right_reg + 1;
 	int left_reg = right_reg + 1;
 
 
-	if ((right_val = aci_getvalue(miro, right_reg)) < 0) {
+	right_val = aci_getvalue(miro->aci, right_reg);
+	if (right_val < 0) {
 		snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", right_reg, right_val);
 		snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", right_reg, right_val);
 		return right_val;
 		return right_val;
 	}
 	}
 
 
-	if ((left_val = aci_getvalue(miro, left_reg)) < 0) {
+	left_val = aci_getvalue(miro->aci, left_reg);
+	if (left_val < 0) {
 		snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", left_reg, left_val);
 		snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", left_reg, left_val);
 		return left_val;
 		return left_val;
 	}
 	}
@@ -451,6 +484,7 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
 			       struct snd_ctl_elem_value *ucontrol)
 			       struct snd_ctl_elem_value *ucontrol)
 {
 {
 	struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
 	struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
+	struct snd_miro_aci *aci = miro->aci;
 	int left, right, left_old, right_old;
 	int left, right, left_old, right_old;
 	int setreg_left, setreg_right, getreg_left, getreg_right;
 	int setreg_left, setreg_right, getreg_left, getreg_right;
 	int change, error;
 	int change, error;
@@ -459,21 +493,21 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
 	right = ucontrol->value.integer.value[1];
 	right = ucontrol->value.integer.value[1];
 
 
 	setreg_right = (kcontrol->private_value >> 8) & 0xff;
 	setreg_right = (kcontrol->private_value >> 8) & 0xff;
-	if (setreg_right == ACI_SET_MASTER) {
-		setreg_left = setreg_right + 1;
-	} else {
-		setreg_left = setreg_right + 8;
-	}
+	setreg_left = setreg_right + 8;
+	if (setreg_right == ACI_SET_MASTER)
+		setreg_left -= 7;
 
 
 	getreg_right = kcontrol->private_value & 0xff;
 	getreg_right = kcontrol->private_value & 0xff;
 	getreg_left = getreg_right + 1;
 	getreg_left = getreg_right + 1;
 
 
-	if ((left_old = aci_getvalue(miro, getreg_left)) < 0) {
+	left_old = aci_getvalue(aci, getreg_left);
+	if (left_old < 0) {
 		snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_left, left_old);
 		snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_left, left_old);
 		return left_old;
 		return left_old;
 	}
 	}
 
 
-	if ((right_old = aci_getvalue(miro, getreg_right)) < 0) {
+	right_old = aci_getvalue(aci, getreg_right);
+	if (right_old < 0) {
 		snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_right, right_old);
 		snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_right, right_old);
 		return right_old;
 		return right_old;
 	}
 	}
@@ -492,13 +526,15 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
 			right_old = 0x80 - right_old;
 			right_old = 0x80 - right_old;
 
 
 		if (left >= 0) {
 		if (left >= 0) {
-			if ((error = aci_setvalue(miro, setreg_left, left)) < 0) {
+			error = aci_setvalue(aci, setreg_left, left);
+			if (error < 0) {
 				snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
 				snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
 					   left, error);
 					   left, error);
 				return error;
 				return error;
 			}
 			}
 		} else {
 		} else {
-			if ((error = aci_setvalue(miro, setreg_left, 0x80 - left)) < 0) {
+			error = aci_setvalue(aci, setreg_left, 0x80 - left);
+			if (error < 0) {
 				snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
 				snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
 					   0x80 - left, error);
 					   0x80 - left, error);
 				return error;
 				return error;
@@ -506,13 +542,15 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
 		}
 		}
 
 
 		if (right >= 0) {
 		if (right >= 0) {
-			if ((error = aci_setvalue(miro, setreg_right, right)) < 0) {
+			error = aci_setvalue(aci, setreg_right, right);
+			if (error < 0) {
 				snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
 				snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
 					   right, error);
 					   right, error);
 				return error;
 				return error;
 			}
 			}
 		} else {
 		} else {
-			if ((error = aci_setvalue(miro, setreg_right, 0x80 - right)) < 0) {
+			error = aci_setvalue(aci, setreg_right, 0x80 - right);
+			if (error < 0) {
 				snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
 				snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
 					   0x80 - right, error);
 					   0x80 - right, error);
 				return error;
 				return error;
@@ -530,12 +568,14 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
 		left_old = 0x20 - left_old;
 		left_old = 0x20 - left_old;
 		right_old = 0x20 - right_old;
 		right_old = 0x20 - right_old;
 
 
-		if ((error = aci_setvalue(miro, setreg_left, 0x20 - left)) < 0) {
+		error = aci_setvalue(aci, setreg_left, 0x20 - left);
+		if (error < 0) {
 			snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
 			snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
 				   0x20 - left, error);
 				   0x20 - left, error);
 			return error;
 			return error;
 		}
 		}
-		if ((error = aci_setvalue(miro, setreg_right, 0x20 - right)) < 0) {
+		error = aci_setvalue(aci, setreg_right, 0x20 - right);
+		if (error < 0) {
 			snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
 			snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
 				   0x20 - right, error);
 				   0x20 - right, error);
 			return error;
 			return error;
@@ -633,11 +673,13 @@ static unsigned char aci_init_values[][2] __devinitdata = {
 static int __devinit snd_set_aci_init_values(struct snd_miro *miro)
 static int __devinit snd_set_aci_init_values(struct snd_miro *miro)
 {
 {
 	int idx, error;
 	int idx, error;
+	struct snd_miro_aci *aci = miro->aci;
 
 
 	/* enable WSS on PCM1 */
 	/* enable WSS on PCM1 */
 
 
-	if ((miro->aci_product == 'A') && wss) {
-		if ((error = aci_setvalue(miro, ACI_SET_WSS, wss)) < 0) {
+	if ((aci->aci_product == 'A') && wss) {
+		error = aci_setvalue(aci, ACI_SET_WSS, wss);
+		if (error < 0) {
 			snd_printk(KERN_ERR "enabling WSS mode failed\n");
 			snd_printk(KERN_ERR "enabling WSS mode failed\n");
 			return error;
 			return error;
 		}
 		}
@@ -646,7 +688,8 @@ static int __devinit snd_set_aci_init_values(struct snd_miro *miro)
 	/* enable IDE port */
 	/* enable IDE port */
 
 
 	if (ide) {
 	if (ide) {
-		if ((error = aci_setvalue(miro, ACI_SET_IDE, ide)) < 0) {
+		error = aci_setvalue(aci, ACI_SET_IDE, ide);
+		if (error < 0) {
 			snd_printk(KERN_ERR "enabling IDE port failed\n");
 			snd_printk(KERN_ERR "enabling IDE port failed\n");
 			return error;
 			return error;
 		}
 		}
@@ -654,32 +697,31 @@ static int __devinit snd_set_aci_init_values(struct snd_miro *miro)
 
 
 	/* set common aci values */
 	/* set common aci values */
 
 
-	for (idx = 0; idx < ARRAY_SIZE(aci_init_values); idx++)
-                if ((error = aci_setvalue(miro, aci_init_values[idx][0], 
-					  aci_init_values[idx][1])) < 0) {
+	for (idx = 0; idx < ARRAY_SIZE(aci_init_values); idx++) {
+		error = aci_setvalue(aci, aci_init_values[idx][0],
+				     aci_init_values[idx][1]);
+		if (error < 0) {
 			snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n", 
 			snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n", 
 				   aci_init_values[idx][0], error);
 				   aci_init_values[idx][0], error);
                         return error;
                         return error;
                 }
                 }
-
-	miro->aci_amp = 0;
-	miro->aci_preamp = 0;
-	miro->aci_solomode = 1;
+	}
+	aci->aci_amp = 0;
+	aci->aci_preamp = 0;
+	aci->aci_solomode = 1;
 
 
 	return 0;
 	return 0;
 }
 }
 
 
-static int __devinit snd_miro_mixer(struct snd_miro *miro)
+static int __devinit snd_miro_mixer(struct snd_card *card,
+				    struct snd_miro *miro)
 {
 {
-	struct snd_card *card;
 	unsigned int idx;
 	unsigned int idx;
 	int err;
 	int err;
 
 
-	if (snd_BUG_ON(!miro || !miro->card))
+	if (snd_BUG_ON(!miro || !card))
 		return -EINVAL;
 		return -EINVAL;
 
 
-	card = miro->card;
-
 	switch (miro->hardware) {
 	switch (miro->hardware) {
 	case OPTi9XX_HW_82C924:
 	case OPTi9XX_HW_82C924:
 		strcpy(card->mixername, "ACI & OPTi924");
 		strcpy(card->mixername, "ACI & OPTi924");
@@ -697,7 +739,8 @@ static int __devinit snd_miro_mixer(struct snd_miro *miro)
 			return err;
 			return err;
 	}
 	}
 
 
-	if ((miro->aci_product == 'A') || (miro->aci_product == 'B')) {
+	if ((miro->aci->aci_product == 'A') ||
+	    (miro->aci->aci_product == 'B')) {
 		/* PCM1/PCM12 with power-amp and Line 2 */
 		/* PCM1/PCM12 with power-amp and Line 2 */
 		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_line_control[0], miro))) < 0)
 		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_line_control[0], miro))) < 0)
 			return err;
 			return err;
@@ -705,16 +748,17 @@ static int __devinit snd_miro_mixer(struct snd_miro *miro)
 			return err;
 			return err;
 	}
 	}
 
 
-	if ((miro->aci_product == 'B') || (miro->aci_product == 'C')) {
+	if ((miro->aci->aci_product == 'B') ||
+	    (miro->aci->aci_product == 'C')) {
 		/* PCM12/PCM20 with mic-preamp */
 		/* PCM12/PCM20 with mic-preamp */
 		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_preamp_control[0], miro))) < 0)
 		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_preamp_control[0], miro))) < 0)
 			return err;
 			return err;
-		if (miro->aci_version >= 176)
+		if (miro->aci->aci_version >= 176)
 			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_capture_control[0], miro))) < 0)
 			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_capture_control[0], miro))) < 0)
 				return err;
 				return err;
 	}
 	}
 
 
-	if (miro->aci_product == 'C') {
+	if (miro->aci->aci_product == 'C') {
 		/* PCM20 with radio and 7 band equalizer */
 		/* PCM20 with radio and 7 band equalizer */
 		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_radio_control[0], miro))) < 0)
 		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_radio_control[0], miro))) < 0)
 			return err;
 			return err;
@@ -757,21 +801,26 @@ static int __devinit snd_miro_init(struct snd_miro *chip,
 	chip->irq = -1;
 	chip->irq = -1;
 	chip->dma1 = -1;
 	chip->dma1 = -1;
 	chip->dma2 = -1;
 	chip->dma2 = -1;
-	chip->fm_port = -1;
 	chip->mpu_port = -1;
 	chip->mpu_port = -1;
 	chip->mpu_irq = -1;
 	chip->mpu_irq = -1;
 
 
+	chip->pwd_reg = 3;
+
+#ifdef CONFIG_PNP
+	if (isapnp && chip->mc_base)
+		/* PnP resource gives the least 10 bits */
+		chip->mc_base |= 0xc00;
+	else
+#endif
+		chip->mc_base = 0xf8c;
+
 	switch (hardware) {
 	switch (hardware) {
 	case OPTi9XX_HW_82C929:
 	case OPTi9XX_HW_82C929:
-		chip->mc_base = 0xf8c;
 		chip->password = 0xe3;
 		chip->password = 0xe3;
-		chip->pwd_reg = 3;
 		break;
 		break;
 
 
 	case OPTi9XX_HW_82C924:
 	case OPTi9XX_HW_82C924:
-		chip->mc_base = 0xf8c;
 		chip->password = 0xe5;
 		chip->password = 0xe5;
-		chip->pwd_reg = 3;
 		break;
 		break;
 
 
 	default:
 	default:
@@ -853,14 +902,15 @@ static void snd_miro_proc_read(struct snd_info_entry * entry,
 			       struct snd_info_buffer *buffer)
 			       struct snd_info_buffer *buffer)
 {
 {
 	struct snd_miro *miro = (struct snd_miro *) entry->private_data;
 	struct snd_miro *miro = (struct snd_miro *) entry->private_data;
+	struct snd_miro_aci *aci = miro->aci;
 	char* model = "unknown";
 	char* model = "unknown";
 
 
 	/* miroSOUND PCM1 pro, early PCM12 */
 	/* miroSOUND PCM1 pro, early PCM12 */
 
 
 	if ((miro->hardware == OPTi9XX_HW_82C929) &&
 	if ((miro->hardware == OPTi9XX_HW_82C929) &&
-	    (miro->aci_vendor == 'm') && 
-	    (miro->aci_product == 'A')) {
-		switch(miro->aci_version) {
+	    (aci->aci_vendor == 'm') &&
+	    (aci->aci_product == 'A')) {
+		switch (aci->aci_version) {
 		case 3:
 		case 3:
 			model = "miroSOUND PCM1 pro";
 			model = "miroSOUND PCM1 pro";
 			break;
 			break;
@@ -873,9 +923,9 @@ static void snd_miro_proc_read(struct snd_info_entry * entry,
 	/* miroSOUND PCM12, PCM12 (Rev. E), PCM12 pnp */
 	/* miroSOUND PCM12, PCM12 (Rev. E), PCM12 pnp */
 
 
 	if ((miro->hardware == OPTi9XX_HW_82C924) &&
 	if ((miro->hardware == OPTi9XX_HW_82C924) &&
-	    (miro->aci_vendor == 'm') && 
-	    (miro->aci_product == 'B')) {
-		switch(miro->aci_version) {
+	    (aci->aci_vendor == 'm') &&
+	    (aci->aci_product == 'B')) {
+		switch (aci->aci_version) {
 		case 4:
 		case 4:
 			model = "miroSOUND PCM12";
 			model = "miroSOUND PCM12";
 			break;
 			break;
@@ -891,9 +941,9 @@ static void snd_miro_proc_read(struct snd_info_entry * entry,
 	/* miroSOUND PCM20 radio */
 	/* miroSOUND PCM20 radio */
 
 
 	if ((miro->hardware == OPTi9XX_HW_82C924) &&
 	if ((miro->hardware == OPTi9XX_HW_82C924) &&
-	    (miro->aci_vendor == 'm') && 
-	    (miro->aci_product == 'C')) {
-		switch(miro->aci_version) {
+	    (aci->aci_vendor == 'm') &&
+	    (aci->aci_product == 'C')) {
+		switch (aci->aci_version) {
 		case 7:
 		case 7:
 			model = "miroSOUND PCM20 radio (Rev. E)";
 			model = "miroSOUND PCM20 radio (Rev. E)";
 			break;
 			break;
@@ -917,17 +967,17 @@ static void snd_miro_proc_read(struct snd_info_entry * entry,
 
 
 	snd_iprintf(buffer, "ACI information:\n");
 	snd_iprintf(buffer, "ACI information:\n");
 	snd_iprintf(buffer, "  vendor  : ");
 	snd_iprintf(buffer, "  vendor  : ");
-	switch(miro->aci_vendor) {
+	switch (aci->aci_vendor) {
 	case 'm':
 	case 'm':
 		snd_iprintf(buffer, "Miro\n");
 		snd_iprintf(buffer, "Miro\n");
 		break;
 		break;
 	default:
 	default:
-		snd_iprintf(buffer, "unknown (0x%x)\n", miro->aci_vendor);
+		snd_iprintf(buffer, "unknown (0x%x)\n", aci->aci_vendor);
 		break;
 		break;
 	}
 	}
 
 
 	snd_iprintf(buffer, "  product : ");
 	snd_iprintf(buffer, "  product : ");
-	switch(miro->aci_product) {
+	switch (aci->aci_product) {
 	case 'A':
 	case 'A':
 		snd_iprintf(buffer, "miroSOUND PCM1 pro / (early) PCM12\n");
 		snd_iprintf(buffer, "miroSOUND PCM1 pro / (early) PCM12\n");
 		break;
 		break;
@@ -938,26 +988,27 @@ static void snd_miro_proc_read(struct snd_info_entry * entry,
 		snd_iprintf(buffer, "miroSOUND PCM20 radio\n");
 		snd_iprintf(buffer, "miroSOUND PCM20 radio\n");
 		break;
 		break;
 	default:
 	default:
-		snd_iprintf(buffer, "unknown (0x%x)\n", miro->aci_product);
+		snd_iprintf(buffer, "unknown (0x%x)\n", aci->aci_product);
 		break;
 		break;
 	}
 	}
 
 
 	snd_iprintf(buffer, "  firmware: %d (0x%x)\n",
 	snd_iprintf(buffer, "  firmware: %d (0x%x)\n",
-		    miro->aci_version, miro->aci_version);
+		    aci->aci_version, aci->aci_version);
 	snd_iprintf(buffer, "  port    : 0x%lx-0x%lx\n", 
 	snd_iprintf(buffer, "  port    : 0x%lx-0x%lx\n", 
-		    miro->aci_port, miro->aci_port+2);
+		    aci->aci_port, aci->aci_port+2);
 	snd_iprintf(buffer, "  wss     : 0x%x\n", wss);
 	snd_iprintf(buffer, "  wss     : 0x%x\n", wss);
 	snd_iprintf(buffer, "  ide     : 0x%x\n", ide);
 	snd_iprintf(buffer, "  ide     : 0x%x\n", ide);
-	snd_iprintf(buffer, "  solomode: 0x%x\n", miro->aci_solomode);
-	snd_iprintf(buffer, "  amp     : 0x%x\n", miro->aci_amp);
-	snd_iprintf(buffer, "  preamp  : 0x%x\n", miro->aci_preamp);
+	snd_iprintf(buffer, "  solomode: 0x%x\n", aci->aci_solomode);
+	snd_iprintf(buffer, "  amp     : 0x%x\n", aci->aci_amp);
+	snd_iprintf(buffer, "  preamp  : 0x%x\n", aci->aci_preamp);
 }
 }
 
 
-static void __devinit snd_miro_proc_init(struct snd_miro * miro)
+static void __devinit snd_miro_proc_init(struct snd_card *card,
+					 struct snd_miro *miro)
 {
 {
 	struct snd_info_entry *entry;
 	struct snd_info_entry *entry;
 
 
-	if (! snd_card_proc_new(miro->card, "miro", &entry))
+	if (!snd_card_proc_new(card, "miro", &entry))
 		snd_info_set_text_ops(entry, miro, snd_miro_proc_read);
 		snd_info_set_text_ops(entry, miro, snd_miro_proc_read);
 }
 }
 
 
@@ -974,37 +1025,40 @@ static int __devinit snd_miro_configure(struct snd_miro *chip)
 	unsigned char mpu_irq_bits;
 	unsigned char mpu_irq_bits;
 	unsigned long flags;
 	unsigned long flags;
 
 
+	snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
+	snd_miro_write_mask(chip, OPTi9XX_MC_REG(2), 0x20, 0x20); /* OPL4 */
+	snd_miro_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
+
 	switch (chip->hardware) {
 	switch (chip->hardware) {
 	case OPTi9XX_HW_82C924:
 	case OPTi9XX_HW_82C924:
 		snd_miro_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02);
 		snd_miro_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02);
-		snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
-		snd_miro_write_mask(chip, OPTi9XX_MC_REG(2), 0x20, 0x20); /* OPL4 */
 		snd_miro_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff);
 		snd_miro_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff);
-		snd_miro_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
 		break;
 		break;
 	case OPTi9XX_HW_82C929:
 	case OPTi9XX_HW_82C929:
 		/* untested init commands for OPTi929 */
 		/* untested init commands for OPTi929 */
-		snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
-		snd_miro_write_mask(chip, OPTi9XX_MC_REG(2), 0x20, 0x20); /* OPL4 */
 		snd_miro_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c);
 		snd_miro_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c);
-		snd_miro_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
 		break;
 		break;
 	default:
 	default:
 		snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
 		snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
-	switch (chip->wss_base) {
-	case 0x530:
+	/* PnP resource says it decodes only 10 bits of address */
+	switch (chip->wss_base & 0x3ff) {
+	case 0x130:
+		chip->wss_base = 0x530;
 		wss_base_bits = 0x00;
 		wss_base_bits = 0x00;
 		break;
 		break;
-	case 0x604:
+	case 0x204:
+		chip->wss_base = 0x604;
 		wss_base_bits = 0x03;
 		wss_base_bits = 0x03;
 		break;
 		break;
-	case 0xe80:
+	case 0x280:
+		chip->wss_base = 0xe80;
 		wss_base_bits = 0x01;
 		wss_base_bits = 0x01;
 		break;
 		break;
-	case 0xf40:
+	case 0x340:
+		chip->wss_base = 0xf40;
 		wss_base_bits = 0x02;
 		wss_base_bits = 0x02;
 		break;
 		break;
 	default:
 	default:
@@ -1122,75 +1176,92 @@ static int __devinit snd_miro_configure(struct snd_miro *chip)
 	return 0;
 	return 0;
 }
 }
 
 
+static int __devinit snd_miro_opti_check(struct snd_miro *chip)
+{
+	unsigned char value;
+
+	chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size,
+					   "OPTi9xx MC");
+	if (chip->res_mc_base == NULL)
+		return -ENOMEM;
+
+	value = snd_miro_read(chip, OPTi9XX_MC_REG(1));
+	if (value != 0xff && value != inb(chip->mc_base + OPTi9XX_MC_REG(1)))
+		if (value == snd_miro_read(chip, OPTi9XX_MC_REG(1)))
+			return 0;
+
+	release_and_free_resource(chip->res_mc_base);
+	chip->res_mc_base = NULL;
+
+	return -ENODEV;
+}
+
 static int __devinit snd_card_miro_detect(struct snd_card *card,
 static int __devinit snd_card_miro_detect(struct snd_card *card,
 					  struct snd_miro *chip)
 					  struct snd_miro *chip)
 {
 {
 	int i, err;
 	int i, err;
-	unsigned char value;
 
 
 	for (i = OPTi9XX_HW_82C929; i <= OPTi9XX_HW_82C924; i++) {
 	for (i = OPTi9XX_HW_82C929; i <= OPTi9XX_HW_82C924; i++) {
 
 
 		if ((err = snd_miro_init(chip, i)) < 0)
 		if ((err = snd_miro_init(chip, i)) < 0)
 			return err;
 			return err;
 
 
-		if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL)
-			continue;
-
-		value = snd_miro_read(chip, OPTi9XX_MC_REG(1));
-		if ((value != 0xff) && (value != inb(chip->mc_base + 1)))
-			if (value == snd_miro_read(chip, OPTi9XX_MC_REG(1)))
-				return 1;
-
-		release_and_free_resource(chip->res_mc_base);
-		chip->res_mc_base = NULL;
-
+		err = snd_miro_opti_check(chip);
+		if (err == 0)
+			return 1;
 	}
 	}
 
 
 	return -ENODEV;
 	return -ENODEV;
 }
 }
 
 
 static int __devinit snd_card_miro_aci_detect(struct snd_card *card,
 static int __devinit snd_card_miro_aci_detect(struct snd_card *card,
-					      struct snd_miro * miro)
+					      struct snd_miro *miro)
 {
 {
 	unsigned char regval;
 	unsigned char regval;
 	int i;
 	int i;
+	struct snd_miro_aci *aci = &aci_device;
+
+	miro->aci = aci;
 
 
-	mutex_init(&miro->aci_mutex);
+	mutex_init(&aci->aci_mutex);
 
 
 	/* get ACI port from OPTi9xx MC 4 */
 	/* get ACI port from OPTi9xx MC 4 */
 
 
-	miro->mc_base = 0xf8c;
 	regval=inb(miro->mc_base + 4);
 	regval=inb(miro->mc_base + 4);
-	miro->aci_port = (regval & 0x10) ? 0x344: 0x354;
+	aci->aci_port = (regval & 0x10) ? 0x344 : 0x354;
 
 
-	if ((miro->res_aci_port = request_region(miro->aci_port, 3, "miro aci")) == NULL) {
+	miro->res_aci_port = request_region(aci->aci_port, 3, "miro aci");
+	if (miro->res_aci_port == NULL) {
 		snd_printk(KERN_ERR "aci i/o area 0x%lx-0x%lx already used.\n", 
 		snd_printk(KERN_ERR "aci i/o area 0x%lx-0x%lx already used.\n", 
-			   miro->aci_port, miro->aci_port+2);
+			   aci->aci_port, aci->aci_port+2);
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 
 
         /* force ACI into a known state */
         /* force ACI into a known state */
 	for (i = 0; i < 3; i++)
 	for (i = 0; i < 3; i++)
-		if (aci_cmd(miro, ACI_ERROR_OP, -1, -1) < 0) {
+		if (snd_aci_cmd(aci, ACI_ERROR_OP, -1, -1) < 0) {
 			snd_printk(KERN_ERR "can't force aci into known state.\n");
 			snd_printk(KERN_ERR "can't force aci into known state.\n");
 			return -ENXIO;
 			return -ENXIO;
 		}
 		}
 
 
-	if ((miro->aci_vendor=aci_cmd(miro, ACI_READ_IDCODE, -1, -1)) < 0 ||
-	    (miro->aci_product=aci_cmd(miro, ACI_READ_IDCODE, -1, -1)) < 0) {
-		snd_printk(KERN_ERR "can't read aci id on 0x%lx.\n", miro->aci_port);
+	aci->aci_vendor = snd_aci_cmd(aci, ACI_READ_IDCODE, -1, -1);
+	aci->aci_product = snd_aci_cmd(aci, ACI_READ_IDCODE, -1, -1);
+	if (aci->aci_vendor < 0 || aci->aci_product < 0) {
+		snd_printk(KERN_ERR "can't read aci id on 0x%lx.\n",
+			   aci->aci_port);
 		return -ENXIO;
 		return -ENXIO;
 	}
 	}
 
 
-	if ((miro->aci_version=aci_cmd(miro, ACI_READ_VERSION, -1, -1)) < 0) {
+	aci->aci_version = snd_aci_cmd(aci, ACI_READ_VERSION, -1, -1);
+	if (aci->aci_version < 0) {
 		snd_printk(KERN_ERR "can't read aci version on 0x%lx.\n", 
 		snd_printk(KERN_ERR "can't read aci version on 0x%lx.\n", 
-			   miro->aci_port);
+			   aci->aci_port);
 		return -ENXIO;
 		return -ENXIO;
 	}
 	}
 
 
-	if (aci_cmd(miro, ACI_INIT, -1, -1) < 0 ||
-	    aci_cmd(miro, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0 ||
-	    aci_cmd(miro, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0) {
+	if (snd_aci_cmd(aci, ACI_INIT, -1, -1) < 0 ||
+	    snd_aci_cmd(aci, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0 ||
+	    snd_aci_cmd(aci, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0) {
 		snd_printk(KERN_ERR "can't initialize aci.\n"); 
 		snd_printk(KERN_ERR "can't initialize aci.\n"); 
 		return -ENXIO;
 		return -ENXIO;
 	}
 	}
@@ -1201,157 +1272,80 @@ static int __devinit snd_card_miro_aci_detect(struct snd_card *card,
 static void snd_card_miro_free(struct snd_card *card)
 static void snd_card_miro_free(struct snd_card *card)
 {
 {
 	struct snd_miro *miro = card->private_data;
 	struct snd_miro *miro = card->private_data;
-        
+
 	release_and_free_resource(miro->res_aci_port);
 	release_and_free_resource(miro->res_aci_port);
+	if (miro->aci)
+		miro->aci->aci_port = 0;
 	release_and_free_resource(miro->res_mc_base);
 	release_and_free_resource(miro->res_mc_base);
 }
 }
 
 
-static int __devinit snd_miro_match(struct device *devptr, unsigned int n)
-{
-	return 1;
-}
-
-static int __devinit snd_miro_probe(struct device *devptr, unsigned int n)
+static int __devinit snd_miro_probe(struct snd_card *card)
 {
 {
-	static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
-	static long possible_mpu_ports[] = {0x330, 0x300, 0x310, 0x320, -1};
-	static int possible_irqs[] = {11, 9, 10, 7, -1};
-	static int possible_mpu_irqs[] = {10, 5, 9, 7, -1};
-	static int possible_dma1s[] = {3, 1, 0, -1};
-	static int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}};
-
 	int error;
 	int error;
-	struct snd_miro *miro;
+	struct snd_miro *miro = card->private_data;
 	struct snd_wss *codec;
 	struct snd_wss *codec;
 	struct snd_timer *timer;
 	struct snd_timer *timer;
-	struct snd_card *card;
 	struct snd_pcm *pcm;
 	struct snd_pcm *pcm;
 	struct snd_rawmidi *rmidi;
 	struct snd_rawmidi *rmidi;
 
 
-	error = snd_card_create(index, id, THIS_MODULE,
-				sizeof(struct snd_miro), &card);
-	if (error < 0)
-		return error;
-
-	card->private_free = snd_card_miro_free;
-	miro = card->private_data;
-	miro->card = card;
-
-	if ((error = snd_card_miro_aci_detect(card, miro)) < 0) {
-		snd_card_free(card);
-		snd_printk(KERN_ERR "unable to detect aci chip\n");
-		return -ENODEV;
+	if (!miro->res_mc_base) {
+		miro->res_mc_base = request_region(miro->mc_base,
+						miro->mc_base_size,
+						"miro (OPTi9xx MC)");
+		if (miro->res_mc_base == NULL) {
+			snd_printk(KERN_ERR "request for OPTI9xx MC failed\n");
+			return -ENOMEM;
+		}
 	}
 	}
 
 
-	/* init proc interface */
-	snd_miro_proc_init(miro);
-
-	if ((error = snd_card_miro_detect(card, miro)) < 0) {
+	error = snd_card_miro_aci_detect(card, miro);
+	if (error < 0) {
 		snd_card_free(card);
 		snd_card_free(card);
-		snd_printk(KERN_ERR "unable to detect OPTi9xx chip\n");
+		snd_printk(KERN_ERR "unable to detect aci chip\n");
 		return -ENODEV;
 		return -ENODEV;
 	}
 	}
 
 
-	if (! miro->res_mc_base &&
-	    (miro->res_mc_base = request_region(miro->mc_base, miro->mc_base_size,
-						"miro (OPTi9xx MC)")) == NULL) {
-		snd_card_free(card);
-		snd_printk(KERN_ERR "request for OPTI9xx MC failed\n");
-		return -ENOMEM;
-	}
-
 	miro->wss_base = port;
 	miro->wss_base = port;
-	miro->fm_port = fm_port;
 	miro->mpu_port = mpu_port;
 	miro->mpu_port = mpu_port;
 	miro->irq = irq;
 	miro->irq = irq;
 	miro->mpu_irq = mpu_irq;
 	miro->mpu_irq = mpu_irq;
 	miro->dma1 = dma1;
 	miro->dma1 = dma1;
 	miro->dma2 = dma2;
 	miro->dma2 = dma2;
 
 
-	if (miro->wss_base == SNDRV_AUTO_PORT) {
-		if ((miro->wss_base = snd_legacy_find_free_ioport(possible_ports, 4)) < 0) {
-			snd_card_free(card);
-			snd_printk(KERN_ERR "unable to find a free WSS port\n");
-			return -EBUSY;
-		}
-	}
-
-	if (miro->mpu_port == SNDRV_AUTO_PORT) {
-		if ((miro->mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2)) < 0) {
-			snd_card_free(card);
-			snd_printk(KERN_ERR "unable to find a free MPU401 port\n");
-			return -EBUSY;
-		}
-	}
-	if (miro->irq == SNDRV_AUTO_IRQ) {
-		if ((miro->irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
-			snd_card_free(card);
-			snd_printk(KERN_ERR "unable to find a free IRQ\n");
-			return -EBUSY;
-		}
-	}
-	if (miro->mpu_irq == SNDRV_AUTO_IRQ) {
-		if ((miro->mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs)) < 0) {
-			snd_card_free(card);
-			snd_printk(KERN_ERR "unable to find a free MPU401 IRQ\n");
-			return -EBUSY;
-		}
-	}
-	if (miro->dma1 == SNDRV_AUTO_DMA) {
-		if ((miro->dma1 = snd_legacy_find_free_dma(possible_dma1s)) < 0) {
-			snd_card_free(card);
-			snd_printk(KERN_ERR "unable to find a free DMA1\n");
-			return -EBUSY;
-		}
-	}
-	if (miro->dma2 == SNDRV_AUTO_DMA) {
-		if ((miro->dma2 = snd_legacy_find_free_dma(possible_dma2s[miro->dma1 % 4])) < 0) {
-			snd_card_free(card);
-			snd_printk(KERN_ERR "unable to find a free DMA2\n");
-			return -EBUSY;
-		}
-	}
+	/* init proc interface */
+	snd_miro_proc_init(card, miro);
 
 
 	error = snd_miro_configure(miro);
 	error = snd_miro_configure(miro);
-	if (error) {
-		snd_card_free(card);
+	if (error)
 		return error;
 		return error;
-	}
 
 
 	error = snd_wss_create(card, miro->wss_base + 4, -1,
 	error = snd_wss_create(card, miro->wss_base + 4, -1,
-				miro->irq, miro->dma1, miro->dma2,
-				WSS_HW_AD1845, 0, &codec);
-	if (error < 0) {
-		snd_card_free(card);
+			       miro->irq, miro->dma1, miro->dma2,
+			       WSS_HW_DETECT, 0, &codec);
+	if (error < 0)
 		return error;
 		return error;
-	}
 
 
 	error = snd_wss_pcm(codec, 0, &pcm);
 	error = snd_wss_pcm(codec, 0, &pcm);
-	if (error < 0)  {
-		snd_card_free(card);
+	if (error < 0)
 		return error;
 		return error;
-	}
+
 	error = snd_wss_mixer(codec);
 	error = snd_wss_mixer(codec);
-	if (error < 0) {
-		snd_card_free(card);
+	if (error < 0)
 		return error;
 		return error;
-	}
+
 	error = snd_wss_timer(codec, 0, &timer);
 	error = snd_wss_timer(codec, 0, &timer);
-	if (error < 0) {
-		snd_card_free(card);
+	if (error < 0)
 		return error;
 		return error;
-	}
 
 
 	miro->pcm = pcm;
 	miro->pcm = pcm;
 
 
-	if ((error = snd_miro_mixer(miro)) < 0) {
-		snd_card_free(card);
+	error = snd_miro_mixer(card, miro);
+	if (error < 0)
 		return error;
 		return error;
-	}
 
 
-	if (miro->aci_vendor == 'm') {
+	if (miro->aci->aci_vendor == 'm') {
 		/* It looks like a miro sound card. */
 		/* It looks like a miro sound card. */
-		switch (miro->aci_product) {
+		switch (miro->aci->aci_product) {
 		case 'A':
 		case 'A':
 			sprintf(card->shortname, 
 			sprintf(card->shortname, 
 				"miroSOUND PCM1 pro / PCM12");
 				"miroSOUND PCM1 pro / PCM12");
@@ -1380,30 +1374,131 @@ static int __devinit snd_miro_probe(struct device *devptr, unsigned int n)
 		card->shortname, miro->name, pcm->name, miro->wss_base + 4,
 		card->shortname, miro->name, pcm->name, miro->wss_base + 4,
 		miro->irq, miro->dma1, miro->dma2);
 		miro->irq, miro->dma1, miro->dma2);
 
 
-	if (miro->mpu_port <= 0 || miro->mpu_port == SNDRV_AUTO_PORT)
+	if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT)
 		rmidi = NULL;
 		rmidi = NULL;
-	else
-		if ((error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
-				miro->mpu_port, 0, miro->mpu_irq, IRQF_DISABLED,
-				&rmidi)))
-			snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n", miro->mpu_port);
+	else {
+		error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+				mpu_port, 0, miro->mpu_irq, IRQF_DISABLED,
+				&rmidi);
+		if (error < 0)
+			snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",
+				   mpu_port);
+	}
 
 
-	if (miro->fm_port > 0 && miro->fm_port != SNDRV_AUTO_PORT) {
+	if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) {
 		struct snd_opl3 *opl3 = NULL;
 		struct snd_opl3 *opl3 = NULL;
 		struct snd_opl4 *opl4;
 		struct snd_opl4 *opl4;
-		if (snd_opl4_create(card, miro->fm_port, miro->fm_port - 8, 
+
+		if (snd_opl4_create(card, fm_port, fm_port - 8,
 				    2, &opl3, &opl4) < 0)
 				    2, &opl3, &opl4) < 0)
-			snd_printk(KERN_WARNING "no OPL4 device at 0x%lx\n", miro->fm_port);
+			snd_printk(KERN_WARNING "no OPL4 device at 0x%lx\n",
+				   fm_port);
 	}
 	}
 
 
-	if ((error = snd_set_aci_init_values(miro)) < 0) {
-		snd_card_free(card);
+	error = snd_set_aci_init_values(miro);
+	if (error < 0)
                 return error;
                 return error;
+
+	return snd_card_register(card);
+}
+
+static int __devinit snd_miro_isa_match(struct device *devptr, unsigned int n)
+{
+#ifdef CONFIG_PNP
+	if (snd_miro_pnp_is_probed)
+		return 0;
+	if (isapnp)
+		return 0;
+#endif
+	return 1;
+}
+
+static int __devinit snd_miro_isa_probe(struct device *devptr, unsigned int n)
+{
+	static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
+	static long possible_mpu_ports[] = {0x330, 0x300, 0x310, 0x320, -1};
+	static int possible_irqs[] = {11, 9, 10, 7, -1};
+	static int possible_mpu_irqs[] = {10, 5, 9, 7, -1};
+	static int possible_dma1s[] = {3, 1, 0, -1};
+	static int possible_dma2s[][2] = { {1, -1}, {0, -1}, {-1, -1},
+					   {0, -1} };
+
+	int error;
+	struct snd_miro *miro;
+	struct snd_card *card;
+
+	error = snd_card_create(index, id, THIS_MODULE,
+				sizeof(struct snd_miro), &card);
+	if (error < 0)
+		return error;
+
+	card->private_free = snd_card_miro_free;
+	miro = card->private_data;
+
+	error = snd_card_miro_detect(card, miro);
+	if (error < 0) {
+		snd_card_free(card);
+		snd_printk(KERN_ERR "unable to detect OPTi9xx chip\n");
+		return -ENODEV;
+	}
+
+	if (port == SNDRV_AUTO_PORT) {
+		port = snd_legacy_find_free_ioport(possible_ports, 4);
+		if (port < 0) {
+			snd_card_free(card);
+			snd_printk(KERN_ERR "unable to find a free WSS port\n");
+			return -EBUSY;
+		}
+	}
+
+	if (mpu_port == SNDRV_AUTO_PORT) {
+		mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2);
+		if (mpu_port < 0) {
+			snd_card_free(card);
+			snd_printk(KERN_ERR
+				   "unable to find a free MPU401 port\n");
+			return -EBUSY;
+		}
+	}
+
+	if (irq == SNDRV_AUTO_IRQ) {
+		irq = snd_legacy_find_free_irq(possible_irqs);
+		if (irq < 0) {
+			snd_card_free(card);
+			snd_printk(KERN_ERR "unable to find a free IRQ\n");
+			return -EBUSY;
+		}
+	}
+	if (mpu_irq == SNDRV_AUTO_IRQ) {
+		mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs);
+		if (mpu_irq < 0) {
+			snd_card_free(card);
+			snd_printk(KERN_ERR
+				   "unable to find a free MPU401 IRQ\n");
+			return -EBUSY;
+		}
+	}
+	if (dma1 == SNDRV_AUTO_DMA) {
+		dma1 = snd_legacy_find_free_dma(possible_dma1s);
+		if (dma1 < 0) {
+			snd_card_free(card);
+			snd_printk(KERN_ERR "unable to find a free DMA1\n");
+			return -EBUSY;
+		}
+	}
+	if (dma2 == SNDRV_AUTO_DMA) {
+		dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4]);
+		if (dma2 < 0) {
+			snd_card_free(card);
+			snd_printk(KERN_ERR "unable to find a free DMA2\n");
+			return -EBUSY;
+		}
 	}
 	}
 
 
 	snd_card_set_dev(card, devptr);
 	snd_card_set_dev(card, devptr);
 
 
-	if ((error = snd_card_register(card))) {
+	error = snd_miro_probe(card);
+	if (error < 0) {
 		snd_card_free(card);
 		snd_card_free(card);
 		return error;
 		return error;
 	}
 	}
@@ -1412,7 +1507,8 @@ static int __devinit snd_miro_probe(struct device *devptr, unsigned int n)
 	return 0;
 	return 0;
 }
 }
 
 
-static int __devexit snd_miro_remove(struct device *devptr, unsigned int dev)
+static int __devexit snd_miro_isa_remove(struct device *devptr,
+					 unsigned int dev)
 {
 {
 	snd_card_free(dev_get_drvdata(devptr));
 	snd_card_free(dev_get_drvdata(devptr));
 	dev_set_drvdata(devptr, NULL);
 	dev_set_drvdata(devptr, NULL);
@@ -1422,23 +1518,164 @@ static int __devexit snd_miro_remove(struct device *devptr, unsigned int dev)
 #define DEV_NAME "miro"
 #define DEV_NAME "miro"
 
 
 static struct isa_driver snd_miro_driver = {
 static struct isa_driver snd_miro_driver = {
-	.match		= snd_miro_match,
-	.probe		= snd_miro_probe,
-	.remove		= __devexit_p(snd_miro_remove),
+	.match		= snd_miro_isa_match,
+	.probe		= snd_miro_isa_probe,
+	.remove		= __devexit_p(snd_miro_isa_remove),
 	/* FIXME: suspend/resume */
 	/* FIXME: suspend/resume */
 	.driver		= {
 	.driver		= {
 		.name	= DEV_NAME
 		.name	= DEV_NAME
 	},
 	},
 };
 };
 
 
+#ifdef CONFIG_PNP
+
+static int __devinit snd_card_miro_pnp(struct snd_miro *chip,
+					struct pnp_card_link *card,
+					const struct pnp_card_device_id *pid)
+{
+	struct pnp_dev *pdev;
+	int err;
+	struct pnp_dev *devmpu;
+	struct pnp_dev *devmc;
+
+	pdev = pnp_request_card_device(card, pid->devs[0].id, NULL);
+	if (pdev == NULL)
+		return -EBUSY;
+
+	devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL);
+	if (devmpu == NULL)
+		return -EBUSY;
+
+	devmc = pnp_request_card_device(card, pid->devs[2].id, NULL);
+	if (devmc == NULL)
+		return -EBUSY;
+
+	err = pnp_activate_dev(pdev);
+	if (err < 0) {
+		snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err);
+		return err;
+	}
+
+	err = pnp_activate_dev(devmc);
+	if (err < 0) {
+		snd_printk(KERN_ERR "OPL syntg pnp configure failure: %d\n",
+				    err);
+		return err;
+	}
+
+	port = pnp_port_start(pdev, 1);
+	fm_port = pnp_port_start(pdev, 2) + 8;
+
+	/*
+	 * The MC(0) is never accessed and the miroSOUND PCM20 card does not
+	 * include it in the PnP resource range. OPTI93x include it.
+	 */
+	chip->mc_base = pnp_port_start(devmc, 0) - 1;
+	chip->mc_base_size = pnp_port_len(devmc, 0) + 1;
+
+	irq = pnp_irq(pdev, 0);
+	dma1 = pnp_dma(pdev, 0);
+	dma2 = pnp_dma(pdev, 1);
+
+	if (mpu_port > 0) {
+		err = pnp_activate_dev(devmpu);
+		if (err < 0) {
+			snd_printk(KERN_ERR "MPU401 pnp configure failure\n");
+			mpu_port = -1;
+			return err;
+		}
+		mpu_port = pnp_port_start(devmpu, 0);
+		mpu_irq = pnp_irq(devmpu, 0);
+	}
+	return 0;
+}
+
+static int __devinit snd_miro_pnp_probe(struct pnp_card_link *pcard,
+					const struct pnp_card_device_id *pid)
+{
+	struct snd_card *card;
+	int err;
+	struct snd_miro *miro;
+
+	if (snd_miro_pnp_is_probed)
+		return -EBUSY;
+	if (!isapnp)
+		return -ENODEV;
+	err = snd_card_create(index, id, THIS_MODULE,
+				sizeof(struct snd_miro), &card);
+	if (err < 0)
+		return err;
+
+	card->private_free = snd_card_miro_free;
+	miro = card->private_data;
+
+	err = snd_card_miro_pnp(miro, pcard, pid);
+	if (err) {
+		snd_card_free(card);
+		return err;
+	}
+
+	/* only miroSOUND PCM20 and PCM12 == OPTi924 */
+	err = snd_miro_init(miro, OPTi9XX_HW_82C924);
+	if (err) {
+		snd_card_free(card);
+		return err;
+	}
+
+	err = snd_miro_opti_check(miro);
+	if (err) {
+		snd_printk(KERN_ERR "OPTI chip not found\n");
+		snd_card_free(card);
+		return err;
+	}
+
+	snd_card_set_dev(card, &pcard->card->dev);
+	err = snd_miro_probe(card);
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	pnp_set_card_drvdata(pcard, card);
+	snd_miro_pnp_is_probed = 1;
+	return 0;
+}
+
+static void __devexit snd_miro_pnp_remove(struct pnp_card_link * pcard)
+{
+	snd_card_free(pnp_get_card_drvdata(pcard));
+	pnp_set_card_drvdata(pcard, NULL);
+	snd_miro_pnp_is_probed = 0;
+}
+
+static struct pnp_card_driver miro_pnpc_driver = {
+	.flags		= PNP_DRIVER_RES_DISABLE,
+	.name		= "miro",
+	.id_table	= snd_miro_pnpids,
+	.probe		= snd_miro_pnp_probe,
+	.remove		= __devexit_p(snd_miro_pnp_remove),
+};
+#endif
+
 static int __init alsa_card_miro_init(void)
 static int __init alsa_card_miro_init(void)
 {
 {
+#ifdef CONFIG_PNP
+	pnp_register_card_driver(&miro_pnpc_driver);
+	if (snd_miro_pnp_is_probed)
+		return 0;
+	pnp_unregister_card_driver(&miro_pnpc_driver);
+#endif
 	return isa_register_driver(&snd_miro_driver, 1);
 	return isa_register_driver(&snd_miro_driver, 1);
 }
 }
 
 
 static void __exit alsa_card_miro_exit(void)
 static void __exit alsa_card_miro_exit(void)
 {
 {
-	isa_unregister_driver(&snd_miro_driver);
+	if (!snd_miro_pnp_is_probed) {
+		isa_unregister_driver(&snd_miro_driver);
+		return;
+	}
+#ifdef CONFIG_PNP
+	pnp_unregister_card_driver(&miro_pnpc_driver);
+#endif
 }
 }
 
 
 module_init(alsa_card_miro_init)
 module_init(alsa_card_miro_init)

+ 43 - 67
sound/isa/opti9xx/opti92x-ad1848.c

@@ -141,15 +141,7 @@ struct snd_opti9xx {
 
 
 	spinlock_t lock;
 	spinlock_t lock;
 
 
-	long wss_base;
 	int irq;
 	int irq;
-	int dma1;
-	int dma2;
-
-	long fm_port;
-
-	long mpu_port;
-	int mpu_irq;
 
 
 #ifdef CONFIG_PNP
 #ifdef CONFIG_PNP
 	struct pnp_dev *dev;
 	struct pnp_dev *dev;
@@ -216,13 +208,7 @@ static int __devinit snd_opti9xx_init(struct snd_opti9xx *chip,
 
 
 	spin_lock_init(&chip->lock);
 	spin_lock_init(&chip->lock);
 
 
-	chip->wss_base = -1;
 	chip->irq = -1;
 	chip->irq = -1;
-	chip->dma1 = -1;
-	chip->dma2 = -1;
-	chip->fm_port = -1;
-	chip->mpu_port = -1;
-	chip->mpu_irq = -1;
 
 
 	switch (hardware) {
 	switch (hardware) {
 #ifndef OPTi93X
 #ifndef OPTi93X
@@ -348,7 +334,10 @@ static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg,
 		(snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask)))
 		(snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask)))
 
 
 
 
-static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip)
+static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip,
+					   long wss_base,
+					   int irq, int dma1, int dma2,
+					   long mpu_port, int mpu_irq)
 {
 {
 	unsigned char wss_base_bits;
 	unsigned char wss_base_bits;
 	unsigned char irq_bits;
 	unsigned char irq_bits;
@@ -416,7 +405,7 @@ static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip)
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
-	switch (chip->wss_base) {
+	switch (wss_base) {
 	case 0x530:
 	case 0x530:
 		wss_base_bits = 0x00;
 		wss_base_bits = 0x00;
 		break;
 		break;
@@ -430,14 +419,13 @@ static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip)
 		wss_base_bits = 0x02;
 		wss_base_bits = 0x02;
 		break;
 		break;
 	default:
 	default:
-		snd_printk(KERN_WARNING "WSS port 0x%lx not valid\n",
-			   chip->wss_base);
+		snd_printk(KERN_WARNING "WSS port 0x%lx not valid\n", wss_base);
 		goto __skip_base;
 		goto __skip_base;
 	}
 	}
 	snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30);
 	snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30);
 
 
 __skip_base:
 __skip_base:
-	switch (chip->irq) {
+	switch (irq) {
 //#ifdef OPTi93X
 //#ifdef OPTi93X
 	case 5:
 	case 5:
 		irq_bits = 0x05;
 		irq_bits = 0x05;
@@ -456,11 +444,11 @@ static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip)
 		irq_bits = 0x04;
 		irq_bits = 0x04;
 		break;
 		break;
 	default:
 	default:
-		snd_printk(KERN_WARNING "WSS irq # %d not valid\n", chip->irq);
+		snd_printk(KERN_WARNING "WSS irq # %d not valid\n", irq);
 		goto __skip_resources;
 		goto __skip_resources;
 	}
 	}
 
 
-	switch (chip->dma1) {
+	switch (dma1) {
 	case 0:
 	case 0:
 		dma_bits = 0x01;
 		dma_bits = 0x01;
 		break;
 		break;
@@ -471,38 +459,36 @@ static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip)
 		dma_bits = 0x03;
 		dma_bits = 0x03;
 		break;
 		break;
 	default:
 	default:
-		snd_printk(KERN_WARNING "WSS dma1 # %d not valid\n",
-			   chip->dma1);
+		snd_printk(KERN_WARNING "WSS dma1 # %d not valid\n", dma1);
 		goto __skip_resources;
 		goto __skip_resources;
 	}
 	}
 
 
 #if defined(CS4231) || defined(OPTi93X)
 #if defined(CS4231) || defined(OPTi93X)
-	if (chip->dma1 == chip->dma2) {
+	if (dma1 == dma2) {
 		snd_printk(KERN_ERR "don't want to share dmas\n");
 		snd_printk(KERN_ERR "don't want to share dmas\n");
 		return -EBUSY;
 		return -EBUSY;
 	}
 	}
 
 
-	switch (chip->dma2) {
+	switch (dma2) {
 	case 0:
 	case 0:
 	case 1:
 	case 1:
 		break;
 		break;
 	default:
 	default:
-		snd_printk(KERN_WARNING "WSS dma2 # %d not valid\n",
-			   chip->dma2);
+		snd_printk(KERN_WARNING "WSS dma2 # %d not valid\n", dma2);
 		goto __skip_resources;
 		goto __skip_resources;
 	}
 	}
 	dma_bits |= 0x04;
 	dma_bits |= 0x04;
 #endif	/* CS4231 || OPTi93X */
 #endif	/* CS4231 || OPTi93X */
 
 
 #ifndef OPTi93X
 #ifndef OPTi93X
-	 outb(irq_bits << 3 | dma_bits, chip->wss_base);
+	 outb(irq_bits << 3 | dma_bits, wss_base);
 #else /* OPTi93X */
 #else /* OPTi93X */
 	snd_opti9xx_write(chip, OPTi9XX_MC_REG(3), (irq_bits << 3 | dma_bits));
 	snd_opti9xx_write(chip, OPTi9XX_MC_REG(3), (irq_bits << 3 | dma_bits));
 #endif /* OPTi93X */
 #endif /* OPTi93X */
 
 
 __skip_resources:
 __skip_resources:
 	if (chip->hardware > OPTi9XX_HW_82C928) {
 	if (chip->hardware > OPTi9XX_HW_82C928) {
-		switch (chip->mpu_port) {
+		switch (mpu_port) {
 		case 0:
 		case 0:
 		case -1:
 		case -1:
 			break;
 			break;
@@ -520,12 +506,11 @@ static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip)
 			break;
 			break;
 		default:
 		default:
 			snd_printk(KERN_WARNING
 			snd_printk(KERN_WARNING
-				   "MPU-401 port 0x%lx not valid\n",
-				chip->mpu_port);
+				   "MPU-401 port 0x%lx not valid\n", mpu_port);
 			goto __skip_mpu;
 			goto __skip_mpu;
 		}
 		}
 
 
-		switch (chip->mpu_irq) {
+		switch (mpu_irq) {
 		case 5:
 		case 5:
 			mpu_irq_bits = 0x02;
 			mpu_irq_bits = 0x02;
 			break;
 			break;
@@ -540,12 +525,12 @@ static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip)
 			break;
 			break;
 		default:
 		default:
 			snd_printk(KERN_WARNING "MPU-401 irq # %d not valid\n",
 			snd_printk(KERN_WARNING "MPU-401 irq # %d not valid\n",
-				chip->mpu_irq);
+				mpu_irq);
 			goto __skip_mpu;
 			goto __skip_mpu;
 		}
 		}
 
 
 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6),
 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6),
-			(chip->mpu_port <= 0) ? 0x00 :
+			(mpu_port <= 0) ? 0x00 :
 				0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3,
 				0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3,
 			0xf8);
 			0xf8);
 	}
 	}
@@ -701,6 +686,7 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
 {
 {
 	static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
 	static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
 	int error;
 	int error;
+	int xdma2;
 	struct snd_opti9xx *chip = card->private_data;
 	struct snd_opti9xx *chip = card->private_data;
 	struct snd_wss *codec;
 	struct snd_wss *codec;
 #ifdef CS4231
 #ifdef CS4231
@@ -715,31 +701,25 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
 						"OPTi9xx MC")) == NULL)
 						"OPTi9xx MC")) == NULL)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	chip->wss_base = port;
-	chip->fm_port = fm_port;
-	chip->mpu_port = mpu_port;
-	chip->irq = irq;
-	chip->mpu_irq = mpu_irq;
-	chip->dma1 = dma1;
 #if defined(CS4231) || defined(OPTi93X)
 #if defined(CS4231) || defined(OPTi93X)
-	chip->dma2 = dma2;
+	xdma2 = dma2;
 #else
 #else
-	chip->dma2 = -1;
+	xdma2 = -1;
 #endif
 #endif
 
 
-	if (chip->wss_base == SNDRV_AUTO_PORT) {
-		chip->wss_base = snd_legacy_find_free_ioport(possible_ports, 4);
-		if (chip->wss_base < 0) {
+	if (port == SNDRV_AUTO_PORT) {
+		port = snd_legacy_find_free_ioport(possible_ports, 4);
+		if (port < 0) {
 			snd_printk(KERN_ERR "unable to find a free WSS port\n");
 			snd_printk(KERN_ERR "unable to find a free WSS port\n");
 			return -EBUSY;
 			return -EBUSY;
 		}
 		}
 	}
 	}
-	error = snd_opti9xx_configure(chip);
+	error = snd_opti9xx_configure(chip, port, irq, dma1, xdma2,
+				      mpu_port, mpu_irq);
 	if (error)
 	if (error)
 		return error;
 		return error;
 
 
-	error = snd_wss_create(card, chip->wss_base + 4, -1,
-			       chip->irq, chip->dma1, chip->dma2,
+	error = snd_wss_create(card, port + 4, -1, irq, dma1, xdma2,
 #ifdef OPTi93X
 #ifdef OPTi93X
 			       WSS_HW_OPTI93X, WSS_HWSHARE_IRQ,
 			       WSS_HW_OPTI93X, WSS_HWSHARE_IRQ,
 #else
 #else
@@ -763,35 +743,35 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
 		return error;
 		return error;
 #endif
 #endif
 #ifdef OPTi93X
 #ifdef OPTi93X
-	error = request_irq(chip->irq, snd_opti93x_interrupt,
+	error = request_irq(irq, snd_opti93x_interrupt,
 			    IRQF_DISABLED, DEV_NAME" - WSS", codec);
 			    IRQF_DISABLED, DEV_NAME" - WSS", codec);
 	if (error < 0) {
 	if (error < 0) {
 		snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", chip->irq);
 		snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", chip->irq);
 		return error;
 		return error;
 	}
 	}
 #endif
 #endif
+	chip->irq = irq;
 	strcpy(card->driver, chip->name);
 	strcpy(card->driver, chip->name);
 	sprintf(card->shortname, "OPTi %s", card->driver);
 	sprintf(card->shortname, "OPTi %s", card->driver);
 #if defined(CS4231) || defined(OPTi93X)
 #if defined(CS4231) || defined(OPTi93X)
 	sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d&%d",
 	sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d&%d",
-		card->shortname, pcm->name, chip->wss_base + 4,
-		chip->irq, chip->dma1, chip->dma2);
+		card->shortname, pcm->name, port + 4, irq, dma1, xdma2);
 #else
 #else
 	sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d",
 	sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d",
-		card->shortname, pcm->name, chip->wss_base + 4,
-		chip->irq, chip->dma1);
+		card->shortname, pcm->name, port + 4, irq, dma1);
 #endif	/* CS4231 || OPTi93X */
 #endif	/* CS4231 || OPTi93X */
 
 
-	if (chip->mpu_port <= 0 || chip->mpu_port == SNDRV_AUTO_PORT)
+	if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT)
 		rmidi = NULL;
 		rmidi = NULL;
-	else
-		if ((error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
-				chip->mpu_port, 0, chip->mpu_irq, IRQF_DISABLED,
-				&rmidi)))
+	else {
+		error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+				mpu_port, 0, mpu_irq, IRQF_DISABLED, &rmidi);
+		if (error)
 			snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",
 			snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",
-				   chip->mpu_port);
+				   mpu_port);
+	}
 
 
-	if (chip->fm_port > 0 && chip->fm_port != SNDRV_AUTO_PORT) {
+	if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) {
 		struct snd_opl3 *opl3 = NULL;
 		struct snd_opl3 *opl3 = NULL;
 #ifndef OPTi93X
 #ifndef OPTi93X
 		if (chip->hardware == OPTi9XX_HW_82C928 ||
 		if (chip->hardware == OPTi9XX_HW_82C928 ||
@@ -801,9 +781,7 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
 			/* assume we have an OPL4 */
 			/* assume we have an OPL4 */
 			snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2),
 			snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2),
 					       0x20, 0x20);
 					       0x20, 0x20);
-			if (snd_opl4_create(card,
-					    chip->fm_port,
-					    chip->fm_port - 8,
+			if (snd_opl4_create(card, fm_port, fm_port - 8,
 					    2, &opl3, &opl4) < 0) {
 					    2, &opl3, &opl4) < 0) {
 				/* no luck, use OPL3 instead */
 				/* no luck, use OPL3 instead */
 				snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2),
 				snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2),
@@ -811,12 +789,10 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
 			}
 			}
 		}
 		}
 #endif	/* !OPTi93X */
 #endif	/* !OPTi93X */
-		if (!opl3 && snd_opl3_create(card,
-					     chip->fm_port,
-					     chip->fm_port + 2,
+		if (!opl3 && snd_opl3_create(card, fm_port, fm_port + 2,
 					     OPL3_HW_AUTO, 0, &opl3) < 0) {
 					     OPL3_HW_AUTO, 0, &opl3) < 0) {
 			snd_printk(KERN_WARNING "no OPL device at 0x%lx-0x%lx\n",
 			snd_printk(KERN_WARNING "no OPL device at 0x%lx-0x%lx\n",
-				   chip->fm_port, chip->fm_port + 4 - 1);
+				   fm_port, fm_port + 4 - 1);
 		}
 		}
 		if (opl3) {
 		if (opl3) {
 			error = snd_opl3_hwdep_new(opl3, 0, 1, &synth);
 			error = snd_opl3_hwdep_new(opl3, 0, 1, &synth);

+ 2 - 2
sound/isa/sb/sb_mixer.c

@@ -631,7 +631,7 @@ static struct sbmix_elem snd_sb16_ctl_mic_play_switch =
 static struct sbmix_elem snd_sb16_ctl_mic_play_vol =
 static struct sbmix_elem snd_sb16_ctl_mic_play_vol =
 	SB_SINGLE("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31);
 	SB_SINGLE("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31);
 static struct sbmix_elem snd_sb16_ctl_pc_speaker_vol =
 static struct sbmix_elem snd_sb16_ctl_pc_speaker_vol =
-	SB_SINGLE("PC Speaker Volume", SB_DSP4_SPEAKER_DEV, 6, 3);
+	SB_SINGLE("Beep Volume", SB_DSP4_SPEAKER_DEV, 6, 3);
 static struct sbmix_elem snd_sb16_ctl_capture_vol =
 static struct sbmix_elem snd_sb16_ctl_capture_vol =
 	SB_DOUBLE("Capture Volume", SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3);
 	SB_DOUBLE("Capture Volume", SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3);
 static struct sbmix_elem snd_sb16_ctl_play_vol =
 static struct sbmix_elem snd_sb16_ctl_play_vol =
@@ -689,7 +689,7 @@ static struct sbmix_elem snd_dt019x_ctl_cd_play_vol =
 static struct sbmix_elem snd_dt019x_ctl_mic_play_vol =
 static struct sbmix_elem snd_dt019x_ctl_mic_play_vol =
 	SB_SINGLE("Mic Playback Volume", SB_DT019X_MIC_DEV, 4, 7);
 	SB_SINGLE("Mic Playback Volume", SB_DT019X_MIC_DEV, 4, 7);
 static struct sbmix_elem snd_dt019x_ctl_pc_speaker_vol =
 static struct sbmix_elem snd_dt019x_ctl_pc_speaker_vol =
-	SB_SINGLE("PC Speaker Volume", SB_DT019X_SPKR_DEV, 0,  7);
+	SB_SINGLE("Beep Volume", SB_DT019X_SPKR_DEV, 0,  7);
 static struct sbmix_elem snd_dt019x_ctl_line_play_vol =
 static struct sbmix_elem snd_dt019x_ctl_line_play_vol =
 	SB_DOUBLE("Line Playback Volume", SB_DT019X_LINE_DEV, SB_DT019X_LINE_DEV, 4,0, 15);
 	SB_DOUBLE("Line Playback Volume", SB_DT019X_LINE_DEV, SB_DT019X_LINE_DEV, 4,0, 15);
 static struct sbmix_elem snd_dt019x_ctl_pcm_play_switch =
 static struct sbmix_elem snd_dt019x_ctl_pcm_play_switch =

+ 319 - 408
sound/isa/sscape.c

@@ -1,5 +1,5 @@
 /*
 /*
- *   Low-level ALSA driver for the ENSONIQ SoundScape PnP
+ *   Low-level ALSA driver for the ENSONIQ SoundScape
  *   Copyright (c) by Chris Rankin
  *   Copyright (c) by Chris Rankin
  *
  *
  *   This driver was written in part using information obtained from
  *   This driver was written in part using information obtained from
@@ -25,31 +25,36 @@
 #include <linux/err.h>
 #include <linux/err.h>
 #include <linux/isa.h>
 #include <linux/isa.h>
 #include <linux/delay.h>
 #include <linux/delay.h>
+#include <linux/firmware.h>
 #include <linux/pnp.h>
 #include <linux/pnp.h>
 #include <linux/spinlock.h>
 #include <linux/spinlock.h>
 #include <linux/moduleparam.h>
 #include <linux/moduleparam.h>
 #include <asm/dma.h>
 #include <asm/dma.h>
 #include <sound/core.h>
 #include <sound/core.h>
-#include <sound/hwdep.h>
 #include <sound/wss.h>
 #include <sound/wss.h>
 #include <sound/mpu401.h>
 #include <sound/mpu401.h>
 #include <sound/initval.h>
 #include <sound/initval.h>
 
 
-#include <sound/sscape_ioctl.h>
-
 
 
 MODULE_AUTHOR("Chris Rankin");
 MODULE_AUTHOR("Chris Rankin");
-MODULE_DESCRIPTION("ENSONIQ SoundScape PnP driver");
+MODULE_DESCRIPTION("ENSONIQ SoundScape driver");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
-
-static int index[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_IDX;
-static char* id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_STR;
-static long port[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PORT;
-static long wss_port[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PORT;
-static int irq[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_IRQ;
-static int mpu_irq[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_IRQ;
-static int dma[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_DMA;
-static int dma2[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_DMA;
+MODULE_FIRMWARE("sndscape.co0");
+MODULE_FIRMWARE("sndscape.co1");
+MODULE_FIRMWARE("sndscape.co2");
+MODULE_FIRMWARE("sndscape.co3");
+MODULE_FIRMWARE("sndscape.co4");
+MODULE_FIRMWARE("scope.cod");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
+static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
+static int dma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+static bool joystick[SNDRV_CARDS];
 
 
 module_param_array(index, int, NULL, 0444);
 module_param_array(index, int, NULL, 0444);
 MODULE_PARM_DESC(index, "Index number for SoundScape soundcard");
 MODULE_PARM_DESC(index, "Index number for SoundScape soundcard");
@@ -75,6 +80,9 @@ MODULE_PARM_DESC(dma, "DMA # for SoundScape driver.");
 module_param_array(dma2, int, NULL, 0444);
 module_param_array(dma2, int, NULL, 0444);
 MODULE_PARM_DESC(dma2, "DMA2 # for SoundScape driver.");
 MODULE_PARM_DESC(dma2, "DMA2 # for SoundScape driver.");
 
 
+module_param_array(joystick, bool, NULL, 0444);
+MODULE_PARM_DESC(joystick, "Enable gameport.");
+
 #ifdef CONFIG_PNP
 #ifdef CONFIG_PNP
 static int isa_registered;
 static int isa_registered;
 static int pnp_registered;
 static int pnp_registered;
@@ -101,14 +109,14 @@ MODULE_DEVICE_TABLE(pnp_card, sscape_pnpids);
 #define RX_READY 0x01
 #define RX_READY 0x01
 #define TX_READY 0x02
 #define TX_READY 0x02
 
 
-#define CMD_ACK           0x80
-#define CMD_SET_MIDI_VOL  0x84
-#define CMD_GET_MIDI_VOL  0x85
-#define CMD_XXX_MIDI_VOL  0x86
-#define CMD_SET_EXTMIDI   0x8a
-#define CMD_GET_EXTMIDI   0x8b
-#define CMD_SET_MT32      0x8c
-#define CMD_GET_MT32      0x8d
+#define CMD_ACK			0x80
+#define CMD_SET_MIDI_VOL	0x84
+#define CMD_GET_MIDI_VOL	0x85
+#define CMD_XXX_MIDI_VOL	0x86
+#define CMD_SET_EXTMIDI		0x8a
+#define CMD_GET_EXTMIDI		0x8b
+#define CMD_SET_MT32		0x8c
+#define CMD_GET_MT32		0x8d
 
 
 enum GA_REG {
 enum GA_REG {
 	GA_INTSTAT_REG = 0,
 	GA_INTSTAT_REG = 0,
@@ -127,7 +135,8 @@ enum GA_REG {
 
 
 
 
 enum card_type {
 enum card_type {
-	SSCAPE,
+	MEDIA_FX,	/* Sequoia S-1000 */
+	SSCAPE,		/* Sequoia S-2000 */
 	SSCAPE_PNP,
 	SSCAPE_PNP,
 	SSCAPE_VIVO,
 	SSCAPE_VIVO,
 };
 };
@@ -140,16 +149,7 @@ struct soundscape {
 	struct resource *io_res;
 	struct resource *io_res;
 	struct resource *wss_res;
 	struct resource *wss_res;
 	struct snd_wss *chip;
 	struct snd_wss *chip;
-	struct snd_mpu401 *mpu;
-	struct snd_hwdep *hw;
 
 
-	/*
-	 * The MIDI device won't work until we've loaded
-	 * its firmware via a hardware-dependent device IOCTL
-	 */
-	spinlock_t fwlock;
-	int hw_in_use;
-	unsigned long midi_usage;
 	unsigned char midi_vol;
 	unsigned char midi_vol;
 };
 };
 
 
@@ -161,28 +161,21 @@ static inline struct soundscape *get_card_soundscape(struct snd_card *c)
 	return (struct soundscape *) (c->private_data);
 	return (struct soundscape *) (c->private_data);
 }
 }
 
 
-static inline struct soundscape *get_mpu401_soundscape(struct snd_mpu401 * mpu)
-{
-	return (struct soundscape *) (mpu->private_data);
-}
-
-static inline struct soundscape *get_hwdep_soundscape(struct snd_hwdep * hw)
-{
-	return (struct soundscape *) (hw->private_data);
-}
-
-
 /*
 /*
  * Allocates some kernel memory that we can use for DMA.
  * Allocates some kernel memory that we can use for DMA.
  * I think this means that the memory has to map to
  * I think this means that the memory has to map to
  * contiguous pages of physical memory.
  * contiguous pages of physical memory.
  */
  */
-static struct snd_dma_buffer *get_dmabuf(struct snd_dma_buffer *buf, unsigned long size)
+static struct snd_dma_buffer *get_dmabuf(struct snd_dma_buffer *buf,
+					 unsigned long size)
 {
 {
 	if (buf) {
 	if (buf) {
-		if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV, snd_dma_isa_data(),
+		if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV,
+						 snd_dma_isa_data(),
 						 size, buf) < 0) {
 						 size, buf) < 0) {
-			snd_printk(KERN_ERR "sscape: Failed to allocate %lu bytes for DMA\n", size);
+			snd_printk(KERN_ERR "sscape: Failed to allocate "
+					    "%lu bytes for DMA\n",
+					    size);
 			return NULL;
 			return NULL;
 		}
 		}
 	}
 	}
@@ -199,13 +192,13 @@ static void free_dmabuf(struct snd_dma_buffer *buf)
 		snd_dma_free_pages(buf);
 		snd_dma_free_pages(buf);
 }
 }
 
 
-
 /*
 /*
  * This function writes to the SoundScape's control registers,
  * This function writes to the SoundScape's control registers,
  * but doesn't do any locking. It's up to the caller to do that.
  * but doesn't do any locking. It's up to the caller to do that.
  * This is why this function is "unsafe" ...
  * This is why this function is "unsafe" ...
  */
  */
-static inline void sscape_write_unsafe(unsigned io_base, enum GA_REG reg, unsigned char val)
+static inline void sscape_write_unsafe(unsigned io_base, enum GA_REG reg,
+				       unsigned char val)
 {
 {
 	outb(reg, ODIE_ADDR_IO(io_base));
 	outb(reg, ODIE_ADDR_IO(io_base));
 	outb(val, ODIE_DATA_IO(io_base));
 	outb(val, ODIE_DATA_IO(io_base));
@@ -215,7 +208,8 @@ static inline void sscape_write_unsafe(unsigned io_base, enum GA_REG reg, unsign
  * Write to the SoundScape's control registers, and do the
  * Write to the SoundScape's control registers, and do the
  * necessary locking ...
  * necessary locking ...
  */
  */
-static void sscape_write(struct soundscape *s, enum GA_REG reg, unsigned char val)
+static void sscape_write(struct soundscape *s, enum GA_REG reg,
+			 unsigned char val)
 {
 {
 	unsigned long flags;
 	unsigned long flags;
 
 
@@ -228,7 +222,8 @@ static void sscape_write(struct soundscape *s, enum GA_REG reg, unsigned char va
  * Read from the SoundScape's control registers, but leave any
  * Read from the SoundScape's control registers, but leave any
  * locking to the caller. This is why the function is "unsafe" ...
  * locking to the caller. This is why the function is "unsafe" ...
  */
  */
-static inline unsigned char sscape_read_unsafe(unsigned io_base, enum GA_REG reg)
+static inline unsigned char sscape_read_unsafe(unsigned io_base,
+					       enum GA_REG reg)
 {
 {
 	outb(reg, ODIE_ADDR_IO(io_base));
 	outb(reg, ODIE_ADDR_IO(io_base));
 	return inb(ODIE_DATA_IO(io_base));
 	return inb(ODIE_DATA_IO(io_base));
@@ -257,9 +252,8 @@ static inline void set_midi_mode_unsafe(unsigned io_base)
 static inline int host_read_unsafe(unsigned io_base)
 static inline int host_read_unsafe(unsigned io_base)
 {
 {
 	int data = -1;
 	int data = -1;
-	if ((inb(HOST_CTRL_IO(io_base)) & RX_READY) != 0) {
+	if ((inb(HOST_CTRL_IO(io_base)) & RX_READY) != 0)
 		data = inb(HOST_DATA_IO(io_base));
 		data = inb(HOST_DATA_IO(io_base));
-	}
 
 
 	return data;
 	return data;
 }
 }
@@ -301,7 +295,7 @@ static inline int host_write_unsafe(unsigned io_base, unsigned char data)
  * Also leaves all locking-issues to the caller ...
  * Also leaves all locking-issues to the caller ...
  */
  */
 static int host_write_ctrl_unsafe(unsigned io_base, unsigned char data,
 static int host_write_ctrl_unsafe(unsigned io_base, unsigned char data,
-                                  unsigned timeout)
+				  unsigned timeout)
 {
 {
 	int err;
 	int err;
 
 
@@ -320,7 +314,7 @@ static int host_write_ctrl_unsafe(unsigned io_base, unsigned char data,
  *
  *
  * NOTE: This check is based upon observation, not documentation.
  * NOTE: This check is based upon observation, not documentation.
  */
  */
-static inline int verify_mpu401(const struct snd_mpu401 * mpu)
+static inline int verify_mpu401(const struct snd_mpu401 *mpu)
 {
 {
 	return ((inb(MPU401C(mpu)) & 0xc0) == 0x80);
 	return ((inb(MPU401C(mpu)) & 0xc0) == 0x80);
 }
 }
@@ -328,7 +322,7 @@ static inline int verify_mpu401(const struct snd_mpu401 * mpu)
 /*
 /*
  * This is apparently the standard way to initailise an MPU-401
  * This is apparently the standard way to initailise an MPU-401
  */
  */
-static inline void initialise_mpu401(const struct snd_mpu401 * mpu)
+static inline void initialise_mpu401(const struct snd_mpu401 *mpu)
 {
 {
 	outb(0, MPU401D(mpu));
 	outb(0, MPU401D(mpu));
 }
 }
@@ -338,9 +332,10 @@ static inline void initialise_mpu401(const struct snd_mpu401 * mpu)
  * The AD1845 detection fails if we *don't* do this, so I
  * The AD1845 detection fails if we *don't* do this, so I
  * think that this is a good idea ...
  * think that this is a good idea ...
  */
  */
-static inline void activate_ad1845_unsafe(unsigned io_base)
+static void activate_ad1845_unsafe(unsigned io_base)
 {
 {
-	sscape_write_unsafe(io_base, GA_HMCTL_REG, (sscape_read_unsafe(io_base, GA_HMCTL_REG) & 0xcf) | 0x10);
+	unsigned char val = sscape_read_unsafe(io_base, GA_HMCTL_REG);
+	sscape_write_unsafe(io_base, GA_HMCTL_REG, (val & 0xcf) | 0x10);
 	sscape_write_unsafe(io_base, GA_CDCFG_REG, 0x80);
 	sscape_write_unsafe(io_base, GA_CDCFG_REG, 0x80);
 }
 }
 
 
@@ -359,24 +354,27 @@ static void soundscape_free(struct snd_card *c)
  * Tell the SoundScape to begin a DMA tranfer using the given channel.
  * Tell the SoundScape to begin a DMA tranfer using the given channel.
  * All locking issues are left to the caller.
  * All locking issues are left to the caller.
  */
  */
-static inline void sscape_start_dma_unsafe(unsigned io_base, enum GA_REG reg)
+static void sscape_start_dma_unsafe(unsigned io_base, enum GA_REG reg)
 {
 {
-	sscape_write_unsafe(io_base, reg, sscape_read_unsafe(io_base, reg) | 0x01);
-	sscape_write_unsafe(io_base, reg, sscape_read_unsafe(io_base, reg) & 0xfe);
+	sscape_write_unsafe(io_base, reg,
+			    sscape_read_unsafe(io_base, reg) | 0x01);
+	sscape_write_unsafe(io_base, reg,
+			    sscape_read_unsafe(io_base, reg) & 0xfe);
 }
 }
 
 
 /*
 /*
  * Wait for a DMA transfer to complete. This is a "limited busy-wait",
  * Wait for a DMA transfer to complete. This is a "limited busy-wait",
  * and all locking issues are left to the caller.
  * and all locking issues are left to the caller.
  */
  */
-static int sscape_wait_dma_unsafe(unsigned io_base, enum GA_REG reg, unsigned timeout)
+static int sscape_wait_dma_unsafe(unsigned io_base, enum GA_REG reg,
+				  unsigned timeout)
 {
 {
 	while (!(sscape_read_unsafe(io_base, reg) & 0x01) && (timeout != 0)) {
 	while (!(sscape_read_unsafe(io_base, reg) & 0x01) && (timeout != 0)) {
 		udelay(100);
 		udelay(100);
 		--timeout;
 		--timeout;
 	} /* while */
 	} /* while */
 
 
-	return (sscape_read_unsafe(io_base, reg) & 0x01);
+	return sscape_read_unsafe(io_base, reg) & 0x01;
 }
 }
 
 
 /*
 /*
@@ -392,12 +390,12 @@ static int obp_startup_ack(struct soundscape *s, unsigned timeout)
 
 
 	do {
 	do {
 		unsigned long flags;
 		unsigned long flags;
-		unsigned char x;
+		int x;
 
 
 		spin_lock_irqsave(&s->lock, flags);
 		spin_lock_irqsave(&s->lock, flags);
-		x = inb(HOST_DATA_IO(s->io_base));
+		x = host_read_unsafe(s->io_base);
 		spin_unlock_irqrestore(&s->lock, flags);
 		spin_unlock_irqrestore(&s->lock, flags);
-		if ((x & 0xfe) == 0xfe)
+		if (x == 0xfe || x == 0xff)
 			return 1;
 			return 1;
 
 
 		msleep(10);
 		msleep(10);
@@ -419,10 +417,10 @@ static int host_startup_ack(struct soundscape *s, unsigned timeout)
 
 
 	do {
 	do {
 		unsigned long flags;
 		unsigned long flags;
-		unsigned char x;
+		int x;
 
 
 		spin_lock_irqsave(&s->lock, flags);
 		spin_lock_irqsave(&s->lock, flags);
-		x = inb(HOST_DATA_IO(s->io_base));
+		x = host_read_unsafe(s->io_base);
 		spin_unlock_irqrestore(&s->lock, flags);
 		spin_unlock_irqrestore(&s->lock, flags);
 		if (x == 0xfe)
 		if (x == 0xfe)
 			return 1;
 			return 1;
@@ -436,15 +434,15 @@ static int host_startup_ack(struct soundscape *s, unsigned timeout)
 /*
 /*
  * Upload a byte-stream into the SoundScape using DMA channel A.
  * Upload a byte-stream into the SoundScape using DMA channel A.
  */
  */
-static int upload_dma_data(struct soundscape *s,
-                           const unsigned char __user *data,
-                           size_t size)
+static int upload_dma_data(struct soundscape *s, const unsigned char *data,
+			   size_t size)
 {
 {
 	unsigned long flags;
 	unsigned long flags;
 	struct snd_dma_buffer dma;
 	struct snd_dma_buffer dma;
 	int ret;
 	int ret;
+	unsigned char val;
 
 
-	if (!get_dmabuf(&dma, PAGE_ALIGN(size)))
+	if (!get_dmabuf(&dma, PAGE_ALIGN(32 * 1024)))
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	spin_lock_irqsave(&s->lock, flags);
 	spin_lock_irqsave(&s->lock, flags);
@@ -452,70 +450,57 @@ static int upload_dma_data(struct soundscape *s,
 	/*
 	/*
 	 * Reset the board ...
 	 * Reset the board ...
 	 */
 	 */
-	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, sscape_read_unsafe(s->io_base, GA_HMCTL_REG) & 0x3f);
+	val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
+	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val & 0x3f);
 
 
 	/*
 	/*
 	 * Enable the DMA channels and configure them ...
 	 * Enable the DMA channels and configure them ...
 	 */
 	 */
-	sscape_write_unsafe(s->io_base, GA_DMACFG_REG, 0x50);
-	sscape_write_unsafe(s->io_base, GA_DMAA_REG, (s->chip->dma1 << 4) | DMA_8BIT);
+	val = (s->chip->dma1 << 4) | DMA_8BIT;
+	sscape_write_unsafe(s->io_base, GA_DMAA_REG, val);
 	sscape_write_unsafe(s->io_base, GA_DMAB_REG, 0x20);
 	sscape_write_unsafe(s->io_base, GA_DMAB_REG, 0x20);
 
 
 	/*
 	/*
 	 * Take the board out of reset ...
 	 * Take the board out of reset ...
 	 */
 	 */
-	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, sscape_read_unsafe(s->io_base, GA_HMCTL_REG) | 0x80);
+	val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
+	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val | 0x80);
 
 
 	/*
 	/*
-	 * Upload the user's data (firmware?) to the SoundScape
+	 * Upload the firmware to the SoundScape
 	 * board through the DMA channel ...
 	 * board through the DMA channel ...
 	 */
 	 */
 	while (size != 0) {
 	while (size != 0) {
 		unsigned long len;
 		unsigned long len;
 
 
-		/*
-		 * Apparently, copying to/from userspace can sleep.
-		 * We are therefore forbidden from holding any
-		 * spinlocks while we copy ...
-		 */
-		spin_unlock_irqrestore(&s->lock, flags);
-
-		/*
-		 * Remember that the data that we want to DMA
-		 * comes from USERSPACE. We have already verified
-		 * the userspace pointer ...
-		 */
 		len = min(size, dma.bytes);
 		len = min(size, dma.bytes);
-		len -= __copy_from_user(dma.area, data, len);
+		memcpy(dma.area, data, len);
 		data += len;
 		data += len;
 		size -= len;
 		size -= len;
 
 
-		/*
-		 * Grab that spinlock again, now that we've
-		 * finished copying!
-		 */
-		spin_lock_irqsave(&s->lock, flags);
-
 		snd_dma_program(s->chip->dma1, dma.addr, len, DMA_MODE_WRITE);
 		snd_dma_program(s->chip->dma1, dma.addr, len, DMA_MODE_WRITE);
 		sscape_start_dma_unsafe(s->io_base, GA_DMAA_REG);
 		sscape_start_dma_unsafe(s->io_base, GA_DMAA_REG);
 		if (!sscape_wait_dma_unsafe(s->io_base, GA_DMAA_REG, 5000)) {
 		if (!sscape_wait_dma_unsafe(s->io_base, GA_DMAA_REG, 5000)) {
 			/*
 			/*
-			 * Don't forget to release this spinlock we're holding ...
+			 * Don't forget to release this spinlock we're holding
 			 */
 			 */
 			spin_unlock_irqrestore(&s->lock, flags);
 			spin_unlock_irqrestore(&s->lock, flags);
 
 
-			snd_printk(KERN_ERR "sscape: DMA upload has timed out\n");
+			snd_printk(KERN_ERR
+					"sscape: DMA upload has timed out\n");
 			ret = -EAGAIN;
 			ret = -EAGAIN;
 			goto _release_dma;
 			goto _release_dma;
 		}
 		}
 	} /* while */
 	} /* while */
 
 
 	set_host_mode_unsafe(s->io_base);
 	set_host_mode_unsafe(s->io_base);
+	outb(0x0, s->io_base);
 
 
 	/*
 	/*
 	 * Boot the board ... (I think)
 	 * Boot the board ... (I think)
 	 */
 	 */
-	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, sscape_read_unsafe(s->io_base, GA_HMCTL_REG) | 0x40);
+	val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
+	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val | 0x40);
 	spin_unlock_irqrestore(&s->lock, flags);
 	spin_unlock_irqrestore(&s->lock, flags);
 
 
 	/*
 	/*
@@ -525,10 +510,12 @@ static int upload_dma_data(struct soundscape *s,
 	 */
 	 */
 	ret = 0;
 	ret = 0;
 	if (!obp_startup_ack(s, 5000)) {
 	if (!obp_startup_ack(s, 5000)) {
-		snd_printk(KERN_ERR "sscape: No response from on-board processor after upload\n");
+		snd_printk(KERN_ERR "sscape: No response "
+				    "from on-board processor after upload\n");
 		ret = -EAGAIN;
 		ret = -EAGAIN;
 	} else if (!host_startup_ack(s, 5000)) {
 	} else if (!host_startup_ack(s, 5000)) {
-		snd_printk(KERN_ERR "sscape: SoundScape failed to initialise\n");
+		snd_printk(KERN_ERR
+				"sscape: SoundScape failed to initialise\n");
 		ret = -EAGAIN;
 		ret = -EAGAIN;
 	}
 	}
 
 
@@ -536,7 +523,7 @@ static int upload_dma_data(struct soundscape *s,
 	/*
 	/*
 	 * NOTE!!! We are NOT holding any spinlocks at this point !!!
 	 * NOTE!!! We are NOT holding any spinlocks at this point !!!
 	 */
 	 */
-	sscape_write(s, GA_DMAA_REG, (s->ic_type == IC_ODIE ? 0x70 : 0x40));
+	sscape_write(s, GA_DMAA_REG, (s->ic_type == IC_OPUS ? 0x40 : 0x70));
 	free_dmabuf(&dma);
 	free_dmabuf(&dma);
 
 
 	return ret;
 	return ret;
@@ -546,167 +533,76 @@ static int upload_dma_data(struct soundscape *s,
  * Upload the bootblock(?) into the SoundScape. The only
  * Upload the bootblock(?) into the SoundScape. The only
  * purpose of this block of code seems to be to tell
  * purpose of this block of code seems to be to tell
  * us which version of the microcode we should be using.
  * us which version of the microcode we should be using.
- *
- * NOTE: The boot-block data resides in USER-SPACE!!!
- *       However, we have already verified its memory
- *       addresses by the time we get here.
  */
  */
-static int sscape_upload_bootblock(struct soundscape *sscape, struct sscape_bootblock __user *bb)
+static int sscape_upload_bootblock(struct snd_card *card)
 {
 {
+	struct soundscape *sscape = get_card_soundscape(card);
 	unsigned long flags;
 	unsigned long flags;
+	const struct firmware *init_fw = NULL;
 	int data = 0;
 	int data = 0;
 	int ret;
 	int ret;
 
 
-	ret = upload_dma_data(sscape, bb->code, sizeof(bb->code));
-
-	spin_lock_irqsave(&sscape->lock, flags);
-	if (ret == 0) {
-		data = host_read_ctrl_unsafe(sscape->io_base, 100);
-	}
-	set_midi_mode_unsafe(sscape->io_base);
-	spin_unlock_irqrestore(&sscape->lock, flags);
-
-	if (ret == 0) {
-		if (data < 0) {
-			snd_printk(KERN_ERR "sscape: timeout reading firmware version\n");
-			ret = -EAGAIN;
-		}
-		else if (__copy_to_user(&bb->version, &data, sizeof(bb->version))) {
-			ret = -EFAULT;
-		}
+	ret = request_firmware(&init_fw, "scope.cod", card->dev);
+	if (ret < 0) {
+		snd_printk(KERN_ERR "sscape: Error loading scope.cod");
+		return ret;
 	}
 	}
+	ret = upload_dma_data(sscape, init_fw->data, init_fw->size);
 
 
-	return ret;
-}
-
-/*
- * Upload the microcode into the SoundScape. The
- * microcode is 64K of data, and if we try to copy
- * it into a local variable then we will SMASH THE
- * KERNEL'S STACK! We therefore leave it in USER
- * SPACE, and save ourselves from copying it at all.
- */
-static int sscape_upload_microcode(struct soundscape *sscape,
-                                   const struct sscape_microcode __user *mc)
-{
-	unsigned long flags;
-	char __user *code;
-	int err;
+	release_firmware(init_fw);
 
 
-	/*
-	 * We are going to have to copy this data into a special
-	 * DMA-able buffer before we can upload it. We shall therefore
-	 * just check that the data pointer is valid for now.
-	 *
-	 * NOTE: This buffer is 64K long! That's WAY too big to
-	 *       copy into a stack-temporary anyway.
-	 */
-	if ( get_user(code, &mc->code) ||
-	     !access_ok(VERIFY_READ, code, SSCAPE_MICROCODE_SIZE) )
-		return -EFAULT;
+	spin_lock_irqsave(&sscape->lock, flags);
+	if (ret == 0)
+		data = host_read_ctrl_unsafe(sscape->io_base, 100);
 
 
-	if ((err = upload_dma_data(sscape, code, SSCAPE_MICROCODE_SIZE)) == 0) {
-		snd_printk(KERN_INFO "sscape: MIDI firmware loaded\n");
-	}
+	if (data & 0x10)
+		sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2f);
 
 
-	spin_lock_irqsave(&sscape->lock, flags);
-	set_midi_mode_unsafe(sscape->io_base);
 	spin_unlock_irqrestore(&sscape->lock, flags);
 	spin_unlock_irqrestore(&sscape->lock, flags);
 
 
-	initialise_mpu401(sscape->mpu);
+	data &= 0xf;
+	if (ret == 0 && data > 7) {
+		snd_printk(KERN_ERR
+				"sscape: timeout reading firmware version\n");
+		ret = -EAGAIN;
+	}
 
 
-	return err;
+	return (ret == 0) ? data : ret;
 }
 }
 
 
 /*
 /*
- * Hardware-specific device functions, to implement special
- * IOCTLs for the SoundScape card. This is how we upload
- * the microcode into the card, for example, and so we
- * must ensure that no two processes can open this device
- * simultaneously, and that we can't open it at all if
- * someone is using the MIDI device.
+ * Upload the microcode into the SoundScape.
  */
  */
-static int sscape_hw_open(struct snd_hwdep * hw, struct file *file)
+static int sscape_upload_microcode(struct snd_card *card, int version)
 {
 {
-	register struct soundscape *sscape = get_hwdep_soundscape(hw);
-	unsigned long flags;
+	struct soundscape *sscape = get_card_soundscape(card);
+	const struct firmware *init_fw = NULL;
+	char name[14];
 	int err;
 	int err;
 
 
-	spin_lock_irqsave(&sscape->fwlock, flags);
+	snprintf(name, sizeof(name), "sndscape.co%d", version);
 
 
-	if ((sscape->midi_usage != 0) || sscape->hw_in_use) {
-		err = -EBUSY;
-	} else {
-		sscape->hw_in_use = 1;
-		err = 0;
+	err = request_firmware(&init_fw, name, card->dev);
+	if (err < 0) {
+		snd_printk(KERN_ERR "sscape: Error loading sndscape.co%d",
+				version);
+		return err;
 	}
 	}
+	err = upload_dma_data(sscape, init_fw->data, init_fw->size);
+	if (err == 0)
+		snd_printk(KERN_INFO "sscape: MIDI firmware loaded %d KBs\n",
+				init_fw->size >> 10);
 
 
-	spin_unlock_irqrestore(&sscape->fwlock, flags);
-	return err;
-}
-
-static int sscape_hw_release(struct snd_hwdep * hw, struct file *file)
-{
-	register struct soundscape *sscape = get_hwdep_soundscape(hw);
-	unsigned long flags;
-
-	spin_lock_irqsave(&sscape->fwlock, flags);
-	sscape->hw_in_use = 0;
-	spin_unlock_irqrestore(&sscape->fwlock, flags);
-	return 0;
-}
-
-static int sscape_hw_ioctl(struct snd_hwdep * hw, struct file *file,
-                           unsigned int cmd, unsigned long arg)
-{
-	struct soundscape *sscape = get_hwdep_soundscape(hw);
-	int err = -EBUSY;
-
-	switch (cmd) {
-	case SND_SSCAPE_LOAD_BOOTB:
-		{
-			register struct sscape_bootblock __user *bb = (struct sscape_bootblock __user *) arg;
-
-			/*
-			 * We are going to have to copy this data into a special
-			 * DMA-able buffer before we can upload it. We shall therefore
-			 * just check that the data pointer is valid for now ...
-			 */
-			if ( !access_ok(VERIFY_READ, bb->code, sizeof(bb->code)) )
-				return -EFAULT;
-
-			/*
-			 * Now check that we can write the firmware version number too...
-			 */
-			if ( !access_ok(VERIFY_WRITE, &bb->version, sizeof(bb->version)) )
-				return -EFAULT;
-
-			err = sscape_upload_bootblock(sscape, bb);
-		}
-		break;
-
-	case SND_SSCAPE_LOAD_MCODE:
-		{
-			register const struct sscape_microcode __user *mc = (const struct sscape_microcode __user *) arg;
-
-			err = sscape_upload_microcode(sscape, mc);
-		}
-		break;
-
-	default:
-		err = -EINVAL;
-		break;
-	} /* switch */
+	release_firmware(init_fw);
 
 
 	return err;
 	return err;
 }
 }
 
 
-
 /*
 /*
  * Mixer control for the SoundScape's MIDI device.
  * Mixer control for the SoundScape's MIDI device.
  */
  */
 static int sscape_midi_info(struct snd_kcontrol *ctl,
 static int sscape_midi_info(struct snd_kcontrol *ctl,
-                            struct snd_ctl_elem_info *uinfo)
+			    struct snd_ctl_elem_info *uinfo)
 {
 {
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 	uinfo->count = 1;
 	uinfo->count = 1;
@@ -716,7 +612,7 @@ static int sscape_midi_info(struct snd_kcontrol *ctl,
 }
 }
 
 
 static int sscape_midi_get(struct snd_kcontrol *kctl,
 static int sscape_midi_get(struct snd_kcontrol *kctl,
-                           struct snd_ctl_elem_value *uctl)
+			   struct snd_ctl_elem_value *uctl)
 {
 {
 	struct snd_wss *chip = snd_kcontrol_chip(kctl);
 	struct snd_wss *chip = snd_kcontrol_chip(kctl);
 	struct snd_card *card = chip->card;
 	struct snd_card *card = chip->card;
@@ -730,16 +626,18 @@ static int sscape_midi_get(struct snd_kcontrol *kctl,
 }
 }
 
 
 static int sscape_midi_put(struct snd_kcontrol *kctl,
 static int sscape_midi_put(struct snd_kcontrol *kctl,
-                           struct snd_ctl_elem_value *uctl)
+			   struct snd_ctl_elem_value *uctl)
 {
 {
 	struct snd_wss *chip = snd_kcontrol_chip(kctl);
 	struct snd_wss *chip = snd_kcontrol_chip(kctl);
 	struct snd_card *card = chip->card;
 	struct snd_card *card = chip->card;
-	register struct soundscape *s = get_card_soundscape(card);
+	struct soundscape *s = get_card_soundscape(card);
 	unsigned long flags;
 	unsigned long flags;
 	int change;
 	int change;
+	unsigned char new_val;
 
 
 	spin_lock_irqsave(&s->lock, flags);
 	spin_lock_irqsave(&s->lock, flags);
 
 
+	new_val = uctl->value.integer.value[0] & 127;
 	/*
 	/*
 	 * We need to put the board into HOST mode before we
 	 * We need to put the board into HOST mode before we
 	 * can send any volume-changing HOST commands ...
 	 * can send any volume-changing HOST commands ...
@@ -752,15 +650,16 @@ static int sscape_midi_put(struct snd_kcontrol *kctl,
 	 * and then perform another volume-related command. Perhaps the
 	 * and then perform another volume-related command. Perhaps the
 	 * first command is an "open" and the second command is a "close"?
 	 * first command is an "open" and the second command is a "close"?
 	 */
 	 */
-	if (s->midi_vol == ((unsigned char) uctl->value.integer. value[0] & 127)) {
+	if (s->midi_vol == new_val) {
 		change = 0;
 		change = 0;
 		goto __skip_change;
 		goto __skip_change;
 	}
 	}
-	change = (host_write_ctrl_unsafe(s->io_base, CMD_SET_MIDI_VOL, 100)
-	          && host_write_ctrl_unsafe(s->io_base, ((unsigned char) uctl->value.integer. value[0]) & 127, 100)
-	          && host_write_ctrl_unsafe(s->io_base, CMD_XXX_MIDI_VOL, 100));
-	s->midi_vol = (unsigned char) uctl->value.integer.value[0] & 127;
-      __skip_change:
+	change = host_write_ctrl_unsafe(s->io_base, CMD_SET_MIDI_VOL, 100)
+		 && host_write_ctrl_unsafe(s->io_base, new_val, 100)
+		 && host_write_ctrl_unsafe(s->io_base, CMD_XXX_MIDI_VOL, 100)
+		 && host_write_ctrl_unsafe(s->io_base, new_val, 100);
+	s->midi_vol = new_val;
+__skip_change:
 
 
 	/*
 	/*
 	 * Take the board out of HOST mode and back into MIDI mode ...
 	 * Take the board out of HOST mode and back into MIDI mode ...
@@ -784,20 +683,25 @@ static struct snd_kcontrol_new midi_mixer_ctl = {
  * These IRQs are encoded as bit patterns so that they can be
  * These IRQs are encoded as bit patterns so that they can be
  * written to the control registers.
  * written to the control registers.
  */
  */
-static unsigned __devinit get_irq_config(int irq)
+static unsigned __devinit get_irq_config(int sscape_type, int irq)
 {
 {
 	static const int valid_irq[] = { 9, 5, 7, 10 };
 	static const int valid_irq[] = { 9, 5, 7, 10 };
+	static const int old_irq[] = { 9, 7, 5, 15 };
 	unsigned cfg;
 	unsigned cfg;
 
 
-	for (cfg = 0; cfg < ARRAY_SIZE(valid_irq); ++cfg) {
-		if (irq == valid_irq[cfg])
-			return cfg;
-	} /* for */
+	if (sscape_type == MEDIA_FX) {
+		for (cfg = 0; cfg < ARRAY_SIZE(old_irq); ++cfg)
+			if (irq == old_irq[cfg])
+				return cfg;
+	} else {
+		for (cfg = 0; cfg < ARRAY_SIZE(valid_irq); ++cfg)
+			if (irq == valid_irq[cfg])
+				return cfg;
+	}
 
 
 	return INVALID_IRQ;
 	return INVALID_IRQ;
 }
 }
 
 
-
 /*
 /*
  * Perform certain arcane port-checks to see whether there
  * Perform certain arcane port-checks to see whether there
  * is a SoundScape board lurking behind the given ports.
  * is a SoundScape board lurking behind the given ports.
@@ -842,11 +746,38 @@ static int __devinit detect_sscape(struct soundscape *s, long wss_io)
 	if (s->type != SSCAPE_VIVO && (d & 0x9f) != 0x0e)
 	if (s->type != SSCAPE_VIVO && (d & 0x9f) != 0x0e)
 		goto _done;
 		goto _done;
 
 
-	d  = sscape_read_unsafe(s->io_base, GA_HMCTL_REG) & 0x3f;
-	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, d | 0xc0);
+	if (s->ic_type == IC_OPUS)
+		activate_ad1845_unsafe(s->io_base);
 
 
 	if (s->type == SSCAPE_VIVO)
 	if (s->type == SSCAPE_VIVO)
 		wss_io += 4;
 		wss_io += 4;
+
+	d  = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
+	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, d | 0xc0);
+
+	/* wait for WSS codec */
+	for (d = 0; d < 500; d++) {
+		if ((inb(wss_io) & 0x80) == 0)
+			break;
+		spin_unlock_irqrestore(&s->lock, flags);
+		msleep(1);
+		spin_lock_irqsave(&s->lock, flags);
+	}
+
+	if ((inb(wss_io) & 0x80) != 0)
+		goto _done;
+
+	if (inb(wss_io + 2) == 0xff)
+		goto _done;
+
+	d  = sscape_read_unsafe(s->io_base, GA_HMCTL_REG) & 0x3f;
+	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, d);
+
+	if ((inb(wss_io) & 0x80) != 0)
+		s->type = MEDIA_FX;
+
+	d = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
+	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, d | 0xc0);
 	/* wait for WSS codec */
 	/* wait for WSS codec */
 	for (d = 0; d < 500; d++) {
 	for (d = 0; d < 500; d++) {
 		if ((inb(wss_io) & 0x80) == 0)
 		if ((inb(wss_io) & 0x80) == 0)
@@ -855,14 +786,13 @@ static int __devinit detect_sscape(struct soundscape *s, long wss_io)
 		msleep(1);
 		msleep(1);
 		spin_lock_irqsave(&s->lock, flags);
 		spin_lock_irqsave(&s->lock, flags);
 	}
 	}
-	snd_printd(KERN_INFO "init delay = %d ms\n", d);
 
 
 	/*
 	/*
 	 * SoundScape successfully detected!
 	 * SoundScape successfully detected!
 	 */
 	 */
 	retval = 1;
 	retval = 1;
 
 
-	_done:
+_done:
 	spin_unlock_irqrestore(&s->lock, flags);
 	spin_unlock_irqrestore(&s->lock, flags);
 	return retval;
 	return retval;
 }
 }
@@ -873,63 +803,35 @@ static int __devinit detect_sscape(struct soundscape *s, long wss_io)
  * to crash the machine. Also check that someone isn't using the hardware
  * to crash the machine. Also check that someone isn't using the hardware
  * IOCTL device.
  * IOCTL device.
  */
  */
-static int mpu401_open(struct snd_mpu401 * mpu)
+static int mpu401_open(struct snd_mpu401 *mpu)
 {
 {
-	int err;
-
 	if (!verify_mpu401(mpu)) {
 	if (!verify_mpu401(mpu)) {
-		snd_printk(KERN_ERR "sscape: MIDI disabled, please load firmware\n");
-		err = -ENODEV;
-	} else {
-		register struct soundscape *sscape = get_mpu401_soundscape(mpu);
-		unsigned long flags;
-
-		spin_lock_irqsave(&sscape->fwlock, flags);
-
-		if (sscape->hw_in_use || (sscape->midi_usage == ULONG_MAX)) {
-			err = -EBUSY;
-		} else {
-			++(sscape->midi_usage);
-			err = 0;
-		}
-
-		spin_unlock_irqrestore(&sscape->fwlock, flags);
+		snd_printk(KERN_ERR "sscape: MIDI disabled, "
+				    "please load firmware\n");
+		return -ENODEV;
 	}
 	}
 
 
-	return err;
-}
-
-static void mpu401_close(struct snd_mpu401 * mpu)
-{
-	register struct soundscape *sscape = get_mpu401_soundscape(mpu);
-	unsigned long flags;
-
-	spin_lock_irqsave(&sscape->fwlock, flags);
-	--(sscape->midi_usage);
-	spin_unlock_irqrestore(&sscape->fwlock, flags);
+	return 0;
 }
 }
 
 
 /*
 /*
  * Initialse an MPU-401 subdevice for MIDI support on the SoundScape.
  * Initialse an MPU-401 subdevice for MIDI support on the SoundScape.
  */
  */
-static int __devinit create_mpu401(struct snd_card *card, int devnum, unsigned long port, int irq)
+static int __devinit create_mpu401(struct snd_card *card, int devnum,
+				   unsigned long port, int irq)
 {
 {
 	struct soundscape *sscape = get_card_soundscape(card);
 	struct soundscape *sscape = get_card_soundscape(card);
 	struct snd_rawmidi *rawmidi;
 	struct snd_rawmidi *rawmidi;
 	int err;
 	int err;
 
 
-	if ((err = snd_mpu401_uart_new(card, devnum,
-	                               MPU401_HW_MPU401,
-	                               port, MPU401_INFO_INTEGRATED,
-	                               irq, IRQF_DISABLED,
-	                               &rawmidi)) == 0) {
-		struct snd_mpu401 *mpu = (struct snd_mpu401 *) rawmidi->private_data;
+	err = snd_mpu401_uart_new(card, devnum, MPU401_HW_MPU401, port,
+				  MPU401_INFO_INTEGRATED, irq, IRQF_DISABLED,
+				  &rawmidi);
+	if (err == 0) {
+		struct snd_mpu401 *mpu = rawmidi->private_data;
 		mpu->open_input = mpu401_open;
 		mpu->open_input = mpu401_open;
 		mpu->open_output = mpu401_open;
 		mpu->open_output = mpu401_open;
-		mpu->close_input = mpu401_close;
-		mpu->close_output = mpu401_close;
 		mpu->private_data = sscape;
 		mpu->private_data = sscape;
-		sscape->mpu = mpu;
 
 
 		initialise_mpu401(mpu);
 		initialise_mpu401(mpu);
 	}
 	}
@@ -950,32 +852,34 @@ static int __devinit create_ad1845(struct snd_card *card, unsigned port,
 	register struct soundscape *sscape = get_card_soundscape(card);
 	register struct soundscape *sscape = get_card_soundscape(card);
 	struct snd_wss *chip;
 	struct snd_wss *chip;
 	int err;
 	int err;
+	int codec_type = WSS_HW_DETECT;
 
 
-	if (sscape->type == SSCAPE_VIVO)
-		port += 4;
+	switch (sscape->type) {
+	case MEDIA_FX:
+	case SSCAPE:
+		/*
+		 * There are some freak examples of early Soundscape cards
+		 * with CS4231 instead of AD1848/CS4248. Unfortunately, the
+		 * CS4231 works only in CS4248 compatibility mode on
+		 * these cards so force it.
+		 */
+		if (sscape->ic_type != IC_OPUS)
+			codec_type = WSS_HW_AD1848;
+		break;
 
 
-	if (dma1 == dma2)
-		dma2 = -1;
+	case SSCAPE_VIVO:
+		port += 4;
+		break;
+	default:
+		break;
+	}
 
 
 	err = snd_wss_create(card, port, -1, irq, dma1, dma2,
 	err = snd_wss_create(card, port, -1, irq, dma1, dma2,
-			     WSS_HW_DETECT, WSS_HWSHARE_DMA1, &chip);
+			     codec_type, WSS_HWSHARE_DMA1, &chip);
 	if (!err) {
 	if (!err) {
 		unsigned long flags;
 		unsigned long flags;
 		struct snd_pcm *pcm;
 		struct snd_pcm *pcm;
 
 
-/*
- * It turns out that the PLAYBACK_ENABLE bit is set
- * by the lowlevel driver ...
- *
-#define AD1845_IFACE_CONFIG  \
-           (CS4231_AUTOCALIB | CS4231_RECORD_ENABLE | CS4231_PLAYBACK_ENABLE)
-    snd_wss_mce_up(chip);
-    spin_lock_irqsave(&chip->reg_lock, flags);
-    snd_wss_out(chip, CS4231_IFACE_CTRL, AD1845_IFACE_CONFIG);
-    spin_unlock_irqrestore(&chip->reg_lock, flags);
-    snd_wss_mce_down(chip);
- */
-
 		if (sscape->type != SSCAPE_VIVO) {
 		if (sscape->type != SSCAPE_VIVO) {
 			/*
 			/*
 			 * The input clock frequency on the SoundScape must
 			 * The input clock frequency on the SoundScape must
@@ -1022,17 +926,10 @@ static int __devinit create_ad1845(struct snd_card *card, unsigned port,
 			}
 			}
 		}
 		}
 
 
-		strcpy(card->driver, "SoundScape");
-		strcpy(card->shortname, pcm->name);
-		snprintf(card->longname, sizeof(card->longname),
-			 "%s at 0x%lx, IRQ %d, DMA1 %d, DMA2 %d\n",
-			 pcm->name, chip->port, chip->irq,
-			 chip->dma1, chip->dma2);
-
 		sscape->chip = chip;
 		sscape->chip = chip;
 	}
 	}
 
 
-	_error:
+_error:
 	return err;
 	return err;
 }
 }
 
 
@@ -1051,21 +948,8 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
 	struct resource *wss_res;
 	struct resource *wss_res;
 	unsigned long flags;
 	unsigned long flags;
 	int err;
 	int err;
-
-	/*
-	 * Check that the user didn't pass us garbage data ...
-	 */
-	irq_cfg = get_irq_config(irq[dev]);
-	if (irq_cfg == INVALID_IRQ) {
-		snd_printk(KERN_ERR "sscape: Invalid IRQ %d\n", irq[dev]);
-		return -ENXIO;
-	}
-
-	mpu_irq_cfg = get_irq_config(mpu_irq[dev]);
-	if (mpu_irq_cfg == INVALID_IRQ) {
-		printk(KERN_ERR "sscape: Invalid IRQ %d\n", mpu_irq[dev]);
-		return -ENXIO;
-	}
+	int val;
+	const char *name;
 
 
 	/*
 	/*
 	 * Grab IO ports that we will need to probe so that we
 	 * Grab IO ports that we will need to probe so that we
@@ -1098,41 +982,51 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
 	}
 	}
 
 
 	spin_lock_init(&sscape->lock);
 	spin_lock_init(&sscape->lock);
-	spin_lock_init(&sscape->fwlock);
 	sscape->io_res = io_res;
 	sscape->io_res = io_res;
 	sscape->wss_res = wss_res;
 	sscape->wss_res = wss_res;
 	sscape->io_base = port[dev];
 	sscape->io_base = port[dev];
 
 
 	if (!detect_sscape(sscape, wss_port[dev])) {
 	if (!detect_sscape(sscape, wss_port[dev])) {
-		printk(KERN_ERR "sscape: hardware not detected at 0x%x\n", sscape->io_base);
+		printk(KERN_ERR "sscape: hardware not detected at 0x%x\n",
+			sscape->io_base);
 		err = -ENODEV;
 		err = -ENODEV;
 		goto _release_dma;
 		goto _release_dma;
 	}
 	}
 
 
-	printk(KERN_INFO "sscape: hardware detected at 0x%x, using IRQ %d, DMA %d\n",
-			 sscape->io_base, irq[dev], dma[dev]);
+	switch (sscape->type) {
+	case MEDIA_FX:
+		name = "MediaFX/SoundFX";
+		break;
+	case SSCAPE:
+		name = "Soundscape";
+		break;
+	case SSCAPE_PNP:
+		name = "Soundscape PnP";
+		break;
+	case SSCAPE_VIVO:
+		name = "Soundscape VIVO";
+		break;
+	default:
+		name = "unknown Soundscape";
+		break;
+	}
 
 
-	if (sscape->type != SSCAPE_VIVO) {
-		/*
-		 * Now create the hardware-specific device so that we can
-		 * load the microcode into the on-board processor.
-		 * We cannot use the MPU-401 MIDI system until this firmware
-		 * has been loaded into the card.
-		 */
-		err = snd_hwdep_new(card, "MC68EC000", 0, &(sscape->hw));
-		if (err < 0) {
-			printk(KERN_ERR "sscape: Failed to create "
-					"firmware device\n");
-			goto _release_dma;
-		}
-		strlcpy(sscape->hw->name, "SoundScape M68K",
-			sizeof(sscape->hw->name));
-		sscape->hw->name[sizeof(sscape->hw->name) - 1] = '\0';
-		sscape->hw->iface = SNDRV_HWDEP_IFACE_SSCAPE;
-		sscape->hw->ops.open = sscape_hw_open;
-		sscape->hw->ops.release = sscape_hw_release;
-		sscape->hw->ops.ioctl = sscape_hw_ioctl;
-		sscape->hw->private_data = sscape;
+	printk(KERN_INFO "sscape: %s card detected at 0x%x, using IRQ %d, DMA %d\n",
+			 name, sscape->io_base, irq[dev], dma[dev]);
+
+	/*
+	 * Check that the user didn't pass us garbage data ...
+	 */
+	irq_cfg = get_irq_config(sscape->type, irq[dev]);
+	if (irq_cfg == INVALID_IRQ) {
+		snd_printk(KERN_ERR "sscape: Invalid IRQ %d\n", irq[dev]);
+		return -ENXIO;
+	}
+
+	mpu_irq_cfg = get_irq_config(sscape->type, mpu_irq[dev]);
+	if (mpu_irq_cfg == INVALID_IRQ) {
+		snd_printk(KERN_ERR "sscape: Invalid IRQ %d\n", mpu_irq[dev]);
+		return -ENXIO;
 	}
 	}
 
 
 	/*
 	/*
@@ -1141,9 +1035,6 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
 	 */
 	 */
 	spin_lock_irqsave(&sscape->lock, flags);
 	spin_lock_irqsave(&sscape->lock, flags);
 
 
-	activate_ad1845_unsafe(sscape->io_base);
-
-	sscape_write_unsafe(sscape->io_base, GA_INTENA_REG, 0x00); /* disable */
 	sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2e);
 	sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2e);
 	sscape_write_unsafe(sscape->io_base, GA_SMCFGB_REG, 0x00);
 	sscape_write_unsafe(sscape->io_base, GA_SMCFGB_REG, 0x00);
 
 
@@ -1151,15 +1042,23 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
 	 * Enable and configure the DMA channels ...
 	 * Enable and configure the DMA channels ...
 	 */
 	 */
 	sscape_write_unsafe(sscape->io_base, GA_DMACFG_REG, 0x50);
 	sscape_write_unsafe(sscape->io_base, GA_DMACFG_REG, 0x50);
-	dma_cfg = (sscape->ic_type == IC_ODIE ? 0x70 : 0x40);
+	dma_cfg = (sscape->ic_type == IC_OPUS ? 0x40 : 0x70);
 	sscape_write_unsafe(sscape->io_base, GA_DMAA_REG, dma_cfg);
 	sscape_write_unsafe(sscape->io_base, GA_DMAA_REG, dma_cfg);
 	sscape_write_unsafe(sscape->io_base, GA_DMAB_REG, 0x20);
 	sscape_write_unsafe(sscape->io_base, GA_DMAB_REG, 0x20);
 
 
-	sscape_write_unsafe(sscape->io_base,
-	                    GA_INTCFG_REG, 0xf0 | (mpu_irq_cfg << 2) | mpu_irq_cfg);
+	mpu_irq_cfg |= mpu_irq_cfg << 2;
+	val = sscape_read_unsafe(sscape->io_base, GA_HMCTL_REG) & 0xF7;
+	if (joystick[dev])
+		val |= 8;
+	sscape_write_unsafe(sscape->io_base, GA_HMCTL_REG, val | 0x10);
+	sscape_write_unsafe(sscape->io_base, GA_INTCFG_REG, 0xf0 | mpu_irq_cfg);
 	sscape_write_unsafe(sscape->io_base,
 	sscape_write_unsafe(sscape->io_base,
 			    GA_CDCFG_REG, 0x09 | DMA_8BIT
 			    GA_CDCFG_REG, 0x09 | DMA_8BIT
 			    | (dma[dev] << 4) | (irq_cfg << 1));
 			    | (dma[dev] << 4) | (irq_cfg << 1));
+	/*
+	 * Enable the master IRQ ...
+	 */
+	sscape_write_unsafe(sscape->io_base, GA_INTENA_REG, 0x80);
 
 
 	spin_unlock_irqrestore(&sscape->lock, flags);
 	spin_unlock_irqrestore(&sscape->lock, flags);
 
 
@@ -1170,32 +1069,56 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
 	err = create_ad1845(card, wss_port[dev], irq[dev],
 	err = create_ad1845(card, wss_port[dev], irq[dev],
 			    dma[dev], dma2[dev]);
 			    dma[dev], dma2[dev]);
 	if (err < 0) {
 	if (err < 0) {
-		printk(KERN_ERR "sscape: No AD1845 device at 0x%lx, IRQ %d\n",
-		       wss_port[dev], irq[dev]);
+		snd_printk(KERN_ERR
+				"sscape: No AD1845 device at 0x%lx, IRQ %d\n",
+				wss_port[dev], irq[dev]);
 		goto _release_dma;
 		goto _release_dma;
 	}
 	}
+	strcpy(card->driver, "SoundScape");
+	strcpy(card->shortname, name);
+	snprintf(card->longname, sizeof(card->longname),
+		 "%s at 0x%lx, IRQ %d, DMA1 %d, DMA2 %d\n",
+		 name, sscape->chip->port, sscape->chip->irq,
+		 sscape->chip->dma1, sscape->chip->dma2);
+
 #define MIDI_DEVNUM  0
 #define MIDI_DEVNUM  0
 	if (sscape->type != SSCAPE_VIVO) {
 	if (sscape->type != SSCAPE_VIVO) {
-		err = create_mpu401(card, MIDI_DEVNUM, port[dev], mpu_irq[dev]);
-		if (err < 0) {
-			printk(KERN_ERR "sscape: Failed to create "
-					"MPU-401 device at 0x%lx\n",
-					port[dev]);
-			goto _release_dma;
-		}
+		err = sscape_upload_bootblock(card);
+		if (err >= 0)
+			err = sscape_upload_microcode(card, err);
 
 
-		/*
-		 * Enable the master IRQ ...
-		 */
-		sscape_write(sscape, GA_INTENA_REG, 0x80);
+		if (err == 0) {
+			err = create_mpu401(card, MIDI_DEVNUM, port[dev],
+					    mpu_irq[dev]);
+			if (err < 0) {
+				snd_printk(KERN_ERR "sscape: Failed to create "
+						"MPU-401 device at 0x%lx\n",
+						port[dev]);
+				goto _release_dma;
+			}
 
 
-		/*
-		 * Initialize mixer
-		 */
-		sscape->midi_vol = 0;
-		host_write_ctrl_unsafe(sscape->io_base, CMD_SET_MIDI_VOL, 100);
-		host_write_ctrl_unsafe(sscape->io_base, 0, 100);
-		host_write_ctrl_unsafe(sscape->io_base, CMD_XXX_MIDI_VOL, 100);
+			/*
+			 * Initialize mixer
+			 */
+			spin_lock_irqsave(&sscape->lock, flags);
+			sscape->midi_vol = 0;
+			host_write_ctrl_unsafe(sscape->io_base,
+						CMD_SET_MIDI_VOL, 100);
+			host_write_ctrl_unsafe(sscape->io_base,
+						sscape->midi_vol, 100);
+			host_write_ctrl_unsafe(sscape->io_base,
+						CMD_XXX_MIDI_VOL, 100);
+			host_write_ctrl_unsafe(sscape->io_base,
+						sscape->midi_vol, 100);
+			host_write_ctrl_unsafe(sscape->io_base,
+						CMD_SET_EXTMIDI, 100);
+			host_write_ctrl_unsafe(sscape->io_base,
+						0, 100);
+			host_write_ctrl_unsafe(sscape->io_base, CMD_ACK, 100);
+
+			set_midi_mode_unsafe(sscape->io_base);
+			spin_unlock_irqrestore(&sscape->lock, flags);
+		}
 	}
 	}
 
 
 	/*
 	/*
@@ -1231,7 +1154,8 @@ static int __devinit snd_sscape_match(struct device *pdev, unsigned int i)
 	    mpu_irq[i] == SNDRV_AUTO_IRQ ||
 	    mpu_irq[i] == SNDRV_AUTO_IRQ ||
 	    dma[i] == SNDRV_AUTO_DMA) {
 	    dma[i] == SNDRV_AUTO_DMA) {
 		printk(KERN_INFO
 		printk(KERN_INFO
-		       "sscape: insufficient parameters, need IO, IRQ, MPU-IRQ and DMA\n");
+		       "sscape: insufficient parameters, "
+		       "need IO, IRQ, MPU-IRQ and DMA\n");
 		return 0;
 		return 0;
 	}
 	}
 
 
@@ -1253,13 +1177,15 @@ static int __devinit snd_sscape_probe(struct device *pdev, unsigned int dev)
 	sscape->type = SSCAPE;
 	sscape->type = SSCAPE;
 
 
 	dma[dev] &= 0x03;
 	dma[dev] &= 0x03;
+	snd_card_set_dev(card, pdev);
+
 	ret = create_sscape(dev, card);
 	ret = create_sscape(dev, card);
 	if (ret < 0)
 	if (ret < 0)
 		goto _release_card;
 		goto _release_card;
 
 
-	snd_card_set_dev(card, pdev);
-	if ((ret = snd_card_register(card)) < 0) {
-		printk(KERN_ERR "sscape: Failed to register sound card\n");
+	ret = snd_card_register(card);
+	if (ret < 0) {
+		snd_printk(KERN_ERR "sscape: Failed to register sound card\n");
 		goto _release_card;
 		goto _release_card;
 	}
 	}
 	dev_set_drvdata(pdev, card);
 	dev_set_drvdata(pdev, card);
@@ -1311,36 +1237,20 @@ static int __devinit sscape_pnp_detect(struct pnp_card_link *pcard,
 	 * Allow this function to fail *quietly* if all the ISA PnP
 	 * Allow this function to fail *quietly* if all the ISA PnP
 	 * devices were configured using module parameters instead.
 	 * devices were configured using module parameters instead.
 	 */
 	 */
-	if ((idx = get_next_autoindex(idx)) >= SNDRV_CARDS)
+	idx = get_next_autoindex(idx);
+	if (idx >= SNDRV_CARDS)
 		return -ENOSPC;
 		return -ENOSPC;
 
 
-	/*
-	 * We have found a candidate ISA PnP card. Now we
-	 * have to check that it has the devices that we
-	 * expect it to have.
-	 *
-	 * We will NOT try and autoconfigure all of the resources
-	 * needed and then activate the card as we are assuming that
-	 * has already been done at boot-time using /proc/isapnp.
-	 * We shall simply try to give each active card the resources
-	 * that it wants. This is a sensible strategy for a modular
-	 * system where unused modules are unloaded regularly.
-	 *
-	 * This strategy is utterly useless if we compile the driver
-	 * into the kernel, of course.
-	 */
-	// printk(KERN_INFO "sscape: %s\n", card->name);
-
 	/*
 	/*
 	 * Check that we still have room for another sound card ...
 	 * Check that we still have room for another sound card ...
 	 */
 	 */
 	dev = pnp_request_card_device(pcard, pid->devs[0].id, NULL);
 	dev = pnp_request_card_device(pcard, pid->devs[0].id, NULL);
-	if (! dev)
+	if (!dev)
 		return -ENODEV;
 		return -ENODEV;
 
 
 	if (!pnp_is_active(dev)) {
 	if (!pnp_is_active(dev)) {
 		if (pnp_activate_dev(dev) < 0) {
 		if (pnp_activate_dev(dev) < 0) {
-			printk(KERN_INFO "sscape: device is inactive\n");
+			snd_printk(KERN_INFO "sscape: device is inactive\n");
 			return -EBUSY;
 			return -EBUSY;
 		}
 		}
 	}
 	}
@@ -1378,14 +1288,15 @@ static int __devinit sscape_pnp_detect(struct pnp_card_link *pcard,
 		wss_port[idx] = pnp_port_start(dev, 1);
 		wss_port[idx] = pnp_port_start(dev, 1);
 		dma2[idx] = pnp_dma(dev, 1);
 		dma2[idx] = pnp_dma(dev, 1);
 	}
 	}
+	snd_card_set_dev(card, &pcard->card->dev);
 
 
 	ret = create_sscape(idx, card);
 	ret = create_sscape(idx, card);
 	if (ret < 0)
 	if (ret < 0)
 		goto _release_card;
 		goto _release_card;
 
 
-	snd_card_set_dev(card, &pcard->card->dev);
-	if ((ret = snd_card_register(card)) < 0) {
-		printk(KERN_ERR "sscape: Failed to register sound card\n");
+	ret = snd_card_register(card);
+	if (ret < 0) {
+		snd_printk(KERN_ERR "sscape: Failed to register sound card\n");
 		goto _release_card;
 		goto _release_card;
 	}
 	}
 
 

+ 40 - 65
sound/isa/wss/wss_lib.c

@@ -1682,7 +1682,7 @@ static void snd_wss_resume(struct snd_wss *chip)
 }
 }
 #endif /* CONFIG_PM */
 #endif /* CONFIG_PM */
 
 
-int snd_wss_free(struct snd_wss *chip)
+static int snd_wss_free(struct snd_wss *chip)
 {
 {
 	release_and_free_resource(chip->res_port);
 	release_and_free_resource(chip->res_port);
 	release_and_free_resource(chip->res_cport);
 	release_and_free_resource(chip->res_cport);
@@ -1705,7 +1705,6 @@ int snd_wss_free(struct snd_wss *chip)
 	kfree(chip);
 	kfree(chip);
 	return 0;
 	return 0;
 }
 }
-EXPORT_SYMBOL(snd_wss_free);
 
 
 static int snd_wss_dev_free(struct snd_device *device)
 static int snd_wss_dev_free(struct snd_device *device)
 {
 {
@@ -2198,84 +2197,61 @@ EXPORT_SYMBOL(snd_wss_put_double);
 static const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
 static const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
 static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
 static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
 static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
 static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
 
 
-static struct snd_kcontrol_new snd_ad1848_controls[] = {
-WSS_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT,
-	   7, 7, 1, 1),
+static struct snd_kcontrol_new snd_wss_controls[] = {
+WSS_DOUBLE("PCM Playback Switch", 0,
+		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
 WSS_DOUBLE_TLV("PCM Playback Volume", 0,
 WSS_DOUBLE_TLV("PCM Playback Volume", 0,
-	       CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1,
-	       db_scale_6bit),
+		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1,
+		db_scale_6bit),
 WSS_DOUBLE("Aux Playback Switch", 0,
 WSS_DOUBLE("Aux Playback Switch", 0,
-	   CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
+		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
 WSS_DOUBLE_TLV("Aux Playback Volume", 0,
 WSS_DOUBLE_TLV("Aux Playback Volume", 0,
-	       CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1,
-	       db_scale_5bit_12db_max),
+		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1,
+		db_scale_5bit_12db_max),
 WSS_DOUBLE("Aux Playback Switch", 1,
 WSS_DOUBLE("Aux Playback Switch", 1,
-	   CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
+		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
 WSS_DOUBLE_TLV("Aux Playback Volume", 1,
 WSS_DOUBLE_TLV("Aux Playback Volume", 1,
-	       CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1,
-	       db_scale_5bit_12db_max),
+		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1,
+		db_scale_5bit_12db_max),
 WSS_DOUBLE_TLV("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT,
 WSS_DOUBLE_TLV("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT,
 		0, 0, 15, 0, db_scale_rec_gain),
 		0, 0, 15, 0, db_scale_rec_gain),
 {
 {
-	.name = "Capture Source",
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Capture Source",
 	.info = snd_wss_info_mux,
 	.info = snd_wss_info_mux,
 	.get = snd_wss_get_mux,
 	.get = snd_wss_get_mux,
 	.put = snd_wss_put_mux,
 	.put = snd_wss_put_mux,
 },
 },
-WSS_SINGLE("Loopback Capture Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
-WSS_SINGLE_TLV("Loopback Capture Volume", 0, CS4231_LOOPBACK, 1, 63, 0,
-	       db_scale_6bit),
-};
-
-static struct snd_kcontrol_new snd_wss_controls[] = {
-WSS_DOUBLE("PCM Playback Switch", 0,
-		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
-WSS_DOUBLE("PCM Playback Volume", 0,
-		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
+WSS_DOUBLE("Mic Boost (+20dB)", 0,
+		CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0),
+WSS_SINGLE("Loopback Capture Switch", 0,
+		CS4231_LOOPBACK, 0, 1, 0),
+WSS_SINGLE_TLV("Loopback Capture Volume", 0, CS4231_LOOPBACK, 2, 63, 1,
+		db_scale_6bit),
 WSS_DOUBLE("Line Playback Switch", 0,
 WSS_DOUBLE("Line Playback Switch", 0,
 		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
 		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
-WSS_DOUBLE("Line Playback Volume", 0,
-		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
-WSS_DOUBLE("Aux Playback Switch", 0,
-		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
-WSS_DOUBLE("Aux Playback Volume", 0,
-		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
-WSS_DOUBLE("Aux Playback Switch", 1,
-		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
-WSS_DOUBLE("Aux Playback Volume", 1,
-		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
-WSS_SINGLE("Mono Playback Switch", 0,
+WSS_DOUBLE_TLV("Line Playback Volume", 0,
+		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1,
+		db_scale_5bit_12db_max),
+WSS_SINGLE("Beep Playback Switch", 0,
 		CS4231_MONO_CTRL, 7, 1, 1),
 		CS4231_MONO_CTRL, 7, 1, 1),
-WSS_SINGLE("Mono Playback Volume", 0,
-		CS4231_MONO_CTRL, 0, 15, 1),
+WSS_SINGLE_TLV("Beep Playback Volume", 0,
+		CS4231_MONO_CTRL, 0, 15, 1,
+		db_scale_4bit),
 WSS_SINGLE("Mono Output Playback Switch", 0,
 WSS_SINGLE("Mono Output Playback Switch", 0,
 		CS4231_MONO_CTRL, 6, 1, 1),
 		CS4231_MONO_CTRL, 6, 1, 1),
-WSS_SINGLE("Mono Output Playback Bypass", 0,
+WSS_SINGLE("Beep Bypass Playback Switch", 0,
 		CS4231_MONO_CTRL, 5, 1, 0),
 		CS4231_MONO_CTRL, 5, 1, 0),
-WSS_DOUBLE("Capture Volume", 0,
-		CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
-{
-	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name = "Capture Source",
-	.info = snd_wss_info_mux,
-	.get = snd_wss_get_mux,
-	.put = snd_wss_put_mux,
-},
-WSS_DOUBLE("Mic Boost", 0,
-		CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0),
-WSS_SINGLE("Loopback Capture Switch", 0,
-		CS4231_LOOPBACK, 0, 1, 0),
-WSS_SINGLE("Loopback Capture Volume", 0,
-		CS4231_LOOPBACK, 2, 63, 1)
 };
 };
 
 
 static struct snd_kcontrol_new snd_opti93x_controls[] = {
 static struct snd_kcontrol_new snd_opti93x_controls[] = {
 WSS_DOUBLE("Master Playback Switch", 0,
 WSS_DOUBLE("Master Playback Switch", 0,
 		OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1),
 		OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1),
-WSS_DOUBLE("Master Playback Volume", 0,
-		OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1),
+WSS_DOUBLE_TLV("Master Playback Volume", 0,
+		OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1,
+		db_scale_6bit),
 WSS_DOUBLE("PCM Playback Switch", 0,
 WSS_DOUBLE("PCM Playback Switch", 0,
 		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
 		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
 WSS_DOUBLE("PCM Playback Volume", 0,
 WSS_DOUBLE("PCM Playback Volume", 0,
@@ -2334,22 +2310,21 @@ int snd_wss_mixer(struct snd_wss *chip)
 			if (err < 0)
 			if (err < 0)
 				return err;
 				return err;
 		}
 		}
-	else if (chip->hardware & WSS_HW_AD1848_MASK)
-		for (idx = 0; idx < ARRAY_SIZE(snd_ad1848_controls); idx++) {
-			err = snd_ctl_add(card,
-					snd_ctl_new1(&snd_ad1848_controls[idx],
-						     chip));
-			if (err < 0)
-				return err;
-		}
-	else
-		for (idx = 0; idx < ARRAY_SIZE(snd_wss_controls); idx++) {
+	else {
+		int count = ARRAY_SIZE(snd_wss_controls);
+
+		/* Use only the first 11 entries on AD1848 */
+		if (chip->hardware & WSS_HW_AD1848_MASK)
+			count = 11;
+
+		for (idx = 0; idx < count; idx++) {
 			err = snd_ctl_add(card,
 			err = snd_ctl_add(card,
 					snd_ctl_new1(&snd_wss_controls[idx],
 					snd_ctl_new1(&snd_wss_controls[idx],
 						     chip));
 						     chip));
 			if (err < 0)
 			if (err < 0)
 				return err;
 				return err;
 		}
 		}
+	}
 	return 0;
 	return 0;
 }
 }
 EXPORT_SYMBOL(snd_wss_mixer);
 EXPORT_SYMBOL(snd_wss_mixer);

+ 0 - 12
sound/oss/Kconfig

@@ -287,18 +287,6 @@ config SOUND_DMAP
 
 
 	  Say Y unless you have 16MB or more RAM or a PCI sound card.
 	  Say Y unless you have 16MB or more RAM or a PCI sound card.
 
 
-config SOUND_SSCAPE
-	tristate "Ensoniq SoundScape support"
-	help
-	  Answer Y if you have a sound card based on the Ensoniq SoundScape
-	  chipset. Such cards are being manufactured at least by Ensoniq, Spea
-	  and Reveal (Reveal makes also other cards).
-
-	  If you compile the driver into the kernel, you have to add
-	  "sscape=<io>,<irq>,<dma>,<mpuio>,<mpuirq>" to the kernel command
-	  line.
-
-
 config SOUND_VMIDI
 config SOUND_VMIDI
 	tristate "Loopback MIDI device support"
 	tristate "Loopback MIDI device support"
 	help
 	help

+ 0 - 1
sound/oss/Makefile

@@ -13,7 +13,6 @@ obj-$(CONFIG_SOUND_SH_DAC_AUDIO)	+= sh_dac_audio.o
 obj-$(CONFIG_SOUND_AEDSP16)	+= aedsp16.o
 obj-$(CONFIG_SOUND_AEDSP16)	+= aedsp16.o
 obj-$(CONFIG_SOUND_PSS)		+= pss.o ad1848.o mpu401.o
 obj-$(CONFIG_SOUND_PSS)		+= pss.o ad1848.o mpu401.o
 obj-$(CONFIG_SOUND_TRIX)	+= trix.o ad1848.o sb_lib.o uart401.o
 obj-$(CONFIG_SOUND_TRIX)	+= trix.o ad1848.o sb_lib.o uart401.o
-obj-$(CONFIG_SOUND_SSCAPE)	+= sscape.o ad1848.o mpu401.o
 obj-$(CONFIG_SOUND_MSS)		+= ad1848.o
 obj-$(CONFIG_SOUND_MSS)		+= ad1848.o
 obj-$(CONFIG_SOUND_PAS)		+= pas2.o sb.o sb_lib.o uart401.o
 obj-$(CONFIG_SOUND_PAS)		+= pas2.o sb.o sb_lib.o uart401.o
 obj-$(CONFIG_SOUND_SB)		+= sb.o sb_lib.o uart401.o
 obj-$(CONFIG_SOUND_SB)		+= sb.o sb_lib.o uart401.o

+ 1 - 1
sound/oss/audio.c

@@ -838,7 +838,7 @@ static int dma_ioctl(int dev, unsigned int cmd, void __user *arg)
 					if ((err = audio_devs[dev]->d->prepare_for_input(dev,
 					if ((err = audio_devs[dev]->d->prepare_for_input(dev,
 						     dmap_in->fragment_size, dmap_in->nbufs)) < 0) {
 						     dmap_in->fragment_size, dmap_in->nbufs)) < 0) {
 						spin_unlock_irqrestore(&dmap_in->lock,flags);
 						spin_unlock_irqrestore(&dmap_in->lock,flags);
-						return -err;
+						return err;
 					}
 					}
 					dmap_in->dma_mode = DMODE_INPUT;
 					dmap_in->dma_mode = DMODE_INPUT;
 					audio_devs[dev]->enable_bits |= PCM_ENABLE_INPUT;
 					audio_devs[dev]->enable_bits |= PCM_ENABLE_INPUT;

+ 1 - 1
sound/oss/midi_synth.c

@@ -426,7 +426,7 @@ midi_synth_open(int dev, int mode)
 	int             err;
 	int             err;
 	struct midi_input_info *inc;
 	struct midi_input_info *inc;
 
 
-	if (orig_dev < 0 || orig_dev > num_midis || midi_devs[orig_dev] == NULL)
+	if (orig_dev < 0 || orig_dev >= num_midis || midi_devs[orig_dev] == NULL)
 		return -ENXIO;
 		return -ENXIO;
 
 
 	midi2synth[orig_dev] = dev;
 	midi2synth[orig_dev] = dev;

+ 1 - 1
sound/oss/mpu401.c

@@ -770,7 +770,7 @@ static int mpu_synth_ioctl(int dev, unsigned int cmd, void __user *arg)
 
 
 	midi_dev = synth_devs[dev]->midi_dev;
 	midi_dev = synth_devs[dev]->midi_dev;
 
 
-	if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev] == NULL)
+	if (midi_dev < 0 || midi_dev >= num_midis || midi_devs[midi_dev] == NULL)
 		return -ENXIO;
 		return -ENXIO;
 
 
 	devc = &dev_conf[midi_dev];
 	devc = &dev_conf[midi_dev];

+ 0 - 3
sound/oss/sh_dac_audio.c

@@ -164,9 +164,6 @@ static ssize_t dac_audio_write(struct file *file, const char *buf, size_t count,
 	int free;
 	int free;
 	int nbytes;
 	int nbytes;
 
 
-	if (count < 0)
-		return -EINVAL;
-
 	if (!count) {
 	if (!count) {
 		dac_audio_sync();
 		dac_audio_sync();
 		return 0;
 		return 0;

+ 0 - 1480
sound/oss/sscape.c

@@ -1,1480 +0,0 @@
-/*
- * sound/oss/sscape.c
- *
- * Low level driver for Ensoniq SoundScape
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- *
- * Thomas Sailer   	: ioctl code reworked (vmalloc/vfree removed)
- * Sergey Smitienko	: ensoniq p'n'p support
- * Christoph Hellwig	: adapted to module_init/module_exit
- * Bartlomiej Zolnierkiewicz : added __init to attach_sscape()
- * Chris Rankin		: Specify that this module owns the coprocessor
- * Arnaldo C. de Melo	: added missing restore_flags in sscape_pnp_upload_file
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-
-#include "sound_config.h"
-#include "sound_firmware.h"
-
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/fcntl.h>
-#include <linux/ctype.h>
-#include <linux/stddef.h>
-#include <linux/kmod.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-#include <linux/wait.h>
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/proc_fs.h>
-#include <linux/mm.h>
-#include <linux/spinlock.h>
-
-#include "coproc.h"
-
-#include "ad1848.h"
-#include "mpu401.h"
-
-/*
- *    I/O ports
- */
-#define MIDI_DATA       0
-#define MIDI_CTRL       1
-#define HOST_CTRL       2
-#define TX_READY	0x02
-#define RX_READY	0x01
-#define HOST_DATA       3
-#define ODIE_ADDR       4
-#define ODIE_DATA       5
-
-/*
- *    Indirect registers
- */
-
-#define GA_INTSTAT_REG	0
-#define GA_INTENA_REG	1
-#define GA_DMAA_REG	2
-#define GA_DMAB_REG	3
-#define GA_INTCFG_REG	4
-#define GA_DMACFG_REG	5
-#define GA_CDCFG_REG	6
-#define GA_SMCFGA_REG	7
-#define GA_SMCFGB_REG	8
-#define GA_HMCTL_REG	9
-
-/*
- * DMA channel identifiers (A and B)
- */
-
-#define SSCAPE_DMA_A	0
-#define SSCAPE_DMA_B	1
-
-#define PORT(name)	(devc->base+name)
-
-/*
- * Host commands recognized by the OBP microcode
- */
- 
-#define CMD_GEN_HOST_ACK	0x80
-#define CMD_GEN_MPU_ACK		0x81
-#define CMD_GET_BOARD_TYPE	0x82
-#define CMD_SET_CONTROL		0x88	/* Old firmware only */
-#define CMD_GET_CONTROL		0x89	/* Old firmware only */
-#define CTL_MASTER_VOL		0
-#define CTL_MIC_MODE		2
-#define CTL_SYNTH_VOL		4
-#define CTL_WAVE_VOL		7
-#define CMD_SET_EXTMIDI		0x8a
-#define CMD_GET_EXTMIDI		0x8b
-#define CMD_SET_MT32		0x8c
-#define CMD_GET_MT32		0x8d
-
-#define CMD_ACK			0x80
-
-#define	IC_ODIE			1
-#define	IC_OPUS			2
-
-typedef struct sscape_info
-{
-	int	base, irq, dma;
-	
-	int	codec, codec_irq;	/* required to setup pnp cards*/
-	int	codec_type;
-	int	ic_type;
-	char*	raw_buf;
-	unsigned long	raw_buf_phys;
-	int	buffsize;		/* -------------------------- */
-	spinlock_t lock;
-	int	ok;	/* Properly detected */
-	int	failed;
-	int	dma_allocated;
-	int	codec_audiodev;
-	int	opened;
-	int	*osp;
-	int	my_audiodev;
-} sscape_info;
-
-static struct sscape_info adev_info = {
-	0
-};
-
-static struct sscape_info *devc = &adev_info;
-static int sscape_mididev = -1;
-
-/* Some older cards have assigned interrupt bits differently than new ones */
-static char valid_interrupts_old[] = {
-	9, 7, 5, 15
-};
-
-static char valid_interrupts_new[] = {
-	9, 5, 7, 10
-};
-
-static char *valid_interrupts = valid_interrupts_new;
-
-/*
- *	See the bottom of the driver. This can be set by spea =0/1.
- */
- 
-#ifdef REVEAL_SPEA
-static char old_hardware = 1;
-#else
-static char old_hardware;
-#endif
-
-static void sleep(unsigned howlong)
-{
-	current->state = TASK_INTERRUPTIBLE;
-	schedule_timeout(howlong);
-}
-
-static unsigned char sscape_read(struct sscape_info *devc, int reg)
-{
-	unsigned long flags;
-	unsigned char val;
-
-	spin_lock_irqsave(&devc->lock,flags);
-	outb(reg, PORT(ODIE_ADDR));
-	val = inb(PORT(ODIE_DATA));
-	spin_unlock_irqrestore(&devc->lock,flags);
-	return val;
-}
-
-static void __sscape_write(int reg, int data)
-{
-	outb(reg, PORT(ODIE_ADDR));
-	outb(data, PORT(ODIE_DATA));
-}
-
-static void sscape_write(struct sscape_info *devc, int reg, int data)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&devc->lock,flags);
-	__sscape_write(reg, data);
-	spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static unsigned char sscape_pnp_read_codec(sscape_info* devc, unsigned char reg)
-{
-	unsigned char res;
-	unsigned long flags;
-
-	spin_lock_irqsave(&devc->lock,flags);
-	outb( reg, devc -> codec);
-	res = inb (devc -> codec + 1);
-	spin_unlock_irqrestore(&devc->lock,flags);
-	return res;
-
-}
-
-static void sscape_pnp_write_codec(sscape_info* devc, unsigned char reg, unsigned char data)
-{
-	unsigned long flags;
-	
-	spin_lock_irqsave(&devc->lock,flags);
-	outb( reg, devc -> codec);
-	outb( data, devc -> codec + 1);
-	spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static void host_open(struct sscape_info *devc)
-{
-	outb((0x00), PORT(HOST_CTRL));	/* Put the board to the host mode */
-}
-
-static void host_close(struct sscape_info *devc)
-{
-	outb((0x03), PORT(HOST_CTRL));	/* Put the board to the MIDI mode */
-}
-
-static int host_write(struct sscape_info *devc, unsigned char *data, int count)
-{
-	unsigned long flags;
-	int i, timeout_val;
-
-	spin_lock_irqsave(&devc->lock,flags);
-	/*
-	 * Send the command and data bytes
-	 */
-
-	for (i = 0; i < count; i++)
-	{
-		for (timeout_val = 10000; timeout_val > 0; timeout_val--)
-			if (inb(PORT(HOST_CTRL)) & TX_READY)
-				break;
-
-		if (timeout_val <= 0)
-		{
-				spin_unlock_irqrestore(&devc->lock,flags);
-			    return 0;
-		}
-		outb(data[i], PORT(HOST_DATA));
-	}
-	spin_unlock_irqrestore(&devc->lock,flags);
-	return 1;
-}
-
-static int host_read(struct sscape_info *devc)
-{
-	unsigned long flags;
-	int timeout_val;
-	unsigned char data;
-
-	spin_lock_irqsave(&devc->lock,flags);
-	/*
-	 * Read a byte
-	 */
-
-	for (timeout_val = 10000; timeout_val > 0; timeout_val--)
-		if (inb(PORT(HOST_CTRL)) & RX_READY)
-			break;
-
-	if (timeout_val <= 0)
-	{
-		spin_unlock_irqrestore(&devc->lock,flags);
-		return -1;
-	}
-	data = inb(PORT(HOST_DATA));
-	spin_unlock_irqrestore(&devc->lock,flags);
-	return data;
-}
-
-#if 0 /* unused */
-static int host_command1(struct sscape_info *devc, int cmd)
-{
-	unsigned char buf[10];
-	buf[0] = (unsigned char) (cmd & 0xff);
-	return host_write(devc, buf, 1);
-}
-#endif /* unused */
-
-
-static int host_command2(struct sscape_info *devc, int cmd, int parm1)
-{
-	unsigned char buf[10];
-
-	buf[0] = (unsigned char) (cmd & 0xff);
-	buf[1] = (unsigned char) (parm1 & 0xff);
-
-	return host_write(devc, buf, 2);
-}
-
-static int host_command3(struct sscape_info *devc, int cmd, int parm1, int parm2)
-{
-	unsigned char buf[10];
-
-	buf[0] = (unsigned char) (cmd & 0xff);
-	buf[1] = (unsigned char) (parm1 & 0xff);
-	buf[2] = (unsigned char) (parm2 & 0xff);
-	return host_write(devc, buf, 3);
-}
-
-static void set_mt32(struct sscape_info *devc, int value)
-{
-	host_open(devc);
-	host_command2(devc, CMD_SET_MT32, value ? 1 : 0);
-	if (host_read(devc) != CMD_ACK)
-	{
-		/* printk( "SNDSCAPE: Setting MT32 mode failed\n"); */
-	}
-	host_close(devc);
-}
-
-static void set_control(struct sscape_info *devc, int ctrl, int value)
-{
-	host_open(devc);
-	host_command3(devc, CMD_SET_CONTROL, ctrl, value);
-	if (host_read(devc) != CMD_ACK)
-	{
-		/* printk( "SNDSCAPE: Setting control (%d) failed\n",  ctrl); */
-	}
-	host_close(devc);
-}
-
-static void do_dma(struct sscape_info *devc, int dma_chan, unsigned long buf, int blk_size, int mode)
-{
-	unsigned char temp;
-
-	if (dma_chan != SSCAPE_DMA_A)
-	{
-		printk(KERN_WARNING "soundscape: Tried to use DMA channel  != A. Why?\n");
-		return;
-	}
-	audio_devs[devc->codec_audiodev]->flags &= ~DMA_AUTOMODE;
-	DMAbuf_start_dma(devc->codec_audiodev, buf, blk_size, mode);
-	audio_devs[devc->codec_audiodev]->flags |= DMA_AUTOMODE;
-
-	temp = devc->dma << 4;	/* Setup DMA channel select bits */
-	if (devc->dma <= 3)
-		temp |= 0x80;	/* 8 bit DMA channel */
-
-	temp |= 1;		/* Trigger DMA */
-	sscape_write(devc, GA_DMAA_REG, temp);
-	temp &= 0xfe;		/* Clear DMA trigger */
-	sscape_write(devc, GA_DMAA_REG, temp);
-}
-
-static int verify_mpu(struct sscape_info *devc)
-{
-	/*
-	 * The SoundScape board could be in three modes (MPU, 8250 and host).
-	 * If the card is not in the MPU mode, enabling the MPU driver will
-	 * cause infinite loop (the driver believes that there is always some
-	 * received data in the buffer.
-	 *
-	 * Detect this by looking if there are more than 10 received MIDI bytes
-	 * (0x00) in the buffer.
-	 */
-
-	int i;
-
-	for (i = 0; i < 10; i++)
-	{
-		if (inb(devc->base + HOST_CTRL) & 0x80)
-			return 1;
-
-		if (inb(devc->base) != 0x00)
-			return 1;
-	}
-	printk(KERN_WARNING "SoundScape: The device is not in the MPU-401 mode\n");
-	return 0;
-}
-
-static int sscape_coproc_open(void *dev_info, int sub_device)
-{
-	if (sub_device == COPR_MIDI)
-	{
-		set_mt32(devc, 0);
-		if (!verify_mpu(devc))
-			return -EIO;
-	}
-	return 0;
-}
-
-static void sscape_coproc_close(void *dev_info, int sub_device)
-{
-	struct sscape_info *devc = dev_info;
-	unsigned long   flags;
-
-	spin_lock_irqsave(&devc->lock,flags);
-	if (devc->dma_allocated)
-	{
-		__sscape_write(GA_DMAA_REG, 0x20);	/* DMA channel disabled */
-		devc->dma_allocated = 0;
-	}
-	spin_unlock_irqrestore(&devc->lock,flags);
-	return;
-}
-
-static void sscape_coproc_reset(void *dev_info)
-{
-}
-
-static int sscape_download_boot(struct sscape_info *devc, unsigned char *block, int size, int flag)
-{
-	unsigned long flags;
-	unsigned char temp;
-	volatile int done, timeout_val;
-	static unsigned char codec_dma_bits;
-
-	if (flag & CPF_FIRST)
-	{
-		/*
-		 * First block. Have to allocate DMA and to reset the board
-		 * before continuing.
-		 */
-
-		spin_lock_irqsave(&devc->lock,flags);
-		codec_dma_bits = sscape_read(devc, GA_CDCFG_REG);
-
-		if (devc->dma_allocated == 0)
-			devc->dma_allocated = 1;
-
-		spin_unlock_irqrestore(&devc->lock,flags);
-
-		sscape_write(devc, GA_HMCTL_REG, 
-			(temp = sscape_read(devc, GA_HMCTL_REG)) & 0x3f);	/*Reset */
-
-		for (timeout_val = 10000; timeout_val > 0; timeout_val--)
-			sscape_read(devc, GA_HMCTL_REG);	/* Delay */
-
-		/* Take board out of reset */
-		sscape_write(devc, GA_HMCTL_REG,
-			(temp = sscape_read(devc, GA_HMCTL_REG)) | 0x80);
-	}
-	/*
-	 * Transfer one code block using DMA
-	 */
-	if (audio_devs[devc->codec_audiodev]->dmap_out->raw_buf == NULL)
-	{
-		printk(KERN_WARNING "soundscape: DMA buffer not available\n");
-		return 0;
-	}
-	memcpy(audio_devs[devc->codec_audiodev]->dmap_out->raw_buf, block, size);
-
-	spin_lock_irqsave(&devc->lock,flags);
-	
-	/******** INTERRUPTS DISABLED NOW ********/
-	
-	do_dma(devc, SSCAPE_DMA_A,
-	       audio_devs[devc->codec_audiodev]->dmap_out->raw_buf_phys,
-	       size, DMA_MODE_WRITE);
-
-	/*
-	 * Wait until transfer completes.
-	 */
-	
-	done = 0;
-	timeout_val = 30;
-	while (!done && timeout_val-- > 0)
-	{
-		int resid;
-
-		if (HZ / 50)
-			sleep(HZ / 50);
-		clear_dma_ff(devc->dma);
-		if ((resid = get_dma_residue(devc->dma)) == 0)
-			done = 1;
-	}
-
-	spin_unlock_irqrestore(&devc->lock,flags);
-	if (!done)
-		return 0;
-
-	if (flag & CPF_LAST)
-	{
-		/*
-		 * Take the board out of reset
-		 */
-		outb((0x00), PORT(HOST_CTRL));
-		outb((0x00), PORT(MIDI_CTRL));
-
-		temp = sscape_read(devc, GA_HMCTL_REG);
-		temp |= 0x40;
-		sscape_write(devc, GA_HMCTL_REG, temp);	/* Kickstart the board */
-
-		/*
-		 * Wait until the ODB wakes up
-		 */
-		spin_lock_irqsave(&devc->lock,flags);
-		done = 0;
-		timeout_val = 5 * HZ;
-		while (!done && timeout_val-- > 0)
-		{
-			unsigned char x;
-			
-			sleep(1);
-			x = inb(PORT(HOST_DATA));
-			if (x == 0xff || x == 0xfe)		/* OBP startup acknowledge */
-			{
-				DDB(printk("Soundscape: Acknowledge = %x\n", x));
-				done = 1;
-			}
-		}
-		sscape_write(devc, GA_CDCFG_REG, codec_dma_bits);
-
-		spin_unlock_irqrestore(&devc->lock,flags);
-		if (!done)
-		{
-			printk(KERN_ERR "soundscape: The OBP didn't respond after code download\n");
-			return 0;
-		}
-		spin_lock_irqsave(&devc->lock,flags);
-		done = 0;
-		timeout_val = 5 * HZ;
-		while (!done && timeout_val-- > 0)
-		{
-			sleep(1);
-			if (inb(PORT(HOST_DATA)) == 0xfe)	/* Host startup acknowledge */
-				done = 1;
-		}
-		spin_unlock_irqrestore(&devc->lock,flags);
-		if (!done)
-		{
-			printk(KERN_ERR "soundscape: OBP Initialization failed.\n");
-			return 0;
-		}
-		printk(KERN_INFO "SoundScape board initialized OK\n");
-		set_control(devc, CTL_MASTER_VOL, 100);
-		set_control(devc, CTL_SYNTH_VOL, 100);
-
-#ifdef SSCAPE_DEBUG3
-		/*
-		 * Temporary debugging aid. Print contents of the registers after
-		 * downloading the code.
-		 */
-		{
-			int i;
-
-			for (i = 0; i < 13; i++)
-				printk("I%d = %02x (new value)\n", i, sscape_read(devc, i));
-		}
-#endif
-
-	}
-	return 1;
-}
-
-static int download_boot_block(void *dev_info, copr_buffer * buf)
-{
-	if (buf->len <= 0 || buf->len > sizeof(buf->data))
-		return -EINVAL;
-
-	if (!sscape_download_boot(devc, buf->data, buf->len, buf->flags))
-	{
-		printk(KERN_ERR "soundscape: Unable to load microcode block to the OBP.\n");
-		return -EIO;
-	}
-	return 0;
-}
-
-static int sscape_coproc_ioctl(void *dev_info, unsigned int cmd, void __user *arg, int local)
-{
-	copr_buffer *buf;
-	int err;
-
-	switch (cmd) 
-	{
-		case SNDCTL_COPR_RESET:
-			sscape_coproc_reset(dev_info);
-			return 0;
-
-		case SNDCTL_COPR_LOAD:
-			buf = (copr_buffer *) vmalloc(sizeof(copr_buffer));
-			if (buf == NULL)
-				return -ENOSPC;
-			if (copy_from_user(buf, arg, sizeof(copr_buffer))) 
-			{
-				vfree(buf);
-				return -EFAULT;
-			}
-			err = download_boot_block(dev_info, buf);
-			vfree(buf);
-			return err;
-		
-		default:
-			return -EINVAL;
-	}
-}
-
-static coproc_operations sscape_coproc_operations =
-{
-	"SoundScape M68K",
-	THIS_MODULE,
-	sscape_coproc_open,
-	sscape_coproc_close,
-	sscape_coproc_ioctl,
-	sscape_coproc_reset,
-	&adev_info
-};
-
-static struct resource *sscape_ports;
-static int sscape_is_pnp;
-
-static void __init attach_sscape(struct address_info *hw_config)
-{
-#ifndef SSCAPE_REGS
-	/*
-	 * Config register values for Spea/V7 Media FX and Ensoniq S-2000.
-	 * These values are card
-	 * dependent. If you have another SoundScape based card, you have to
-	 * find the correct values. Do the following:
-	 *  - Compile this driver with SSCAPE_DEBUG1 defined.
-	 *  - Shut down and power off your machine.
-	 *  - Boot with DOS so that the SSINIT.EXE program is run.
-	 *  - Warm boot to {Linux|SYSV|BSD} and write down the lines displayed
-	 *    when detecting the SoundScape.
-	 *  - Modify the following list to use the values printed during boot.
-	 *    Undefine the SSCAPE_DEBUG1
-	 */
-#define SSCAPE_REGS { \
-/* I0 */	0x00, \
-/* I1 */	0xf0, /* Note! Ignored. Set always to 0xf0 */ \
-/* I2 */	0x20, /* Note! Ignored. Set always to 0x20 */ \
-/* I3 */	0x20, /* Note! Ignored. Set always to 0x20 */ \
-/* I4 */	0xf5, /* Ignored */ \
-/* I5 */	0x10, \
-/* I6 */	0x00, \
-/* I7 */	0x2e, /* I7 MEM config A. Likely to vary between models */ \
-/* I8 */	0x00, /* I8 MEM config B. Likely to vary between models */ \
-/* I9 */	0x40 /* Ignored */ \
-	}
-#endif
-
-	unsigned long   flags;
-	static unsigned char regs[10] = SSCAPE_REGS;
-
-	int i, irq_bits = 0xff;
-
-	if (old_hardware)
-	{
-		valid_interrupts = valid_interrupts_old;
-		conf_printf("Ensoniq SoundScape (old)", hw_config);
-	}
-	else
-		conf_printf("Ensoniq SoundScape", hw_config);
-
-	for (i = 0; i < 4; i++)
-	{
-		if (hw_config->irq == valid_interrupts[i])
-		{
-			irq_bits = i;
-			break;
-		}
-	}
-	if (hw_config->irq > 15 || (regs[4] = irq_bits == 0xff))
-	{
-		printk(KERN_ERR "Invalid IRQ%d\n", hw_config->irq);
-		release_region(devc->base, 2);
-		release_region(devc->base + 2, 6);
-		if (sscape_is_pnp)
-			release_region(devc->codec, 2);
-		return;
-	}
-	
-	if (!sscape_is_pnp) {
-	
-		spin_lock_irqsave(&devc->lock,flags);
-		/* Host interrupt enable */
-		sscape_write(devc, 1, 0xf0);	/* All interrupts enabled */
-		/* DMA A status/trigger register */
-		sscape_write(devc, 2, 0x20);	/* DMA channel disabled */
-		/* DMA B status/trigger register */
-		sscape_write(devc, 3, 0x20);	/* DMA channel disabled */
-		/* Host interrupt config reg */
-		sscape_write(devc, 4, 0xf0 | (irq_bits << 2) | irq_bits);
-		/* Don't destroy CD-ROM DMA config bits (0xc0) */
-		sscape_write(devc, 5, (regs[5] & 0x3f) | (sscape_read(devc, 5) & 0xc0));
-		/* CD-ROM config (WSS codec actually) */
-		sscape_write(devc, 6, regs[6]);
-		sscape_write(devc, 7, regs[7]);
-		sscape_write(devc, 8, regs[8]);
-		/* Master control reg. Don't modify CR-ROM bits. Disable SB emul */
-		sscape_write(devc, 9, (sscape_read(devc, 9) & 0xf0) | 0x08);
-		spin_unlock_irqrestore(&devc->lock,flags);
-	}
-#ifdef SSCAPE_DEBUG2
-	/*
-	 * Temporary debugging aid. Print contents of the registers after
-	 * changing them.
-	 */
-	{
-		int i;
-
-		for (i = 0; i < 13; i++)
-			printk("I%d = %02x (new value)\n", i, sscape_read(devc, i));
-	}
-#endif
-
-	if (probe_mpu401(hw_config, sscape_ports))
-		hw_config->always_detect = 1;
-	hw_config->name = "SoundScape";
-
-	hw_config->irq *= -1;	/* Negative value signals IRQ sharing */
-	attach_mpu401(hw_config, THIS_MODULE);
-	hw_config->irq *= -1;	/* Restore it */
-
-	if (hw_config->slots[1] != -1)	/* The MPU driver installed itself */
-	{
-		sscape_mididev = hw_config->slots[1];
-		midi_devs[hw_config->slots[1]]->coproc = &sscape_coproc_operations;
-	}
-	sscape_write(devc, GA_INTENA_REG, 0x80);	/* Master IRQ enable */
-	devc->ok = 1;
-	devc->failed = 0;
-}
-
-static int detect_ga(sscape_info * devc)
-{
-	unsigned char save;
-
-	DDB(printk("Entered Soundscape detect_ga(%x)\n", devc->base));
-
-	/*
-	 * First check that the address register of "ODIE" is
-	 * there and that it has exactly 4 writable bits.
-	 * First 4 bits
-	 */
-	
-	if ((save = inb(PORT(ODIE_ADDR))) & 0xf0)
-	{
-		DDB(printk("soundscape: Detect error A\n"));
-		return 0;
-	}
-	outb((0x00), PORT(ODIE_ADDR));
-	if (inb(PORT(ODIE_ADDR)) != 0x00)
-	{
-		DDB(printk("soundscape: Detect error B\n"));
-		return 0;
-	}
-	outb((0xff), PORT(ODIE_ADDR));
-	if (inb(PORT(ODIE_ADDR)) != 0x0f)
-	{
-		DDB(printk("soundscape: Detect error C\n"));
-		return 0;
-	}
-	outb((save), PORT(ODIE_ADDR));
-
-	/*
-	 * Now verify that some indirect registers return zero on some bits.
-	 * This may break the driver with some future revisions of "ODIE" but...
-	 */
-
-	if (sscape_read(devc, 0) & 0x0c)
-	{
-		DDB(printk("soundscape: Detect error D (%x)\n", sscape_read(devc, 0)));
-		return 0;
-	}
-	if (sscape_read(devc, 1) & 0x0f)
-	{
-		DDB(printk("soundscape: Detect error E\n"));
-		return 0;
-	}
-	if (sscape_read(devc, 5) & 0x0f)
-	{
-		DDB(printk("soundscape: Detect error F\n"));
-		return 0;
-	}
-	return 1;
-}
-
-static	int sscape_read_host_ctrl(sscape_info* devc)
-{
-	return host_read(devc);
-}
-
-static	void sscape_write_host_ctrl2(sscape_info *devc, int a, int b)
-{
-	host_command2(devc, a, b);
-}
-
-static int sscape_alloc_dma(sscape_info *devc)
-{
-	char *start_addr, *end_addr;
-	int dma_pagesize;
-	int sz, size;
-	struct page *page;
-
-	if (devc->raw_buf != NULL) return 0;	/* Already done */
-	dma_pagesize = (devc->dma < 4) ? (64 * 1024) : (128 * 1024);
-	devc->raw_buf = NULL;
-	devc->buffsize = 8192*4;
-	if (devc->buffsize > dma_pagesize) devc->buffsize = dma_pagesize;
-	start_addr = NULL;
-	/*
-	 * Now loop until we get a free buffer. Try to get smaller buffer if
-	 * it fails. Don't accept smaller than 8k buffer for performance
-	 * reasons.
-	 */
-	while (start_addr == NULL && devc->buffsize > PAGE_SIZE) {
-		for (sz = 0, size = PAGE_SIZE; size < devc->buffsize; sz++, size <<= 1);
-		devc->buffsize = PAGE_SIZE * (1 << sz);
-		start_addr = (char *) __get_free_pages(GFP_ATOMIC|GFP_DMA, sz);
-		if (start_addr == NULL) devc->buffsize /= 2;
-	}
-
-	if (start_addr == NULL) {
-		printk(KERN_ERR "sscape pnp init error: Couldn't allocate DMA buffer\n");
-		return 0;
-	} else {
-		/* make some checks */
-		end_addr = start_addr + devc->buffsize - 1;		
-		/* now check if it fits into the same dma-pagesize */
-
-		if (((long) start_addr & ~(dma_pagesize - 1)) != ((long) end_addr & ~(dma_pagesize - 1))
-		    || end_addr >= (char *) (MAX_DMA_ADDRESS)) {
-			printk(KERN_ERR "sscape pnp: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, devc->buffsize);
-			return 0;
-		}
-	}
-	devc->raw_buf = start_addr;
-	devc->raw_buf_phys = virt_to_bus(start_addr);
-
-	for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++)
-		SetPageReserved(page);
-	return 1;
-}
-
-static void sscape_free_dma(sscape_info *devc)
-{
-	int sz, size;
-	unsigned long start_addr, end_addr;
-	struct page *page;
-
-	if (devc->raw_buf == NULL) return;
-	for (sz = 0, size = PAGE_SIZE; size < devc->buffsize; sz++, size <<= 1);
-	start_addr = (unsigned long) devc->raw_buf;
-	end_addr = start_addr + devc->buffsize;
-
-	for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++)
-		ClearPageReserved(page);
-
-	free_pages((unsigned long) devc->raw_buf, sz);
-	devc->raw_buf = NULL;
-}
-
-/* Intel version !!!!!!!!! */
-
-static int sscape_start_dma(int chan, unsigned long physaddr, int count, int dma_mode)
-{
-	unsigned long flags;
-
-	flags = claim_dma_lock();
-	disable_dma(chan);
-	clear_dma_ff(chan);
-	set_dma_mode(chan, dma_mode);
-	set_dma_addr(chan, physaddr);
-	set_dma_count(chan, count);
-	enable_dma(chan);
-	release_dma_lock(flags);
-	return 0;
-}
-
-static void sscape_pnp_start_dma(sscape_info* devc, int arg )
-{
-	int reg;
-	if (arg == 0) reg = 2;
-	else reg = 3;
-
-	sscape_write(devc, reg, sscape_read( devc, reg) | 0x01);
-	sscape_write(devc, reg, sscape_read( devc, reg) & 0xFE);
-}
-
-static int sscape_pnp_wait_dma (sscape_info* devc, int arg )
-{
-	int		reg;
-	unsigned long	i;
-	unsigned char	d;
-
-	if (arg == 0) reg = 2;
-	else reg = 3;
-
-	sleep ( 1 );
-	i = 0;
-	do {
-		d = sscape_read(devc, reg) & 1;
-		if ( d == 1)  break;
-		i++;
-	} while (i < 500000);
-	d = sscape_read(devc, reg) & 1; 
-	return d;
-}
-
-static	int	sscape_pnp_alloc_dma(sscape_info* devc)
-{
-	/* printk(KERN_INFO "sscape: requesting dma\n"); */
-	if (request_dma(devc -> dma, "sscape")) return 0;
-	/* printk(KERN_INFO "sscape: dma channel allocated\n"); */
-	if (!sscape_alloc_dma(devc)) {
-		free_dma(devc -> dma);
-		return 0;
-	};
-	return 1;
-}
-
-static	void	sscape_pnp_free_dma(sscape_info* devc)
-{
-	sscape_free_dma( devc);
-	free_dma(devc -> dma );	
-	/* printk(KERN_INFO "sscape: dma released\n"); */
-}
-
-static	int	sscape_pnp_upload_file(sscape_info* devc, char* fn)
-{	
-	int	     	done = 0;
-	int	     	timeout_val;
-	char*	     	data,*dt;
-	int	     	len,l;
-	unsigned long	flags;
-
-	sscape_write( devc, 9, sscape_read(devc, 9 )  & 0x3F );
-	sscape_write( devc, 2, (devc -> dma << 4) | 0x80 );
-	sscape_write( devc, 3, 0x20 );
-	sscape_write( devc, 9, sscape_read( devc, 9 )  | 0x80 );
-	
-	len = mod_firmware_load(fn, &data);
-	if (len == 0) {
-		    printk(KERN_ERR "sscape: file not found: %s\n", fn);
-		    return 0;
-	}
-	dt = data;
-	spin_lock_irqsave(&devc->lock,flags);
-	while ( len > 0 ) {
-		if (len > devc -> buffsize) l = devc->buffsize;
-		else l = len;
-		len -= l;		
-		memcpy(devc->raw_buf, dt, l); dt += l;
-		sscape_start_dma(devc->dma, devc->raw_buf_phys, l, 0x48);
-		sscape_pnp_start_dma ( devc, 0 );
-		if (sscape_pnp_wait_dma ( devc, 0 ) == 0) {
-			spin_unlock_irqrestore(&devc->lock,flags);
-			return 0;
-		}
-	}
-	
-	spin_unlock_irqrestore(&devc->lock,flags);
-	vfree(data);
-	
-	outb(0, devc -> base + 2);
-	outb(0, devc -> base);
-
-	sscape_write ( devc, 9, sscape_read( devc, 9 ) | 0x40);
-
-	timeout_val = 5 * HZ; 
-	while (!done && timeout_val-- > 0)
-	{
-		unsigned char x;
-		sleep(1);
-		x = inb( devc -> base + 3);
-		if (x == 0xff || x == 0xfe)		/* OBP startup acknowledge */
-		{
-			//printk(KERN_ERR "Soundscape: Acknowledge = %x\n", x);
-			done = 1;
-		}
-	}
-	timeout_val = 5 * HZ;
-	done = 0;
-	while (!done && timeout_val-- > 0)
-	{
-		unsigned char x;
-		sleep(1);
-		x = inb( devc -> base + 3);
-		if (x == 0xfe)		/* OBP startup acknowledge */
-		{
-			//printk(KERN_ERR "Soundscape: Acknowledge = %x\n", x);
-			done = 1;
-		}
-	}
-
-	if ( !done ) printk(KERN_ERR "soundscape: OBP Initialization failed.\n");
-
-	sscape_write( devc, 2, devc->ic_type == IC_ODIE ? 0x70 : 0x40);
-	sscape_write( devc, 3, (devc -> dma << 4) + 0x80);
-	return 1;
-}
-
-static void __init sscape_pnp_init_hw(sscape_info* devc)
-{	
-	unsigned char midi_irq = 0, sb_irq = 0;
-	unsigned i;
-	static	char code_file_name[23] = "/sndscape/sndscape.cox";
-	
-	int sscape_joystic_enable	= 0x7f;
-	int sscape_mic_enable		= 0;
-	int sscape_ext_midi		= 0;		
-
-	if ( !sscape_pnp_alloc_dma(devc) ) {
-		printk(KERN_ERR "sscape: faild to allocate dma\n");
-		return;
-	}
-
-	for (i = 0; i < 4; i++) {
-		if ( devc -> irq   == valid_interrupts[i] ) 
-			midi_irq = i;
-		if ( devc -> codec_irq == valid_interrupts[i] ) 
-			sb_irq = i;
-	}
-
-	sscape_write( devc, 5, 0x50);
-	sscape_write( devc, 7, 0x2e);
-	sscape_write( devc, 8, 0x00);
-
-	sscape_write( devc, 2, devc->ic_type == IC_ODIE ? 0x70 : 0x40);
-	sscape_write( devc, 3, ( devc -> dma << 4) | 0x80);
-
-	sscape_write (devc, 4, 0xF0 | (midi_irq<<2) | midi_irq);
-
-	i = 0x10; //sscape_read(devc, 9) & (devc->ic_type == IC_ODIE ? 0xf0 : 0xc0);
-	if (sscape_joystic_enable) i |= 8;
-	
-	sscape_write (devc, 9, i);
-	sscape_write (devc, 6, 0x80);
-	sscape_write (devc, 1, 0x80);
-
-	if (devc -> codec_type == 2) {
-		sscape_pnp_write_codec( devc, 0x0C, 0x50);
-		sscape_pnp_write_codec( devc, 0x10, sscape_pnp_read_codec( devc, 0x10) & 0x3F);
-		sscape_pnp_write_codec( devc, 0x11, sscape_pnp_read_codec( devc, 0x11) | 0xC0);
-		sscape_pnp_write_codec( devc, 29, 0x20);
-	}
-
-	if (sscape_pnp_upload_file(devc, "/sndscape/scope.cod") == 0 ) {
-		printk(KERN_ERR "sscape: faild to upload file /sndscape/scope.cod\n");
-		sscape_pnp_free_dma(devc);
-		return;
-	}
-
-	i = sscape_read_host_ctrl( devc );
-	
-	if ( (i & 0x0F) >  7 ) {
-		printk(KERN_ERR "sscape: scope.cod faild\n");
-		sscape_pnp_free_dma(devc);
-		return;
-	}
-	if ( i & 0x10 ) sscape_write( devc, 7, 0x2F);
-	code_file_name[21] = (char) ( i & 0x0F) + 0x30;
-	if (sscape_pnp_upload_file( devc, code_file_name) == 0) {
-		printk(KERN_ERR "sscape: faild to upload file %s\n", code_file_name);
-		sscape_pnp_free_dma(devc);
-		return;
-	}
-	
-	if (devc->ic_type != IC_ODIE) {
-		sscape_pnp_write_codec( devc, 10, (sscape_pnp_read_codec(devc, 10) & 0x7f) |
-		 ( sscape_mic_enable == 0 ? 0x00 : 0x80) );
-	}
-	sscape_write_host_ctrl2( devc, 0x84, 0x64 );  /* MIDI volume */
-	sscape_write_host_ctrl2( devc, 0x86, 0x64 );  /* MIDI volume?? */
-	sscape_write_host_ctrl2( devc, 0x8A, sscape_ext_midi);
-
-	sscape_pnp_write_codec ( devc, 6, 0x3f ); //WAV_VOL
-	sscape_pnp_write_codec ( devc, 7, 0x3f ); //WAV_VOL
-	sscape_pnp_write_codec ( devc, 2, 0x1F ); //WD_CDXVOLL
-	sscape_pnp_write_codec ( devc, 3, 0x1F ); //WD_CDXVOLR
-
-	if (devc -> codec_type == 1) {
-		sscape_pnp_write_codec ( devc, 4, 0x1F );
-		sscape_pnp_write_codec ( devc, 5, 0x1F );
-		sscape_write_host_ctrl2( devc, 0x88, sscape_mic_enable);
-	} else {
-		int t;
-		sscape_pnp_write_codec ( devc, 0x10, 0x1F << 1);
-		sscape_pnp_write_codec ( devc, 0x11, 0xC0 | (0x1F << 1));
-
-		t = sscape_pnp_read_codec( devc, 0x00) & 0xDF;
-		if ( (sscape_mic_enable == 0)) t |= 0;
-		else t |= 0x20;
-		sscape_pnp_write_codec ( devc, 0x00, t);
-		t = sscape_pnp_read_codec( devc, 0x01) & 0xDF;
-		if ( (sscape_mic_enable == 0) ) t |= 0;
-		else t |= 0x20;
-		sscape_pnp_write_codec ( devc, 0x01, t);
-		sscape_pnp_write_codec ( devc, 0x40 | 29 , 0x20);
-		outb(0, devc -> codec);
-	}
-	if (devc -> ic_type == IC_OPUS ) {
-		int i = sscape_read( devc, 9 );
-		sscape_write( devc, 9, i | 3 );
-		sscape_write( devc, 3, 0x40);
-
-		if (request_region(0x228, 1, "sscape setup junk")) {
-			outb(0, 0x228);
-			release_region(0x228,1);
-		}
-		sscape_write( devc, 3, (devc -> dma << 4) | 0x80);
-		sscape_write( devc, 9, i );
-	}
-	
-	host_close ( devc );
-	sscape_pnp_free_dma(devc);
-}
-
-static int __init detect_sscape_pnp(sscape_info* devc)
-{
-	long	 i, irq_bits = 0xff;
-	unsigned int d;
-
-	DDB(printk("Entered detect_sscape_pnp(%x)\n", devc->base));
-
-	if (!request_region(devc->codec, 2, "sscape codec")) {
-		printk(KERN_ERR "detect_sscape_pnp: port %x is not free\n", devc->codec);	
-		return 0;
-	}
-
-	if ((inb(devc->base + 2) & 0x78) != 0)
-		goto fail;
-
-	d = inb ( devc -> base + 4) & 0xF0;
-	if (d & 0x80)
-		goto fail;
-	
-	if (d == 0) {
-		devc->codec_type = 1;
-		devc->ic_type = IC_ODIE;
-	} else if ( (d & 0x60) != 0) {
-		devc->codec_type = 2;
-		devc->ic_type = IC_OPUS;
-	} else if ( (d & 0x40) != 0) {	/* WTF? */
-		devc->codec_type = 2;
-		devc->ic_type = IC_ODIE;
-	} else
-		goto fail;
-	
-	sscape_is_pnp = 1;
-		
-	outb(0xFA, devc -> base+4);
-	if  ((inb( devc -> base+4) & 0x9F) != 0x0A)
-		goto fail;
-	outb(0xFE, devc -> base+4);
-	if  ( (inb(devc -> base+4) & 0x9F) != 0x0E)
-		goto fail;
-	if  ( (inb(devc -> base+5) & 0x9F) != 0x0E)
-		goto fail;
-
-	if (devc->codec_type == 2) {
-		if (devc->codec != devc->base + 8) {
-			printk("soundscape warning: incorrect codec port specified\n");
-			goto fail;
-		}
-		d = 0x10 | (sscape_read(devc, 9)  & 0xCF);
-		sscape_write(devc, 9, d);
-		sscape_write(devc, 6, 0x80);
-	} else {
-		//todo: check codec is not base + 8
-	}
-
-	d  = (sscape_read(devc, 9) & 0x3F) | 0xC0;
-	sscape_write(devc, 9, d);
-
-	for (i = 0; i < 550000; i++)
-		if ( !(inb(devc -> codec) & 0x80) ) break;
-
-	d = inb(devc -> codec);
-	if (d & 0x80)
-		goto fail;
-	if ( inb(devc -> codec + 2) == 0xFF)
-		goto fail;
-
-	sscape_write(devc, 9, sscape_read(devc, 9)  & 0x3F );
-
-	d  = inb(devc -> codec) & 0x80;
-	if ( d == 0) {
-		printk(KERN_INFO "soundscape: hardware detected\n");
-		valid_interrupts = valid_interrupts_new;
-	} else	{
-		printk(KERN_INFO "soundscape: board looks like media fx\n");
-		valid_interrupts = valid_interrupts_old;
-		old_hardware = 1;
-	}
-
-	sscape_write( devc, 9, 0xC0 | (sscape_read(devc, 9)  & 0x3F) );
-
-	for (i = 0; i < 550000; i++)
-		if ( !(inb(devc -> codec) & 0x80)) 
-			break;
-		
-	sscape_pnp_init_hw(devc);
-
-	for (i = 0; i < 4; i++)
-	{
-		if (devc->codec_irq == valid_interrupts[i]) {
-			irq_bits = i;
-			break;
-		}
-	}	
-	sscape_write(devc, GA_INTENA_REG, 0x00);
-	sscape_write(devc, GA_DMACFG_REG, 0x50);
-	sscape_write(devc, GA_DMAA_REG, 0x70);
-	sscape_write(devc, GA_DMAB_REG, 0x20);
-	sscape_write(devc, GA_INTCFG_REG, 0xf0);
-	sscape_write(devc, GA_CDCFG_REG, 0x89 | (devc->dma << 4) | (irq_bits << 1));
-
-	sscape_pnp_write_codec( devc, 0, sscape_pnp_read_codec( devc, 0) | 0x20);
-	sscape_pnp_write_codec( devc, 0, sscape_pnp_read_codec( devc, 1) | 0x20);
-
-	return 1;
-fail:
-	release_region(devc->codec, 2);
-	return 0;
-}
-
-static int __init probe_sscape(struct address_info *hw_config)
-{
-	devc->base = hw_config->io_base;
-	devc->irq = hw_config->irq;
-	devc->dma = hw_config->dma;
-	devc->osp = hw_config->osp;
-
-#ifdef SSCAPE_DEBUG1
-	/*
-	 * Temporary debugging aid. Print contents of the registers before
-	 * changing them.
-	 */
-	{
-		int i;
-
-		for (i = 0; i < 13; i++)
-			printk("I%d = %02x (old value)\n", i, sscape_read(devc, i));
-	}
-#endif
-	devc->failed = 1;
-
-	sscape_ports = request_region(devc->base, 2, "mpu401");
-	if (!sscape_ports)
-		return 0;
-
-	if (!request_region(devc->base + 2, 6, "SoundScape")) {
-		release_region(devc->base, 2);
-		return 0;
-	}
-
-	if (!detect_ga(devc)) {
-		if (detect_sscape_pnp(devc))
-			return 1;
-		release_region(devc->base, 2);
-		release_region(devc->base + 2, 6);
-		return 0;
-	}
-
-	if (old_hardware)	/* Check that it's really an old Spea/Reveal card. */
-	{
-		unsigned char   tmp;
-		int             cc;
-
-		if (!((tmp = sscape_read(devc, GA_HMCTL_REG)) & 0xc0))
-		{
-			sscape_write(devc, GA_HMCTL_REG, tmp | 0x80);
-			for (cc = 0; cc < 200000; ++cc)
-				inb(devc->base + ODIE_ADDR);
-		}
-	}
-	return 1;
-}
-
-static int __init init_ss_ms_sound(struct address_info *hw_config)
-{
-	int i, irq_bits = 0xff;
-	int ad_flags = 0;
-	struct resource *ports;
-	
-	if (devc->failed)
-	{
-		printk(KERN_ERR "soundscape: Card not detected\n");
-		return 0;
-	}
-	if (devc->ok == 0)
-	{
-		printk(KERN_ERR "soundscape: Invalid initialization order.\n");
-		return 0;
-	}
-	for (i = 0; i < 4; i++)
-	{
-		if (hw_config->irq == valid_interrupts[i])
-		{
-			irq_bits = i;
-			break;
-		}
-	}
-	if (irq_bits == 0xff) {
-		printk(KERN_ERR "soundscape: Invalid MSS IRQ%d\n", hw_config->irq);
-		return 0;
-	}
-	
-	if (old_hardware)
-		ad_flags = 0x12345677;	/* Tell that we may have a CS4248 chip (Spea-V7 Media FX) */
-	else if (sscape_is_pnp)
-		ad_flags = 0x87654321;  /* Tell that we have a soundscape pnp with 1845 chip */
-
-	ports = request_region(hw_config->io_base, 4, "ad1848");
-	if (!ports) {
-		printk(KERN_ERR "soundscape: ports busy\n");
-		return 0;
-	}
-
-	if (!ad1848_detect(ports, &ad_flags, hw_config->osp)) {
-		release_region(hw_config->io_base, 4);
-		return 0;
-	}
-
- 	if (!sscape_is_pnp)  /*pnp is already setup*/
- 	{
- 		/*
-     		 * Setup the DMA polarity.
- 	    	 */
- 		sscape_write(devc, GA_DMACFG_REG, 0x50);
- 	
- 		/*
- 		 * Take the gate-array off of the DMA channel.
- 		 */
- 		sscape_write(devc, GA_DMAB_REG, 0x20);
- 	
- 		/*
- 		 * Init the AD1848 (CD-ROM) config reg.
- 		 */
- 		sscape_write(devc, GA_CDCFG_REG, 0x89 | (hw_config->dma << 4) | (irq_bits << 1));
- 	}
- 	
- 	if (hw_config->irq == devc->irq)
- 		printk(KERN_WARNING "soundscape: Warning! The WSS mode can't share IRQ with MIDI\n");
- 				
-	hw_config->slots[0] = ad1848_init(
-			sscape_is_pnp ? "SoundScape" : "SoundScape PNP",
-			ports,
-			hw_config->irq,
-			hw_config->dma,
-			hw_config->dma,
-			0,
-			devc->osp,
-			THIS_MODULE);
-
- 					  
-	if (hw_config->slots[0] != -1)	/* The AD1848 driver installed itself */
-	{
-		audio_devs[hw_config->slots[0]]->coproc = &sscape_coproc_operations;
-		devc->codec_audiodev = hw_config->slots[0];
-		devc->my_audiodev = hw_config->slots[0];
-
-		/* Set proper routings here (what are they) */
-		AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE);
-	}
-		
-#ifdef SSCAPE_DEBUG5
-	/*
-	 * Temporary debugging aid. Print contents of the registers
-	 * after the AD1848 device has been initialized.
-	 */
-	{
-		int i;
-
-		for (i = 0; i < 13; i++)
-			printk("I%d = %02x\n", i, sscape_read(devc, i));
-	}
-#endif
-	return 1;
-}
-
-static void __exit unload_sscape(struct address_info *hw_config)
-{
-	release_region(devc->base + 2, 6);
-	unload_mpu401(hw_config);
-	if (sscape_is_pnp)
-		release_region(devc->codec, 2);
-}
-
-static void __exit unload_ss_ms_sound(struct address_info *hw_config)
-{
-	ad1848_unload(hw_config->io_base,
-		      hw_config->irq,
-		      devc->dma,
-		      devc->dma,
-		      0);
-	sound_unload_audiodev(hw_config->slots[0]);
-}
-
-static struct address_info cfg;
-static struct address_info cfg_mpu;
-
-static int __initdata spea = -1;
-static int mss = 0;
-static int __initdata dma = -1;
-static int __initdata irq = -1;
-static int __initdata io = -1;
-static int __initdata mpu_irq = -1;
-static int __initdata mpu_io = -1;
-
-module_param(dma, int, 0);
-module_param(irq, int, 0);
-module_param(io, int, 0);
-module_param(spea, int, 0);		/* spea=0/1 set the old_hardware */
-module_param(mpu_irq, int, 0);
-module_param(mpu_io, int, 0);
-module_param(mss, int, 0);
-
-static int __init init_sscape(void)
-{
-	printk(KERN_INFO "Soundscape driver Copyright (C) by Hannu Savolainen 1993-1996\n");
-	
-	cfg.irq = irq;
-	cfg.dma = dma;
-	cfg.io_base = io;
-
-	cfg_mpu.irq = mpu_irq;
-	cfg_mpu.io_base = mpu_io;
-	/* WEH - Try to get right dma channel */
-        cfg_mpu.dma = dma;
-	
-	devc->codec = cfg.io_base;
-	devc->codec_irq = cfg.irq;
-	devc->codec_type = 0;
-	devc->ic_type = 0;
-	devc->raw_buf = NULL;
-	spin_lock_init(&devc->lock);
-
-	if (cfg.dma == -1 || cfg.irq == -1 || cfg.io_base == -1) {
-		printk(KERN_ERR "DMA, IRQ, and IO port must be specified.\n");
-		return -EINVAL;
-	}
-	
-	if (cfg_mpu.irq == -1 && cfg_mpu.io_base != -1) {
-		printk(KERN_ERR "MPU_IRQ must be specified if MPU_IO is set.\n");
-		return -EINVAL;
-	}
-	
-	if(spea != -1) {
-		old_hardware = spea;
-		printk(KERN_INFO "Forcing %s hardware support.\n",
-			spea?"new":"old");
-	}	
-	if (probe_sscape(&cfg_mpu) == 0)
-		return -ENODEV;
-
-	attach_sscape(&cfg_mpu);
-	
-	mss = init_ss_ms_sound(&cfg);
-
-	return 0;
-}
-
-static void __exit cleanup_sscape(void)
-{
-	if (mss)
-		unload_ss_ms_sound(&cfg);
-	unload_sscape(&cfg_mpu);
-}
-
-module_init(init_sscape);
-module_exit(cleanup_sscape);
-
-#ifndef MODULE
-static int __init setup_sscape(char *str)
-{
-	/* io, irq, dma, mpu_io, mpu_irq */
-	int ints[6];
-	
-	str = get_options(str, ARRAY_SIZE(ints), ints);
-	
-	io	= ints[1];
-	irq	= ints[2];
-	dma	= ints[3];
-	mpu_io	= ints[4];
-	mpu_irq	= ints[5];
-
-	return 1;
-}
-
-__setup("sscape=", setup_sscape);
-#endif
-MODULE_LICENSE("GPL");

+ 1 - 0
sound/pci/Kconfig

@@ -570,6 +570,7 @@ config SND_ICE1712
 	tristate "ICEnsemble ICE1712 (Envy24)"
 	tristate "ICEnsemble ICE1712 (Envy24)"
 	select SND_MPU401_UART
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
 	select SND_AC97_CODEC
+	select BITREVERSE
 	help
 	help
 	  Say Y here to include support for soundcards based on the
 	  Say Y here to include support for soundcards based on the
 	  ICE1712 (Envy24) chip.
 	  ICE1712 (Envy24) chip.

+ 3 - 3
sound/pci/ac97/ac97_codec.c

@@ -603,8 +603,8 @@ AC97_SINGLE("Tone Control - Treble", AC97_MASTER_TONE, 0, 15, 1)
 };
 };
 
 
 static const struct snd_kcontrol_new snd_ac97_controls_pc_beep[2] = {
 static const struct snd_kcontrol_new snd_ac97_controls_pc_beep[2] = {
-AC97_SINGLE("PC Speaker Playback Switch", AC97_PC_BEEP, 15, 1, 1),
-AC97_SINGLE("PC Speaker Playback Volume", AC97_PC_BEEP, 1, 15, 1)
+AC97_SINGLE("Beep Playback Switch", AC97_PC_BEEP, 15, 1, 1),
+AC97_SINGLE("Beep Playback Volume", AC97_PC_BEEP, 1, 15, 1)
 };
 };
 
 
 static const struct snd_kcontrol_new snd_ac97_controls_mic_boost =
 static const struct snd_kcontrol_new snd_ac97_controls_mic_boost =
@@ -1393,7 +1393,7 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
 		}
 		}
 	}
 	}
 	
 	
-	/* build PC Speaker controls */
+	/* build Beep controls */
 	if (!(ac97->flags & AC97_HAS_NO_PC_BEEP) && 
 	if (!(ac97->flags & AC97_HAS_NO_PC_BEEP) && 
 		((ac97->flags & AC97_HAS_PC_BEEP) ||
 		((ac97->flags & AC97_HAS_PC_BEEP) ||
 	    snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP))) {
 	    snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP))) {

+ 6 - 6
sound/pci/ac97/ac97_patch.c

@@ -800,12 +800,12 @@ AC97_SINGLE("Mono Switch", AC97_MASTER_TONE, 7, 1, 1),
 AC97_SINGLE("Mono ZC Switch", AC97_MASTER_TONE, 6, 1, 0),
 AC97_SINGLE("Mono ZC Switch", AC97_MASTER_TONE, 6, 1, 0),
 AC97_SINGLE("Mono Volume", AC97_MASTER_TONE, 0, 31, 1),
 AC97_SINGLE("Mono Volume", AC97_MASTER_TONE, 0, 31, 1),
 
 
-AC97_SINGLE("PC Beep to Headphone Switch", AC97_AUX, 15, 1, 1),
-AC97_SINGLE("PC Beep to Headphone Volume", AC97_AUX, 12, 7, 1),
-AC97_SINGLE("PC Beep to Master Switch", AC97_AUX, 11, 1, 1),
-AC97_SINGLE("PC Beep to Master Volume", AC97_AUX, 8, 7, 1),
-AC97_SINGLE("PC Beep to Mono Switch", AC97_AUX, 7, 1, 1),
-AC97_SINGLE("PC Beep to Mono Volume", AC97_AUX, 4, 7, 1),
+AC97_SINGLE("Beep to Headphone Switch", AC97_AUX, 15, 1, 1),
+AC97_SINGLE("Beep to Headphone Volume", AC97_AUX, 12, 7, 1),
+AC97_SINGLE("Beep to Master Switch", AC97_AUX, 11, 1, 1),
+AC97_SINGLE("Beep to Master Volume", AC97_AUX, 8, 7, 1),
+AC97_SINGLE("Beep to Mono Switch", AC97_AUX, 7, 1, 1),
+AC97_SINGLE("Beep to Mono Volume", AC97_AUX, 4, 7, 1),
 
 
 AC97_SINGLE("Voice to Headphone Switch", AC97_PCM, 15, 1, 1),
 AC97_SINGLE("Voice to Headphone Switch", AC97_PCM, 15, 1, 1),
 AC97_SINGLE("Voice to Headphone Volume", AC97_PCM, 12, 7, 1),
 AC97_SINGLE("Voice to Headphone Volume", AC97_PCM, 12, 7, 1),

+ 2 - 2
sound/pci/azt3328.c

@@ -830,8 +830,8 @@ static struct snd_kcontrol_new snd_azf3328_mixer_controls[] __devinitdata = {
 	AZF3328_MIXER_SWITCH("Mic Boost (+20dB)", IDX_MIXER_MIC, 6, 0),
 	AZF3328_MIXER_SWITCH("Mic Boost (+20dB)", IDX_MIXER_MIC, 6, 0),
 	AZF3328_MIXER_SWITCH("Line Playback Switch", IDX_MIXER_LINEIN, 15, 1),
 	AZF3328_MIXER_SWITCH("Line Playback Switch", IDX_MIXER_LINEIN, 15, 1),
 	AZF3328_MIXER_VOL_STEREO("Line Playback Volume", IDX_MIXER_LINEIN, 0x1f, 1),
 	AZF3328_MIXER_VOL_STEREO("Line Playback Volume", IDX_MIXER_LINEIN, 0x1f, 1),
-	AZF3328_MIXER_SWITCH("PC Speaker Playback Switch", IDX_MIXER_PCBEEP, 15, 1),
-	AZF3328_MIXER_VOL_SPECIAL("PC Speaker Playback Volume", IDX_MIXER_PCBEEP, 0x0f, 1, 1),
+	AZF3328_MIXER_SWITCH("Beep Playback Switch", IDX_MIXER_PCBEEP, 15, 1),
+	AZF3328_MIXER_VOL_SPECIAL("Beep Playback Volume", IDX_MIXER_PCBEEP, 0x0f, 1, 1),
 	AZF3328_MIXER_SWITCH("Video Playback Switch", IDX_MIXER_VIDEO, 15, 1),
 	AZF3328_MIXER_SWITCH("Video Playback Switch", IDX_MIXER_VIDEO, 15, 1),
 	AZF3328_MIXER_VOL_STEREO("Video Playback Volume", IDX_MIXER_VIDEO, 0x1f, 1),
 	AZF3328_MIXER_VOL_STEREO("Video Playback Volume", IDX_MIXER_VIDEO, 0x1f, 1),
 	AZF3328_MIXER_SWITCH("Aux Playback Switch", IDX_MIXER_AUX, 15, 1),
 	AZF3328_MIXER_SWITCH("Aux Playback Switch", IDX_MIXER_AUX, 15, 1),

+ 2 - 2
sound/pci/ca0106/ca0106_mixer.c

@@ -792,8 +792,8 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
 		"Phone Playback Volume",
 		"Phone Playback Volume",
 		"Video Playback Switch",
 		"Video Playback Switch",
 		"Video Playback Volume",
 		"Video Playback Volume",
-		"PC Speaker Playback Switch",
-		"PC Speaker Playback Volume",
+		"Beep Playback Switch",
+		"Beep Playback Volume",
 		"Mono Output Select",
 		"Mono Output Select",
 		"Capture Source",
 		"Capture Source",
 		"Capture Switch",
 		"Capture Switch",

+ 2 - 2
sound/pci/ca0106/ca0106_proc.c

@@ -304,7 +304,7 @@ static void snd_ca0106_proc_reg_write32(struct snd_info_entry *entry,
         while (!snd_info_get_line(buffer, line, sizeof(line))) {
         while (!snd_info_get_line(buffer, line, sizeof(line))) {
                 if (sscanf(line, "%x %x", &reg, &val) != 2)
                 if (sscanf(line, "%x %x", &reg, &val) != 2)
                         continue;
                         continue;
-                if ((reg < 0x40) && (reg >=0) && (val <= 0xffffffff) ) {
+		if (reg < 0x40 && val <= 0xffffffff) {
 			spin_lock_irqsave(&emu->emu_lock, flags);
 			spin_lock_irqsave(&emu->emu_lock, flags);
 			outl(val, emu->port + (reg & 0xfffffffc));
 			outl(val, emu->port + (reg & 0xfffffffc));
 			spin_unlock_irqrestore(&emu->emu_lock, flags);
 			spin_unlock_irqrestore(&emu->emu_lock, flags);
@@ -405,7 +405,7 @@ static void snd_ca0106_proc_reg_write(struct snd_info_entry *entry,
         while (!snd_info_get_line(buffer, line, sizeof(line))) {
         while (!snd_info_get_line(buffer, line, sizeof(line))) {
                 if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
                 if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
                         continue;
                         continue;
-                if ((reg < 0x80) && (reg >=0) && (val <= 0xffffffff) && (channel_id >=0) && (channel_id <= 3) )
+		if (reg < 0x80 && val <= 0xffffffff && channel_id <= 3)
                         snd_ca0106_ptr_write(emu, reg, channel_id, val);
                         snd_ca0106_ptr_write(emu, reg, channel_id, val);
         }
         }
 }
 }

+ 2 - 2
sound/pci/cmipci.c

@@ -2302,7 +2302,7 @@ static struct snd_kcontrol_new snd_cmipci_mixers[] __devinitdata = {
 	CMIPCI_SB_VOL_MONO("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31),
 	CMIPCI_SB_VOL_MONO("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31),
 	CMIPCI_SB_SW_MONO("Mic Playback Switch", 0),
 	CMIPCI_SB_SW_MONO("Mic Playback Switch", 0),
 	CMIPCI_DOUBLE("Mic Capture Switch", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0, 1, 0, 0),
 	CMIPCI_DOUBLE("Mic Capture Switch", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0, 1, 0, 0),
-	CMIPCI_SB_VOL_MONO("PC Speaker Playback Volume", SB_DSP4_SPEAKER_DEV, 6, 3),
+	CMIPCI_SB_VOL_MONO("Beep Playback Volume", SB_DSP4_SPEAKER_DEV, 6, 3),
 	CMIPCI_MIXER_VOL_STEREO("Aux Playback Volume", CM_REG_AUX_VOL, 4, 0, 15),
 	CMIPCI_MIXER_VOL_STEREO("Aux Playback Volume", CM_REG_AUX_VOL, 4, 0, 15),
 	CMIPCI_MIXER_SW_STEREO("Aux Playback Switch", CM_REG_MIXER2, CM_VAUXLM_SHIFT, CM_VAUXRM_SHIFT, 0),
 	CMIPCI_MIXER_SW_STEREO("Aux Playback Switch", CM_REG_MIXER2, CM_VAUXLM_SHIFT, CM_VAUXRM_SHIFT, 0),
 	CMIPCI_MIXER_SW_STEREO("Aux Capture Switch", CM_REG_MIXER2, CM_RAUXLEN_SHIFT, CM_RAUXREN_SHIFT, 0),
 	CMIPCI_MIXER_SW_STEREO("Aux Capture Switch", CM_REG_MIXER2, CM_RAUXLEN_SHIFT, CM_RAUXREN_SHIFT, 0),
@@ -2310,7 +2310,7 @@ static struct snd_kcontrol_new snd_cmipci_mixers[] __devinitdata = {
 	CMIPCI_MIXER_VOL_MONO("Mic Capture Volume", CM_REG_MIXER2, CM_VADMIC_SHIFT, 7),
 	CMIPCI_MIXER_VOL_MONO("Mic Capture Volume", CM_REG_MIXER2, CM_VADMIC_SHIFT, 7),
 	CMIPCI_SB_VOL_MONO("Phone Playback Volume", CM_REG_EXTENT_IND, 5, 7),
 	CMIPCI_SB_VOL_MONO("Phone Playback Volume", CM_REG_EXTENT_IND, 5, 7),
 	CMIPCI_DOUBLE("Phone Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 4, 4, 1, 0, 0),
 	CMIPCI_DOUBLE("Phone Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 4, 4, 1, 0, 0),
-	CMIPCI_DOUBLE("PC Speaker Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 3, 3, 1, 0, 0),
+	CMIPCI_DOUBLE("Beep Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 3, 3, 1, 0, 0),
 	CMIPCI_DOUBLE("Mic Boost Capture Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 0, 0, 1, 0, 0),
 	CMIPCI_DOUBLE("Mic Boost Capture Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 0, 0, 1, 0, 0),
 };
 };
 
 

+ 1 - 1
sound/pci/ctxfi/ctatc.c

@@ -240,7 +240,7 @@ static int select_rom(unsigned int pitch)
 	} else if (pitch == 0x02000000) {
 	} else if (pitch == 0x02000000) {
 		/* pitch == 2 */
 		/* pitch == 2 */
 		return 3;
 		return 3;
-	} else if (pitch >= 0x0 && pitch <= 0x08000000) {
+	} else if (pitch <= 0x08000000) {
 		/* 0 <= pitch <= 8 */
 		/* 0 <= pitch <= 8 */
 		return 0;
 		return 0;
 	} else {
 	} else {

+ 1 - 2
sound/pci/emu10k1/emu10k1x.c

@@ -1040,8 +1040,7 @@ static void snd_emu10k1x_proc_reg_write(struct snd_info_entry *entry,
 		if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
 		if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
 			continue;
 			continue;
 
 
-		if ((reg < 0x49) && (reg >= 0) && (val <= 0xffffffff) 
-		    && (channel_id >= 0) && (channel_id <= 2) )
+		if (reg < 0x49 && val <= 0xffffffff && channel_id <= 2)
 			snd_emu10k1x_ptr_write(emu, reg, channel_id, val);
 			snd_emu10k1x_ptr_write(emu, reg, channel_id, val);
 	}
 	}
 }
 }

+ 2 - 2
sound/pci/emu10k1/emumixer.c

@@ -1818,8 +1818,8 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
 		"Master Playback Switch", "Master Capture Switch",
 		"Master Playback Switch", "Master Capture Switch",
 		"Master Playback Volume", "Master Capture Volume",
 		"Master Playback Volume", "Master Capture Volume",
 		"Wave Master Playback Volume", "Master Playback Volume",
 		"Wave Master Playback Volume", "Master Playback Volume",
-		"PC Speaker Playback Switch", "PC Speaker Capture Switch",
-		"PC Speaker Playback Volume", "PC Speaker Capture Volume",
+		"Beep Playback Switch", "Beep Capture Switch",
+		"Beep Playback Volume", "Beep Capture Volume",
 		"Phone Playback Switch", "Phone Capture Switch",
 		"Phone Playback Switch", "Phone Capture Switch",
 		"Phone Playback Volume", "Phone Capture Volume",
 		"Phone Playback Volume", "Phone Capture Volume",
 		"Mic Playback Switch", "Mic Capture Switch",
 		"Mic Playback Switch", "Mic Capture Switch",

+ 2 - 2
sound/pci/emu10k1/emuproc.c

@@ -451,7 +451,7 @@ static void snd_emu_proc_io_reg_write(struct snd_info_entry *entry,
 	while (!snd_info_get_line(buffer, line, sizeof(line))) {
 	while (!snd_info_get_line(buffer, line, sizeof(line))) {
 		if (sscanf(line, "%x %x", &reg, &val) != 2)
 		if (sscanf(line, "%x %x", &reg, &val) != 2)
 			continue;
 			continue;
-		if ((reg < 0x40) && (reg >= 0) && (val <= 0xffffffff) ) {
+		if (reg < 0x40 && val <= 0xffffffff) {
 			spin_lock_irqsave(&emu->emu_lock, flags);
 			spin_lock_irqsave(&emu->emu_lock, flags);
 			outl(val, emu->port + (reg & 0xfffffffc));
 			outl(val, emu->port + (reg & 0xfffffffc));
 			spin_unlock_irqrestore(&emu->emu_lock, flags);
 			spin_unlock_irqrestore(&emu->emu_lock, flags);
@@ -527,7 +527,7 @@ static void snd_emu_proc_ptr_reg_write(struct snd_info_entry *entry,
 	while (!snd_info_get_line(buffer, line, sizeof(line))) {
 	while (!snd_info_get_line(buffer, line, sizeof(line))) {
 		if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
 		if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
 			continue;
 			continue;
-		if ((reg < 0xa0) && (reg >= 0) && (val <= 0xffffffff) && (channel_id >= 0) && (channel_id <= 3) )
+		if (reg < 0xa0 && val <= 0xffffffff && channel_id <= 3)
 			snd_ptr_write(emu, iobase, reg, channel_id, val);
 			snd_ptr_write(emu, iobase, reg, channel_id, val);
 	}
 	}
 }
 }

+ 1 - 1
sound/pci/emu10k1/io.c

@@ -256,7 +256,7 @@ int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value)
 	if (reg > 0x3f)
 	if (reg > 0x3f)
 		return 1;
 		return 1;
 	reg += 0x40; /* 0x40 upwards are registers. */
 	reg += 0x40; /* 0x40 upwards are registers. */
-	if (value < 0 || value > 0x3f) /* 0 to 0x3f are values */
+	if (value > 0x3f) /* 0 to 0x3f are values */
 		return 1;
 		return 1;
 	spin_lock_irqsave(&emu->emu_lock, flags);
 	spin_lock_irqsave(&emu->emu_lock, flags);
 	outl(reg, emu->port + A_IOCFG);
 	outl(reg, emu->port + A_IOCFG);

+ 1 - 1
sound/pci/es1938.c

@@ -1387,7 +1387,7 @@ ES1938_DOUBLE_TLV("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0,
 		  db_scale_line),
 		  db_scale_line),
 ES1938_DOUBLE_TLV("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0,
 ES1938_DOUBLE_TLV("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0,
 		  db_scale_capture),
 		  db_scale_capture),
-ES1938_SINGLE("PC Speaker Volume", 0, 0x3c, 0, 7, 0),
+ES1938_SINGLE("Beep Volume", 0, 0x3c, 0, 7, 0),
 ES1938_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0),
 ES1938_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0),
 ES1938_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
 ES1938_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
 {
 {

+ 27 - 13
sound/pci/fm801.c

@@ -55,7 +55,7 @@ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card *
  *    1 = MediaForte 256-PCS
  *    1 = MediaForte 256-PCS
  *    2 = MediaForte 256-PCPR
  *    2 = MediaForte 256-PCPR
  *    3 = MediaForte 64-PCR
  *    3 = MediaForte 64-PCR
- *   16 = setup tuner only (this is additional bit), i.e. SF-64-PCR FM card
+ *   16 = setup tuner only (this is additional bit), i.e. SF64-PCR FM card
  *  High 16-bits are video (radio) device number + 1
  *  High 16-bits are video (radio) device number + 1
  */
  */
 static int tea575x_tuner[SNDRV_CARDS];
 static int tea575x_tuner[SNDRV_CARDS];
@@ -67,7 +67,10 @@ MODULE_PARM_DESC(id, "ID string for the FM801 soundcard.");
 module_param_array(enable, bool, NULL, 0444);
 module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "Enable FM801 soundcard.");
 MODULE_PARM_DESC(enable, "Enable FM801 soundcard.");
 module_param_array(tea575x_tuner, int, NULL, 0444);
 module_param_array(tea575x_tuner, int, NULL, 0444);
-MODULE_PARM_DESC(tea575x_tuner, "Enable TEA575x tuner.");
+MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (1 = SF256-PCS, 2=SF256-PCPR, 3=SF64-PCR, +16=tuner-only).");
+
+#define TUNER_ONLY		(1<<4)
+#define TUNER_TYPE_MASK		(~TUNER_ONLY & 0xFFFF)
 
 
 /*
 /*
  *  Direct registers
  *  Direct registers
@@ -160,7 +163,7 @@ struct fm801 {
 	unsigned int multichannel: 1,	/* multichannel support */
 	unsigned int multichannel: 1,	/* multichannel support */
 		     secondary: 1;	/* secondary codec */
 		     secondary: 1;	/* secondary codec */
 	unsigned char secondary_addr;	/* address of the secondary codec */
 	unsigned char secondary_addr;	/* address of the secondary codec */
-	unsigned int tea575x_tuner;	/* tuner flags */
+	unsigned int tea575x_tuner;	/* tuner access method & flags */
 
 
 	unsigned short ply_ctrl; /* playback control */
 	unsigned short ply_ctrl; /* playback control */
 	unsigned short cap_ctrl; /* capture control */
 	unsigned short cap_ctrl; /* capture control */
@@ -1287,7 +1290,7 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
 {
 {
 	unsigned short cmdw;
 	unsigned short cmdw;
 
 
-	if (chip->tea575x_tuner & 0x0010)
+	if (chip->tea575x_tuner & TUNER_ONLY)
 		goto __ac97_ok;
 		goto __ac97_ok;
 
 
 	/* codec cold reset + AC'97 warm reset */
 	/* codec cold reset + AC'97 warm reset */
@@ -1296,11 +1299,13 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
 	udelay(100);
 	udelay(100);
 	outw(0, FM801_REG(chip, CODEC_CTRL));
 	outw(0, FM801_REG(chip, CODEC_CTRL));
 
 
-	if (wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750)) < 0) {
-		snd_printk(KERN_ERR "Primary AC'97 codec not found\n");
-		if (! resume)
-			return -EIO;
-	}
+	if (wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750)) < 0)
+		if (!resume) {
+			snd_printk(KERN_INFO "Primary AC'97 codec not found, "
+					    "assume SF64-PCR (tuner-only)\n");
+			chip->tea575x_tuner = 3 | TUNER_ONLY;
+			goto __ac97_ok;
+		}
 
 
 	if (chip->multichannel) {
 	if (chip->multichannel) {
 		if (chip->secondary_addr) {
 		if (chip->secondary_addr) {
@@ -1414,7 +1419,7 @@ static int __devinit snd_fm801_create(struct snd_card *card,
 		return err;
 		return err;
 	}
 	}
 	chip->port = pci_resource_start(pci, 0);
 	chip->port = pci_resource_start(pci, 0);
-	if ((tea575x_tuner & 0x0010) == 0) {
+	if ((tea575x_tuner & TUNER_ONLY) == 0) {
 		if (request_irq(pci->irq, snd_fm801_interrupt, IRQF_SHARED,
 		if (request_irq(pci->irq, snd_fm801_interrupt, IRQF_SHARED,
 				"FM801", chip)) {
 				"FM801", chip)) {
 			snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->irq);
 			snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->irq);
@@ -1429,6 +1434,14 @@ static int __devinit snd_fm801_create(struct snd_card *card,
 		chip->multichannel = 1;
 		chip->multichannel = 1;
 
 
 	snd_fm801_chip_init(chip, 0);
 	snd_fm801_chip_init(chip, 0);
+	/* init might set tuner access method */
+	tea575x_tuner = chip->tea575x_tuner;
+
+	if (chip->irq >= 0 && (tea575x_tuner & TUNER_ONLY)) {
+		pci_clear_master(pci);
+		free_irq(chip->irq, chip);
+		chip->irq = -1;
+	}
 
 
 	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
 	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
 		snd_fm801_free(chip);
 		snd_fm801_free(chip);
@@ -1438,12 +1451,13 @@ static int __devinit snd_fm801_create(struct snd_card *card,
 	snd_card_set_dev(card, &pci->dev);
 	snd_card_set_dev(card, &pci->dev);
 
 
 #ifdef TEA575X_RADIO
 #ifdef TEA575X_RADIO
-	if (tea575x_tuner > 0 && (tea575x_tuner & 0x000f) < 4) {
+	if ((tea575x_tuner & TUNER_TYPE_MASK) > 0 &&
+	    (tea575x_tuner & TUNER_TYPE_MASK) < 4) {
 		chip->tea.dev_nr = tea575x_tuner >> 16;
 		chip->tea.dev_nr = tea575x_tuner >> 16;
 		chip->tea.card = card;
 		chip->tea.card = card;
 		chip->tea.freq_fixup = 10700;
 		chip->tea.freq_fixup = 10700;
 		chip->tea.private_data = chip;
 		chip->tea.private_data = chip;
-		chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & 0x000f) - 1];
+		chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & TUNER_TYPE_MASK) - 1];
 		snd_tea575x_init(&chip->tea);
 		snd_tea575x_init(&chip->tea);
 	}
 	}
 #endif
 #endif
@@ -1483,7 +1497,7 @@ static int __devinit snd_card_fm801_probe(struct pci_dev *pci,
 	sprintf(card->longname, "%s at 0x%lx, irq %i",
 	sprintf(card->longname, "%s at 0x%lx, irq %i",
 		card->shortname, chip->port, chip->irq);
 		card->shortname, chip->port, chip->irq);
 
 
-	if (tea575x_tuner[dev] & 0x0010)
+	if (chip->tea575x_tuner & TUNER_ONLY)
 		goto __fm801_tuner_only;
 		goto __fm801_tuner_only;
 
 
 	if ((err = snd_fm801_pcm(chip, 0, NULL)) < 0) {
 	if ((err = snd_fm801_pcm(chip, 0, NULL)) < 0) {

+ 12 - 1
sound/pci/hda/Kconfig

@@ -38,9 +38,20 @@ config SND_HDA_INPUT_BEEP
 	  Say Y here to build a digital beep interface for HD-audio
 	  Say Y here to build a digital beep interface for HD-audio
 	  driver. This interface is used to generate digital beeps.
 	  driver. This interface is used to generate digital beeps.
 
 
+config SND_HDA_INPUT_BEEP_MODE
+	int "Digital beep registration mode (0=off, 1=on, 2=mute sw on/off)"
+	depends on SND_HDA_INPUT_BEEP=y
+	default "1"
+	range 0 2
+	help
+	  Set 0 to disable the digital beep interface for HD-audio by default.
+	  Set 1 to always enable the digital beep interface for HD-audio by
+	  default. Set 2 to control the beep device registration to input
+	  layer using a "Beep Switch" in mixer applications.
+
 config SND_HDA_INPUT_JACK
 config SND_HDA_INPUT_JACK
 	bool "Support jack plugging notification via input layer"
 	bool "Support jack plugging notification via input layer"
-	depends on INPUT=y || INPUT=SND_HDA_INTEL
+	depends on INPUT=y || INPUT=SND
 	select SND_JACK
 	select SND_JACK
 	help
 	help
 	  Say Y here to enable the jack plugging notification via
 	  Say Y here to enable the jack plugging notification via

+ 96 - 18
sound/pci/hda/hda_beep.c

@@ -113,23 +113,25 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
 	return 0;
 	return 0;
 }
 }
 
 
-int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
+static void snd_hda_do_detach(struct hda_beep *beep)
+{
+	input_unregister_device(beep->dev);
+	beep->dev = NULL;
+	cancel_work_sync(&beep->beep_work);
+	/* turn off beep for sure */
+	snd_hda_codec_write_cache(beep->codec, beep->nid, 0,
+				  AC_VERB_SET_BEEP_CONTROL, 0);
+}
+
+static int snd_hda_do_attach(struct hda_beep *beep)
 {
 {
 	struct input_dev *input_dev;
 	struct input_dev *input_dev;
-	struct hda_beep *beep;
+	struct hda_codec *codec = beep->codec;
 	int err;
 	int err;
 
 
-	if (!snd_hda_get_bool_hint(codec, "beep"))
-		return 0; /* disabled explicitly */
-
-	beep = kzalloc(sizeof(*beep), GFP_KERNEL);
-	if (beep == NULL)
-		return -ENOMEM;
-	snprintf(beep->phys, sizeof(beep->phys),
-		"card%d/codec#%d/beep0", codec->bus->card->number, codec->addr);
 	input_dev = input_allocate_device();
 	input_dev = input_allocate_device();
 	if (!input_dev) {
 	if (!input_dev) {
-		kfree(beep);
+		printk(KERN_INFO "hda_beep: unable to allocate input device\n");
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 
 
@@ -151,21 +153,96 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
 	err = input_register_device(input_dev);
 	err = input_register_device(input_dev);
 	if (err < 0) {
 	if (err < 0) {
 		input_free_device(input_dev);
 		input_free_device(input_dev);
-		kfree(beep);
+		printk(KERN_INFO "hda_beep: unable to register input device\n");
 		return err;
 		return err;
 	}
 	}
+	beep->dev = input_dev;
+	return 0;
+}
+
+static void snd_hda_do_register(struct work_struct *work)
+{
+	struct hda_beep *beep =
+		container_of(work, struct hda_beep, register_work);
+
+	mutex_lock(&beep->mutex);
+	if (beep->enabled && !beep->dev)
+		snd_hda_do_attach(beep);
+	mutex_unlock(&beep->mutex);
+}
+
+static void snd_hda_do_unregister(struct work_struct *work)
+{
+	struct hda_beep *beep =
+		container_of(work, struct hda_beep, unregister_work.work);
+
+	mutex_lock(&beep->mutex);
+	if (!beep->enabled && beep->dev)
+		snd_hda_do_detach(beep);
+	mutex_unlock(&beep->mutex);
+}
 
 
+int snd_hda_enable_beep_device(struct hda_codec *codec, int enable)
+{
+	struct hda_beep *beep = codec->beep;
+	enable = !!enable;
+	if (beep == NULL)
+		return 0;
+	if (beep->enabled != enable) {
+		beep->enabled = enable;
+		if (!enable) {
+			/* turn off beep */
+			snd_hda_codec_write_cache(beep->codec, beep->nid, 0,
+						  AC_VERB_SET_BEEP_CONTROL, 0);
+		}
+		if (beep->mode == HDA_BEEP_MODE_SWREG) {
+			if (enable) {
+				cancel_delayed_work(&beep->unregister_work);
+				schedule_work(&beep->register_work);
+			} else {
+				schedule_delayed_work(&beep->unregister_work,
+									   HZ);
+			}
+		}
+		return 1;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_enable_beep_device);
+
+int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
+{
+	struct hda_beep *beep;
+
+	if (!snd_hda_get_bool_hint(codec, "beep"))
+		return 0; /* disabled explicitly by hints */
+	if (codec->beep_mode == HDA_BEEP_MODE_OFF)
+		return 0; /* disabled by module option */
+
+	beep = kzalloc(sizeof(*beep), GFP_KERNEL);
+	if (beep == NULL)
+		return -ENOMEM;
+	snprintf(beep->phys, sizeof(beep->phys),
+		"card%d/codec#%d/beep0", codec->bus->card->number, codec->addr);
 	/* enable linear scale */
 	/* enable linear scale */
 	snd_hda_codec_write(codec, nid, 0,
 	snd_hda_codec_write(codec, nid, 0,
 		AC_VERB_SET_DIGI_CONVERT_2, 0x01);
 		AC_VERB_SET_DIGI_CONVERT_2, 0x01);
 
 
 	beep->nid = nid;
 	beep->nid = nid;
-	beep->dev = input_dev;
 	beep->codec = codec;
 	beep->codec = codec;
-	beep->enabled = 1;
+	beep->mode = codec->beep_mode;
 	codec->beep = beep;
 	codec->beep = beep;
 
 
+	INIT_WORK(&beep->register_work, &snd_hda_do_register);
+	INIT_DELAYED_WORK(&beep->unregister_work, &snd_hda_do_unregister);
 	INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
 	INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
+	mutex_init(&beep->mutex);
+
+	if (beep->mode == HDA_BEEP_MODE_ON) {
+		beep->enabled = 1;
+		snd_hda_do_register(&beep->register_work);
+	}
+
 	return 0;
 	return 0;
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device);
 EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device);
@@ -174,11 +251,12 @@ void snd_hda_detach_beep_device(struct hda_codec *codec)
 {
 {
 	struct hda_beep *beep = codec->beep;
 	struct hda_beep *beep = codec->beep;
 	if (beep) {
 	if (beep) {
-		cancel_work_sync(&beep->beep_work);
-
-		input_unregister_device(beep->dev);
-		kfree(beep);
+		cancel_work_sync(&beep->register_work);
+		cancel_delayed_work(&beep->unregister_work);
+		if (beep->enabled)
+			snd_hda_do_detach(beep);
 		codec->beep = NULL;
 		codec->beep = NULL;
+		kfree(beep);
 	}
 	}
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);
 EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);

+ 10 - 0
sound/pci/hda/hda_beep.h

@@ -24,19 +24,29 @@
 
 
 #include "hda_codec.h"
 #include "hda_codec.h"
 
 
+#define HDA_BEEP_MODE_OFF	0
+#define HDA_BEEP_MODE_ON	1
+#define HDA_BEEP_MODE_SWREG	2
+
 /* beep information */
 /* beep information */
 struct hda_beep {
 struct hda_beep {
 	struct input_dev *dev;
 	struct input_dev *dev;
 	struct hda_codec *codec;
 	struct hda_codec *codec;
+	unsigned int mode;
 	char phys[32];
 	char phys[32];
 	int tone;
 	int tone;
 	hda_nid_t nid;
 	hda_nid_t nid;
 	unsigned int enabled:1;
 	unsigned int enabled:1;
+	unsigned int request_enable:1;
 	unsigned int linear_tone:1;	/* linear tone for IDT/STAC codec */
 	unsigned int linear_tone:1;	/* linear tone for IDT/STAC codec */
+	struct work_struct register_work; /* registration work */
+	struct delayed_work unregister_work; /* unregistration work */
 	struct work_struct beep_work; /* scheduled task for beep event */
 	struct work_struct beep_work; /* scheduled task for beep event */
+	struct mutex mutex;
 };
 };
 
 
 #ifdef CONFIG_SND_HDA_INPUT_BEEP
 #ifdef CONFIG_SND_HDA_INPUT_BEEP
+int snd_hda_enable_beep_device(struct hda_codec *codec, int enable);
 int snd_hda_attach_beep_device(struct hda_codec *codec, int nid);
 int snd_hda_attach_beep_device(struct hda_codec *codec, int nid);
 void snd_hda_detach_beep_device(struct hda_codec *codec);
 void snd_hda_detach_beep_device(struct hda_codec *codec);
 #else
 #else

+ 515 - 92
sound/pci/hda/hda_codec.c

@@ -30,6 +30,7 @@
 #include <sound/tlv.h>
 #include <sound/tlv.h>
 #include <sound/initval.h>
 #include <sound/initval.h>
 #include "hda_local.h"
 #include "hda_local.h"
+#include "hda_beep.h"
 #include <sound/hda_hwdep.h>
 #include <sound/hda_hwdep.h>
 
 
 /*
 /*
@@ -93,6 +94,13 @@ static void hda_keep_power_on(struct hda_codec *codec);
 static inline void hda_keep_power_on(struct hda_codec *codec) {}
 static inline void hda_keep_power_on(struct hda_codec *codec) {}
 #endif
 #endif
 
 
+/**
+ * snd_hda_get_jack_location - Give a location string of the jack
+ * @cfg: pin default config value
+ *
+ * Parse the pin default config value and returns the string of the
+ * jack location, e.g. "Rear", "Front", etc.
+ */
 const char *snd_hda_get_jack_location(u32 cfg)
 const char *snd_hda_get_jack_location(u32 cfg)
 {
 {
 	static char *bases[7] = {
 	static char *bases[7] = {
@@ -120,6 +128,13 @@ const char *snd_hda_get_jack_location(u32 cfg)
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_get_jack_location);
 EXPORT_SYMBOL_HDA(snd_hda_get_jack_location);
 
 
+/**
+ * snd_hda_get_jack_connectivity - Give a connectivity string of the jack
+ * @cfg: pin default config value
+ *
+ * Parse the pin default config value and returns the string of the
+ * jack connectivity, i.e. external or internal connection.
+ */
 const char *snd_hda_get_jack_connectivity(u32 cfg)
 const char *snd_hda_get_jack_connectivity(u32 cfg)
 {
 {
 	static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
 	static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
@@ -128,6 +143,13 @@ const char *snd_hda_get_jack_connectivity(u32 cfg)
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_get_jack_connectivity);
 EXPORT_SYMBOL_HDA(snd_hda_get_jack_connectivity);
 
 
+/**
+ * snd_hda_get_jack_type - Give a type string of the jack
+ * @cfg: pin default config value
+ *
+ * Parse the pin default config value and returns the string of the
+ * jack type, i.e. the purpose of the jack, such as Line-Out or CD.
+ */
 const char *snd_hda_get_jack_type(u32 cfg)
 const char *snd_hda_get_jack_type(u32 cfg)
 {
 {
 	static char *jack_types[16] = {
 	static char *jack_types[16] = {
@@ -515,6 +537,7 @@ static int snd_hda_bus_dev_register(struct snd_device *device)
 	struct hda_codec *codec;
 	struct hda_codec *codec;
 	list_for_each_entry(codec, &bus->codec_list, list) {
 	list_for_each_entry(codec, &bus->codec_list, list) {
 		snd_hda_hwdep_add_sysfs(codec);
 		snd_hda_hwdep_add_sysfs(codec);
+		snd_hda_hwdep_add_power_sysfs(codec);
 	}
 	}
 	return 0;
 	return 0;
 }
 }
@@ -820,6 +843,16 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
 	return 0;
 	return 0;
 }
 }
 
 
+/**
+ * snd_hda_codec_set_pincfg - Override a pin default configuration
+ * @codec: the HDA codec
+ * @nid: NID to set the pin config
+ * @cfg: the pin default config value
+ *
+ * Override a pin default configuration value in the cache.
+ * This value can be read by snd_hda_codec_get_pincfg() in a higher
+ * priority than the real hardware value.
+ */
 int snd_hda_codec_set_pincfg(struct hda_codec *codec,
 int snd_hda_codec_set_pincfg(struct hda_codec *codec,
 			     hda_nid_t nid, unsigned int cfg)
 			     hda_nid_t nid, unsigned int cfg)
 {
 {
@@ -827,7 +860,15 @@ int snd_hda_codec_set_pincfg(struct hda_codec *codec,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_set_pincfg);
 EXPORT_SYMBOL_HDA(snd_hda_codec_set_pincfg);
 
 
-/* get the current pin config value of the given pin NID */
+/**
+ * snd_hda_codec_get_pincfg - Obtain a pin-default configuration
+ * @codec: the HDA codec
+ * @nid: NID to get the pin config
+ *
+ * Get the current pin config value of the given pin NID.
+ * If the pincfg value is cached or overridden via sysfs or driver,
+ * returns the cached value.
+ */
 unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
 unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
 {
 {
 	struct hda_pincfg *pin;
 	struct hda_pincfg *pin;
@@ -944,7 +985,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
 	mutex_init(&codec->control_mutex);
 	mutex_init(&codec->control_mutex);
 	init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
 	init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
 	init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
 	init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
-	snd_array_init(&codec->mixers, sizeof(struct snd_kcontrol *), 32);
+	snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 60);
 	snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
 	snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
 	snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
 	snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
 	if (codec->bus->modelname) {
 	if (codec->bus->modelname) {
@@ -1026,6 +1067,15 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_new);
 EXPORT_SYMBOL_HDA(snd_hda_codec_new);
 
 
+/**
+ * snd_hda_codec_configure - (Re-)configure the HD-audio codec
+ * @codec: the HDA codec
+ *
+ * Start parsing of the given codec tree and (re-)initialize the whole
+ * patch instance.
+ *
+ * Returns 0 if successful or a negative error code.
+ */
 int snd_hda_codec_configure(struct hda_codec *codec)
 int snd_hda_codec_configure(struct hda_codec *codec)
 {
 {
 	int err;
 	int err;
@@ -1088,6 +1138,11 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream);
 EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream);
 
 
+/**
+ * snd_hda_codec_cleanup_stream - clean up the codec for closing
+ * @codec: the CODEC to clean up
+ * @nid: the NID to clean up
+ */
 void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
 void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
 {
 {
 	if (!nid)
 	if (!nid)
@@ -1163,8 +1218,17 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key)
 	return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key);
 	return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key);
 }
 }
 
 
-/*
- * query AMP capabilities for the given widget and direction
+/**
+ * query_amp_caps - query AMP capabilities
+ * @codec: the HD-auio codec
+ * @nid: the NID to query
+ * @direction: either #HDA_INPUT or #HDA_OUTPUT
+ *
+ * Query AMP capabilities for the given widget and direction.
+ * Returns the obtained capability bits.
+ *
+ * When cap bits have been already read, this doesn't read again but
+ * returns the cached value.
  */
  */
 u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
 u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
 {
 {
@@ -1187,6 +1251,19 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
 }
 }
 EXPORT_SYMBOL_HDA(query_amp_caps);
 EXPORT_SYMBOL_HDA(query_amp_caps);
 
 
+/**
+ * snd_hda_override_amp_caps - Override the AMP capabilities
+ * @codec: the CODEC to clean up
+ * @nid: the NID to clean up
+ * @direction: either #HDA_INPUT or #HDA_OUTPUT
+ * @caps: the capability bits to set
+ *
+ * Override the cached AMP caps bits value by the given one.
+ * This function is useful if the driver needs to adjust the AMP ranges,
+ * e.g. limit to 0dB, etc.
+ *
+ * Returns zero if successful or a negative error code.
+ */
 int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
 int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
 			      unsigned int caps)
 			      unsigned int caps)
 {
 {
@@ -1222,6 +1299,17 @@ static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid)
 	return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
 	return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
 }
 }
 
 
+/**
+ * snd_hda_query_pin_caps - Query PIN capabilities
+ * @codec: the HD-auio codec
+ * @nid: the NID to query
+ *
+ * Query PIN capabilities for the given widget.
+ * Returns the obtained capability bits.
+ *
+ * When cap bits have been already read, this doesn't read again but
+ * returns the cached value.
+ */
 u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
 u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
 {
 {
 	return query_caps_hash(codec, nid, HDA_HASH_PINCAP_KEY(nid),
 	return query_caps_hash(codec, nid, HDA_HASH_PINCAP_KEY(nid),
@@ -1229,6 +1317,40 @@ u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
 EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
 
 
+/**
+ * snd_hda_pin_sense - execute pin sense measurement
+ * @codec: the CODEC to sense
+ * @nid: the pin NID to sense
+ *
+ * Execute necessary pin sense measurement and return its Presence Detect,
+ * Impedance, ELD Valid etc. status bits.
+ */
+u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid)
+{
+	u32 pincap = snd_hda_query_pin_caps(codec, nid);
+
+	if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
+		snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0);
+
+	return snd_hda_codec_read(codec, nid, 0,
+				  AC_VERB_GET_PIN_SENSE, 0);
+}
+EXPORT_SYMBOL_HDA(snd_hda_pin_sense);
+
+/**
+ * snd_hda_jack_detect - query pin Presence Detect status
+ * @codec: the CODEC to sense
+ * @nid: the pin NID to sense
+ *
+ * Query and return the pin's Presence Detect status.
+ */
+int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
+{
+        u32 sense = snd_hda_pin_sense(codec, nid);
+        return !!(sense & AC_PINSENSE_PRESENCE);
+}
+EXPORT_SYMBOL_HDA(snd_hda_jack_detect);
+
 /*
 /*
  * read the current volume to info
  * read the current volume to info
  * if the cache exists, read the cache value.
  * if the cache exists, read the cache value.
@@ -1269,8 +1391,15 @@ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
 	info->vol[ch] = val;
 	info->vol[ch] = val;
 }
 }
 
 
-/*
- * read AMP value.  The volume is between 0 to 0x7f, 0x80 = mute bit.
+/**
+ * snd_hda_codec_amp_read - Read AMP value
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @ch: channel (left=0 or right=1)
+ * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @index: the index value (only for input direction)
+ *
+ * Read AMP value.  The volume is between 0 to 0x7f, 0x80 = mute bit.
  */
  */
 int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
 int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
 			   int direction, int index)
 			   int direction, int index)
@@ -1283,8 +1412,18 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read);
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read);
 
 
-/*
- * update the AMP value, mask = bit mask to set, val = the value
+/**
+ * snd_hda_codec_amp_update - update the AMP value
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @ch: channel (left=0 or right=1)
+ * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
+ *
+ * Update the AMP value with a bit mask.
+ * Returns 0 if the value is unchanged, 1 if changed.
  */
  */
 int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
 int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
 			     int direction, int idx, int mask, int val)
 			     int direction, int idx, int mask, int val)
@@ -1303,8 +1442,17 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update);
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update);
 
 
-/*
- * update the AMP stereo with the same mask and value
+/**
+ * snd_hda_codec_amp_stereo - update the AMP stereo values
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
+ *
+ * Update the AMP values like snd_hda_codec_amp_update(), but for a
+ * stereo widget with the same mask and value.
  */
  */
 int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
 int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
 			     int direction, int idx, int mask, int val)
 			     int direction, int idx, int mask, int val)
@@ -1318,7 +1466,12 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
 
 
 #ifdef SND_HDA_NEEDS_RESUME
 #ifdef SND_HDA_NEEDS_RESUME
-/* resume the all amp commands from the cache */
+/**
+ * snd_hda_codec_resume_amp - Resume all AMP commands from the cache
+ * @codec: HD-audio codec
+ *
+ * Resume the all amp commands from the cache.
+ */
 void snd_hda_codec_resume_amp(struct hda_codec *codec)
 void snd_hda_codec_resume_amp(struct hda_codec *codec)
 {
 {
 	struct hda_amp_info *buffer = codec->amp_cache.buf.list;
 	struct hda_amp_info *buffer = codec->amp_cache.buf.list;
@@ -1344,7 +1497,12 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
 EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
 EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
 #endif /* SND_HDA_NEEDS_RESUME */
 #endif /* SND_HDA_NEEDS_RESUME */
 
 
-/* volume */
+/**
+ * snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
 int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
 int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
 				  struct snd_ctl_elem_info *uinfo)
 				  struct snd_ctl_elem_info *uinfo)
 {
 {
@@ -1400,6 +1558,12 @@ update_amp_value(struct hda_codec *codec, hda_nid_t nid,
 					HDA_AMP_VOLMASK, val);
 					HDA_AMP_VOLMASK, val);
 }
 }
 
 
+/**
+ * snd_hda_mixer_amp_volume_get - Get callback for a standard AMP mixer volume
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
 int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
 int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
 				 struct snd_ctl_elem_value *ucontrol)
 				 struct snd_ctl_elem_value *ucontrol)
 {
 {
@@ -1419,6 +1583,12 @@ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get);
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get);
 
 
+/**
+ * snd_hda_mixer_amp_volume_put - Put callback for a standard AMP mixer volume
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
 int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
 int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
 				 struct snd_ctl_elem_value *ucontrol)
 				 struct snd_ctl_elem_value *ucontrol)
 {
 {
@@ -1443,6 +1613,12 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_put);
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_put);
 
 
+/**
+ * snd_hda_mixer_amp_volume_put - TLV callback for a standard AMP mixer volume
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
 int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 			  unsigned int size, unsigned int __user *_tlv)
 			  unsigned int size, unsigned int __user *_tlv)
 {
 {
@@ -1472,8 +1648,16 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_tlv);
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_tlv);
 
 
-/*
- * set (static) TLV for virtual master volume; recalculated as max 0dB
+/**
+ * snd_hda_set_vmaster_tlv - Set TLV for a virtual master control
+ * @codec: HD-audio codec
+ * @nid: NID of a reference widget
+ * @dir: #HDA_INPUT or #HDA_OUTPUT
+ * @tlv: TLV data to be stored, at least 4 elements
+ *
+ * Set (static) TLV data for a virtual master volume using the AMP caps
+ * obtained from the reference NID.
+ * The volume range is recalculated as if the max volume is 0dB.
  */
  */
 void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
 void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
 			     unsigned int *tlv)
 			     unsigned int *tlv)
@@ -1507,6 +1691,13 @@ _snd_hda_find_mixer_ctl(struct hda_codec *codec,
 	return snd_ctl_find_id(codec->bus->card, &id);
 	return snd_ctl_find_id(codec->bus->card, &id);
 }
 }
 
 
+/**
+ * snd_hda_find_mixer_ctl - Find a mixer control element with the given name
+ * @codec: HD-audio codec
+ * @name: ctl id name string
+ *
+ * Get the control element with the given id string and IFACE_MIXER.
+ */
 struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
 struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
 					    const char *name)
 					    const char *name)
 {
 {
@@ -1514,30 +1705,57 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl);
 EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl);
 
 
-/* Add a control element and assign to the codec */
-int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl)
+/**
+ * snd_hda_ctl-add - Add a control element and assign to the codec
+ * @codec: HD-audio codec
+ * @nid: corresponding NID (optional)
+ * @kctl: the control element to assign
+ *
+ * Add the given control element to an array inside the codec instance.
+ * All control elements belonging to a codec are supposed to be added
+ * by this function so that a proper clean-up works at the free or
+ * reconfiguration time.
+ *
+ * If non-zero @nid is passed, the NID is assigned to the control element.
+ * The assignment is shown in the codec proc file.
+ *
+ * snd_hda_ctl_add() checks the control subdev id field whether
+ * #HDA_SUBDEV_NID_FLAG bit is set.  If set (and @nid is zero), the lower
+ * bits value is taken as the NID to assign.
+ */
+int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
+		    struct snd_kcontrol *kctl)
 {
 {
 	int err;
 	int err;
-	struct snd_kcontrol **knewp;
+	struct hda_nid_item *item;
 
 
+	if (kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) {
+		if (nid == 0)
+			nid = kctl->id.subdevice & 0xffff;
+		kctl->id.subdevice = 0;
+	}
 	err = snd_ctl_add(codec->bus->card, kctl);
 	err = snd_ctl_add(codec->bus->card, kctl);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
-	knewp = snd_array_new(&codec->mixers);
-	if (!knewp)
+	item = snd_array_new(&codec->mixers);
+	if (!item)
 		return -ENOMEM;
 		return -ENOMEM;
-	*knewp = kctl;
+	item->kctl = kctl;
+	item->nid = nid;
 	return 0;
 	return 0;
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_ctl_add);
 EXPORT_SYMBOL_HDA(snd_hda_ctl_add);
 
 
-/* Clear all controls assigned to the given codec */
+/**
+ * snd_hda_ctls_clear - Clear all controls assigned to the given codec
+ * @codec: HD-audio codec
+ */
 void snd_hda_ctls_clear(struct hda_codec *codec)
 void snd_hda_ctls_clear(struct hda_codec *codec)
 {
 {
 	int i;
 	int i;
-	struct snd_kcontrol **kctls = codec->mixers.list;
+	struct hda_nid_item *items = codec->mixers.list;
 	for (i = 0; i < codec->mixers.used; i++)
 	for (i = 0; i < codec->mixers.used; i++)
-		snd_ctl_remove(codec->bus->card, kctls[i]);
+		snd_ctl_remove(codec->bus->card, items[i].kctl);
 	snd_array_free(&codec->mixers);
 	snd_array_free(&codec->mixers);
 }
 }
 
 
@@ -1563,6 +1781,16 @@ static void hda_unlock_devices(struct snd_card *card)
 	spin_unlock(&card->files_lock);
 	spin_unlock(&card->files_lock);
 }
 }
 
 
+/**
+ * snd_hda_codec_reset - Clear all objects assigned to the codec
+ * @codec: HD-audio codec
+ *
+ * This frees the all PCM and control elements assigned to the codec, and
+ * clears the caches and restores the pin default configurations.
+ *
+ * When a device is being used, it returns -EBSY.  If successfully freed,
+ * returns zero.
+ */
 int snd_hda_codec_reset(struct hda_codec *codec)
 int snd_hda_codec_reset(struct hda_codec *codec)
 {
 {
 	struct snd_card *card = codec->bus->card;
 	struct snd_card *card = codec->bus->card;
@@ -1626,7 +1854,22 @@ int snd_hda_codec_reset(struct hda_codec *codec)
 	return 0;
 	return 0;
 }
 }
 
 
-/* create a virtual master control and add slaves */
+/**
+ * snd_hda_add_vmaster - create a virtual master control and add slaves
+ * @codec: HD-audio codec
+ * @name: vmaster control name
+ * @tlv: TLV data (optional)
+ * @slaves: slave control names (optional)
+ *
+ * Create a virtual master control with the given name.  The TLV data
+ * must be either NULL or a valid data.
+ *
+ * @slaves is a NULL-terminated array of strings, each of which is a
+ * slave control name.  All controls with these names are assigned to
+ * the new virtual master control.
+ *
+ * This function returns zero if successful or a negative error code.
+ */
 int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
 int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
 			unsigned int *tlv, const char **slaves)
 			unsigned int *tlv, const char **slaves)
 {
 {
@@ -1643,7 +1886,7 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
 	kctl = snd_ctl_make_virtual_master(name, tlv);
 	kctl = snd_ctl_make_virtual_master(name, tlv);
 	if (!kctl)
 	if (!kctl)
 		return -ENOMEM;
 		return -ENOMEM;
-	err = snd_hda_ctl_add(codec, kctl);
+	err = snd_hda_ctl_add(codec, 0, kctl);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
 	
 	
@@ -1668,7 +1911,12 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_add_vmaster);
 EXPORT_SYMBOL_HDA(snd_hda_add_vmaster);
 
 
-/* switch */
+/**
+ * snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
 int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
 int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
 				  struct snd_ctl_elem_info *uinfo)
 				  struct snd_ctl_elem_info *uinfo)
 {
 {
@@ -1682,6 +1930,12 @@ int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_info);
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_info);
 
 
+/**
+ * snd_hda_mixer_amp_switch_get - Get callback for a standard AMP mixer switch
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
 int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
 int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
 				 struct snd_ctl_elem_value *ucontrol)
 				 struct snd_ctl_elem_value *ucontrol)
 {
 {
@@ -1702,6 +1956,12 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_get);
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_get);
 
 
+/**
+ * snd_hda_mixer_amp_switch_put - Put callback for a standard AMP mixer switch
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
 int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
 int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
 				 struct snd_ctl_elem_value *ucontrol)
 				 struct snd_ctl_elem_value *ucontrol)
 {
 {
@@ -1733,6 +1993,25 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put);
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put);
 
 
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+/**
+ * snd_hda_mixer_amp_switch_put_beep - Put callback for a beep AMP switch
+ *
+ * This function calls snd_hda_enable_beep_device(), which behaves differently
+ * depending on beep_mode option.
+ */
+int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	long *valp = ucontrol->value.integer.value;
+
+	snd_hda_enable_beep_device(codec, *valp);
+	return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+}
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put_beep);
+#endif /* CONFIG_SND_HDA_INPUT_BEEP */
+
 /*
 /*
  * bound volume controls
  * bound volume controls
  *
  *
@@ -1742,6 +2021,12 @@ EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put);
 #define AMP_VAL_IDX_SHIFT	19
 #define AMP_VAL_IDX_SHIFT	19
 #define AMP_VAL_IDX_MASK	(0x0f<<19)
 #define AMP_VAL_IDX_MASK	(0x0f<<19)
 
 
+/**
+ * snd_hda_mixer_bind_switch_get - Get callback for a bound volume control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_MUTE*() macros.
+ */
 int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol,
 int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol,
 				  struct snd_ctl_elem_value *ucontrol)
 				  struct snd_ctl_elem_value *ucontrol)
 {
 {
@@ -1759,6 +2044,12 @@ int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_get);
 EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_get);
 
 
+/**
+ * snd_hda_mixer_bind_switch_put - Put callback for a bound volume control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_MUTE*() macros.
+ */
 int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
 int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
 				  struct snd_ctl_elem_value *ucontrol)
 				  struct snd_ctl_elem_value *ucontrol)
 {
 {
@@ -1783,8 +2074,11 @@ int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_put);
 EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_put);
 
 
-/*
- * generic bound volume/swtich controls
+/**
+ * snd_hda_mixer_bind_ctls_info - Info callback for a generic bound control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros.
  */
  */
 int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
 int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
 				 struct snd_ctl_elem_info *uinfo)
 				 struct snd_ctl_elem_info *uinfo)
@@ -1803,6 +2097,12 @@ int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_info);
 EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_info);
 
 
+/**
+ * snd_hda_mixer_bind_ctls_get - Get callback for a generic bound control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros.
+ */
 int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
 int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
 				struct snd_ctl_elem_value *ucontrol)
 				struct snd_ctl_elem_value *ucontrol)
 {
 {
@@ -1820,6 +2120,12 @@ int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_get);
 EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_get);
 
 
+/**
+ * snd_hda_mixer_bind_ctls_put - Put callback for a generic bound control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros.
+ */
 int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
 int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
 				struct snd_ctl_elem_value *ucontrol)
 				struct snd_ctl_elem_value *ucontrol)
 {
 {
@@ -1843,6 +2149,12 @@ int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_put);
 EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_put);
 
 
+/**
+ * snd_hda_mixer_bind_tlv - TLV callback for a generic bound control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_VOL() macro.
+ */
 int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 			   unsigned int size, unsigned int __user *tlv)
 			   unsigned int size, unsigned int __user *tlv)
 {
 {
@@ -2126,7 +2438,7 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
 			return -ENOMEM;
 			return -ENOMEM;
 		kctl->id.index = idx;
 		kctl->id.index = idx;
 		kctl->private_value = nid;
 		kctl->private_value = nid;
-		err = snd_hda_ctl_add(codec, kctl);
+		err = snd_hda_ctl_add(codec, nid, kctl);
 		if (err < 0)
 		if (err < 0)
 			return err;
 			return err;
 	}
 	}
@@ -2165,14 +2477,19 @@ static struct snd_kcontrol_new spdif_share_sw = {
 	.put = spdif_share_sw_put,
 	.put = spdif_share_sw_put,
 };
 };
 
 
+/**
+ * snd_hda_create_spdif_share_sw - create Default PCM switch
+ * @codec: the HDA codec
+ * @mout: multi-out instance
+ */
 int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
 int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
 				  struct hda_multi_out *mout)
 				  struct hda_multi_out *mout)
 {
 {
 	if (!mout->dig_out_nid)
 	if (!mout->dig_out_nid)
 		return 0;
 		return 0;
 	/* ATTENTION: here mout is passed as private_data, instead of codec */
 	/* ATTENTION: here mout is passed as private_data, instead of codec */
-	return snd_hda_ctl_add(codec,
-			   snd_ctl_new1(&spdif_share_sw, mout));
+	return snd_hda_ctl_add(codec, mout->dig_out_nid,
+			      snd_ctl_new1(&spdif_share_sw, mout));
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_create_spdif_share_sw);
 EXPORT_SYMBOL_HDA(snd_hda_create_spdif_share_sw);
 
 
@@ -2276,7 +2593,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
 		if (!kctl)
 		if (!kctl)
 			return -ENOMEM;
 			return -ENOMEM;
 		kctl->private_value = nid;
 		kctl->private_value = nid;
-		err = snd_hda_ctl_add(codec, kctl);
+		err = snd_hda_ctl_add(codec, nid, kctl);
 		if (err < 0)
 		if (err < 0)
 			return err;
 			return err;
 	}
 	}
@@ -2332,7 +2649,12 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache);
 EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache);
 
 
-/* resume the all commands from the cache */
+/**
+ * snd_hda_codec_resume_cache - Resume the all commands from the cache
+ * @codec: HD-audio codec
+ *
+ * Execute all verbs recorded in the command caches to resume.
+ */
 void snd_hda_codec_resume_cache(struct hda_codec *codec)
 void snd_hda_codec_resume_cache(struct hda_codec *codec)
 {
 {
 	struct hda_cache_head *buffer = codec->cmd_cache.buf.list;
 	struct hda_cache_head *buffer = codec->cmd_cache.buf.list;
@@ -2452,9 +2774,11 @@ static void hda_call_codec_suspend(struct hda_codec *codec)
 			    codec->afg ? codec->afg : codec->mfg,
 			    codec->afg ? codec->afg : codec->mfg,
 			    AC_PWRST_D3);
 			    AC_PWRST_D3);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #ifdef CONFIG_SND_HDA_POWER_SAVE
+	snd_hda_update_power_acct(codec);
 	cancel_delayed_work(&codec->power_work);
 	cancel_delayed_work(&codec->power_work);
 	codec->power_on = 0;
 	codec->power_on = 0;
 	codec->power_transition = 0;
 	codec->power_transition = 0;
+	codec->power_jiffies = jiffies;
 #endif
 #endif
 }
 }
 
 
@@ -2756,8 +3080,12 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
 }
 }
 
 
 /**
 /**
- * snd_hda_is_supported_format - check whether the given node supports
- * the format val
+ * snd_hda_is_supported_format - Check the validity of the format
+ * @codec: HD-audio codec
+ * @nid: NID to check
+ * @format: the HD-audio format value to check
+ *
+ * Check whether the given node supports the format value.
  *
  *
  * Returns 1 if supported, 0 if not.
  * Returns 1 if supported, 0 if not.
  */
  */
@@ -2877,51 +3205,36 @@ static int set_pcm_default_values(struct hda_codec *codec,
 	return 0;
 	return 0;
 }
 }
 
 
+/* global */
+const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = {
+	"Audio", "SPDIF", "HDMI", "Modem"
+};
+
 /*
 /*
  * get the empty PCM device number to assign
  * get the empty PCM device number to assign
  */
  */
 static int get_empty_pcm_device(struct hda_bus *bus, int type)
 static int get_empty_pcm_device(struct hda_bus *bus, int type)
 {
 {
-	static const char *dev_name[HDA_PCM_NTYPES] = {
-		"Audio", "SPDIF", "HDMI", "Modem"
-	};
-	/* starting device index for each PCM type */
-	static int dev_idx[HDA_PCM_NTYPES] = {
-		[HDA_PCM_TYPE_AUDIO] = 0,
-		[HDA_PCM_TYPE_SPDIF] = 1,
-		[HDA_PCM_TYPE_HDMI] = 3,
-		[HDA_PCM_TYPE_MODEM] = 6
+	/* audio device indices; not linear to keep compatibility */
+	static int audio_idx[HDA_PCM_NTYPES][5] = {
+		[HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 },
+		[HDA_PCM_TYPE_SPDIF] = { 1, -1 },
+		[HDA_PCM_TYPE_HDMI]  = { 3, 7, 8, 9, -1 },
+		[HDA_PCM_TYPE_MODEM] = { 6, -1 },
 	};
 	};
-	/* normal audio device indices; not linear to keep compatibility */
-	static int audio_idx[4] = { 0, 2, 4, 5 };
-	int i, dev;
-
-	switch (type) {
-	case HDA_PCM_TYPE_AUDIO:
-		for (i = 0; i < ARRAY_SIZE(audio_idx); i++) {
-			dev = audio_idx[i];
-			if (!test_bit(dev, bus->pcm_dev_bits))
-				goto ok;
-		}
-		snd_printk(KERN_WARNING "Too many audio devices\n");
-		return -EAGAIN;
-	case HDA_PCM_TYPE_SPDIF:
-	case HDA_PCM_TYPE_HDMI:
-	case HDA_PCM_TYPE_MODEM:
-		dev = dev_idx[type];
-		if (test_bit(dev, bus->pcm_dev_bits)) {
-			snd_printk(KERN_WARNING "%s already defined\n",
-				   dev_name[type]);
-			return -EAGAIN;
-		}
-		break;
-	default:
+	int i;
+
+	if (type >= HDA_PCM_NTYPES) {
 		snd_printk(KERN_WARNING "Invalid PCM type %d\n", type);
 		snd_printk(KERN_WARNING "Invalid PCM type %d\n", type);
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
- ok:
-	set_bit(dev, bus->pcm_dev_bits);
-	return dev;
+
+	for (i = 0; audio_idx[type][i] >= 0 ; i++)
+		if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits))
+			return audio_idx[type][i];
+
+	snd_printk(KERN_WARNING "Too many %s devices\n", snd_hda_pcm_type_name[type]);
+	return -EAGAIN;
 }
 }
 
 
 /*
 /*
@@ -3159,14 +3472,14 @@ EXPORT_SYMBOL_HDA(snd_hda_check_board_codec_sid_config);
  */
  */
 int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
 int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
 {
 {
- 	int err;
+	int err;
 
 
 	for (; knew->name; knew++) {
 	for (; knew->name; knew++) {
 		struct snd_kcontrol *kctl;
 		struct snd_kcontrol *kctl;
 		kctl = snd_ctl_new1(knew, codec);
 		kctl = snd_ctl_new1(knew, codec);
 		if (!kctl)
 		if (!kctl)
 			return -ENOMEM;
 			return -ENOMEM;
-		err = snd_hda_ctl_add(codec, kctl);
+		err = snd_hda_ctl_add(codec, 0, kctl);
 		if (err < 0) {
 		if (err < 0) {
 			if (!codec->addr)
 			if (!codec->addr)
 				return err;
 				return err;
@@ -3174,7 +3487,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
 			if (!kctl)
 			if (!kctl)
 				return -ENOMEM;
 				return -ENOMEM;
 			kctl->id.device = codec->addr;
 			kctl->id.device = codec->addr;
-			err = snd_hda_ctl_add(codec, kctl);
+			err = snd_hda_ctl_add(codec, 0, kctl);
 			if (err < 0)
 			if (err < 0)
 				return err;
 				return err;
 		}
 		}
@@ -3207,8 +3520,27 @@ static void hda_keep_power_on(struct hda_codec *codec)
 {
 {
 	codec->power_count++;
 	codec->power_count++;
 	codec->power_on = 1;
 	codec->power_on = 1;
+	codec->power_jiffies = jiffies;
 }
 }
 
 
+/* update the power on/off account with the current jiffies */
+void snd_hda_update_power_acct(struct hda_codec *codec)
+{
+	unsigned long delta = jiffies - codec->power_jiffies;
+	if (codec->power_on)
+		codec->power_on_acct += delta;
+	else
+		codec->power_off_acct += delta;
+	codec->power_jiffies += delta;
+}
+
+/**
+ * snd_hda_power_up - Power-up the codec
+ * @codec: HD-audio codec
+ *
+ * Increment the power-up counter and power up the hardware really when
+ * not turned on yet.
+ */ 
 void snd_hda_power_up(struct hda_codec *codec)
 void snd_hda_power_up(struct hda_codec *codec)
 {
 {
 	struct hda_bus *bus = codec->bus;
 	struct hda_bus *bus = codec->bus;
@@ -3217,7 +3549,9 @@ void snd_hda_power_up(struct hda_codec *codec)
 	if (codec->power_on || codec->power_transition)
 	if (codec->power_on || codec->power_transition)
 		return;
 		return;
 
 
+	snd_hda_update_power_acct(codec);
 	codec->power_on = 1;
 	codec->power_on = 1;
+	codec->power_jiffies = jiffies;
 	if (bus->ops.pm_notify)
 	if (bus->ops.pm_notify)
 		bus->ops.pm_notify(bus);
 		bus->ops.pm_notify(bus);
 	hda_call_codec_resume(codec);
 	hda_call_codec_resume(codec);
@@ -3229,9 +3563,13 @@ EXPORT_SYMBOL_HDA(snd_hda_power_up);
 #define power_save(codec)	\
 #define power_save(codec)	\
 	((codec)->bus->power_save ? *(codec)->bus->power_save : 0)
 	((codec)->bus->power_save ? *(codec)->bus->power_save : 0)
 
 
-#define power_save(codec)	\
-	((codec)->bus->power_save ? *(codec)->bus->power_save : 0)
-
+/**
+ * snd_hda_power_down - Power-down the codec
+ * @codec: HD-audio codec
+ *
+ * Decrement the power-up counter and schedules the power-off work if
+ * the counter rearches to zero.
+ */ 
 void snd_hda_power_down(struct hda_codec *codec)
 void snd_hda_power_down(struct hda_codec *codec)
 {
 {
 	--codec->power_count;
 	--codec->power_count;
@@ -3245,6 +3583,19 @@ void snd_hda_power_down(struct hda_codec *codec)
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_power_down);
 EXPORT_SYMBOL_HDA(snd_hda_power_down);
 
 
+/**
+ * snd_hda_check_amp_list_power - Check the amp list and update the power
+ * @codec: HD-audio codec
+ * @check: the object containing an AMP list and the status
+ * @nid: NID to check / update
+ *
+ * Check whether the given NID is in the amp list.  If it's in the list,
+ * check the current AMP status, and update the the power-status according
+ * to the mute status.
+ *
+ * This function is supposed to be set or called from the check_power_status
+ * patch ops.
+ */ 
 int snd_hda_check_amp_list_power(struct hda_codec *codec,
 int snd_hda_check_amp_list_power(struct hda_codec *codec,
 				 struct hda_loopback_check *check,
 				 struct hda_loopback_check *check,
 				 hda_nid_t nid)
 				 hda_nid_t nid)
@@ -3286,6 +3637,10 @@ EXPORT_SYMBOL_HDA(snd_hda_check_amp_list_power);
 /*
 /*
  * Channel mode helper
  * Channel mode helper
  */
  */
+
+/**
+ * snd_hda_ch_mode_info - Info callback helper for the channel mode enum
+ */
 int snd_hda_ch_mode_info(struct hda_codec *codec,
 int snd_hda_ch_mode_info(struct hda_codec *codec,
 			 struct snd_ctl_elem_info *uinfo,
 			 struct snd_ctl_elem_info *uinfo,
 			 const struct hda_channel_mode *chmode,
 			 const struct hda_channel_mode *chmode,
@@ -3302,6 +3657,9 @@ int snd_hda_ch_mode_info(struct hda_codec *codec,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_ch_mode_info);
 EXPORT_SYMBOL_HDA(snd_hda_ch_mode_info);
 
 
+/**
+ * snd_hda_ch_mode_get - Get callback helper for the channel mode enum
+ */
 int snd_hda_ch_mode_get(struct hda_codec *codec,
 int snd_hda_ch_mode_get(struct hda_codec *codec,
 			struct snd_ctl_elem_value *ucontrol,
 			struct snd_ctl_elem_value *ucontrol,
 			const struct hda_channel_mode *chmode,
 			const struct hda_channel_mode *chmode,
@@ -3320,6 +3678,9 @@ int snd_hda_ch_mode_get(struct hda_codec *codec,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_ch_mode_get);
 EXPORT_SYMBOL_HDA(snd_hda_ch_mode_get);
 
 
+/**
+ * snd_hda_ch_mode_put - Put callback helper for the channel mode enum
+ */
 int snd_hda_ch_mode_put(struct hda_codec *codec,
 int snd_hda_ch_mode_put(struct hda_codec *codec,
 			struct snd_ctl_elem_value *ucontrol,
 			struct snd_ctl_elem_value *ucontrol,
 			const struct hda_channel_mode *chmode,
 			const struct hda_channel_mode *chmode,
@@ -3344,6 +3705,10 @@ EXPORT_SYMBOL_HDA(snd_hda_ch_mode_put);
 /*
 /*
  * input MUX helper
  * input MUX helper
  */
  */
+
+/**
+ * snd_hda_input_mux_info_info - Info callback helper for the input-mux enum
+ */
 int snd_hda_input_mux_info(const struct hda_input_mux *imux,
 int snd_hda_input_mux_info(const struct hda_input_mux *imux,
 			   struct snd_ctl_elem_info *uinfo)
 			   struct snd_ctl_elem_info *uinfo)
 {
 {
@@ -3362,6 +3727,9 @@ int snd_hda_input_mux_info(const struct hda_input_mux *imux,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_input_mux_info);
 EXPORT_SYMBOL_HDA(snd_hda_input_mux_info);
 
 
+/**
+ * snd_hda_input_mux_info_put - Put callback helper for the input-mux enum
+ */
 int snd_hda_input_mux_put(struct hda_codec *codec,
 int snd_hda_input_mux_put(struct hda_codec *codec,
 			  const struct hda_input_mux *imux,
 			  const struct hda_input_mux *imux,
 			  struct snd_ctl_elem_value *ucontrol,
 			  struct snd_ctl_elem_value *ucontrol,
@@ -3421,8 +3789,29 @@ static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid)
 	}
 	}
 }
 }
 
 
-/*
- * open the digital out in the exclusive mode
+/**
+ * snd_hda_bus_reboot_notify - call the reboot notifier of each codec
+ * @bus: HD-audio bus
+ */
+void snd_hda_bus_reboot_notify(struct hda_bus *bus)
+{
+	struct hda_codec *codec;
+
+	if (!bus)
+		return;
+	list_for_each_entry(codec, &bus->codec_list, list) {
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+		if (!codec->power_on)
+			continue;
+#endif
+		if (codec->patch_ops.reboot_notify)
+			codec->patch_ops.reboot_notify(codec);
+	}
+}
+EXPORT_SYMBOL_HDA(snd_hda_bus_reboot_notify);
+
+/**
+ * snd_hda_multi_out_dig_open - open the digital out in the exclusive mode
  */
  */
 int snd_hda_multi_out_dig_open(struct hda_codec *codec,
 int snd_hda_multi_out_dig_open(struct hda_codec *codec,
 			       struct hda_multi_out *mout)
 			       struct hda_multi_out *mout)
@@ -3437,6 +3826,9 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_open);
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_open);
 
 
+/**
+ * snd_hda_multi_out_dig_prepare - prepare the digital out stream
+ */
 int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
 int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
 				  struct hda_multi_out *mout,
 				  struct hda_multi_out *mout,
 				  unsigned int stream_tag,
 				  unsigned int stream_tag,
@@ -3450,6 +3842,9 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_prepare);
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_prepare);
 
 
+/**
+ * snd_hda_multi_out_dig_cleanup - clean-up the digital out stream
+ */
 int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec,
 int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec,
 				  struct hda_multi_out *mout)
 				  struct hda_multi_out *mout)
 {
 {
@@ -3460,8 +3855,8 @@ int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_cleanup);
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_cleanup);
 
 
-/*
- * release the digital out
+/**
+ * snd_hda_multi_out_dig_close - release the digital out stream
  */
  */
 int snd_hda_multi_out_dig_close(struct hda_codec *codec,
 int snd_hda_multi_out_dig_close(struct hda_codec *codec,
 				struct hda_multi_out *mout)
 				struct hda_multi_out *mout)
@@ -3473,8 +3868,12 @@ int snd_hda_multi_out_dig_close(struct hda_codec *codec,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_close);
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_close);
 
 
-/*
- * set up more restrictions for analog out
+/**
+ * snd_hda_multi_out_analog_open - open analog outputs
+ *
+ * Open analog outputs and set up the hw-constraints.
+ * If the digital outputs can be opened as slave, open the digital
+ * outputs, too.
  */
  */
 int snd_hda_multi_out_analog_open(struct hda_codec *codec,
 int snd_hda_multi_out_analog_open(struct hda_codec *codec,
 				  struct hda_multi_out *mout,
 				  struct hda_multi_out *mout,
@@ -3519,9 +3918,11 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_open);
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_open);
 
 
-/*
- * set up the i/o for analog out
- * when the digital out is available, copy the front out to digital out, too.
+/**
+ * snd_hda_multi_out_analog_prepare - Preapre the analog outputs.
+ *
+ * Set up the i/o for analog out.
+ * When the digital out is available, copy the front out to digital out, too.
  */
  */
 int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
 int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
 				     struct hda_multi_out *mout,
 				     struct hda_multi_out *mout,
@@ -3578,8 +3979,8 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare);
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare);
 
 
-/*
- * clean up the setting for analog out
+/**
+ * snd_hda_multi_out_analog_cleanup - clean up the setting for analog out
  */
  */
 int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
 int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
 				     struct hda_multi_out *mout)
 				     struct hda_multi_out *mout)
@@ -3965,8 +4366,14 @@ EXPORT_SYMBOL_HDA(snd_hda_resume);
  * generic arrays
  * generic arrays
  */
  */
 
 
-/* get a new element from the given array
- * if it exceeds the pre-allocated array size, re-allocate the array
+/**
+ * snd_array_new - get a new element from the given array
+ * @array: the array object
+ * 
+ * Get a new element from the given array.  If it exceeds the
+ * pre-allocated array size, re-allocate the array.
+ *
+ * Returns NULL if allocation failed.
  */
  */
 void *snd_array_new(struct snd_array *array)
 void *snd_array_new(struct snd_array *array)
 {
 {
@@ -3990,7 +4397,10 @@ void *snd_array_new(struct snd_array *array)
 }
 }
 EXPORT_SYMBOL_HDA(snd_array_new);
 EXPORT_SYMBOL_HDA(snd_array_new);
 
 
-/* free the given array elements */
+/**
+ * snd_array_free - free the given array elements
+ * @array: the array object
+ */
 void snd_array_free(struct snd_array *array)
 void snd_array_free(struct snd_array *array)
 {
 {
 	kfree(array->list);
 	kfree(array->list);
@@ -4000,7 +4410,12 @@ void snd_array_free(struct snd_array *array)
 }
 }
 EXPORT_SYMBOL_HDA(snd_array_free);
 EXPORT_SYMBOL_HDA(snd_array_free);
 
 
-/*
+/**
+ * snd_print_pcm_rates - Print the supported PCM rates to the string buffer
+ * @pcm: PCM caps bits
+ * @buf: the string buffer to write
+ * @buflen: the max buffer length
+ *
  * used by hda_proc.c and hda_eld.c
  * used by hda_proc.c and hda_eld.c
  */
  */
 void snd_print_pcm_rates(int pcm, char *buf, int buflen)
 void snd_print_pcm_rates(int pcm, char *buf, int buflen)
@@ -4019,6 +4434,14 @@ void snd_print_pcm_rates(int pcm, char *buf, int buflen)
 }
 }
 EXPORT_SYMBOL_HDA(snd_print_pcm_rates);
 EXPORT_SYMBOL_HDA(snd_print_pcm_rates);
 
 
+/**
+ * snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer
+ * @pcm: PCM caps bits
+ * @buf: the string buffer to write
+ * @buflen: the max buffer length
+ *
+ * used by hda_proc.c and hda_eld.c
+ */
 void snd_print_pcm_bits(int pcm, char *buf, int buflen)
 void snd_print_pcm_bits(int pcm, char *buf, int buflen)
 {
 {
 	static unsigned int bits[] = { 8, 16, 20, 24, 32 };
 	static unsigned int bits[] = { 8, 16, 20, 24, 32 };

+ 11 - 0
sound/pci/hda/hda_codec.h

@@ -286,6 +286,10 @@ enum {
 #define AC_PWRST_D1SUP			(1<<1)
 #define AC_PWRST_D1SUP			(1<<1)
 #define AC_PWRST_D2SUP			(1<<2)
 #define AC_PWRST_D2SUP			(1<<2)
 #define AC_PWRST_D3SUP			(1<<3)
 #define AC_PWRST_D3SUP			(1<<3)
+#define AC_PWRST_D3COLDSUP		(1<<4)
+#define AC_PWRST_S3D3COLDSUP		(1<<29)
+#define AC_PWRST_CLKSTOP		(1<<30)
+#define AC_PWRST_EPSS			(1U<<31)
 
 
 /* Power state values */
 /* Power state values */
 #define AC_PWRST_SETTING		(0xf<<0)
 #define AC_PWRST_SETTING		(0xf<<0)
@@ -674,6 +678,7 @@ struct hda_codec_ops {
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid);
 	int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid);
 #endif
 #endif
+	void (*reboot_notify)(struct hda_codec *codec);
 };
 };
 
 
 /* record for amp information cache */
 /* record for amp information cache */
@@ -771,6 +776,7 @@ struct hda_codec {
 
 
 	/* beep device */
 	/* beep device */
 	struct hda_beep *beep;
 	struct hda_beep *beep;
+	unsigned int beep_mode;
 
 
 	/* widget capabilities cache */
 	/* widget capabilities cache */
 	unsigned int num_nodes;
 	unsigned int num_nodes;
@@ -811,6 +817,9 @@ struct hda_codec {
 	unsigned int power_transition :1; /* power-state in transition */
 	unsigned int power_transition :1; /* power-state in transition */
 	int power_count;	/* current (global) power refcount */
 	int power_count;	/* current (global) power refcount */
 	struct delayed_work power_work; /* delayed task for powerdown */
 	struct delayed_work power_work; /* delayed task for powerdown */
+	unsigned long power_on_acct;
+	unsigned long power_off_acct;
+	unsigned long power_jiffies;
 #endif
 #endif
 
 
 	/* codec-specific additional proc output */
 	/* codec-specific additional proc output */
@@ -910,6 +919,7 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
  * Misc
  * Misc
  */
  */
 void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
 void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
+void snd_hda_bus_reboot_notify(struct hda_bus *bus);
 
 
 /*
 /*
  * power management
  * power management
@@ -933,6 +943,7 @@ const char *snd_hda_get_jack_location(u32 cfg);
 void snd_hda_power_up(struct hda_codec *codec);
 void snd_hda_power_up(struct hda_codec *codec);
 void snd_hda_power_down(struct hda_codec *codec);
 void snd_hda_power_down(struct hda_codec *codec);
 #define snd_hda_codec_needs_resume(codec) codec->power_count
 #define snd_hda_codec_needs_resume(codec) codec->power_count
+void snd_hda_update_power_acct(struct hda_codec *codec);
 #else
 #else
 static inline void snd_hda_power_up(struct hda_codec *codec) {}
 static inline void snd_hda_power_up(struct hda_codec *codec) {}
 static inline void snd_hda_power_down(struct hda_codec *codec) {}
 static inline void snd_hda_power_down(struct hda_codec *codec) {}

Some files were not shown because too many files changed in this diff