http://linux-sound.bkbits.net/linux-sound perex@suse.cz|ChangeSet|20050127072045|45912 perex # This is a BitKeeper generated diff -Nru style patch. # # ChangeSet # 2005/01/27 08:20:45+01:00 perex@suse.cz # [ALSA] Removed file added by mistake # # Removed include/sound/version.h~ # # BitKeeper/deleted/.del-version.h~~37c78b8aa77b0731 # 2005/01/27 08:15:16+01:00 perex@suse.cz +0 -0 # Delete: include/sound/version.h~ # # ChangeSet # 2005/01/25 20:52:17+01:00 perex@suse.cz # [ALSA] replace schedule_timeout() with msleep() # # EMU8000 driver # Use msleep() instead of schedule_timeout() to guarantee the task # delays as expected. # # Signed-off-by: Takashi Iwai # # sound/isa/sb/emu8000.c # 2005/01/24 08:00:18+01:00 perex@suse.cz +2 -2 # [ALSA] replace schedule_timeout() with msleep() # # D:2005/01/24 15:00:18 # C:EMU8000 driver # F:isa/sb/emu8000.c:1.24->1.25 # L:Use msleep() instead of schedule_timeout() to guarantee the task # L:delays as expected. # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/25 20:50:28+01:00 perex@suse.cz # [ALSA] insert set_current_state() before schedule_timeout() # # Wavefront drivers # Insert set_current_state() before schedule_timeout(). Without the # insertion, schedule_timeout() returns immediately, resulting in an # effective busy-wait. # # Signed-off-by: Nishanth Aravamudan # Signed-off-by: Takashi Iwai # # sound/isa/wavefront/wavefront_synth.c # 2005/01/24 07:59:27+01:00 perex@suse.cz +1 -0 # [ALSA] insert set_current_state() before schedule_timeout() # # D:2005/01/24 14:59:27 # C:Wavefront drivers # F:isa/wavefront/wavefront_synth.c:1.23->1.24 # L:Insert set_current_state() before schedule_timeout(). Without the # L:insertion, schedule_timeout() returns immediately, resulting in an # L:effective busy-wait. # Signed-off-by: Nishanth Aravamudan # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/25 20:48:58+01:00 perex@suse.cz # [ALSA] replace schedule_timeout() with msleep_interruptible() # # GUS Library # Use msleep_interruptible() instead of custom wait code involving # schedule_timeout() to guarantee the task delays as expected. This also # removes a dependence on the value of HZ. # # Signed-off-by: Nishanth Aravamudan # Signed-off-by: Takashi Iwai # # sound/isa/gus/gus_reset.c # 2005/01/24 07:58:37+01:00 perex@suse.cz +1 -6 # [ALSA] replace schedule_timeout() with msleep_interruptible() # # D:2005/01/24 14:58:37 # C:GUS Library # F:isa/gus/gus_reset.c:1.7->1.8 # L:Use msleep_interruptible() instead of custom wait code involving # L:schedule_timeout() to guarantee the task delays as expected. This also # L:removes a dependence on the value of HZ. # Signed-off-by: Nishanth Aravamudan # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/25 20:47:19+01:00 perex@suse.cz # [ALSA] insert set_current_state() before schedule_timeout() # # GUS Library # Insert set_current_state() before schedule_timeout(). Without the # insertion, schedule_timeout() returns immediately. # # Signed-off-by: Nishanth Aravamudan # Signed-off-by: Takashi Iwai # # sound/isa/gus/gus_pcm.c # 2005/01/24 07:57:51+01:00 perex@suse.cz +1 -0 # [ALSA] insert set_current_state() before schedule_timeout() # # D:2005/01/24 14:57:51 # C:GUS Library # F:isa/gus/gus_pcm.c:1.23->1.24 # L:Insert set_current_state() before schedule_timeout(). Without the # L:insertion, schedule_timeout() returns immediately. # Signed-off-by: Nishanth Aravamudan # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/25 20:41:00+01:00 perex@suse.cz # [ALSA] replace schedule_timeout() with msleep() # # Digigram VX core # Use msleep() instead of schedule_timeout() to guarantee the task # delays as expected. # # Signed-off-by: Nishanth Aravamudan # Signed-off-by: Takashi Iwai # # sound/drivers/vx/vx_core.c # 2005/01/24 07:56:51+01:00 perex@suse.cz +3 -5 # [ALSA] replace schedule_timeout() with msleep() # # D:2005/01/24 14:56:51 # C:Digigram VX core # F:drivers/vx/vx_core.c:1.13->1.14 # L:Use msleep() instead of schedule_timeout() to guarantee the task # L:delays as expected. # Signed-off-by: Nishanth Aravamudan # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/25 20:39:04+01:00 perex@suse.cz # [ALSA] replace schedule_timeout() with msleep() # # RawMidi Midlevel # Use msleep instead of schedule_timeout() to guarantee the task delays # as expected. This also removes a dependence on the value of HZ. # # Signed-off-by: Nishanth Aravamudan # Signed-off-by: Takashi Iwai # # sound/core/rawmidi.c # 2005/01/24 07:55:43+01:00 perex@suse.cz +3 -4 # [ALSA] replace schedule_timeout() with msleep() # # D:2005/01/24 14:55:43 # C:RawMidi Midlevel # F:core/rawmidi.c:1.52->1.53 # L:Use msleep instead of schedule_timeout() to guarantee the task delays # L:as expected. This also removes a dependence on the value of HZ. # Signed-off-by: Nishanth Aravamudan # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/25 20:37:18+01:00 perex@suse.cz # [ALSA] Special AC97 patch for ASUS W1000/CMI9739 laptop # # AC97 Codec # This patch fixes sound output on the ASUS W1000 laptop with the CMI9739 # chip. It wrongly reports that it has a SPDIF in, when in fact we wish to # use the EAPD pin. # # Signed-off-by: James Courtier-Dutton # Signed-off-by: Takashi Iwai # # sound/pci/ac97/ac97_patch.c # 2005/01/24 05:11:00+01:00 perex@suse.cz +9 -0 # [ALSA] Special AC97 patch for ASUS W1000/CMI9739 laptop # # D:2005/01/24 12:11:00 # C:AC97 Codec # F:pci/ac97/ac97_patch.c:1.70->1.71 # L:This patch fixes sound output on the ASUS W1000 laptop with the CMI9739 # L:chip. It wrongly reports that it has a SPDIF in, when in fact we wish to # L:use the EAPD pin. # Signed-off-by: James Courtier-Dutton # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/25 20:35:33+01:00 perex@suse.cz # [ALSA] Warning doc about VIA82xx recording # # Documentation # Add warning about the consequences of adjusting the 'Input Source Select' # of VIA82xx. # # Signed-off-by: Ross Kendall Axe # Signed-off-by: Takashi Iwai # # Documentation/sound/alsa/VIA82xx-mixer.txt # 2005/01/25 14:32:28+01:00 perex@suse.cz +8 -0 # [ALSA] Warning doc about VIA82xx recording # # D:2005/01/21 19:33:11 # C:Documentation # F:Documentation/VIA82xx-mixer.txt:INITIAL->1.1 # L:Add warning about the consequences of adjusting the 'Input Source Select' # L:of VIA82xx. # Signed-off-by: Ross Kendall Axe # Signed-off-by: Takashi Iwai # # Documentation/sound/alsa/VIA82xx-mixer.txt # 2005/01/25 14:32:28+01:00 perex@suse.cz +0 -0 # BitKeeper file /home/perex/bk/linux-sound/work/Documentation/sound/alsa/VIA82xx-mixer.txt # # ChangeSet # 2005/01/25 20:33:42+01:00 perex@suse.cz # [ALSA] fix usage of preprocessor directive inside macro # # HDA Intel driver # gcc-2 complains about preprocessor directives inside a macro argument list # # Signed-off-by: Clemens Ladisch # # sound/pci/hda/hda_intel.c # 2005/01/21 10:46:00+01:00 perex@suse.cz +3 -3 # [ALSA] fix usage of preprocessor directive inside macro # # D:2005/01/21 17:46:00 # C:HDA Intel driver # F:pci/hda/hda_intel.c:1.1->1.2 # L:gcc-2 complains about preprocessor directives inside a macro argument list # Signed-off-by: Clemens Ladisch # # ChangeSet # 2005/01/25 20:32:04+01:00 perex@suse.cz # [ALSA] add more Yamaha USB MIDI quirks # # USB generic driver # add support for Yamaha UC-MX, UC-KX, CLP-175, SPX2000 # # Signed-off-by: Clemens Ladisch # # sound/usb/usbquirks.h # 2005/01/21 01:32:12+01:00 perex@suse.cz +6 -0 # [ALSA] add more Yamaha USB MIDI quirks # # D:2005/01/21 08:32:12 # C:USB generic driver # F:usb/usbquirks.h:1.39->1.40 # L:add support for Yamaha UC-MX, UC-KX, CLP-175, SPX2000 # Signed-off-by: Clemens Ladisch # # ChangeSet # 2005/01/20 22:29:20+01:00 perex@suse.cz # [ALSA] remove obsolete sound/core/ioctl32 directory # # The compatibility layer is integrated to ALSA midlevel code now. # # include/sound/version.h~ # 2005/01/20 22:26:24+01:00 perex@suse.cz +3 -0 # # include/sound/version.h~ # 2005/01/20 22:26:24+01:00 perex@suse.cz +0 -0 # BitKeeper file /home/perex/bk/linux-sound/work/include/sound/version.h~ # # BitKeeper/deleted/.del-hwdep32.c~2628af71433fb282 # 2005/01/20 22:14:14+01:00 perex@suse.cz +0 -0 # Delete: sound/core/ioctl32/hwdep32.c # # BitKeeper/deleted/.del-ioctl32.h~6fa31b659e702cbd # 2005/01/20 22:14:12+01:00 perex@suse.cz +0 -0 # Delete: sound/core/ioctl32/ioctl32.h # # BitKeeper/deleted/.del-ioctl32.c~e6621ca58003986f # 2005/01/20 22:14:11+01:00 perex@suse.cz +0 -0 # Delete: sound/core/ioctl32/ioctl32.c # # BitKeeper/deleted/.del-Makefile~767d5bad6308fa6d # 2005/01/20 22:14:08+01:00 perex@suse.cz +0 -0 # Delete: sound/core/ioctl32/Makefile # # BitKeeper/deleted/.del-pcm32.c~79101ddd8c8dbba8 # 2005/01/20 22:14:05+01:00 perex@suse.cz +0 -0 # Delete: sound/core/ioctl32/pcm32.c # # BitKeeper/deleted/.del-rawmidi32.c~b24d28b2a36477f1 # 2005/01/20 22:14:03+01:00 perex@suse.cz +0 -0 # Delete: sound/core/ioctl32/rawmidi32.c # # BitKeeper/deleted/.del-seq32.c~c96c6dffe1307ca0 # 2005/01/20 22:14:00+01:00 perex@suse.cz +0 -0 # Delete: sound/core/ioctl32/seq32.c # # BitKeeper/deleted/.del-timer32.c~e0c2522dbc4e6d68 # 2005/01/20 22:13:55+01:00 perex@suse.cz +0 -0 # Delete: sound/core/ioctl32/timer32.c # # ChangeSet # 2005/01/20 21:47:16+01:00 perex@suse.cz # Merge suse.cz:/home/perex/bk/linux-sound/linux-sound # into suse.cz:/home/perex/bk/linux-sound/work # # sound/pci/Kconfig # 2005/01/20 21:46:53+01:00 perex@suse.cz +0 -0 # Auto merged # # ChangeSet # 2005/01/20 20:33:37+01:00 perex@suse.cz # [ALSA] Use DEFINE_SPINLOCK(), DEFINE_RWLOCK() macros # # ALSA Core,PCM Midlevel,Timer Midlevel,ALSA sequencer # ALSA<-OSS sequencer # Replace spin/rwlock definitions with DEFINE_SPINLOCK() and DEFINE_RWLOCK() # macros. # # Signed-off-by: Takashi Iwai # # sound/core/timer.c # 2005/01/20 10:42:36+01:00 perex@suse.cz +1 -1 # [ALSA] Use DEFINE_SPINLOCK(), DEFINE_RWLOCK() macros # # D:2005/01/20 17:42:36 # C:ALSA Core,PCM Midlevel,Timer Midlevel,ALSA sequencer # C:ALSA<-OSS sequencer # F:core/init.c:1.54->1.55 # F:core/memory.c:1.36->1.37 # F:core/pcm_native.c:1.112->1.113 # F:core/timer.c:1.64->1.65 # F:core/seq/seq_clientmgr.c:1.41->1.42 # F:core/seq/seq_queue.c:1.15->1.16 # F:core/seq/oss/seq_oss_midi.c:1.16->1.17 # F:core/seq/oss/seq_oss_synth.c:1.16->1.17 # L:Replace spin/rwlock definitions with DEFINE_SPINLOCK() and DEFINE_RWLOCK() # L:macros. # Signed-off-by: Takashi Iwai # # sound/core/seq/seq_queue.c # 2005/01/20 10:42:37+01:00 perex@suse.cz +1 -1 # [ALSA] Use DEFINE_SPINLOCK(), DEFINE_RWLOCK() macros # # D:2005/01/20 17:42:36 # C:ALSA Core,PCM Midlevel,Timer Midlevel,ALSA sequencer # C:ALSA<-OSS sequencer # F:core/init.c:1.54->1.55 # F:core/memory.c:1.36->1.37 # F:core/pcm_native.c:1.112->1.113 # F:core/timer.c:1.64->1.65 # F:core/seq/seq_clientmgr.c:1.41->1.42 # F:core/seq/seq_queue.c:1.15->1.16 # F:core/seq/oss/seq_oss_midi.c:1.16->1.17 # F:core/seq/oss/seq_oss_synth.c:1.16->1.17 # L:Replace spin/rwlock definitions with DEFINE_SPINLOCK() and DEFINE_RWLOCK() # L:macros. # Signed-off-by: Takashi Iwai # # sound/core/seq/seq_clientmgr.c # 2005/01/20 10:42:37+01:00 perex@suse.cz +1 -1 # [ALSA] Use DEFINE_SPINLOCK(), DEFINE_RWLOCK() macros # # D:2005/01/20 17:42:36 # C:ALSA Core,PCM Midlevel,Timer Midlevel,ALSA sequencer # C:ALSA<-OSS sequencer # F:core/init.c:1.54->1.55 # F:core/memory.c:1.36->1.37 # F:core/pcm_native.c:1.112->1.113 # F:core/timer.c:1.64->1.65 # F:core/seq/seq_clientmgr.c:1.41->1.42 # F:core/seq/seq_queue.c:1.15->1.16 # F:core/seq/oss/seq_oss_midi.c:1.16->1.17 # F:core/seq/oss/seq_oss_synth.c:1.16->1.17 # L:Replace spin/rwlock definitions with DEFINE_SPINLOCK() and DEFINE_RWLOCK() # L:macros. # Signed-off-by: Takashi Iwai # # sound/core/seq/oss/seq_oss_synth.c # 2005/01/20 10:42:37+01:00 perex@suse.cz +1 -1 # [ALSA] Use DEFINE_SPINLOCK(), DEFINE_RWLOCK() macros # # D:2005/01/20 17:42:36 # C:ALSA Core,PCM Midlevel,Timer Midlevel,ALSA sequencer # C:ALSA<-OSS sequencer # F:core/init.c:1.54->1.55 # F:core/memory.c:1.36->1.37 # F:core/pcm_native.c:1.112->1.113 # F:core/timer.c:1.64->1.65 # F:core/seq/seq_clientmgr.c:1.41->1.42 # F:core/seq/seq_queue.c:1.15->1.16 # F:core/seq/oss/seq_oss_midi.c:1.16->1.17 # F:core/seq/oss/seq_oss_synth.c:1.16->1.17 # L:Replace spin/rwlock definitions with DEFINE_SPINLOCK() and DEFINE_RWLOCK() # L:macros. # Signed-off-by: Takashi Iwai # # sound/core/seq/oss/seq_oss_midi.c # 2005/01/20 10:42:37+01:00 perex@suse.cz +1 -1 # [ALSA] Use DEFINE_SPINLOCK(), DEFINE_RWLOCK() macros # # D:2005/01/20 17:42:36 # C:ALSA Core,PCM Midlevel,Timer Midlevel,ALSA sequencer # C:ALSA<-OSS sequencer # F:core/init.c:1.54->1.55 # F:core/memory.c:1.36->1.37 # F:core/pcm_native.c:1.112->1.113 # F:core/timer.c:1.64->1.65 # F:core/seq/seq_clientmgr.c:1.41->1.42 # F:core/seq/seq_queue.c:1.15->1.16 # F:core/seq/oss/seq_oss_midi.c:1.16->1.17 # F:core/seq/oss/seq_oss_synth.c:1.16->1.17 # L:Replace spin/rwlock definitions with DEFINE_SPINLOCK() and DEFINE_RWLOCK() # L:macros. # Signed-off-by: Takashi Iwai # # sound/core/pcm_native.c # 2005/01/20 10:42:36+01:00 perex@suse.cz +1 -1 # [ALSA] Use DEFINE_SPINLOCK(), DEFINE_RWLOCK() macros # # D:2005/01/20 17:42:36 # C:ALSA Core,PCM Midlevel,Timer Midlevel,ALSA sequencer # C:ALSA<-OSS sequencer # F:core/init.c:1.54->1.55 # F:core/memory.c:1.36->1.37 # F:core/pcm_native.c:1.112->1.113 # F:core/timer.c:1.64->1.65 # F:core/seq/seq_clientmgr.c:1.41->1.42 # F:core/seq/seq_queue.c:1.15->1.16 # F:core/seq/oss/seq_oss_midi.c:1.16->1.17 # F:core/seq/oss/seq_oss_synth.c:1.16->1.17 # L:Replace spin/rwlock definitions with DEFINE_SPINLOCK() and DEFINE_RWLOCK() # L:macros. # Signed-off-by: Takashi Iwai # # sound/core/memory.c # 2005/01/20 10:42:36+01:00 perex@suse.cz +2 -2 # [ALSA] Use DEFINE_SPINLOCK(), DEFINE_RWLOCK() macros # # D:2005/01/20 17:42:36 # C:ALSA Core,PCM Midlevel,Timer Midlevel,ALSA sequencer # C:ALSA<-OSS sequencer # F:core/init.c:1.54->1.55 # F:core/memory.c:1.36->1.37 # F:core/pcm_native.c:1.112->1.113 # F:core/timer.c:1.64->1.65 # F:core/seq/seq_clientmgr.c:1.41->1.42 # F:core/seq/seq_queue.c:1.15->1.16 # F:core/seq/oss/seq_oss_midi.c:1.16->1.17 # F:core/seq/oss/seq_oss_synth.c:1.16->1.17 # L:Replace spin/rwlock definitions with DEFINE_SPINLOCK() and DEFINE_RWLOCK() # L:macros. # Signed-off-by: Takashi Iwai # # sound/core/init.c # 2005/01/20 10:42:36+01:00 perex@suse.cz +1 -1 # [ALSA] Use DEFINE_SPINLOCK(), DEFINE_RWLOCK() macros # # D:2005/01/20 17:42:36 # C:ALSA Core,PCM Midlevel,Timer Midlevel,ALSA sequencer # C:ALSA<-OSS sequencer # F:core/init.c:1.54->1.55 # F:core/memory.c:1.36->1.37 # F:core/pcm_native.c:1.112->1.113 # F:core/timer.c:1.64->1.65 # F:core/seq/seq_clientmgr.c:1.41->1.42 # F:core/seq/seq_queue.c:1.15->1.16 # F:core/seq/oss/seq_oss_midi.c:1.16->1.17 # F:core/seq/oss/seq_oss_synth.c:1.16->1.17 # L:Replace spin/rwlock definitions with DEFINE_SPINLOCK() and DEFINE_RWLOCK() # L:macros. # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/20 20:32:02+01:00 perex@suse.cz # [ALSA] Remove snd-ioctl32 entry # # ALSA Core # Remove the entry for snd-ioctl32. The 32bit wrapper is built in the core # module. # # Signed-off-by: Takashi Iwai # # sound/core/Makefile # 2005/01/20 10:16:39+01:00 perex@suse.cz +0 -1 # [ALSA] Remove snd-ioctl32 entry # # D:2005/01/20 17:16:35 # C:ALSA Core # F:core/Kconfig:1.7->1.8 # F:core/Makefile:1.54->1.55 # L:Remove the entry for snd-ioctl32. The 32bit wrapper is built in the core # L:module. # Signed-off-by: Takashi Iwai # # sound/core/Kconfig # 2005/01/20 10:16:35+01:00 perex@suse.cz +0 -14 # [ALSA] Remove snd-ioctl32 entry # # D:2005/01/20 17:16:35 # C:ALSA Core # F:core/Kconfig:1.7->1.8 # F:core/Makefile:1.54->1.55 # L:Remove the entry for snd-ioctl32. The 32bit wrapper is built in the core # L:module. # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/20 20:30:46+01:00 perex@suse.cz # [ALSA] Export new register/unregister functions # # ALSA Core # Export new register/unregister functions for compat control-ioctls. # # Signed-off-by: Takashi Iwai # # sound/core/sound.c # 2005/01/20 10:15:37+01:00 perex@suse.cz +4 -0 # [ALSA] Export new register/unregister functions # # D:2005/01/20 17:15:37 # C:ALSA Core # F:core/sound.c:1.71->1.72 # L:Export new register/unregister functions for compat control-ioctls. # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/20 20:29:26+01:00 perex@suse.cz # [ALSA] unlocked/compat_ioctl rewrite for OSS compatible drivers # # ALSA<-OSS emulation,ALSA<-OSS sequencer # The ioctl handlers for OSS compatible drivers are rewritten using # unlocked/compat_ioctl. # # Signed-off-by: Takashi Iwai # # sound/core/seq/oss/seq_oss.c # 2005/01/20 10:14:45+01:00 perex@suse.cz +11 -10 # [ALSA] unlocked/compat_ioctl rewrite for OSS compatible drivers # # D:2005/01/20 17:14:44 # C:ALSA<-OSS emulation,ALSA<-OSS sequencer # F:core/oss/mixer_oss.c:1.37->1.38 # F:core/oss/pcm_oss.c:1.83->1.84 # F:core/seq/oss/seq_oss.c:1.17->1.18 # L:The ioctl handlers for OSS compatible drivers are rewritten using # L:unlocked/compat_ioctl. # Signed-off-by: Takashi Iwai # # sound/core/oss/pcm_oss.c # 2005/01/20 10:14:45+01:00 perex@suse.cz +9 -13 # [ALSA] unlocked/compat_ioctl rewrite for OSS compatible drivers # # D:2005/01/20 17:14:44 # C:ALSA<-OSS emulation,ALSA<-OSS sequencer # F:core/oss/mixer_oss.c:1.37->1.38 # F:core/oss/pcm_oss.c:1.83->1.84 # F:core/seq/oss/seq_oss.c:1.17->1.18 # L:The ioctl handlers for OSS compatible drivers are rewritten using # L:unlocked/compat_ioctl. # Signed-off-by: Takashi Iwai # # sound/core/oss/mixer_oss.c # 2005/01/20 10:14:44+01:00 perex@suse.cz +11 -10 # [ALSA] unlocked/compat_ioctl rewrite for OSS compatible drivers # # D:2005/01/20 17:14:44 # C:ALSA<-OSS emulation,ALSA<-OSS sequencer # F:core/oss/mixer_oss.c:1.37->1.38 # F:core/oss/pcm_oss.c:1.83->1.84 # F:core/seq/oss/seq_oss.c:1.17->1.18 # L:The ioctl handlers for OSS compatible drivers are rewritten using # L:unlocked/compat_ioctl. # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/20 20:28:06+01:00 perex@suse.cz # [ALSA] unlocked/compat_ioctl rewrite for hwdep, rawmidi, timer and sequencer API # # HWDEP Midlevel,RawMidi Midlevel,Timer Midlevel,ALSA sequencer # The ioctl handler for hwdep, rawmidi, timer and sequencer API are rewritten # using unlocked/compat_ioctl. # The 32bit wrapper is merged to the core module. # # Signed-off-by: Takashi Iwai # # sound/core/timer_compat.c # 2005/01/20 19:16:31+01:00 perex@suse.cz +119 -0 # [ALSA] unlocked/compat_ioctl rewrite for hwdep, rawmidi, timer and sequencer API # # D:2005/01/20 17:13:40 # C:HWDEP Midlevel,RawMidi Midlevel,Timer Midlevel,ALSA sequencer # F:core/hwdep.c:1.28->1.29 # F:core/hwdep_compat.c:INITIAL->1.1 # F:core/rawmidi.c:1.51->1.52 # F:core/rawmidi_compat.c:INITIAL->1.1 # F:core/timer.c:1.63->1.64 # F:core/timer_compat.c:INITIAL->1.1 # F:core/seq/seq_clientmgr.c:1.40->1.41 # F:core/seq/seq_compat.c:INITIAL->1.1 # F:include/hwdep.h:1.6->1.7 # L:The ioctl handler for hwdep, rawmidi, timer and sequencer API are rewritten # L:using unlocked/compat_ioctl. # L:The 32bit wrapper is merged to the core module. # Signed-off-by: Takashi Iwai # # sound/core/seq/seq_compat.c # 2005/01/20 19:17:01+01:00 perex@suse.cz +137 -0 # [ALSA] unlocked/compat_ioctl rewrite for hwdep, rawmidi, timer and sequencer API # # D:2005/01/20 17:13:40 # C:HWDEP Midlevel,RawMidi Midlevel,Timer Midlevel,ALSA sequencer # F:core/hwdep.c:1.28->1.29 # F:core/hwdep_compat.c:INITIAL->1.1 # F:core/rawmidi.c:1.51->1.52 # F:core/rawmidi_compat.c:INITIAL->1.1 # F:core/timer.c:1.63->1.64 # F:core/timer_compat.c:INITIAL->1.1 # F:core/seq/seq_clientmgr.c:1.40->1.41 # F:core/seq/seq_compat.c:INITIAL->1.1 # F:include/hwdep.h:1.6->1.7 # L:The ioctl handler for hwdep, rawmidi, timer and sequencer API are rewritten # L:using unlocked/compat_ioctl. # L:The 32bit wrapper is merged to the core module. # Signed-off-by: Takashi Iwai # # sound/core/rawmidi_compat.c # 2005/01/20 19:16:21+01:00 perex@suse.cz +120 -0 # [ALSA] unlocked/compat_ioctl rewrite for hwdep, rawmidi, timer and sequencer API # # D:2005/01/20 17:13:40 # C:HWDEP Midlevel,RawMidi Midlevel,Timer Midlevel,ALSA sequencer # F:core/hwdep.c:1.28->1.29 # F:core/hwdep_compat.c:INITIAL->1.1 # F:core/rawmidi.c:1.51->1.52 # F:core/rawmidi_compat.c:INITIAL->1.1 # F:core/timer.c:1.63->1.64 # F:core/timer_compat.c:INITIAL->1.1 # F:core/seq/seq_clientmgr.c:1.40->1.41 # F:core/seq/seq_compat.c:INITIAL->1.1 # F:include/hwdep.h:1.6->1.7 # L:The ioctl handler for hwdep, rawmidi, timer and sequencer API are rewritten # L:using unlocked/compat_ioctl. # L:The 32bit wrapper is merged to the core module. # Signed-off-by: Takashi Iwai # # sound/core/timer_compat.c # 2005/01/20 19:16:31+01:00 perex@suse.cz +0 -0 # BitKeeper file /home/perex/bk/linux-sound/work/sound/core/timer_compat.c # # sound/core/timer.c # 2005/01/20 10:13:40+01:00 perex@suse.cz +9 -14 # [ALSA] unlocked/compat_ioctl rewrite for hwdep, rawmidi, timer and sequencer API # # D:2005/01/20 17:13:40 # C:HWDEP Midlevel,RawMidi Midlevel,Timer Midlevel,ALSA sequencer # F:core/hwdep.c:1.28->1.29 # F:core/hwdep_compat.c:INITIAL->1.1 # F:core/rawmidi.c:1.51->1.52 # F:core/rawmidi_compat.c:INITIAL->1.1 # F:core/timer.c:1.63->1.64 # F:core/timer_compat.c:INITIAL->1.1 # F:core/seq/seq_clientmgr.c:1.40->1.41 # F:core/seq/seq_compat.c:INITIAL->1.1 # F:include/hwdep.h:1.6->1.7 # L:The ioctl handler for hwdep, rawmidi, timer and sequencer API are rewritten # L:using unlocked/compat_ioctl. # L:The 32bit wrapper is merged to the core module. # Signed-off-by: Takashi Iwai # # sound/core/seq/seq_compat.c # 2005/01/20 19:17:01+01:00 perex@suse.cz +0 -0 # BitKeeper file /home/perex/bk/linux-sound/work/sound/core/seq/seq_compat.c # # sound/core/seq/seq_clientmgr.c # 2005/01/20 10:13:41+01:00 perex@suse.cz +9 -9 # [ALSA] unlocked/compat_ioctl rewrite for hwdep, rawmidi, timer and sequencer API # # D:2005/01/20 17:13:40 # C:HWDEP Midlevel,RawMidi Midlevel,Timer Midlevel,ALSA sequencer # F:core/hwdep.c:1.28->1.29 # F:core/hwdep_compat.c:INITIAL->1.1 # F:core/rawmidi.c:1.51->1.52 # F:core/rawmidi_compat.c:INITIAL->1.1 # F:core/timer.c:1.63->1.64 # F:core/timer_compat.c:INITIAL->1.1 # F:core/seq/seq_clientmgr.c:1.40->1.41 # F:core/seq/seq_compat.c:INITIAL->1.1 # F:include/hwdep.h:1.6->1.7 # L:The ioctl handler for hwdep, rawmidi, timer and sequencer API are rewritten # L:using unlocked/compat_ioctl. # L:The 32bit wrapper is merged to the core module. # Signed-off-by: Takashi Iwai # # sound/core/rawmidi_compat.c # 2005/01/20 19:16:21+01:00 perex@suse.cz +0 -0 # BitKeeper file /home/perex/bk/linux-sound/work/sound/core/rawmidi_compat.c # # sound/core/rawmidi.c # 2005/01/20 10:13:40+01:00 perex@suse.cz +13 -14 # [ALSA] unlocked/compat_ioctl rewrite for hwdep, rawmidi, timer and sequencer API # # D:2005/01/20 17:13:40 # C:HWDEP Midlevel,RawMidi Midlevel,Timer Midlevel,ALSA sequencer # F:core/hwdep.c:1.28->1.29 # F:core/hwdep_compat.c:INITIAL->1.1 # F:core/rawmidi.c:1.51->1.52 # F:core/rawmidi_compat.c:INITIAL->1.1 # F:core/timer.c:1.63->1.64 # F:core/timer_compat.c:INITIAL->1.1 # F:core/seq/seq_clientmgr.c:1.40->1.41 # F:core/seq/seq_compat.c:INITIAL->1.1 # F:include/hwdep.h:1.6->1.7 # L:The ioctl handler for hwdep, rawmidi, timer and sequencer API are rewritten # L:using unlocked/compat_ioctl. # L:The 32bit wrapper is merged to the core module. # Signed-off-by: Takashi Iwai # # sound/core/hwdep.c # 2005/01/20 10:13:40+01:00 perex@suse.cz +11 -14 # [ALSA] unlocked/compat_ioctl rewrite for hwdep, rawmidi, timer and sequencer API # # D:2005/01/20 17:13:40 # C:HWDEP Midlevel,RawMidi Midlevel,Timer Midlevel,ALSA sequencer # F:core/hwdep.c:1.28->1.29 # F:core/hwdep_compat.c:INITIAL->1.1 # F:core/rawmidi.c:1.51->1.52 # F:core/rawmidi_compat.c:INITIAL->1.1 # F:core/timer.c:1.63->1.64 # F:core/timer_compat.c:INITIAL->1.1 # F:core/seq/seq_clientmgr.c:1.40->1.41 # F:core/seq/seq_compat.c:INITIAL->1.1 # F:include/hwdep.h:1.6->1.7 # L:The ioctl handler for hwdep, rawmidi, timer and sequencer API are rewritten # L:using unlocked/compat_ioctl. # L:The 32bit wrapper is merged to the core module. # Signed-off-by: Takashi Iwai # # include/sound/hwdep.h # 2005/01/20 10:13:41+01:00 perex@suse.cz +1 -0 # [ALSA] unlocked/compat_ioctl rewrite for hwdep, rawmidi, timer and sequencer API # # D:2005/01/20 17:13:40 # C:HWDEP Midlevel,RawMidi Midlevel,Timer Midlevel,ALSA sequencer # F:core/hwdep.c:1.28->1.29 # F:core/hwdep_compat.c:INITIAL->1.1 # F:core/rawmidi.c:1.51->1.52 # F:core/rawmidi_compat.c:INITIAL->1.1 # F:core/timer.c:1.63->1.64 # F:core/timer_compat.c:INITIAL->1.1 # F:core/seq/seq_clientmgr.c:1.40->1.41 # F:core/seq/seq_compat.c:INITIAL->1.1 # F:include/hwdep.h:1.6->1.7 # L:The ioctl handler for hwdep, rawmidi, timer and sequencer API are rewritten # L:using unlocked/compat_ioctl. # L:The 32bit wrapper is merged to the core module. # Signed-off-by: Takashi Iwai # # sound/core/hwdep_compat.c # 2005/01/20 19:16:12+01:00 perex@suse.cz +77 -0 # [ALSA] unlocked/compat_ioctl rewrite for hwdep, rawmidi, timer and sequencer API # # D:2005/01/20 17:13:40 # C:HWDEP Midlevel,RawMidi Midlevel,Timer Midlevel,ALSA sequencer # F:core/hwdep.c:1.28->1.29 # F:core/hwdep_compat.c:INITIAL->1.1 # F:core/rawmidi.c:1.51->1.52 # F:core/rawmidi_compat.c:INITIAL->1.1 # F:core/timer.c:1.63->1.64 # F:core/timer_compat.c:INITIAL->1.1 # F:core/seq/seq_clientmgr.c:1.40->1.41 # F:core/seq/seq_compat.c:INITIAL->1.1 # F:include/hwdep.h:1.6->1.7 # L:The ioctl handler for hwdep, rawmidi, timer and sequencer API are rewritten # L:using unlocked/compat_ioctl. # L:The 32bit wrapper is merged to the core module. # Signed-off-by: Takashi Iwai # # sound/core/hwdep_compat.c # 2005/01/20 19:16:12+01:00 perex@suse.cz +0 -0 # BitKeeper file /home/perex/bk/linux-sound/work/sound/core/hwdep_compat.c # # ChangeSet # 2005/01/20 20:26:43+01:00 perex@suse.cz # [ALSA] unlocked/compat_ioctl rewrite for PCM API # # PCM Midlevel # The ioctl handler for PCM API is rewritten using unlocked/compat_ioctl. # The 32bit wrapper is merged to the core module. # # Signed-off-by: Takashi Iwai # # sound/core/pcm_native.c # 2005/01/20 10:11:46+01:00 perex@suse.cz +17 -18 # [ALSA] unlocked/compat_ioctl rewrite for PCM API # # D:2005/01/20 17:11:46 # C:PCM Midlevel # F:core/pcm.c:1.49->1.50 # F:core/pcm_compat.c:INITIAL->1.1 # F:core/pcm_native.c:1.111->1.112 # L:The ioctl handler for PCM API is rewritten using unlocked/compat_ioctl. # L:The 32bit wrapper is merged to the core module. # Signed-off-by: Takashi Iwai # # sound/core/pcm.c # 2005/01/20 10:11:46+01:00 perex@suse.cz +2 -0 # [ALSA] unlocked/compat_ioctl rewrite for PCM API # # D:2005/01/20 17:11:46 # C:PCM Midlevel # F:core/pcm.c:1.49->1.50 # F:core/pcm_compat.c:INITIAL->1.1 # F:core/pcm_native.c:1.111->1.112 # L:The ioctl handler for PCM API is rewritten using unlocked/compat_ioctl. # L:The 32bit wrapper is merged to the core module. # Signed-off-by: Takashi Iwai # # sound/core/pcm_compat.c # 2005/01/20 19:15:57+01:00 perex@suse.cz +513 -0 # [ALSA] unlocked/compat_ioctl rewrite for PCM API # # D:2005/01/20 17:11:46 # C:PCM Midlevel # F:core/pcm.c:1.49->1.50 # F:core/pcm_compat.c:INITIAL->1.1 # F:core/pcm_native.c:1.111->1.112 # L:The ioctl handler for PCM API is rewritten using unlocked/compat_ioctl. # L:The 32bit wrapper is merged to the core module. # Signed-off-by: Takashi Iwai # # sound/core/pcm_compat.c # 2005/01/20 19:15:57+01:00 perex@suse.cz +0 -0 # BitKeeper file /home/perex/bk/linux-sound/work/sound/core/pcm_compat.c # # ChangeSet # 2005/01/20 20:25:19+01:00 perex@suse.cz # [ALSA] unlocked/compat_ioctl rewrite for control API # # Control Midlevel # ioctl handler for control API is rewritten using unlocked/compat_ioctl. # The 32bit wrapper is merged to the core module. # # Added a new register/unregister function for compat control ioctls. # # Signed-off-by: Takashi Iwai # # sound/core/control.c # 2005/01/20 10:10:23+01:00 perex@suse.cz +95 -81 # [ALSA] unlocked/compat_ioctl rewrite for control API # # D:2005/01/20 17:10:23 # C:Control Midlevel # F:core/control.c:1.55->1.56 # F:core/control_compat.c:INITIAL->1.1 # F:include/control.h:1.11->1.12 # L:ioctl handler for control API is rewritten using unlocked/compat_ioctl. # L:The 32bit wrapper is merged to the core module. # L: # L:Added a new register/unregister function for compat control ioctls. # Signed-off-by: Takashi Iwai # # include/sound/control.h # 2005/01/20 10:10:24+01:00 perex@suse.cz +7 -0 # [ALSA] unlocked/compat_ioctl rewrite for control API # # D:2005/01/20 17:10:23 # C:Control Midlevel # F:core/control.c:1.55->1.56 # F:core/control_compat.c:INITIAL->1.1 # F:include/control.h:1.11->1.12 # L:ioctl handler for control API is rewritten using unlocked/compat_ioctl. # L:The 32bit wrapper is merged to the core module. # L: # L:Added a new register/unregister function for compat control ioctls. # Signed-off-by: Takashi Iwai # # sound/core/control_compat.c # 2005/01/20 19:15:43+01:00 perex@suse.cz +412 -0 # [ALSA] unlocked/compat_ioctl rewrite for control API # # D:2005/01/20 17:10:23 # C:Control Midlevel # F:core/control.c:1.55->1.56 # F:core/control_compat.c:INITIAL->1.1 # F:include/control.h:1.11->1.12 # L:ioctl handler for control API is rewritten using unlocked/compat_ioctl. # L:The 32bit wrapper is merged to the core module. # L: # L:Added a new register/unregister function for compat control ioctls. # Signed-off-by: Takashi Iwai # # sound/core/control_compat.c # 2005/01/20 19:15:43+01:00 perex@suse.cz +0 -0 # BitKeeper file /home/perex/bk/linux-sound/work/sound/core/control_compat.c # # ChangeSet # 2005/01/20 20:23:54+01:00 perex@suse.cz # [ALSA] Add Intel HDA driver # # Documentation,PCI drivers,HDA generic driver,HDA Codec driver # HDA Intel driver # Added a new Intel High-Definition audio driver. # The driver consists of two separate modules: the generic support # module for HD codecs (snd-hda-codec), and the driver for Intel ICH6/7 # chipset (snd-hda-intel). The snd-hda-intel was called formerly # snd-azx in the ALSA 1.0.8 rlease. # # Signed-off-by: Takashi Iwai # # sound/pci/hda/patch_realtek.c # 2005/01/20 19:15:31+01:00 perex@suse.cz +1174 -0 # [ALSA] Add Intel HDA driver # # D:2005/01/20 15:02:27 # C:Documentation,PCI drivers,HDA generic driver,HDA Codec driver # C:HDA Intel driver # F:Documentation/ALSA-Configuration.txt:1.63->1.64 # F:Documentation/hda_codec.txt:INITIAL->1.1 # F:pci/Kconfig:1.39->1.40 # F:pci/Makefile:1.19->1.20 # F:pci/hda/Makefile:INITIAL->1.1 # F:pci/hda/hda_codec.c:INITIAL->1.1 # F:pci/hda/hda_codec.h:INITIAL->1.1 # F:pci/hda/hda_generic.c:INITIAL->1.1 # F:pci/hda/hda_intel.c:INITIAL->1.1 # F:pci/hda/hda_local.h:INITIAL->1.1 # F:pci/hda/hda_patch.h:INITIAL->1.1 # F:pci/hda/hda_proc.c:INITIAL->1.1 # F:pci/hda/patch_cmedia.c:INITIAL->1.1 # F:pci/hda/patch_realtek.c:INITIAL->1.1 # L:Added a new Intel High-Definition audio driver. # L:The driver consists of two separate modules: the generic support # L:module for HD codecs (snd-hda-codec), and the driver for Intel ICH6/7 # L:chipset (snd-hda-intel). The snd-hda-intel was called formerly # L:snd-azx in the ALSA 1.0.8 rlease. # Signed-off-by: Takashi Iwai # # sound/pci/hda/patch_cmedia.c # 2005/01/20 19:15:26+01:00 perex@suse.cz +614 -0 # [ALSA] Add Intel HDA driver # # D:2005/01/20 15:02:27 # C:Documentation,PCI drivers,HDA generic driver,HDA Codec driver # C:HDA Intel driver # F:Documentation/ALSA-Configuration.txt:1.63->1.64 # F:Documentation/hda_codec.txt:INITIAL->1.1 # F:pci/Kconfig:1.39->1.40 # F:pci/Makefile:1.19->1.20 # F:pci/hda/Makefile:INITIAL->1.1 # F:pci/hda/hda_codec.c:INITIAL->1.1 # F:pci/hda/hda_codec.h:INITIAL->1.1 # F:pci/hda/hda_generic.c:INITIAL->1.1 # F:pci/hda/hda_intel.c:INITIAL->1.1 # F:pci/hda/hda_local.h:INITIAL->1.1 # F:pci/hda/hda_patch.h:INITIAL->1.1 # F:pci/hda/hda_proc.c:INITIAL->1.1 # F:pci/hda/patch_cmedia.c:INITIAL->1.1 # F:pci/hda/patch_realtek.c:INITIAL->1.1 # L:Added a new Intel High-Definition audio driver. # L:The driver consists of two separate modules: the generic support # L:module for HD codecs (snd-hda-codec), and the driver for Intel ICH6/7 # L:chipset (snd-hda-intel). The snd-hda-intel was called formerly # L:snd-azx in the ALSA 1.0.8 rlease. # Signed-off-by: Takashi Iwai # # sound/pci/hda/hda_proc.c # 2005/01/20 19:15:21+01:00 perex@suse.cz +298 -0 # [ALSA] Add Intel HDA driver # # D:2005/01/20 15:02:27 # C:Documentation,PCI drivers,HDA generic driver,HDA Codec driver # C:HDA Intel driver # F:Documentation/ALSA-Configuration.txt:1.63->1.64 # F:Documentation/hda_codec.txt:INITIAL->1.1 # F:pci/Kconfig:1.39->1.40 # F:pci/Makefile:1.19->1.20 # F:pci/hda/Makefile:INITIAL->1.1 # F:pci/hda/hda_codec.c:INITIAL->1.1 # F:pci/hda/hda_codec.h:INITIAL->1.1 # F:pci/hda/hda_generic.c:INITIAL->1.1 # F:pci/hda/hda_intel.c:INITIAL->1.1 # F:pci/hda/hda_local.h:INITIAL->1.1 # F:pci/hda/hda_patch.h:INITIAL->1.1 # F:pci/hda/hda_proc.c:INITIAL->1.1 # F:pci/hda/patch_cmedia.c:INITIAL->1.1 # F:pci/hda/patch_realtek.c:INITIAL->1.1 # L:Added a new Intel High-Definition audio driver. # L:The driver consists of two separate modules: the generic support # L:module for HD codecs (snd-hda-codec), and the driver for Intel ICH6/7 # L:chipset (snd-hda-intel). The snd-hda-intel was called formerly # L:snd-azx in the ALSA 1.0.8 rlease. # Signed-off-by: Takashi Iwai # # sound/pci/hda/hda_patch.h # 2005/01/20 19:15:17+01:00 perex@suse.cz +14 -0 # [ALSA] Add Intel HDA driver # # D:2005/01/20 15:02:27 # C:Documentation,PCI drivers,HDA generic driver,HDA Codec driver # C:HDA Intel driver # F:Documentation/ALSA-Configuration.txt:1.63->1.64 # F:Documentation/hda_codec.txt:INITIAL->1.1 # F:pci/Kconfig:1.39->1.40 # F:pci/Makefile:1.19->1.20 # F:pci/hda/Makefile:INITIAL->1.1 # F:pci/hda/hda_codec.c:INITIAL->1.1 # F:pci/hda/hda_codec.h:INITIAL->1.1 # F:pci/hda/hda_generic.c:INITIAL->1.1 # F:pci/hda/hda_intel.c:INITIAL->1.1 # F:pci/hda/hda_local.h:INITIAL->1.1 # F:pci/hda/hda_patch.h:INITIAL->1.1 # F:pci/hda/hda_proc.c:INITIAL->1.1 # F:pci/hda/patch_cmedia.c:INITIAL->1.1 # F:pci/hda/patch_realtek.c:INITIAL->1.1 # L:Added a new Intel High-Definition audio driver. # L:The driver consists of two separate modules: the generic support # L:module for HD codecs (snd-hda-codec), and the driver for Intel ICH6/7 # L:chipset (snd-hda-intel). The snd-hda-intel was called formerly # L:snd-azx in the ALSA 1.0.8 rlease. # Signed-off-by: Takashi Iwai # # sound/pci/hda/hda_local.h # 2005/01/20 19:15:12+01:00 perex@suse.cz +159 -0 # [ALSA] Add Intel HDA driver # # D:2005/01/20 15:02:27 # C:Documentation,PCI drivers,HDA generic driver,HDA Codec driver # C:HDA Intel driver # F:Documentation/ALSA-Configuration.txt:1.63->1.64 # F:Documentation/hda_codec.txt:INITIAL->1.1 # F:pci/Kconfig:1.39->1.40 # F:pci/Makefile:1.19->1.20 # F:pci/hda/Makefile:INITIAL->1.1 # F:pci/hda/hda_codec.c:INITIAL->1.1 # F:pci/hda/hda_codec.h:INITIAL->1.1 # F:pci/hda/hda_generic.c:INITIAL->1.1 # F:pci/hda/hda_intel.c:INITIAL->1.1 # F:pci/hda/hda_local.h:INITIAL->1.1 # F:pci/hda/hda_patch.h:INITIAL->1.1 # F:pci/hda/hda_proc.c:INITIAL->1.1 # F:pci/hda/patch_cmedia.c:INITIAL->1.1 # F:pci/hda/patch_realtek.c:INITIAL->1.1 # L:Added a new Intel High-Definition audio driver. # L:The driver consists of two separate modules: the generic support # L:module for HD codecs (snd-hda-codec), and the driver for Intel ICH6/7 # L:chipset (snd-hda-intel). The snd-hda-intel was called formerly # L:snd-azx in the ALSA 1.0.8 rlease. # Signed-off-by: Takashi Iwai # # sound/pci/hda/hda_intel.c # 2005/01/20 19:15:07+01:00 perex@suse.cz +1449 -0 # [ALSA] Add Intel HDA driver # # D:2005/01/20 15:02:27 # C:Documentation,PCI drivers,HDA generic driver,HDA Codec driver # C:HDA Intel driver # F:Documentation/ALSA-Configuration.txt:1.63->1.64 # F:Documentation/hda_codec.txt:INITIAL->1.1 # F:pci/Kconfig:1.39->1.40 # F:pci/Makefile:1.19->1.20 # F:pci/hda/Makefile:INITIAL->1.1 # F:pci/hda/hda_codec.c:INITIAL->1.1 # F:pci/hda/hda_codec.h:INITIAL->1.1 # F:pci/hda/hda_generic.c:INITIAL->1.1 # F:pci/hda/hda_intel.c:INITIAL->1.1 # F:pci/hda/hda_local.h:INITIAL->1.1 # F:pci/hda/hda_patch.h:INITIAL->1.1 # F:pci/hda/hda_proc.c:INITIAL->1.1 # F:pci/hda/patch_cmedia.c:INITIAL->1.1 # F:pci/hda/patch_realtek.c:INITIAL->1.1 # L:Added a new Intel High-Definition audio driver. # L:The driver consists of two separate modules: the generic support # L:module for HD codecs (snd-hda-codec), and the driver for Intel ICH6/7 # L:chipset (snd-hda-intel). The snd-hda-intel was called formerly # L:snd-azx in the ALSA 1.0.8 rlease. # Signed-off-by: Takashi Iwai # # sound/pci/hda/hda_generic.c # 2005/01/20 19:15:01+01:00 perex@suse.cz +898 -0 # [ALSA] Add Intel HDA driver # # D:2005/01/20 15:02:27 # C:Documentation,PCI drivers,HDA generic driver,HDA Codec driver # C:HDA Intel driver # F:Documentation/ALSA-Configuration.txt:1.63->1.64 # F:Documentation/hda_codec.txt:INITIAL->1.1 # F:pci/Kconfig:1.39->1.40 # F:pci/Makefile:1.19->1.20 # F:pci/hda/Makefile:INITIAL->1.1 # F:pci/hda/hda_codec.c:INITIAL->1.1 # F:pci/hda/hda_codec.h:INITIAL->1.1 # F:pci/hda/hda_generic.c:INITIAL->1.1 # F:pci/hda/hda_intel.c:INITIAL->1.1 # F:pci/hda/hda_local.h:INITIAL->1.1 # F:pci/hda/hda_patch.h:INITIAL->1.1 # F:pci/hda/hda_proc.c:INITIAL->1.1 # F:pci/hda/patch_cmedia.c:INITIAL->1.1 # F:pci/hda/patch_realtek.c:INITIAL->1.1 # L:Added a new Intel High-Definition audio driver. # L:The driver consists of two separate modules: the generic support # L:module for HD codecs (snd-hda-codec), and the driver for Intel ICH6/7 # L:chipset (snd-hda-intel). The snd-hda-intel was called formerly # L:snd-azx in the ALSA 1.0.8 rlease. # Signed-off-by: Takashi Iwai # # sound/pci/hda/hda_codec.h # 2005/01/20 19:14:56+01:00 perex@suse.cz +602 -0 # [ALSA] Add Intel HDA driver # # D:2005/01/20 15:02:27 # C:Documentation,PCI drivers,HDA generic driver,HDA Codec driver # C:HDA Intel driver # F:Documentation/ALSA-Configuration.txt:1.63->1.64 # F:Documentation/hda_codec.txt:INITIAL->1.1 # F:pci/Kconfig:1.39->1.40 # F:pci/Makefile:1.19->1.20 # F:pci/hda/Makefile:INITIAL->1.1 # F:pci/hda/hda_codec.c:INITIAL->1.1 # F:pci/hda/hda_codec.h:INITIAL->1.1 # F:pci/hda/hda_generic.c:INITIAL->1.1 # F:pci/hda/hda_intel.c:INITIAL->1.1 # F:pci/hda/hda_local.h:INITIAL->1.1 # F:pci/hda/hda_patch.h:INITIAL->1.1 # F:pci/hda/hda_proc.c:INITIAL->1.1 # F:pci/hda/patch_cmedia.c:INITIAL->1.1 # F:pci/hda/patch_realtek.c:INITIAL->1.1 # L:Added a new Intel High-Definition audio driver. # L:The driver consists of two separate modules: the generic support # L:module for HD codecs (snd-hda-codec), and the driver for Intel ICH6/7 # L:chipset (snd-hda-intel). The snd-hda-intel was called formerly # L:snd-azx in the ALSA 1.0.8 rlease. # Signed-off-by: Takashi Iwai # # sound/pci/hda/hda_codec.c # 2005/01/20 19:14:51+01:00 perex@suse.cz +1731 -0 # [ALSA] Add Intel HDA driver # # D:2005/01/20 15:02:27 # C:Documentation,PCI drivers,HDA generic driver,HDA Codec driver # C:HDA Intel driver # F:Documentation/ALSA-Configuration.txt:1.63->1.64 # F:Documentation/hda_codec.txt:INITIAL->1.1 # F:pci/Kconfig:1.39->1.40 # F:pci/Makefile:1.19->1.20 # F:pci/hda/Makefile:INITIAL->1.1 # F:pci/hda/hda_codec.c:INITIAL->1.1 # F:pci/hda/hda_codec.h:INITIAL->1.1 # F:pci/hda/hda_generic.c:INITIAL->1.1 # F:pci/hda/hda_intel.c:INITIAL->1.1 # F:pci/hda/hda_local.h:INITIAL->1.1 # F:pci/hda/hda_patch.h:INITIAL->1.1 # F:pci/hda/hda_proc.c:INITIAL->1.1 # F:pci/hda/patch_cmedia.c:INITIAL->1.1 # F:pci/hda/patch_realtek.c:INITIAL->1.1 # L:Added a new Intel High-Definition audio driver. # L:The driver consists of two separate modules: the generic support # L:module for HD codecs (snd-hda-codec), and the driver for Intel ICH6/7 # L:chipset (snd-hda-intel). The snd-hda-intel was called formerly # L:snd-azx in the ALSA 1.0.8 rlease. # Signed-off-by: Takashi Iwai # # sound/pci/hda/Makefile # 2005/01/20 19:14:46+01:00 perex@suse.cz +7 -0 # [ALSA] Add Intel HDA driver # # D:2005/01/20 15:02:27 # C:Documentation,PCI drivers,HDA generic driver,HDA Codec driver # C:HDA Intel driver # F:Documentation/ALSA-Configuration.txt:1.63->1.64 # F:Documentation/hda_codec.txt:INITIAL->1.1 # F:pci/Kconfig:1.39->1.40 # F:pci/Makefile:1.19->1.20 # F:pci/hda/Makefile:INITIAL->1.1 # F:pci/hda/hda_codec.c:INITIAL->1.1 # F:pci/hda/hda_codec.h:INITIAL->1.1 # F:pci/hda/hda_generic.c:INITIAL->1.1 # F:pci/hda/hda_intel.c:INITIAL->1.1 # F:pci/hda/hda_local.h:INITIAL->1.1 # F:pci/hda/hda_patch.h:INITIAL->1.1 # F:pci/hda/hda_proc.c:INITIAL->1.1 # F:pci/hda/patch_cmedia.c:INITIAL->1.1 # F:pci/hda/patch_realtek.c:INITIAL->1.1 # L:Added a new Intel High-Definition audio driver. # L:The driver consists of two separate modules: the generic support # L:module for HD codecs (snd-hda-codec), and the driver for Intel ICH6/7 # L:chipset (snd-hda-intel). The snd-hda-intel was called formerly # L:snd-azx in the ALSA 1.0.8 rlease. # Signed-off-by: Takashi Iwai # # sound/pci/hda/patch_realtek.c # 2005/01/20 19:15:31+01:00 perex@suse.cz +0 -0 # BitKeeper file /home/perex/bk/linux-sound/work/sound/pci/hda/patch_realtek.c # # sound/pci/hda/patch_cmedia.c # 2005/01/20 19:15:26+01:00 perex@suse.cz +0 -0 # BitKeeper file /home/perex/bk/linux-sound/work/sound/pci/hda/patch_cmedia.c # # sound/pci/hda/hda_proc.c # 2005/01/20 19:15:21+01:00 perex@suse.cz +0 -0 # BitKeeper file /home/perex/bk/linux-sound/work/sound/pci/hda/hda_proc.c # # sound/pci/hda/hda_patch.h # 2005/01/20 19:15:17+01:00 perex@suse.cz +0 -0 # BitKeeper file /home/perex/bk/linux-sound/work/sound/pci/hda/hda_patch.h # # sound/pci/hda/hda_local.h # 2005/01/20 19:15:12+01:00 perex@suse.cz +0 -0 # BitKeeper file /home/perex/bk/linux-sound/work/sound/pci/hda/hda_local.h # # sound/pci/hda/hda_intel.c # 2005/01/20 19:15:07+01:00 perex@suse.cz +0 -0 # BitKeeper file /home/perex/bk/linux-sound/work/sound/pci/hda/hda_intel.c # # sound/pci/hda/hda_generic.c # 2005/01/20 19:15:01+01:00 perex@suse.cz +0 -0 # BitKeeper file /home/perex/bk/linux-sound/work/sound/pci/hda/hda_generic.c # # sound/pci/hda/hda_codec.h # 2005/01/20 19:14:56+01:00 perex@suse.cz +0 -0 # BitKeeper file /home/perex/bk/linux-sound/work/sound/pci/hda/hda_codec.h # # sound/pci/hda/hda_codec.c # 2005/01/20 19:14:51+01:00 perex@suse.cz +0 -0 # BitKeeper file /home/perex/bk/linux-sound/work/sound/pci/hda/hda_codec.c # # sound/pci/hda/Makefile # 2005/01/20 19:14:46+01:00 perex@suse.cz +0 -0 # BitKeeper file /home/perex/bk/linux-sound/work/sound/pci/hda/Makefile # # sound/pci/Makefile # 2005/01/20 08:02:28+01:00 perex@suse.cz +1 -0 # [ALSA] Add Intel HDA driver # # D:2005/01/20 15:02:27 # C:Documentation,PCI drivers,HDA generic driver,HDA Codec driver # C:HDA Intel driver # F:Documentation/ALSA-Configuration.txt:1.63->1.64 # F:Documentation/hda_codec.txt:INITIAL->1.1 # F:pci/Kconfig:1.39->1.40 # F:pci/Makefile:1.19->1.20 # F:pci/hda/Makefile:INITIAL->1.1 # F:pci/hda/hda_codec.c:INITIAL->1.1 # F:pci/hda/hda_codec.h:INITIAL->1.1 # F:pci/hda/hda_generic.c:INITIAL->1.1 # F:pci/hda/hda_intel.c:INITIAL->1.1 # F:pci/hda/hda_local.h:INITIAL->1.1 # F:pci/hda/hda_patch.h:INITIAL->1.1 # F:pci/hda/hda_proc.c:INITIAL->1.1 # F:pci/hda/patch_cmedia.c:INITIAL->1.1 # F:pci/hda/patch_realtek.c:INITIAL->1.1 # L:Added a new Intel High-Definition audio driver. # L:The driver consists of two separate modules: the generic support # L:module for HD codecs (snd-hda-codec), and the driver for Intel ICH6/7 # L:chipset (snd-hda-intel). The snd-hda-intel was called formerly # L:snd-azx in the ALSA 1.0.8 rlease. # Signed-off-by: Takashi Iwai # # sound/pci/Kconfig # 2005/01/20 08:02:28+01:00 perex@suse.cz +11 -1 # [ALSA] Add Intel HDA driver # # D:2005/01/20 15:02:27 # C:Documentation,PCI drivers,HDA generic driver,HDA Codec driver # C:HDA Intel driver # F:Documentation/ALSA-Configuration.txt:1.63->1.64 # F:Documentation/hda_codec.txt:INITIAL->1.1 # F:pci/Kconfig:1.39->1.40 # F:pci/Makefile:1.19->1.20 # F:pci/hda/Makefile:INITIAL->1.1 # F:pci/hda/hda_codec.c:INITIAL->1.1 # F:pci/hda/hda_codec.h:INITIAL->1.1 # F:pci/hda/hda_generic.c:INITIAL->1.1 # F:pci/hda/hda_intel.c:INITIAL->1.1 # F:pci/hda/hda_local.h:INITIAL->1.1 # F:pci/hda/hda_patch.h:INITIAL->1.1 # F:pci/hda/hda_proc.c:INITIAL->1.1 # F:pci/hda/patch_cmedia.c:INITIAL->1.1 # F:pci/hda/patch_realtek.c:INITIAL->1.1 # L:Added a new Intel High-Definition audio driver. # L:The driver consists of two separate modules: the generic support # L:module for HD codecs (snd-hda-codec), and the driver for Intel ICH6/7 # L:chipset (snd-hda-intel). The snd-hda-intel was called formerly # L:snd-azx in the ALSA 1.0.8 rlease. # Signed-off-by: Takashi Iwai # # Documentation/sound/alsa/ALSA-Configuration.txt # 2005/01/20 08:02:27+01:00 perex@suse.cz +31 -0 # [ALSA] Add Intel HDA driver # # D:2005/01/20 15:02:27 # C:Documentation,PCI drivers,HDA generic driver,HDA Codec driver # C:HDA Intel driver # F:Documentation/ALSA-Configuration.txt:1.63->1.64 # F:Documentation/hda_codec.txt:INITIAL->1.1 # F:pci/Kconfig:1.39->1.40 # F:pci/Makefile:1.19->1.20 # F:pci/hda/Makefile:INITIAL->1.1 # F:pci/hda/hda_codec.c:INITIAL->1.1 # F:pci/hda/hda_codec.h:INITIAL->1.1 # F:pci/hda/hda_generic.c:INITIAL->1.1 # F:pci/hda/hda_intel.c:INITIAL->1.1 # F:pci/hda/hda_local.h:INITIAL->1.1 # F:pci/hda/hda_patch.h:INITIAL->1.1 # F:pci/hda/hda_proc.c:INITIAL->1.1 # F:pci/hda/patch_cmedia.c:INITIAL->1.1 # F:pci/hda/patch_realtek.c:INITIAL->1.1 # L:Added a new Intel High-Definition audio driver. # L:The driver consists of two separate modules: the generic support # L:module for HD codecs (snd-hda-codec), and the driver for Intel ICH6/7 # L:chipset (snd-hda-intel). The snd-hda-intel was called formerly # L:snd-azx in the ALSA 1.0.8 rlease. # Signed-off-by: Takashi Iwai # # Documentation/sound/alsa/hda_codec.txt # 2005/01/20 19:14:31+01:00 perex@suse.cz +299 -0 # [ALSA] Add Intel HDA driver # # D:2005/01/20 15:02:27 # C:Documentation,PCI drivers,HDA generic driver,HDA Codec driver # C:HDA Intel driver # F:Documentation/ALSA-Configuration.txt:1.63->1.64 # F:Documentation/hda_codec.txt:INITIAL->1.1 # F:pci/Kconfig:1.39->1.40 # F:pci/Makefile:1.19->1.20 # F:pci/hda/Makefile:INITIAL->1.1 # F:pci/hda/hda_codec.c:INITIAL->1.1 # F:pci/hda/hda_codec.h:INITIAL->1.1 # F:pci/hda/hda_generic.c:INITIAL->1.1 # F:pci/hda/hda_intel.c:INITIAL->1.1 # F:pci/hda/hda_local.h:INITIAL->1.1 # F:pci/hda/hda_patch.h:INITIAL->1.1 # F:pci/hda/hda_proc.c:INITIAL->1.1 # F:pci/hda/patch_cmedia.c:INITIAL->1.1 # F:pci/hda/patch_realtek.c:INITIAL->1.1 # L:Added a new Intel High-Definition audio driver. # L:The driver consists of two separate modules: the generic support # L:module for HD codecs (snd-hda-codec), and the driver for Intel ICH6/7 # L:chipset (snd-hda-intel). The snd-hda-intel was called formerly # L:snd-azx in the ALSA 1.0.8 rlease. # Signed-off-by: Takashi Iwai # # Documentation/sound/alsa/hda_codec.txt # 2005/01/20 19:14:31+01:00 perex@suse.cz +0 -0 # BitKeeper file /home/perex/bk/linux-sound/work/Documentation/sound/alsa/hda_codec.txt # # ChangeSet # 2005/01/20 20:22:29+01:00 perex@suse.cz # [ALSA] Enable HP jack sense for FSC Scenic-W # # AC97 Codec # Enable 'Headphone Jack Sense' control on FSC Scenic-W as default, too. # # Signed-off-by: Takashi Iwai # # sound/pci/ac97/ac97_patch.c # 2005/01/20 04:43:19+01:00 perex@suse.cz +1 -0 # [ALSA] Enable HP jack sense for FSC Scenic-W # # D:2005/01/20 11:43:19 # C:AC97 Codec # F:pci/ac97/ac97_patch.c:1.69->1.70 # L:Enable 'Headphone Jack Sense' control on FSC Scenic-W as default, too. # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/20 20:21:09+01:00 perex@suse.cz # [ALSA] Add quirk for HP nc8000 # # Intel8x0 driver # Added ac97 quirk for HP nc8000. # The list is sorted again. # # Signed-off-by: Takashi Iwai # # sound/pci/intel8x0.c # 2005/01/20 04:41:50+01:00 perex@suse.cz +10 -4 # [ALSA] Add quirk for HP nc8000 # # D:2005/01/20 11:41:50 # C:Intel8x0 driver # F:pci/intel8x0.c:1.189->1.190 # L:Added ac97 quirk for HP nc8000. # L:The list is sorted again. # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/20 20:19:48+01:00 perex@suse.cz # [ALSA] Add quirk for HP pavilion ZV5030US # # ATIIXP driver # Added ac97 quirk for HP Pavilion ZV5030US to bind the control with # mute-LED. # # Signed-off-by: Takashi Iwai # # sound/pci/atiixp.c # 2005/01/19 09:43:06+01:00 perex@suse.cz +6 -0 # [ALSA] Add quirk for HP pavilion ZV5030US # # D:2005/01/19 16:43:06 # C:ATIIXP driver # F:pci/atiixp.c:1.30->1.31 # L:Added ac97 quirk for HP Pavilion ZV5030US to bind the control with # L:mute-LED. # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/20 20:18:30+01:00 perex@suse.cz # [ALSA] Simplify the general ac97 volume/switch callback # # AC97 Codec # Simplified the control callbacks of general AC97 volumes/switches. # # Signed-off-by: Takashi Iwai # # sound/pci/ac97/ac97_local.h # 2005/01/19 05:03:51+01:00 perex@suse.cz +2 -4 # [ALSA] Simplify the general ac97 volume/switch callback # # D:2005/01/19 12:03:49 # C:AC97 Codec # F:pci/ac97/ac97_codec.c:1.168->1.169 # F:pci/ac97/ac97_local.h:1.11->1.12 # L:Simplified the control callbacks of general AC97 volumes/switches. # Signed-off-by: Takashi Iwai # # sound/pci/ac97/ac97_codec.c # 2005/01/19 05:03:49+01:00 perex@suse.cz +36 -37 # [ALSA] Simplify the general ac97 volume/switch callback # # D:2005/01/19 12:03:49 # C:AC97 Codec # F:pci/ac97/ac97_codec.c:1.168->1.169 # F:pci/ac97/ac97_local.h:1.11->1.12 # L:Simplified the control callbacks of general AC97 volumes/switches. # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/20 20:17:15+01:00 perex@suse.cz # [ALSA] Add missing inclusion of linux/device.h # # Digigram VX core,Digigram VX222 driver,Digigram VX Pocket driver # Added the missing inclusion of # # Signed-off-by: Takashi Iwai # # sound/pcmcia/vx/vxp_ops.c # 2005/01/19 04:36:30+01:00 perex@suse.cz +1 -0 # [ALSA] Add missing inclusion of linux/device.h # # D:2005/01/19 11:36:29 # C:Digigram VX core,Digigram VX222 driver,Digigram VX Pocket driver # F:drivers/vx/vx_core.c:1.12->1.13 # F:drivers/vx/vx_hwdep.c:1.4->1.5 # F:pci/vx222/vx222_ops.c:1.8->1.9 # F:pcmcia/vx/vxp_ops.c:1.5->1.6 # L:Added the missing inclusion of # Signed-off-by: Takashi Iwai # # sound/pci/vx222/vx222_ops.c # 2005/01/19 04:36:30+01:00 perex@suse.cz +1 -0 # [ALSA] Add missing inclusion of linux/device.h # # D:2005/01/19 11:36:29 # C:Digigram VX core,Digigram VX222 driver,Digigram VX Pocket driver # F:drivers/vx/vx_core.c:1.12->1.13 # F:drivers/vx/vx_hwdep.c:1.4->1.5 # F:pci/vx222/vx222_ops.c:1.8->1.9 # F:pcmcia/vx/vxp_ops.c:1.5->1.6 # L:Added the missing inclusion of # Signed-off-by: Takashi Iwai # # sound/drivers/vx/vx_hwdep.c # 2005/01/19 04:36:29+01:00 perex@suse.cz +1 -0 # [ALSA] Add missing inclusion of linux/device.h # # D:2005/01/19 11:36:29 # C:Digigram VX core,Digigram VX222 driver,Digigram VX Pocket driver # F:drivers/vx/vx_core.c:1.12->1.13 # F:drivers/vx/vx_hwdep.c:1.4->1.5 # F:pci/vx222/vx222_ops.c:1.8->1.9 # F:pcmcia/vx/vxp_ops.c:1.5->1.6 # L:Added the missing inclusion of # Signed-off-by: Takashi Iwai # # sound/drivers/vx/vx_core.c # 2005/01/19 04:36:29+01:00 perex@suse.cz +1 -0 # [ALSA] Add missing inclusion of linux/device.h # # D:2005/01/19 11:36:29 # C:Digigram VX core,Digigram VX222 driver,Digigram VX Pocket driver # F:drivers/vx/vx_core.c:1.12->1.13 # F:drivers/vx/vx_hwdep.c:1.4->1.5 # F:pci/vx222/vx222_ops.c:1.8->1.9 # F:pcmcia/vx/vxp_ops.c:1.5->1.6 # L:Added the missing inclusion of # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/20 20:15:52+01:00 perex@suse.cz # [ALSA] Add workaround for buggy ATI IXP hardwares # # ATIIXP-modem driver # Added a workaround for buggy ATI IXP hardwares which returns # bogus DMA pointer register value. # # Signed-off-by: Takashi Iwai # # sound/pci/atiixp_modem.c # 2005/01/19 04:35:04+01:00 perex@suse.cz +11 -12 # [ALSA] Add workaround for buggy ATI IXP hardwares # # D:2005/01/19 11:35:04 # C:ATIIXP-modem driver # F:pci/atiixp_modem.c:1.12->1.13 # L:Added a workaround for buggy ATI IXP hardwares which returns # L:bogus DMA pointer register value. # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/20 20:14:34+01:00 perex@suse.cz # [ALSA] Add support for Chaintech 9CJS # # ICE1712 driver # Added the support for Chaintech 9CJS by Delmaire Maxime. # # Signed-off-by: Takashi Iwai # # sound/pci/ice1712/vt1720_mobo.h # 2005/01/19 04:32:46+01:00 perex@suse.cz +3 -1 # [ALSA] Add support for Chaintech 9CJS # # D:2005/01/19 11:32:46 # C:ICE1712 driver # F:pci/ice1712/vt1720_mobo.c:1.2->1.3 # F:pci/ice1712/vt1720_mobo.h:1.2->1.3 # L:Added the support for Chaintech 9CJS by Delmaire Maxime. # Signed-off-by: Takashi Iwai # # sound/pci/ice1712/vt1720_mobo.c # 2005/01/19 04:32:46+01:00 perex@suse.cz +9 -0 # [ALSA] Add support for Chaintech 9CJS # # D:2005/01/19 11:32:46 # C:ICE1712 driver # F:pci/ice1712/vt1720_mobo.c:1.2->1.3 # F:pci/ice1712/vt1720_mobo.h:1.2->1.3 # L:Added the support for Chaintech 9CJS by Delmaire Maxime. # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/20 20:13:15+01:00 perex@suse.cz # [ALSA] AK4117 code - fixed cosmetic typos # # AK4117 receiver # # # Signed-off-by: Jaroslav Kysela # # include/sound/ak4117.h # 2005/01/18 08:41:15+01:00 perex@suse.cz +3 -3 # [ALSA] AK4117 code - fixed cosmetic typos # # D:2005/01/18 15:41:15 # C:AK4117 receiver # F:include/ak4117.h:1.1->1.2 # L: # Signed-off-by: Jaroslav Kysela # # ChangeSet # 2005/01/20 20:11:59+01:00 perex@suse.cz # [ALSA] don't use broken legacy interfaces on M-Audio Quattro/Omnistudio # # USB generic driver # Interfaces 0-2 of M-Audio Quattro/Omnistudio devices duplicate functionality # of interfaces 3-5 and cause errors when used with those. Add a quirk to # tell the driver not to use them. # # Signed-off-by: Clemens Ladisch # # sound/usb/usbquirks.h # 2005/01/17 10:41:38+01:00 perex@suse.cz +70 -5 # [ALSA] don't use broken legacy interfaces on M-Audio Quattro/Omnistudio # # D:2005/01/17 17:41:34 # C:USB generic driver # F:usb/usbaudio.c:1.113->1.114 # F:usb/usbaudio.h:1.35->1.36 # F:usb/usbquirks.h:1.38->1.39 # L:Interfaces 0-2 of M-Audio Quattro/Omnistudio devices duplicate functionality # L:of interfaces 3-5 and cause errors when used with those. Add a quirk to # L:tell the driver not to use them. # Signed-off-by: Clemens Ladisch # # sound/usb/usbaudio.h # 2005/01/17 10:41:38+01:00 perex@suse.cz +3 -0 # [ALSA] don't use broken legacy interfaces on M-Audio Quattro/Omnistudio # # D:2005/01/17 17:41:34 # C:USB generic driver # F:usb/usbaudio.c:1.113->1.114 # F:usb/usbaudio.h:1.35->1.36 # F:usb/usbquirks.h:1.38->1.39 # L:Interfaces 0-2 of M-Audio Quattro/Omnistudio devices duplicate functionality # L:of interfaces 3-5 and cause errors when used with those. Add a quirk to # L:tell the driver not to use them. # Signed-off-by: Clemens Ladisch # # sound/usb/usbaudio.c # 2005/01/17 10:41:34+01:00 perex@suse.cz +2 -0 # [ALSA] don't use broken legacy interfaces on M-Audio Quattro/Omnistudio # # D:2005/01/17 17:41:34 # C:USB generic driver # F:usb/usbaudio.c:1.113->1.114 # F:usb/usbaudio.h:1.35->1.36 # F:usb/usbquirks.h:1.38->1.39 # L:Interfaces 0-2 of M-Audio Quattro/Omnistudio devices duplicate functionality # L:of interfaces 3-5 and cause errors when used with those. Add a quirk to # L:tell the driver not to use them. # Signed-off-by: Clemens Ladisch # # ChangeSet # 2005/01/20 20:10:41+01:00 perex@suse.cz # [ALSA] Fix silent output on some machines with AD1981x codecs # # AC97 Codec # Fixed the default state of 'Headphone Jack Sense' switch on AD1981x # codecs. Setting this on affects the output of some machines (e.g. # Thindpads). # # The default value is set on only hardwares which are known to work. # # Signed-off-by: Takashi Iwai # # sound/pci/ac97/ac97_patch.c # 2005/01/17 06:47:20+01:00 perex@suse.cz +14 -2 # [ALSA] Fix silent output on some machines with AD1981x codecs # # D:2005/01/17 13:47:20 # C:AC97 Codec # F:pci/ac97/ac97_patch.c:1.68->1.69 # L:Fixed the default state of 'Headphone Jack Sense' switch on AD1981x # L:codecs. Setting this on affects the output of some machines (e.g. # L:Thindpads). # L: # L:The default value is set on only hardwares which are known to work. # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/20 20:09:28+01:00 perex@suse.cz # [ALSA] AC'97 Audio support for Intel ICH7 # # Intel8x0 driver # This patch adds the ICH7 AC'97 DID the the intel8x0.c AC'97 audio # driver. This patch was build against 2.6.11-rc1. # # Signed-off-by: Jason Gaston # Signed-off-by: Takashi Iwai # # sound/pci/intel8x0.c # 2005/01/17 04:29:18+01:00 perex@suse.cz +6 -0 # [ALSA] AC'97 Audio support for Intel ICH7 # # D:2005/01/17 11:29:18 # C:Intel8x0 driver # F:pci/intel8x0.c:1.188->1.189 # L:This patch adds the ICH7 AC'97 DID the the intel8x0.c AC'97 audio # L:driver. This patch was build against 2.6.11-rc1. # Signed-off-by: Jason Gaston # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/20 20:08:09+01:00 perex@suse.cz # [ALSA] Fix compilation on big-endian arch # # RME HDSP driver # Fixed typo in the code for big-endian architectures. # # Signed-off-by: Takashi Iwai # # sound/pci/rme9652/hdsp.c # 2005/01/14 12:52:06+01:00 perex@suse.cz +1 -1 # [ALSA] Fix compilation on big-endian arch # # D:2005/01/14 19:52:06 # C:RME HDSP driver # F:pci/rme9652/hdsp.c:1.78->1.79 # L:Fixed typo in the code for big-endian architectures. # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/20 20:07:01+01:00 perex@suse.cz # [ALSA] Show firmware loading state in proc file # # Digigram VX core # Show the firmware loading state in proc file. # # Signed-off-by: Takashi Iwai # # sound/drivers/vx/vx_core.c # 2005/01/14 10:41:36+01:00 perex@suse.cz +4 -0 # [ALSA] Show firmware loading state in proc file # # D:2005/01/14 17:41:36 # C:Digigram VX core # F:drivers/vx/vx_core.c:1.11->1.12 # L:Show the firmware loading state in proc file. # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/20 20:05:33+01:00 perex@suse.cz # [ALSA] Fix struct size mismatch # # IOCTL32 emulation # Fixed the struct size mismatch - should work on SPARC64 now, too. # # Signed-off-by: Takashi Iwai # # sound/core/ioctl32/ioctl32.c # 2005/01/14 04:15:16+01:00 perex@suse.cz +1 -1 # [ALSA] Fix struct size mismatch # # D:2005/01/14 11:15:16 # C:IOCTL32 emulation # F:core/ioctl32/ioctl32.c:1.28->1.29 # L:Fixed the struct size mismatch - should work on SPARC64 now, too. # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/20 20:04:23+01:00 perex@suse.cz # [ALSA] Add missing FORWARD ioctl # # IOCTL32 emulation # Added the missing FORWARD ioctl. # # Signed-off-by: Takashi Iwai # # sound/core/ioctl32/pcm32.c # 2005/01/14 03:51:12+01:00 perex@suse.cz +3 -0 # [ALSA] Add missing FORWARD ioctl # # D:2005/01/14 10:51:12 # C:IOCTL32 emulation # F:core/ioctl32/pcm32.c:1.23->1.24 # L:Added the missing FORWARD ioctl. # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/20 20:03:02+01:00 perex@suse.cz # [ALSA] Fix struct alignment on PPC64 # # IOCTL32 emulation # Fixed the struct size mismatch (due to alignment) of # snd_ctl_elem_value_t for PPC64. # # Signed-off-by: Takashi Iwai # # sound/core/ioctl32/ioctl32.c # 2005/01/14 03:50:29+01:00 perex@suse.cz +3 -0 # [ALSA] Fix struct alignment on PPC64 # # D:2005/01/14 10:50:29 # C:IOCTL32 emulation # F:core/ioctl32/ioctl32.c:1.27->1.28 # L:Fixed the struct size mismatch (due to alignment) of # L:snd_ctl_elem_value_t for PPC64. # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/20 20:01:42+01:00 perex@suse.cz # [ALSA] fix typo # # Documentation # # # Signed-off-by: Clemens Ladisch # # Documentation/sound/alsa/ALSA-Configuration.txt # 2005/01/14 03:15:32+01:00 perex@suse.cz +1 -1 # [ALSA] fix typo # # D:2005/01/14 10:15:32 # C:Documentation # F:Documentation/ALSA-Configuration.txt:1.62->1.63 # L: # Signed-off-by: Clemens Ladisch # # ChangeSet # 2005/01/20 20:00:22+01:00 perex@suse.cz # [ALSA] Fix typos in doc # # Documentation # Fixed typos in the document by Kirill Smelkov # # Signed-off-by: Takashi Iwai # # Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl # 2005/01/13 10:29:03+01:00 perex@suse.cz +16 -13 # [ALSA] Fix typos in doc # # D:2005/01/13 17:29:03 # C:Documentation # F:Documentation/DocBook/writing-an-alsa-driver.tmpl:1.46->1.47 # L:Fixed typos in the document by Kirill Smelkov # Signed-off-by: Takashi Iwai # # ChangeSet # 2005/01/20 19:58:45+01:00 perex@suse.cz # ALSA CVS update # PCM Midlevel # Sumary: Fix comment of snd_pcm_lib_malloc_pages() # # Fixed comment of snd_pcm_lib_malloc_pages() by # Kirill Smelkov . # # Signed-off-by: Takashi Iwai # # sound/core/pcm_memory.c # 2005/01/13 10:27:29+01:00 perex@suse.cz +1 -1 # ALSA CVS update # D:2005/01/13 17:27:29 # C:PCM Midlevel # F:core/pcm_memory.c:1.33->1.34 # L:Sumary: Fix comment of snd_pcm_lib_malloc_pages() # L: # L:Fixed comment of snd_pcm_lib_malloc_pages() by # L:Kirill Smelkov . # Signed-off-by: Takashi Iwai # diff -Nru a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt --- a/Documentation/sound/alsa/ALSA-Configuration.txt 2005-01-28 14:22:13 -08:00 +++ b/Documentation/sound/alsa/ALSA-Configuration.txt 2005-01-28 14:22:13 -08:00 @@ -599,6 +599,37 @@ Module supports up to 8 cards and autoprobe. + Module snd-hda-intel + -------------------- + + Module for Intel HD Audio (ICH6, ICH6M, ICH7) + + model - force the model name + + Module supports up to 8 cards. + + Each codec may have a model table for different configurations. + If your machine isn't listed there, the default (usually minimal) + configuration is set up. You can pass "model=" option to + specify a certain model in such a case. There are different + models depending on the codec chip. + + Model name Description + ---------- ----------- + ALC880 + 3stack 3-jack in back and a headphone out + 3stack-digout 3-jack in back, a HP out and a SPDIF out + 5stack 5-jack in back, 2-jack in front + 5stack-digout 5-jack in back, 2-jack in front, a SPDIF out + w810 3-jack + + CMI9880 + minimal 3-jack in back + min_fp 3-jack in back, 2-jack in front + full 6-jack in back, 2-jack in front + full_dig 6-jack in back, 2-jack in front, SPDIF I/O + allout 5-jack in back, 2-jack in front, SPDIF out + Module snd-hdsp --------------- @@ -689,7 +720,7 @@ hp_only = use headphone control as master swap_hp = swap headphone and master controls swap_surround = swap master and surround controls - ad_shring = for AD1985, turn on OMS bit and use headphone + ad_sharing = for AD1985, turn on OMS bit and use headphone alc_jack = for ALC65x, turn on the jack sense mode inv_eapd = inverted EAPD implementation mute_led = bind EAPD bit for turning on/off mute LED diff -Nru a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl --- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl 2005-01-28 14:22:13 -08:00 +++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl 2005-01-28 14:22:13 -08:00 @@ -110,9 +110,9 @@ - One is the the trees provided as a tarball or via cvs from the + One is the trees provided as a tarball or via cvs from the ALSA's ftp site, and another is the 2.6 (or later) Linux kernel - tree. To synchronize both, the ALSA driver tree is split to + tree. To synchronize both, the ALSA driver tree is split into two different trees: alsa-kernel and alsa-driver. The former contains purely the source codes for the Linux 2.6 (or later) tree. This tree is designed only for compilation on 2.6 or @@ -766,7 +766,7 @@ - The ALSA interfaces like PCM or control API are define in other + The ALSA interfaces like PCM or control API are defined in other header files as <sound/xxx.h>. They have to be included after <sound/core.h>. @@ -1103,7 +1103,7 @@ /* release the irq */ if (chip->irq >= 0) free_irq(chip->irq, (void *)chip); - /* release the i/o ports */ + /* release the i/o ports & memory */ pci_release_regions(chip->pci); /* disable the PCI entry */ pci_disable_device(chip->pci); @@ -1314,6 +1314,7 @@ + It will reserve the i/o port region of 8 bytes of the given PCI device. The returned value, chip->res_port, is allocated via kmalloc() by @@ -1936,6 +1937,7 @@ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mychip_capture_ops); /* pre-allocation of buffers */ + /* NOTE: this may fail */ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), 64*1024, 64*1024); @@ -1950,7 +1952,7 @@
Constructor - A pcm instance is allocated snd_pcm_new() + A pcm instance is allocated by snd_pcm_new() function. It would be better to create a constructor for pcm, namely, @@ -2235,7 +2237,8 @@ unsigned char *dma_area; /* DMA area */ dma_addr_t dma_addr; /* physical bus address (not accessible from main CPU) */ size_t dma_bytes; /* size of DMA area */ - void *dma_private; /* private DMA data for the memory allocator */ + + struct snd_dma_buffer *dma_buffer_p; /* allocated buffer */ #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) /* -- OSS things -- */ @@ -2250,7 +2253,7 @@ For the operators (callbacks) of each sound driver, most of these records are supposed to be read-only. Only the PCM - middle-layer changes / updates these info. The excpetions are + middle-layer changes / updates these info. The exceptions are the hardware description (hw), interrupt callbacks (transfer_ack_xxx), DMA buffer information, and the private data. Besides, if you use the standard buffer allocation @@ -3250,7 +3253,7 @@ There are many different constraints. - Look in sound/asound.h for a complete list. + Look in sound/pcm.h for a complete list. You can even define your own constraint rules. For example, let's suppose my_chip can manage a substream of 1 channel if and only if the format is S16_LE, otherwise it supports any format @@ -4066,7 +4069,7 @@ Both snd_ac97_write() and snd_ac97_update() functions are used to set a value to the given register - (AC97_XXX). The different between them is + (AC97_XXX). The difference between them is that snd_ac97_update() doesn't write a value if the given value has been already set, while snd_ac97_write() always rewrites the @@ -4152,8 +4155,8 @@ Proc Files The ALSA AC97 interface will create a proc file such as - /proc/asound/card0/ac97#0 and - ac97#0regs. You can refer to these files to + /proc/asound/card0/codec97#0/ac97#0-0 and + ac97#0-0+regs. You can refer to these files to see the current status and registers of the codec.
@@ -4633,7 +4636,7 @@ where size is the byte size to be pre-allocated and the max is the maximal size to be changed via prealloc proc file. - The allocator will try to get as the large area as possible + The allocator will try to get as large area as possible within the given size.
@@ -4855,7 +4858,7 @@ If your hardware supports the page table like emu10k1 or the buffer descriptors like via82xx, you can use the scatter-gather (SG) DMA. ALSA provides an interface for handling SG-buffers. - The API is provided in <sound/pcm_sgbuf.h>. + The API is provided in <sound/pcm.h>. diff -Nru a/Documentation/sound/alsa/VIA82xx-mixer.txt b/Documentation/sound/alsa/VIA82xx-mixer.txt --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/sound/alsa/VIA82xx-mixer.txt 2005-01-28 14:22:13 -08:00 @@ -0,0 +1,8 @@ + + VIA82xx mixer + ============= + +On many VIA82xx boards, the 'Input Source Select' mixer control does not work. +Setting it to 'Input2' on such boards will cause recording to hang, or fail +with EIO (input/output error) via OSS emulation. This control should be left +at 'Input1' for such cards. diff -Nru a/Documentation/sound/alsa/hda_codec.txt b/Documentation/sound/alsa/hda_codec.txt --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/sound/alsa/hda_codec.txt 2005-01-28 14:22:13 -08:00 @@ -0,0 +1,299 @@ +Notes on Universal Interface for Intel High Definition Audio Codec +------------------------------------------------------------------ + +Takashi Iwai + + +[Still a draft version] + + +General +======= + +The snd-hda-codec module supports the generic access function for the +High Definition (HD) audio codecs. It's designed to be independent +from the controller code like ac97 codec module. The real accessors +from/to the controller must be implemented in the lowlevel driver. + +The structure of this module is similar with ac97_codec module. +Each codec chip belongs to a bus class which communicates with the +controller. + + +Initialization of Bus Instance +============================== + +The card driver has to create struct hda_bus at first. The template +struct should be filled and passed to the constructor: + +struct hda_bus_template { + void *private_data; + struct pci_dev *pci; + const char *modelname; + struct hda_bus_ops ops; +}; + +The card driver can set and use the private_data field to retrieve its +own data in callback functions. The pci field is used when the patch +needs to check the PCI subsystem IDs, so on. For non-PCI system, it +doesn't have to be set, of course. +The modelname field specifies the board's specific configuration. The +string is passed to the codec parser, and it depends on the parser how +the string is used. +These fields, private_data, pci and modelname are all optional. + +The ops field contains the callback functions as the following: + +struct hda_bus_ops { + int (*command)(struct hda_codec *codec, hda_nid_t nid, int direct, + unsigned int verb, unsigned int parm); + unsigned int (*get_response)(struct hda_codec *codec); + void (*private_free)(struct hda_bus *); +}; + +The command callback is called when the codec module needs to send a +VERB to the controller. It's always a single command. +The get_response callback is called when the codec requires the answer +for the last command. These two callbacks are mandatory and have to +be given. +The last, private_free callback, is optional. It's called in the +destructor to release any necessary data in the lowlevel driver. + +The bus instance is created via snd_hda_bus_new(). You need to pass +the card instance, the template, and the pointer to store the +resultant bus instance. + +int snd_hda_bus_new(snd_card_t *card, const struct hda_bus_template *temp, + struct hda_bus **busp); + +It returns zero if successful. A negative return value means any +error during creation. + + +Creation of Codec Instance +========================== + +Each codec chip on the board is then created on the BUS instance. +To create a codec instance, call snd_hda_codec_new(). + +int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, + struct hda_codec **codecp); + +The first argument is the BUS instance, the second argument is the +address of the codec, and the last one is the pointer to store the +resultant codec instance (can be NULL if not needed). + +The codec is stored in a linked list of bus instance. You can follow +the codec list like: + + struct list_head *p; + struct hda_codec *codec; + list_for_each(p, &bus->codec_list) { + codec = list_entry(p, struct hda_codec, list); + ... + } + +The codec isn't initialized at this stage properly. The +initialization sequence is called when the controls are built later. + + +Codec Access +============ + +To access codec, use snd_codec_read() and snd_codec_write(). +snd_hda_param_read() is for reading parameters. +For writing a sequence of verbs, use snd_hda_sequence_write(). + +To retrieve the number of sub nodes connected to the given node, use +snd_hda_get_sub_nodes(). The connection list can be obtained via +snd_hda_get_connections() call. + +When an unsolicited event happens, pass the event via +snd_hda_queue_unsol_event() so that the codec routines will process it +later. + + +(Mixer) Controls +================ + +To create mixer controls of all codecs, call +snd_hda_build_controls(). It then builds the mixers and does +initialization stuff on each codec. + + +PCM Stuff +========= + +snd_hda_build_pcms() gives the necessary information to create PCM +streams. When it's called, each codec belonging to the bus stores +codec->num_pcms and codec->pcm_info fields. The num_pcms indicates +the number of elements in pcm_info array. The card driver is supposed +to traverse the codec linked list, read the pcm information in +pcm_info array, and build pcm instances according to them. + +The pcm_info array contains the following record: + +/* PCM information for each substream */ +struct hda_pcm_stream { + unsigned int substreams; /* number of substreams, 0 = not exist */ + unsigned int channels_min; /* min. number of channels */ + unsigned int channels_max; /* max. number of channels */ + hda_nid_t nid; /* default NID to query rates/formats/bps, or set up */ + u32 rates; /* supported rates */ + u64 formats; /* supported formats (SNDRV_PCM_FMTBIT_) */ + unsigned int maxbps; /* supported max. bit per sample */ + struct hda_pcm_ops ops; +}; + +/* for PCM creation */ +struct hda_pcm { + char *name; + struct hda_pcm_stream stream[2]; +}; + +The name can be passed to snd_pcm_new(). The stream field contains +the information for playback (SNDRV_PCM_STREAM_PLAYBACK = 0) and +capture (SNDRV_PCM_STREAM_CAPTURE = 1) directions. The card driver +should pass substreams to snd_pcm_new() for the number of substreams +to create. + +The channels_min, channels_max, rates and formats should be copied to +runtime->hw record. They and maxbps fields are used also to compute +the format value for the HDA codec and controller. Call +snd_hda_calc_stream_format() to get the format value. + +The ops field contains the following callback functions: + +struct hda_pcm_ops { + int (*open)(struct hda_pcm_stream *info, struct hda_codec *codec, + snd_pcm_substream_t *substream); + int (*close)(struct hda_pcm_stream *info, struct hda_codec *codec, + snd_pcm_substream_t *substream); + int (*prepare)(struct hda_pcm_stream *info, struct hda_codec *codec, + unsigned int stream_tag, unsigned int format, + snd_pcm_substream_t *substream); + int (*cleanup)(struct hda_pcm_stream *info, struct hda_codec *codec, + snd_pcm_substream_t *substream); +}; + +All are non-NULL, so you can call them safely without NULL check. + +The open callback should be called in PCM open after runtime->hw is +set up. It may override some setting and constraints additionally. +Similarly, the close callback should be called in the PCM close. + +The prepare callback should be called in PCM prepare. This will set +up the codec chip properly for the operation. The cleanup should be +called in hw_free to clean up the configuration. + +The caller should check the return value, at least for open and +prepare callbacks. When a negative value is returned, some error +occurred. + + +Proc Files +========== + +Each codec dumps the widget node information in +/proc/asound/card*/codec#* file. This information would be really +helpful for debugging. Please provide its contents together with the +bug report. + + +Power Management +================ + +It's simple: +Call snd_hda_suspend() in the PM suspend callback. +Call snd_hda_resume() in the PM resume callback. + + +Codec Preset (Patch) +==================== + +To set up and handle the codec functionality fully, each codec may +have a codec preset (patch). It's defined in struct hda_codec_preset: + + struct hda_codec_preset { + unsigned int id; + unsigned int mask; + unsigned int subs; + unsigned int subs_mask; + unsigned int rev; + const char *name; + int (*patch)(struct hda_codec *codec); + }; + +When the codec id and codec subsystem id match with the given id and +subs fields bitwise (with bitmask mask and subs_mask), the callback +patch is called. The patch callback should initialize the codec and +set the codec->patch_ops field. This is defined as below: + + struct hda_codec_ops { + int (*build_controls)(struct hda_codec *codec); + int (*build_pcms)(struct hda_codec *codec); + int (*init)(struct hda_codec *codec); + void (*free)(struct hda_codec *codec); + void (*unsol_event)(struct hda_codec *codec, unsigned int res); + #ifdef CONFIG_PM + int (*suspend)(struct hda_codec *codec, unsigned int state); + int (*resume)(struct hda_codec *codec, unsigned int state); + #endif + }; + +The build_controls callback is called from snd_hda_build_controls(). +Similarly, the build_pcms callback is called from +snd_hda_build_pcms(). The init callback is called after +build_controls to initialize the hardware. +The free callback is called as a destructor. + +The unsol_event callback is called when an unsolicited event is +received. + +The suspend and resume callbacks are for power management. + +Each entry can be NULL if not necessary to be called. + + +Generic Parser +============== + +When the device doesn't match with any given presets, the widgets are +parsed via th generic parser (hda_generic.c). Its support is +limited: no multi-channel support, for example. + + +Digital I/O +=========== + +Call snd_hda_create_spdif_out_ctls() from the patch to create controls +related with SPDIF out. In the patch resume callback, call +snd_hda_resume_spdif(). + + +Helper Functions +================ + +snd_hda_get_codec_name() stores the codec name on the given string. + +snd_hda_check_board_config() can be used to obtain the configuration +information matching with the device. Define the table with struct +hda_board_config entries (zero-terminated), and pass it to the +function. The function checks the modelname given as a module +parameter, and PCI subsystem IDs. If the matching entry is found, it +returns the config field value. + +snd_hda_add_new_ctls() can be used to create and add control entries. +Pass the zero-terminated array of snd_kcontrol_new_t. The same array +can be passed to snd_hda_resume_ctls() for resume. +Note that this will call control->put callback of these entries. So, +put callback should check codec->in_resume and force to restore the +given value if it's non-zero even if the value is identical with the +cached value. + +Macros HDA_CODEC_VOLUME(), HDA_CODEC_MUTE() and their variables can be +used for the entry of snd_kcontrol_new_t. + +The input MUX helper callbacks for such a control are provided, too: +snd_hda_input_mux_info() and snd_hda_input_mux_put(). See +patch_realtek.c for example. diff -Nru a/include/sound/ak4117.h b/include/sound/ak4117.h --- a/include/sound/ak4117.h 2005-01-28 14:22:13 -08:00 +++ b/include/sound/ak4117.h 2005-01-28 14:22:13 -08:00 @@ -106,7 +106,7 @@ #define AK4117_DIF_24L (AK4117_DIF2) /* STDO: 24-bit, left justified */ #define AK4117_DIF_24I2S (AK4117_DIF2|AK4117_DIF0) /* STDO: I2S */ -/* AK4117_REG_INT0_MASK & AK4117_INT1_MASK */ +/* AK4117_REG_INT0_MASK & AK4117_REG_INT1_MASK */ #define AK4117_MULK (1<<7) /* mask enable for UNLOCK bit */ #define AK4117_MPAR (1<<6) /* mask enable for PAR bit */ #define AK4117_MAUTO (1<<5) /* mask enable for AUTO bit */ @@ -181,8 +181,8 @@ int snd_ak4117_create(snd_card_t *card, ak4117_read_t *read, ak4117_write_t *write, unsigned char pgm[5], void *private_data, ak4117_t **r_ak4117); -void snd_ak4117_reg_write(ak4117_t *chip, unsigned char reg, unsigned char mask, unsigned char val); -void snd_ak4117_reinit(ak4117_t *chip); +void snd_ak4117_reg_write(ak4117_t *ak4117, unsigned char reg, unsigned char mask, unsigned char val); +void snd_ak4117_reinit(ak4117_t *ak4117); int snd_ak4117_build(ak4117_t *ak4117, snd_pcm_substream_t *capture_substream); int snd_ak4117_external_rate(ak4117_t *ak4117); int snd_ak4117_check_rate_and_errors(ak4117_t *ak4117, unsigned int flags); diff -Nru a/include/sound/control.h b/include/sound/control.h --- a/include/sound/control.h 2005-01-28 14:22:13 -08:00 +++ b/include/sound/control.h 2005-01-28 14:22:13 -08:00 @@ -119,6 +119,13 @@ int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn); int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn); +#ifdef CONFIG_COMPAT +int snd_ctl_register_ioctl_compat(snd_kctl_ioctl_func_t fcn); +int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn); +#else +#define snd_ctl_register_ioctl_compat(fcn) +#define snd_ctl_unregister_ioctl_compat(fcn) +#endif int snd_ctl_elem_read(snd_card_t *card, snd_ctl_elem_value_t *control); int snd_ctl_elem_write(snd_card_t *card, snd_ctl_file_t *file, snd_ctl_elem_value_t *control); diff -Nru a/include/sound/hwdep.h b/include/sound/hwdep.h --- a/include/sound/hwdep.h 2005-01-28 14:22:13 -08:00 +++ b/include/sound/hwdep.h 2005-01-28 14:22:13 -08:00 @@ -38,6 +38,7 @@ int (*release) (snd_hwdep_t * hw, struct file * file); unsigned int (*poll) (snd_hwdep_t * hw, struct file * file, poll_table * wait); int (*ioctl) (snd_hwdep_t * hw, struct file * file, unsigned int cmd, unsigned long arg); + int (*ioctl_compat) (snd_hwdep_t * hw, struct file * file, unsigned int cmd, unsigned long arg); int (*mmap) (snd_hwdep_t * hw, struct file * file, struct vm_area_struct * vma); int (*dsp_status) (snd_hwdep_t * hw, snd_hwdep_dsp_status_t * status); int (*dsp_load) (snd_hwdep_t * hw, snd_hwdep_dsp_image_t * image); diff -Nru a/sound/core/Kconfig b/sound/core/Kconfig --- a/sound/core/Kconfig 2005-01-28 14:22:13 -08:00 +++ b/sound/core/Kconfig 2005-01-28 14:22:13 -08:00 @@ -81,20 +81,6 @@ To compile this driver as a module, choose M here: the module will be called snd-seq-oss. -config SND_BIT32_EMUL - tristate "Emulation for 32-bit applications" - depends on SND && COMPAT - select SND_PCM - select SND_RAWMIDI - select SND_TIMER - select SND_HWDEP - help - Say Y here to enable the emulation for 32-bit ALSA-native - applications. - - To compile this driver as a module, choose M here: the module - will be called snd-ioctl32. - config SND_RTCTIMER tristate "RTC Timer support" depends on SND && RTC diff -Nru a/sound/core/Makefile b/sound/core/Makefile --- a/sound/core/Makefile 2005-01-28 14:22:13 -08:00 +++ b/sound/core/Makefile 2005-01-28 14:22:13 -08:00 @@ -31,4 +31,3 @@ obj-$(CONFIG_SND_OSSEMUL) += oss/ obj-$(CONFIG_SND_SEQUENCER) += seq/ -obj-$(CONFIG_SND_BIT32_EMUL) += ioctl32/ diff -Nru a/sound/core/control.c b/sound/core/control.c --- a/sound/core/control.c 2005-01-28 14:22:13 -08:00 +++ b/sound/core/control.c 2005-01-28 14:22:13 -08:00 @@ -43,6 +43,9 @@ static DECLARE_RWSEM(snd_ioctl_rwsem); static LIST_HEAD(snd_control_ioctls); +#ifdef CONFIG_COMPAT +static LIST_HEAD(snd_control_compat_ioctls); +#endif static int snd_ctl_open(struct inode *inode, struct file *file) { @@ -595,43 +598,51 @@ return 0; } -static int snd_ctl_elem_info(snd_ctl_file_t *ctl, snd_ctl_elem_info_t __user *_info) +static int snd_ctl_elem_info(snd_ctl_file_t *ctl, snd_ctl_elem_info_t *info) { snd_card_t *card = ctl->card; - snd_ctl_elem_info_t info; snd_kcontrol_t *kctl; snd_kcontrol_volatile_t *vd; unsigned int index_offset; int result; - if (copy_from_user(&info, _info, sizeof(info))) - return -EFAULT; down_read(&card->controls_rwsem); - kctl = snd_ctl_find_id(card, &info.id); + kctl = snd_ctl_find_id(card, &info->id); if (kctl == NULL) { up_read(&card->controls_rwsem); return -ENOENT; } #ifdef CONFIG_SND_DEBUG - info.access = 0; + info->access = 0; #endif - result = kctl->info(kctl, &info); + result = kctl->info(kctl, info); if (result >= 0) { - snd_assert(info.access == 0, ); - index_offset = snd_ctl_get_ioff(kctl, &info.id); + snd_assert(info->access == 0, ); + index_offset = snd_ctl_get_ioff(kctl, &info->id); vd = &kctl->vd[index_offset]; - snd_ctl_build_ioff(&info.id, kctl, index_offset); - info.access = vd->access; + snd_ctl_build_ioff(&info->id, kctl, index_offset); + info->access = vd->access; if (vd->owner) { - info.access |= SNDRV_CTL_ELEM_ACCESS_LOCK; + info->access |= SNDRV_CTL_ELEM_ACCESS_LOCK; if (vd->owner == ctl) - info.access |= SNDRV_CTL_ELEM_ACCESS_OWNER; - info.owner = vd->owner_pid; + info->access |= SNDRV_CTL_ELEM_ACCESS_OWNER; + info->owner = vd->owner_pid; } else { - info.owner = -1; + info->owner = -1; } } up_read(&card->controls_rwsem); + return result; +} + +static int snd_ctl_elem_info_user(snd_ctl_file_t *ctl, snd_ctl_elem_info_t __user *_info) +{ + snd_ctl_elem_info_t info; + int result; + + if (copy_from_user(&info, _info, sizeof(info))) + return -EFAULT; + result = snd_ctl_elem_info(ctl, &info); if (result >= 0) if (copy_to_user(_info, &info, sizeof(info))) return -EFAULT; @@ -816,14 +827,6 @@ struct user_element *ue = kcontrol->private_data; *uinfo = ue->info; - if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) { - uinfo->value.enumerated.items = ue->info.value.enumerated.items; - if (uinfo->value.enumerated.item >= ue->info.value.enumerated.items) - uinfo->value.enumerated.item = 0; - strlcpy(uinfo->value.enumerated.name, - (char *)ue->priv_data + uinfo->value.enumerated.item * 64, - 64); - } return 0; } @@ -851,28 +854,25 @@ kfree(kcontrol->private_data); } -static int snd_ctl_elem_add(snd_ctl_file_t *file, snd_ctl_elem_info_t __user *_info, int replace) +static int snd_ctl_elem_add(snd_ctl_file_t *file, snd_ctl_elem_info_t *info, int replace) { snd_card_t *card = file->card; - snd_ctl_elem_info_t info; snd_kcontrol_t kctl, *_kctl; unsigned int access; - long private_size, extra_size; + long private_size; struct user_element *ue; int idx, err; if (card->user_ctl_count >= MAX_USER_CONTROLS) return -ENOMEM; - if (copy_from_user(&info, _info, sizeof(info))) - return -EFAULT; - if (info.count > 1024) + if (info->count > 1024) return -EINVAL; - access = info.access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : - (info.access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|SNDRV_CTL_ELEM_ACCESS_INACTIVE)); - info.id.numid = 0; + access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : + (info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|SNDRV_CTL_ELEM_ACCESS_INACTIVE)); + info->id.numid = 0; memset(&kctl, 0, sizeof(kctl)); down_write(&card->controls_rwsem); - _kctl = snd_ctl_find_id(card, &info.id); + _kctl = snd_ctl_find_id(card, &info->id); err = 0; if (_kctl) { if (replace) @@ -886,67 +886,50 @@ up_write(&card->controls_rwsem); if (err < 0) return err; - memcpy(&kctl.id, &info.id, sizeof(info.id)); - kctl.count = info.owner ? info.owner : 1; + memcpy(&kctl.id, &info->id, sizeof(info->id)); + kctl.count = info->owner ? info->owner : 1; access |= SNDRV_CTL_ELEM_ACCESS_USER; kctl.info = snd_ctl_elem_user_info; if (access & SNDRV_CTL_ELEM_ACCESS_READ) kctl.get = snd_ctl_elem_user_get; if (access & SNDRV_CTL_ELEM_ACCESS_WRITE) kctl.put = snd_ctl_elem_user_put; - extra_size = 0; - switch (info.type) { + switch (info->type) { case SNDRV_CTL_ELEM_TYPE_BOOLEAN: private_size = sizeof(char); - if (info.count > 128) + if (info->count > 128) return -EINVAL; break; case SNDRV_CTL_ELEM_TYPE_INTEGER: private_size = sizeof(long); - if (info.count > 128) + if (info->count > 128) return -EINVAL; break; case SNDRV_CTL_ELEM_TYPE_INTEGER64: private_size = sizeof(long long); - if (info.count > 64) + if (info->count > 64) return -EINVAL; break; - case SNDRV_CTL_ELEM_TYPE_ENUMERATED: - private_size = sizeof(unsigned int); - if (info.count > 128) - return -EINVAL; - if (info.value.enumerated.items > 128) - return -EINVAL; - extra_size = info.value.enumerated.items * 64; - break; case SNDRV_CTL_ELEM_TYPE_BYTES: private_size = sizeof(unsigned char); - if (info.count > 512) + if (info->count > 512) return -EINVAL; break; case SNDRV_CTL_ELEM_TYPE_IEC958: private_size = sizeof(struct sndrv_aes_iec958); - if (info.count != 1) + if (info->count != 1) return -EINVAL; break; default: return -EINVAL; } - private_size *= info.count; - ue = kcalloc(1, sizeof(struct user_element) + private_size + extra_size, GFP_KERNEL); + private_size *= info->count; + ue = kcalloc(1, sizeof(struct user_element) + private_size, GFP_KERNEL); if (ue == NULL) return -ENOMEM; - ue->info = info; + ue->info = *info; ue->elem_data = (char *)ue + sizeof(ue); ue->elem_data_size = private_size; - if (extra_size) { - ue->priv_data = (char *)ue + sizeof(ue) + private_size; - ue->priv_data_size = extra_size; - if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) { - if (copy_from_user(ue->priv_data, *(char __user **)info.value.enumerated.name, extra_size)) - return -EFAULT; - } - } kctl.private_free = snd_ctl_elem_user_free; _kctl = snd_ctl_new(&kctl, access); if (_kctl == NULL) { @@ -969,6 +952,14 @@ return 0; } +static int snd_ctl_elem_add_user(snd_ctl_file_t *file, snd_ctl_elem_info_t __user *_info, int replace) +{ + snd_ctl_elem_info_t info; + if (copy_from_user(&info, _info, sizeof(info))) + return -EFAULT; + return snd_ctl_elem_add(file, &info, replace); +} + static int snd_ctl_elem_remove(snd_ctl_file_t *file, snd_ctl_elem_id_t __user *_id) { snd_ctl_elem_id_t id; @@ -1039,8 +1030,7 @@ } #endif -static inline int _snd_ctl_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { snd_ctl_file_t *ctl; snd_card_t *card; @@ -1061,7 +1051,7 @@ case SNDRV_CTL_IOCTL_ELEM_LIST: return snd_ctl_elem_list(ctl->card, argp); case SNDRV_CTL_IOCTL_ELEM_INFO: - return snd_ctl_elem_info(ctl, argp); + return snd_ctl_elem_info_user(ctl, argp); case SNDRV_CTL_IOCTL_ELEM_READ: return snd_ctl_elem_read_user(ctl->card, argp); case SNDRV_CTL_IOCTL_ELEM_WRITE: @@ -1071,7 +1061,7 @@ case SNDRV_CTL_IOCTL_ELEM_UNLOCK: return snd_ctl_elem_unlock(ctl, argp); case SNDRV_CTL_IOCTL_ELEM_ADD: - return snd_ctl_elem_add(ctl, argp, 0); + return snd_ctl_elem_add_user(ctl, argp, 0); case SNDRV_CTL_IOCTL_ELEM_REPLACE: return snd_ctl_elem_add(ctl, argp, 1); case SNDRV_CTL_IOCTL_ELEM_REMOVE: @@ -1113,17 +1103,6 @@ return -ENOTTY; } -/* FIXME: need to unlock BKL to allow preemption */ -static int snd_ctl_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int err; - unlock_kernel(); - err = _snd_ctl_ioctl(inode, file, cmd, arg); - lock_kernel(); - return err; -} - static ssize_t snd_ctl_read(struct file *file, char __user *buffer, size_t count, loff_t * offset) { snd_ctl_file_t *ctl; @@ -1199,7 +1178,7 @@ * register the device-specific control-ioctls. * called from each device manager like pcm.c, hwdep.c, etc. */ -int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn) +static int _snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head *lists) { snd_kctl_ioctl_t *pn; @@ -1208,22 +1187,34 @@ return -ENOMEM; pn->fioctl = fcn; down_write(&snd_ioctl_rwsem); - list_add_tail(&pn->list, &snd_control_ioctls); + list_add_tail(&pn->list, lists); up_write(&snd_ioctl_rwsem); return 0; } +int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn) +{ + return _snd_ctl_register_ioctl(fcn, &snd_control_ioctls); +} + +#ifdef CONFIG_COMPAT +int snd_ctl_register_ioctl_compat(snd_kctl_ioctl_func_t fcn) +{ + return _snd_ctl_register_ioctl(fcn, &snd_control_compat_ioctls); +} +#endif + /* * de-register the device-specific control-ioctls. */ -int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn) +static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head *lists) { struct list_head *list; snd_kctl_ioctl_t *p; snd_runtime_check(fcn != NULL, return -EINVAL); down_write(&snd_ioctl_rwsem); - list_for_each(list, &snd_control_ioctls) { + list_for_each(list, lists) { p = list_entry(list, snd_kctl_ioctl_t, list); if (p->fioctl == fcn) { list_del(&p->list); @@ -1237,6 +1228,19 @@ return -EINVAL; } +int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn) +{ + return _snd_ctl_unregister_ioctl(fcn, &snd_control_ioctls); +} + +#ifdef CONFIG_COMPAT +int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn) +{ + return _snd_ctl_unregister_ioctl(fcn, &snd_control_compat_ioctls); +} + +#endif + static int snd_ctl_fasync(int fd, struct file * file, int on) { snd_ctl_file_t *ctl; @@ -1249,6 +1253,15 @@ } /* + * ioctl32 compat + */ +#ifdef CONFIG_COMPAT +#include "control_compat.c" +#else +#define snd_ctl_ioctl_compat NULL +#endif + +/* * INIT PART */ @@ -1259,7 +1272,8 @@ .open = snd_ctl_open, .release = snd_ctl_release, .poll = snd_ctl_poll, - .ioctl = snd_ctl_ioctl, + .unlocked_ioctl = snd_ctl_ioctl, + .compat_ioctl = snd_ctl_ioctl_compat, .fasync = snd_ctl_fasync, }; diff -Nru a/sound/core/control_compat.c b/sound/core/control_compat.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/sound/core/control_compat.c 2005-01-28 14:22:13 -08:00 @@ -0,0 +1,412 @@ +/* + * compat ioctls for control API + * + * Copyright (c) by Takashi Iwai + * + * 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 + */ + +/* this file included from control.c */ + +#include + +struct sndrv_ctl_elem_list32 { + u32 offset; + u32 space; + u32 used; + u32 count; + u32 pids; + unsigned char reserved[50]; +} /* don't set packed attribute here */; + +static int snd_ctl_elem_list_compat(snd_card_t *card, struct sndrv_ctl_elem_list32 __user *data32) +{ + struct sndrv_ctl_elem_list __user *data; + compat_caddr_t ptr; + int err; + + data = compat_alloc_user_space(sizeof(*data)); + + /* offset, space, used, count */ + if (copy_in_user(data, data32, 4 * sizeof(u32))) + return -EFAULT; + /* pids */ + if (get_user(ptr, &data32->pids) || + put_user(compat_ptr(ptr), &data->pids)) + return -EFAULT; + err = snd_ctl_elem_list(card, data); + if (err < 0) + return err; + /* copy the result */ + if (copy_in_user(data32, data, 4 * sizeof(u32))) + return -EFAULT; + return 0; +} + +/* + * control element info + * it uses union, so the things are not easy.. + */ + +struct sndrv_ctl_elem_info32 { + struct sndrv_ctl_elem_id id; // the size of struct is same + s32 type; + u32 access; + u32 count; + s32 owner; + union { + struct { + s32 min; + s32 max; + s32 step; + } integer; + struct { + u64 min; + u64 max; + u64 step; + } integer64; + struct { + u32 items; + u32 item; + char name[64]; + } enumerated; + unsigned char reserved[128]; + } value; + unsigned char reserved[64]; +} __attribute__((packed)); + +static int snd_ctl_elem_info_compat(snd_ctl_file_t *ctl, struct sndrv_ctl_elem_info32 __user *data32) +{ + struct sndrv_ctl_elem_info *data; + int err; + + data = kcalloc(1, sizeof(*data), GFP_KERNEL); + if (! data) + return -ENOMEM; + + err = -EFAULT; + /* copy id */ + if (copy_from_user(&data->id, &data32->id, sizeof(data->id))) + goto error; + /* we need to copy the item index. + * hope this doesn't break anything.. + */ + if (get_user(data->value.enumerated.item, &data32->value.enumerated.item)) + goto error; + err = snd_ctl_elem_info(ctl, data); + if (err < 0) + goto error; + /* restore info to 32bit */ + err = -EFAULT; + /* id, type, access, count */ + if (copy_to_user(&data32->id, &data->id, sizeof(data->id)) || + copy_to_user(&data32->type, &data->type, 3 * sizeof(u32))) + goto error; + if (put_user(data->owner, &data32->owner)) + goto error; + switch (data->type) { + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: + case SNDRV_CTL_ELEM_TYPE_INTEGER: + if (put_user(data->value.integer.min, &data32->value.integer.min) || + put_user(data->value.integer.max, &data32->value.integer.max) || + put_user(data->value.integer.step, &data32->value.integer.step)) + goto error; + break; + case SNDRV_CTL_ELEM_TYPE_INTEGER64: + if (copy_to_user(&data32->value.integer64, + &data->value.integer64, + sizeof(data->value.integer64))) + goto error; + break; + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: + if (copy_to_user(&data32->value.enumerated, + &data->value.enumerated, + sizeof(data->value.enumerated))) + goto error; + break; + default: + break; + } + err = 0; + error: + kfree(data); + return err; +} + +/* read / write */ +struct sndrv_ctl_elem_value32 { + struct sndrv_ctl_elem_id id; + unsigned int indirect; /* bit-field causes misalignment */ + union { + s32 integer[128]; + unsigned char data[512]; +#ifndef CONFIG_X86_64 + s64 integer64[64]; +#endif + } value; + unsigned char reserved[128]; +}; + + +/* get the value type and count of the control */ +static int get_ctl_type(snd_card_t *card, snd_ctl_elem_id_t *id, int *countp) +{ + snd_kcontrol_t *kctl; + snd_ctl_elem_info_t info; + int err; + + down_read(&card->controls_rwsem); + kctl = snd_ctl_find_id(card, id); + if (! kctl) { + up_read(&card->controls_rwsem); + return -ENXIO; + } + info.id = *id; + err = kctl->info(kctl, &info); + up_read(&card->controls_rwsem); + if (err >= 0) { + err = info.type; + *countp = info.count; + } + return err; +} + +static int get_elem_size(int type, int count) +{ + switch (type) { + case SNDRV_CTL_ELEM_TYPE_INTEGER64: + return sizeof(s64) * count; + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: + return sizeof(int) * count; + case SNDRV_CTL_ELEM_TYPE_BYTES: + return 512; + case SNDRV_CTL_ELEM_TYPE_IEC958: + return sizeof(struct sndrv_aes_iec958); + default: + return -1; + } +} + +static int copy_ctl_value_from_user(snd_card_t *card, + struct sndrv_ctl_elem_value *data, + struct sndrv_ctl_elem_value32 __user *data32, + int *typep, int *countp) +{ + int i, type, count, size; + unsigned int indirect; + + if (copy_from_user(&data->id, &data32->id, sizeof(data->id))) + return -EFAULT; + if (get_user(indirect, &data32->indirect)) + return -EFAULT; + if (indirect) + return -EINVAL; + type = get_ctl_type(card, &data->id, &count); + if (type < 0) + return type; + + if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || + type == SNDRV_CTL_ELEM_TYPE_INTEGER) { + for (i = 0; i < count; i++) { + int val; + if (get_user(val, &data32->value.integer[i])) + return -EFAULT; + data->value.integer.value[i] = val; + } + } else { + size = get_elem_size(type, count); + if (size < 0) { + printk(KERN_ERR "snd_ioctl32_ctl_elem_value: unknown type %d\n", type); + return -EINVAL; + } + if (copy_from_user(data->value.bytes.data, + data32->value.data, size)) + return -EFAULT; + } + + *typep = type; + *countp = count; + return 0; +} + +/* restore the value to 32bit */ +static int copy_ctl_value_to_user(struct sndrv_ctl_elem_value32 __user *data32, + struct sndrv_ctl_elem_value *data, + int type, int count) +{ + int i, size; + + if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || + type == SNDRV_CTL_ELEM_TYPE_INTEGER) { + for (i = 0; i < count; i++) { + int val; + val = data->value.integer.value[i]; + if (put_user(val, &data32->value.integer[i])) + return -EFAULT; + } + } else { + size = get_elem_size(type, count); + if (copy_to_user(data32->value.data, + data->value.bytes.data, size)) + return -EFAULT; + } + return 0; +} + +static int snd_ctl_elem_read_user_compat(snd_card_t *card, + struct sndrv_ctl_elem_value32 __user *data32) +{ + struct sndrv_ctl_elem_value *data; + int err, type, count; + + data = kcalloc(1, sizeof(*data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0) + goto error; + if ((err = snd_ctl_elem_read(card, data)) < 0) + goto error; + err = copy_ctl_value_to_user(data32, data, type, count); + error: + kfree(data); + return err; +} + +static int snd_ctl_elem_write_user_compat(snd_ctl_file_t *file, + struct sndrv_ctl_elem_value32 __user *data32) +{ + struct sndrv_ctl_elem_value *data; + int err, type, count; + + data = kcalloc(1, sizeof(*data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + if ((err = copy_ctl_value_from_user(file->card, data, data32, &type, &count)) < 0) + goto error; + if ((err = snd_ctl_elem_write(file->card, file, data)) < 0) + goto error; + err = copy_ctl_value_to_user(data32, data, type, count); + error: + kfree(data); + return err; +} + +/* add or replace a user control */ +static int snd_ctl_elem_add_compat(snd_ctl_file_t *file, + struct sndrv_ctl_elem_info32 __user *data32, + int replace) +{ + struct sndrv_ctl_elem_info *data; + int err; + + data = kcalloc(1, sizeof(*data), GFP_KERNEL); + if (! data) + return -ENOMEM; + + err = -EFAULT; + /* id, type, access, count */ \ + if (copy_from_user(&data->id, &data32->id, sizeof(data->id)) || + copy_from_user(&data->type, &data32->type, 3 * sizeof(u32))) + goto error; + if (get_user(data->owner, &data32->owner) || + get_user(data->type, &data32->type)) + goto error; + switch (data->type) { + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: + case SNDRV_CTL_ELEM_TYPE_INTEGER: + if (get_user(data->value.integer.min, &data32->value.integer.min) || + get_user(data->value.integer.max, &data32->value.integer.max) || + get_user(data->value.integer.step, &data32->value.integer.step)) + goto error; + break; + case SNDRV_CTL_ELEM_TYPE_INTEGER64: + if (copy_from_user(&data->value.integer64, + &data32->value.integer64, + sizeof(data->value.integer64))) + goto error; + break; + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: + if (copy_from_user(&data->value.enumerated, + &data32->value.enumerated, + sizeof(data->value.enumerated))) + goto error; + break; + default: + break; + } + err = snd_ctl_elem_add(file, data, replace); + error: + kfree(data); + return err; +} + +enum { + SNDRV_CTL_IOCTL_ELEM_LIST32 = _IOWR('U', 0x10, struct sndrv_ctl_elem_list32), + SNDRV_CTL_IOCTL_ELEM_INFO32 = _IOWR('U', 0x11, struct sndrv_ctl_elem_info32), + SNDRV_CTL_IOCTL_ELEM_READ32 = _IOWR('U', 0x12, struct sndrv_ctl_elem_value32), + SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct sndrv_ctl_elem_value32), + SNDRV_CTL_IOCTL_ELEM_ADD32 = _IOWR('U', 0x17, struct sndrv_ctl_elem_info32), + SNDRV_CTL_IOCTL_ELEM_REPLACE32 = _IOWR('U', 0x18, struct sndrv_ctl_elem_info32), +}; + +static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) +{ + snd_ctl_file_t *ctl; + struct list_head *list; + void __user *argp = compat_ptr(arg); + int err; + + ctl = file->private_data; + snd_assert(ctl && ctl->card, return -ENXIO); + + switch (cmd) { + case SNDRV_CTL_IOCTL_PVERSION: + case SNDRV_CTL_IOCTL_CARD_INFO: + case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS: + case SNDRV_CTL_IOCTL_POWER: + case SNDRV_CTL_IOCTL_POWER_STATE: + case SNDRV_CTL_IOCTL_ELEM_LOCK: + case SNDRV_CTL_IOCTL_ELEM_UNLOCK: + return snd_ctl_ioctl(file, cmd, (unsigned long)argp); + case SNDRV_CTL_IOCTL_ELEM_LIST32: + return snd_ctl_elem_list_compat(ctl->card, argp); + case SNDRV_CTL_IOCTL_ELEM_INFO32: + return snd_ctl_elem_info_compat(ctl, argp); + case SNDRV_CTL_IOCTL_ELEM_READ32: + return snd_ctl_elem_read_user_compat(ctl->card, argp); + case SNDRV_CTL_IOCTL_ELEM_WRITE32: + return snd_ctl_elem_write_user_compat(ctl, argp); + case SNDRV_CTL_IOCTL_ELEM_ADD32: + return snd_ctl_elem_add_compat(ctl, argp, 0); + case SNDRV_CTL_IOCTL_ELEM_REPLACE32: + return snd_ctl_elem_add_compat(ctl, argp, 1); + } + + down_read(&snd_ioctl_rwsem); + list_for_each(list, &snd_control_compat_ioctls) { + snd_kctl_ioctl_t *p = list_entry(list, snd_kctl_ioctl_t, list); + if (p->fioctl) { + err = p->fioctl(ctl->card, ctl, cmd, arg); + if (err != -ENOIOCTLCMD) { + up_read(&snd_ioctl_rwsem); + return err; + } + } + } + up_read(&snd_ioctl_rwsem); + return -ENOIOCTLCMD; +} diff -Nru a/sound/core/hwdep.c b/sound/core/hwdep.c --- a/sound/core/hwdep.c 2005-01-28 14:22:13 -08:00 +++ b/sound/core/hwdep.c 2005-01-28 14:22:13 -08:00 @@ -232,8 +232,7 @@ return 0; } -static inline int _snd_hwdep_ioctl(struct inode *inode, struct file * file, - unsigned int cmd, unsigned long arg) +static long snd_hwdep_ioctl(struct file * file, unsigned int cmd, unsigned long arg) { snd_hwdep_t *hw = file->private_data; void __user *argp = (void __user *)arg; @@ -252,17 +251,6 @@ return -ENOTTY; } -/* FIXME: need to unlock BKL to allow preemption */ -static int snd_hwdep_ioctl(struct inode *inode, struct file * file, - unsigned int cmd, unsigned long arg) -{ - int err; - unlock_kernel(); - err = _snd_hwdep_ioctl(inode, file, cmd, arg); - lock_kernel(); - return err; -} - static int snd_hwdep_mmap(struct file * file, struct vm_area_struct * vma) { snd_hwdep_t *hw = file->private_data; @@ -315,6 +303,12 @@ return -ENOIOCTLCMD; } +#ifdef CONFIG_COMPAT +#include "hwdep_compat.c" +#else +#define snd_hwdep_ioctl_compat NULL +#endif + /* */ @@ -328,7 +322,8 @@ .open = snd_hwdep_open, .release = snd_hwdep_release, .poll = snd_hwdep_poll, - .ioctl = snd_hwdep_ioctl, + .unlocked_ioctl = snd_hwdep_ioctl, + .compat_ioctl = snd_hwdep_ioctl_compat, .mmap = snd_hwdep_mmap, }; @@ -509,12 +504,14 @@ } snd_hwdep_proc_entry = entry; snd_ctl_register_ioctl(snd_hwdep_control_ioctl); + snd_ctl_register_ioctl_compat(snd_hwdep_control_ioctl); return 0; } static void __exit alsa_hwdep_exit(void) { snd_ctl_unregister_ioctl(snd_hwdep_control_ioctl); + snd_ctl_unregister_ioctl_compat(snd_hwdep_control_ioctl); if (snd_hwdep_proc_entry) { snd_info_unregister(snd_hwdep_proc_entry); snd_hwdep_proc_entry = NULL; diff -Nru a/sound/core/hwdep_compat.c b/sound/core/hwdep_compat.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/sound/core/hwdep_compat.c 2005-01-28 14:22:13 -08:00 @@ -0,0 +1,77 @@ +/* + * 32bit -> 64bit ioctl wrapper for hwdep API + * Copyright (c) by Takashi Iwai + * + * 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 + * + */ + +/* This file is included from hwdep.c */ + +#include + +struct sndrv_hwdep_dsp_image32 { + u32 index; + unsigned char name[64]; + u32 image; /* pointer */ + u32 length; + u32 driver_data; +} /* don't set packed attribute here */; + +static int snd_hwdep_dsp_load_compat(snd_hwdep_t *hw, + struct sndrv_hwdep_dsp_image32 __user *src) +{ + struct sndrv_hwdep_dsp_image *dst; + compat_caddr_t ptr; + u32 val; + + dst = compat_alloc_user_space(sizeof(*dst)); + + /* index and name */ + if (copy_in_user(dst, src, 4 + 64)) + return -EFAULT; + if (get_user(ptr, &src->image) || + put_user(compat_ptr(ptr), &dst->image)) + return -EFAULT; + if (get_user(val, &src->length) || + put_user(val, &dst->length)) + return -EFAULT; + if (get_user(val, &src->driver_data) || + put_user(val, &dst->driver_data)) + return -EFAULT; + + return snd_hwdep_dsp_load(hw, dst); +} + +enum { + SNDRV_HWDEP_IOCTL_DSP_LOAD32 = _IOW('H', 0x03, struct sndrv_hwdep_dsp_image32) +}; + +static long snd_hwdep_ioctl_compat(struct file * file, unsigned int cmd, unsigned long arg) +{ + snd_hwdep_t *hw = file->private_data; + void __user *argp = compat_ptr(arg); + switch (cmd) { + case SNDRV_HWDEP_IOCTL_PVERSION: + case SNDRV_HWDEP_IOCTL_INFO: + case SNDRV_HWDEP_IOCTL_DSP_STATUS: + return snd_hwdep_ioctl(file, cmd, (unsigned long)argp); + case SNDRV_HWDEP_IOCTL_DSP_LOAD32: + return snd_hwdep_dsp_load_compat(hw, argp); + } + if (hw->ops.ioctl_compat) + return hw->ops.ioctl_compat(hw, file, cmd, arg); + return -ENOIOCTLCMD; +} diff -Nru a/sound/core/init.c b/sound/core/init.c --- a/sound/core/init.c 2005-01-28 14:22:13 -08:00 +++ b/sound/core/init.c 2005-01-28 14:22:13 -08:00 @@ -39,7 +39,7 @@ unsigned int snd_cards_lock = 0; /* locked for registering/using */ snd_card_t *snd_cards[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = NULL}; -rwlock_t snd_card_rwlock = RW_LOCK_UNLOCKED; +DEFINE_RWLOCK(snd_card_rwlock); #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) int (*snd_mixer_oss_notify_callback)(snd_card_t *card, int free_flag); diff -Nru a/sound/core/ioctl32/Makefile b/sound/core/ioctl32/Makefile --- a/sound/core/ioctl32/Makefile 2005-01-28 14:22:13 -08:00 +++ /dev/null Wed Dec 31 16:00:00 196900 @@ -1,11 +0,0 @@ -# -# Makefile for ALSA -# Copyright (c) 1999 by Jaroslav Kysela -# - -snd-ioctl32-objs := ioctl32.o pcm32.o rawmidi32.o timer32.o hwdep32.o -ifneq ($(CONFIG_SND_SEQUENCER),n) - snd-ioctl32-objs += seq32.o -endif - -obj-$(CONFIG_SND_BIT32_EMUL) += snd-ioctl32.o diff -Nru a/sound/core/ioctl32/hwdep32.c b/sound/core/ioctl32/hwdep32.c --- a/sound/core/ioctl32/hwdep32.c 2005-01-28 14:22:13 -08:00 +++ /dev/null Wed Dec 31 16:00:00 196900 @@ -1,73 +0,0 @@ -/* - * 32bit -> 64bit ioctl wrapper for hwdep API - * Copyright (c) by Takashi Iwai - * - * 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 -#include -#include -#include -#include -#include -#include "ioctl32.h" - -struct sndrv_hwdep_dsp_image32 { - u32 index; - unsigned char name[64]; - u32 image; /* pointer */ - u32 length; - u32 driver_data; -} /* don't set packed attribute here */; - -static inline int _snd_ioctl32_hwdep_dsp_image(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl) -{ - struct sndrv_hwdep_dsp_image __user *data, *dst; - struct sndrv_hwdep_dsp_image32 __user *data32, *src; - compat_caddr_t ptr; - - data32 = compat_ptr(arg); - data = compat_alloc_user_space(sizeof(*data)); - - /* index and name */ - if (copy_in_user(data, data32, 4 + 64)) - return -EFAULT; - if (__get_user(ptr, &data32->image) || - __put_user(compat_ptr(ptr), &data->image)) - return -EFAULT; - src = data32; - dst = data; - COPY_CVT(length); - COPY_CVT(driver_data); - return file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)data); -} - -DEFINE_ALSA_IOCTL_ENTRY(hwdep_dsp_image, hwdep_dsp_image, SNDRV_HWDEP_IOCTL_DSP_LOAD); - -#define AP(x) snd_ioctl32_##x - -enum { - SNDRV_HWDEP_IOCTL_DSP_LOAD32 = _IOW('H', 0x03, struct sndrv_hwdep_dsp_image32) -}; - -struct ioctl32_mapper hwdep_mappers[] = { - MAP_COMPAT(SNDRV_HWDEP_IOCTL_PVERSION), - MAP_COMPAT(SNDRV_HWDEP_IOCTL_INFO), - MAP_COMPAT(SNDRV_HWDEP_IOCTL_DSP_STATUS), - { SNDRV_HWDEP_IOCTL_DSP_LOAD32, AP(hwdep_dsp_image) }, - { 0 }, -}; diff -Nru a/sound/core/ioctl32/ioctl32.c b/sound/core/ioctl32/ioctl32.c --- a/sound/core/ioctl32/ioctl32.c 2005-01-28 14:22:13 -08:00 +++ /dev/null Wed Dec 31 16:00:00 196900 @@ -1,430 +0,0 @@ -/* - * 32bit -> 64bit ioctl wrapper for control API - * Copyright (c) by Takashi Iwai - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ioctl32.h" - - -/* - * register/unregister mappers - * exported for other modules - */ - -MODULE_AUTHOR("Takashi Iwai "); -MODULE_DESCRIPTION("ioctl32 wrapper for ALSA"); -MODULE_LICENSE("GPL"); - -int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *)); -int unregister_ioctl32_conversion(unsigned int cmd); - - -int snd_ioctl32_register(struct ioctl32_mapper *mappers) -{ - int err; - struct ioctl32_mapper *m; - - for (m = mappers; m->cmd; m++) { - err = register_ioctl32_conversion(m->cmd, m->handler); - if (err >= 0) - m->registered++; - } - return 0; -} - -void snd_ioctl32_unregister(struct ioctl32_mapper *mappers) -{ - struct ioctl32_mapper *m; - - for (m = mappers; m->cmd; m++) { - if (m->registered) { - unregister_ioctl32_conversion(m->cmd); - m->registered = 0; - } - } -} - - -/* - * compatible wrapper - */ -int snd_ioctl32_compat(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *filp) -{ - if (! filp->f_op || ! filp->f_op->ioctl) - return -ENOTTY; - return filp->f_op->ioctl(filp->f_dentry->d_inode, filp, cmd, arg); -} - - -/* - * Controls - */ - -struct sndrv_ctl_elem_list32 { - u32 offset; - u32 space; - u32 used; - u32 count; - u32 pids; - unsigned char reserved[50]; -} /* don't set packed attribute here */; - -static inline int _snd_ioctl32_ctl_elem_list(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl) -{ - struct sndrv_ctl_elem_list32 __user *data32; - struct sndrv_ctl_elem_list __user *data; - compat_caddr_t ptr; - int err; - - data32 = compat_ptr(arg); - data = compat_alloc_user_space(sizeof(*data)); - - /* offset, space, used, count */ - if (copy_in_user(data, data32, 4 * sizeof(u32))) - return -EFAULT; - /* pids */ - if (__get_user(ptr, &data32->pids) || - __put_user(compat_ptr(ptr), &data->pids)) - return -EFAULT; - err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)data); - if (err < 0) - return err; - /* copy the result */ - if (copy_in_user(data32, data, 4 * sizeof(u32))) - return -EFAULT; - return 0; -} - -DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_list, ctl_elem_list, SNDRV_CTL_IOCTL_ELEM_LIST); - -/* - * control element info - * it uses union, so the things are not easy.. - */ - -struct sndrv_ctl_elem_info32 { - struct sndrv_ctl_elem_id id; // the size of struct is same - s32 type; - u32 access; - u32 count; - s32 owner; - union { - struct { - s32 min; - s32 max; - s32 step; - } integer; - struct { - u64 min; - u64 max; - u64 step; - } integer64; - struct { - u32 items; - u32 item; - char name[64]; - } enumerated; - unsigned char reserved[128]; - } value; - unsigned char reserved[64]; -} __attribute__((packed)); - -static inline int _snd_ioctl32_ctl_elem_info(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl) -{ - struct sndrv_ctl_elem_info __user *data, *src; - struct sndrv_ctl_elem_info32 __user *data32, *dst; - unsigned int type; - int err; - - data32 = compat_ptr(arg); - data = compat_alloc_user_space(sizeof(*data)); - - /* copy id */ - if (copy_in_user(&data->id, &data32->id, sizeof(data->id))) - return -EFAULT; - /* we need to copy the item index. - * hope this doesn't break anything.. - */ - if (copy_in_user(&data->value.enumerated.item, - &data32->value.enumerated.item, - sizeof(data->value.enumerated.item))) - return -EFAULT; - err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)data); - if (err < 0) - return err; - /* restore info to 32bit */ - /* for COPY_CVT macro */ - src = data; - dst = data32; - /* id, type, access, count */ - if (copy_in_user(&data32->id, &data->id, sizeof(data->id)) || - copy_in_user(&data32->type, &data->type, 3 * sizeof(u32))) - return -EFAULT; - COPY_CVT(owner); - __get_user(type, &data->type); - switch (type) { - case SNDRV_CTL_ELEM_TYPE_BOOLEAN: - case SNDRV_CTL_ELEM_TYPE_INTEGER: - COPY_CVT(value.integer.min); - COPY_CVT(value.integer.max); - COPY_CVT(value.integer.step); - break; - case SNDRV_CTL_ELEM_TYPE_INTEGER64: - if (copy_in_user(&data32->value.integer64, - &data->value.integer64, - sizeof(data->value.integer64))) - return -EFAULT; - break; - case SNDRV_CTL_ELEM_TYPE_ENUMERATED: - if (copy_in_user(&data32->value.enumerated, - &data->value.enumerated, - sizeof(data->value.enumerated))) - return -EFAULT; - break; - default: - break; - } - return 0; -} - -DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_info, ctl_elem_info, SNDRV_CTL_IOCTL_ELEM_INFO); - -struct sndrv_ctl_elem_value32 { - struct sndrv_ctl_elem_id id; - unsigned int indirect; /* bit-field causes misalignment */ - union { - s32 integer[128]; /* integer and boolean need conversion */ - unsigned char data[512]; /* others should be compatible */ - } value; - unsigned char reserved[128]; /* not used */ -}; - - -/* hmm, it's so hard to retrieve the value type from the control id.. */ -static int get_ctl_type(snd_card_t *card, snd_ctl_elem_id_t *id) -{ - snd_kcontrol_t *kctl; - snd_ctl_elem_info_t info; - int err; - - down_read(&card->controls_rwsem); - kctl = snd_ctl_find_id(card, id); - if (! kctl) { - up_read(&card->controls_rwsem); - return -ENXIO; - } - info.id = *id; - err = kctl->info(kctl, &info); - up_read(&card->controls_rwsem); - if (err >= 0) - err = info.type; - return err; -} - -extern int snd_major; - -static inline int _snd_ioctl32_ctl_elem_value(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl) -{ - struct sndrv_ctl_elem_value *data; - struct sndrv_ctl_elem_value32 __user *data32; - snd_ctl_file_t *ctl; - int err, i, indirect; - int type; - - /* sanity check */ - if (imajor(file->f_dentry->d_inode) != snd_major || - SNDRV_MINOR_DEVICE(iminor(file->f_dentry->d_inode)) != SNDRV_MINOR_CONTROL) - return -ENOTTY; - - if ((ctl = file->private_data) == NULL) - return -ENOTTY; - - data32 = compat_ptr(arg); - data = kcalloc(1, sizeof(*data), GFP_KERNEL); - if (data == NULL) - return -ENOMEM; - - if (copy_from_user(&data->id, &data32->id, sizeof(data->id))) { - err = -EFAULT; - goto __end; - } - if (__get_user(indirect, &data32->indirect)) { - err = -EFAULT; - goto __end; - } - /* FIXME: indirect access is not supported */ - if (indirect) { - err = -EINVAL; - goto __end; - } - type = get_ctl_type(ctl->card, &data->id); - if (type < 0) { - err = type; - goto __end; - } - - switch (type) { - case SNDRV_CTL_ELEM_TYPE_BOOLEAN: - case SNDRV_CTL_ELEM_TYPE_INTEGER: - for (i = 0; i < 128; i++) { - int val; - if (__get_user(val, &data32->value.integer[i])) { - err = -EFAULT; - goto __end; - } - data->value.integer.value[i] = val; - } - break; - case SNDRV_CTL_ELEM_TYPE_INTEGER64: - case SNDRV_CTL_ELEM_TYPE_ENUMERATED: - case SNDRV_CTL_ELEM_TYPE_BYTES: - case SNDRV_CTL_ELEM_TYPE_IEC958: - if (__copy_from_user(data->value.bytes.data, - data32->value.data, - sizeof(data32->value.data))) { - err = -EFAULT; - goto __end; - } - break; - default: - printk(KERN_ERR "snd_ioctl32_ctl_elem_value: unknown type %d\n", type); - err = -EINVAL; - goto __end; - } - - if (native_ctl == SNDRV_CTL_IOCTL_ELEM_READ) - err = snd_ctl_elem_read(ctl->card, data); - else - err = snd_ctl_elem_write(ctl->card, ctl, data); - if (err < 0) - goto __end; - /* restore info to 32bit */ - switch (type) { - case SNDRV_CTL_ELEM_TYPE_BOOLEAN: - case SNDRV_CTL_ELEM_TYPE_INTEGER: - for (i = 0; i < 128; i++) { - int val; - val = data->value.integer.value[i]; - if (__put_user(val, &data32->value.integer[i])) { - err = -EFAULT; - goto __end; - } - } - break; - default: - if (__copy_to_user(data32->value.data, - data->value.bytes.data, - sizeof(data32->value.data))) { - err = -EFAULT; - goto __end; - } - break; - break; - } - err = 0; - __end: - kfree(data); - return err; -} - -DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_read, ctl_elem_value, SNDRV_CTL_IOCTL_ELEM_READ); -DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_write, ctl_elem_value, SNDRV_CTL_IOCTL_ELEM_WRITE); - -/* - */ - -#define AP(x) snd_ioctl32_##x - -enum { - SNDRV_CTL_IOCTL_ELEM_LIST32 = _IOWR('U', 0x10, struct sndrv_ctl_elem_list32), - SNDRV_CTL_IOCTL_ELEM_INFO32 = _IOWR('U', 0x11, struct sndrv_ctl_elem_info32), - SNDRV_CTL_IOCTL_ELEM_READ32 = _IOWR('U', 0x12, struct sndrv_ctl_elem_value32), - SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct sndrv_ctl_elem_value32), -}; - -static struct ioctl32_mapper control_mappers[] = { - /* controls (without rawmidi, hwdep, timer releated ones) */ - MAP_COMPAT(SNDRV_CTL_IOCTL_PVERSION), - MAP_COMPAT(SNDRV_CTL_IOCTL_CARD_INFO), - { SNDRV_CTL_IOCTL_ELEM_LIST32, AP(ctl_elem_list) }, - { SNDRV_CTL_IOCTL_ELEM_INFO32, AP(ctl_elem_info) }, - { SNDRV_CTL_IOCTL_ELEM_READ32, AP(ctl_elem_read) }, - { SNDRV_CTL_IOCTL_ELEM_WRITE32, AP(ctl_elem_write) }, - MAP_COMPAT(SNDRV_CTL_IOCTL_ELEM_LOCK), - MAP_COMPAT(SNDRV_CTL_IOCTL_ELEM_UNLOCK), - MAP_COMPAT(SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS), - MAP_COMPAT(SNDRV_CTL_IOCTL_HWDEP_INFO), - MAP_COMPAT(SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE), - MAP_COMPAT(SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE), - MAP_COMPAT(SNDRV_CTL_IOCTL_PCM_INFO), - MAP_COMPAT(SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE), - MAP_COMPAT(SNDRV_CTL_IOCTL_POWER), - MAP_COMPAT(SNDRV_CTL_IOCTL_POWER_STATE), - { 0 } -}; - - -/* - */ - -extern struct ioctl32_mapper pcm_mappers[]; -extern struct ioctl32_mapper rawmidi_mappers[]; -extern struct ioctl32_mapper timer_mappers[]; -extern struct ioctl32_mapper hwdep_mappers[]; -#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) -extern struct ioctl32_mapper seq_mappers[]; -#endif - -static void snd_ioctl32_done(void) -{ -#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) - snd_ioctl32_unregister(seq_mappers); -#endif - snd_ioctl32_unregister(hwdep_mappers); - snd_ioctl32_unregister(timer_mappers); - snd_ioctl32_unregister(rawmidi_mappers); - snd_ioctl32_unregister(pcm_mappers); - snd_ioctl32_unregister(control_mappers); -} - -static int __init snd_ioctl32_init(void) -{ - snd_ioctl32_register(control_mappers); - snd_ioctl32_register(pcm_mappers); - snd_ioctl32_register(rawmidi_mappers); - snd_ioctl32_register(timer_mappers); - snd_ioctl32_register(hwdep_mappers); -#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) - snd_ioctl32_register(seq_mappers); -#endif - return 0; -} - -module_init(snd_ioctl32_init) -module_exit(snd_ioctl32_done) diff -Nru a/sound/core/ioctl32/ioctl32.h b/sound/core/ioctl32/ioctl32.h --- a/sound/core/ioctl32/ioctl32.h 2005-01-28 14:22:13 -08:00 +++ /dev/null Wed Dec 31 16:00:00 196900 @@ -1,102 +0,0 @@ -/* - * 32bit -> 64bit ioctl helpers - * Copyright (c) by Takashi Iwai - * - * 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 - * - * - * This file registers the converters from 32-bit ioctls to 64-bit ones. - * The converter assumes that a 32-bit user-pointer can be casted by compat_ptr(x) - * macro to a valid 64-bit pointer which is accessible via copy_from/to_user. - * - */ - -#ifndef __ALSA_IOCTL32_H -#define __ALSA_IOCTL32_H - -#include - -#define COPY(x) \ - do { \ - if (copy_in_user(&dst->x, &src->x, sizeof(dst->x))) \ - return -EFAULT; \ - } while (0) - -#define COPY_ARRAY(x) \ - do { \ - if (copy_in_user(dst->x, src->x, sizeof(dst->x))) \ - return -EFAULT; \ - } while (0) - -#define COPY_CVT(x) \ - do { \ - __typeof__(src->x) __val_tmp; \ - if (get_user(__val_tmp, &src->x) || \ - put_user(__val_tmp, &dst->x))\ - return -EFAULT; \ - } while (0) - -#define convert_from_32(type, dstp, srcp)\ -{\ - struct sndrv_##type __user *dst = dstp;\ - struct sndrv_##type##32 __user *src = srcp;\ - CVT_##sndrv_##type();\ -} - -#define convert_to_32(type, dstp, srcp)\ -{\ - struct sndrv_##type __user *src = srcp;\ - struct sndrv_##type##32 __user *dst = dstp;\ - CVT_##sndrv_##type();\ -} - - -#define DEFINE_ALSA_IOCTL(type) \ -static inline int _snd_ioctl32_##type(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl)\ -{\ - struct sndrv_##type##32 __user *data32;\ - struct sndrv_##type __user *data;\ - int err;\ - data32 = compat_ptr(arg);\ - data = compat_alloc_user_space(sizeof(*data));\ - convert_from_32(type, data, data32);\ - err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)data);\ - if (err < 0) \ - return err;\ - if (native_ctl & (_IOC_READ << _IOC_DIRSHIFT)) {\ - convert_to_32(type, data32, data);\ - }\ - return 0;\ -} - -#define DEFINE_ALSA_IOCTL_ENTRY(name,type,native_ctl) \ -static int snd_ioctl32_##name(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file) {\ - return _snd_ioctl32_##type(fd, cmd, arg, file, native_ctl);\ -} - -#define MAP_COMPAT(ctl) { ctl, snd_ioctl32_compat } - -struct ioctl32_mapper { - unsigned int cmd; - int (*handler)(unsigned int, unsigned int, unsigned long, struct file * filp); - int registered; -}; - -int snd_ioctl32_compat(unsigned int, unsigned int, unsigned long, struct file *); - -int snd_ioctl32_register(struct ioctl32_mapper *mappers); -void snd_ioctl32_unregister(struct ioctl32_mapper *mappers); - -#endif /* __ALSA_IOCTL32_H */ diff -Nru a/sound/core/ioctl32/pcm32.c b/sound/core/ioctl32/pcm32.c --- a/sound/core/ioctl32/pcm32.c 2005-01-28 14:22:13 -08:00 +++ /dev/null Wed Dec 31 16:00:00 196900 @@ -1,461 +0,0 @@ -/* - * 32bit -> 64bit ioctl wrapper for PCM API - * Copyright (c) by Takashi Iwai - * - * 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 -#include -#include -#include -#include -#include -#include -#include "ioctl32.h" - - -/* wrapper for sndrv_pcm_[us]frames */ -struct sndrv_pcm_sframes_str { - sndrv_pcm_sframes_t val; -}; -struct sndrv_pcm_sframes_str32 { - s32 val; -}; -struct sndrv_pcm_uframes_str { - sndrv_pcm_uframes_t val; -}; -struct sndrv_pcm_uframes_str32 { - u32 val; -}; - -#define CVT_sndrv_pcm_sframes_str() { COPY_CVT(val); } -#define CVT_sndrv_pcm_uframes_str() { COPY_CVT(val); } - - -struct sndrv_pcm_hw_params32 { - u32 flags; - struct sndrv_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; /* this must be identical */ - struct sndrv_mask mres[5]; /* reserved masks */ - struct sndrv_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1]; - struct sndrv_interval ires[9]; /* reserved intervals */ - u32 rmask; - u32 cmask; - u32 info; - u32 msbits; - u32 rate_num; - u32 rate_den; - u32 fifo_size; - unsigned char reserved[64]; -} __attribute__((packed)); - -struct sndrv_pcm_sw_params32 { - s32 tstamp_mode; - u32 period_step; - u32 sleep_min; - u32 avail_min; - u32 xfer_align; - u32 start_threshold; - u32 stop_threshold; - u32 silence_threshold; - u32 silence_size; - u32 boundary; - unsigned char reserved[64]; -} __attribute__((packed)); - -#define CVT_sndrv_pcm_sw_params()\ -{\ - COPY(tstamp_mode);\ - COPY(period_step);\ - COPY(sleep_min);\ - COPY_CVT(avail_min);\ - COPY_CVT(xfer_align);\ - COPY_CVT(start_threshold);\ - COPY_CVT(stop_threshold);\ - COPY_CVT(silence_threshold);\ - COPY_CVT(silence_size);\ - COPY_CVT(boundary);\ -} - -struct sndrv_pcm_channel_info32 { - u32 channel; - u32 offset; - u32 first; - u32 step; -} __attribute__((packed)); - -#define CVT_sndrv_pcm_channel_info()\ -{\ - COPY(channel);\ - COPY_CVT(offset);\ - COPY(first);\ - COPY(step);\ -} - -struct sndrv_pcm_status32 { - s32 state; - struct compat_timespec trigger_tstamp; - struct compat_timespec tstamp; - u32 appl_ptr; - u32 hw_ptr; - s32 delay; - u32 avail; - u32 avail_max; - u32 overrange; - s32 suspended_state; - unsigned char reserved[60]; -} __attribute__((packed)); - -#define CVT_sndrv_pcm_status()\ -{\ - COPY(state);\ - COPY_CVT(trigger_tstamp.tv_sec);\ - COPY_CVT(trigger_tstamp.tv_nsec);\ - COPY_CVT(tstamp.tv_sec);\ - COPY_CVT(tstamp.tv_nsec);\ - COPY_CVT(appl_ptr);\ - COPY_CVT(hw_ptr);\ - COPY_CVT(delay);\ - COPY_CVT(avail);\ - COPY_CVT(avail_max);\ - COPY_CVT(overrange);\ - COPY(suspended_state);\ -} - -DEFINE_ALSA_IOCTL(pcm_uframes_str); -DEFINE_ALSA_IOCTL(pcm_sframes_str); -DEFINE_ALSA_IOCTL(pcm_sw_params); -DEFINE_ALSA_IOCTL(pcm_channel_info); -DEFINE_ALSA_IOCTL(pcm_status); - -/* sanity device check */ -extern int snd_major; -static int sanity_check_pcm(struct file *file) -{ - unsigned short minor; - if (imajor(file->f_dentry->d_inode) != snd_major) - return -ENOTTY; - minor = iminor(file->f_dentry->d_inode); - if (minor >= 256 || - minor % SNDRV_MINOR_DEVICES < SNDRV_MINOR_PCM_PLAYBACK) - return -ENOTTY; - return 0; -} - -/* recalcuate the boundary within 32bit */ -static void recalculate_boundary(snd_pcm_runtime_t *runtime) -{ - if (! runtime->buffer_size) - return; - runtime->boundary = runtime->buffer_size; - while (runtime->boundary * 2 <= 0x7fffffffUL - runtime->buffer_size) - runtime->boundary *= 2; -} - -/* both for HW_PARAMS and HW_REFINE */ -static int _snd_ioctl32_pcm_hw_params(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl) -{ - struct sndrv_pcm_hw_params32 __user *data32; - struct sndrv_pcm_hw_params *data; - snd_pcm_file_t *pcm_file; - snd_pcm_substream_t *substream; - snd_pcm_runtime_t *runtime; - int err; - - if (sanity_check_pcm(file)) - return -ENOTTY; - if (! (pcm_file = file->private_data)) - return -ENOTTY; - if (! (substream = pcm_file->substream)) - return -ENOTTY; - if (! (runtime = substream->runtime)) - return -ENOTTY; - - data32 = compat_ptr(arg); - data = kmalloc(sizeof(*data), GFP_KERNEL); - if (data == NULL) - return -ENOMEM; - if (copy_from_user(data, data32, sizeof(*data32))) { - err = -EFAULT; - goto error; - } - if (native_ctl == SNDRV_PCM_IOCTL_HW_REFINE) - err = snd_pcm_hw_refine(substream, data); - else - err = snd_pcm_hw_params(substream, data); - if (err < 0) - goto error; - if (copy_to_user(data32, data, sizeof(*data32)) || - __put_user((u32)data->fifo_size, &data32->fifo_size)) { - err = -EFAULT; - goto error; - } - - if (native_ctl == SNDRV_PCM_IOCTL_HW_PARAMS) - recalculate_boundary(runtime); - error: - kfree(data); - return err; -} - - -/* - */ -struct sndrv_xferi32 { - s32 result; - u32 buf; - u32 frames; -} __attribute__((packed)); - -static int _snd_ioctl32_xferi(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl) -{ - struct sndrv_xferi32 data32; - struct sndrv_xferi __user *data; - snd_pcm_sframes_t result; - int err; - - if (copy_from_user(&data32, (void __user *)arg, sizeof(data32))) - return -EFAULT; - data = compat_alloc_user_space(sizeof(*data)); - if (put_user((snd_pcm_sframes_t)data32.result, &data->result) || - __put_user(compat_ptr(data32.buf), &data->buf) || - __put_user((snd_pcm_uframes_t)data32.frames, &data->frames)) - return -EFAULT; - err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)data); - if (err < 0) - return err; - /* copy the result */ - if (__get_user(result, &data->result)) - return -EFAULT; - data32.result = result; - if (copy_to_user((void __user *)arg, &data32, sizeof(data32))) - return -EFAULT; - return 0; -} - - -/* snd_xfern needs remapping of bufs */ -struct sndrv_xfern32 { - s32 result; - u32 bufs; /* this is void **; */ - u32 frames; -} __attribute__((packed)); - -/* - * xfern ioctl nees to copy (up to) 128 pointers on stack. - * although we may pass the copied pointers through f_op->ioctl, but the ioctl - * handler there expands again the same 128 pointers on stack, so it is better - * to handle the function (calling pcm_readv/writev) directly in this handler. - */ -static int _snd_ioctl32_xfern(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl) -{ - snd_pcm_file_t *pcm_file; - snd_pcm_substream_t *substream; - struct sndrv_xfern32 __user *srcptr = compat_ptr(arg); - struct sndrv_xfern32 data32; - void __user **bufs; - int err = 0, ch, i; - u32 __user *bufptr; - - if (sanity_check_pcm(file)) - return -ENOTTY; - if (! (pcm_file = file->private_data)) - return -ENOTTY; - if (! (substream = pcm_file->substream)) - return -ENOTTY; - if (! substream->runtime) - return -ENOTTY; - - /* check validty of the command */ - switch (native_ctl) { - case SNDRV_PCM_IOCTL_WRITEN_FRAMES: - if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) - return -EINVAL; - if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) - return -EBADFD; - break; - case SNDRV_PCM_IOCTL_READN_FRAMES: - if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) - return -EINVAL; - break; - } - if ((ch = substream->runtime->channels) > 128) - return -EINVAL; - if (copy_from_user(&data32, (void __user *)arg, sizeof(data32))) - return -EFAULT; - bufptr = compat_ptr(data32.bufs); - bufs = kmalloc(sizeof(void __user *) * ch, GFP_KERNEL); - if (bufs == NULL) - return -ENOMEM; - for (i = 0; i < ch; i++) { - u32 ptr; - if (get_user(ptr, bufptr)) { - kfree(bufs); - return -EFAULT; - } - bufs[ch] = compat_ptr(ptr); - bufptr++; - } - switch (native_ctl) { - case SNDRV_PCM_IOCTL_WRITEN_FRAMES: - err = snd_pcm_lib_writev(substream, bufs, data32.frames); - break; - case SNDRV_PCM_IOCTL_READN_FRAMES: - err = snd_pcm_lib_readv(substream, bufs, data32.frames); - break; - } - if (err >= 0) { - if (put_user(err, &srcptr->result)) - err = -EFAULT; - } - kfree(bufs); - return err; -} - - -struct sndrv_pcm_mmap_status32 { - s32 state; - s32 pad1; - u32 hw_ptr; - struct compat_timespec tstamp; - s32 suspended_state; -} __attribute__((packed)); - -struct sndrv_pcm_mmap_control32 { - u32 appl_ptr; - u32 avail_min; -} __attribute__((packed)); - -struct sndrv_pcm_sync_ptr32 { - u32 flags; - union { - struct sndrv_pcm_mmap_status32 status; - unsigned char reserved[64]; - } s; - union { - struct sndrv_pcm_mmap_control32 control; - unsigned char reserved[64]; - } c; -} __attribute__((packed)); - -#define CVT_sndrv_pcm_sync_ptr()\ -{\ - COPY(flags);\ - COPY(s.status.state);\ - COPY(s.status.pad1);\ - COPY_CVT(s.status.hw_ptr);\ - COPY_CVT(s.status.tstamp.tv_sec);\ - COPY_CVT(s.status.tstamp.tv_nsec);\ - COPY(s.status.suspended_state);\ - COPY_CVT(c.control.appl_ptr);\ - COPY_CVT(c.control.avail_min);\ -} - -DEFINE_ALSA_IOCTL(pcm_sync_ptr); - -/* - */ - -DEFINE_ALSA_IOCTL_ENTRY(pcm_hw_refine, pcm_hw_params, SNDRV_PCM_IOCTL_HW_REFINE); -DEFINE_ALSA_IOCTL_ENTRY(pcm_hw_params, pcm_hw_params, SNDRV_PCM_IOCTL_HW_PARAMS); -DEFINE_ALSA_IOCTL_ENTRY(pcm_sw_params, pcm_sw_params, SNDRV_PCM_IOCTL_SW_PARAMS); -DEFINE_ALSA_IOCTL_ENTRY(pcm_status, pcm_status, SNDRV_PCM_IOCTL_STATUS); -DEFINE_ALSA_IOCTL_ENTRY(pcm_delay, pcm_sframes_str, SNDRV_PCM_IOCTL_DELAY); -DEFINE_ALSA_IOCTL_ENTRY(pcm_channel_info, pcm_channel_info, SNDRV_PCM_IOCTL_CHANNEL_INFO); -DEFINE_ALSA_IOCTL_ENTRY(pcm_rewind, pcm_uframes_str, SNDRV_PCM_IOCTL_REWIND); -DEFINE_ALSA_IOCTL_ENTRY(pcm_readi, xferi, SNDRV_PCM_IOCTL_READI_FRAMES); -DEFINE_ALSA_IOCTL_ENTRY(pcm_writei, xferi, SNDRV_PCM_IOCTL_WRITEI_FRAMES); -DEFINE_ALSA_IOCTL_ENTRY(pcm_readn, xfern, SNDRV_PCM_IOCTL_READN_FRAMES); -DEFINE_ALSA_IOCTL_ENTRY(pcm_writen, xfern, SNDRV_PCM_IOCTL_WRITEN_FRAMES); -DEFINE_ALSA_IOCTL_ENTRY(pcm_sync_ptr, pcm_sync_ptr, SNDRV_PCM_IOCTL_SYNC_PTR); - - -/* - * When PCM is used on 32bit mode, we need to disable - * mmap of PCM status/control records because of the size - * incompatibility. - * - * Since INFO ioctl is always called at first, we mark the - * mmap-disabling in this ioctl wrapper. - */ -static int snd_pcm_info_ioctl32(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *filp) -{ - snd_pcm_file_t *pcm_file; - snd_pcm_substream_t *substream; - if (! filp->f_op || ! filp->f_op->ioctl) - return -ENOTTY; - pcm_file = filp->private_data; - if (! pcm_file) - return -ENOTTY; - substream = pcm_file->substream; - if (! substream) - return -ENOTTY; - substream->no_mmap_ctrl = 1; - return filp->f_op->ioctl(filp->f_dentry->d_inode, filp, cmd, arg); -} - -/* - */ -#define AP(x) snd_ioctl32_##x - -enum { - SNDRV_PCM_IOCTL_HW_REFINE32 = _IOWR('A', 0x10, struct sndrv_pcm_hw_params32), - SNDRV_PCM_IOCTL_HW_PARAMS32 = _IOWR('A', 0x11, struct sndrv_pcm_hw_params32), - SNDRV_PCM_IOCTL_SW_PARAMS32 = _IOWR('A', 0x13, struct sndrv_pcm_sw_params32), - SNDRV_PCM_IOCTL_STATUS32 = _IOR('A', 0x20, struct sndrv_pcm_status32), - SNDRV_PCM_IOCTL_DELAY32 = _IOR('A', 0x21, s32), - SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct sndrv_pcm_channel_info32), - SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32), - SNDRV_PCM_IOCTL_WRITEI_FRAMES32 = _IOW('A', 0x50, struct sndrv_xferi32), - SNDRV_PCM_IOCTL_READI_FRAMES32 = _IOR('A', 0x51, struct sndrv_xferi32), - SNDRV_PCM_IOCTL_WRITEN_FRAMES32 = _IOW('A', 0x52, struct sndrv_xfern32), - SNDRV_PCM_IOCTL_READN_FRAMES32 = _IOR('A', 0x53, struct sndrv_xfern32), - SNDRV_PCM_IOCTL_SYNC_PTR32 = _IOWR('A', 0x23, struct sndrv_pcm_sync_ptr32), - -}; - -struct ioctl32_mapper pcm_mappers[] = { - MAP_COMPAT(SNDRV_PCM_IOCTL_PVERSION), - /* MAP_COMPAT(SNDRV_PCM_IOCTL_INFO), */ - { SNDRV_PCM_IOCTL_INFO, snd_pcm_info_ioctl32 }, - MAP_COMPAT(SNDRV_PCM_IOCTL_TSTAMP), - { SNDRV_PCM_IOCTL_HW_REFINE32, AP(pcm_hw_refine) }, - { SNDRV_PCM_IOCTL_HW_PARAMS32, AP(pcm_hw_params) }, - MAP_COMPAT(SNDRV_PCM_IOCTL_HW_FREE), - { SNDRV_PCM_IOCTL_SW_PARAMS32, AP(pcm_sw_params) }, - { SNDRV_PCM_IOCTL_STATUS32, AP(pcm_status) }, - { SNDRV_PCM_IOCTL_DELAY32, AP(pcm_delay) }, - MAP_COMPAT(SNDRV_PCM_IOCTL_HWSYNC), - { SNDRV_PCM_IOCTL_SYNC_PTR32, AP(pcm_sync_ptr) }, - { SNDRV_PCM_IOCTL_CHANNEL_INFO32, AP(pcm_channel_info) }, - MAP_COMPAT(SNDRV_PCM_IOCTL_PREPARE), - MAP_COMPAT(SNDRV_PCM_IOCTL_RESET), - MAP_COMPAT(SNDRV_PCM_IOCTL_START), - MAP_COMPAT(SNDRV_PCM_IOCTL_DROP), - MAP_COMPAT(SNDRV_PCM_IOCTL_DRAIN), - MAP_COMPAT(SNDRV_PCM_IOCTL_PAUSE), - { SNDRV_PCM_IOCTL_REWIND32, AP(pcm_rewind) }, - MAP_COMPAT(SNDRV_PCM_IOCTL_RESUME), - MAP_COMPAT(SNDRV_PCM_IOCTL_XRUN), - { SNDRV_PCM_IOCTL_WRITEI_FRAMES32, AP(pcm_writei) }, - { SNDRV_PCM_IOCTL_READI_FRAMES32, AP(pcm_readi) }, - { SNDRV_PCM_IOCTL_WRITEN_FRAMES32, AP(pcm_writen) }, - { SNDRV_PCM_IOCTL_READN_FRAMES32, AP(pcm_readn) }, - MAP_COMPAT(SNDRV_PCM_IOCTL_LINK), - MAP_COMPAT(SNDRV_PCM_IOCTL_UNLINK), - - { 0 }, -}; diff -Nru a/sound/core/ioctl32/rawmidi32.c b/sound/core/ioctl32/rawmidi32.c --- a/sound/core/ioctl32/rawmidi32.c 2005-01-28 14:22:13 -08:00 +++ /dev/null Wed Dec 31 16:00:00 196900 @@ -1,91 +0,0 @@ -/* - * 32bit -> 64bit ioctl wrapper for raw MIDI API - * Copyright (c) by Takashi Iwai - * - * 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 -#include -#include -#include -#include -#include -#include -#include "ioctl32.h" - -struct sndrv_rawmidi_params32 { - s32 stream; - u32 buffer_size; - u32 avail_min; - unsigned int no_active_sensing; /* avoid bit-field */ - unsigned char reserved[16]; -} __attribute__((packed)); - -#define CVT_sndrv_rawmidi_params()\ -{\ - COPY(stream);\ - COPY_CVT(buffer_size);\ - COPY_CVT(avail_min);\ - if (copy_in_user(((size_t __user *)&dst->avail_min + 1),\ - ((size_t __user *)&src->avail_min + 1), 4)) \ - return -EFAULT;\ -} - -struct sndrv_rawmidi_status32 { - s32 stream; - struct compat_timespec tstamp; - u32 avail; - u32 xruns; - unsigned char reserved[16]; -} __attribute__((packed)); - -#define CVT_sndrv_rawmidi_status()\ -{\ - COPY(stream);\ - COPY_CVT(tstamp.tv_sec);\ - COPY_CVT(tstamp.tv_nsec);\ - COPY_CVT(avail);\ - COPY_CVT(xruns);\ -} - -DEFINE_ALSA_IOCTL(rawmidi_params); -DEFINE_ALSA_IOCTL(rawmidi_status); - -DEFINE_ALSA_IOCTL_ENTRY(rawmidi_params, rawmidi_params, SNDRV_RAWMIDI_IOCTL_PARAMS); -DEFINE_ALSA_IOCTL_ENTRY(rawmidi_status, rawmidi_status, SNDRV_RAWMIDI_IOCTL_STATUS); - -#define AP(x) snd_ioctl32_##x - -enum { - SNDRV_RAWMIDI_IOCTL_PARAMS32 = _IOWR('W', 0x10, struct sndrv_rawmidi_params32), - SNDRV_RAWMIDI_IOCTL_STATUS32 = _IOWR('W', 0x20, struct sndrv_rawmidi_status32), -}; - -struct ioctl32_mapper rawmidi_mappers[] = { - MAP_COMPAT(SNDRV_RAWMIDI_IOCTL_PVERSION), - MAP_COMPAT(SNDRV_RAWMIDI_IOCTL_INFO), - { SNDRV_RAWMIDI_IOCTL_PARAMS32, AP(rawmidi_params) }, - { SNDRV_RAWMIDI_IOCTL_STATUS32, AP(rawmidi_status) }, - MAP_COMPAT(SNDRV_RAWMIDI_IOCTL_DROP), - MAP_COMPAT(SNDRV_RAWMIDI_IOCTL_DRAIN), - - MAP_COMPAT(SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE), - MAP_COMPAT(SNDRV_CTL_IOCTL_RAWMIDI_INFO), - MAP_COMPAT(SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE), - - { 0 }, -}; diff -Nru a/sound/core/ioctl32/seq32.c b/sound/core/ioctl32/seq32.c --- a/sound/core/ioctl32/seq32.c 2005-01-28 14:22:13 -08:00 +++ /dev/null Wed Dec 31 16:00:00 196900 @@ -1,116 +0,0 @@ -/* - * 32bit -> 64bit ioctl wrapper for sequencer API - * Copyright (c) by Takashi Iwai - * - * 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 -#include -#include -#include -#include -#include -#include -#include "ioctl32.h" - -struct sndrv_seq_port_info32 { - struct sndrv_seq_addr addr; /* client/port numbers */ - char name[64]; /* port name */ - - u32 capability; /* port capability bits */ - u32 type; /* port type bits */ - s32 midi_channels; /* channels per MIDI port */ - s32 midi_voices; /* voices per MIDI port */ - s32 synth_voices; /* voices per SYNTH port */ - - s32 read_use; /* R/O: subscribers for output (from this port) */ - s32 write_use; /* R/O: subscribers for input (to this port) */ - - u32 kernel; /* reserved for kernel use (must be NULL) */ - u32 flags; /* misc. conditioning */ - unsigned char time_queue; /* queue # for timestamping */ - char reserved[59]; /* for future use */ -}; - -#define CVT_sndrv_seq_port_info()\ -{\ - COPY(addr);\ - COPY_ARRAY(name);\ - COPY(capability);\ - COPY(type);\ - COPY(midi_channels);\ - COPY(midi_voices);\ - COPY(synth_voices);\ - COPY(read_use);\ - COPY(write_use);\ - COPY(flags);\ - COPY(time_queue);\ -} - -DEFINE_ALSA_IOCTL(seq_port_info); -DEFINE_ALSA_IOCTL_ENTRY(create_port, seq_port_info, SNDRV_SEQ_IOCTL_CREATE_PORT); -DEFINE_ALSA_IOCTL_ENTRY(delete_port, seq_port_info, SNDRV_SEQ_IOCTL_DELETE_PORT); -DEFINE_ALSA_IOCTL_ENTRY(get_port_info, seq_port_info, SNDRV_SEQ_IOCTL_GET_PORT_INFO); -DEFINE_ALSA_IOCTL_ENTRY(set_port_info, seq_port_info, SNDRV_SEQ_IOCTL_SET_PORT_INFO); -DEFINE_ALSA_IOCTL_ENTRY(query_next_port, seq_port_info, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT); - -/* - */ -#define AP(x) snd_ioctl32_##x - -enum { - SNDRV_SEQ_IOCTL_CREATE_PORT32 = _IOWR('S', 0x20, struct sndrv_seq_port_info32), - SNDRV_SEQ_IOCTL_DELETE_PORT32 = _IOW ('S', 0x21, struct sndrv_seq_port_info32), - SNDRV_SEQ_IOCTL_GET_PORT_INFO32 = _IOWR('S', 0x22, struct sndrv_seq_port_info32), - SNDRV_SEQ_IOCTL_SET_PORT_INFO32 = _IOW ('S', 0x23, struct sndrv_seq_port_info32), - SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT32 = _IOWR('S', 0x52, struct sndrv_seq_port_info32), -}; - -struct ioctl32_mapper seq_mappers[] = { - MAP_COMPAT(SNDRV_SEQ_IOCTL_PVERSION), - MAP_COMPAT(SNDRV_SEQ_IOCTL_CLIENT_ID), - MAP_COMPAT(SNDRV_SEQ_IOCTL_SYSTEM_INFO), - MAP_COMPAT(SNDRV_SEQ_IOCTL_GET_CLIENT_INFO), - MAP_COMPAT(SNDRV_SEQ_IOCTL_SET_CLIENT_INFO), - { SNDRV_SEQ_IOCTL_CREATE_PORT32, AP(create_port) }, - { SNDRV_SEQ_IOCTL_DELETE_PORT32, AP(delete_port) }, - { SNDRV_SEQ_IOCTL_GET_PORT_INFO32, AP(get_port_info) }, - { SNDRV_SEQ_IOCTL_SET_PORT_INFO32, AP(set_port_info) }, - MAP_COMPAT(SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT), - MAP_COMPAT(SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT), - MAP_COMPAT(SNDRV_SEQ_IOCTL_CREATE_QUEUE), - MAP_COMPAT(SNDRV_SEQ_IOCTL_DELETE_QUEUE), - MAP_COMPAT(SNDRV_SEQ_IOCTL_GET_QUEUE_INFO), - MAP_COMPAT(SNDRV_SEQ_IOCTL_SET_QUEUE_INFO), - MAP_COMPAT(SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE), - MAP_COMPAT(SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS), - MAP_COMPAT(SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO), - MAP_COMPAT(SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO), - MAP_COMPAT(SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER), - MAP_COMPAT(SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER), - MAP_COMPAT(SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT), - MAP_COMPAT(SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT), - MAP_COMPAT(SNDRV_SEQ_IOCTL_GET_CLIENT_POOL), - MAP_COMPAT(SNDRV_SEQ_IOCTL_SET_CLIENT_POOL), - MAP_COMPAT(SNDRV_SEQ_IOCTL_REMOVE_EVENTS), - MAP_COMPAT(SNDRV_SEQ_IOCTL_QUERY_SUBS), - MAP_COMPAT(SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION), - MAP_COMPAT(SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT), - { SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT32, AP(query_next_port) }, - MAP_COMPAT(SNDRV_SEQ_IOCTL_RUNNING_MODE), - { 0 }, -}; diff -Nru a/sound/core/ioctl32/timer32.c b/sound/core/ioctl32/timer32.c --- a/sound/core/ioctl32/timer32.c 2005-01-28 14:22:13 -08:00 +++ /dev/null Wed Dec 31 16:00:00 196900 @@ -1,105 +0,0 @@ -/* - * 32bit -> 64bit ioctl wrapper for timer API - * Copyright (c) by Takashi Iwai - * - * 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 -#include -#include -#include -#include -#include -#include -#include "ioctl32.h" - -struct sndrv_timer_info32 { - u32 flags; - s32 card; - unsigned char id[64]; - unsigned char name[80]; - u32 reserved0; - u32 resolution; - unsigned char reserved[64]; -}; - -#define CVT_sndrv_timer_info()\ -{\ - COPY(flags);\ - COPY(card);\ - COPY_ARRAY(id);\ - COPY_ARRAY(name);\ - COPY_CVT(resolution);\ -} - -struct sndrv_timer_status32 { - struct compat_timespec tstamp; - u32 resolution; - u32 lost; - u32 overrun; - u32 queue; - unsigned char reserved[64]; -}; - -#define CVT_sndrv_timer_status()\ -{\ - COPY_CVT(tstamp.tv_sec);\ - COPY_CVT(tstamp.tv_nsec);\ - COPY(resolution);\ - COPY(lost);\ - COPY(overrun);\ - COPY(queue);\ -} - -DEFINE_ALSA_IOCTL(timer_info); -DEFINE_ALSA_IOCTL(timer_status); - -DEFINE_ALSA_IOCTL_ENTRY(timer_info, timer_info, SNDRV_TIMER_IOCTL_INFO); -DEFINE_ALSA_IOCTL_ENTRY(timer_status, timer_status, SNDRV_TIMER_IOCTL_STATUS); - -/* - */ - -#define AP(x) snd_ioctl32_##x - -enum { - SNDRV_TIMER_IOCTL_INFO32 = _IOR('T', 0x11, struct sndrv_timer_info32), - SNDRV_TIMER_IOCTL_STATUS32 = _IOW('T', 0x14, struct sndrv_timer_status32), -}; - -struct ioctl32_mapper timer_mappers[] = { - MAP_COMPAT(SNDRV_TIMER_IOCTL_PVERSION), - MAP_COMPAT(SNDRV_TIMER_IOCTL_NEXT_DEVICE), - MAP_COMPAT(SNDRV_TIMER_IOCTL_SELECT), - { SNDRV_TIMER_IOCTL_INFO32, AP(timer_info) }, - MAP_COMPAT(SNDRV_TIMER_IOCTL_PARAMS), - { SNDRV_TIMER_IOCTL_STATUS32, AP(timer_status) }, -#if 0 - /* ** FIXME ** - * The following four entries are disabled because they conflict - * with the TCOC* definitions. - * Unfortunately, the current ioctl32 wrapper uses a single - * hash table for all devices. Once when the wrapper is fixed - * with the table based on devices, they'll be back again. - */ - MAP_COMPAT(SNDRV_TIMER_IOCTL_START), - MAP_COMPAT(SNDRV_TIMER_IOCTL_STOP), - MAP_COMPAT(SNDRV_TIMER_IOCTL_CONTINUE), - MAP_COMPAT(SNDRV_TIMER_IOCTL_PAUSE), -#endif - { 0 }, -}; diff -Nru a/sound/core/memory.c b/sound/core/memory.c --- a/sound/core/memory.c 2005-01-28 14:22:13 -08:00 +++ b/sound/core/memory.c 2005-01-28 14:22:13 -08:00 @@ -50,8 +50,8 @@ static long snd_alloc_vmalloc; static LIST_HEAD(snd_alloc_kmalloc_list); static LIST_HEAD(snd_alloc_vmalloc_list); -static spinlock_t snd_alloc_kmalloc_lock = SPIN_LOCK_UNLOCKED; -static spinlock_t snd_alloc_vmalloc_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(snd_alloc_kmalloc_lock); +static DEFINE_SPINLOCK(snd_alloc_vmalloc_lock); #define KMALLOC_MAGIC 0x87654321 #define VMALLOC_MAGIC 0x87654320 static snd_info_entry_t *snd_memory_info_entry; diff -Nru a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c --- a/sound/core/oss/mixer_oss.c 2005-01-28 14:22:13 -08:00 +++ b/sound/core/oss/mixer_oss.c 2005-01-28 14:22:13 -08:00 @@ -359,16 +359,9 @@ return -ENXIO; } -/* FIXME: need to unlock BKL to allow preemption */ -static int snd_mixer_oss_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long snd_mixer_oss_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - int err; - /* FIXME: need to unlock BKL to allow preemption */ - unlock_kernel(); - err = snd_mixer_oss_ioctl1((snd_mixer_oss_file_t *) file->private_data, cmd, arg); - lock_kernel(); - return err; + return snd_mixer_oss_ioctl1((snd_mixer_oss_file_t *) file->private_data, cmd, arg); } int snd_mixer_oss_ioctl_card(snd_card_t *card, unsigned int cmd, unsigned long arg) @@ -384,6 +377,13 @@ return snd_mixer_oss_ioctl1(&fmixer, cmd, arg); } +#ifdef CONFIG_COMPAT +/* all compatible */ +#define snd_mixer_oss_ioctl_compat snd_mixer_oss_ioctl +#else +#define snd_mixer_oss_ioctl_compat NULL +#endif + /* * REGISTRATION PART */ @@ -393,7 +393,8 @@ .owner = THIS_MODULE, .open = snd_mixer_oss_open, .release = snd_mixer_oss_release, - .ioctl = snd_mixer_oss_ioctl, + .unlocked_ioctl = snd_mixer_oss_ioctl, + .compat_ioctl = snd_mixer_oss_ioctl_compat, }; static snd_minor_t snd_mixer_oss_reg = diff -Nru a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c --- a/sound/core/oss/pcm_oss.c 2005-01-28 14:22:13 -08:00 +++ b/sound/core/oss/pcm_oss.c 2005-01-28 14:22:13 -08:00 @@ -1913,8 +1913,7 @@ return 0; } -static inline int _snd_pcm_oss_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { snd_pcm_oss_file_t *pcm_oss_file; int __user *p = (int __user *)arg; @@ -2073,16 +2072,12 @@ return -EINVAL; } -/* FIXME: need to unlock BKL to allow preemption */ -static int snd_pcm_oss_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int err; - unlock_kernel(); - err = _snd_pcm_oss_ioctl(inode, file, cmd, arg); - lock_kernel(); - return err; -} +#ifdef CONFIG_COMPAT +/* all compatible */ +#define snd_pcm_oss_ioctl_compat snd_pcm_oss_ioctl +#else +#define snd_pcm_oss_ioctl_compat NULL +#endif static ssize_t snd_pcm_oss_read(struct file *file, char __user *buf, size_t count, loff_t *offset) { @@ -2410,7 +2405,8 @@ .open = snd_pcm_oss_open, .release = snd_pcm_oss_release, .poll = snd_pcm_oss_poll, - .ioctl = snd_pcm_oss_ioctl, + .unlocked_ioctl = snd_pcm_oss_ioctl, + .compat_ioctl = snd_pcm_oss_ioctl_compat, .mmap = snd_pcm_oss_mmap, }; diff -Nru a/sound/core/pcm.c b/sound/core/pcm.c --- a/sound/core/pcm.c 2005-01-28 14:22:13 -08:00 +++ b/sound/core/pcm.c 2005-01-28 14:22:13 -08:00 @@ -1004,6 +1004,7 @@ snd_info_entry_t *entry; snd_ctl_register_ioctl(snd_pcm_control_ioctl); + snd_ctl_register_ioctl_compat(snd_pcm_control_ioctl); if ((entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL)) != NULL) { snd_info_set_text_ops(entry, NULL, SNDRV_CARDS * SNDRV_PCM_DEVICES * 128, snd_pcm_proc_read); if (snd_info_register(entry) < 0) { @@ -1018,6 +1019,7 @@ static void __exit alsa_pcm_exit(void) { snd_ctl_unregister_ioctl(snd_pcm_control_ioctl); + snd_ctl_unregister_ioctl_compat(snd_pcm_control_ioctl); if (snd_pcm_proc_entry) { snd_info_unregister(snd_pcm_proc_entry); snd_pcm_proc_entry = NULL; diff -Nru a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/sound/core/pcm_compat.c 2005-01-28 14:22:13 -08:00 @@ -0,0 +1,513 @@ +/* + * 32bit -> 64bit ioctl wrapper for PCM API + * Copyright (c) by Takashi Iwai + * + * 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 + * + */ + +/* This file included from pcm_native.c */ + +#include + +static int snd_pcm_ioctl_delay_compat(snd_pcm_substream_t *substream, + s32 __user *src) +{ + snd_pcm_sframes_t delay; + mm_segment_t fs; + int err; + + fs = snd_enter_user(); + err = snd_pcm_delay(substream, &delay); + snd_leave_user(fs); + if (err < 0) + return err; + if (put_user(delay, src)) + return -EFAULT; + return err; +} + +static int snd_pcm_ioctl_rewind_compat(snd_pcm_substream_t *substream, + u32 __user *src) +{ + snd_pcm_uframes_t frames; + int err; + + if (get_user(frames, src)) + return -EFAULT; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + err = snd_pcm_playback_rewind(substream, frames); + else + err = snd_pcm_capture_rewind(substream, frames); + if (put_user(err, src)) + return -EFAULT; + return err < 0 ? err : 0; +} + +static int snd_pcm_ioctl_forward_compat(snd_pcm_substream_t *substream, + u32 __user *src) +{ + snd_pcm_uframes_t frames; + int err; + + if (get_user(frames, src)) + return -EFAULT; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + err = snd_pcm_playback_forward(substream, frames); + else + err = snd_pcm_capture_forward(substream, frames); + if (put_user(err, src)) + return -EFAULT; + return err < 0 ? err : 0; +} + +struct sndrv_pcm_hw_params32 { + u32 flags; + struct sndrv_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; /* this must be identical */ + struct sndrv_mask mres[5]; /* reserved masks */ + struct sndrv_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1]; + struct sndrv_interval ires[9]; /* reserved intervals */ + u32 rmask; + u32 cmask; + u32 info; + u32 msbits; + u32 rate_num; + u32 rate_den; + u32 fifo_size; + unsigned char reserved[64]; +}; + +struct sndrv_pcm_sw_params32 { + s32 tstamp_mode; + u32 period_step; + u32 sleep_min; + u32 avail_min; + u32 xfer_align; + u32 start_threshold; + u32 stop_threshold; + u32 silence_threshold; + u32 silence_size; + u32 boundary; + unsigned char reserved[64]; +}; + +static int snd_pcm_ioctl_sw_params_compat(snd_pcm_substream_t *substream, + struct sndrv_pcm_sw_params32 __user *src) +{ + snd_pcm_sw_params_t params; + int err; + + memset(¶ms, 0, sizeof(params)); + if (get_user(params.tstamp_mode, &src->tstamp_mode) || + get_user(params.period_step, &src->period_step) || + get_user(params.sleep_min, &src->sleep_min) || + get_user(params.avail_min, &src->avail_min) || + get_user(params.xfer_align, &src->xfer_align) || + get_user(params.start_threshold, &src->start_threshold) || + get_user(params.stop_threshold, &src->stop_threshold) || + get_user(params.silence_threshold, &src->silence_threshold) || + get_user(params.silence_size, &src->silence_size)) + return -EFAULT; + err = snd_pcm_sw_params(substream, ¶ms); + if (err < 0) + return err; + if (put_user(params.boundary, &src->boundary)) + return -EFAULT; + return err; +} + +struct sndrv_pcm_channel_info32 { + u32 channel; + u32 offset; + u32 first; + u32 step; +}; + +static int snd_pcm_ioctl_channel_info_compat(snd_pcm_substream_t *substream, + struct sndrv_pcm_channel_info32 __user *src) +{ + snd_pcm_channel_info_t info; + int err; + + if (get_user(info.channel, &src->channel) || + get_user(info.offset, &src->offset) || + get_user(info.first, &src->first) || + get_user(info.step, &src->step)) + return -EFAULT; + err = snd_pcm_channel_info(substream, &info); + if (err < 0) + return err; + if (put_user(info.channel, &src->channel) || + put_user(info.offset, &src->offset) || + put_user(info.first, &src->first) || + put_user(info.step, &src->step)) + return -EFAULT; + return err; +} + +struct sndrv_pcm_status32 { + s32 state; + struct compat_timespec trigger_tstamp; + struct compat_timespec tstamp; + u32 appl_ptr; + u32 hw_ptr; + s32 delay; + u32 avail; + u32 avail_max; + u32 overrange; + s32 suspended_state; + unsigned char reserved[60]; +} __attribute__((packed)); + + +static int snd_pcm_status_user_compat(snd_pcm_substream_t *substream, + struct sndrv_pcm_status32 __user *src) +{ + snd_pcm_status_t status; + int err; + + err = snd_pcm_status(substream, &status); + if (err < 0) + return err; + + if (put_user(status.state, &src->state) || + put_user(status.trigger_tstamp.tv_sec, &src->trigger_tstamp.tv_sec) || + put_user(status.trigger_tstamp.tv_nsec, &src->trigger_tstamp.tv_nsec) || + put_user(status.tstamp.tv_sec, &src->tstamp.tv_sec) || + put_user(status.tstamp.tv_nsec, &src->tstamp.tv_nsec) || + put_user(status.appl_ptr, &src->appl_ptr) || + put_user(status.hw_ptr, &src->hw_ptr) || + put_user(status.delay, &src->delay) || + put_user(status.avail, &src->avail) || + put_user(status.avail_max, &src->avail_max) || + put_user(status.overrange, &src->overrange) || + put_user(status.suspended_state, &src->suspended_state)) + return -EFAULT; + + return err; +} + +/* recalcuate the boundary within 32bit */ +static void recalculate_boundary(snd_pcm_runtime_t *runtime) +{ + if (! runtime->buffer_size) + return; + runtime->boundary = runtime->buffer_size; + while (runtime->boundary * 2 <= 0x7fffffffUL - runtime->buffer_size) + runtime->boundary *= 2; +} + +/* both for HW_PARAMS and HW_REFINE */ +static int snd_pcm_ioctl_hw_params_compat(snd_pcm_substream_t *substream, + int refine, + struct sndrv_pcm_hw_params32 __user *data32) +{ + struct sndrv_pcm_hw_params *data; + snd_pcm_runtime_t *runtime; + int err; + + if (! (runtime = substream->runtime)) + return -ENOTTY; + + data = kmalloc(sizeof(*data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + /* only fifo_size is different, so just copy all */ + if (copy_from_user(data, data32, sizeof(*data32))) { + err = -EFAULT; + goto error; + } + if (refine) + err = snd_pcm_hw_refine(substream, data); + else + err = snd_pcm_hw_params(substream, data); + if (err < 0) + goto error; + if (copy_to_user(data32, data, sizeof(*data32)) || + put_user(data->fifo_size, &data32->fifo_size)) { + err = -EFAULT; + goto error; + } + + if (! refine) + recalculate_boundary(runtime); + error: + kfree(data); + return err; +} + + +/* + */ +struct sndrv_xferi32 { + s32 result; + u32 buf; + u32 frames; +}; + +static int snd_pcm_ioctl_xferi_compat(snd_pcm_substream_t *substream, + int dir, struct sndrv_xferi32 __user *data32) +{ + compat_caddr_t buf; + u32 frames; + int err; + + if (! substream->runtime) + return -ENOTTY; + if (substream->stream != dir) + return -EINVAL; + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + + if (get_user(buf, &data32->buf) || + get_user(frames, &data32->frames)) + return -EFAULT; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + err = snd_pcm_lib_write(substream, compat_ptr(buf), frames); + else + err = snd_pcm_lib_read(substream, compat_ptr(buf), frames); + if (err < 0) + return err; + /* copy the result */ + if (put_user(err, &data32->result)) + return -EFAULT; + return 0; +} + + +/* snd_xfern needs remapping of bufs */ +struct sndrv_xfern32 { + s32 result; + u32 bufs; /* this is void **; */ + u32 frames; +}; + +/* + * xfern ioctl nees to copy (up to) 128 pointers on stack. + * although we may pass the copied pointers through f_op->ioctl, but the ioctl + * handler there expands again the same 128 pointers on stack, so it is better + * to handle the function (calling pcm_readv/writev) directly in this handler. + */ +static int snd_pcm_ioctl_xfern_compat(snd_pcm_substream_t *substream, + int dir, struct sndrv_xfern32 __user *data32) +{ + compat_caddr_t buf; + compat_caddr_t __user *bufptr; + u32 frames; + void __user **bufs; + int err, ch, i; + + if (! substream->runtime) + return -ENOTTY; + if (substream->stream != dir) + return -EINVAL; + + if ((ch = substream->runtime->channels) > 128) + return -EINVAL; + if (get_user(buf, &data32->bufs) || + get_user(frames, &data32->frames)) + return -EFAULT; + bufptr = compat_ptr(buf); + bufs = kmalloc(sizeof(void __user *) * ch, GFP_KERNEL); + if (bufs == NULL) + return -ENOMEM; + for (i = 0; i < ch; i++) { + u32 ptr; + if (get_user(ptr, bufptr)) { + kfree(bufs); + return -EFAULT; + } + bufs[ch] = compat_ptr(ptr); + bufptr++; + } + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + err = snd_pcm_lib_writev(substream, bufs, frames); + else + err = snd_pcm_lib_readv(substream, bufs, frames); + if (err >= 0) { + if (put_user(err, &data32->result)) + err = -EFAULT; + } + kfree(bufs); + return err; +} + + +struct sndrv_pcm_mmap_status32 { + s32 state; + s32 pad1; + u32 hw_ptr; + struct compat_timespec tstamp; + s32 suspended_state; +} __attribute__((packed)); + +struct sndrv_pcm_mmap_control32 { + u32 appl_ptr; + u32 avail_min; +}; + +struct sndrv_pcm_sync_ptr32 { + u32 flags; + union { + struct sndrv_pcm_mmap_status32 status; + unsigned char reserved[64]; + } s; + union { + struct sndrv_pcm_mmap_control32 control; + unsigned char reserved[64]; + } c; +} __attribute__((packed)); + +static int snd_pcm_ioctl_sync_ptr_compat(snd_pcm_substream_t *substream, + struct sndrv_pcm_sync_ptr32 __user *src) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + volatile struct sndrv_pcm_mmap_status *status; + volatile struct sndrv_pcm_mmap_control *control; + u32 sflags; + struct sndrv_pcm_mmap_control scontrol; + struct sndrv_pcm_mmap_status sstatus; + int err; + + snd_assert(runtime, return -EINVAL); + + if (get_user(sflags, &src->flags) || + get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) || + get_user(scontrol.avail_min, &src->c.control.avail_min)) + return -EFAULT; + if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) { + err = snd_pcm_hwsync(substream); + if (err < 0) + return err; + } + status = runtime->status; + control = runtime->control; + snd_pcm_stream_lock_irq(substream); + if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) + control->appl_ptr = scontrol.appl_ptr; + else + scontrol.appl_ptr = control->appl_ptr; + if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) + control->avail_min = scontrol.avail_min; + else + scontrol.avail_min = control->avail_min; + sstatus.state = status->state; + sstatus.hw_ptr = status->hw_ptr; + sstatus.tstamp = status->tstamp; + sstatus.suspended_state = status->suspended_state; + snd_pcm_stream_unlock_irq(substream); + if (put_user(sstatus.state, &src->s.status.state) || + put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) || + put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp.tv_sec) || + put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp.tv_nsec) || + put_user(sstatus.suspended_state, &src->s.status.suspended_state) || + put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) || + put_user(scontrol.avail_min, &src->c.control.avail_min)) + return -EFAULT; + + return 0; +} + + +/* + */ +enum { + SNDRV_PCM_IOCTL_HW_REFINE32 = _IOWR('A', 0x10, struct sndrv_pcm_hw_params32), + SNDRV_PCM_IOCTL_HW_PARAMS32 = _IOWR('A', 0x11, struct sndrv_pcm_hw_params32), + SNDRV_PCM_IOCTL_SW_PARAMS32 = _IOWR('A', 0x13, struct sndrv_pcm_sw_params32), + SNDRV_PCM_IOCTL_STATUS32 = _IOR('A', 0x20, struct sndrv_pcm_status32), + SNDRV_PCM_IOCTL_DELAY32 = _IOR('A', 0x21, s32), + SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct sndrv_pcm_channel_info32), + SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32), + SNDRV_PCM_IOCTL_FORWARD32 = _IOW('A', 0x49, u32), + SNDRV_PCM_IOCTL_WRITEI_FRAMES32 = _IOW('A', 0x50, struct sndrv_xferi32), + SNDRV_PCM_IOCTL_READI_FRAMES32 = _IOR('A', 0x51, struct sndrv_xferi32), + SNDRV_PCM_IOCTL_WRITEN_FRAMES32 = _IOW('A', 0x52, struct sndrv_xfern32), + SNDRV_PCM_IOCTL_READN_FRAMES32 = _IOR('A', 0x53, struct sndrv_xfern32), + SNDRV_PCM_IOCTL_SYNC_PTR32 = _IOWR('A', 0x23, struct sndrv_pcm_sync_ptr32), + +}; + +static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) +{ + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + void __user *argp = compat_ptr(arg); + + pcm_file = file->private_data; + if (! pcm_file) + return -ENOTTY; + substream = pcm_file->substream; + if (! substream) + return -ENOTTY; + + /* + * When PCM is used on 32bit mode, we need to disable + * mmap of PCM status/control records because of the size + * incompatibility. + */ + substream->no_mmap_ctrl = 1; + + switch (cmd) { + case SNDRV_PCM_IOCTL_PVERSION: + case SNDRV_PCM_IOCTL_INFO: + case SNDRV_PCM_IOCTL_TSTAMP: + case SNDRV_PCM_IOCTL_HWSYNC: + case SNDRV_PCM_IOCTL_PREPARE: + case SNDRV_PCM_IOCTL_RESET: + case SNDRV_PCM_IOCTL_START: + case SNDRV_PCM_IOCTL_DROP: + case SNDRV_PCM_IOCTL_DRAIN: + case SNDRV_PCM_IOCTL_PAUSE: + case SNDRV_PCM_IOCTL_HW_FREE: + case SNDRV_PCM_IOCTL_RESUME: + case SNDRV_PCM_IOCTL_XRUN: + case SNDRV_PCM_IOCTL_LINK: + case SNDRV_PCM_IOCTL_UNLINK: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + return snd_pcm_playback_ioctl1(substream, cmd, argp); + else + return snd_pcm_capture_ioctl1(substream, cmd, argp); + case SNDRV_PCM_IOCTL_HW_REFINE32: + return snd_pcm_ioctl_hw_params_compat(substream, 1, argp); + case SNDRV_PCM_IOCTL_HW_PARAMS32: + return snd_pcm_ioctl_hw_params_compat(substream, 0, argp); + case SNDRV_PCM_IOCTL_SW_PARAMS32: + return snd_pcm_ioctl_sw_params_compat(substream, argp); + case SNDRV_PCM_IOCTL_STATUS32: + return snd_pcm_status_user_compat(substream, argp); + case SNDRV_PCM_IOCTL_SYNC_PTR32: + return snd_pcm_ioctl_sync_ptr_compat(substream, argp); + case SNDRV_PCM_IOCTL_CHANNEL_INFO32: + return snd_pcm_ioctl_channel_info_compat(substream, argp); + case SNDRV_PCM_IOCTL_WRITEI_FRAMES32: + return snd_pcm_ioctl_xferi_compat(substream, SNDRV_PCM_STREAM_PLAYBACK, argp); + case SNDRV_PCM_IOCTL_READI_FRAMES32: + return snd_pcm_ioctl_xferi_compat(substream, SNDRV_PCM_STREAM_CAPTURE, argp); + case SNDRV_PCM_IOCTL_WRITEN_FRAMES32: + return snd_pcm_ioctl_xfern_compat(substream, SNDRV_PCM_STREAM_PLAYBACK, argp); + case SNDRV_PCM_IOCTL_READN_FRAMES32: + return snd_pcm_ioctl_xfern_compat(substream, SNDRV_PCM_STREAM_CAPTURE, argp); + case SNDRV_PCM_IOCTL_DELAY32: + return snd_pcm_ioctl_delay_compat(substream, argp); + case SNDRV_PCM_IOCTL_REWIND32: + return snd_pcm_ioctl_rewind_compat(substream, argp); + case SNDRV_PCM_IOCTL_FORWARD32: + return snd_pcm_ioctl_forward_compat(substream, argp); + } + + return -ENOIOCTLCMD; +} diff -Nru a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c --- a/sound/core/pcm_memory.c 2005-01-28 14:22:13 -08:00 +++ b/sound/core/pcm_memory.c 2005-01-28 14:22:13 -08:00 @@ -291,7 +291,7 @@ * @substream: the substream to allocate the DMA buffer to * @size: the requested buffer size in bytes * - * Allocates the DMA buffer on the BUS type given by + * Allocates the DMA buffer on the BUS type given earlier to * snd_pcm_lib_preallocate_xxx_pages(). * * Returns 1 if the buffer is changed, 0 if not changed, or a negative diff -Nru a/sound/core/pcm_native.c b/sound/core/pcm_native.c --- a/sound/core/pcm_native.c 2005-01-28 14:22:13 -08:00 +++ b/sound/core/pcm_native.c 2005-01-28 14:22:13 -08:00 @@ -65,7 +65,7 @@ * */ -rwlock_t snd_pcm_link_rwlock = RW_LOCK_UNLOCKED; +DEFINE_RWLOCK(snd_pcm_link_rwlock); static DECLARE_RWSEM(snd_pcm_link_rwsem); @@ -2640,40 +2640,28 @@ return snd_pcm_common_ioctl1(substream, cmd, arg); } -static int snd_pcm_playback_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long snd_pcm_playback_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { snd_pcm_file_t *pcm_file; - int err; pcm_file = file->private_data; if (((cmd >> 8) & 0xff) != 'A') return -ENOTTY; - /* FIXME: need to unlock BKL to allow preemption */ - unlock_kernel(); - err = snd_pcm_playback_ioctl1(pcm_file->substream, cmd, (void __user *)arg); - lock_kernel(); - return err; + return snd_pcm_playback_ioctl1(pcm_file->substream, cmd, (void __user *)arg); } -static int snd_pcm_capture_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long snd_pcm_capture_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { snd_pcm_file_t *pcm_file; - int err; pcm_file = file->private_data; if (((cmd >> 8) & 0xff) != 'A') return -ENOTTY; - /* FIXME: need to unlock BKL to allow preemption */ - unlock_kernel(); - err = snd_pcm_capture_ioctl1(pcm_file->substream, cmd, (void __user *)arg); - lock_kernel(); - return err; + return snd_pcm_capture_ioctl1(pcm_file->substream, cmd, (void __user *)arg); } int snd_pcm_kernel_playback_ioctl(snd_pcm_substream_t *substream, @@ -3198,6 +3186,15 @@ } /* + * ioctl32 compat + */ +#ifdef CONFIG_COMPAT +#include "pcm_compat.c" +#else +#define snd_pcm_ioctl_compat NULL +#endif + +/* * To be removed helpers to keep binary compatibility */ @@ -3318,7 +3315,8 @@ .open = snd_pcm_open, .release = snd_pcm_release, .poll = snd_pcm_playback_poll, - .ioctl = snd_pcm_playback_ioctl, + .unlocked_ioctl = snd_pcm_playback_ioctl, + .compat_ioctl = snd_pcm_ioctl_compat, .mmap = snd_pcm_mmap, .fasync = snd_pcm_fasync, }; @@ -3330,7 +3328,8 @@ .open = snd_pcm_open, .release = snd_pcm_release, .poll = snd_pcm_capture_poll, - .ioctl = snd_pcm_capture_ioctl, + .unlocked_ioctl = snd_pcm_capture_ioctl, + .compat_ioctl = snd_pcm_ioctl_compat, .mmap = snd_pcm_mmap, .fasync = snd_pcm_fasync, }; diff -Nru a/sound/core/rawmidi.c b/sound/core/rawmidi.c --- a/sound/core/rawmidi.c 2005-01-28 14:22:13 -08:00 +++ b/sound/core/rawmidi.c 2005-01-28 14:22:13 -08:00 @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -149,10 +150,8 @@ /* we need wait a while to make sure that Tx FIFOs are empty */ if (substream->ops->drain) substream->ops->drain(substream); - else { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ / 20); - } + else + msleep(50); snd_rawmidi_drop_output(substream); } return err; @@ -673,8 +672,7 @@ return 0; } -static inline int _snd_rawmidi_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { snd_rawmidi_file_t *rfile; void __user *argp = (void __user *)arg; @@ -784,17 +782,6 @@ return -ENOTTY; } -/* FIXME: need to unlock BKL to allow preemption */ -static int snd_rawmidi_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int err; - unlock_kernel(); - err = _snd_rawmidi_ioctl(inode, file, cmd, arg); - lock_kernel(); - return err; -} - static int snd_rawmidi_control_ioctl(snd_card_t * card, snd_ctl_file_t * control, unsigned int cmd, @@ -1278,6 +1265,14 @@ } /* + */ +#ifdef CONFIG_COMPAT +#include "rawmidi_compat.c" +#else +#define snd_rawmidi_ioctl_compat NULL +#endif + +/* */ @@ -1347,7 +1342,8 @@ .open = snd_rawmidi_open, .release = snd_rawmidi_release, .poll = snd_rawmidi_poll, - .ioctl = snd_rawmidi_ioctl, + .unlocked_ioctl = snd_rawmidi_ioctl, + .compat_ioctl = snd_rawmidi_ioctl_compat, }; static snd_minor_t snd_rawmidi_reg = @@ -1628,6 +1624,7 @@ { snd_ctl_register_ioctl(snd_rawmidi_control_ioctl); + snd_ctl_register_ioctl_compat(snd_rawmidi_control_ioctl); #ifdef CONFIG_SND_OSSEMUL { int i; /* check device map table */ @@ -1649,6 +1646,7 @@ static void __exit alsa_rawmidi_exit(void) { snd_ctl_unregister_ioctl(snd_rawmidi_control_ioctl); + snd_ctl_unregister_ioctl_compat(snd_rawmidi_control_ioctl); } module_init(alsa_rawmidi_init) diff -Nru a/sound/core/rawmidi_compat.c b/sound/core/rawmidi_compat.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/sound/core/rawmidi_compat.c 2005-01-28 14:22:13 -08:00 @@ -0,0 +1,120 @@ +/* + * 32bit -> 64bit ioctl wrapper for raw MIDI API + * Copyright (c) by Takashi Iwai + * + * 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 + * + */ + +/* This file included from rawmidi.c */ + +#include + +struct sndrv_rawmidi_params32 { + s32 stream; + u32 buffer_size; + u32 avail_min; + unsigned int no_active_sensing; /* avoid bit-field */ + unsigned char reserved[16]; +} __attribute__((packed)); + +static int snd_rawmidi_ioctl_params_compat(snd_rawmidi_file_t *rfile, + struct sndrv_rawmidi_params32 __user *src) +{ + snd_rawmidi_params_t params; + unsigned int val; + + if (rfile->output == NULL) + return -EINVAL; + if (get_user(params.stream, &src->stream) || + get_user(params.buffer_size, &src->buffer_size) || + get_user(params.avail_min, &src->avail_min) || + get_user(val, &src->no_active_sensing)) + return -EFAULT; + params.no_active_sensing = val; + switch (params.stream) { + case SNDRV_RAWMIDI_STREAM_OUTPUT: + return snd_rawmidi_output_params(rfile->output, ¶ms); + case SNDRV_RAWMIDI_STREAM_INPUT: + return snd_rawmidi_input_params(rfile->input, ¶ms); + } + return -EINVAL; +} + +struct sndrv_rawmidi_status32 { + s32 stream; + struct compat_timespec tstamp; + u32 avail; + u32 xruns; + unsigned char reserved[16]; +} __attribute__((packed)); + +static int snd_rawmidi_ioctl_status_compat(snd_rawmidi_file_t *rfile, + struct sndrv_rawmidi_status32 __user *src) +{ + int err; + snd_rawmidi_status_t status; + + if (rfile->output == NULL) + return -EINVAL; + if (get_user(status.stream, &src->stream)) + return -EFAULT; + + switch (status.stream) { + case SNDRV_RAWMIDI_STREAM_OUTPUT: + err = snd_rawmidi_output_status(rfile->output, &status); + break; + case SNDRV_RAWMIDI_STREAM_INPUT: + err = snd_rawmidi_input_status(rfile->input, &status); + break; + default: + return -EINVAL; + } + if (err < 0) + return err; + + if (put_user(status.tstamp.tv_sec, &src->tstamp.tv_sec) || + put_user(status.tstamp.tv_nsec, &src->tstamp.tv_nsec) || + put_user(status.avail, &src->avail) || + put_user(status.xruns, &src->xruns)) + return -EFAULT; + + return 0; +} + +enum { + SNDRV_RAWMIDI_IOCTL_PARAMS32 = _IOWR('W', 0x10, struct sndrv_rawmidi_params32), + SNDRV_RAWMIDI_IOCTL_STATUS32 = _IOWR('W', 0x20, struct sndrv_rawmidi_status32), +}; + +static long snd_rawmidi_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) +{ + snd_rawmidi_file_t *rfile; + void __user *argp = compat_ptr(arg); + + rfile = file->private_data; + switch (cmd) { + case SNDRV_RAWMIDI_IOCTL_PVERSION: + case SNDRV_RAWMIDI_IOCTL_INFO: + case SNDRV_RAWMIDI_IOCTL_DROP: + case SNDRV_RAWMIDI_IOCTL_DRAIN: + return snd_rawmidi_ioctl(file, cmd, (unsigned long)argp); + case SNDRV_RAWMIDI_IOCTL_PARAMS32: + return snd_rawmidi_ioctl_params_compat(rfile, argp); + case SNDRV_RAWMIDI_IOCTL_STATUS32: + return snd_rawmidi_ioctl_status_compat(rfile, argp); + } + return -ENOIOCTLCMD; +} diff -Nru a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c --- a/sound/core/seq/oss/seq_oss.c 2005-01-28 14:22:13 -08:00 +++ b/sound/core/seq/oss/seq_oss.c 2005-01-28 14:22:13 -08:00 @@ -59,7 +59,7 @@ static int odev_release(struct inode *inode, struct file *file); static ssize_t odev_read(struct file *file, char __user *buf, size_t count, loff_t *offset); static ssize_t odev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset); -static int odev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); +static long odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg); static unsigned int odev_poll(struct file *file, poll_table * wait); #ifdef CONFIG_PROC_FS static void info_read(snd_info_entry_t *entry, snd_info_buffer_t *buf); @@ -177,20 +177,20 @@ return snd_seq_oss_write(dp, buf, count, file); } -static int -odev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +static long +odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { seq_oss_devinfo_t *dp; - int err; dp = file->private_data; snd_assert(dp != NULL, return -EIO); - /* FIXME: need to unlock BKL to allow preemption */ - unlock_kernel(); - err = snd_seq_oss_ioctl(dp, cmd, arg); - lock_kernel(); - return err; + return snd_seq_oss_ioctl(dp, cmd, arg); } +#ifdef CONFIG_COMPAT +#define odev_ioctl_compat odev_ioctl +#else +#define odev_ioctl_compat NULL +#endif static unsigned int odev_poll(struct file *file, poll_table * wait) @@ -213,7 +213,8 @@ .open = odev_open, .release = odev_release, .poll = odev_poll, - .ioctl = odev_ioctl, + .unlocked_ioctl = odev_ioctl, + .compat_ioctl = odev_ioctl_compat, }; static snd_minor_t seq_oss_reg = { diff -Nru a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c --- a/sound/core/seq/oss/seq_oss_midi.c 2005-01-28 14:22:13 -08:00 +++ b/sound/core/seq/oss/seq_oss_midi.c 2005-01-28 14:22:13 -08:00 @@ -56,7 +56,7 @@ static int max_midi_devs; static seq_oss_midi_t *midi_devs[SNDRV_SEQ_OSS_MAX_MIDI_DEVS]; -static spinlock_t register_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(register_lock); /* * prototypes diff -Nru a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c --- a/sound/core/seq/oss/seq_oss_synth.c 2005-01-28 14:22:13 -08:00 +++ b/sound/core/seq/oss/seq_oss_synth.c 2005-01-28 14:22:13 -08:00 @@ -75,7 +75,7 @@ "MIDI", /* name */ }; -static spinlock_t register_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(register_lock); /* * prototypes diff -Nru a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c --- a/sound/core/seq/seq_clientmgr.c 2005-01-28 14:22:13 -08:00 +++ b/sound/core/seq/seq_clientmgr.c 2005-01-28 14:22:13 -08:00 @@ -51,7 +51,7 @@ #define SNDRV_SEQ_LFLG_OUTPUT 0x0002 #define SNDRV_SEQ_LFLG_OPEN (SNDRV_SEQ_LFLG_INPUT|SNDRV_SEQ_LFLG_OUTPUT) -static spinlock_t clients_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(clients_lock); static DECLARE_MUTEX(register_mutex); /* @@ -2131,21 +2131,20 @@ } -static int snd_seq_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long snd_seq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { client_t *client = (client_t *) file->private_data; - int err; snd_assert(client != NULL, return -ENXIO); - /* FIXME: need to unlock BKL to allow preemption */ - unlock_kernel(); - err = snd_seq_do_ioctl(client, cmd, (void __user *) arg); - lock_kernel(); - return err; + return snd_seq_do_ioctl(client, cmd, (void __user *) arg); } +#ifdef CONFIG_COMPAT +#include "seq_compat.c" +#else +#define snd_seq_ioctl_compat NULL +#endif /* -------------------------------------------------------- */ @@ -2462,7 +2461,8 @@ .open = snd_seq_open, .release = snd_seq_release, .poll = snd_seq_poll, - .ioctl = snd_seq_ioctl, + .unlocked_ioctl = snd_seq_ioctl, + .compat_ioctl = snd_seq_ioctl_compat, }; static snd_minor_t snd_seq_reg = diff -Nru a/sound/core/seq/seq_compat.c b/sound/core/seq/seq_compat.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/sound/core/seq/seq_compat.c 2005-01-28 14:22:13 -08:00 @@ -0,0 +1,137 @@ +/* + * 32bit -> 64bit ioctl wrapper for sequencer API + * Copyright (c) by Takashi Iwai + * + * 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 + * + */ + +/* This file included from seq.c */ + +#include + +struct sndrv_seq_port_info32 { + struct sndrv_seq_addr addr; /* client/port numbers */ + char name[64]; /* port name */ + + u32 capability; /* port capability bits */ + u32 type; /* port type bits */ + s32 midi_channels; /* channels per MIDI port */ + s32 midi_voices; /* voices per MIDI port */ + s32 synth_voices; /* voices per SYNTH port */ + + s32 read_use; /* R/O: subscribers for output (from this port) */ + s32 write_use; /* R/O: subscribers for input (to this port) */ + + u32 kernel; /* reserved for kernel use (must be NULL) */ + u32 flags; /* misc. conditioning */ + unsigned char time_queue; /* queue # for timestamping */ + char reserved[59]; /* for future use */ +}; + +static int snd_seq_call_port_info_ioctl(client_t *client, unsigned int cmd, + struct sndrv_seq_port_info32 __user *data32) +{ + int err = -EFAULT; + snd_seq_port_info_t *data; + mm_segment_t fs; + + data = kmalloc(sizeof(*data), GFP_KERNEL); + if (! data) + return -ENOMEM; + + if (copy_from_user(data, data32, sizeof(*data32)) || + get_user(data->flags, &data32->flags) || + get_user(data->time_queue, &data32->time_queue)) + goto error; + data->kernel = NULL; + + fs = snd_enter_user(); + err = snd_seq_do_ioctl(client, cmd, data); + snd_leave_user(fs); + if (err < 0) + goto error; + + if (copy_to_user(data32, data, sizeof(*data32)) || + put_user(data->flags, &data32->flags) || + put_user(data->time_queue, &data32->time_queue)) + err = -EFAULT; + + error: + kfree(data); + return err; +} + + + +/* + */ + +enum { + SNDRV_SEQ_IOCTL_CREATE_PORT32 = _IOWR('S', 0x20, struct sndrv_seq_port_info32), + SNDRV_SEQ_IOCTL_DELETE_PORT32 = _IOW ('S', 0x21, struct sndrv_seq_port_info32), + SNDRV_SEQ_IOCTL_GET_PORT_INFO32 = _IOWR('S', 0x22, struct sndrv_seq_port_info32), + SNDRV_SEQ_IOCTL_SET_PORT_INFO32 = _IOW ('S', 0x23, struct sndrv_seq_port_info32), + SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT32 = _IOWR('S', 0x52, struct sndrv_seq_port_info32), +}; + +static long snd_seq_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) +{ + client_t *client = (client_t *) file->private_data; + void __user *argp = compat_ptr(arg); + + snd_assert(client != NULL, return -ENXIO); + + switch (cmd) { + case SNDRV_SEQ_IOCTL_PVERSION: + case SNDRV_SEQ_IOCTL_CLIENT_ID: + case SNDRV_SEQ_IOCTL_SYSTEM_INFO: + case SNDRV_SEQ_IOCTL_GET_CLIENT_INFO: + case SNDRV_SEQ_IOCTL_SET_CLIENT_INFO: + case SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT: + case SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT: + case SNDRV_SEQ_IOCTL_CREATE_QUEUE: + case SNDRV_SEQ_IOCTL_DELETE_QUEUE: + case SNDRV_SEQ_IOCTL_GET_QUEUE_INFO: + case SNDRV_SEQ_IOCTL_SET_QUEUE_INFO: + case SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE: + case SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS: + case SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO: + case SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO: + case SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER: + case SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER: + case SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT: + case SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT: + case SNDRV_SEQ_IOCTL_GET_CLIENT_POOL: + case SNDRV_SEQ_IOCTL_SET_CLIENT_POOL: + case SNDRV_SEQ_IOCTL_REMOVE_EVENTS: + case SNDRV_SEQ_IOCTL_QUERY_SUBS: + case SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION: + case SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT: + case SNDRV_SEQ_IOCTL_RUNNING_MODE: + return snd_seq_do_ioctl(client, cmd, argp); + case SNDRV_SEQ_IOCTL_CREATE_PORT32: + return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, argp); + case SNDRV_SEQ_IOCTL_DELETE_PORT32: + return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_DELETE_PORT, argp); + case SNDRV_SEQ_IOCTL_GET_PORT_INFO32: + return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_GET_PORT_INFO, argp); + case SNDRV_SEQ_IOCTL_SET_PORT_INFO32: + return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_SET_PORT_INFO, argp); + case SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT32: + return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, argp); + } + return -ENOIOCTLCMD; +} diff -Nru a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c --- a/sound/core/seq/seq_queue.c 2005-01-28 14:22:13 -08:00 +++ b/sound/core/seq/seq_queue.c 2005-01-28 14:22:13 -08:00 @@ -49,7 +49,7 @@ /* list of allocated queues */ static queue_t *queue_list[SNDRV_SEQ_MAX_QUEUES]; -static spinlock_t queue_list_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(queue_list_lock); /* number of queues allocated */ static int num_queues; diff -Nru a/sound/core/sound.c b/sound/core/sound.c --- a/sound/core/sound.c 2005-01-28 14:22:13 -08:00 +++ b/sound/core/sound.c 2005-01-28 14:22:13 -08:00 @@ -467,6 +467,10 @@ EXPORT_SYMBOL(snd_ctl_notify); EXPORT_SYMBOL(snd_ctl_register_ioctl); EXPORT_SYMBOL(snd_ctl_unregister_ioctl); +#ifdef CONFIG_COMPAT +EXPORT_SYMBOL(snd_ctl_register_ioctl_compat); +EXPORT_SYMBOL(snd_ctl_unregister_ioctl_compat); +#endif EXPORT_SYMBOL(snd_ctl_elem_read); EXPORT_SYMBOL(snd_ctl_elem_write); /* misc.c */ diff -Nru a/sound/core/timer.c b/sound/core/timer.c --- a/sound/core/timer.c 2005-01-28 14:22:13 -08:00 +++ b/sound/core/timer.c 2005-01-28 14:22:13 -08:00 @@ -76,7 +76,7 @@ static LIST_HEAD(snd_timer_slave_list); /* lock for slave active lists */ -static spinlock_t slave_active_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(slave_active_lock); static DECLARE_MUTEX(register_mutex); @@ -1653,8 +1653,7 @@ return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0; } -static inline int _snd_timer_user_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { snd_timer_user_t *tu; void __user *argp = (void __user *)arg; @@ -1701,17 +1700,6 @@ return -ENOTTY; } -/* FIXME: need to unlock BKL to allow preemption */ -static int snd_timer_user_ioctl(struct inode *inode, struct file * file, - unsigned int cmd, unsigned long arg) -{ - int err; - unlock_kernel(); - err = _snd_timer_user_ioctl(inode, file, cmd, arg); - lock_kernel(); - return err; -} - static int snd_timer_user_fasync(int fd, struct file * file, int on) { snd_timer_user_t *tu; @@ -1803,6 +1791,12 @@ return mask; } +#ifdef CONFIG_COMPAT +#include "timer_compat.c" +#else +#define snd_timer_user_ioctl_compat NULL +#endif + static struct file_operations snd_timer_f_ops = { .owner = THIS_MODULE, @@ -1810,7 +1804,8 @@ .open = snd_timer_user_open, .release = snd_timer_user_release, .poll = snd_timer_user_poll, - .ioctl = snd_timer_user_ioctl, + .unlocked_ioctl = snd_timer_user_ioctl, + .compat_ioctl = snd_timer_user_ioctl_compat, .fasync = snd_timer_user_fasync, }; diff -Nru a/sound/core/timer_compat.c b/sound/core/timer_compat.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/sound/core/timer_compat.c 2005-01-28 14:22:13 -08:00 @@ -0,0 +1,119 @@ +/* + * 32bit -> 64bit ioctl wrapper for timer API + * Copyright (c) by Takashi Iwai + * + * 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 + * + */ + +/* This file included from timer.c */ + +#include + +struct sndrv_timer_info32 { + u32 flags; + s32 card; + unsigned char id[64]; + unsigned char name[80]; + u32 reserved0; + u32 resolution; + unsigned char reserved[64]; +}; + +static int snd_timer_user_info_compat(struct file *file, + struct sndrv_timer_info32 __user *_info) +{ + snd_timer_user_t *tu; + struct sndrv_timer_info32 info; + snd_timer_t *t; + + tu = file->private_data; + snd_assert(tu->timeri != NULL, return -ENXIO); + t = tu->timeri->timer; + snd_assert(t != NULL, return -ENXIO); + memset(&info, 0, sizeof(info)); + info.card = t->card ? t->card->number : -1; + if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) + info.flags |= SNDRV_TIMER_FLG_SLAVE; + strlcpy(info.id, t->id, sizeof(info.id)); + strlcpy(info.name, t->name, sizeof(info.name)); + info.resolution = t->hw.resolution; + if (copy_to_user(_info, &info, sizeof(*_info))) + return -EFAULT; + return 0; +} + +struct sndrv_timer_status32 { + struct compat_timespec tstamp; + u32 resolution; + u32 lost; + u32 overrun; + u32 queue; + unsigned char reserved[64]; +}; + +static int snd_timer_user_status_compat(struct file *file, + struct sndrv_timer_status32 __user *_status) +{ + snd_timer_user_t *tu; + snd_timer_status_t status; + + tu = file->private_data; + snd_assert(tu->timeri != NULL, return -ENXIO); + memset(&status, 0, sizeof(status)); + status.tstamp = tu->tstamp; + status.resolution = snd_timer_resolution(tu->timeri); + status.lost = tu->timeri->lost; + status.overrun = tu->overrun; + spin_lock_irq(&tu->qlock); + status.queue = tu->qused; + spin_unlock_irq(&tu->qlock); + if (copy_to_user(_status, &status, sizeof(status))) + return -EFAULT; + return 0; +} + +/* + */ + +enum { + SNDRV_TIMER_IOCTL_INFO32 = _IOR('T', 0x11, struct sndrv_timer_info32), + SNDRV_TIMER_IOCTL_STATUS32 = _IOW('T', 0x14, struct sndrv_timer_status32), +}; + +static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) +{ + void __user *argp = compat_ptr(arg); + + switch (cmd) { + case SNDRV_TIMER_IOCTL_PVERSION: + case SNDRV_TIMER_IOCTL_TREAD: + case SNDRV_TIMER_IOCTL_GINFO: + case SNDRV_TIMER_IOCTL_GPARAMS: + case SNDRV_TIMER_IOCTL_GSTATUS: + case SNDRV_TIMER_IOCTL_SELECT: + case SNDRV_TIMER_IOCTL_PARAMS: + case SNDRV_TIMER_IOCTL_START: + case SNDRV_TIMER_IOCTL_STOP: + case SNDRV_TIMER_IOCTL_CONTINUE: + case SNDRV_TIMER_IOCTL_NEXT_DEVICE: + return snd_timer_user_ioctl(file, cmd, (unsigned long)argp); + case SNDRV_TIMER_IOCTL_INFO32: + return snd_timer_user_info_compat(file, argp); + case SNDRV_TIMER_IOCTL_STATUS32: + return snd_timer_user_status_compat(file, argp); + } + return -ENOIOCTLCMD; +} diff -Nru a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c --- a/sound/drivers/vx/vx_core.c 2005-01-28 14:22:13 -08:00 +++ b/sound/drivers/vx/vx_core.c 2005-01-28 14:22:13 -08:00 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -45,12 +46,10 @@ */ void snd_vx_delay(vx_core_t *chip, int xmsec) { - if (! in_interrupt() && xmsec >= 1000 / HZ) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((xmsec * HZ + 999) / 1000); - } else { + if (! in_interrupt() && xmsec >= 1000 / HZ) + msleep(xmsec); + else mdelay(xmsec); - } } /* @@ -610,6 +609,10 @@ static char *uer_type[] = { "Consumer", "Professional", "Not Present" }; snd_iprintf(buffer, "%s\n", chip->card->longname); + snd_iprintf(buffer, "Xilinx Firmware: %s\n", + chip->chip_status & VX_STAT_XILINX_LOADED ? "Loaded" : "No"); + snd_iprintf(buffer, "Device Initialized: %s\n", + chip->chip_status & VX_STAT_DEVICE_INIT ? "Yes" : "No"); snd_iprintf(buffer, "DSP audio info:"); if (chip->audio_info & VX_AUDIO_INFO_REAL_TIME) snd_iprintf(buffer, " realtime"); diff -Nru a/sound/drivers/vx/vx_hwdep.c b/sound/drivers/vx/vx_hwdep.c --- a/sound/drivers/vx/vx_hwdep.c 2005-01-28 14:22:13 -08:00 +++ b/sound/drivers/vx/vx_hwdep.c 2005-01-28 14:22:13 -08:00 @@ -21,6 +21,7 @@ */ #include +#include #include #include #include diff -Nru a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c --- a/sound/isa/gus/gus_pcm.c 2005-01-28 14:22:13 -08:00 +++ b/sound/isa/gus/gus_pcm.c 2005-01-28 14:22:13 -08:00 @@ -333,6 +333,7 @@ } } if (count > 0 && !in_interrupt()) { + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); if (signal_pending(current)) return -EAGAIN; diff -Nru a/sound/isa/gus/gus_reset.c b/sound/isa/gus/gus_reset.c --- a/sound/isa/gus/gus_reset.c 2005-01-28 14:22:13 -08:00 +++ b/sound/isa/gus/gus_reset.c 2005-01-28 14:22:13 -08:00 @@ -207,7 +207,6 @@ unsigned long flags; short i, ramp_ok; unsigned short ramp_end; - long time; if (!in_interrupt()) { /* this can't be done in interrupt */ for (i = v_min, ramp_ok = 0; i <= v_max; i++) { @@ -227,11 +226,7 @@ } spin_unlock_irqrestore(&gus->reg_lock, flags); } - time = HZ / 20; - while (time > 0 && !signal_pending(current)) { - set_current_state(TASK_INTERRUPTIBLE); - time = schedule_timeout(time); - } + msleep_interruptible(50); } snd_gf1_clear_voices(gus, v_min, v_max); } diff -Nru a/sound/isa/sb/emu8000.c b/sound/isa/sb/emu8000.c --- a/sound/isa/sb/emu8000.c 2005-01-28 14:22:13 -08:00 +++ b/sound/isa/sb/emu8000.c 2005-01-28 14:22:13 -08:00 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -355,8 +356,7 @@ { send_array(emu, init1, ARRAY_SIZE(init1)/4); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout((HZ * (44099 + 1024)) / 44100); /* wait for 1024 clocks */ + msleep((1024 * 1000) / 44100); /* wait for 1024 clocks */ send_array(emu, init2, ARRAY_SIZE(init2)/4); send_array(emu, init3, ARRAY_SIZE(init3)/4); diff -Nru a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c --- a/sound/isa/wavefront/wavefront_synth.c 2005-01-28 14:22:13 -08:00 +++ b/sound/isa/wavefront/wavefront_synth.c 2005-01-28 14:22:13 -08:00 @@ -1751,6 +1751,7 @@ outb (val,port); spin_unlock_irq(&dev->irq_lock); while (1) { + set_current_state(TASK_INTERRUPTIBLE); if ((timeout = schedule_timeout(timeout)) == 0) return; if (dev->irq_ok) diff -Nru a/sound/pci/Kconfig b/sound/pci/Kconfig --- a/sound/pci/Kconfig 2005-01-28 14:22:13 -08:00 +++ b/sound/pci/Kconfig 2005-01-28 14:22:13 -08:00 @@ -514,5 +514,15 @@ To compile this driver as a module, choose M here: the module will be called snd-vx222. -endmenu +config SND_HDA_INTEL + tristate "Intel HD Audio" + depends on SND + select SND_PCM + help + Say Y here to include support for Intel "High Definition + Audio" (Azalia) motherboard devices. + + To compile this driver as a module, choose M here: the module + will be called snd-hda-intel. +endmenu diff -Nru a/sound/pci/Makefile b/sound/pci/Makefile --- a/sound/pci/Makefile 2005-01-28 14:22:13 -08:00 +++ b/sound/pci/Makefile 2005-01-28 14:22:13 -08:00 @@ -53,6 +53,7 @@ ca0106/ \ cs46xx/ \ emu10k1/ \ + hda/ \ ice1712/ \ korg1212/ \ mixart/ \ diff -Nru a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c --- a/sound/pci/ac97/ac97_codec.c 2005-01-28 14:22:13 -08:00 +++ b/sound/pci/ac97/ac97_codec.c 2005-01-28 14:22:13 -08:00 @@ -438,9 +438,10 @@ } /* - * + * Controls */ +/* input mux */ static int snd_ac97_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { static char *texts[8] = { @@ -481,6 +482,7 @@ return snd_ac97_update(ac97, AC97_REC_SEL, val); } +/* standard stereo enums */ #define AC97_ENUM_DOUBLE(xname, reg, shift, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_info_enum_double, \ .get = snd_ac97_get_enum_double, .put = snd_ac97_put_enum_double, \ @@ -543,6 +545,30 @@ return snd_ac97_update_bits(ac97, reg, 1 << shift, val << shift); } +/* save/restore ac97 v2.3 paging */ +static int snd_ac97_page_save(ac97_t *ac97, int reg, snd_kcontrol_t *kcontrol) +{ + int page_save = -1; + if ((kcontrol->private_value & (1<<25)) && + (ac97->ext_id & AC97_EI_REV_MASK) >= AC97_EI_REV_23 && + (reg >= 0x60 && reg < 0x70)) { + unsigned short page = (kcontrol->private_value >> 26) & 0x0f; + down(&ac97->page_mutex); /* lock paging */ + page_save = snd_ac97_read(ac97, AC97_INT_PAGING) & AC97_PAGE_MASK; + snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page); + } + return page_save; +} + +static void snd_ac97_page_restore(ac97_t *ac97, int page_save) +{ + if (page_save >= 0) { + snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page_save); + up(&ac97->page_mutex); /* unlock paging */ + } +} + +/* volume and switch controls */ int snd_ac97_info_volsw(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { int mask = (kcontrol->private_value >> 16) & 0xff; @@ -564,7 +590,9 @@ int rshift = (kcontrol->private_value >> 12) & 0x0f; int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0x01; - + int page_save; + + page_save = snd_ac97_page_save(ac97, reg, kcontrol); ucontrol->value.integer.value[0] = (snd_ac97_read_cache(ac97, reg) >> shift) & mask; if (shift != rshift) ucontrol->value.integer.value[1] = (snd_ac97_read_cache(ac97, reg) >> rshift) & mask; @@ -573,6 +601,7 @@ if (shift != rshift) ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; } + snd_ac97_page_restore(ac97, page_save); return 0; } @@ -584,8 +613,10 @@ int rshift = (kcontrol->private_value >> 12) & 0x0f; int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0x01; + int err, page_save; unsigned short val, val2, val_mask; + page_save = snd_ac97_page_save(ac97, reg, kcontrol); val = (ucontrol->value.integer.value[0] & mask); if (invert) val = mask - val; @@ -598,47 +629,15 @@ val_mask |= mask << rshift; val |= val2 << rshift; } - return snd_ac97_update_bits(ac97, reg, val_mask, val); + err = snd_ac97_update_bits(ac97, reg, val_mask, val); + snd_ac97_page_restore(ac97, page_save); + return err; } #define AC97_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), .info = snd_ac97_info_volsw, \ .get = snd_ac97_get_volsw, .put = snd_ac97_put_volsw, \ .private_value = (reg) | ((shift_left) << 8) | ((shift_right) << 12) | ((mask) << 16) | ((invert) << 24) } - -static int snd_ac97_getput_page(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol, - int (*func)(snd_kcontrol_t *, snd_ctl_elem_value_t *)) -{ - ac97_t *ac97 = snd_kcontrol_chip(kcontrol); - int reg = kcontrol->private_value & 0xff; - int err; - - if ((ac97->ext_id & AC97_EI_REV_MASK) >= AC97_EI_REV_23 && - (reg >= 0x60 && reg < 0x70)) { - unsigned short page_save; - unsigned short page = (kcontrol->private_value >> 25) & 0x0f; - down(&ac97->page_mutex); /* lock paging */ - page_save = snd_ac97_read(ac97, AC97_INT_PAGING) & AC97_PAGE_MASK; - snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page); - err = func(kcontrol, ucontrol); - snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page_save); - up(&ac97->page_mutex); /* unlock paging */ - } else - err = func(kcontrol, ucontrol); - return err; -} - -/* for rev2.3 paging */ -int snd_ac97_page_get_volsw(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) -{ - return snd_ac97_getput_page(kcontrol, ucontrol, snd_ac97_get_volsw); -} - -/* for rev2.3 paging */ -int snd_ac97_page_put_volsw(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) -{ - return snd_ac97_getput_page(kcontrol, ucontrol, snd_ac97_put_volsw); -} static const snd_kcontrol_new_t snd_ac97_controls_master_mono[2] = { AC97_SINGLE("Master Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1), diff -Nru a/sound/pci/ac97/ac97_local.h b/sound/pci/ac97/ac97_local.h --- a/sound/pci/ac97/ac97_local.h 2005-01-28 14:22:13 -08:00 +++ b/sound/pci/ac97/ac97_local.h 2005-01-28 14:22:13 -08:00 @@ -23,14 +23,14 @@ */ #define AC97_SINGLE_VALUE(reg,shift,mask,invert) ((reg) | ((shift) << 8) | ((shift) << 12) | ((mask) << 16) | ((invert) << 24)) -#define AC97_PAGE_SINGLE_VALUE(reg,shift,mask,invert,page) (AC97_SINGLE_VALUE(reg,shift,mask,invert) | ((page) << 25)) +#define AC97_PAGE_SINGLE_VALUE(reg,shift,mask,invert,page) (AC97_SINGLE_VALUE(reg,shift,mask,invert) | (1<<25) | ((page) << 26)) #define AC97_SINGLE(xname, reg, shift, mask, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_info_volsw, \ .get = snd_ac97_get_volsw, .put = snd_ac97_put_volsw, \ .private_value = AC97_SINGLE_VALUE(reg, shift, mask, invert) } #define AC97_PAGE_SINGLE(xname, reg, shift, mask, invert, page) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_info_volsw, \ - .get = snd_ac97_page_get_volsw, .put = snd_ac97_page_put_volsw, \ + .get = snd_ac97_get_volsw, .put = snd_ac97_put_volsw, \ .private_value = AC97_PAGE_SINGLE_VALUE(reg, shift, mask, invert, page) } /* ac97_codec.c */ @@ -42,8 +42,6 @@ int snd_ac97_info_volsw(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo); int snd_ac97_get_volsw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); int snd_ac97_put_volsw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); -int snd_ac97_page_get_volsw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); -int snd_ac97_page_put_volsw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); int snd_ac97_try_bit(ac97_t * ac97, int reg, int bit); int snd_ac97_remove_ctl(ac97_t *ac97, const char *name, const char *suffix); int snd_ac97_rename_ctl(ac97_t *ac97, const char *src, const char *dst, const char *suffix); diff -Nru a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c --- a/sound/pci/ac97/ac97_patch.c 2005-01-28 14:22:13 -08:00 +++ b/sound/pci/ac97/ac97_patch.c 2005-01-28 14:22:13 -08:00 @@ -1107,13 +1107,26 @@ #endif }; +static void check_ad1981_hp_jack_sense(ac97_t *ac97) +{ + u32 subid = ((u32)ac97->subsystem_vendor << 16) | ac97->subsystem_device; + switch (subid) { + case 0x103c0890: /* HP nc6000 */ + case 0x103c006d: /* HP nx9105 */ + case 0x17340088: /* FSC Scenic-W */ + /* enable headphone jack sense */ + snd_ac97_update_bits(ac97, AC97_AD_JACK_SPDIF, 1<<11, 1<<11); + break; + } +} + int patch_ad1981a(ac97_t *ac97) { patch_ad1881(ac97); ac97->build_ops = &patch_ad1981a_build_ops; snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD198X_MSPLT, AC97_AD198X_MSPLT); ac97->flags |= AC97_STEREO_MUTES; - snd_ac97_update_bits(ac97, AC97_AD_JACK_SPDIF, 1<<11, 1<<11); /* HP jack sense */ + check_ad1981_hp_jack_sense(ac97); return 0; } @@ -1144,7 +1157,7 @@ ac97->build_ops = &patch_ad1981b_build_ops; snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD198X_MSPLT, AC97_AD198X_MSPLT); ac97->flags |= AC97_STEREO_MUTES; - snd_ac97_update_bits(ac97, AC97_AD_JACK_SPDIF, 1<<11, 1<<11); /* HP jack sense */ + check_ad1981_hp_jack_sense(ac97); return 0; } @@ -1903,6 +1916,15 @@ /* FIXME: set up GPIO */ snd_ac97_write_cache(ac97, 0x70, 0x0100); snd_ac97_write_cache(ac97, 0x72, 0x0020); + /* Special exception for ASUS W1000/CMI9739. It does not have an SPDIF in. */ + if (ac97->pci && + ac97->subsystem_vendor == 0x1043 && + ac97->subsystem_device == 0x1843) { + snd_ac97_write_cache(ac97, AC97_CM9739_SPDIF_CTRL, + snd_ac97_read(ac97, AC97_CM9739_SPDIF_CTRL) & ~0x01); + snd_ac97_write_cache(ac97, AC97_CM9739_MULTI_CHAN, + snd_ac97_read(ac97, AC97_CM9739_MULTI_CHAN) | (1 << 14)); + } return 0; } diff -Nru a/sound/pci/atiixp.c b/sound/pci/atiixp.c --- a/sound/pci/atiixp.c 2005-01-28 14:22:13 -08:00 +++ b/sound/pci/atiixp.c 2005-01-28 14:22:13 -08:00 @@ -1332,6 +1332,12 @@ */ static struct ac97_quirk ac97_quirks[] __devinitdata = { + { + .vendor = 0x103c, + .device = 0x006b, + .name = "HP Pavilion ZV5030US", + .type = AC97_TUNE_MUTE_LED + }, { } /* terminator */ }; diff -Nru a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c --- a/sound/pci/atiixp_modem.c 2005-01-28 14:22:13 -08:00 +++ b/sound/pci/atiixp_modem.c 2005-01-28 14:22:13 -08:00 @@ -606,21 +606,20 @@ snd_pcm_runtime_t *runtime = substream->runtime; atiixp_dma_t *dma = (atiixp_dma_t *)runtime->private_data; unsigned int curptr; + int timeout = 1000; - spin_lock(&chip->reg_lock); - curptr = readl(chip->remap_addr + dma->ops->dt_cur); - if (curptr < dma->buf_addr) { - snd_printdd("curptr = %x, base = %x\n", curptr, dma->buf_addr); - curptr = 0; - } else { + while (timeout--) { + curptr = readl(chip->remap_addr + dma->ops->dt_cur); + if (curptr < dma->buf_addr) + continue; curptr -= dma->buf_addr; - if (curptr >= dma->buf_bytes) { - snd_printdd("curptr = %x, size = %x\n", curptr, dma->buf_bytes); - curptr = 0; - } + if (curptr >= dma->buf_bytes) + continue; + return bytes_to_frames(runtime, curptr); } - spin_unlock(&chip->reg_lock); - return bytes_to_frames(runtime, curptr); + snd_printd("atiixp-modem: invalid DMA pointer read 0x%x (buf=%x)\n", + readl(chip->remap_addr + dma->ops->dt_cur), dma->buf_addr); + return 0; } /* diff -Nru a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/sound/pci/hda/Makefile 2005-01-28 14:22:13 -08:00 @@ -0,0 +1,7 @@ +snd-hda-intel-objs := hda_intel.o +snd-hda-codec-objs := hda_codec.o hda_generic.o patch_realtek.o patch_cmedia.o +ifdef CONFIG_PROC_FS +snd-hda-codec-objs += hda_proc.o +endif + +obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o snd-hda-codec.o diff -Nru a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/sound/pci/hda/hda_codec.c 2005-01-28 14:22:13 -08:00 @@ -0,0 +1,1731 @@ +/* + * Universal Interface for Intel High Definition Audio Codec + * + * Copyright (c) 2004 Takashi Iwai + * + * + * This driver 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 driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include "hda_codec.h" +#include +#include +#include "hda_local.h" + + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("Universal interface for High Definition Audio Codec"); +MODULE_LICENSE("GPL"); + + +/* + * vendor / preset table + */ + +struct hda_vendor_id { + unsigned int id; + const char *name; +}; + +/* codec vendor labels */ +static struct hda_vendor_id hda_vendor_ids[] = { + { 0x10ec, "Realtek" }, + { 0x434d, "C-Media" }, + {} /* terminator */ +}; + +/* codec presets */ +#include "hda_patch.h" + + +/** + * snd_hda_codec_read - send a command and get the response + * @codec: the HDA codec + * @nid: NID to send the command + * @direct: direct flag + * @verb: the verb to send + * @parm: the parameter for the verb + * + * Send a single command and read the corresponding response. + * + * Returns the obtained response value, or -1 for an error. + */ +unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, int direct, + unsigned int verb, unsigned int parm) +{ + unsigned int res; + down(&codec->bus->cmd_mutex); + if (! codec->bus->ops.command(codec, nid, direct, verb, parm)) + res = codec->bus->ops.get_response(codec); + else + res = (unsigned int)-1; + up(&codec->bus->cmd_mutex); + return res; +} + +/** + * snd_hda_codec_write - send a single command without waiting for response + * @codec: the HDA codec + * @nid: NID to send the command + * @direct: direct flag + * @verb: the verb to send + * @parm: the parameter for the verb + * + * Send a single command without waiting for response. + * + * Returns 0 if successful, or a negative error code. + */ +int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct, + unsigned int verb, unsigned int parm) +{ + int err; + down(&codec->bus->cmd_mutex); + err = codec->bus->ops.command(codec, nid, direct, verb, parm); + up(&codec->bus->cmd_mutex); + return err; +} + +/** + * snd_hda_sequence_write - sequence writes + * @codec: the HDA codec + * @seq: VERB array to send + * + * Send the commands sequentially from the given array. + * The array must be terminated with NID=0. + */ +void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq) +{ + for (; seq->nid; seq++) + snd_hda_codec_write(codec, seq->nid, 0, seq->verb, seq->param); +} + +/** + * snd_hda_get_sub_nodes - get the range of sub nodes + * @codec: the HDA codec + * @nid: NID to parse + * @start_id: the pointer to store the start NID + * + * Parse the NID and store the start NID of its sub-nodes. + * Returns the number of sub-nodes. + */ +int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *start_id) +{ + unsigned int parm; + + parm = snd_hda_param_read(codec, nid, AC_PAR_NODE_COUNT); + *start_id = (parm >> 16) & 0x7fff; + return (int)(parm & 0x7fff); +} + +/** + * snd_hda_get_connections - get connection list + * @codec: the HDA codec + * @nid: NID to parse + * @conn_list: connection list array + * @max_conns: max. number of connections to store + * + * Parses the connection list of the given widget and stores the list + * of NIDs. + * + * Returns the number of connections, or a negative error code. + */ +int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, + hda_nid_t *conn_list, int max_conns) +{ + unsigned int parm; + int i, j, conn_len, num_tupples, conns; + unsigned int shift, num_elems, mask; + + snd_assert(conn_list && max_conns > 0, return -EINVAL); + + parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN); + if (parm & AC_CLIST_LONG) { + /* long form */ + shift = 16; + num_elems = 2; + } else { + /* short form */ + shift = 8; + num_elems = 4; + } + conn_len = parm & AC_CLIST_LENGTH; + num_tupples = num_elems / 2; + mask = (1 << (shift-1)) - 1; + + if (! conn_len) + return 0; /* no connection */ + + if (conn_len == 1) { + /* single connection */ + parm = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_LIST, 0); + conn_list[0] = parm & mask; + return 1; + } + + /* multi connection */ + conns = 0; + for (i = 0; i < conn_len; i += num_elems) { + parm = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_LIST, i); + for (j = 0; j < num_tupples; j++) { + int range_val; + hda_nid_t val1, val2, n; + range_val = parm & (1 << (shift-1)); /* ranges */ + val1 = parm & mask; + parm >>= shift; + val2 = parm & mask; + parm >>= shift; + if (range_val) { + /* ranges between val1 and val2 */ + if (val1 > val2) { + snd_printk(KERN_WARNING "hda_codec: invalid dep_range_val %x:%x\n", val1, val2); + continue; + } + for (n = val1; n <= val2; n++) { + if (conns >= max_conns) + return -EINVAL; + conn_list[conns++] = n; + } + } else { + if (! val1) + break; + if (conns >= max_conns) + return -EINVAL; + conn_list[conns++] = val1; + if (! val2) + break; + if (conns >= max_conns) + return -EINVAL; + conn_list[conns++] = val2; + } + } + } + return conns; +} + + +/** + * snd_hda_queue_unsol_event - add an unsolicited event to queue + * @bus: the BUS + * @res: unsolicited event (lower 32bit of RIRB entry) + * @res_ex: codec addr and flags (upper 32bit or RIRB entry) + * + * Adds the given event to the queue. The events are processed in + * the workqueue asynchronously. Call this function in the interrupt + * hanlder when RIRB receives an unsolicited event. + * + * Returns 0 if successful, or a negative error code. + */ +int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex) +{ + struct hda_bus_unsolicited *unsol; + unsigned int wp; + + if ((unsol = bus->unsol) == NULL) + return 0; + + wp = (unsol->wp + 1) % HDA_UNSOL_QUEUE_SIZE; + unsol->wp = wp; + + wp <<= 1; + unsol->queue[wp] = res; + unsol->queue[wp + 1] = res_ex; + + queue_work(unsol->workq, &unsol->work); + + return 0; +} + +/* + * process queueud unsolicited events + */ +static void process_unsol_events(void *data) +{ + struct hda_bus *bus = data; + struct hda_bus_unsolicited *unsol = bus->unsol; + struct hda_codec *codec; + unsigned int rp, caddr, res; + + while (unsol->rp != unsol->wp) { + rp = (unsol->rp + 1) % HDA_UNSOL_QUEUE_SIZE; + unsol->rp = rp; + rp <<= 1; + res = unsol->queue[rp]; + caddr = unsol->queue[rp + 1]; + if (! (caddr & (1 << 4))) /* no unsolicited event? */ + continue; + codec = bus->caddr_tbl[caddr & 0x0f]; + if (codec && codec->patch_ops.unsol_event) + codec->patch_ops.unsol_event(codec, res); + } +} + +/* + * initialize unsolicited queue + */ +static int init_unsol_queue(struct hda_bus *bus) +{ + struct hda_bus_unsolicited *unsol; + + unsol = kcalloc(1, sizeof(*unsol), GFP_KERNEL); + if (! unsol) { + snd_printk(KERN_ERR "hda_codec: can't allocate unsolicited queue\n"); + return -ENOMEM; + } + unsol->workq = create_workqueue("hda_codec"); + if (! unsol->workq) { + snd_printk(KERN_ERR "hda_codec: can't create workqueue\n"); + kfree(unsol); + return -ENOMEM; + } + INIT_WORK(&unsol->work, process_unsol_events, bus); + bus->unsol = unsol; + return 0; +} + +/* + * destructor + */ +static void snd_hda_codec_free(struct hda_codec *codec); + +static int snd_hda_bus_free(struct hda_bus *bus) +{ + struct list_head *p, *n; + + if (! bus) + return 0; + if (bus->unsol) { + destroy_workqueue(bus->unsol->workq); + kfree(bus->unsol); + } + list_for_each_safe(p, n, &bus->codec_list) { + struct hda_codec *codec = list_entry(p, struct hda_codec, list); + snd_hda_codec_free(codec); + } + if (bus->ops.private_free) + bus->ops.private_free(bus); + kfree(bus); + return 0; +} + +static int snd_hda_bus_dev_free(snd_device_t *device) +{ + struct hda_bus *bus = device->device_data; + return snd_hda_bus_free(bus); +} + +/** + * snd_hda_bus_new - create a HDA bus + * @card: the card entry + * @temp: the template for hda_bus information + * @busp: the pointer to store the created bus instance + * + * Returns 0 if successful, or a negative error code. + */ +int snd_hda_bus_new(snd_card_t *card, const struct hda_bus_template *temp, + struct hda_bus **busp) +{ + struct hda_bus *bus; + int err; + static snd_device_ops_t dev_ops = { + .dev_free = snd_hda_bus_dev_free, + }; + + snd_assert(temp, return -EINVAL); + snd_assert(temp->ops.command && temp->ops.get_response, return -EINVAL); + + if (busp) + *busp = NULL; + + bus = kcalloc(1, sizeof(*bus), GFP_KERNEL); + if (bus == NULL) { + snd_printk(KERN_ERR "can't allocate struct hda_bus\n"); + return -ENOMEM; + } + + bus->card = card; + bus->private_data = temp->private_data; + bus->pci = temp->pci; + bus->modelname = temp->modelname; + bus->ops = temp->ops; + + init_MUTEX(&bus->cmd_mutex); + INIT_LIST_HEAD(&bus->codec_list); + + init_unsol_queue(bus); + + if ((err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops)) < 0) { + snd_hda_bus_free(bus); + return err; + } + if (busp) + *busp = bus; + return 0; +} + + +/* + * find a matching codec preset + */ +static const struct hda_codec_preset *find_codec_preset(struct hda_codec *codec) +{ + const struct hda_codec_preset **tbl, *preset; + + for (tbl = hda_preset_tables; *tbl; tbl++) { + for (preset = *tbl; preset->id; preset++) { + u32 mask = preset->mask; + if (! mask) + mask = ~0; + if (preset->id == (codec->vendor_id & mask)) + return preset; + } + } + return NULL; +} + +/* + * snd_hda_get_codec_name - store the codec name + */ +void snd_hda_get_codec_name(struct hda_codec *codec, + char *name, int namelen) +{ + const struct hda_vendor_id *c; + const char *vendor = NULL; + u16 vendor_id = codec->vendor_id >> 16; + char tmp[16]; + + for (c = hda_vendor_ids; c->id; c++) { + if (c->id == vendor_id) { + vendor = c->name; + break; + } + } + if (! vendor) { + sprintf(tmp, "Generic %04x", vendor_id); + vendor = tmp; + } + if (codec->preset && codec->preset->name) + snprintf(name, namelen, "%s %s", vendor, codec->preset->name); + else + snprintf(name, namelen, "%s ID %x", vendor, codec->vendor_id & 0xffff); +} + +/* + * look for an AFG node + * + * return 0 if not found + */ +static int look_for_afg_node(struct hda_codec *codec) +{ + int i, total_nodes; + hda_nid_t nid; + + total_nodes = snd_hda_get_sub_nodes(codec, AC_NODE_ROOT, &nid); + for (i = 0; i < total_nodes; i++, nid++) { + if (snd_hda_param_read(codec, nid, AC_PAR_FUNCTION_TYPE) == AC_GRP_AUDIO_FUNCTION) + return nid; + } + return 0; +} + +/* + * codec destructor + */ +static void snd_hda_codec_free(struct hda_codec *codec) +{ + if (! codec) + return; + list_del(&codec->list); + codec->bus->caddr_tbl[codec->addr] = NULL; + if (codec->patch_ops.free) + codec->patch_ops.free(codec); + kfree(codec); +} + +static void init_amp_hash(struct hda_codec *codec); + +/** + * snd_hda_codec_new - create a HDA codec + * @bus: the bus to assign + * @codec_addr: the codec address + * @codecp: the pointer to store the generated codec + * + * Returns 0 if successful, or a negative error code. + */ +int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, + struct hda_codec **codecp) +{ + struct hda_codec *codec; + char component[13]; + int err; + + snd_assert(bus, return -EINVAL); + snd_assert(codec_addr <= HDA_MAX_CODEC_ADDRESS, return -EINVAL); + + if (bus->caddr_tbl[codec_addr]) { + snd_printk(KERN_ERR "hda_codec: address 0x%x is already occupied\n", codec_addr); + return -EBUSY; + } + + codec = kcalloc(1, sizeof(*codec), GFP_KERNEL); + if (codec == NULL) { + snd_printk(KERN_ERR "can't allocate struct hda_codec\n"); + return -ENOMEM; + } + + codec->bus = bus; + codec->addr = codec_addr; + init_MUTEX(&codec->spdif_mutex); + init_amp_hash(codec); + + list_add_tail(&codec->list, &bus->codec_list); + bus->caddr_tbl[codec_addr] = codec; + + codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT, AC_PAR_VENDOR_ID); + codec->subsystem_id = snd_hda_param_read(codec, AC_NODE_ROOT, AC_PAR_SUBSYSTEM_ID); + codec->revision_id = snd_hda_param_read(codec, AC_NODE_ROOT, AC_PAR_REV_ID); + + /* FIXME: support for multiple AFGs? */ + codec->afg = look_for_afg_node(codec); + if (! codec->afg) { + snd_printk(KERN_ERR "hda_codec: no AFG node found\n"); + snd_hda_codec_free(codec); + return -ENODEV; + } + + codec->preset = find_codec_preset(codec); + if (! *bus->card->mixername) + snd_hda_get_codec_name(codec, bus->card->mixername, + sizeof(bus->card->mixername)); + + if (codec->preset && codec->preset->patch) + err = codec->preset->patch(codec); + else + err = snd_hda_parse_generic_codec(codec); + if (err < 0) { + snd_hda_codec_free(codec); + return err; + } + + snd_hda_codec_proc_new(codec); + + sprintf(component, "HDA:%08x", codec->vendor_id); + snd_component_add(codec->bus->card, component); + + if (codecp) + *codecp = codec; + return 0; +} + +/** + * snd_hda_codec_setup_stream - set up the codec for streaming + * @codec: the CODEC to set up + * @nid: the NID to set up + * @stream_tag: stream tag to pass, it's between 0x1 and 0xf. + * @channel_id: channel id to pass, zero based. + * @format: stream format. + */ +void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stream_tag, + int channel_id, int format) +{ + snd_printdd("hda_codec_setup_stream: NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n", + nid, stream_tag, channel_id, format); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, + (stream_tag << 4) | channel_id); + msleep(1); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format); +} + + +/* + * amp access functions + */ + +#define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + (idx) * 32 + (dir) * 64) +#define INFO_AMP_CAPS (1<<0) +#define INFO_AMP_VOL (1<<1) + +/* initialize the hash table */ +static void init_amp_hash(struct hda_codec *codec) +{ + memset(codec->amp_hash, 0xff, sizeof(codec->amp_hash)); + codec->num_amp_entries = 0; +} + +/* query the hash. allocate an entry if not found. */ +static struct hda_amp_info *get_alloc_amp_hash(struct hda_codec *codec, u32 key) +{ + u16 idx = key % (u16)ARRAY_SIZE(codec->amp_hash); + u16 cur = codec->amp_hash[idx]; + struct hda_amp_info *info; + + while (cur != 0xffff) { + info = &codec->amp_info[cur]; + if (info->key == key) + return info; + cur = info->next; + } + + /* add a new hash entry */ + if (codec->num_amp_entries >= ARRAY_SIZE(codec->amp_info)) { + snd_printk(KERN_ERR "hda_codec: Tooooo many amps!\n"); + return NULL; + } + cur = codec->num_amp_entries++; + info = &codec->amp_info[cur]; + info->key = key; + info->status = 0; /* not initialized yet */ + info->next = codec->amp_hash[idx]; + codec->amp_hash[idx] = cur; + + return info; +} + +/* + * query AMP capabilities for the given widget and direction + */ +static u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) +{ + struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, 0)); + + if (! info) + return 0; + if (! (info->status & INFO_AMP_CAPS)) { + info->amp_caps = snd_hda_param_read(codec, nid, direction == HDA_OUTPUT ? + AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); + info->status |= INFO_AMP_CAPS; + } + return info->amp_caps; +} + +/* + * read the current volume to info + * if the cache exists, read from the cache. + */ +static void get_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, + hda_nid_t nid, int ch, int direction, int index) +{ + u32 val, parm; + + if (info->status & (INFO_AMP_VOL << ch)) + return; + + parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT; + parm |= direction == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT; + parm |= index; + val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, parm); + info->vol[ch] = val & 0xff; + info->status |= INFO_AMP_VOL << ch; +} + +/* + * write the current volume in info to the h/w + */ +static void put_vol_mute(struct hda_codec *codec, + hda_nid_t nid, int ch, int direction, int index, int val) +{ + u32 parm; + + parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT; + parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT; + parm |= index << AC_AMP_SET_INDEX_SHIFT; + parm |= val; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm); +} + +/* + * read/write 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 direction, int index) +{ + struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index)); + if (! info) + return 0; + get_vol_mute(codec, info, nid, ch, direction, index); + return info->vol[ch]; +} + +int snd_hda_codec_amp_write(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int val) +{ + struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx)); + if (! info) + return 0; + get_vol_mute(codec, info, nid, ch, direction, idx); + if (info->vol[ch] == val && ! codec->in_resume) + return 0; + put_vol_mute(codec, nid, ch, direction, idx, val); + info->vol[ch] = val; + return 1; +} + + +/* + * AMP control callbacks + */ +/* retrieve parameters from private_value */ +#define get_amp_nid(kc) ((kc)->private_value & 0xffff) +#define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3) +#define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1) +#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf) + +/* volume */ +int snd_hda_mixer_amp_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + u16 nid = get_amp_nid(kcontrol); + u8 chs = get_amp_channels(kcontrol); + int dir = get_amp_direction(kcontrol); + u32 caps; + + caps = query_amp_caps(codec, nid, dir); + caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; /* num steps */ + if (! caps) { + printk(KERN_WARNING "hda_codec: num_steps = 0 for NID=0x%x\n", nid); + return -EINVAL; + } + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = chs == 3 ? 2 : 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = caps; + return 0; +} + +int snd_hda_mixer_amp_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = get_amp_nid(kcontrol); + int chs = get_amp_channels(kcontrol); + int dir = get_amp_direction(kcontrol); + int idx = get_amp_index(kcontrol); + long *valp = ucontrol->value.integer.value; + + if (chs & 1) + *valp++ = snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & 0x7f; + if (chs & 2) + *valp = snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 0x7f; + return 0; +} + +int snd_hda_mixer_amp_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = get_amp_nid(kcontrol); + int chs = get_amp_channels(kcontrol); + int dir = get_amp_direction(kcontrol); + int idx = get_amp_index(kcontrol); + int val; + long *valp = ucontrol->value.integer.value; + int change = 0; + + if (chs & 1) { + val = *valp & 0x7f; + val |= snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & 0x80; + change = snd_hda_codec_amp_write(codec, nid, 0, dir, idx, val); + valp++; + } + if (chs & 2) { + val = *valp & 0x7f; + val |= snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 0x80; + change |= snd_hda_codec_amp_write(codec, nid, 1, dir, idx, val); + } + return change; +} + +/* switch */ +int snd_hda_mixer_amp_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + int chs = get_amp_channels(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = chs == 3 ? 2 : 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +int snd_hda_mixer_amp_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = get_amp_nid(kcontrol); + int chs = get_amp_channels(kcontrol); + int dir = get_amp_direction(kcontrol); + int idx = get_amp_index(kcontrol); + long *valp = ucontrol->value.integer.value; + + if (chs & 1) + *valp++ = (snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & 0x80) ? 0 : 1; + if (chs & 2) + *valp = (snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 0x80) ? 0 : 1; + return 0; +} + +int snd_hda_mixer_amp_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = get_amp_nid(kcontrol); + int chs = get_amp_channels(kcontrol); + int dir = get_amp_direction(kcontrol); + int idx = get_amp_index(kcontrol); + int val; + long *valp = ucontrol->value.integer.value; + int change = 0; + + if (chs & 1) { + val = snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & 0x7f; + val |= *valp ? 0 : 0x80; + change = snd_hda_codec_amp_write(codec, nid, 0, dir, idx, val); + valp++; + } + if (chs & 2) { + val = snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 0x7f; + val |= *valp ? 0 : 0x80; + change = snd_hda_codec_amp_write(codec, nid, 1, dir, idx, val); + } + return change; +} + +/* + * SPDIF out controls + */ + +static int snd_hda_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_hda_spdif_cmask_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL | + IEC958_AES0_NONAUDIO | + IEC958_AES0_CON_EMPHASIS_5015 | + IEC958_AES0_CON_NOT_COPYRIGHT; + ucontrol->value.iec958.status[1] = IEC958_AES1_CON_CATEGORY | + IEC958_AES1_CON_ORIGINAL; + return 0; +} + +static int snd_hda_spdif_pmask_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL | + IEC958_AES0_NONAUDIO | + IEC958_AES0_PRO_EMPHASIS_5015; + return 0; +} + +static int snd_hda_spdif_default_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + + ucontrol->value.iec958.status[0] = codec->spdif_status & 0xff; + ucontrol->value.iec958.status[1] = (codec->spdif_status >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (codec->spdif_status >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (codec->spdif_status >> 24) & 0xff; + + return 0; +} + +static int snd_hda_spdif_default_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value; + unsigned int sbits; + unsigned short val; + int change; + + val = 0; + down(&codec->spdif_mutex); + sbits = codec->spdif_status & 1; + sbits |= ucontrol->value.iec958.status[0] & (IEC958_AES0_PROFESSIONAL| + IEC958_AES0_NONAUDIO); + if (sbits & IEC958_AES0_PROFESSIONAL) + val = 1 << 6; + if (sbits & IEC958_AES0_NONAUDIO) + val |= 1 << 5; + if (sbits & IEC958_AES0_PROFESSIONAL) { + sbits |= ucontrol->value.iec958.status[0] & IEC958_AES0_PRO_EMPHASIS; + if ((sbits & IEC958_AES0_PRO_EMPHASIS) == IEC958_AES0_PRO_EMPHASIS_5015) + val |= 1 << 3; + } else { + sbits |= ucontrol->value.iec958.status[0] & (IEC958_AES0_CON_EMPHASIS| + IEC958_AES0_CON_NOT_COPYRIGHT); + if ((sbits & IEC958_AES0_CON_EMPHASIS) == IEC958_AES0_CON_EMPHASIS_5015) + val |= 1 << 3; + if (! (sbits & IEC958_AES0_CON_NOT_COPYRIGHT)) + val |= 1 << 4; + sbits |= ucontrol->value.iec958.status[1] << 8; + if (sbits & (IEC958_AES1_CON_ORIGINAL << 8)) + val |= 1 << 7; + val |= sbits & (IEC958_AES1_CON_CATEGORY << 8); + } + + change = codec->spdif_status != sbits; + codec->spdif_status = sbits; + + if (change) { + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, val & 0xff); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_2, val >> 8); + } + + up(&codec->spdif_mutex); + return change; +} + +static int snd_hda_spdif_out_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_hda_spdif_out_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = codec->spdif_status & 1; + return 0; +} + +static int snd_hda_spdif_out_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value; + unsigned int sbits; + int change; + + down(&codec->spdif_mutex); + sbits = codec->spdif_status & ~1; + if (ucontrol->value.integer.value[0]) + sbits |= 1; + change = codec->spdif_status != sbits; + if (change) { + codec->spdif_status = sbits; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, sbits & 0xff); + } + up(&codec->spdif_mutex); + return change; +} + +static snd_kcontrol_new_t dig_mixes[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + .info = snd_hda_spdif_mask_info, + .get = snd_hda_spdif_cmask_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), + .info = snd_hda_spdif_mask_info, + .get = snd_hda_spdif_pmask_get, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + .info = snd_hda_spdif_mask_info, + .get = snd_hda_spdif_default_get, + .put = snd_hda_spdif_default_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), + .info = snd_hda_spdif_out_switch_info, + .get = snd_hda_spdif_out_switch_get, + .put = snd_hda_spdif_out_switch_put, + }, + { } /* end */ +}; + +/** + * snd_hda_create_spdif_out_ctls - create SPDIF-related controls + * @codec: the HDA codec + * @nid: audio out widget NID + * + * Creates controls related with the SPDIF output. + * Called from each patch supporting the SPDIF out. + * + * Returns 0 if successful, or a negative error code. + */ +int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) +{ + int err; + snd_kcontrol_t *kctl; + snd_kcontrol_new_t *dig_mix; + + for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) { + kctl = snd_ctl_new1(dig_mix, codec); + kctl->private_value = nid; + if ((err = snd_ctl_add(codec->bus->card, kctl)) < 0) + return err; + } +#if 0 + /* not enabled, consumer, audio, no emphasis, original, PCM coder */ + codec->spdif_status = (1 << 7) | (0x02 << 8); +#else + codec->spdif_status = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0); +#endif + return 0; +} + +/** + * snd_hda_build_controls - build mixer controls + * @bus: the BUS + * + * Creates mixer controls for each codec included in the bus. + * + * Returns 0 if successful, otherwise a negative error code. + */ +int snd_hda_build_controls(struct hda_bus *bus) +{ + struct list_head *p; + + /* build controls */ + list_for_each(p, &bus->codec_list) { + struct hda_codec *codec = list_entry(p, struct hda_codec, list); + int err; + if (! codec->patch_ops.build_controls) + continue; + err = codec->patch_ops.build_controls(codec); + if (err < 0) + return err; + } + + /* initialize */ + list_for_each(p, &bus->codec_list) { + struct hda_codec *codec = list_entry(p, struct hda_codec, list); + int err; + if (! codec->patch_ops.init) + continue; + err = codec->patch_ops.init(codec); + if (err < 0) + return err; + } + return 0; +} + + +/* + * stream formats + */ +static unsigned int rate_bits[][3] = { + /* rate in Hz, ALSA rate bitmask, HDA format value */ + { 8000, SNDRV_PCM_RATE_8000, 0x0500 }, /* 1/6 x 48 */ + { 11025, SNDRV_PCM_RATE_11025, 0x4300 }, /* 1/4 x 44 */ + { 16000, SNDRV_PCM_RATE_16000, 0x0200 }, /* 1/3 x 48 */ + { 22050, SNDRV_PCM_RATE_22050, 0x4100 }, /* 1/2 x 44 */ + { 32000, SNDRV_PCM_RATE_32000, 0x0a00 }, /* 2/3 x 48 */ + { 44100, SNDRV_PCM_RATE_44100, 0x4000 }, /* 44 */ + { 48000, SNDRV_PCM_RATE_48000, 0x0000 }, /* 48 */ + { 88200, SNDRV_PCM_RATE_88200, 0x4800 }, /* 2 x 44 */ + { 96000, SNDRV_PCM_RATE_96000, 0x0800 }, /* 2 x 48 */ + { 176400, SNDRV_PCM_RATE_176400, 0x5800 },/* 4 x 44 */ + { 192000, SNDRV_PCM_RATE_192000, 0x1800 }, /* 4 x 48 */ + { 0 } +}; + +/** + * snd_hda_calc_stream_format - calculate format bitset + * @rate: the sample rate + * @channels: the number of channels + * @format: the PCM format (SNDRV_PCM_FORMAT_XXX) + * @maxbps: the max. bps + * + * Calculate the format bitset from the given rate, channels and th PCM format. + * + * Return zero if invalid. + */ +unsigned int snd_hda_calc_stream_format(unsigned int rate, + unsigned int channels, + unsigned int format, + unsigned int maxbps) +{ + int i; + unsigned int val = 0; + + for (i = 0; rate_bits[i][0]; i++) + if (rate_bits[i][0] == rate) { + val = rate_bits[i][2]; + break; + } + if (! rate_bits[i][0]) { + snd_printdd("invalid rate %d\n", rate); + return 0; + } + + if (channels == 0 || channels > 8) { + snd_printdd("invalid channels %d\n", channels); + return 0; + } + val |= channels - 1; + + switch (snd_pcm_format_width(format)) { + case 8: val |= 0x00; break; + case 16: val |= 0x10; break; + case 20: + case 24: + case 32: + if (maxbps >= 32) + val |= 0x40; + else if (maxbps >= 24) + val |= 0x30; + else + val |= 0x20; + break; + default: + snd_printdd("invalid format width %d\n", snd_pcm_format_width(format)); + return 0; + } + + return val; +} + +/** + * snd_hda_query_supported_pcm - query the supported PCM rates and formats + * @codec: the HDA codec + * @nid: NID to query + * @ratesp: the pointer to store the detected rate bitflags + * @formatsp: the pointer to store the detected formats + * @bpsp: the pointer to store the detected format widths + * + * Queries the supported PCM rates and formats. The NULL @ratesp, @formatsp + * or @bsps argument is ignored. + * + * Returns 0 if successful, otherwise a negative error code. + */ +int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, + u32 *ratesp, u64 *formatsp, unsigned int *bpsp) +{ + int i; + unsigned int val, streams; + + val = 0; + if (nid != codec->afg && + snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) & AC_WCAP_FORMAT_OVRD) { + val = snd_hda_param_read(codec, nid, AC_PAR_PCM); + if (val == -1) + return -EIO; + } + if (! val) + val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM); + + if (ratesp) { + u32 rates = 0; + for (i = 0; rate_bits[i][0]; i++) { + if (val & (1 << i)) + rates |= rate_bits[i][1]; + } + *ratesp = rates; + } + + if (formatsp || bpsp) { + u64 formats = 0; + unsigned int bps; + unsigned int wcaps; + + wcaps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); + streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM); + if (streams == -1) + return -EIO; + if (! streams) { + streams = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM); + if (streams == -1) + return -EIO; + } + + bps = 0; + if (streams & AC_SUPFMT_PCM) { + if (val & AC_SUPPCM_BITS_8) { + formats |= SNDRV_PCM_FMTBIT_U8; + bps = 8; + } + if (val & AC_SUPPCM_BITS_16) { + formats |= SNDRV_PCM_FMTBIT_S16_LE; + bps = 16; + } + if (wcaps & AC_WCAP_DIGITAL) { + if (val & AC_SUPPCM_BITS_32) + formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE; + if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24)) + formats |= SNDRV_PCM_FMTBIT_S32_LE; + if (val & AC_SUPPCM_BITS_24) + bps = 24; + else if (val & AC_SUPPCM_BITS_20) + bps = 20; + } else if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24|AC_SUPPCM_BITS_32)) { + formats |= SNDRV_PCM_FMTBIT_S32_LE; + if (val & AC_SUPPCM_BITS_32) + bps = 32; + else if (val & AC_SUPPCM_BITS_20) + bps = 20; + else if (val & AC_SUPPCM_BITS_24) + bps = 24; + } + } + else if (streams == AC_SUPFMT_FLOAT32) { /* should be exclusive */ + formats |= SNDRV_PCM_FMTBIT_FLOAT_LE; + bps = 32; + } else if (streams == AC_SUPFMT_AC3) { /* should be exclusive */ + /* temporary hack: we have still no proper support + * for the direct AC3 stream... + */ + formats |= SNDRV_PCM_FMTBIT_U8; + bps = 8; + } + if (formatsp) + *formatsp = formats; + if (bpsp) + *bpsp = bps; + } + + return 0; +} + +/** + * snd_hda_is_supported_format - check whether the given node supports the format val + * + * Returns 1 if supported, 0 if not. + */ +int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, + unsigned int format) +{ + int i; + unsigned int val = 0, rate; + + if (nid != codec->afg && + snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) & AC_WCAP_FORMAT_OVRD) { + val = snd_hda_param_read(codec, nid, AC_PAR_PCM); + if (val == -1) + return 0; + } + if (! val) { + val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM); + if (val == -1) + return 0; + } + + rate = format & 0xffff; + for (i = 0; rate_bits[i][0]; i++) + if (rate_bits[i][2] == rate) { + if (val & (1 << i)) + break; + return 0; + } + if (! rate_bits[i][0]) + return 0; + + val = snd_hda_param_read(codec, nid, AC_PAR_STREAM); + if (val == -1) + return 0; + if (! val && nid != codec->afg) + val = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM); + if (! val || val == -1) + return 0; + + if (val & AC_SUPFMT_PCM) { + switch (format & 0xf0) { + case 0x00: + if (! (val & AC_SUPPCM_BITS_8)) + return 0; + break; + case 0x10: + if (! (val & AC_SUPPCM_BITS_16)) + return 0; + break; + case 0x20: + if (! (val & AC_SUPPCM_BITS_20)) + return 0; + break; + case 0x30: + if (! (val & AC_SUPPCM_BITS_24)) + return 0; + break; + case 0x40: + if (! (val & AC_SUPPCM_BITS_32)) + return 0; + break; + default: + return 0; + } + } else { + /* FIXME: check for float32 and AC3? */ + } + + return 1; +} + +/* + * PCM stuff + */ +static int hda_pcm_default_open_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + snd_pcm_substream_t *substream) +{ + return 0; +} + +static int hda_pcm_default_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + snd_pcm_substream_t *substream) +{ + snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); + return 0; +} + +static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + snd_pcm_substream_t *substream) +{ + snd_hda_codec_setup_stream(codec, hinfo->nid, 0, 0, 0); + return 0; +} + +static int set_pcm_default_values(struct hda_codec *codec, struct hda_pcm_stream *info) +{ + if (info->nid) { + /* query support PCM information from the given NID */ + if (! info->rates || ! info->formats) + snd_hda_query_supported_pcm(codec, info->nid, + info->rates ? NULL : &info->rates, + info->formats ? NULL : &info->formats, + info->maxbps ? NULL : &info->maxbps); + } + if (info->ops.open == NULL) + info->ops.open = hda_pcm_default_open_close; + if (info->ops.close == NULL) + info->ops.close = hda_pcm_default_open_close; + if (info->ops.prepare == NULL) { + snd_assert(info->nid, return -EINVAL); + info->ops.prepare = hda_pcm_default_prepare; + } + if (info->ops.prepare == NULL) { + snd_assert(info->nid, return -EINVAL); + info->ops.prepare = hda_pcm_default_prepare; + } + if (info->ops.cleanup == NULL) { + snd_assert(info->nid, return -EINVAL); + info->ops.cleanup = hda_pcm_default_cleanup; + } + return 0; +} + +/** + * snd_hda_build_pcms - build PCM information + * @bus: the BUS + * + * Create PCM information for each codec included in the bus. + * + * The build_pcms codec patch is requested to set up codec->num_pcms and + * codec->pcm_info properly. The array is referred by the top-level driver + * to create its PCM instances. + * The allocated codec->pcm_info should be released in codec->patch_ops.free + * callback. + * + * At least, substreams, channels_min and channels_max must be filled for + * each stream. substreams = 0 indicates that the stream doesn't exist. + * When rates and/or formats are zero, the supported values are queried + * from the given nid. The nid is used also by the default ops.prepare + * and ops.cleanup callbacks. + * + * The driver needs to call ops.open in its open callback. Similarly, + * ops.close is supposed to be called in the close callback. + * ops.prepare should be called in the prepare or hw_params callback + * with the proper parameters for set up. + * ops.cleanup should be called in hw_free for clean up of streams. + * + * This function returns 0 if successfull, or a negative error code. + */ +int snd_hda_build_pcms(struct hda_bus *bus) +{ + struct list_head *p; + + list_for_each(p, &bus->codec_list) { + struct hda_codec *codec = list_entry(p, struct hda_codec, list); + unsigned int pcm, s; + int err; + if (! codec->patch_ops.build_pcms) + continue; + err = codec->patch_ops.build_pcms(codec); + if (err < 0) + return err; + for (pcm = 0; pcm < codec->num_pcms; pcm++) { + for (s = 0; s < 2; s++) { + struct hda_pcm_stream *info; + info = &codec->pcm_info[pcm].stream[s]; + if (! info->substreams) + continue; + err = set_pcm_default_values(codec, info); + if (err < 0) + return err; + } + } + } + return 0; +} + + +/** + * snd_hda_check_board_config - compare the current codec with the config table + * @codec: the HDA codec + * @tbl: configuration table, terminated by null entries + * + * Compares the modelname or PCI subsystem id of the current codec with the + * given configuration table. If a matching entry is found, returns its + * config value (supposed to be 0 or positive). + * + * If no entries are matching, the function returns a negative value. + */ +int snd_hda_check_board_config(struct hda_codec *codec, struct hda_board_config *tbl) +{ + struct hda_board_config *c; + + if (codec->bus->modelname) { + for (c = tbl; c->modelname || c->pci_vendor; c++) { + if (c->modelname && + ! strcmp(codec->bus->modelname, c->modelname)) { + snd_printd(KERN_INFO "hda_codec: model '%s' is selected\n", c->modelname); + return c->config; + } + } + } + + if (codec->bus->pci) { + u16 subsystem_vendor, subsystem_device; + pci_read_config_word(codec->bus->pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor); + pci_read_config_word(codec->bus->pci, PCI_SUBSYSTEM_ID, &subsystem_device); + for (c = tbl; c->modelname || c->pci_vendor; c++) { + if (c->pci_vendor == subsystem_vendor && + c->pci_device == subsystem_device) + return c->config; + } + } + return -1; +} + +/** + * snd_hda_add_new_ctls - create controls from the array + * @codec: the HDA codec + * @knew: the array of snd_kcontrol_new_t + * + * This helper function creates and add new controls in the given array. + * The array must be terminated with an empty entry as terminator. + * + * Returns 0 if successful, or a negative error code. + */ +int snd_hda_add_new_ctls(struct hda_codec *codec, snd_kcontrol_new_t *knew) +{ + int err; + + for (; knew->name; knew++) { + err = snd_ctl_add(codec->bus->card, snd_ctl_new1(knew, codec)); + if (err < 0) + return err; + } + return 0; +} + + +/* + * input MUX helper + */ +int snd_hda_input_mux_info(const struct hda_input_mux *imux, snd_ctl_elem_info_t *uinfo) +{ + unsigned int index; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = imux->num_items; + index = uinfo->value.enumerated.item; + if (index >= imux->num_items) + index = imux->num_items - 1; + strcpy(uinfo->value.enumerated.name, imux->items[index].label); + return 0; +} + +int snd_hda_input_mux_put(struct hda_codec *codec, const struct hda_input_mux *imux, + snd_ctl_elem_value_t *ucontrol, hda_nid_t nid, + unsigned int *cur_val) +{ + unsigned int idx; + + idx = ucontrol->value.enumerated.item[0]; + if (idx >= imux->num_items) + idx = imux->num_items - 1; + if (*cur_val == idx && ! codec->in_resume) + return 0; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, + imux->items[idx].index); + *cur_val = idx; + return 1; +} + + +/* + * Multi-channel / digital-out PCM helper functions + */ + +/* + * open the digital out in the exclusive mode + */ +int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mout) +{ + down(&codec->spdif_mutex); + if (mout->dig_out_used) { + up(&codec->spdif_mutex); + return -EBUSY; /* already being used */ + } + mout->dig_out_used = HDA_DIG_EXCLUSIVE; + up(&codec->spdif_mutex); + return 0; +} + +/* + * release the digital out + */ +int snd_hda_multi_out_dig_close(struct hda_codec *codec, struct hda_multi_out *mout) +{ + down(&codec->spdif_mutex); + mout->dig_out_used = 0; + up(&codec->spdif_mutex); + return 0; +} + +/* + * set up more restrictions for analog out + */ +int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_multi_out *mout, + snd_pcm_substream_t *substream) +{ + substream->runtime->hw.channels_max = mout->max_channels; + return snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, 2); +} + +/* + * 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, struct hda_multi_out *mout, + unsigned int stream_tag, + unsigned int format, + snd_pcm_substream_t *substream) +{ + hda_nid_t *nids = mout->dac_nids; + int chs = substream->runtime->channels; + int i; + + down(&codec->spdif_mutex); + if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { + if (chs == 2 && + snd_hda_is_supported_format(codec, mout->dig_out_nid, format)) { + + mout->dig_out_used = HDA_DIG_ANALOG_DUP; + /* setup digital receiver */ + snd_hda_codec_setup_stream(codec, mout->dig_out_nid, + stream_tag, 0, format); + + if (codec->spdif_status & AC_DIG1_NONAUDIO) { + /* non-audio SPDIF out, turning off all DACs */ + for (i = 0; i < mout->num_dacs; i++) + snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); + if (mout->hp_nid) + snd_hda_codec_setup_stream(codec, mout->hp_nid,0, 0, 0); + up(&codec->spdif_mutex); + return 0; + } + } else { + mout->dig_out_used = 0; + snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0); + } + } + up(&codec->spdif_mutex); + + /* front */ + snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 0, format); + if (mout->hp_nid) + /* headphone out will just decode front left/right (stereo) */ + snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format); + /* surrounds */ + for (i = 0; i < mout->num_dacs; i++) { + if (i == HDA_REAR && chs == 2) /* copy front to rear */ + snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 0, format); + else if (chs >= (i + 1) * 2) /* independent out */ + snd_hda_codec_setup_stream(codec, nids[i], stream_tag, i * 2, + format); + } + return 0; +} + +/* + * clean up the setting for analog out + */ +int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, struct hda_multi_out *mout) +{ + hda_nid_t *nids = mout->dac_nids; + int i; + + for (i = 0; i < mout->num_dacs; i++) + snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); + if (mout->hp_nid) + snd_hda_codec_setup_stream(codec, mout->hp_nid, 0, 0, 0); + down(&codec->spdif_mutex); + if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) { + snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0); + mout->dig_out_used = 0; + } + up(&codec->spdif_mutex); + return 0; +} + +#ifdef CONFIG_PM +/* + * power management + */ + +/** + * snd_hda_suspend - suspend the codecs + * @bus: the HDA bus + * @state: suspsend state + * + * Returns 0 if successful. + */ +int snd_hda_suspend(struct hda_bus *bus, unsigned int state) +{ + struct list_head *p; + + /* FIXME: should handle power widget capabilities */ + list_for_each(p, &bus->codec_list) { + struct hda_codec *codec = list_entry(p, struct hda_codec, list); + if (codec->patch_ops.suspend) + codec->patch_ops.suspend(codec, state); + } + return 0; +} + +/** + * snd_hda_resume - resume the codecs + * @bus: the HDA bus + * @state: resume state + * + * Returns 0 if successful. + */ +int snd_hda_resume(struct hda_bus *bus, unsigned int state) +{ + struct list_head *p; + + list_for_each(p, &bus->codec_list) { + struct hda_codec *codec = list_entry(p, struct hda_codec, list); + if (codec->patch_ops.resume) + codec->patch_ops.resume(codec, state); + } + return 0; +} + +/** + * snd_hda_resume_ctls - resume controls in the new control list + * @codec: the HDA codec + * @knew: the array of snd_kcontrol_new_t + * + * This function resumes the mixer controls in the snd_kcontrol_new_t array, + * originally for snd_hda_add_new_ctls(). + * The array must be terminated with an empty entry as terminator. + */ +int snd_hda_resume_ctls(struct hda_codec *codec, snd_kcontrol_new_t *knew) +{ + snd_ctl_elem_value_t *val; + + val = kmalloc(sizeof(*val), GFP_KERNEL); + if (! val) + return -ENOMEM; + codec->in_resume = 1; + for (; knew->name; knew++) { + int i, count; + count = knew->count ? knew->count : 1; + for (i = 0; i < count; i++) { + memset(val, 0, sizeof(*val)); + val->id.iface = knew->iface; + val->id.device = knew->device; + val->id.subdevice = knew->subdevice; + strcpy(val->id.name, knew->name); + val->id.index = knew->index ? knew->index : i; + /* Assume that get callback reads only from cache, + * not accessing to the real hardware + */ + if (snd_ctl_elem_read(codec->bus->card, val) < 0) + continue; + snd_ctl_elem_write(codec->bus->card, NULL, val); + } + } + codec->in_resume = 0; + kfree(val); + return 0; +} + +/** + * snd_hda_resume_spdi_out - resume the digital I/O + * @codec: the HDA codec + */ +int snd_hda_resume_spdif_out(struct hda_codec *codec) +{ + return snd_hda_resume_ctls(codec, dig_mixes); +} +#endif + +/* + * symbols exported for controller modules + */ +EXPORT_SYMBOL(snd_hda_codec_read); +EXPORT_SYMBOL(snd_hda_codec_write); +EXPORT_SYMBOL(snd_hda_sequence_write); +EXPORT_SYMBOL(snd_hda_get_sub_nodes); +EXPORT_SYMBOL(snd_hda_queue_unsol_event); +EXPORT_SYMBOL(snd_hda_bus_new); +EXPORT_SYMBOL(snd_hda_codec_new); +EXPORT_SYMBOL(snd_hda_codec_setup_stream); +EXPORT_SYMBOL(snd_hda_calc_stream_format); +EXPORT_SYMBOL(snd_hda_build_pcms); +EXPORT_SYMBOL(snd_hda_build_controls); +#ifdef CONFIG_PM +EXPORT_SYMBOL(snd_hda_suspend); +EXPORT_SYMBOL(snd_hda_resume); +#endif + +/* + * INIT part + */ + +static int __init alsa_hda_init(void) +{ + return 0; +} + +static void __exit alsa_hda_exit(void) +{ +} + +module_init(alsa_hda_init) +module_exit(alsa_hda_exit) diff -Nru a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/sound/pci/hda/hda_codec.h 2005-01-28 14:22:13 -08:00 @@ -0,0 +1,602 @@ +/* + * Universal Interface for Intel High Definition Audio Codec + * + * Copyright (c) 2004 Takashi Iwai + * + * 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. + */ + +#ifndef __SOUND_HDA_CODEC_H +#define __SOUND_HDA_CODEC_H + +#include +#include +#include + +/* + * nodes + */ +#define AC_NODE_ROOT 0x00 + +/* + * function group types + */ +enum { + AC_GRP_AUDIO_FUNCTION = 0x01, + AC_GRP_MODEM_FUNCTION = 0x02, +}; + +/* + * widget types + */ +enum { + AC_WID_AUD_OUT, /* Audio Out */ + AC_WID_AUD_IN, /* Audio In */ + AC_WID_AUD_MIX, /* Audio Mixer */ + AC_WID_AUD_SEL, /* Audio Selector */ + AC_WID_PIN, /* Pin Complex */ + AC_WID_POWER, /* Power */ + AC_WID_VOL_KNB, /* Volume Knob */ + AC_WID_BEEP, /* Beep Generator */ + AC_WID_VENDOR = 0x0f /* Vendor specific */ +}; + +/* + * GET verbs + */ +#define AC_VERB_GET_STREAM_FORMAT 0x0a00 +#define AC_VERB_GET_AMP_GAIN_MUTE 0x0b00 +#define AC_VERB_GET_PROC_COEF 0x0c00 +#define AC_VERB_GET_COEF_INDEX 0x0d00 +#define AC_VERB_PARAMETERS 0x0f00 +#define AC_VERB_GET_CONNECT_SEL 0x0f01 +#define AC_VERB_GET_CONNECT_LIST 0x0f02 +#define AC_VERB_GET_PROC_STATE 0x0f03 +#define AC_VERB_GET_SDI_SELECT 0x0f04 +#define AC_VERB_GET_POWER_STATE 0x0f05 +#define AC_VERB_GET_CONV 0x0f06 +#define AC_VERB_GET_PIN_WIDGET_CONTROL 0x0f07 +#define AC_VERB_GET_UNSOLICITED_RESPONSE 0x0f08 +#define AC_VERB_GET_PIN_SENSE 0x0f09 +#define AC_VERB_GET_BEEP_CONTROL 0x0f0a +#define AC_VERB_GET_EAPD_BTLENABLE 0x0f0c +#define AC_VERB_GET_DIGI_CONVERT 0x0f0d +#define AC_VERB_GET_VOLUME_KNOB_CONTROL 0x0f0f +/* f10-f1a: GPIO */ +#define AC_VERB_GET_CONFIG_DEFAULT 0x0f1c + +/* + * SET verbs + */ +#define AC_VERB_SET_STREAM_FORMAT 0x200 +#define AC_VERB_SET_AMP_GAIN_MUTE 0x300 +#define AC_VERB_SET_PROC_COEF 0x400 +#define AC_VERB_SET_COEF_INDEX 0x500 +#define AC_VERB_SET_CONNECT_SEL 0x701 +#define AC_VERB_SET_PROC_STATE 0x703 +#define AC_VERB_SET_SDI_SELECT 0x704 +#define AC_VERB_SET_POWER_STATE 0x705 +#define AC_VERB_SET_CHANNEL_STREAMID 0x706 +#define AC_VERB_SET_PIN_WIDGET_CONTROL 0x707 +#define AC_VERB_SET_UNSOLICITED_ENABLE 0x708 +#define AC_VERB_SET_PIN_SENSE 0x709 +#define AC_VERB_SET_BEEP_CONTROL 0x70a +#define AC_VERB_SET_EAPD_BTLENALBE 0x70c +#define AC_VERB_SET_DIGI_CONVERT_1 0x70d +#define AC_VERB_SET_DIGI_CONVERT_2 0x70e +#define AC_VERB_SET_VOLUME_KNOB_CONTROL 0x70f +#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 0x71c +#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1 0x71d +#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2 0x71e +#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_3 0x71f +#define AC_VERB_SET_CODEC_RESET 0x7ff + +/* + * Parameter IDs + */ +#define AC_PAR_VENDOR_ID 0x00 +#define AC_PAR_SUBSYSTEM_ID 0x01 +#define AC_PAR_REV_ID 0x02 +#define AC_PAR_NODE_COUNT 0x04 +#define AC_PAR_FUNCTION_TYPE 0x05 +#define AC_PAR_AUDIO_FG_CAP 0x08 +#define AC_PAR_AUDIO_WIDGET_CAP 0x09 +#define AC_PAR_PCM 0x0a +#define AC_PAR_STREAM 0x0b +#define AC_PAR_PIN_CAP 0x0c +#define AC_PAR_AMP_IN_CAP 0x0d +#define AC_PAR_CONNLIST_LEN 0x0e +#define AC_PAR_POWER_STATE 0x0f +#define AC_PAR_PROC_CAP 0x10 +#define AC_PAR_GPIO_CAP 0x11 +#define AC_PAR_AMP_OUT_CAP 0x12 + +/* + * AC_VERB_PARAMETERS results (32bit) + */ + +/* Function Group Type */ +#define AC_FGT_TYPE (0xff<<0) +#define AC_FGT_TYPE_SHIFT 0 +#define AC_FGT_UNSOL_CAP (1<<8) + +/* Audio Function Group Capabilities */ +#define AC_AFG_OUT_DELAY (0xf<<0) +#define AC_AFG_IN_DELAY (0xf<<8) +#define AC_AFG_BEEP_GEN (1<<16) + +/* Audio Widget Capabilities */ +#define AC_WCAP_STEREO (1<<0) /* stereo I/O */ +#define AC_WCAP_IN_AMP (1<<1) /* AMP-in present */ +#define AC_WCAP_OUT_AMP (1<<2) /* AMP-out present */ +#define AC_WCAP_AMP_OVRD (1<<3) /* AMP-parameter override */ +#define AC_WCAP_FORMAT_OVRD (1<<4) /* format override */ +#define AC_WCAP_STRIPE (1<<5) /* stripe */ +#define AC_WCAP_PROC_WID (1<<6) /* Proc Widget */ +#define AC_WCAP_UNSOL_CAP (1<<7) /* Unsol capable */ +#define AC_WCAP_CONN_LIST (1<<8) /* connection list */ +#define AC_WCAP_DIGITAL (1<<9) /* digital I/O */ +#define AC_WCAP_POWER (1<<10) /* power control */ +#define AC_WCAP_LR_SWAP (1<<11) /* L/R swap */ +#define AC_WCAP_DELAY (0xf<<16) +#define AC_WCAP_DELAY_SHIFT 16 +#define AC_WCAP_TYPE (0xf<<20) +#define AC_WCAP_TYPE_SHIFT 20 + +/* supported PCM rates and bits */ +#define AC_SUPPCM_RATES (0xfff << 0) +#define AC_SUPPCM_BITS_8 (1<<16) +#define AC_SUPPCM_BITS_16 (1<<17) +#define AC_SUPPCM_BITS_20 (1<<18) +#define AC_SUPPCM_BITS_24 (1<<19) +#define AC_SUPPCM_BITS_32 (1<<20) + +/* supported PCM stream format */ +#define AC_SUPFMT_PCM (1<<0) +#define AC_SUPFMT_FLOAT32 (1<<1) +#define AC_SUPFMT_AC3 (1<<2) + +/* Pin widget capabilies */ +#define AC_PINCAP_IMP_SENSE (1<<0) /* impedance sense capable */ +#define AC_PINCAP_TRIG_REQ (1<<1) /* trigger required */ +#define AC_PINCAP_PRES_DETECT (1<<2) /* presence detect capable */ +#define AC_PINCAP_HP_DRV (1<<3) /* headphone drive capable */ +#define AC_PINCAP_OUT (1<<4) /* output capable */ +#define AC_PINCAP_IN (1<<5) /* input capable */ +#define AC_PINCAP_BALANCE (1<<6) /* balanced I/O capable */ +#define AC_PINCAP_VREF (7<<8) +#define AC_PINCAP_VREF_SHIFT 8 +#define AC_PINCAP_EAPD (1<<16) /* EAPD capable */ +/* Vref status (used in pin cap and pin ctl) */ +#define AC_PIN_VREF_HIZ (1<<0) /* Hi-Z */ +#define AC_PIN_VREF_50 (1<<1) /* 50% */ +#define AC_PIN_VREF_GRD (1<<2) /* ground */ +#define AC_PIN_VREF_80 (1<<4) /* 80% */ +#define AC_PIN_VREF_100 (1<<5) /* 100% */ + + +/* Amplifier capabilities */ +#define AC_AMPCAP_OFFSET (0x7f<<0) /* 0dB offset */ +#define AC_AMPCAP_OFFSET_SHIFT 0 +#define AC_AMPCAP_NUM_STEPS (0x7f<<8) /* number of steps */ +#define AC_AMPCAP_NUM_STEPS_SHIFT 8 +#define AC_AMPCAP_STEP_SIZE (0x7f<<16) /* step size 0-32dB in 0.25dB */ +#define AC_AMPCAP_STEP_SIZE_SHIFT 16 +#define AC_AMPCAP_MUTE (1<<31) /* mute capable */ +#define AC_AMPCAP_MUTE_SHIFT 31 + +/* Connection list */ +#define AC_CLIST_LENGTH (0x7f<<0) +#define AC_CLIST_LONG (1<<7) + +/* Supported power status */ +#define AC_PWRST_D0SUP (1<<0) +#define AC_PWRST_D1SUP (1<<1) +#define AC_PWRST_D2SUP (1<<2) +#define AC_PWRST_D3SUP (1<<3) + +/* Processing capabilies */ +#define AC_PCAP_BENIGN (1<<0) +#define AC_PCAP_NUM_COEF (0xff<<8) + +/* Volume knobs capabilities */ +#define AC_KNBCAP_NUM_STEPS (0x7f<<0) +#define AC_KNBCAP_DELTA (1<<8) + +/* + * Control Parameters + */ + +/* Amp gain/mute */ +#define AC_AMP_MUTE (1<<8) +#define AC_AMP_GAIN (0x7f) +#define AC_AMP_GET_INDEX (0xf<<0) + +#define AC_AMP_GET_LEFT (1<<13) +#define AC_AMP_GET_RIGHT (0<<13) +#define AC_AMP_GET_OUTPUT (1<<15) +#define AC_AMP_GET_INPUT (0<<15) + +#define AC_AMP_SET_INDEX (0xf<<8) +#define AC_AMP_SET_INDEX_SHIFT 8 +#define AC_AMP_SET_RIGHT (1<<12) +#define AC_AMP_SET_LEFT (1<<13) +#define AC_AMP_SET_INPUT (1<<14) +#define AC_AMP_SET_OUTPUT (1<<15) + +/* DIGITAL1 bits */ +#define AC_DIG1_ENABLE (1<<0) +#define AC_DIG1_V (1<<1) +#define AC_DIG1_VCFG (1<<2) +#define AC_DIG1_EMPHASIS (1<<3) +#define AC_DIG1_COPYRIGHT (1<<4) +#define AC_DIG1_NONAUDIO (1<<5) +#define AC_DIG1_PROFESSIONAL (1<<6) +#define AC_DIG1_LEVEL (1<<7) + +/* Pin widget control - 8bit */ +#define AC_PINCTL_VREFEN (0x7<<0) +#define AC_PINCTL_IN_EN (1<<5) +#define AC_PINCTL_OUT_EN (1<<6) +#define AC_PINCTL_HP_EN (1<<7) + +/* configuration default - 32bit */ +#define AC_DEFCFG_SEQUENCE (0xf<<0) +#define AC_DEFCFG_DEF_ASSOC (0xf<<4) +#define AC_DEFCFG_MISC (0xf<<8) +#define AC_DEFCFG_COLOR (0xf<<12) +#define AC_DEFCFG_COLOR_SHIFT 12 +#define AC_DEFCFG_CONN_TYPE (0xf<<16) +#define AC_DEFCFG_CONN_TYPE_SHIFT 16 +#define AC_DEFCFG_DEVICE (0xf<<20) +#define AC_DEFCFG_DEVICE_SHIFT 20 +#define AC_DEFCFG_LOCATION (0x3f<<24) +#define AC_DEFCFG_LOCATION_SHIFT 24 +#define AC_DEFCFG_PORT_CONN (0x3<<30) +#define AC_DEFCFG_PORT_CONN_SHIFT 30 + +/* device device types (0x0-0xf) */ +enum { + AC_JACK_LINE_OUT, + AC_JACK_SPEAKER, + AC_JACK_HP_OUT, + AC_JACK_CD, + AC_JACK_SPDIF_OUT, + AC_JACK_DIG_OTHER_OUT, + AC_JACK_MODEM_LINE_SIDE, + AC_JACK_MODEM_HAND_SIDE, + AC_JACK_LINE_IN, + AC_JACK_AUX, + AC_JACK_MIC_IN, + AC_JACK_TELEPHONY, + AC_JACK_SPDIF_IN, + AC_JACK_DIG_OTHER_IN, + AC_JACK_OTHER = 0xf, +}; + +/* jack connection types (0x0-0xf) */ +enum { + AC_JACK_CONN_UNKNOWN, + AC_JACK_CONN_1_8, + AC_JACK_CONN_1_4, + AC_JACK_CONN_ATAPI, + AC_JACK_CONN_RCA, + AC_JACK_CONN_OPTICAL, + AC_JACK_CONN_OTHER_DIGITAL, + AC_JACK_CONN_OTHER_ANALOG, + AC_JACK_CONN_DIN, + AC_JACK_CONN_XLR, + AC_JACK_CONN_RJ11, + AC_JACK_CONN_COMB, + AC_JACK_CONN_OTHER = 0xf, +}; + +/* jack colors (0x0-0xf) */ +enum { + AC_JACK_COLOR_UNKNOWN, + AC_JACK_COLOR_BLACK, + AC_JACK_COLOR_GREY, + AC_JACK_COLOR_BLUE, + AC_JACK_COLOR_GREEN, + AC_JACK_COLOR_RED, + AC_JACK_COLOR_ORANGE, + AC_JACK_COLOR_YELLOW, + AC_JACK_COLOR_PURPLE, + AC_JACK_COLOR_PINK, + AC_JACK_COLOR_WHITE = 0xe, + AC_JACK_COLOR_OTHER, +}; + +/* Jack location (0x0-0x3f) */ +/* common case */ +enum { + AC_JACK_LOC_NONE, + AC_JACK_LOC_REAR, + AC_JACK_LOC_FRONT, + AC_JACK_LOC_LEFT, + AC_JACK_LOC_RIGHT, + AC_JACK_LOC_TOP, + AC_JACK_LOC_BOTTOM, +}; +/* bits 4-5 */ +enum { + AC_JACK_LOC_EXTERNAL = 0x00, + AC_JACK_LOC_INTERNAL = 0x10, + AC_JACK_LOC_SEPARATE = 0x20, + AC_JACK_LOC_OTHER = 0x30, +}; +enum { + /* external on primary chasis */ + AC_JACK_LOC_REAR_PANEL = 0x07, + AC_JACK_LOC_DRIVE_BAY, + /* internal */ + AC_JACK_LOC_RISER = 0x17, + AC_JACK_LOC_HDMI, + AC_JACK_LOC_ATAPI, + /* others */ + AC_JACK_LOC_MOBILE_IN = 0x37, + AC_JACK_LOC_MOBILE_OUT, +}; + +/* Port connectivity (0-3) */ +enum { + AC_JACK_PORT_COMPLEX, + AC_JACK_PORT_NONE, + AC_JACK_PORT_FIXED, + AC_JACK_PORT_BOTH, +}; + +/* max. connections to a widget */ +#define HDA_MAX_CONNECTIONS 16 + +/* max. codec address */ +#define HDA_MAX_CODEC_ADDRESS 0x0f + +/* + * Structures + */ + +struct hda_bus; +struct hda_codec; +struct hda_pcm; +struct hda_pcm_stream; +struct hda_bus_unsolicited; + +/* NID type */ +typedef u16 hda_nid_t; + +/* bus operators */ +struct hda_bus_ops { + /* send a single command */ + int (*command)(struct hda_codec *codec, hda_nid_t nid, int direct, + unsigned int verb, unsigned int parm); + /* get a response from the last command */ + unsigned int (*get_response)(struct hda_codec *codec); + /* free the private data */ + void (*private_free)(struct hda_bus *); +}; + +/* template to pass to the bus constructor */ +struct hda_bus_template { + void *private_data; + struct pci_dev *pci; + const char *modelname; + struct hda_bus_ops ops; +}; + +/* + * codec bus + * + * each controller needs to creata a hda_bus to assign the accessor. + * A hda_bus contains several codecs in the list codec_list. + */ +struct hda_bus { + snd_card_t *card; + + /* copied from template */ + void *private_data; + struct pci_dev *pci; + const char *modelname; + struct hda_bus_ops ops; + + /* codec linked list */ + struct list_head codec_list; + struct hda_codec *caddr_tbl[HDA_MAX_CODEC_ADDRESS]; /* caddr -> codec */ + + struct semaphore cmd_mutex; + + /* unsolicited event queue */ + struct hda_bus_unsolicited *unsol; + + snd_info_entry_t *proc; +}; + +/* + * codec preset + * + * Known codecs have the patch to build and set up the controls/PCMs + * better than the generic parser. + */ +struct hda_codec_preset { + unsigned int id; + unsigned int mask; + unsigned int subs; + unsigned int subs_mask; + unsigned int rev; + const char *name; + int (*patch)(struct hda_codec *codec); +}; + +/* ops set by the preset patch */ +struct hda_codec_ops { + int (*build_controls)(struct hda_codec *codec); + int (*build_pcms)(struct hda_codec *codec); + int (*init)(struct hda_codec *codec); + void (*free)(struct hda_codec *codec); + void (*unsol_event)(struct hda_codec *codec, unsigned int res); +#ifdef CONFIG_PM + int (*suspend)(struct hda_codec *codec, unsigned int state); + int (*resume)(struct hda_codec *codec, unsigned int state); +#endif +}; + +/* record for amp information cache */ +struct hda_amp_info { + u32 key; /* hash key */ + u32 amp_caps; /* amp capabilities */ + u16 vol[2]; /* current volume & mute*/ + u16 status; /* update flag */ + u16 next; /* next link */ +}; + +/* PCM callbacks */ +struct hda_pcm_ops { + int (*open)(struct hda_pcm_stream *info, struct hda_codec *codec, + snd_pcm_substream_t *substream); + int (*close)(struct hda_pcm_stream *info, struct hda_codec *codec, + snd_pcm_substream_t *substream); + int (*prepare)(struct hda_pcm_stream *info, struct hda_codec *codec, + unsigned int stream_tag, unsigned int format, + snd_pcm_substream_t *substream); + int (*cleanup)(struct hda_pcm_stream *info, struct hda_codec *codec, + snd_pcm_substream_t *substream); +}; + +/* PCM information for each substream */ +struct hda_pcm_stream { + unsigned int substreams; /* number of substreams, 0 = not exist */ + unsigned int channels_min; /* min. number of channels */ + unsigned int channels_max; /* max. number of channels */ + hda_nid_t nid; /* default NID to query rates/formats/bps, or set up */ + u32 rates; /* supported rates */ + u64 formats; /* supported formats (SNDRV_PCM_FMTBIT_) */ + unsigned int maxbps; /* supported max. bit per sample */ + struct hda_pcm_ops ops; +}; + +/* for PCM creation */ +struct hda_pcm { + char *name; + struct hda_pcm_stream stream[2]; +}; + +/* codec information */ +struct hda_codec { + struct hda_bus *bus; + unsigned int addr; /* codec addr*/ + struct list_head list; /* list point */ + + hda_nid_t afg; /* AFG node id */ + + /* ids */ + u32 vendor_id; + u32 subsystem_id; + u32 revision_id; + + /* detected preset */ + const struct hda_codec_preset *preset; + + /* set by patch */ + struct hda_codec_ops patch_ops; + + /* resume phase - all controls should update even if + * the values are not changed + */ + unsigned int in_resume; + + /* PCM to create, set by patch_ops.build_pcms callback */ + unsigned int num_pcms; + struct hda_pcm *pcm_info; + + /* codec specific info */ + void *spec; + + /* hash for amp access */ + u16 amp_hash[32]; + int num_amp_entries; + struct hda_amp_info amp_info[128]; /* big enough? */ + + struct semaphore spdif_mutex; + unsigned int spdif_status; +}; + +/* direction */ +enum { + HDA_INPUT, HDA_OUTPUT +}; + + +/* + * constructors + */ +int snd_hda_bus_new(snd_card_t *card, const struct hda_bus_template *temp, + struct hda_bus **busp); +int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, + struct hda_codec **codecp); + +/* + * low level functions + */ +unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, int direct, + unsigned int verb, unsigned int parm); +int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct, + unsigned int verb, unsigned int parm); +#define snd_hda_param_read(codec, nid, param) snd_hda_codec_read(codec, nid, 0, AC_VERB_PARAMETERS, param) +int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *start_id); +int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); + +struct hda_verb { + hda_nid_t nid; + u32 verb; + u32 param; +}; + +void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq); + +/* unsolicited event */ +int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex); + +/* + * Mixer + */ +int snd_hda_build_controls(struct hda_bus *bus); + +/* + * PCM + */ +int snd_hda_build_pcms(struct hda_bus *bus); +void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stream_tag, + int channel_id, int format); +unsigned int snd_hda_calc_stream_format(unsigned int rate, unsigned int channels, + unsigned int format, unsigned int maxbps); +int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, + u32 *ratesp, u64 *formatsp, unsigned int *bpsp); +int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, + unsigned int format); + +/* + * Misc + */ +void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen); + +/* + * power management + */ +#ifdef CONFIG_PM +int snd_hda_suspend(struct hda_bus *bus, unsigned int state); +int snd_hda_resume(struct hda_bus *bus, unsigned int state); +#endif + +#endif /* __SOUND_HDA_CODEC_H */ diff -Nru a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/sound/pci/hda/hda_generic.c 2005-01-28 14:22:13 -08:00 @@ -0,0 +1,898 @@ +/* + * Universal Interface for Intel High Definition Audio Codec + * + * Generic widget tree parser + * + * Copyright (c) 2004 Takashi Iwai + * + * This driver 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 driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include "hda_codec.h" +#include "hda_local.h" + +/* widget node for parsing */ +struct hda_gnode { + hda_nid_t nid; /* NID of this widget */ + unsigned short nconns; /* number of input connections */ + hda_nid_t conn_list[HDA_MAX_CONNECTIONS]; /* input connections */ + unsigned int wid_caps; /* widget capabilities */ + unsigned char type; /* widget type */ + unsigned char pin_ctl; /* pin controls */ + unsigned char checked; /* the flag indicates that the node is already parsed */ + unsigned int pin_caps; /* pin widget capabilities */ + unsigned int def_cfg; /* default configuration */ + unsigned int amp_out_caps; /* AMP out capabilities */ + unsigned int amp_in_caps; /* AMP in capabilities */ + struct list_head list; +}; + +/* pathc-specific record */ +struct hda_gspec { + struct hda_gnode *dac_node; /* DAC node */ + struct hda_gnode *out_pin_node; /* Output pin (Line-Out) node */ + struct hda_gnode *pcm_vol_node; /* Node for PCM volume */ + unsigned int pcm_vol_index; /* connection of PCM volume */ + + struct hda_gnode *adc_node; /* ADC node */ + struct hda_gnode *cap_vol_node; /* Node for capture volume */ + unsigned int cur_cap_src; /* current capture source */ + struct hda_input_mux input_mux; + char cap_labels[HDA_MAX_NUM_INPUTS][16]; + + unsigned int def_amp_in_caps; + unsigned int def_amp_out_caps; + + struct hda_pcm pcm_rec; /* PCM information */ + + struct list_head nid_list; /* list of widgets */ +}; + +/* + * retrieve the default device type from the default config value + */ +#define get_defcfg_type(node) (((node)->def_cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT) +#define get_defcfg_location(node) (((node)->def_cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT) + +/* + * destructor + */ +static void snd_hda_generic_free(struct hda_codec *codec) +{ + struct hda_gspec *spec = codec->spec; + struct list_head *p, *n; + + if (! spec) + return; + /* free all widgets */ + list_for_each_safe(p, n, &spec->nid_list) { + struct hda_gnode *node = list_entry(p, struct hda_gnode, list); + kfree(node); + } + kfree(spec); +} + + +/* + * add a new widget node and read its attributes + */ +static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid_t nid) +{ + struct hda_gnode *node; + int nconns; + + node = kcalloc(1, sizeof(*node), GFP_KERNEL); + if (node == NULL) + return -ENOMEM; + node->nid = nid; + nconns = snd_hda_get_connections(codec, nid, node->conn_list, HDA_MAX_CONNECTIONS); + if (nconns < 0) { + kfree(node); + return nconns; + } + node->nconns = nconns; + node->wid_caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); + node->type = (node->wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + + if (node->type == AC_WID_PIN) { + node->pin_caps = snd_hda_param_read(codec, node->nid, AC_PAR_PIN_CAP); + node->pin_ctl = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + node->def_cfg = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); + } + + if (node->wid_caps & AC_WCAP_OUT_AMP) { + if (node->wid_caps & AC_WCAP_AMP_OVRD) + node->amp_out_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_OUT_CAP); + if (! node->amp_out_caps) + node->amp_out_caps = spec->def_amp_out_caps; + } + if (node->wid_caps & AC_WCAP_IN_AMP) { + if (node->wid_caps & AC_WCAP_AMP_OVRD) + node->amp_in_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_IN_CAP); + if (! node->amp_in_caps) + node->amp_in_caps = spec->def_amp_in_caps; + } + list_add_tail(&node->list, &spec->nid_list); + return 0; +} + +/* + * build the AFG subtree + */ +static int build_afg_tree(struct hda_codec *codec) +{ + struct hda_gspec *spec = codec->spec; + int i, nodes, err; + hda_nid_t nid; + + snd_assert(spec, return -EINVAL); + + spec->def_amp_out_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_OUT_CAP); + spec->def_amp_in_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_IN_CAP); + + nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); + if (! nid || nodes < 0) { + printk(KERN_ERR "Invalid AFG subtree\n"); + return -EINVAL; + } + + /* parse all nodes belonging to the AFG */ + for (i = 0; i < nodes; i++, nid++) { + if ((err = add_new_node(codec, spec, nid)) < 0) + return err; + } + + return 0; +} + + +/* + * look for the node record for the given NID + */ +/* FIXME: should avoid the braindead linear search */ +static struct hda_gnode *hda_get_node(struct hda_gspec *spec, hda_nid_t nid) +{ + struct list_head *p; + struct hda_gnode *node; + + list_for_each(p, &spec->nid_list) { + node = list_entry(p, struct hda_gnode, list); + if (node->nid == nid) + return node; + } + return NULL; +} + +/* + * unmute (and set max vol) the output amplifier + */ +static int unmute_output(struct hda_codec *codec, struct hda_gnode *node) +{ + unsigned int val, ofs; + snd_printdd("UNMUTE OUT: NID=0x%x\n", node->nid); + val = (node->amp_out_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; + ofs = (node->amp_out_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; + if (val >= ofs) + val -= ofs; + val |= AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT; + val |= AC_AMP_SET_OUTPUT; + return snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, val); +} + +/* + * unmute (and set max vol) the input amplifier + */ +static int unmute_input(struct hda_codec *codec, struct hda_gnode *node, unsigned int index) +{ + unsigned int val, ofs; + snd_printdd("UNMUTE IN: NID=0x%x IDX=0x%x\n", node->nid, index); + val = (node->amp_in_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; + ofs = (node->amp_in_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; + if (val >= ofs) + val -= ofs; + val |= AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT; + val |= AC_AMP_SET_INPUT; + // awk added - fixed to allow unmuting of indexed amps + val |= index << AC_AMP_SET_INDEX_SHIFT; + return snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, val); +} + +/* + * select the input connection of the given node. + */ +static int select_input_connection(struct hda_codec *codec, struct hda_gnode *node, + unsigned int index) +{ + snd_printdd("CONNECT: NID=0x%x IDX=0x%x\n", node->nid, index); + return snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_CONNECT_SEL, index); +} + +/* + * clear checked flag of each node in the node list + */ +static void clear_check_flags(struct hda_gspec *spec) +{ + struct list_head *p; + struct hda_gnode *node; + + list_for_each(p, &spec->nid_list) { + node = list_entry(p, struct hda_gnode, list); + node->checked = 0; + } +} + +/* + * parse the output path recursively until reach to an audio output widget + * + * returns 0 if not found, 1 if found, or a negative error code. + */ +static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec, + struct hda_gnode *node) +{ + int i, err; + struct hda_gnode *child; + + if (node->checked) + return 0; + + node->checked = 1; + if (node->type == AC_WID_AUD_OUT) { + snd_printdd("AUD_OUT found %x\n", node->nid); + if (spec->dac_node) { + /* already DAC node is assigned, just unmute & connect */ + return node == spec->dac_node; + } + spec->dac_node = node; + if (node->wid_caps & AC_WCAP_OUT_AMP) { + spec->pcm_vol_node = node; + spec->pcm_vol_index = 0; + } + return 1; /* found */ + } + + for (i = 0; i < node->nconns; i++) { + child = hda_get_node(spec, node->conn_list[i]); + if (! child) + continue; + err = parse_output_path(codec, spec, child); + if (err < 0) + return err; + else if (err > 0) { + /* found one, + * select the path, unmute both input and output + */ + if (node->nconns > 1) + select_input_connection(codec, node, i); + unmute_input(codec, node, i); + unmute_output(codec, node); + if (! spec->pcm_vol_node) { + if (node->wid_caps & AC_WCAP_IN_AMP) { + spec->pcm_vol_node = node; + spec->pcm_vol_index = i; + } else if (node->wid_caps & AC_WCAP_OUT_AMP) { + spec->pcm_vol_node = node; + spec->pcm_vol_index = 0; + } + } + return 1; + } + } + return 0; +} + +/* + * Look for the output PIN widget with the given jack type + * and parse the output path to that PIN. + * + * Returns the PIN node when the path to DAC is established. + */ +static struct hda_gnode *parse_output_jack(struct hda_codec *codec, + struct hda_gspec *spec, + int jack_type) +{ + struct list_head *p; + struct hda_gnode *node; + int err; + + list_for_each(p, &spec->nid_list) { + node = list_entry(p, struct hda_gnode, list); + if (node->type != AC_WID_PIN) + continue; + /* output capable? */ + if (! (node->pin_caps & AC_PINCAP_OUT)) + continue; + if (jack_type >= 0) { + if (jack_type != get_defcfg_type(node)) + continue; + } else { + /* output as default? */ + if (! (node->pin_ctl & AC_PINCTL_OUT_EN)) + continue; + } + clear_check_flags(spec); + err = parse_output_path(codec, spec, node); + if (err < 0) + return NULL; + else if (err > 0) { + /* unmute the PIN output */ + unmute_output(codec, node); + /* set PIN-Out enable */ + snd_hda_codec_write(codec, node->nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN); + return node; + } + } + return NULL; +} + + +/* + * parse outputs + */ +static int parse_output(struct hda_codec *codec) +{ + struct hda_gspec *spec = codec->spec; + struct hda_gnode *node; + + /* + * Look for the output PIN widget + */ + /* first, look for the line-out pin */ + node = parse_output_jack(codec, spec, AC_JACK_LINE_OUT); + if (node) /* found, remember the PIN node */ + spec->out_pin_node = node; + /* look for the HP-out pin */ + node = parse_output_jack(codec, spec, AC_JACK_HP_OUT); + if (node) { + if (! spec->out_pin_node) + spec->out_pin_node = node; + } + + if (! spec->out_pin_node) { + /* no line-out or HP pins found, + * then choose for the first output pin + */ + spec->out_pin_node = parse_output_jack(codec, spec, -1); + if (! spec->out_pin_node) + snd_printd("hda_generic: no proper output path found\n"); + } + + return 0; +} + +/* + * input MUX + */ + +/* control callbacks */ +static int capture_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gspec *spec = codec->spec; + return snd_hda_input_mux_info(&spec->input_mux, uinfo); +} + +static int capture_source_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gspec *spec = codec->spec; + + ucontrol->value.enumerated.item[0] = spec->cur_cap_src; + return 0; +} + +static int capture_source_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gspec *spec = codec->spec; + return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol, + spec->adc_node->nid, &spec->cur_cap_src); +} + +/* + * return the string name of the given input PIN widget + */ +static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl) +{ + unsigned int location = get_defcfg_location(node); + switch (get_defcfg_type(node)) { + case AC_JACK_LINE_IN: + if ((location & 0x0f) == AC_JACK_LOC_FRONT) + return "Front Line"; + return "Line"; + case AC_JACK_CD: + if (pinctl) + *pinctl |= AC_PIN_VREF_GRD; + return "CD"; + case AC_JACK_AUX: + if ((location & 0x0f) == AC_JACK_LOC_FRONT) + return "Front Aux"; + return "Aux"; + case AC_JACK_MIC_IN: + if ((location & 0x0f) == AC_JACK_LOC_FRONT) + return "Front Mic"; + return "Mic"; + case AC_JACK_SPDIF_IN: + return "SPDIF"; + case AC_JACK_DIG_OTHER_IN: + return "Digital"; + } + return NULL; +} + +/* + * parse the nodes recursively until reach to the input PIN + * + * returns 0 if not found, 1 if found, or a negative error code. + */ +static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec, + struct hda_gnode *node) +{ + int i, err; + unsigned int pinctl; + char *label; + const char *type; + + if (node->checked) + return 0; + + node->checked = 1; + if (node->type != AC_WID_PIN) { + for (i = 0; i < node->nconns; i++) { + struct hda_gnode *child; + child = hda_get_node(spec, node->conn_list[i]); + if (! child) + continue; + err = parse_adc_sub_nodes(codec, spec, child); + if (err < 0) + return err; + if (err > 0) { + /* found one, + * select the path, unmute both input and output + */ + if (node->nconns > 1) + select_input_connection(codec, node, i); + unmute_input(codec, node, i); + unmute_output(codec, node); + return err; + } + } + return 0; + } + + /* input capable? */ + if (! (node->pin_caps & AC_PINCAP_IN)) + return 0; + + if (spec->input_mux.num_items >= HDA_MAX_NUM_INPUTS) { + snd_printk(KERN_ERR "hda_generic: Too many items for capture\n"); + return -EINVAL; + } + + pinctl = AC_PINCTL_IN_EN; + /* create a proper capture source label */ + type = get_input_type(node, &pinctl); + if (! type) { + /* input as default? */ + if (! (node->pin_ctl & AC_PINCTL_IN_EN)) + return 0; + type = "Input"; + } + label = spec->cap_labels[spec->input_mux.num_items]; + strcpy(label, type); + spec->input_mux.items[spec->input_mux.num_items].label = label; + + /* unmute the PIN external input */ + unmute_input(codec, node, 0); /* index = 0? */ + /* set PIN-In enable */ + snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl); + + return 1; /* found */ +} + +/* + * parse input + */ +static int parse_input_path(struct hda_codec *codec, struct hda_gnode *adc_node) +{ + struct hda_gspec *spec = codec->spec; + struct hda_gnode *node; + int i, err; + + snd_printdd("AUD_IN = %x\n", adc_node->nid); + clear_check_flags(spec); + + // awk added - fixed no recording due to muted widget + unmute_input(codec, adc_node, 0); + + /* + * check each connection of the ADC + * if it reaches to a proper input PIN, add the path as the + * input path. + */ + for (i = 0; i < adc_node->nconns; i++) { + node = hda_get_node(spec, adc_node->conn_list[i]); + if (! node) + continue; + err = parse_adc_sub_nodes(codec, spec, node); + if (err < 0) + return err; + else if (err > 0) { + struct hda_input_mux_item *csrc = &spec->input_mux.items[spec->input_mux.num_items]; + char *buf = spec->cap_labels[spec->input_mux.num_items]; + int ocap; + for (ocap = 0; ocap < spec->input_mux.num_items; ocap++) { + if (! strcmp(buf, spec->cap_labels[ocap])) { + /* same label already exists, + * put the index number to be unique + */ + sprintf(buf, "%s %d", spec->cap_labels[ocap], + spec->input_mux.num_items); + } + } + csrc->index = i; + spec->input_mux.num_items++; + } + } + + if (! spec->input_mux.num_items) + return 0; /* no input path found... */ + + snd_printdd("[Capture Source] NID=0x%x, #SRC=%d\n", adc_node->nid, spec->input_mux.num_items); + for (i = 0; i < spec->input_mux.num_items; i++) + snd_printdd(" [%s] IDX=0x%x\n", spec->input_mux.items[i].label, + spec->input_mux.items[i].index); + + spec->adc_node = adc_node; + return 1; +} + +/* + * parse input + */ +static int parse_input(struct hda_codec *codec) +{ + struct hda_gspec *spec = codec->spec; + struct list_head *p; + struct hda_gnode *node; + int err; + + /* + * At first we look for an audio input widget. + * If it reaches to certain input PINs, we take it as the + * input path. + */ + list_for_each(p, &spec->nid_list) { + node = list_entry(p, struct hda_gnode, list); + if (node->type == AC_WID_AUD_IN) { + err = parse_input_path(codec, node); + if (err < 0) + return err; + else if (err > 0) + return 0; + } + } + snd_printd("hda_generic: no proper input path found\n"); + return 0; +} + +/* + * create mixer controls if possible + */ +#define DIR_OUT 0x1 +#define DIR_IN 0x2 + +static int create_mixer(struct hda_codec *codec, struct hda_gnode *node, + unsigned int index, const char *type, const char *dir_sfx) +{ + char name[32]; + int err; + int created = 0; + + if (type) + sprintf(name, "%s %s Switch", type, dir_sfx); + else + sprintf(name, "%s Switch", dir_sfx); + if ((node->wid_caps & AC_WCAP_IN_AMP) && + (node->amp_in_caps & AC_AMPCAP_MUTE)) { + snd_kcontrol_new_t knew = + HDA_CODEC_MUTE(name, node->nid, index, HDA_INPUT); + snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index); + if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0) + return err; + created = 1; + } else if ((node->wid_caps & AC_WCAP_OUT_AMP) && + (node->amp_out_caps & AC_AMPCAP_MUTE)) { + snd_kcontrol_new_t knew = + HDA_CODEC_MUTE(name, node->nid, 0, HDA_OUTPUT); + snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid); + if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0) + return err; + created = 1; + } + + if (type) + sprintf(name, "%s %s Volume", type, dir_sfx); + else + sprintf(name, "%s Volume", dir_sfx); + if ((node->wid_caps & AC_WCAP_IN_AMP) && + (node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) { + snd_kcontrol_new_t knew = + HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT); + snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index); + if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0) + return err; + created = 1; + } else if ((node->wid_caps & AC_WCAP_OUT_AMP) && + (node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) { + snd_kcontrol_new_t knew = + HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT); + snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid); + if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0) + return err; + created = 1; + } + + return created; +} + +/* + * check whether the controls with the given name and direction suffix already exist + */ +static int check_existing_control(struct hda_codec *codec, const char *type, const char *dir) +{ + snd_ctl_elem_id_t id; + memset(&id, 0, sizeof(id)); + sprintf(id.name, "%s %s Volume", type, dir); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + if (snd_ctl_find_id(codec->bus->card, &id)) + return 1; + sprintf(id.name, "%s %s Switch", type, dir); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + if (snd_ctl_find_id(codec->bus->card, &id)) + return 1; + return 0; +} + +/* + * build output mixer controls + */ +static int build_output_controls(struct hda_codec *codec) +{ + struct hda_gspec *spec = codec->spec; + int err; + + err = create_mixer(codec, spec->pcm_vol_node, spec->pcm_vol_index, + "PCM", "Playback"); + if (err < 0) + return err; + return 0; +} + +/* create capture volume/switch */ +static int build_input_controls(struct hda_codec *codec) +{ + struct hda_gspec *spec = codec->spec; + struct hda_gnode *adc_node = spec->adc_node; + int err; + + if (! adc_node) + return 0; /* not found */ + + /* create capture volume and switch controls if the ADC has an amp */ + err = create_mixer(codec, adc_node, 0, NULL, "Capture"); + + /* create input MUX if multiple sources are available */ + if (spec->input_mux.num_items > 1) { + static snd_kcontrol_new_t cap_sel = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = capture_source_info, + .get = capture_source_get, + .put = capture_source_put, + }; + if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&cap_sel, codec))) < 0) + return err; + spec->cur_cap_src = 0; + select_input_connection(codec, adc_node, spec->input_mux.items[0].index); + } + return 0; +} + + +/* + * parse the nodes recursively until reach to the output PIN. + * + * returns 0 - if not found, + * 1 - if found, but no mixer is created + * 2 - if found and mixer was already created, (just skip) + * a negative error code + */ +static int parse_loopback_path(struct hda_codec *codec, struct hda_gspec *spec, + struct hda_gnode *node, struct hda_gnode *dest_node, + const char *type) +{ + int i, err; + + if (node->checked) + return 0; + + node->checked = 1; + if (node == dest_node) { + /* loopback connection found */ + return 1; + } + + for (i = 0; i < node->nconns; i++) { + struct hda_gnode *child = hda_get_node(spec, node->conn_list[i]); + if (! child) + continue; + err = parse_loopback_path(codec, spec, child, dest_node, type); + if (err < 0) + return err; + else if (err >= 1) { + if (err == 1) { + err = create_mixer(codec, node, i, type, "Playback"); + if (err < 0) + return err; + if (err > 0) + return 2; /* ok, created */ + /* not created, maybe in the lower path */ + err = 1; + } + /* connect and unmute */ + if (node->nconns > 1) + select_input_connection(codec, node, i); + unmute_input(codec, node, i); + unmute_output(codec, node); + return err; + } + } + return 0; +} + +/* + * parse the tree and build the loopback controls + */ +static int build_loopback_controls(struct hda_codec *codec) +{ + struct hda_gspec *spec = codec->spec; + struct list_head *p; + struct hda_gnode *node; + int err; + const char *type; + + if (! spec->out_pin_node) + return 0; + + list_for_each(p, &spec->nid_list) { + node = list_entry(p, struct hda_gnode, list); + if (node->type != AC_WID_PIN) + continue; + /* input capable? */ + if (! (node->pin_caps & AC_PINCAP_IN)) + return 0; + type = get_input_type(node, NULL); + if (type) { + if (check_existing_control(codec, type, "Playback")) + continue; + clear_check_flags(spec); + err = parse_loopback_path(codec, spec, spec->out_pin_node, + node, type); + if (err < 0) + return err; + if (! err) + continue; + } + } + return 0; +} + +/* + * build mixer controls + */ +static int build_generic_controls(struct hda_codec *codec) +{ + int err; + + if ((err = build_input_controls(codec)) < 0 || + (err = build_output_controls(codec)) < 0 || + (err = build_loopback_controls(codec)) < 0) + return err; + + return 0; +} + +/* + * PCM + */ +static struct hda_pcm_stream generic_pcm_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, +}; + +static int build_generic_pcms(struct hda_codec *codec) +{ + struct hda_gspec *spec = codec->spec; + struct hda_pcm *info = &spec->pcm_rec; + + if (! spec->dac_node && ! spec->adc_node) { + snd_printd("hda_generic: no PCM found\n"); + return 0; + } + + codec->num_pcms = 1; + codec->pcm_info = info; + + info->name = "HDA Generic"; + if (spec->dac_node) { + info->stream[0] = generic_pcm_playback; + info->stream[0].nid = spec->dac_node->nid; + } + if (spec->adc_node) { + info->stream[1] = generic_pcm_playback; + info->stream[1].nid = spec->adc_node->nid; + } + + return 0; +} + + +/* + */ +static struct hda_codec_ops generic_patch_ops = { + .build_controls = build_generic_controls, + .build_pcms = build_generic_pcms, + .free = snd_hda_generic_free, +}; + +/* + * the generic parser + */ +int snd_hda_parse_generic_codec(struct hda_codec *codec) +{ + struct hda_gspec *spec; + int err; + + spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); + if (spec == NULL) { + printk(KERN_ERR "hda_generic: can't allocate spec\n"); + return -ENOMEM; + } + codec->spec = spec; + INIT_LIST_HEAD(&spec->nid_list); + + if ((err = build_afg_tree(codec)) < 0) + goto error; + + if ((err = parse_input(codec)) < 0 || + (err = parse_output(codec)) < 0) + goto error; + + codec->patch_ops = generic_patch_ops; + + return 0; + + error: + snd_hda_generic_free(codec); + return err; +} diff -Nru a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/sound/pci/hda/hda_intel.c 2005-01-28 14:22:13 -08:00 @@ -0,0 +1,1449 @@ +/* + * + * hda_intel.c - Implementation of primary alsa driver code base for Intel HD Audio. + * + * Copyright(c) 2004 Intel Corporation. All rights reserved. + * + * Copyright (c) 2004 Takashi Iwai + * PeiSen Hou + * + * 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. + * + * CONTACTS: + * + * Matt Jared matt.jared@intel.com + * Andy Kopp andy.kopp@intel.com + * Dan Kogan dan.d.kogan@intel.com + * + * CHANGES: + * + * 2004.12.01 Major rewrite by tiwai, merged the work of pshou + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hda_codec.h" + + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static char *model[SNDRV_CARDS]; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for Intel HD audio interface."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for Intel HD audio interface."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable Intel HD audio interface."); +module_param_array(model, charp, NULL, 0444); +MODULE_PARM_DESC(model, "Use the given board model."); + +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{Intel, ICH6}," + "{Intel, ICH6M}," + "{Intel, ICH7}}"); +MODULE_DESCRIPTION("Intel HDA driver"); + +#define SFX "hda-intel: " + +/* + * registers + */ +#define ICH6_REG_GCAP 0x00 +#define ICH6_REG_VMIN 0x02 +#define ICH6_REG_VMAJ 0x03 +#define ICH6_REG_OUTPAY 0x04 +#define ICH6_REG_INPAY 0x06 +#define ICH6_REG_GCTL 0x08 +#define ICH6_REG_WAKEEN 0x0c +#define ICH6_REG_STATESTS 0x0e +#define ICH6_REG_GSTS 0x10 +#define ICH6_REG_INTCTL 0x20 +#define ICH6_REG_INTSTS 0x24 +#define ICH6_REG_WALCLK 0x30 +#define ICH6_REG_SYNC 0x34 +#define ICH6_REG_CORBLBASE 0x40 +#define ICH6_REG_CORBUBASE 0x44 +#define ICH6_REG_CORBWP 0x48 +#define ICH6_REG_CORBRP 0x4A +#define ICH6_REG_CORBCTL 0x4c +#define ICH6_REG_CORBSTS 0x4d +#define ICH6_REG_CORBSIZE 0x4e + +#define ICH6_REG_RIRBLBASE 0x50 +#define ICH6_REG_RIRBUBASE 0x54 +#define ICH6_REG_RIRBWP 0x58 +#define ICH6_REG_RINTCNT 0x5a +#define ICH6_REG_RIRBCTL 0x5c +#define ICH6_REG_RIRBSTS 0x5d +#define ICH6_REG_RIRBSIZE 0x5e + +#define ICH6_REG_IC 0x60 +#define ICH6_REG_IR 0x64 +#define ICH6_REG_IRS 0x68 +#define ICH6_IRS_VALID (1<<1) +#define ICH6_IRS_BUSY (1<<0) + +#define ICH6_REG_DPLBASE 0x70 +#define ICH6_REG_DPUBASE 0x74 +#define ICH6_DPLBASE_ENABLE 0x1 /* Enable position buffer */ + +/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ +enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; + +/* stream register offsets from stream base */ +#define ICH6_REG_SD_CTL 0x00 +#define ICH6_REG_SD_STS 0x03 +#define ICH6_REG_SD_LPIB 0x04 +#define ICH6_REG_SD_CBL 0x08 +#define ICH6_REG_SD_LVI 0x0c +#define ICH6_REG_SD_FIFOW 0x0e +#define ICH6_REG_SD_FIFOSIZE 0x10 +#define ICH6_REG_SD_FORMAT 0x12 +#define ICH6_REG_SD_BDLPL 0x18 +#define ICH6_REG_SD_BDLPU 0x1c + +/* PCI space */ +#define ICH6_PCIREG_TCSEL 0x44 + +/* + * other constants + */ + +/* max number of SDs */ +#define MAX_ICH6_DEV 8 +/* max number of fragments - we may use more if allocating more pages for BDL */ +#define AZX_MAX_FRAG (PAGE_SIZE / (MAX_ICH6_DEV * 16)) +/* max buffer size - no h/w limit, you can increase as you like */ +#define AZX_MAX_BUF_SIZE (1024*1024*1024) +/* max number of PCM devics per card */ +#define AZX_MAX_PCMS 8 + +/* RIRB int mask: overrun[2], response[0] */ +#define RIRB_INT_RESPONSE 0x01 +#define RIRB_INT_OVERRUN 0x04 +#define RIRB_INT_MASK 0x05 + +/* STATESTS int mask: SD2,SD1,SD0 */ +#define STATESTS_INT_MASK 0x07 +#define AZX_MAX_CODECS 3 + +/* SD_CTL bits */ +#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */ +#define SD_CTL_DMA_START 0x02 /* stream DMA start bit */ +#define SD_CTL_STREAM_TAG_MASK (0xf << 20) +#define SD_CTL_STREAM_TAG_SHIFT 20 + +/* SD_CTL and SD_STS */ +#define SD_INT_DESC_ERR 0x10 /* descriptor error interrupt */ +#define SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */ +#define SD_INT_COMPLETE 0x04 /* completion interrupt */ +#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|SD_INT_COMPLETE) + +/* SD_STS */ +#define SD_STS_FIFO_READY 0x20 /* FIFO ready */ + +/* INTCTL and INTSTS */ +#define ICH6_INT_ALL_STREAM 0xff /* all stream interrupts */ +#define ICH6_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */ +#define ICH6_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */ + +/* GCTL reset bit */ +#define ICH6_GCTL_RESET (1<<0) + +/* CORB/RIRB control, read/write pointer */ +#define ICH6_RBCTL_DMA_EN 0x02 /* enable DMA */ +#define ICH6_RBCTL_IRQ_EN 0x01 /* enable IRQ */ +#define ICH6_RBRWP_CLR 0x8000 /* read/write pointer clear */ +/* below are so far hardcoded - should read registers in future */ +#define ICH6_MAX_CORB_ENTRIES 256 +#define ICH6_MAX_RIRB_ENTRIES 256 + + +/* + * Use CORB/RIRB for communication from/to codecs. + * This is the way recommended by Intel (see below). + */ +#define USE_CORB_RIRB + +/* + * Define this if use the position buffer instead of reading SD_LPIB + * It's not used as default since SD_LPIB seems to give more accurate position + */ +/* #define USE_POSBUF */ + +/* + */ + +typedef struct snd_azx azx_t; +typedef struct snd_azx_rb azx_rb_t; +typedef struct snd_azx_dev azx_dev_t; + +struct snd_azx_dev { + u32 *bdl; /* virtual address of the BDL */ + dma_addr_t bdl_addr; /* physical address of the BDL */ + volatile u32 *posbuf; /* position buffer pointer */ + + unsigned int bufsize; /* size of the play buffer in bytes */ + unsigned int fragsize; /* size of each period in bytes */ + unsigned int frags; /* number for period in the play buffer */ + unsigned int fifo_size; /* FIFO size */ + + void __iomem *sd_addr; /* stream descriptor pointer */ + + u32 sd_int_sta_mask; /* stream int status mask */ + + /* pcm support */ + snd_pcm_substream_t *substream; /* assigned substream, set in PCM open */ + unsigned int format_val; /* format value to be set in the controller and the codec */ + unsigned char stream_tag; /* assigned stream */ + unsigned char index; /* stream index */ + + unsigned int opened: 1; + unsigned int running: 1; +}; + +/* CORB/RIRB */ +struct snd_azx_rb { + u32 *buf; /* CORB/RIRB buffer + * Each CORB entry is 4byte, RIRB is 8byte + */ + dma_addr_t addr; /* physical address of CORB/RIRB buffer */ + /* for RIRB */ + unsigned short rp, wp; /* read/write pointers */ + int cmds; /* number of pending requests */ + u32 res; /* last read value */ +}; + +struct snd_azx { + snd_card_t *card; + struct pci_dev *pci; + + /* pci resources */ + unsigned long addr; + void __iomem *remap_addr; + int irq; + + /* locks */ + spinlock_t reg_lock; + struct semaphore open_mutex; + + /* streams */ + azx_dev_t azx_dev[MAX_ICH6_DEV]; + + /* PCM */ + unsigned int pcm_devs; + snd_pcm_t *pcm[AZX_MAX_PCMS]; + + /* HD codec */ + unsigned short codec_mask; + struct hda_bus *bus; + + /* CORB/RIRB */ + azx_rb_t corb; + azx_rb_t rirb; + + /* BDL, CORB/RIRB and position buffers */ + struct snd_dma_buffer bdl; + struct snd_dma_buffer rb; + struct snd_dma_buffer posbuf; +}; + +/* + * macros for easy use + */ +#define azx_writel(chip,reg,value) \ + writel(value, (chip)->remap_addr + ICH6_REG_##reg) +#define azx_readl(chip,reg) \ + readl((chip)->remap_addr + ICH6_REG_##reg) +#define azx_writew(chip,reg,value) \ + writew(value, (chip)->remap_addr + ICH6_REG_##reg) +#define azx_readw(chip,reg) \ + readw((chip)->remap_addr + ICH6_REG_##reg) +#define azx_writeb(chip,reg,value) \ + writeb(value, (chip)->remap_addr + ICH6_REG_##reg) +#define azx_readb(chip,reg) \ + readb((chip)->remap_addr + ICH6_REG_##reg) + +#define azx_sd_writel(dev,reg,value) \ + writel(value, (dev)->sd_addr + ICH6_REG_##reg) +#define azx_sd_readl(dev,reg) \ + readl((dev)->sd_addr + ICH6_REG_##reg) +#define azx_sd_writew(dev,reg,value) \ + writew(value, (dev)->sd_addr + ICH6_REG_##reg) +#define azx_sd_readw(dev,reg) \ + readw((dev)->sd_addr + ICH6_REG_##reg) +#define azx_sd_writeb(dev,reg,value) \ + writeb(value, (dev)->sd_addr + ICH6_REG_##reg) +#define azx_sd_readb(dev,reg) \ + readb((dev)->sd_addr + ICH6_REG_##reg) + +/* for pcm support */ +#define get_azx_dev(substream) (azx_dev_t*)(substream->runtime->private_data) + +/* Get the upper 32bit of the given dma_addr_t + * Compiler should optimize and eliminate the code if dma_addr_t is 32bit + */ +#define upper_32bit(addr) (sizeof(addr) > 4 ? (u32)((addr) >> 32) : (u32)0) + + +/* + * Interface for HD codec + */ + +#ifdef USE_CORB_RIRB +/* + * CORB / RIRB interface + */ +static int azx_alloc_cmd_io(azx_t *chip) +{ + int err; + + /* single page (at least 4096 bytes) must suffice for both ringbuffes */ + err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), + PAGE_SIZE, &chip->rb); + if (err < 0) { + snd_printk(KERN_ERR SFX "cannot allocate CORB/RIRB\n"); + return err; + } + return 0; +} + +static void azx_init_cmd_io(azx_t *chip) +{ + /* CORB set up */ + chip->corb.addr = chip->rb.addr; + chip->corb.buf = (u32 *)chip->rb.area; + azx_writel(chip, CORBLBASE, (u32)chip->corb.addr); + azx_writel(chip, CORBUBASE, upper_32bit(chip->corb.addr)); + + /* set the corb write pointer to 0 */ + azx_writew(chip, CORBWP, 0); + /* reset the corb hw read pointer */ + azx_writew(chip, CORBRP, ICH6_RBRWP_CLR); + /* enable corb dma */ + azx_writeb(chip, CORBCTL, ICH6_RBCTL_DMA_EN); + + /* RIRB set up */ + chip->rirb.addr = chip->rb.addr + 2048; + chip->rirb.buf = (u32 *)(chip->rb.area + 2048); + azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr); + azx_writel(chip, RIRBUBASE, upper_32bit(chip->rirb.addr)); + + /* reset the rirb hw write pointer */ + azx_writew(chip, RIRBWP, ICH6_RBRWP_CLR); + /* set N=1, get RIRB response interrupt for new entry */ + azx_writew(chip, RINTCNT, 1); + /* enable rirb dma and response irq */ +#ifdef USE_CORB_RIRB + azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN); +#else + azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN); +#endif + chip->rirb.rp = chip->rirb.cmds = 0; +} + +static void azx_free_cmd_io(azx_t *chip) +{ + /* disable ringbuffer DMAs */ + azx_writeb(chip, RIRBCTL, 0); + azx_writeb(chip, CORBCTL, 0); +} + +/* send a command */ +static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid, int direct, + unsigned int verb, unsigned int para) +{ + azx_t *chip = codec->bus->private_data; + unsigned int wp; + u32 val; + + val = (u32)(codec->addr & 0x0f) << 28; + val |= (u32)direct << 27; + val |= (u32)nid << 20; + val |= verb << 8; + val |= para; + + /* add command to corb */ + wp = azx_readb(chip, CORBWP); + wp++; + wp %= ICH6_MAX_CORB_ENTRIES; + + spin_lock_irq(&chip->reg_lock); + chip->rirb.cmds++; + chip->corb.buf[wp] = cpu_to_le32(val); + azx_writel(chip, CORBWP, wp); + spin_unlock_irq(&chip->reg_lock); + + return 0; +} + +#define ICH6_RIRB_EX_UNSOL_EV (1<<4) + +/* retrieve RIRB entry - called from interrupt handler */ +static void azx_update_rirb(azx_t *chip) +{ + unsigned int rp, wp; + u32 res, res_ex; + + wp = azx_readb(chip, RIRBWP); + if (wp == chip->rirb.wp) + return; + chip->rirb.wp = wp; + + while (chip->rirb.rp != wp) { + chip->rirb.rp++; + chip->rirb.rp %= ICH6_MAX_RIRB_ENTRIES; + + rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */ + res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]); + res = le32_to_cpu(chip->rirb.buf[rp]); + if (res_ex & ICH6_RIRB_EX_UNSOL_EV) + snd_hda_queue_unsol_event(chip->bus, res, res_ex); + else if (chip->rirb.cmds) { + chip->rirb.cmds--; + chip->rirb.res = res; + } + } +} + +/* receive a response */ +static unsigned int azx_get_response(struct hda_codec *codec) +{ + azx_t *chip = codec->bus->private_data; + int timeout = 50; + + while (chip->rirb.cmds) { + if (! --timeout) { + snd_printk(KERN_ERR "azx_get_response timeout\n"); + chip->rirb.rp = azx_readb(chip, RIRBWP); + chip->rirb.cmds = 0; + return -1; + } + msleep(1); + } + return chip->rirb.res; /* the last value */ +} + +#else +/* + * Use the single immediate command instead of CORB/RIRB for simplicity + * + * Note: according to Intel, this is not preferred use. The command was + * intended for the BIOS only, and may get confused with unsolicited + * responses. So, we shouldn't use it for normal operation from the + * driver. + * I left the codes, however, for debugging/testing purposes. + */ + +#define azx_alloc_cmd_io(chip) 0 +#define azx_init_cmd_io(chip) +#define azx_free_cmd_io(chip) + +/* send a command */ +static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid, int direct, + unsigned int verb, unsigned int para) +{ + azx_t *chip = codec->bus->private_data; + u32 val; + int timeout = 50; + + val = (u32)(codec->addr & 0x0f) << 28; + val |= (u32)direct << 27; + val |= (u32)nid << 20; + val |= verb << 8; + val |= para; + + while (timeout--) { + /* check ICB busy bit */ + if (! (azx_readw(chip, IRS) & ICH6_IRS_BUSY)) { + /* Clear IRV valid bit */ + azx_writew(chip, IRS, azx_readw(chip, IRS) | ICH6_IRS_VALID); + azx_writel(chip, IC, val); + azx_writew(chip, IRS, azx_readw(chip, IRS) | ICH6_IRS_BUSY); + return 0; + } + udelay(1); + } + snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n", azx_readw(chip, IRS), val); + return -EIO; +} + +/* receive a response */ +static unsigned int azx_get_response(struct hda_codec *codec) +{ + azx_t *chip = codec->bus->private_data; + int timeout = 50; + + while (timeout--) { + /* check IRV busy bit */ + if (azx_readw(chip, IRS) & ICH6_IRS_VALID) + return azx_readl(chip, IR); + udelay(1); + } + snd_printd(SFX "get_response timeout: IRS=0x%x\n", azx_readw(chip, IRS)); + return (unsigned int)-1; +} + +#define azx_update_rirb(chip) + +#endif /* USE_CORB_RIRB */ + +/* reset codec link */ +static int azx_reset(azx_t *chip) +{ + int count; + + /* reset controller */ + azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~ICH6_GCTL_RESET); + + count = 50; + while (azx_readb(chip, GCTL) && --count) + msleep(1); + + /* delay for >= 100us for codec PLL to settle per spec + * Rev 0.9 section 5.5.1 + */ + msleep(1); + + /* Bring controller out of reset */ + azx_writeb(chip, GCTL, azx_readb(chip, GCTL) | ICH6_GCTL_RESET); + + count = 50; + while (! azx_readb(chip, GCTL) && --count) + msleep(1); + + /* Brent Chartrand said to wait >= 540us for codecs to intialize */ + msleep(1); + + /* check to see if controller is ready */ + if (! azx_readb(chip, GCTL)) { + snd_printd("azx_reset: controller not ready!\n"); + return -EBUSY; + } + + /* detect codecs */ + if (! chip->codec_mask) { + chip->codec_mask = azx_readw(chip, STATESTS); + snd_printdd("codec_mask = 0x%x\n", chip->codec_mask); + } + + return 0; +} + + +/* + * Lowlevel interface + */ + +/* enable interrupts */ +static void azx_int_enable(azx_t *chip) +{ + /* enable controller CIE and GIE */ + azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) | + ICH6_INT_CTRL_EN | ICH6_INT_GLOBAL_EN); +} + +/* disable interrupts */ +static void azx_int_disable(azx_t *chip) +{ + int i; + + /* disable interrupts in stream descriptor */ + for (i = 0; i < MAX_ICH6_DEV; i++) { + azx_dev_t *azx_dev = &chip->azx_dev[i]; + azx_sd_writeb(azx_dev, SD_CTL, + azx_sd_readb(azx_dev, SD_CTL) & ~SD_INT_MASK); + } + + /* disable SIE for all streams */ + azx_writeb(chip, INTCTL, 0); + + /* disable controller CIE and GIE */ + azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) & + ~(ICH6_INT_CTRL_EN | ICH6_INT_GLOBAL_EN)); +} + +/* clear interrupts */ +static void azx_int_clear(azx_t *chip) +{ + int i; + + /* clear stream status */ + for (i = 0; i < MAX_ICH6_DEV; i++) { + azx_dev_t *azx_dev = &chip->azx_dev[i]; + azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); + } + + /* clear STATESTS */ + azx_writeb(chip, STATESTS, STATESTS_INT_MASK); + + /* clear rirb status */ + azx_writeb(chip, RIRBSTS, RIRB_INT_MASK); + + /* clear int status */ + azx_writel(chip, INTSTS, ICH6_INT_CTRL_EN | ICH6_INT_ALL_STREAM); +} + +/* start a stream */ +static void azx_stream_start(azx_t *chip, azx_dev_t *azx_dev) +{ + /* enable SIE */ + azx_writeb(chip, INTCTL, + azx_readb(chip, INTCTL) | (1 << azx_dev->index)); + /* set DMA start and interrupt mask */ + azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) | + SD_CTL_DMA_START | SD_INT_MASK); +} + +/* stop a stream */ +static void azx_stream_stop(azx_t *chip, azx_dev_t *azx_dev) +{ + /* stop DMA */ + azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) & + ~(SD_CTL_DMA_START | SD_INT_MASK)); + azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */ + /* disable SIE */ + azx_writeb(chip, INTCTL, + azx_readb(chip, INTCTL) & ~(1 << azx_dev->index)); +} + + +/* + * initialize the chip + */ +static void azx_init_chip(azx_t *chip) +{ + unsigned char tcsel_reg; + + /* Clear bits 0-2 of PCI register TCSEL (at offset 0x44) + * TCSEL == Traffic Class Select Register, which sets PCI express QOS + * Ensuring these bits are 0 clears playback static on some HD Audio codecs + */ + pci_read_config_byte (chip->pci, ICH6_PCIREG_TCSEL, &tcsel_reg); + pci_write_config_byte(chip->pci, ICH6_PCIREG_TCSEL, tcsel_reg & 0xf8); + + /* reset controller */ + azx_reset(chip); + + /* initialize interrupts */ + azx_int_clear(chip); + azx_int_enable(chip); + + /* initialize the codec command I/O */ + azx_init_cmd_io(chip); + +#ifdef USE_POSBUF + /* program the position buffer */ + azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr); + azx_writel(chip, DPUBASE, upper_32bit(chip->posbuf.addr)); +#endif +} + + +/* + * interrupt handler + */ +static irqreturn_t azx_interrupt(int irq, void* dev_id, struct pt_regs *regs) +{ + azx_t *chip = dev_id; + azx_dev_t *azx_dev; + u32 status; + int i; + + spin_lock(&chip->reg_lock); + + status = azx_readl(chip, INTSTS); + if (status == 0) { + spin_unlock(&chip->reg_lock); + return IRQ_NONE; + } + + for (i = 0; i < MAX_ICH6_DEV; i++) { + azx_dev = &chip->azx_dev[i]; + if (status & azx_dev->sd_int_sta_mask) { + azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); + if (azx_dev->substream && azx_dev->running) { + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(azx_dev->substream); + spin_lock(&chip->reg_lock); + } + } + } + + /* clear rirb int */ + status = azx_readb(chip, RIRBSTS); + if (status & RIRB_INT_MASK) { + if (status & RIRB_INT_RESPONSE) + azx_update_rirb(chip); + azx_writeb(chip, RIRBSTS, RIRB_INT_MASK); + } + +#if 0 + /* clear state status int */ + if (azx_readb(chip, STATESTS) & 0x04) + azx_writeb(chip, STATESTS, 0x04); +#endif + spin_unlock(&chip->reg_lock); + + return IRQ_HANDLED; +} + + +/* + * set up BDL entries + */ +static void azx_setup_periods(azx_dev_t *azx_dev) +{ + u32 *bdl = azx_dev->bdl; + dma_addr_t dma_addr = azx_dev->substream->runtime->dma_addr; + int idx; + + /* reset BDL address */ + azx_sd_writel(azx_dev, SD_BDLPL, 0); + azx_sd_writel(azx_dev, SD_BDLPU, 0); + + /* program the initial BDL entries */ + for (idx = 0; idx < azx_dev->frags; idx++) { + unsigned int off = idx << 2; /* 4 dword step */ + dma_addr_t addr = dma_addr + idx * azx_dev->fragsize; + /* program the address field of the BDL entry */ + bdl[off] = cpu_to_le32((u32)addr); + bdl[off+1] = cpu_to_le32(upper_32bit(addr)); + + /* program the size field of the BDL entry */ + bdl[off+2] = cpu_to_le32(azx_dev->fragsize); + + /* program the IOC to enable interrupt when buffer completes */ + bdl[off+3] = cpu_to_le32(0x01); + } +} + +/* + * set up the SD for streaming + */ +static int azx_setup_controller(azx_t *chip, azx_dev_t *azx_dev) +{ + unsigned char val; + int timeout; + + /* make sure the run bit is zero for SD */ + azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) & ~SD_CTL_DMA_START); + /* reset stream */ + azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) | SD_CTL_STREAM_RESET); + udelay(3); + timeout = 300; + while (!((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) && + --timeout) + ; + val &= ~SD_CTL_STREAM_RESET; + azx_sd_writeb(azx_dev, SD_CTL, val); + udelay(3); + + timeout = 300; + /* waiting for hardware to report that the stream is out of reset */ + while (((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) && + --timeout) + ; + + /* program the stream_tag */ + azx_sd_writel(azx_dev, SD_CTL, + (azx_sd_readl(azx_dev, SD_CTL) & ~SD_CTL_STREAM_TAG_MASK) | + (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT)); + + /* program the length of samples in cyclic buffer */ + azx_sd_writel(azx_dev, SD_CBL, azx_dev->bufsize); + + /* program the stream format */ + /* this value needs to be the same as the one programmed */ + azx_sd_writew(azx_dev, SD_FORMAT, azx_dev->format_val); + + /* program the stream LVI (last valid index) of the BDL */ + azx_sd_writew(azx_dev, SD_LVI, azx_dev->frags - 1); + + /* program the BDL address */ + /* lower BDL address */ + azx_sd_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl_addr); + /* upper BDL address */ + azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl_addr)); + +#ifdef USE_POSBUF + /* enable the position buffer */ + if (! (azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE)) + azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE); +#endif + /* set the interrupt enable bits in the descriptor control register */ + azx_sd_writel(azx_dev, SD_CTL, azx_sd_readl(azx_dev, SD_CTL) | SD_INT_MASK); + + return 0; +} + + +/* + * Codec initialization + */ + +static int __devinit azx_codec_create(azx_t *chip, const char *model) +{ + struct hda_bus_template bus_temp; + int c, codecs, err; + + memset(&bus_temp, 0, sizeof(bus_temp)); + bus_temp.private_data = chip; + bus_temp.modelname = model; + bus_temp.pci = chip->pci; + bus_temp.ops.command = azx_send_cmd; + bus_temp.ops.get_response = azx_get_response; + + if ((err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus)) < 0) + return err; + + codecs = 0; + for (c = 0; c < AZX_MAX_CODECS; c++) { + if (chip->codec_mask & (1 << c)) { + err = snd_hda_codec_new(chip->bus, c, NULL); + if (err < 0) + continue; + codecs++; + } + } + if (! codecs) { + snd_printk(KERN_ERR SFX "no codecs initialized\n"); + return -ENXIO; + } + + return 0; +} + + +/* + * PCM support + */ + +/* assign a stream for the PCM */ +static inline azx_dev_t *azx_assign_device(azx_t *chip, int stream) +{ + int dev, i; + dev = stream == SNDRV_PCM_STREAM_PLAYBACK ? 4 : 0; + for (i = 0; i < 4; i++, dev++) + if (! chip->azx_dev[dev].opened) { + chip->azx_dev[dev].opened = 1; + return &chip->azx_dev[dev]; + } + return NULL; +} + +/* release the assigned stream */ +static inline void azx_release_device(azx_dev_t *azx_dev) +{ + azx_dev->opened = 0; +} + +static snd_pcm_hardware_t azx_pcm_hw = { + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = AZX_MAX_BUF_SIZE, + .period_bytes_min = 128, + .period_bytes_max = AZX_MAX_BUF_SIZE / 2, + .periods_min = 2, + .periods_max = AZX_MAX_FRAG, + .fifo_size = 0, +}; + +struct azx_pcm { + azx_t *chip; + struct hda_codec *codec; + struct hda_pcm_stream *hinfo[2]; +}; + +static int azx_pcm_open(snd_pcm_substream_t *substream) +{ + struct azx_pcm *apcm = snd_pcm_substream_chip(substream); + struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; + azx_t *chip = apcm->chip; + azx_dev_t *azx_dev; + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + int err; + + down(&chip->open_mutex); + azx_dev = azx_assign_device(chip, substream->stream); + if (azx_dev == NULL) { + up(&chip->open_mutex); + return -EBUSY; + } + runtime->hw = azx_pcm_hw; + runtime->hw.channels_min = hinfo->channels_min; + runtime->hw.channels_max = hinfo->channels_max; + runtime->hw.formats = hinfo->formats; + runtime->hw.rates = hinfo->rates; + snd_pcm_limit_hw_rates(runtime); + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + if ((err = hinfo->ops.open(hinfo, apcm->codec, substream)) < 0) { + azx_release_device(azx_dev); + up(&chip->open_mutex); + return err; + } + spin_lock_irqsave(&chip->reg_lock, flags); + azx_dev->substream = substream; + azx_dev->running = 0; + spin_unlock_irqrestore(&chip->reg_lock, flags); + + runtime->private_data = azx_dev; + up(&chip->open_mutex); + return 0; +} + +static int azx_pcm_close(snd_pcm_substream_t *substream) +{ + struct azx_pcm *apcm = snd_pcm_substream_chip(substream); + struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; + azx_t *chip = apcm->chip; + azx_dev_t *azx_dev = get_azx_dev(substream); + unsigned long flags; + + down(&chip->open_mutex); + spin_lock_irqsave(&chip->reg_lock, flags); + azx_dev->substream = NULL; + azx_dev->running = 0; + spin_unlock_irqrestore(&chip->reg_lock, flags); + azx_release_device(azx_dev); + hinfo->ops.close(hinfo, apcm->codec, substream); + up(&chip->open_mutex); + return 0; +} + +static int azx_pcm_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int azx_pcm_hw_free(snd_pcm_substream_t *substream) +{ + struct azx_pcm *apcm = snd_pcm_substream_chip(substream); + azx_dev_t *azx_dev = get_azx_dev(substream); + struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; + + /* reset BDL address */ + azx_sd_writel(azx_dev, SD_BDLPL, 0); + azx_sd_writel(azx_dev, SD_BDLPU, 0); + azx_sd_writel(azx_dev, SD_CTL, 0); + + hinfo->ops.cleanup(hinfo, apcm->codec, substream); + + return snd_pcm_lib_free_pages(substream); +} + +static int azx_pcm_prepare(snd_pcm_substream_t *substream) +{ + struct azx_pcm *apcm = snd_pcm_substream_chip(substream); + azx_t *chip = apcm->chip; + azx_dev_t *azx_dev = get_azx_dev(substream); + struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; + snd_pcm_runtime_t *runtime = substream->runtime; + + azx_dev->bufsize = snd_pcm_lib_buffer_bytes(substream); + azx_dev->fragsize = snd_pcm_lib_period_bytes(substream); + azx_dev->frags = azx_dev->bufsize / azx_dev->fragsize; + azx_dev->format_val = snd_hda_calc_stream_format(runtime->rate, + runtime->channels, + runtime->format, + hinfo->maxbps); + if (! azx_dev->format_val) { + snd_printk(KERN_ERR SFX "invalid format_val, rate=%d, ch=%d, format=%d\n", + runtime->rate, runtime->channels, runtime->format); + return -EINVAL; + } + + snd_printdd("azx_pcm_prepare: bufsize=0x%x, fragsize=0x%x, format=0x%x\n", + azx_dev->bufsize, azx_dev->fragsize, azx_dev->format_val); + azx_setup_periods(azx_dev); + azx_setup_controller(chip, azx_dev); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1; + else + azx_dev->fifo_size = 0; + + return hinfo->ops.prepare(hinfo, apcm->codec, azx_dev->stream_tag, + azx_dev->format_val, substream); +} + +static int azx_pcm_trigger(snd_pcm_substream_t *substream, int cmd) +{ + struct azx_pcm *apcm = snd_pcm_substream_chip(substream); + azx_dev_t *azx_dev = get_azx_dev(substream); + azx_t *chip = apcm->chip; + int err = 0; + + spin_lock(&chip->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_START: + azx_stream_start(chip, azx_dev); + azx_dev->running = 1; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + azx_stream_stop(chip, azx_dev); + azx_dev->running = 0; + break; + default: + err = -EINVAL; + } + spin_unlock(&chip->reg_lock); + if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH || + cmd == SNDRV_PCM_TRIGGER_STOP) { + int timeout = 5000; + while (azx_sd_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START && --timeout) + ; + } + return err; +} + +static snd_pcm_uframes_t azx_pcm_pointer(snd_pcm_substream_t *substream) +{ + azx_dev_t *azx_dev = get_azx_dev(substream); + unsigned int pos; + +#ifdef USE_POSBUF + /* use the position buffer */ + pos = *azx_dev->posbuf; +#else + /* read LPIB */ + pos = azx_sd_readl(azx_dev, SD_LPIB) + azx_dev->fifo_size; +#endif + if (pos >= azx_dev->bufsize) + pos = 0; + return bytes_to_frames(substream->runtime, pos); +} + +static snd_pcm_ops_t azx_pcm_ops = { + .open = azx_pcm_open, + .close = azx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = azx_pcm_hw_params, + .hw_free = azx_pcm_hw_free, + .prepare = azx_pcm_prepare, + .trigger = azx_pcm_trigger, + .pointer = azx_pcm_pointer, +}; + +static void azx_pcm_free(snd_pcm_t *pcm) +{ + kfree(pcm->private_data); +} + +static int __devinit create_codec_pcm(azx_t *chip, struct hda_codec *codec, + struct hda_pcm *cpcm, int pcm_dev) +{ + int err; + snd_pcm_t *pcm; + struct azx_pcm *apcm; + + snd_assert(cpcm->stream[0].substreams || cpcm->stream[1].substreams, return -EINVAL); + snd_assert(cpcm->name, return -EINVAL); + + err = snd_pcm_new(chip->card, cpcm->name, pcm_dev, + cpcm->stream[0].substreams, cpcm->stream[1].substreams, + &pcm); + if (err < 0) + return err; + strcpy(pcm->name, cpcm->name); + apcm = kmalloc(sizeof(*apcm), GFP_KERNEL); + if (apcm == NULL) + return -ENOMEM; + apcm->chip = chip; + apcm->codec = codec; + apcm->hinfo[0] = &cpcm->stream[0]; + apcm->hinfo[1] = &cpcm->stream[1]; + pcm->private_data = apcm; + pcm->private_free = azx_pcm_free; + if (cpcm->stream[0].substreams) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &azx_pcm_ops); + if (cpcm->stream[1].substreams) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + 1024 * 64, 1024 * 128); + chip->pcm[pcm_dev] = pcm; + + return 0; +} + +static int __devinit azx_pcm_create(azx_t *chip) +{ + struct list_head *p; + struct hda_codec *codec; + int c, err; + int pcm_dev; + + if ((err = snd_hda_build_pcms(chip->bus)) < 0) + return err; + + pcm_dev = 0; + list_for_each(p, &chip->bus->codec_list) { + codec = list_entry(p, struct hda_codec, list); + for (c = 0; c < codec->num_pcms; c++) { + if (pcm_dev >= AZX_MAX_PCMS) { + snd_printk(KERN_ERR SFX "Too many PCMs\n"); + return -EINVAL; + } + err = create_codec_pcm(chip, codec, &codec->pcm_info[c], pcm_dev); + if (err < 0) + return err; + pcm_dev++; + } + } + return 0; +} + +/* + * mixer creation - all stuff is implemented in hda module + */ +static int __devinit azx_mixer_create(azx_t *chip) +{ + return snd_hda_build_controls(chip->bus); +} + + +/* + * initialize SD streams + */ +static int __devinit azx_init_stream(azx_t *chip) +{ + int i; + + /* initialize each stream (aka device) + * assign the starting bdl address to each stream (device) and initialize + */ + for (i = 0; i < MAX_ICH6_DEV; i++) { + unsigned int off = sizeof(u32) * (i * AZX_MAX_FRAG * 4); + azx_dev_t *azx_dev = &chip->azx_dev[i]; + azx_dev->bdl = (u32 *)(chip->bdl.area + off); + azx_dev->bdl_addr = chip->bdl.addr + off; +#ifdef USE_POSBUF + azx_dev->posbuf = (volatile u32 *)(chip->posbuf.area + i * 8); +#endif + /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ + azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80); + /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */ + azx_dev->sd_int_sta_mask = 1 << i; + /* stream tag: must be non-zero and unique */ + azx_dev->index = i; + azx_dev->stream_tag = i + 1; + } + + return 0; +} + + +#ifdef CONFIG_PM +/* + * power management + */ +static int azx_suspend(snd_card_t *card, unsigned int state) +{ + azx_t *chip = card->pm_private_data; + int i; + + for (i = 0; i < chip->pcm_devs; i++) + if (chip->pcm[i]) + snd_pcm_suspend_all(chip->pcm[i]); + snd_hda_suspend(chip->bus, state); + azx_free_cmd_io(chip); + pci_disable_device(chip->pci); + return 0; +} + +static int azx_resume(snd_card_t *card, unsigned int state) +{ + azx_t *chip = card->pm_private_data; + + pci_enable_device(chip->pci); + pci_set_master(chip->pci); + azx_init_chip(chip); + snd_hda_resume(chip->bus, state); + return 0; +} +#endif /* CONFIG_PM */ + + +/* + * destructor + */ +static int azx_free(azx_t *chip) +{ + if (chip->remap_addr) { + int i; + + for (i = 0; i < MAX_ICH6_DEV; i++) + azx_stream_stop(chip, &chip->azx_dev[i]); + + /* disable interrupts */ + azx_int_disable(chip); + azx_int_clear(chip); + + /* disable CORB/RIRB */ + azx_free_cmd_io(chip); + + /* disable position buffer */ + azx_writel(chip, DPLBASE, 0); + azx_writel(chip, DPUBASE, 0); + + /* wait a little for interrupts to finish */ + msleep(1); + + iounmap(chip->remap_addr); + } + + if (chip->irq >= 0) + free_irq(chip->irq, (void*)chip); + + if (chip->bdl.area) + snd_dma_free_pages(&chip->bdl); + if (chip->rb.area) + snd_dma_free_pages(&chip->rb); +#ifdef USE_POSBUF + if (chip->posbuf.area) + snd_dma_free_pages(&chip->posbuf); +#endif + pci_release_regions(chip->pci); + pci_disable_device(chip->pci); + kfree(chip); + + return 0; +} + +static int azx_dev_free(snd_device_t *device) +{ + return azx_free(device->device_data); +} + +/* + * constructor + */ +static int __devinit azx_create(snd_card_t *card, struct pci_dev *pci, azx_t **rchip) +{ + azx_t *chip; + int err = 0; + static snd_device_ops_t ops = { + .dev_free = azx_dev_free, + }; + + *rchip = NULL; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); + + if (NULL == chip) { + snd_printk(KERN_ERR SFX "cannot allocate chip\n"); + pci_disable_device(pci); + return -ENOMEM; + } + + spin_lock_init(&chip->reg_lock); + init_MUTEX(&chip->open_mutex); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + + if ((err = pci_request_regions(pci, "ICH HD audio")) < 0) { + kfree(chip); + pci_disable_device(pci); + return err; + } + + chip->addr = pci_resource_start(pci,0); + chip->remap_addr = ioremap_nocache(chip->addr, pci_resource_len(pci,0)); + if (chip->remap_addr == NULL) { + snd_printk(KERN_ERR SFX "ioremap error\n"); + err = -ENXIO; + goto errout; + } + + if (request_irq(pci->irq, azx_interrupt, SA_INTERRUPT|SA_SHIRQ, + "HDA Intel", (void*)chip)) { + snd_printk(KERN_ERR SFX "unable to grab IRQ %d\n", pci->irq); + err = -EBUSY; + goto errout; + } + chip->irq = pci->irq; + + pci_set_master(pci); + synchronize_irq(chip->irq); + + /* allocate memory for the BDL for each stream */ + if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), + PAGE_SIZE, &chip->bdl)) < 0) { + snd_printk(KERN_ERR SFX "cannot allocate BDL\n"); + goto errout; + } +#ifdef USE_POSBUF + /* allocate memory for the position buffer */ + if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), + MAX_ICH6_DEV * 8, &chip->posbuf)) < 0) { + snd_printk(KERN_ERR SFX "cannot allocate posbuf\n"); + goto errout; + } +#endif + /* allocate CORB/RIRB */ + if ((err = azx_alloc_cmd_io(chip)) < 0) + goto errout; + + /* initialize streams */ + azx_init_stream(chip); + + /* initialize chip */ + azx_init_chip(chip); + + /* codec detection */ + if (! chip->codec_mask) { + snd_printk(KERN_ERR SFX "no codecs found!\n"); + err = -ENODEV; + goto errout; + } + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) <0) { + snd_printk(KERN_ERR SFX "Error creating device [card]!\n"); + goto errout; + } + + *rchip = chip; + return 0; + + errout: + azx_free(chip); + return err; +} + +static int __devinit azx_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) +{ + static int dev; + snd_card_t *card; + azx_t *chip; + int err = 0; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (! enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (NULL == card) { + snd_printk(KERN_ERR SFX "Error creating card!\n"); + return -ENOMEM; + } + + if ((err = azx_create(card, pci, &chip)) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, "HDA-Intel"); + strcpy(card->shortname, "HDA Intel"); + sprintf(card->longname, "%s at 0x%lx irq %i", card->shortname, chip->addr, chip->irq); + + /* create codec instances */ + if ((err = azx_codec_create(chip, model[dev])) < 0) { + snd_card_free(card); + return err; + } + + /* create PCM streams */ + if ((err = azx_pcm_create(chip)) < 0) { + snd_card_free(card); + return err; + } + + /* create mixer controls */ + if ((err = azx_mixer_create(chip)) < 0) { + snd_card_free(card); + return err; + } + + snd_card_set_pm_callback(card, azx_suspend, azx_resume, chip); + snd_card_set_dev(card, &pci->dev); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + pci_set_drvdata(pci, card); + dev++; + + return err; +} + +static void __devexit azx_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +/* PCI IDs */ +static struct pci_device_id azx_ids[] = { + { 0x8086, 0x2668, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH6 */ + { 0x8086, 0x27d8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH7 */ + { 0, } +}; +MODULE_DEVICE_TABLE(pci, azx_ids); + +/* pci_driver definition */ +static struct pci_driver driver = { + .name = "HDA Intel", + .id_table = azx_ids, + .probe = azx_probe, + .remove = __devexit_p(azx_remove), + SND_PCI_PM_CALLBACKS +}; + +static int __init alsa_card_azx_init(void) +{ + return pci_module_init(&driver); +} + +static void __exit alsa_card_azx_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_azx_init) +module_exit(alsa_card_azx_exit) diff -Nru a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/sound/pci/hda/hda_local.h 2005-01-28 14:22:13 -08:00 @@ -0,0 +1,159 @@ +/* + * Universal Interface for Intel High Definition Audio Codec + * + * Local helper functions + * + * Copyright (c) 2004 Takashi Iwai + * + * 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. + */ + +#ifndef __SOUND_HDA_LOCAL_H +#define __SOUND_HDA_LOCAL_H + +/* + * for mixer controls + */ +#define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) ((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19)) +#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ + .info = snd_hda_mixer_amp_volume_info, \ + .get = snd_hda_mixer_amp_volume_get, \ + .put = snd_hda_mixer_amp_volume_put, \ + .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) } +#define HDA_CODEC_VOLUME_IDX(xname, xcidx, nid, xindex, direction) \ + HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, 3, xindex, direction) +#define HDA_CODEC_VOLUME_MONO(xname, nid, channel, xindex, direction) \ + HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, 3, xindex, direction) +#define HDA_CODEC_VOLUME(xname, nid, xindex, direction) \ + HDA_CODEC_VOLUME_MONO(xname, nid, 3, xindex, direction) +#define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ + .info = snd_hda_mixer_amp_switch_info, \ + .get = snd_hda_mixer_amp_switch_get, \ + .put = snd_hda_mixer_amp_switch_put, \ + .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) } +#define HDA_CODEC_MUTE_IDX(xname, xcidx, nid, xindex, direction) \ + HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, 3, xindex, direction) +#define HDA_CODEC_MUTE_MONO(xname, nid, channel, xindex, direction) \ + HDA_CODEC_MUTE_MONO_IDX(xname, 0, nid, 3, xindex, direction) +#define HDA_CODEC_MUTE(xname, nid, xindex, direction) \ + HDA_CODEC_MUTE_MONO(xname, nid, 3, xindex, direction) + +int snd_hda_mixer_amp_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo); +int snd_hda_mixer_amp_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol); +int snd_hda_mixer_amp_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol); +int snd_hda_mixer_amp_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo); +int snd_hda_mixer_amp_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol); +int snd_hda_mixer_amp_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol); + +int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid); + +/* + * input MUX helper + */ +#define HDA_MAX_NUM_INPUTS 8 +struct hda_input_mux_item { + const char *label; + unsigned int index; +}; +struct hda_input_mux { + unsigned int num_items; + struct hda_input_mux_item items[HDA_MAX_NUM_INPUTS]; +}; + +int snd_hda_input_mux_info(const struct hda_input_mux *imux, snd_ctl_elem_info_t *uinfo); +int snd_hda_input_mux_put(struct hda_codec *codec, const struct hda_input_mux *imux, + snd_ctl_elem_value_t *ucontrol, hda_nid_t nid, + unsigned int *cur_val); + +/* + * Multi-channel / digital-out PCM helper + */ + +enum { HDA_FRONT, HDA_REAR, HDA_CLFE, HDA_SIDE }; /* index for dac_nidx */ +enum { HDA_DIG_NONE, HDA_DIG_EXCLUSIVE, HDA_DIG_ANALOG_DUP }; /* dig_out_used */ + +struct hda_multi_out { + int num_dacs; /* # of DACs, must be more than 1 */ + hda_nid_t *dac_nids; /* DAC list */ + hda_nid_t hp_nid; /* optional DAC for HP, 0 when not exists */ + hda_nid_t dig_out_nid; /* digital out audio widget */ + int max_channels; /* currently supported analog channels */ + int dig_out_used; /* current usage of digital out (HDA_DIG_XXX) */ +}; + +int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mout); +int snd_hda_multi_out_dig_close(struct hda_codec *codec, struct hda_multi_out *mout); +int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_multi_out *mout, + snd_pcm_substream_t *substream); +int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_out *mout, + unsigned int stream_tag, + unsigned int format, + snd_pcm_substream_t *substream); +int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, struct hda_multi_out *mout); + +/* + * generic codec parser + */ +int snd_hda_parse_generic_codec(struct hda_codec *codec); + +/* + * generic proc interface + */ +#ifdef CONFIG_PROC_FS +int snd_hda_codec_proc_new(struct hda_codec *codec); +#else +static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; } +#endif + +/* + * Misc + */ +struct hda_board_config { + const char *modelname; + int config; + unsigned short pci_vendor; + unsigned short pci_device; +}; + +int snd_hda_check_board_config(struct hda_codec *codec, struct hda_board_config *tbl); +int snd_hda_add_new_ctls(struct hda_codec *codec, snd_kcontrol_new_t *knew); + +/* + * power management + */ +#ifdef CONFIG_PM +int snd_hda_resume_ctls(struct hda_codec *codec, snd_kcontrol_new_t *knew); +int snd_hda_resume_spdif_out(struct hda_codec *codec); +#endif + +/* + * unsolicited event handler + */ + +#define HDA_UNSOL_QUEUE_SIZE 64 + +struct hda_bus_unsolicited { + /* ring buffer */ + u32 queue[HDA_UNSOL_QUEUE_SIZE * 2]; + unsigned int rp, wp; + + /* workqueue */ + struct workqueue_struct *workq; + struct work_struct work; +}; + +#endif /* __SOUND_HDA_LOCAL_H */ diff -Nru a/sound/pci/hda/hda_patch.h b/sound/pci/hda/hda_patch.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/sound/pci/hda/hda_patch.h 2005-01-28 14:22:13 -08:00 @@ -0,0 +1,14 @@ +/* + * HDA Patches - included by hda_codec.c + */ + +/* Realtek codecs */ +extern struct hda_codec_preset snd_hda_preset_realtek[]; +/* C-Media codecs */ +extern struct hda_codec_preset snd_hda_preset_cmedia[]; + +static const struct hda_codec_preset *hda_preset_tables[] = { + snd_hda_preset_realtek, + snd_hda_preset_cmedia, + NULL +}; diff -Nru a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/sound/pci/hda/hda_proc.c 2005-01-28 14:22:13 -08:00 @@ -0,0 +1,298 @@ +/* + * Universal Interface for Intel High Definition Audio Codec + * + * Generic proc interface + * + * Copyright (c) 2004 Takashi Iwai + * + * + * This driver 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 driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include "hda_codec.h" + +static const char *get_wid_type_name(unsigned int wid_value) +{ + static char *names[16] = { + [AC_WID_AUD_OUT] = "Audio Output", + [AC_WID_AUD_IN] = "Audio Input", + [AC_WID_AUD_MIX] = "Audio Mixer", + [AC_WID_AUD_SEL] = "Audio Selector", + [AC_WID_PIN] = "Pin Complex", + [AC_WID_POWER] = "Power Widget", + [AC_WID_VOL_KNB] = "Volume Knob Widget", + [AC_WID_BEEP] = "Beep Generator Widget", + [AC_WID_VENDOR] = "Vendor Defined Widget", + }; + wid_value &= 0xf; + if (names[wid_value]) + return names[wid_value]; + else + return "UNKOWN Widget"; +} + +static void print_amp_caps(snd_info_buffer_t *buffer, + struct hda_codec *codec, hda_nid_t nid, int dir) +{ + unsigned int caps; + if (dir == HDA_OUTPUT) + caps = snd_hda_param_read(codec, nid, AC_PAR_AMP_OUT_CAP); + else + caps = snd_hda_param_read(codec, nid, AC_PAR_AMP_IN_CAP); + if (caps == -1 || caps == 0) { + snd_iprintf(buffer, "N/A\n"); + return; + } + snd_iprintf(buffer, "ofs=0x%02x, nsteps=0x%02x, stepsize=0x%02x, mute=%x\n", + caps & AC_AMPCAP_OFFSET, + (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT, + (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT, + (caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT); +} + +static void print_amp_vals(snd_info_buffer_t *buffer, + struct hda_codec *codec, hda_nid_t nid, + int dir, int stereo) +{ + unsigned int val; + if (stereo) { + val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, + AC_AMP_GET_LEFT | + (dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : + AC_AMP_GET_INPUT)); + snd_iprintf(buffer, "0x%02x ", val); + } + val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, + AC_AMP_GET_RIGHT | + (dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : + AC_AMP_GET_INPUT)); + snd_iprintf(buffer, "0x%02x\n", val); +} + +static void print_pcm_caps(snd_info_buffer_t *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int pcm = snd_hda_param_read(codec, nid, AC_PAR_PCM); + unsigned int stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM); + if (pcm == -1 || stream == -1) { + snd_iprintf(buffer, "N/A\n"); + return; + } + snd_iprintf(buffer, "rates 0x%03x, bits 0x%02x, types 0x%x\n", + pcm & AC_SUPPCM_RATES, (pcm >> 16) & 0xff, stream & 0xf); +} + +static const char *get_jack_location(u32 cfg) +{ + static char *bases[7] = { + "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom", + }; + static unsigned char specials_idx[] = { + 0x07, 0x08, + 0x17, 0x18, 0x19, + 0x37, 0x38 + }; + static char *specials[] = { + "Rear Panel", "Drive Bar", + "Riser", "HDMI", "ATAPI", + "Mobile-In", "Mobile-Out" + }; + int i; + cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT; + if ((cfg & 0x0f) < 7) + return bases[cfg & 0x0f]; + for (i = 0; i < ARRAY_SIZE(specials_idx); i++) { + if (cfg == specials_idx[i]) + return specials[i]; + } + return "UNKNOWN"; +} + +static const char *get_jack_connection(u32 cfg) +{ + static char *names[16] = { + "Unknown", "1/8", "1/4", "ATAPI", + "RCA", "Optical","Digital", "Analog", + "DIN", "XLR", "RJ11", "Comb", + NULL, NULL, NULL, "Other" + }; + cfg = (cfg & AC_DEFCFG_CONN_TYPE) >> AC_DEFCFG_CONN_TYPE_SHIFT; + if (names[cfg]) + return names[cfg]; + else + return "UNKNOWN"; +} + +static const char *get_jack_color(u32 cfg) +{ + static char *names[16] = { + "Unknown", "Black", "Grey", "Blue", + "Green", "Red", "Orange", "Yellow", + "Purple", "Pink", NULL, NULL, + NULL, NULL, "White", "Other", + }; + cfg = (cfg & AC_DEFCFG_COLOR) >> AC_DEFCFG_COLOR_SHIFT; + if (names[cfg]) + return names[cfg]; + else + return "UNKNOWN"; +} + +static void print_pin_caps(snd_info_buffer_t *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + static char *jack_types[16] = { + "Line Out", "Speaker", "HP Out", "CD", + "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand", + "Line In", "Aux", "Mic", "Telephony", + "SPDIF In", "Digitial In", "Reserved", "Other" + }; + static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" }; + unsigned int caps; + + caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); + snd_iprintf(buffer, " Pincap 0x08%x:", caps); + if (caps & AC_PINCAP_IN) + snd_iprintf(buffer, " IN"); + if (caps & AC_PINCAP_OUT) + snd_iprintf(buffer, " OUT"); + if (caps & AC_PINCAP_HP_DRV) + snd_iprintf(buffer, " HP"); + snd_iprintf(buffer, "\n"); + caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); + snd_iprintf(buffer, " Pin Default 0x%08x: %s at %s %s\n", caps, + jack_types[(caps & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT], + jack_locations[(caps >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3], + get_jack_location(caps)); + snd_iprintf(buffer, " Conn = %s, Color = %s\n", + get_jack_connection(caps), + get_jack_color(caps)); +} + + +static void print_codec_info(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + struct hda_codec *codec = entry->private_data; + char buf[32]; + hda_nid_t nid; + int i, nodes; + + snd_hda_get_codec_name(codec, buf, sizeof(buf)); + snd_iprintf(buffer, "Codec: %s\n", buf); + snd_iprintf(buffer, "Address: %d\n", codec->addr); + snd_iprintf(buffer, "Vendor Id: 0x%x\n", codec->vendor_id); + snd_iprintf(buffer, "Subsystem Id: 0x%x\n", codec->subsystem_id); + snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id); + snd_iprintf(buffer, "Default PCM: "); + print_pcm_caps(buffer, codec, codec->afg); + snd_iprintf(buffer, "Default Amp-In caps: "); + print_amp_caps(buffer, codec, codec->afg, HDA_INPUT); + snd_iprintf(buffer, "Default Amp-Out caps: "); + print_amp_caps(buffer, codec, codec->afg, HDA_OUTPUT); + + nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); + if (! nid || nodes < 0) { + snd_iprintf(buffer, "Invalid AFG subtree\n"); + return; + } + for (i = 0; i < nodes; i++, nid++) { + unsigned int wid_caps = snd_hda_param_read(codec, nid, + AC_PAR_AUDIO_WIDGET_CAP); + unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid, + get_wid_type_name(wid_type), wid_caps); + if (wid_caps & AC_WCAP_STEREO) + snd_iprintf(buffer, " Stereo"); + else + snd_iprintf(buffer, " Mono"); + if (wid_caps & AC_WCAP_DIGITAL) + snd_iprintf(buffer, " Digital"); + if (wid_caps & AC_WCAP_IN_AMP) + snd_iprintf(buffer, " Amp-In"); + if (wid_caps & AC_WCAP_OUT_AMP) + snd_iprintf(buffer, " Amp-Out"); + snd_iprintf(buffer, "\n"); + + if (wid_caps & AC_WCAP_IN_AMP) { + snd_iprintf(buffer, " Amp-In caps: "); + print_amp_caps(buffer, codec, nid, HDA_INPUT); + snd_iprintf(buffer, " Amp-In vals: "); + print_amp_vals(buffer, codec, nid, HDA_INPUT, + wid_caps & AC_WCAP_STEREO); + } + if (wid_caps & AC_WCAP_OUT_AMP) { + snd_iprintf(buffer, " Amp-Out caps: "); + print_amp_caps(buffer, codec, nid, HDA_OUTPUT); + snd_iprintf(buffer, " Amp-Out vals: "); + print_amp_vals(buffer, codec, nid, HDA_OUTPUT, + wid_caps & AC_WCAP_STEREO); + } + + if (wid_type == AC_WID_PIN) { + unsigned int pinctls; + print_pin_caps(buffer, codec, nid); + pinctls = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_iprintf(buffer, " Pin-ctls: 0x%02x:", pinctls); + if (pinctls & AC_PINCTL_IN_EN) + snd_iprintf(buffer, " IN"); + if (pinctls & AC_PINCTL_OUT_EN) + snd_iprintf(buffer, " OUT"); + if (pinctls & AC_PINCTL_HP_EN) + snd_iprintf(buffer, " HP"); + snd_iprintf(buffer, "\n"); + } + + if ((wid_type == AC_WID_AUD_OUT || wid_type == AC_WID_AUD_IN) && + (wid_caps & AC_WCAP_FORMAT_OVRD)) { + snd_iprintf(buffer, " PCM: "); + print_pcm_caps(buffer, codec, nid); + } + + if (wid_caps & AC_WCAP_CONN_LIST) { + hda_nid_t conn[HDA_MAX_CONNECTIONS]; + int c, conn_len; + conn_len = snd_hda_get_connections(codec, nid, conn, + HDA_MAX_CONNECTIONS); + snd_iprintf(buffer, " Connection: %d\n", conn_len); + snd_iprintf(buffer, " "); + for (c = 0; c < conn_len; c++) + snd_iprintf(buffer, " 0x%02x", conn[c]); + snd_iprintf(buffer, "\n"); + } + } +} + +/* + * create a proc read + */ +int snd_hda_codec_proc_new(struct hda_codec *codec) +{ + char name[32]; + snd_info_entry_t *entry; + int err; + + snprintf(name, sizeof(name), "codec#%d", codec->addr); + err = snd_card_proc_new(codec->bus->card, name, &entry); + if (err < 0) + return err; + + snd_info_set_text_ops(entry, codec, 32 * 1024, print_codec_info); + return 0; +} + diff -Nru a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/sound/pci/hda/patch_cmedia.c 2005-01-28 14:22:13 -08:00 @@ -0,0 +1,614 @@ +/* + * Universal Interface for Intel High Definition Audio Codec + * + * HD audio interface patch for C-Media CMI9880 + * + * Copyright (c) 2004 Takashi Iwai + * + * + * This driver 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 driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include "hda_codec.h" +#include "hda_local.h" + + +/* board config type */ +enum { + CMI_MINIMAL, /* back 3-jack */ + CMI_MIN_FP, /* back 3-jack + front-panel 2-jack */ + CMI_FULL, /* back 6-jack + front-panel 2-jack */ + CMI_FULL_DIG, /* back 6-jack + front-panel 2-jack + digital I/O */ + CMI_ALLOUT, /* back 5-jack + front-panel 2-jack + digital out */ +}; + +struct cmi_spec { + int board_config; + unsigned int surr_switch: 1; /* switchable line,mic */ + unsigned int no_line_in: 1; /* no line-in (5-jack) */ + unsigned int front_panel: 1; /* has front-panel 2-jack */ + + /* playback */ + struct hda_multi_out multiout; + + /* capture */ + hda_nid_t *adc_nids; + hda_nid_t dig_in_nid; + + /* capture source */ + const struct hda_input_mux *input_mux; + unsigned int cur_mux[2]; + + /* channel mode */ + unsigned int num_ch_modes; + unsigned int cur_ch_mode; + const struct cmi_channel_mode *channel_modes; + + struct hda_pcm pcm_rec[2]; /* PCM information */ +}; + +/* + * input MUX + */ +static int cmi_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cmi_spec *spec = codec->spec; + return snd_hda_input_mux_info(spec->input_mux, uinfo); +} + +static int cmi_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cmi_spec *spec = codec->spec; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + + ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; + return 0; +} + +static int cmi_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cmi_spec *spec = codec->spec; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + + return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, + spec->adc_nids[adc_idx], &spec->cur_mux[adc_idx]); +} + +/* + * shared line-in, mic for surrounds + */ + +/* 3-stack / 2 channel */ +static struct hda_verb cmi9880_ch2_init[] = { + /* set line-in PIN for input */ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* set mic PIN for input, also enable vref */ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* route front PCM (DAC1) to HP */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, + {} +}; + +/* 3-stack / 6 channel */ +static struct hda_verb cmi9880_ch6_init[] = { + /* set line-in PIN for input */ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* set mic PIN for input, also enable vref */ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* route front PCM (DAC1) to HP */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, + {} +}; + +/* 3-stack+front / 8 channel */ +static struct hda_verb cmi9880_ch8_init[] = { + /* set line-in PIN for input */ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* set mic PIN for input, also enable vref */ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* route rear-surround PCM (DAC4) to HP */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x03 }, + {} +}; + +struct cmi_channel_mode { + unsigned int channels; + const struct hda_verb *sequence; +}; + +static struct cmi_channel_mode cmi9880_channel_modes[3] = { + { 2, cmi9880_ch2_init }, + { 6, cmi9880_ch6_init }, + { 8, cmi9880_ch8_init }, +}; + +static int cmi_ch_mode_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cmi_spec *spec = codec->spec; + + snd_assert(spec->channel_modes, return -EINVAL); + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = spec->num_ch_modes; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + sprintf(uinfo->value.enumerated.name, "%dch", + spec->channel_modes[uinfo->value.enumerated.item].channels); + return 0; +} + +static int cmi_ch_mode_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cmi_spec *spec = codec->spec; + + ucontrol->value.enumerated.item[0] = spec->cur_ch_mode; + return 0; +} + +static int cmi_ch_mode_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cmi_spec *spec = codec->spec; + + snd_assert(spec->channel_modes, return -EINVAL); + if (ucontrol->value.enumerated.item[0] >= spec->num_ch_modes) + ucontrol->value.enumerated.item[0] = spec->num_ch_modes; + if (ucontrol->value.enumerated.item[0] == spec->cur_ch_mode && + ! codec->in_resume) + return 0; + + spec->cur_ch_mode = ucontrol->value.enumerated.item[0]; + snd_hda_sequence_write(codec, spec->channel_modes[spec->cur_ch_mode].sequence); + spec->multiout.max_channels = spec->channel_modes[spec->cur_ch_mode].channels; + return 1; +} + +/* + */ +static snd_kcontrol_new_t cmi9880_basic_mixer[] = { + /* CMI9880 has no playback volumes! */ + HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), /* front */ + HDA_CODEC_MUTE("Surround Playback Switch", 0x04, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Side Playback Switch", 0x06, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + * FIXME: the controls appear in the "playback" view! + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = cmi_mux_enum_info, + .get = cmi_mux_enum_get, + .put = cmi_mux_enum_put, + }, + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0, HDA_INPUT), + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x23, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x23, 0, HDA_OUTPUT), + { } /* end */ +}; + +/* + * shared I/O pins + */ +static snd_kcontrol_new_t cmi9880_ch_mode_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = cmi_ch_mode_info, + .get = cmi_ch_mode_get, + .put = cmi_ch_mode_put, + }, + { } /* end */ +}; + +/* AUD-in selections: + * 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x1f 0x20 + */ +static struct hda_input_mux cmi9880_basic_mux = { + .num_items = 4, + .items = { + { "Front Mic", 0x5 }, + { "Rear Mic", 0x2 }, + { "Line", 0x1 }, + { "CD", 0x7 }, + } +}; + +static struct hda_input_mux cmi9880_no_line_mux = { + .num_items = 3, + .items = { + { "Front Mic", 0x5 }, + { "Rear Mic", 0x2 }, + { "CD", 0x7 }, + } +}; + +/* front, rear, clfe, rear_surr */ +static hda_nid_t cmi9880_dac_nids[4] = { + 0x03, 0x04, 0x05, 0x06 +}; +/* ADC0, ADC1 */ +static hda_nid_t cmi9880_adc_nids[2] = { + 0x08, 0x09 +}; + +#define CMI_DIG_OUT_NID 0x07 +#define CMI_DIG_IN_NID 0x0a + +/* + */ +static struct hda_verb cmi9880_basic_init[] = { + /* port-D for line out (rear panel) */ + { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + /* port-E for HP out (front panel) */ + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + /* route front PCM to HP */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* port-A for surround (rear panel) */ + { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + /* port-G for CLFE (rear panel) */ + { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + /* port-H for side (rear panel) */ + { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + /* port-C for line-in (rear panel) */ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* port-B for mic-in (rear panel) with vref */ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* port-F for mic-in (front panel) with vref */ + { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* CD-in */ + { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* route front mic to ADC1/2 */ + { 0x08, AC_VERB_SET_CONNECT_SEL, 0x05 }, + { 0x09, AC_VERB_SET_CONNECT_SEL, 0x05 }, + {} /* terminator */ +}; + +static struct hda_verb cmi9880_allout_init[] = { + /* port-D for line out (rear panel) */ + { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + /* port-E for HP out (front panel) */ + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + /* route front PCM to HP */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* port-A for side (rear panel) */ + { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + /* port-G for CLFE (rear panel) */ + { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + /* port-C for surround (rear panel) */ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + /* port-B for mic-in (rear panel) with vref */ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* port-F for mic-in (front panel) with vref */ + { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* CD-in */ + { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* route front mic to ADC1/2 */ + { 0x08, AC_VERB_SET_CONNECT_SEL, 0x05 }, + { 0x09, AC_VERB_SET_CONNECT_SEL, 0x05 }, + {} /* terminator */ +}; + +/* + */ +static int cmi9880_build_controls(struct hda_codec *codec) +{ + struct cmi_spec *spec = codec->spec; + int err; + + err = snd_hda_add_new_ctls(codec, cmi9880_basic_mixer); + if (err < 0) + return err; + if (spec->surr_switch) { + err = snd_hda_add_new_ctls(codec, cmi9880_ch_mode_mixer); + if (err < 0) + return err; + } + if (spec->multiout.dig_out_nid) { + err = snd_hda_create_spdif_out_ctls(codec, CMI_DIG_OUT_NID); + if (err < 0) + return err; + } + /* TODO: digital-in */ + return 0; +} + +static int cmi9880_init(struct hda_codec *codec) +{ + struct cmi_spec *spec = codec->spec; + if (spec->board_config == CMI_ALLOUT) + snd_hda_sequence_write(codec, cmi9880_allout_init); + else + snd_hda_sequence_write(codec, cmi9880_basic_init); + return 0; +} + +#ifdef CONFIG_PM +/* + * resume + */ +static int cmi9880_resume(struct hda_codec *codec, unsigned int state) +{ + struct cmi_spec *spec = codec->spec; + + cmi9880_init(codec); + snd_hda_resume_ctls(codec, cmi9880_basic_mixer); + if (spec->surr_switch) + snd_hda_resume_ctls(codec, cmi9880_ch_mode_mixer); + if (spec->multiout.dig_out_nid) + snd_hda_resume_spdif_out(codec); + + return 0; +} +#endif + +/* + * Analog playback callbacks + */ +static int cmi9880_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + snd_pcm_substream_t *substream) +{ + struct cmi_spec *spec = codec->spec; + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream); +} + +static int cmi9880_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + snd_pcm_substream_t *substream) +{ + struct cmi_spec *spec = codec->spec; + return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, + format, substream); +} + +static int cmi9880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + snd_pcm_substream_t *substream) +{ + struct cmi_spec *spec = codec->spec; + return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); +} + +/* + * Digital out + */ +static int cmi9880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + snd_pcm_substream_t *substream) +{ + struct cmi_spec *spec = codec->spec; + return snd_hda_multi_out_dig_open(codec, &spec->multiout); +} + +static int cmi9880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + snd_pcm_substream_t *substream) +{ + struct cmi_spec *spec = codec->spec; + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +/* + * Analog capture + */ +static int cmi9880_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + snd_pcm_substream_t *substream) +{ + struct cmi_spec *spec = codec->spec; + + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], + stream_tag, 0, format); + return 0; +} + +static int cmi9880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + snd_pcm_substream_t *substream) +{ + struct cmi_spec *spec = codec->spec; + + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0); + return 0; +} + + +/* + */ +static struct hda_pcm_stream cmi9880_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 8, + .nid = 0x03, /* NID to query formats and rates */ + .ops = { + .open = cmi9880_playback_pcm_open, + .prepare = cmi9880_playback_pcm_prepare, + .cleanup = cmi9880_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream cmi9880_pcm_analog_capture = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x08, /* NID to query formats and rates */ + .ops = { + .prepare = cmi9880_capture_pcm_prepare, + .cleanup = cmi9880_capture_pcm_cleanup + }, +}; + +static struct hda_pcm_stream cmi9880_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in cmi9880_build_pcms */ + .ops = { + .open = cmi9880_dig_playback_pcm_open, + .close = cmi9880_dig_playback_pcm_close + }, +}; + +static struct hda_pcm_stream cmi9880_pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in cmi9880_build_pcms */ +}; + +static int cmi9880_build_pcms(struct hda_codec *codec) +{ + struct cmi_spec *spec = codec->spec; + struct hda_pcm *info = spec->pcm_rec; + + codec->num_pcms = 1; + codec->pcm_info = info; + + info->name = "CMI9880"; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cmi9880_pcm_analog_playback; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = cmi9880_pcm_analog_capture; + + if (spec->multiout.dig_out_nid || spec->dig_in_nid) { + codec->num_pcms++; + info++; + info->name = "CMI9880 Digital"; + if (spec->multiout.dig_out_nid) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cmi9880_pcm_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; + } + if (spec->dig_in_nid) { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = cmi9880_pcm_digital_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; + } + } + + return 0; +} + +static void cmi9880_free(struct hda_codec *codec) +{ + kfree(codec->spec); +} + +/* + */ + +static struct hda_board_config cmi9880_cfg_tbl[] = { + { .modelname = "minimal", .config = CMI_MINIMAL }, + { .modelname = "min_fp", .config = CMI_MIN_FP }, + { .modelname = "full", .config = CMI_FULL }, + { .modelname = "full_dig", .config = CMI_FULL_DIG }, + { .modelname = "allout", .config = CMI_ALLOUT }, + {} /* terminator */ +}; + +static struct hda_codec_ops cmi9880_patch_ops = { + .build_controls = cmi9880_build_controls, + .build_pcms = cmi9880_build_pcms, + .init = cmi9880_init, + .free = cmi9880_free, +#ifdef CONFIG_PM + .resume = cmi9880_resume, +#endif +}; + +static int patch_cmi9880(struct hda_codec *codec) +{ + struct cmi_spec *spec; + + spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + spec->board_config = snd_hda_check_board_config(codec, cmi9880_cfg_tbl); + if (spec->board_config < 0) { + snd_printd(KERN_INFO "hda_codec: Unknown model for CMI9880\n"); + spec->board_config = CMI_MINIMAL; + } + + switch (spec->board_config) { + case CMI_MINIMAL: + case CMI_MIN_FP: + spec->surr_switch = 1; + if (spec->board_config == CMI_MINIMAL) + spec->num_ch_modes = 2; + else { + spec->front_panel = 1; + spec->num_ch_modes = 3; + } + spec->channel_modes = cmi9880_channel_modes; + spec->multiout.max_channels = cmi9880_channel_modes[0].channels; + spec->input_mux = &cmi9880_basic_mux; + break; + case CMI_FULL: + case CMI_FULL_DIG: + spec->front_panel = 1; + spec->multiout.max_channels = 8; + spec->input_mux = &cmi9880_basic_mux; + if (spec->board_config == CMI_FULL_DIG) { + spec->multiout.dig_out_nid = CMI_DIG_OUT_NID; + spec->dig_in_nid = CMI_DIG_IN_NID; + } + break; + case CMI_ALLOUT: + spec->front_panel = 1; + spec->multiout.max_channels = 8; + spec->no_line_in = 1; + spec->input_mux = &cmi9880_no_line_mux; + spec->multiout.dig_out_nid = CMI_DIG_OUT_NID; + break; + } + + spec->multiout.num_dacs = 4; + spec->multiout.dac_nids = cmi9880_dac_nids; + + spec->adc_nids = cmi9880_adc_nids; + + codec->patch_ops = cmi9880_patch_ops; + + return 0; +} + +/* + * patch entries + */ +struct hda_codec_preset snd_hda_preset_cmedia[] = { + { .id = 0x434d4980, .name = "CMI9880", .patch = patch_cmi9880 }, + {} /* terminator */ +}; diff -Nru a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/sound/pci/hda/patch_realtek.c 2005-01-28 14:22:13 -08:00 @@ -0,0 +1,1174 @@ +/* + * Universal Interface for Intel High Definition Audio Codec + * + * HD audio interface patch for ALC 260/880 codecs + * + * Copyright (c) 2004 PeiSen Hou + * Takashi Iwai + * + * This driver 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 driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include "hda_codec.h" +#include "hda_local.h" + + +/* ALC880 board config type */ +enum { + ALC880_MINIMAL, + ALC880_3ST, + ALC880_3ST_DIG, + ALC880_5ST, + ALC880_5ST_DIG, + ALC880_W810, +}; + +struct alc_spec { + /* codec parameterization */ + unsigned int front_panel: 1; + + snd_kcontrol_new_t* mixers[2]; + unsigned int num_mixers; + + struct hda_verb *init_verbs; + + char* stream_name_analog; + struct hda_pcm_stream *stream_analog_playback; + struct hda_pcm_stream *stream_analog_capture; + + char* stream_name_digital; + struct hda_pcm_stream *stream_digital_playback; + struct hda_pcm_stream *stream_digital_capture; + + /* playback */ + struct hda_multi_out multiout; + + /* capture */ + unsigned int num_adc_nids; + hda_nid_t *adc_nids; + hda_nid_t dig_in_nid; + + /* capture source */ + const struct hda_input_mux *input_mux; + unsigned int cur_mux[3]; + + /* channel model */ + const struct alc_channel_mode *channel_mode; + int num_channel_mode; + + /* PCM information */ + struct hda_pcm pcm_rec[2]; +}; + +/* DAC/ADC assignment */ + +static hda_nid_t alc880_dac_nids[4] = { + /* front, rear, clfe, rear_surr */ + 0x02, 0x05, 0x04, 0x03 +}; + +static hda_nid_t alc880_w810_dac_nids[3] = { + /* front, rear/surround, clfe */ + 0x02, 0x03, 0x04 +}; + +static hda_nid_t alc880_adc_nids[3] = { + /* ADC0-2 */ + 0x07, 0x08, 0x09, +}; + +#define ALC880_DIGOUT_NID 0x06 +#define ALC880_DIGIN_NID 0x0a + +static hda_nid_t alc260_dac_nids[1] = { + /* front */ + 0x02, +}; + +static hda_nid_t alc260_adc_nids[2] = { + /* ADC0-1 */ + 0x04, 0x05, +}; + +#define ALC260_DIGOUT_NID 0x03 +#define ALC260_DIGIN_NID 0x06 + +static struct hda_input_mux alc880_capture_source = { + .num_items = 4, + .items = { + { "Mic-1", 0x0 }, + { "Mic-2", 0x3 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, +}; + +static struct hda_input_mux alc260_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, +}; + +/* + * input MUX handling + */ +static int alc_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + return snd_hda_input_mux_info(spec->input_mux, uinfo); +} + +static int alc_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + + ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; + return 0; +} + +static int alc_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, + spec->adc_nids[adc_idx], &spec->cur_mux[adc_idx]); +} + +/* + * channel mode setting + */ +struct alc_channel_mode { + int channels; + const struct hda_verb *sequence; +}; + + +/* + * channel source setting (2/6 channel selection for 3-stack) + */ + +/* + * set the path ways for 2 channel output + * need to set the codec line out and mic 1 pin widgets to inputs + */ +static struct hda_verb alc880_threestack_ch2_init[] = { + /* set pin widget 1Ah (line in) for input */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* set pin widget 18h (mic1) for input, for mic also enable the vref */ + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* mute the output for Line In PW */ + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, + /* mute for Mic1 PW */ + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, + { } /* end */ +}; + +/* + * 6ch mode + * need to set the codec line out and mic 1 pin widgets to outputs + */ +static struct hda_verb alc880_threestack_ch6_init[] = { + /* set pin widget 1Ah (line in) for output */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* set pin widget 18h (mic1) for output */ + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* unmute the output for Line In PW */ + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 }, + /* unmute for Mic1 PW */ + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 }, + /* for rear channel output using Line In 1 + * set select widget connection (nid = 0x12) - to summer node + * for rear NID = 0x0f...offset 3 in connection list + */ + { 0x12, AC_VERB_SET_CONNECT_SEL, 0x3 }, + /* for Mic1 - retask for center/lfe */ + /* set select widget connection (nid = 0x10) - to summer node for + * front CLFE NID = 0x0e...offset 2 in connection list + */ + { 0x10, AC_VERB_SET_CONNECT_SEL, 0x2 }, + { } /* end */ +}; + +static struct alc_channel_mode alc880_threestack_modes[2] = { + { 2, alc880_threestack_ch2_init }, + { 6, alc880_threestack_ch6_init }, +}; + + +/* + * channel source setting (6/8 channel selection for 5-stack) + */ + +/* set the path ways for 6 channel output + * need to set the codec line out and mic 1 pin widgets to inputs + */ +static struct hda_verb alc880_fivestack_ch6_init[] = { + /* set pin widget 1Ah (line in) for input */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* mute the output for Line In PW */ + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, + { } /* end */ +}; + +/* need to set the codec line out and mic 1 pin widgets to outputs */ +static struct hda_verb alc880_fivestack_ch8_init[] = { + /* set pin widget 1Ah (line in) for output */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* unmute the output for Line In PW */ + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 }, + /* output for surround channel output using Line In 1 */ + /* set select widget connection (nid = 0x12) - to summer node + * for surr_rear NID = 0x0d...offset 1 in connection list + */ + { 0x12, AC_VERB_SET_CONNECT_SEL, 0x1 }, + { } /* end */ +}; + +static struct alc_channel_mode alc880_fivestack_modes[2] = { + { 6, alc880_fivestack_ch6_init }, + { 8, alc880_fivestack_ch8_init }, +}; + +/* + * channel source setting for W810 system + * + * W810 has rear IO for: + * Front (DAC 02) + * Surround (DAC 03) + * Center/LFE (DAC 04) + * Digital out (06) + * + * The system also has a pair of internal speakers, and a headphone jack. + * These are both connected to Line2 on the codec, hence to DAC 02. + * + * There is a variable resistor to control the speaker or headphone + * volume. This is a hardware-only device without a software API. + * + * Plugging headphones in will disable the internal speakers. This is + * implemented in hardware, not via the driver using jack sense. In + * a similar fashion, plugging into the rear socket marked "front" will + * disable both the speakers and headphones. + * + * For input, there's a microphone jack, and an "audio in" jack. + * These may not do anything useful with this driver yet, because I + * haven't setup any initialization verbs for these yet... + */ + +static struct alc_channel_mode alc880_w810_modes[1] = { + { 6, NULL } +}; + +/* + */ +static int alc880_ch_mode_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + + snd_assert(spec->channel_mode, return -ENXIO); + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item >= 2) + uinfo->value.enumerated.item = 1; + sprintf(uinfo->value.enumerated.name, "%dch", + spec->channel_mode[uinfo->value.enumerated.item].channels); + return 0; +} + +static int alc880_ch_mode_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + + snd_assert(spec->channel_mode, return -ENXIO); + ucontrol->value.enumerated.item[0] = + (spec->multiout.max_channels == spec->channel_mode[0].channels) ? 0 : 1; + return 0; +} + +static int alc880_ch_mode_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + int mode; + + snd_assert(spec->channel_mode, return -ENXIO); + mode = ucontrol->value.enumerated.item[0] ? 1 : 0; + if (spec->multiout.max_channels == spec->channel_mode[mode].channels && + ! codec->in_resume) + return 0; + + /* change the current channel setting */ + spec->multiout.max_channels = spec->channel_mode[mode].channels; + if (spec->channel_mode[mode].sequence) + snd_hda_sequence_write(codec, spec->channel_mode[mode].sequence); + + return 1; +} + + +/* + */ + +static snd_kcontrol_new_t alc880_base_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x1a, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x18, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x18, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic-1 Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic-1 Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic-2 Playback Volume", 0x0b, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("Mic-2 Playback Switch", 0x0b, 0x3, HDA_INPUT), + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x19, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + * FIXME: the controls appear in the "playback" view! + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc_mux_enum_info, + .get = alc_mux_enum_get, + .put = alc_mux_enum_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc880_ch_mode_info, + .get = alc880_ch_mode_get, + .put = alc880_ch_mode_put, + }, + { } /* end */ +}; + +static snd_kcontrol_new_t alc880_side_mixer[] = { + HDA_CODEC_VOLUME("Side Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Side Playback Switch", 0x19, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +static snd_kcontrol_new_t alc880_w810_base_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x16, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + * FIXME: the controls appear in the "playback" view! + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 3, + .info = alc_mux_enum_info, + .get = alc_mux_enum_get, + .put = alc_mux_enum_put, + }, + { } /* end */ +}; + +/* + */ +static int alc_build_controls(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int err; + int i; + + for (i = 0; i < spec->num_mixers; i++) { + err = snd_hda_add_new_ctls(codec, spec->mixers[i]); + if (err < 0) + return err; + } + + if (spec->multiout.dig_out_nid) { + err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); + if (err < 0) + return err; + } + return 0; +} + +/* + * initialize the codec volumes, etc + */ + +static struct hda_verb alc880_init_verbs_three_stack[] = { + /* Line In pin widget for input */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + /* CD pin widget for input */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + /* Mic1 (rear panel) pin widget for input and vref at 80% */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + /* Mic2 (front panel) pin widget for input and vref at 80% */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + /* unmute amp left and right */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, + /* set connection select to line in (default select for this ADC) */ + {0x07, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* unmute front mixer amp left (volume = 0) */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* mute pin widget amp left and right (no gain on this amp) */ + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + /* unmute rear mixer amp left and right (volume = 0) */ + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* mute pin widget amp left and right (no gain on this amp) */ + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + /* unmute rear mixer amp left and right (volume = 0) */ + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* mute pin widget amp left and right (no gain on this amp) */ + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + + /* using rear surround as the path for headphone output */ + /* unmute rear surround mixer amp left and right (volume = 0) */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* PASD 3 stack boards use the Mic 2 as the headphone output */ + /* need to program the selector associated with the Mic 2 pin widget to + * surround path (index 0x01) for headphone output */ + {0x11, AC_VERB_SET_CONNECT_SEL, 0x01}, + /* mute pin widget amp left and right (no gain on this amp) */ + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + /* need to retask the Mic 2 pin widget to output */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) for mixer widget(nid=0x0B) + * to support the input path of analog loopback + * Note: PASD motherboards uses the Line In 2 as the input for front panel + * mic (mic 2) + */ + /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03 */ + /* unmute CD */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, + /* unmute Line In */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, + /* unmute Mic 1 */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + /* unmute Line In 2 (for PASD boards Mic 2) */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, + + /* Unmute input amps for the line out paths to support the output path of + * analog loopback + * the mixers on the output path has 2 inputs, one from the DAC and one + * from the mixer + */ + /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */ + /* Unmute Front out path */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + /* Unmute Surround (used as HP) out path */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + /* Unmute C/LFE out path */ + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + /* Unmute rear Surround out path */ + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + + { } +}; + +static struct hda_verb alc880_init_verbs_five_stack[] = { + /* Line In pin widget for input */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + /* CD pin widget for input */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + /* Mic1 (rear panel) pin widget for input and vref at 80% */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + /* Mic2 (front panel) pin widget for input and vref at 80% */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + /* unmute amp left and right */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, + /* set connection select to line in (default select for this ADC) */ + {0x07, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* unmute front mixer amp left and right (volume = 0) */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* mute pin widget amp left and right (no gain on this amp) */ + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + /* five rear and clfe */ + /* unmute rear mixer amp left and right (volume = 0) */ + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* mute pin widget amp left and right (no gain on this amp) */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + /* unmute clfe mixer amp left and right (volume = 0) */ + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* mute pin widget amp left and right (no gain on this amp) */ + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + + /* using rear surround as the path for headphone output */ + /* unmute rear surround mixer amp left and right (volume = 0) */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* PASD 3 stack boards use the Mic 2 as the headphone output */ + /* need to program the selector associated with the Mic 2 pin widget to + * surround path (index 0x01) for headphone output + */ + {0x11, AC_VERB_SET_CONNECT_SEL, 0x01}, + /* mute pin widget amp left and right (no gain on this amp) */ + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + /* need to retask the Mic 2 pin widget to output */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) for mixer + * widget(nid=0x0B) to support the input path of analog loopback + */ + /* Note: PASD motherboards uses the Line In 2 as the input for front panel mic (mic 2) */ + /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03*/ + /* unmute CD */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, + /* unmute Line In */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, + /* unmute Mic 1 */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + /* unmute Line In 2 (for PASD boards Mic 2) */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, + + /* Unmute input amps for the line out paths to support the output path of + * analog loopback + * the mixers on the output path has 2 inputs, one from the DAC and + * one from the mixer + */ + /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */ + /* Unmute Front out path */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + /* Unmute Surround (used as HP) out path */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + /* Unmute C/LFE out path */ + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + /* Unmute rear Surround out path */ + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + + { } +}; + +static struct hda_verb alc880_w810_init_verbs[] = { + /* front channel selector/amp: input 0: DAC: unmuted, (no volume selection) */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, + + /* front channel selector/amp: input 1: capture mix: muted, (no volume selection) */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7180}, + + /* front channel selector/amp: output 0: unmuted, max volume */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + + /* front out pin: muted, (no volume selection) */ + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + + /* front out pin: NOT headphone enable, out enable, vref disabled */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + + + /* surround channel selector/amp: input 0: DAC: unmuted, (no volume selection) */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, + + /* surround channel selector/amp: input 1: capture mix: muted, (no volume selection) */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7180}, + + /* surround channel selector/amp: output 0: unmuted, max volume */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + + /* surround out pin: muted, (no volume selection) */ + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + + /* surround out pin: NOT headphone enable, out enable, vref disabled */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + + + /* c/lfe channel selector/amp: input 0: DAC: unmuted, (no volume selection) */ + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, + + /* c/lfe channel selector/amp: input 1: capture mix: muted, (no volume selection) */ + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0x7180}, + + /* c/lfe channel selector/amp: output 0: unmuted, max volume */ + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + + /* c/lfe out pin: muted, (no volume selection) */ + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + + /* c/lfe out pin: NOT headphone enable, out enable, vref disabled */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + + + /* hphone/speaker input selector: front DAC */ + {0x13, AC_VERB_SET_CONNECT_SEL, 0x0}, + + /* hphone/speaker out pin: muted, (no volume selection) */ + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + + /* hphone/speaker out pin: NOT headphone enable, out enable, vref disabled */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + + + { } +}; + +static int alc_init(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + snd_hda_sequence_write(codec, spec->init_verbs); + return 0; +} + +#ifdef CONFIG_PM +/* + * resume + */ +static int alc_resume(struct hda_codec *codec, unsigned int state) +{ + struct alc_spec *spec = codec->spec; + int i; + + alc_init(codec); + for (i = 0; i < spec->num_mixers; i++) { + snd_hda_resume_ctls(codec, spec->mixers[i]); + } + if (spec->multiout.dig_out_nid) + snd_hda_resume_spdif_out(codec); + + return 0; +} +#endif + +/* + * Analog playback callbacks + */ +static int alc880_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + snd_pcm_substream_t *substream) +{ + struct alc_spec *spec = codec->spec; + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream); +} + +static int alc880_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + snd_pcm_substream_t *substream) +{ + struct alc_spec *spec = codec->spec; + return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, + format, substream); +} + +static int alc880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + snd_pcm_substream_t *substream) +{ + struct alc_spec *spec = codec->spec; + return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); +} + +/* + * Digital out + */ +static int alc880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + snd_pcm_substream_t *substream) +{ + struct alc_spec *spec = codec->spec; + return snd_hda_multi_out_dig_open(codec, &spec->multiout); +} + +static int alc880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + snd_pcm_substream_t *substream) +{ + struct alc_spec *spec = codec->spec; + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +/* + * Analog capture + */ +static int alc880_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + snd_pcm_substream_t *substream) +{ + struct alc_spec *spec = codec->spec; + + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], + stream_tag, 0, format); + return 0; +} + +static int alc880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + snd_pcm_substream_t *substream) +{ + struct alc_spec *spec = codec->spec; + + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0); + return 0; +} + + +/* + */ +static struct hda_pcm_stream alc880_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 8, + .nid = 0x02, /* NID to query formats and rates */ + .ops = { + .open = alc880_playback_pcm_open, + .prepare = alc880_playback_pcm_prepare, + .cleanup = alc880_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream alc880_pcm_analog_capture = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x07, /* NID to query formats and rates */ + .ops = { + .prepare = alc880_capture_pcm_prepare, + .cleanup = alc880_capture_pcm_cleanup + }, +}; + +static struct hda_pcm_stream alc880_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in alc_build_pcms */ + .ops = { + .open = alc880_dig_playback_pcm_open, + .close = alc880_dig_playback_pcm_close + }, +}; + +static struct hda_pcm_stream alc880_pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in alc_build_pcms */ +}; + +static int alc_build_pcms(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct hda_pcm *info = spec->pcm_rec; + int i; + + codec->num_pcms = 1; + codec->pcm_info = info; + + info->name = spec->stream_name_analog; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback); + info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); + + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0; + for (i = 0; i < spec->num_channel_mode; i++) { + if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels; + } + } + + if (spec->multiout.dig_out_nid || spec->dig_in_nid) { + codec->num_pcms++; + info++; + info->name = spec->stream_name_digital; + if (spec->multiout.dig_out_nid) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_digital_playback); + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; + } + if (spec->dig_in_nid) { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_digital_capture); + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; + } + } + + return 0; +} + +static void alc_free(struct hda_codec *codec) +{ + kfree(codec->spec); +} + +/* + */ +static struct hda_codec_ops alc_patch_ops = { + .build_controls = alc_build_controls, + .build_pcms = alc_build_pcms, + .init = alc_init, + .free = alc_free, +#ifdef CONFIG_PM + .resume = alc_resume, +#endif +}; + +/* + */ + +static struct hda_board_config alc880_cfg_tbl[] = { + /* Back 3 jack, front 2 jack */ + { .modelname = "3stack", .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe200, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe201, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe202, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe203, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe204, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe205, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe206, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe207, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe208, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe209, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe20a, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe20b, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe20c, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe20d, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe20e, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe20f, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe210, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe211, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe214, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe302, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe303, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe304, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe306, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe307, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xe404, .config = ALC880_3ST }, + { .pci_vendor = 0x8086, .pci_device = 0xa101, .config = ALC880_3ST }, + { .pci_vendor = 0x107b, .pci_device = 0x3031, .config = ALC880_3ST }, + { .pci_vendor = 0x107b, .pci_device = 0x4036, .config = ALC880_3ST }, + { .pci_vendor = 0x107b, .pci_device = 0x4037, .config = ALC880_3ST }, + { .pci_vendor = 0x107b, .pci_device = 0x4038, .config = ALC880_3ST }, + { .pci_vendor = 0x107b, .pci_device = 0x4040, .config = ALC880_3ST }, + { .pci_vendor = 0x107b, .pci_device = 0x4041, .config = ALC880_3ST }, + + /* Back 3 jack, front 2 jack (Internal add Aux-In) */ + { .pci_vendor = 0x1025, .pci_device = 0xe310, .config = ALC880_3ST }, + + /* Back 3 jack plus 1 SPDIF out jack, front 2 jack */ + { .modelname = "3stack-digout", .config = ALC880_3ST_DIG }, + { .pci_vendor = 0x8086, .pci_device = 0xe308, .config = ALC880_3ST_DIG }, + + /* Back 3 jack plus 1 SPDIF out jack, front 2 jack (Internal add Aux-In)*/ + { .pci_vendor = 0x8086, .pci_device = 0xe305, .config = ALC880_3ST_DIG }, + { .pci_vendor = 0x8086, .pci_device = 0xd402, .config = ALC880_3ST_DIG }, + { .pci_vendor = 0x1025, .pci_device = 0xe309, .config = ALC880_3ST_DIG }, + + /* Back 5 jack, front 2 jack */ + { .modelname = "5stack", .config = ALC880_5ST }, + { .pci_vendor = 0x107b, .pci_device = 0x3033, .config = ALC880_5ST }, + { .pci_vendor = 0x107b, .pci_device = 0x4039, .config = ALC880_5ST }, + { .pci_vendor = 0x107b, .pci_device = 0x3032, .config = ALC880_5ST }, + { .pci_vendor = 0x103c, .pci_device = 0x2a09, .config = ALC880_5ST }, + + /* Back 5 jack plus 1 SPDIF out jack, front 2 jack */ + { .modelname = "5stack-digout", .config = ALC880_5ST_DIG }, + { .pci_vendor = 0x8086, .pci_device = 0xe224, .config = ALC880_5ST_DIG }, + { .pci_vendor = 0x8086, .pci_device = 0xe400, .config = ALC880_5ST_DIG }, + { .pci_vendor = 0x8086, .pci_device = 0xe401, .config = ALC880_5ST_DIG }, + { .pci_vendor = 0x8086, .pci_device = 0xe402, .config = ALC880_5ST_DIG }, + { .pci_vendor = 0x8086, .pci_device = 0xd400, .config = ALC880_5ST_DIG }, + { .pci_vendor = 0x8086, .pci_device = 0xd401, .config = ALC880_5ST_DIG }, + { .pci_vendor = 0x8086, .pci_device = 0xa100, .config = ALC880_5ST_DIG }, + { .pci_vendor = 0x1565, .pci_device = 0x8202, .config = ALC880_5ST_DIG }, + + { .modelname = "w810", .config = ALC880_W810 }, + { .pci_vendor = 0x161f, .pci_device = 0x203d, .config = ALC880_W810 }, + + {} +}; + +static int patch_alc880(struct hda_codec *codec) +{ + struct alc_spec *spec; + int board_config; + + spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + board_config = snd_hda_check_board_config(codec, alc880_cfg_tbl); + if (board_config < 0) { + snd_printd(KERN_INFO "hda_codec: Unknown model for ALC880\n"); + board_config = ALC880_MINIMAL; + } + + switch (board_config) { + case ALC880_W810: + spec->mixers[spec->num_mixers] = alc880_w810_base_mixer; + spec->num_mixers++; + break; + default: + spec->mixers[spec->num_mixers] = alc880_base_mixer; + spec->num_mixers++; + break; + } + + switch (board_config) { + case ALC880_3ST_DIG: + case ALC880_5ST_DIG: + case ALC880_W810: + spec->multiout.dig_out_nid = ALC880_DIGOUT_NID; + break; + default: + break; + } + + switch (board_config) { + case ALC880_3ST: + case ALC880_3ST_DIG: + case ALC880_5ST: + case ALC880_5ST_DIG: + case ALC880_W810: + spec->front_panel = 1; + break; + default: + break; + } + + switch (board_config) { + case ALC880_5ST: + case ALC880_5ST_DIG: + spec->mixers[spec->num_mixers] = alc880_side_mixer; + spec->num_mixers++; + spec->init_verbs = alc880_init_verbs_five_stack; + spec->channel_mode = alc880_fivestack_modes; + spec->num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes); + break; + case ALC880_W810: + spec->init_verbs = alc880_w810_init_verbs; + spec->channel_mode = alc880_w810_modes; + spec->num_channel_mode = ARRAY_SIZE(alc880_w810_modes); + break; + default: + spec->init_verbs = alc880_init_verbs_three_stack; + spec->channel_mode = alc880_threestack_modes; + spec->num_channel_mode = ARRAY_SIZE(alc880_threestack_modes); + break; + } + + spec->stream_name_analog = "ALC880 Analog"; + spec->stream_analog_playback = &alc880_pcm_analog_playback; + spec->stream_analog_capture = &alc880_pcm_analog_capture; + + spec->stream_name_digital = "ALC880 Digital"; + spec->stream_digital_playback = &alc880_pcm_digital_playback; + spec->stream_digital_capture = &alc880_pcm_digital_capture; + + spec->multiout.max_channels = spec->channel_mode[0].channels; + + switch (board_config) { + case ALC880_W810: + spec->multiout.num_dacs = ARRAY_SIZE(alc880_w810_dac_nids); + spec->multiout.dac_nids = alc880_w810_dac_nids; + // No dedicated headphone socket - it's shared with built-in speakers. + break; + default: + spec->multiout.num_dacs = ARRAY_SIZE(alc880_dac_nids); + spec->multiout.dac_nids = alc880_dac_nids; + spec->multiout.hp_nid = 0x03; /* rear-surround NID */ + break; + } + + spec->input_mux = &alc880_capture_source; + spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids); + spec->adc_nids = alc880_adc_nids; + + codec->patch_ops = alc_patch_ops; + + return 0; +} + +/* + * ALC260 support + */ + +/* + * This is just place-holder, so there's something for alc_build_pcms to look + * at when it calculates the maximum number of channels. ALC260 has no mixer + * element which allows changing the channel mode, so the verb list is + * never used. + */ +static struct alc_channel_mode alc260_modes[1] = { + { 2, 0 }, +}; + +snd_kcontrol_new_t alc260_base_mixer[] = { + HDA_CODEC_VOLUME("PCM Playback Volume", 0x08, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x0f, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x07, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x07, 0x05, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x10, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x04, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x04, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = alc_mux_enum_info, + .get = alc_mux_enum_get, + .put = alc_mux_enum_put, + }, + { } /* end */ +}; + +static struct hda_verb alc260_init_verbs[] = { + /* Line In pin widget for input */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + /* CD pin widget for input */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + /* Mic1 (rear panel) pin widget for input and vref at 80% */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + /* Mic2 (front panel) pin widget for input and vref at 80% */ + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + /* unmute amp left and right */ + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, + /* set connection select to line in (default select for this ADC) */ + {0x04, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* unmute Line-Out mixer amp left and right (volume = 0) */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* mute pin widget amp left and right (no gain on this amp) */ + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + /* unmute HP mixer amp left and right (volume = 0) */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* mute pin widget amp left and right (no gain on this amp) */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03 */ + /* unmute CD */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, + /* unmute Line In */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, + /* unmute Mic */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */ + /* Unmute Front out path */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + /* Unmute Headphone out path */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + { } +}; + +static struct hda_pcm_stream alc260_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .nid = 0x2, +}; + +static struct hda_pcm_stream alc260_pcm_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .nid = 0x4, +}; + +static int patch_alc260(struct hda_codec *codec) +{ + struct alc_spec *spec; + + spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + spec->mixers[spec->num_mixers] = alc260_base_mixer; + spec->num_mixers++; + + spec->init_verbs = alc260_init_verbs; + spec->channel_mode = alc260_modes; + spec->num_channel_mode = ARRAY_SIZE(alc260_modes); + + spec->stream_name_analog = "ALC260 Analog"; + spec->stream_analog_playback = &alc260_pcm_analog_playback; + spec->stream_analog_capture = &alc260_pcm_analog_capture; + + spec->multiout.max_channels = spec->channel_mode[0].channels; + spec->multiout.num_dacs = ARRAY_SIZE(alc260_dac_nids); + spec->multiout.dac_nids = alc260_dac_nids; + + spec->input_mux = &alc260_capture_source; + spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids); + spec->adc_nids = alc260_adc_nids; + + codec->patch_ops = alc_patch_ops; + + return 0; +} + +/* + * patch entries + */ +struct hda_codec_preset snd_hda_preset_realtek[] = { + { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 }, + { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 }, + {} /* terminator */ +}; diff -Nru a/sound/pci/ice1712/vt1720_mobo.c b/sound/pci/ice1712/vt1720_mobo.c --- a/sound/pci/ice1712/vt1720_mobo.c 2005-01-28 14:22:13 -08:00 +++ b/sound/pci/ice1712/vt1720_mobo.c 2005-01-28 14:22:13 -08:00 @@ -101,6 +101,15 @@ .eeprom_size = sizeof(k8x800_eeprom), .eeprom_data = k8x800_eeprom, }, + { + .subvendor = VT1720_SUBDEVICE_9CJS, + .name = "Chaintech 9CJS", + /* identical with k8x800 */ + .chip_init = k8x800_init, + .build_controls = k8x800_add_controls, + .eeprom_size = sizeof(k8x800_eeprom), + .eeprom_data = k8x800_eeprom, + }, { } /* terminator */ }; diff -Nru a/sound/pci/ice1712/vt1720_mobo.h b/sound/pci/ice1712/vt1720_mobo.h --- a/sound/pci/ice1712/vt1720_mobo.h 2005-01-28 14:22:13 -08:00 +++ b/sound/pci/ice1712/vt1720_mobo.h 2005-01-28 14:22:13 -08:00 @@ -26,11 +26,13 @@ #define VT1720_MOBO_DEVICE_DESC "{Albatron,K8X800 Pro II},"\ "{Chaintech,ZNF3-150},"\ - "{Chaintech,ZNF3-250}," + "{Chaintech,ZNF3-250},"\ + "{Chaintech,9CJS}," #define VT1720_SUBDEVICE_K8X800 0xf217052c #define VT1720_SUBDEVICE_ZNF3_150 0x0f2741f6 #define VT1720_SUBDEVICE_ZNF3_250 0x0f2745f6 +#define VT1720_SUBDEVICE_9CJS 0x0f272327 extern struct snd_ice1712_card_info snd_vt1720_mobo_cards[]; diff -Nru a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c --- a/sound/pci/intel8x0.c 2005-01-28 14:22:13 -08:00 +++ b/sound/pci/intel8x0.c 2005-01-28 14:22:13 -08:00 @@ -53,6 +53,7 @@ "{Intel,82801DB-ICH4}," "{Intel,ICH5}," "{Intel,ICH6}," + "{Intel,ICH7}," "{Intel,6300ESB}," "{Intel,MX440}," "{SiS,SI7012}," @@ -120,6 +121,9 @@ #ifndef PCI_DEVICE_ID_INTEL_ICH6_3 #define PCI_DEVICE_ID_INTEL_ICH6_3 0x266e #endif +#ifndef PCI_DEVICE_ID_INTEL_ICH7_20 +#define PCI_DEVICE_ID_INTEL_ICH7_20 0x27de +#endif #ifndef PCI_DEVICE_ID_SI_7012 #define PCI_DEVICE_ID_SI_7012 0x7012 #endif @@ -438,6 +442,7 @@ { 0x8086, 0x24d5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ICH5 */ { 0x8086, 0x25a6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ESB */ { 0x8086, 0x266e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ICH6 */ + { 0x8086, 0x27de, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ICH7 */ { 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 440MX */ { 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_SIS }, /* SI7012 */ { 0x10de, 0x01b1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE */ @@ -1780,18 +1785,24 @@ }, { .vendor = 0x103c, - .device = 0x129d, - .name = "HP xw8000", - .type = AC97_TUNE_HP_ONLY + .device = 0x088c, + .name = "HP nc8000", + .type = AC97_TUNE_MUTE_LED }, { .vendor = 0x103c, .device = 0x0890, - .name = "HP NC6000", + .name = "HP nc6000", .type = AC97_TUNE_MUTE_LED }, { .vendor = 0x103c, + .device = 0x129d, + .name = "HP xw8000", + .type = AC97_TUNE_HP_ONLY + }, + { + .vendor = 0x103c, .device = 0x12f1, .name = "HP xw8200", /* AD1981B*/ .type = AC97_TUNE_HP_ONLY @@ -2703,6 +2714,7 @@ { PCI_DEVICE_ID_INTEL_ICH5, "Intel ICH5" }, { PCI_DEVICE_ID_INTEL_ESB_5, "Intel 6300ESB" }, { PCI_DEVICE_ID_INTEL_ICH6_3, "Intel ICH6" }, + { PCI_DEVICE_ID_INTEL_ICH7_20, "Intel ICH7" }, { PCI_DEVICE_ID_SI_7012, "SiS SI7012" }, { PCI_DEVICE_ID_NVIDIA_MCP_AUDIO, "NVidia nForce" }, { PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO, "NVidia nForce2" }, diff -Nru a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c --- a/sound/pci/rme9652/hdsp.c 2005-01-28 14:22:13 -08:00 +++ b/sound/pci/rme9652/hdsp.c 2005-01-28 14:22:13 -08:00 @@ -4958,7 +4958,7 @@ #ifdef SNDRV_BIG_ENDIAN { int i; - u32 *src = hdsp->data; + u32 *src = (u32*)fw->data; for (i = 0; i < ARRAY_SIZE(hdsp->firmware_cache); i++, src++) hdsp->firmware_cache[i] = ((*src & 0x000000ff) << 16) | ((*src & 0x0000ff00) << 8) | diff -Nru a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c --- a/sound/pci/vx222/vx222_ops.c 2005-01-28 14:22:13 -08:00 +++ b/sound/pci/vx222/vx222_ops.c 2005-01-28 14:22:13 -08:00 @@ -22,6 +22,7 @@ #include #include +#include #include #include #include diff -Nru a/sound/pcmcia/vx/vxp_ops.c b/sound/pcmcia/vx/vxp_ops.c --- a/sound/pcmcia/vx/vxp_ops.c 2005-01-28 14:22:13 -08:00 +++ b/sound/pcmcia/vx/vxp_ops.c 2005-01-28 14:22:13 -08:00 @@ -22,6 +22,7 @@ #include #include +#include #include #include #include diff -Nru a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c --- a/sound/usb/usbaudio.c 2005-01-28 14:22:13 -08:00 +++ b/sound/usb/usbaudio.c 2005-01-28 14:22:13 -08:00 @@ -2980,6 +2980,8 @@ return create_ua700_ua25_quirk(chip, iface); case QUIRK_AUDIO_EDIROL_UA1000: return create_ua1000_quirk(chip, iface); + case QUIRK_IGNORE_INTERFACE: + return 0; default: snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type); return -ENXIO; diff -Nru a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h --- a/sound/usb/usbaudio.h 2005-01-28 14:22:13 -08:00 +++ b/sound/usb/usbaudio.h 2005-01-28 14:22:13 -08:00 @@ -157,6 +157,7 @@ #define QUIRK_MIDI_STANDARD_INTERFACE 6 #define QUIRK_AUDIO_EDIROL_UA700_UA25 7 #define QUIRK_AUDIO_EDIROL_UA1000 8 +#define QUIRK_IGNORE_INTERFACE 9 typedef struct snd_usb_audio_quirk snd_usb_audio_quirk_t; typedef struct snd_usb_midi_endpoint_info snd_usb_midi_endpoint_info_t; @@ -189,6 +190,8 @@ /* for QUIRK_AUDIO/MIDI_STANDARD_INTERFACE, data is NULL */ /* for QUIRK_AUDIO_EDIROL_UA700_UA25/UA1000, data is NULL */ + +/* for QUIRK_IGNORE_INTERFACE, data is null */ /* */ diff -Nru a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h --- a/sound/usb/usbquirks.h 2005-01-28 14:22:13 -08:00 +++ b/sound/usb/usbquirks.h 2005-01-28 14:22:13 -08:00 @@ -72,6 +72,8 @@ YAMAHA_DEVICE(0x1008, "UX96"), YAMAHA_DEVICE(0x1009, "UX16"), YAMAHA_INTERFACE(0x100a, 3, "EOS BX"), +YAMAHA_DEVICE(0x100c, "UC-MX"), +YAMAHA_DEVICE(0x100d, "UC-KX"), YAMAHA_DEVICE(0x100e, "S08"), YAMAHA_DEVICE(0x100f, "CLP-150"), YAMAHA_DEVICE(0x1010, "CLP-170"), @@ -86,6 +88,7 @@ YAMAHA_DEVICE(0x101a, "CVP-210"), YAMAHA_DEVICE(0x101b, "PSR-1100"), YAMAHA_DEVICE(0x101c, "PSR-2100"), +YAMAHA_DEVICE(0x101d, "CLP-175"), YAMAHA_DEVICE(0x101e, "PSR-K1"), YAMAHA_DEVICE(0x1020, "EZ-250i"), YAMAHA_DEVICE(0x1021, "MOTIF ES 6"), @@ -125,9 +128,12 @@ YAMAHA_DEVICE(0x5006, "NHB32-C"), YAMAHA_DEVICE(0x5007, "DM1000"), YAMAHA_DEVICE(0x5008, "01V96"), +YAMAHA_DEVICE(0x5009, "SPX2000"), YAMAHA_DEVICE(0x500a, "PM5D"), YAMAHA_DEVICE(0x500b, "DME64N"), YAMAHA_DEVICE(0x500c, "DME24N"), +YAMAHA_DEVICE(0x500d, NULL), +YAMAHA_DEVICE(0x500e, NULL), YAMAHA_DEVICE(0x7000, "DTX"), YAMAHA_DEVICE(0x7010, "UB99"), #undef YAMAHA_DEVICE @@ -876,6 +882,22 @@ * but captured samples are big-endian (see usbaudio.c). */ { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 3, + .type = QUIRK_IGNORE_INTERFACE + }, + { .ifnum = 4, .type = QUIRK_AUDIO_STANDARD_INTERFACE }, @@ -884,6 +906,10 @@ .type = QUIRK_AUDIO_STANDARD_INTERFACE }, { + .ifnum = 6, + .type = QUIRK_IGNORE_INTERFACE + }, + { .ifnum = 7, .type = QUIRK_AUDIO_STANDARD_INTERFACE }, @@ -936,11 +962,56 @@ .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "M-Audio", .product_name = "OmniStudio", - .ifnum = 9, - .type = QUIRK_MIDI_MIDIMAN, - .data = & (const snd_usb_midi_endpoint_info_t) { - .out_cables = 0x0001, - .in_cables = 0x0001 + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = & (const snd_usb_audio_quirk_t[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 3, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 4, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 5, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 6, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 7, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 8, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 9, + .type = QUIRK_MIDI_MIDIMAN, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + }, + { + .ifnum = -1 + } } } },