GIT 675c837e5bd60e78eaccdf7755e104d3f6497a2a git+ssh://master.kernel.org/pub/scm/linux/kernel/git/perex/alsa-current.git commit 675c837e5bd60e78eaccdf7755e104d3f6497a2a Author: Tobin Davis Date: Mon Aug 21 19:02:10 2006 +0200 [ALSA] hda-codec - Fix headphone output for some Intel 945 systems This patch enables headphone output at initialization for Intel 945 based systems that don't have proper detection circuitry. Signed-off-by: Tobin Davis Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 92b9b2a15c031d17c2dc567171606517e1b1c25e Author: Tobin Davis Date: Mon Aug 21 19:01:12 2006 +0200 [ALSA] hda-codec - add missing device ids This patch adds missing device ids for Intel 915 and D102GGC motherboards. Signed-off-by: Tobin Davis Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit fa7c7c9c0ed5ada3766aa6d6d0e6792581ad026a Author: Clemens Ladisch Date: Fri Aug 18 09:03:45 2006 +0200 [ALSA] riptide: fix compile errors with older gcc Change the syntax of a union initialization that is not understood by gcc 2.x. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela commit 4f88b1573b44a63ff81811a4eac293fe25a4a5e8 Author: Takashi Iwai Date: Thu Aug 17 18:21:36 2006 +0200 [ALSA] Add missing TLV callbacks for HD-audio codecs Added missing TLV callbacks for HD-audio codec supports. Also cleaned up the tlv callback for ad1986a (no mutex is needed there). Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 44e8f658e814d360826dc67e13fb09275bb8ca4e Author: Takashi Iwai Date: Thu Aug 17 17:12:19 2006 +0200 [ALSA] Fix some typos in snd-dummy driver Fixed some typos in snd-dummy driver. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 529938524e64fb2aba83485165adbb8927b43b17 Author: Krzysztof Helt Date: Thu Aug 17 16:59:28 2006 +0200 [ALSA] sparc dbri: removal of redudant volatile keywords It removes redudant volatile keywords. Signed-off-by: Krzysztof Helt Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 0aa25850ddaf3520bf6a69084943fd2dd44bd427 Author: Krzysztof Helt Date: Thu Aug 17 16:58:45 2006 +0200 [ALSA] sparc dbri: removal of unused struct members It removes unused or rarely used members of defined structures. Signed-off-by: Krzysztof Helt Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit cf24b5ba8fe43541b9e8543501dd9b23a1abb6da Author: Takashi Iwai Date: Thu Aug 17 16:23:07 2006 +0200 [ALSA] Fix compile errors with older gcc Fixed compile errors with older gcc for initialization of a union. sound/pci/ca0106/ca0106_mixer.c: At top level: sound/pci/ca0106/ca0106_mixer.c:499: unknown field 'p' specified in initializer sound/pci/ca0106/ca0106_mixer.c:499: warning: missing braces around initializer sound/pci/ca0106/ca0106_mixer.c:499: warning: (near initialization for 'snd_ca0106_volume_ctls[0].tlv') Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit ff30c4c7f8eaf03950c0631bd5ee5058bc8c0efa Author: Guillaume Munch Date: Wed Aug 16 19:35:12 2006 +0200 [ALSA] Add support for Sony Vaio AR 11B This patch adds automatic detection for Sigmatel ID 7664, the sound chip in Sony Vaio AR 11B (european name). - patch_stac7661 becomes patch_stac766x - .id = 0x83847664 is added Signed-off-by: Guillaume Munch Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit b1c98be6872a7e9c28cfafd305368f343b8cd18a Author: Magnus Sandin Date: Wed Aug 16 15:25:23 2006 +0200 [ALSA] Fix for LG K1 Express Laptop Attached is the patch for the LG K1 Express (K1-2333V) laptop that enables sound output. Signed-off-by: Magnus Sandin Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit ac56bc3ed60ce267aebc9de2550fdf0895714233 Author: Alexey Dobriyan Date: Wed Aug 16 12:56:53 2006 +0200 [ALSA] emu10k1x: simplify around pci_register_driver() Report errors to modprobe as side effect. Signed-off-by: Alexey Dobriyan Signed-off-by: Andrew Morton Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit d1cc5c085cf5a6cbdba6b6f641882211cac75db3 Author: Tobias Klauser Date: Wed Aug 16 12:56:16 2006 +0200 [ALSA] sound/pci/fm801: Use ARRAY_SIZE macro Use ARRAY_SIZE macro instead of sizeof(x)/sizeof(x[0]) Signed-off-by: Tobias Klauser Signed-off-by: Andrew Morton Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit ea4716f88a39065571f5fe4b98e0862dc3eab6f8 Author: Krzysztof Helt Date: Wed Aug 16 12:54:29 2006 +0200 [ALSA] sparc dbri removal of DBRI_NO_INTS This patch removes define DBR_NO_INTS and all code related to handling more than one dbri irq statuses block. Signed-off-by: Krzysztof Helt Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit aa0a5878f223fb966c985b27bc1597d4c65a6dbe Author: Krzysztof Helt Date: Wed Aug 16 12:53:34 2006 +0200 [ALSA] dbri driver cleanup This is a small clean up of the dbri driver for sparc machines. It contains also a fix to DBRI interrupt queue initialization. Signed-off-by: Krzysztof Helt Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 5320b70362bba13d358fd61f8e50f9ebd5f85b50 Author: Jaroslav Kysela Date: Tue Aug 15 14:39:07 2006 +0200 [ALSA] ice1712 - fix 1600->16000Hz value typo Signed-off-by: Jaroslav Kysela commit 0d99fd093d29181687dd2a1d834374507d9885fb Author: Takashi Iwai Date: Wed Aug 9 16:57:28 2006 +0200 [ALSA] Added model for Uniwill laptop with ALC861 Added a new model 'uniwill-m31' for Uniwill laptops with ALC861 codec chip. The patch is taken from ALSA bug#2035, and modifeid. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 810d18c035d08fe83283ec81e55ae4572ff397bc Author: Takashi Iwai Date: Wed Aug 9 14:51:14 2006 +0200 [ALSA] Select I2C and I2C_POWERMAC in aoa/codecs/Kconfig Added the missing selection of I2C and I2C_POWERMAC for Onyx and TAS codecs in aoa/codecs/Kconfig. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit b5416196339d256a2f0d85e5db10346a8b6fe6df Author: Takashi Iwai Date: Wed Aug 9 14:33:27 2006 +0200 [ALSA] Fix compile warnings in ak4xxx-adda.c Fixed compile warnings in ak4xxx-adda.c reagarding missing enum cases in switch. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 24f436fc26a6b9c8cc691a4537d80ccdfafcbf37 Author: Jochen Voss Date: Wed Aug 9 14:26:26 2006 +0200 [ALSA] Revolution 5.1 - complete the AK5365 support Complete the AK5365 support. This adds a boolean control to toggle the soft mute feature of the AK5365 chip. Signed-off-by: Jochen Voss Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 708432d5b459039d754f8091f9839dd5ee7b3fdb Author: Jochen Voss Date: Tue Aug 8 21:13:42 2006 +0200 [ALSA] Revolution 5.1 - register the AK5365 ADC with ALSA Enable capture support for the M-Audio Revolution 5.1 card, by registering the ADC with ALSA. Signed-off-by: Jochen Voss Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit d4119fd0323de661a0c469ba0f196eece3376d40 Author: Jochen Voss Date: Tue Aug 8 21:12:44 2006 +0200 [ALSA] Revolution 5.1 - add AK5365 ADC support Add support for the AK5365 ADC. Signed-off-by: Jochen Voss Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 758fc90474a2ec377ed78e4bbbaefab1b92d63a4 Author: Danny Tholen Date: Tue Aug 8 18:59:07 2006 +0200 [ALSA] [snd-hda-intel] fix sound on some Asus W6A chips This patch adds support in ALSA snd-hda-intel driver for Asus W6A motherboard as reported in MDV Bugzilla #19962 (see http://qa.mandriva.com/show_bug.cgi?id=19962) Signed-off-by: Danny Tholen Signed-off-by: Thomas Backlund Signed-off-by: Thierry Vignaud Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 349750b6789cf0839d19853c112162a92d0d0010 Author: Takashi Iwai Date: Tue Aug 8 13:01:14 2006 +0200 [ALSA] Fix the preselected model for HP machine Fixed the preselected model for a HP machine with SSID 103c:3010 to use hp-3013 (ALSA bug#2157). Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 041f8dc5b7dddc240eef64124afd24f957de0ac9 Author: Takashi Iwai Date: Fri Aug 4 19:08:03 2006 +0200 [ALSA] usb-audio - Fix a typo of CONFIG_PROC_FS Fixed a typo of CONFIG_PROC_FS in usbaudio.c. The stream proc file appears again. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 153793bb4fdea43e8d2d59d3ca3db8cbecf77d0e Author: Takashi Iwai Date: Fri Aug 4 12:57:19 2006 +0200 [ALSA] Fix substream selection in PCM and rawmidi The PCM and rawmidi substreams can be selected explicitly by opening control handle and set via *_PREFER_SUBDEVICE ioctl. But, when multiple controls are opened, the driver gets confused. The patch fixes the initialization of prefer_*_subdevice and the check of multiple controls. The first set subdevice is picked up as the valid one. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit e720a6de158d2ee767dbb217d07913cb50288fb2 Author: Dmitry Torokhov Date: Thu Aug 3 15:06:14 2006 +0200 [ALSA] ppc-beep - handle errors from input_register_device() ppc-beep: handle errors from input_register_device() (Also fixed the wrong memory release in the error path.) Signed-off-by: Dmitry Torokhov Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit ad9f2744630b19aad42df51deba59a76ddb83ca3 Author: Takashi Iwai Date: Mon Jul 31 16:51:51 2006 +0200 [ALSA] Fix control/status mmap with shared PCM substream The flag to avoid 32bit-incompatible mmap for control/status records should be outside the pcm substream instance since a substream can be shared among multiple opens. Now it's flagged in pcm_file list that is directly assigned to file->private_data. Also, removed snd_pcm_add_file() and remove_file() functions and substream.files field that are not really used in the code. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 30b6b375ff5f9c4a59fe287b2cdf63f4f69653c1 Author: Ondrej Zary Date: Mon Jul 31 12:51:57 2006 +0200 [ALSA] es18xx - Add PnP BIOS support This patch adds PnP BIOS support to es18xx driver. It allows ESS ES18xx sound chips integrated in some notebooks (such as DTK FortisPro TOP-5A) that don't appear as ISA cards (they aren't recognized by ISA PnP, only by PnP BIOS) to 'just work' automatically. Signed-off-by: Ondrej Zary Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 4e4f54108984a5ca6f8b7f1a4ef72b8d4ed99364 Author: Matt Porter Date: Mon Jul 31 12:49:34 2006 +0200 [ALSA] hda: sigmatel 9205 family support Adds support for the '9205 family' which includes some other part numbers but 9205 is the first one. These are 4 channel codecs, some have digital mic capability. Support for the digital mic feature will come later. Signed-off-by: Matt Porter Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 91800fc69d2f1674c57c9d437414dc170a3144c3 Author: James Courtier-Dutton Date: Fri Jul 28 22:27:56 2006 +0100 [ALSA] snd-emu10k1: Add a comment explaining the conversion function for dB gain. Signed-off-by: James Courtier-Dutton Signed-off-by: Jaroslav Kysela commit 06831a85730245e7698e67ff8c731b746b8191ed Author: Takashi Iwai Date: Fri Jul 28 14:47:34 2006 +0200 [ALSA] Fix noisy output with shared channel mode with hd-audio - Fix the wrong initialization of num_dacs when changing the channel mode between 2 and multi-channel modes. It must be evaluated after calling snd_hda_ch_mode_put() - Added the similar check of num_dacs fix in Realtek code. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 075d1dbfb45fb995aa59f30dcaec40e77dbe6eb0 Author: Takashi Iwai Date: Fri Jul 28 14:44:31 2006 +0200 [ALSA] Don't set up the same PID twice in snd_hda_multi_out_analog_prepare Check the hp_nid whether it's identical with front pin to avoid the setup of the same widget node twice. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 245a429977ada3cb64c2badd3c47f3c142edd287 Author: Takashi Iwai Date: Fri Jul 28 14:42:36 2006 +0200 [ALSA] Misc fixes for Realtek HD-audio codecs - Added model=arima for Arima W820Di1 with ALC882 codec chip - Added EAPD-control verbs to TCL S700 init verbs - Added missing model strings for Realtek codecs (to be specified via module option explicitly for testing/debugging) Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 8d21303a41187859eca45ceed5e670f233a5e1c1 Author: Matthias Koenig Date: Thu Jul 27 16:59:23 2006 +0200 [ALSA] Add snd-mts64 driver for ESI Miditerminal 4140 Added snd-mts64 driver for Ego Systems (ESI) Miditerminal 4140 by Matthias Koenig . The driver requires parport (CONFIG_PARPORT). Signed-off-by: Matthias Koenig Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 3af9ca40306c99a918d70dbc257cc5fbcbdd523d Author: Takashi Iwai Date: Thu Jul 27 15:50:14 2006 +0200 [ALSA] Fix Makefile of cs5535audio Use ifeq instead of ifdef in Makefile to make the maintenance of out-of-kernel tree easier. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 051936b87f090988134427ca0e88e0b727698368 Author: Jaroslav Kysela Date: Thu Jul 27 10:44:30 2006 +0200 [ALSA] HDA driver - do not set mute flag for dB scale (follow HDA specification) Signed-off-by: Jaroslav Kysela commit f15a1e7653a56801b94a84b878ef4cf74133cf4f Author: Takashi Iwai Date: Tue Jul 25 15:29:37 2006 +0200 [ALSA] via82xx - Add dxs_support entry for a FSC machine Added dxs_support=5 entry for a FSC machine. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit a1b88423dfb81667d1f23ad4ef72f0f20dbd9b62 Author: Takashi Iwai Date: Tue Jul 25 14:51:16 2006 +0200 [ALSA] Added model for ASUS M2NPV-VM mobo Added the proper model (3stack) for ASUS M2NPV-VM mobo with AD1986A codec. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit f0266cd5962aa13cd24b7e06b82a60b0df87ee27 Author: Takashi Iwai Date: Tue Jul 25 14:51:16 2006 +0200 [ALSA] Add support of Benq laptop with ALC262 Added the support of Benq laptop with ALC262 codec. A model string 'benq' is added, too. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 04c4dfa3525d28c44d54270832de591a55dfbda4 Author: Takashi Iwai Date: Tue Jul 25 14:51:15 2006 +0200 [ALSA] Add hp-bpc model type for HP laptops Added 'hp-bpc' model type for HP xw4400-compatible laptops. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit afb0158d779d50568189933f837cd81f685fa019 Author: Takashi Iwai Date: Tue Jul 25 14:51:15 2006 +0200 [ALSA] Add model entry for Clevo m665n laptop Added the proper model entry for Clevo m665n laptop with ALC880 codec. Also, added a model string 'clevo' to enable the clevo-type model option. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 1c15280b59873121741d952b2c9a1d916be97f1d Author: Takashi Iwai Date: Tue Jul 25 14:51:14 2006 +0200 [ALSA] Add model entry for Samsung X10 laptop Added the proper model entry (laptop-eapd) for Samsung X10-T2300 Culesa laptop with AD1986A codec. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit fa6635a6b37f852f0308925c35962fd7eca1d46d Author: James Courtier-Dutton Date: Sat Jul 22 17:02:10 2006 +0100 [ALSA] snd-emu10k1: Implement dB gain infomation. Signed-off-by: James Courtier-Dutton Signed-off-by: Jaroslav Kysela commit fd620ca03e865dc4ac212b633ca44dd8e66792a1 Author: James Courtier-Dutton Date: Sat Jul 22 15:02:48 2006 +0100 [ALSA] snd-ca0106: Fix dB gain TLVs. Signed-off-by: James Courtier-Dutton Signed-off-by: Jaroslav Kysela commit 1c860ee63510d9f32f399d09e7aaf7b85844b56e Author: Clemens Ladisch Date: Fri Jul 21 10:46:18 2006 +0200 [ALSA] usb-audio: add more Yamaha devices Add some quirks for some unknown Yamaha USB MIDI devices. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela commit a041e32be9b83d7e7871c66fc3ca824f1d4119de Author: Clemens Ladisch Date: Fri Jul 21 10:45:19 2006 +0200 [ALSA] system timer: remove unused snd_timer_system_private.timer field Remove the snd_timer_system_private structure's timer field that was never used. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela commit 1b8cfa8b13423e5bc02215dd8d75bc39cad12112 Author: Clemens Ladisch Date: Mon Jul 17 16:53:57 2006 +0200 [ALSA] timer: fix timer rescheduling When checking whether a hardware timer needs to be rescheduled, we have to compare against the previously scheduled interval and not against the actual interval between the last two interrupts. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela commit f10664e7c56878918ecb1f8eaaa2ec97b82341a6 Author: Clemens Ladisch Date: Mon Jul 17 16:52:09 2006 +0200 [ALSA] system timer: clear correction value when timer stops Do not retain the old correction value when the timer was stopped. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela commit eee9a8edb56b8221fca322bf3b51d6bfb9519270 Author: Clemens Ladisch Date: Mon Jul 17 16:51:37 2006 +0200 [ALSA] system timer: accumulate correction for multiple lost ticks When multiple timer interrupts arrive too late, correct for all delays instead of ignoring the earlier ones. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela commit b862e7e3662556e09a2882a4436ad9ac32391019 Author: Clemens Ladisch Date: Mon Jul 17 16:50:56 2006 +0200 [ALSA] system timer: fix lost ticks correction adjustment Fix the adjustment of the lost ticks correction variable in the case when the correction has been fully taken into account in the next timer expiration value. Subtracting the scheduled ticks value would result in an underflow. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela commit 109823095319f7dd854e7e979361d7ea5eb48abc Author: Takashi Iwai Date: Fri Jul 14 15:18:19 2006 +0200 [ALSA] Add TLV support to snd-usb-audio driver Added TLV-read support to snd-usb-audio driver for passing the volume dB scale information to user-space. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit bb1de77c32b2c2a62351c0c81556c22cecb5af2f Author: Takashi Iwai Date: Fri Jul 14 14:39:34 2006 +0200 [ALSA] Remove unused tlv_rw field from struct snd_kcontrol Remove unused tlv_rw field from struct snd_kcontrol. The callback is set in tlv.c field, instead. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit ededc0a89af008b3c1704451bde0f56b69c78fe7 Author: James Courtier-Dutton Date: Sat Jul 8 16:39:30 2006 +0100 [ALSA] snd-emu10k1: Implement 24bit capture via Philips 1361T ADC for SB0240 card. Signed-off-by: James Courtier-Dutton Signed-off-by: Jaroslav Kysela commit 4251e666f68d30bdf8ad6a3e43913a8c994ee8a7 Author: Matt Porter Date: Thu Jul 6 18:49:10 2006 +0200 [ALSA] hda: fix sigmatel 9227/8/9 codec support SigmaTel 9227/8/9 IDs must use the 927x patch. Signed-off-by: Matt Porter Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit b5ad844f7cb2bb89cca8f6658520fc8113ec6b6f Author: Jaroslav Kysela Date: Wed Jul 5 17:39:49 2006 +0200 [ALSA] HDA codec & CA0106 - add/fix TLV support Signed-off-by: Jaroslav Kysela commit fed3798f9eda074f35b6f54b3547cc32225acb17 Author: Jaroslav Kysela Date: Wed Jul 5 17:39:14 2006 +0200 [ALSA] HDA codec - little code & comment cleanup Signed-off-by: Jaroslav Kysela commit c3e918a494796c0bc881da14d447852fc0e16feb Author: Jaroslav Kysela Date: Wed Jul 5 17:34:51 2006 +0200 [ALSA] Control API - more robust TLV implementation - added callback option - added READ/WRITE/COMMAND flags to access member - added WRITE/COMMAND ioctls - added SNDRV_CTL_EVENT_MASK_TLV for TLV change notifications - added TLV support to ELEM_ADD ioctl Signed-off-by: Jaroslav Kysela commit 25805c46c126a72db0714033540ec1afee2774ee Author: Jaroslav Kysela Date: Tue Jul 4 13:39:55 2006 +0200 [ALSA] fm801: fixed broken previous patch for the FM tuner only code - do not allocate and enable interrupt - do not do the FM tuner mute (it should be handled more cleanly) Signed-off-by: Jaroslav Kysela commit 8c9a1e2fd3f264b1fdcaf992c70ced529db90ac0 Author: Andy Shevchenko Date: Tue Jul 4 12:05:14 2006 +0200 [ALSA] fm801: Support FM only card Signed-off-by: Andy Shevchenko Signed-off-by: Jaroslav Kysela commit 810bcc9aa6f32bf4087ea8b8232cd0340460634c Author: Mike Rapoport Date: Thu Jun 29 17:15:33 2006 +0200 [ALSA] add codec-specific controls for UCB1400 This patch adds some codec-specific controls for Philips UCB1400 codec. Signed-off-by: Mike Rapoport Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit c63d22f201d7f3ba0e14d74c4193a1e86fc2aaa3 Author: Takashi Iwai Date: Tue Jun 27 18:28:53 2006 +0200 [ALSA] Add experimental support of aggressive AC97 power-saving mode Added CONFIG_SND_AC97_POWER_SAVE kernel config to enable the support of aggressive AC97 power-saving mode. In this mode, the AC97 powerdown register bits are dynamically controlled at each open/close of PCM streams. The mode is activated via power_save option for snd-ac97-codec driver. As default it's off. It can be turned on/off on the fly via sysfs, too. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 82b2c1a93bd8300f3e3f8e4b392022b980aa8c1d Author: Takashi Iwai Date: Fri Jun 23 14:38:26 2006 +0200 [ALSA] Deprecate snd_card_free_in_thread() Deprecated snd_card_free_in_thread(), replaced with snd_card_free_when_closed(). Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 1b65d3af2d044b42e04b46a670c2c887cc9d140e Author: Takashi Iwai Date: Fri Jun 23 14:38:23 2006 +0200 [ALSA] Unregister device files at disconnection Orignally proposed by Sam Revitch . Unregister device files at disconnection to avoid the futher accesses. Also, the dev_unregister callback is removed and replaced with the combination of disconnect + free. A new function snd_card_free_when_closed() is introduced, which is used in USB disconnect callback. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit ab05c64b367c7c7e6b2a93425c90dc2e119d5235 Author: Takashi Iwai Date: Fri Jun 23 14:37:59 2006 +0200 [ALSA] Fix disconnection of proc interface - Add the linked list to each proc entry to enable a single-shot disconnection (unregister) - Deprecate snd_info_unregister(), use snd_info_free_entry() - Removed NULL checks of snd_info_free_entry() Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 9697e12463f24cd90d589a9f91cdf4e7073dcfed Author: Jaroslav Kysela Date: Thu Jun 1 18:34:01 2006 +0200 [ALSA] Control API - TLV implementation for additional information like dB scale This patch implements a TLV mechanism to transfer an additional information like dB scale to the user space. The types might be extended in future. Acked-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index f61af23..d7e95f1 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -778,11 +778,16 @@ Prior to version 0.9.0rc4 options had a 6stack-digout 6-jack with a SPDIF out w810 3-jack z71v 3-jack (HP shared SPDIF) - asus 3-jack + asus 3-jack (ASUS Mobo) + asus-w1v ASUS W1V + asus-dig ASUS with SPDIF out + asus-dig2 ASUS with SPDIF out (using GPIO2) uniwill 3-jack F1734 2-jack lg LG laptop (m1 express dual) lg-lw LG LW20 laptop + tcl TCL S700 + clevo Clevo laptops (m520G, m665n) test for testing/debugging purpose, almost all controls can be adjusted. Appearing only when compiled with $CONFIG_SND_DEBUG=y @@ -790,6 +795,7 @@ Prior to version 0.9.0rc4 options had a ALC260 hp HP machines + hp-3013 HP machines (3013-variant) fujitsu Fujitsu S7020 acer Acer TravelMate basic fixed pin assignment (old default model) @@ -797,24 +803,31 @@ Prior to version 0.9.0rc4 options had a ALC262 fujitsu Fujitsu Laptop + hp-bpc HP xw4400/6400/8400/9400 laptops + benq Benq ED8 basic fixed pin assignment w/o SPDIF auto auto-config reading BIOS (default) ALC882/885 3stack-dig 3-jack with SPDIF I/O 6stck-dig 6-jack digital with SPDIF I/O + arima Arima W820Di1 auto auto-config reading BIOS (default) ALC883/888 3stack-dig 3-jack with SPDIF I/O 6stack-dig 6-jack digital with SPDIF I/O - 6stack-dig-demo 6-stack digital for Intel demo board + 3stack-6ch 3-jack 6-channel + 3stack-6ch-dig 3-jack 6-channel with SPDIF I/O + 6stack-dig-demo 6-jack digital for Intel demo board auto auto-config reading BIOS (default) ALC861/660 3stack 3-jack 3stack-dig 3-jack with SPDIF I/O 6stack-dig 6-jack with SPDIF I/O + 3stack-660 3-jack (for ALC660) + uniwill-m31 Uniwill M31 laptop auto auto-config reading BIOS (default) CMI9880 @@ -843,10 +856,10 @@ Prior to version 0.9.0rc4 options had a 3stack-dig ditto with SPDIF laptop 3-jack with hp-jack automute laptop-dig ditto with SPDIF - auto auto-confgi reading BIOS (default) + auto auto-config reading BIOS (default) - STAC7661(?) - vaio Setup for VAIO FE550G/SZ110 + STAC7664/7661(?) + vaio Setup for VAIO FE550G/SZ110/AR11B If the default configuration doesn't work and one of the above matches with your device, report it together with the PCI @@ -1213,6 +1226,14 @@ Prior to version 0.9.0rc4 options had a Module supports only 1 card. This module has no enable option. + Module snd-mts64 + ---------------- + + Module for Ego Systems (ESI) Miditerminal 4140 + + This module supports multiple devices. + Requires parport (CONFIG_PARPORT). + Module snd-nm256 ---------------- diff --git a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl index b8dc51c..4807ef7 100644 --- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl +++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl @@ -1054,9 +1054,8 @@ For a device which allows hotplugging, you can use - snd_card_free_in_thread. This one will - postpone the destruction and wait in a kernel-thread until all - devices are closed. + snd_card_free_when_closed. This one will + postpone the destruction until all devices are closed. diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h index 758f8bf..4c43521 100644 --- a/include/sound/ac97_codec.h +++ b/include/sound/ac97_codec.h @@ -27,6 +27,7 @@ #define __SOUND_AC97_CODEC_H #include #include +#include #include "pcm.h" #include "control.h" #include "info.h" @@ -140,6 +141,20 @@ #define AC97_GP_DRSS_MASK 0x0c00 /* doub #define AC97_GP_DRSS_1011 0x0000 /* LR(C) 10+11(+12) */ #define AC97_GP_DRSS_78 0x0400 /* LR 7+8 */ +/* powerdown bits */ +#define AC97_PD_ADC_STATUS 0x0001 /* ADC status (RO) */ +#define AC97_PD_DAC_STATUS 0x0002 /* DAC status (RO) */ +#define AC97_PD_MIXER_STATUS 0x0004 /* Analog mixer status (RO) */ +#define AC97_PD_VREF_STATUS 0x0008 /* Vref status (RO) */ +#define AC97_PD_PR0 0x0100 /* Power down PCM ADCs and input MUX */ +#define AC97_PD_PR1 0x0200 /* Power down PCM front DAC */ +#define AC97_PD_PR2 0x0400 /* Power down Mixer (Vref still on) */ +#define AC97_PD_PR3 0x0800 /* Power down Mixer (Vref off) */ +#define AC97_PD_PR4 0x1000 /* Power down AC-Link */ +#define AC97_PD_PR5 0x2000 /* Disable internal clock usage */ +#define AC97_PD_PR6 0x4000 /* Headphone amplifier */ +#define AC97_PD_EAPD 0x8000 /* External Amplifer Power Down (EAPD) */ + /* extended audio ID bit defines */ #define AC97_EI_VRA 0x0001 /* Variable bit rate supported */ #define AC97_EI_DRA 0x0002 /* Double rate supported */ @@ -359,6 +374,7 @@ #define AC97_SCAP_INDEP_SDIN (1<<6) /* i #define AC97_SCAP_INV_EAPD (1<<7) /* inverted EAPD */ #define AC97_SCAP_DETECT_BY_VENDOR (1<<8) /* use vendor registers for read tests */ #define AC97_SCAP_NO_SPDIF (1<<9) /* don't build SPDIF controls */ +#define AC97_SCAP_EAPD_LED (1<<10) /* EAPD as mute LED */ /* ac97->flags */ #define AC97_HAS_PC_BEEP (1<<0) /* force PC Speaker usage */ @@ -491,6 +507,12 @@ struct snd_ac97 { /* jack-sharing info */ unsigned char indep_surround; unsigned char channel_mode; + +#ifdef CONFIG_SND_AC97_POWER_SAVE + unsigned int power_up; /* power states */ + struct workqueue_struct *power_workq; + struct work_struct power_work; +#endif struct device dev; }; @@ -532,6 +554,15 @@ unsigned short snd_ac97_read(struct snd_ void snd_ac97_write_cache(struct snd_ac97 *ac97, unsigned short reg, unsigned short value); int snd_ac97_update(struct snd_ac97 *ac97, unsigned short reg, unsigned short value); int snd_ac97_update_bits(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value); +#ifdef CONFIG_SND_AC97_POWER_SAVE +int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup); +#else +static inline int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, + int powerup) +{ + return 0; +} +#endif #ifdef CONFIG_PM void snd_ac97_suspend(struct snd_ac97 *ac97); void snd_ac97_resume(struct snd_ac97 *ac97); @@ -583,6 +614,7 @@ struct ac97_pcm { copy_flag: 1, /* lowlevel driver must fill all entries */ spdif: 1; /* spdif pcm */ unsigned short aslots; /* active slots */ + unsigned short cur_dbl; /* current double-rate state */ unsigned int rates; /* available rates */ struct { unsigned short slots; /* driver input: requested AC97 slot numbers */ diff --git a/include/sound/ak4xxx-adda.h b/include/sound/ak4xxx-adda.h index 3d98884..65ddfa3 100644 --- a/include/sound/ak4xxx-adda.h +++ b/include/sound/ak4xxx-adda.h @@ -53,7 +53,8 @@ struct snd_akm4xxx { unsigned int idx_offset; /* control index offset */ enum { SND_AK4524, SND_AK4528, SND_AK4529, - SND_AK4355, SND_AK4358, SND_AK4381 + SND_AK4355, SND_AK4358, SND_AK4381, + SND_AK5365 } type; unsigned int *num_stereo; /* array of combined counts * for the mixer diff --git a/include/sound/asound.h b/include/sound/asound.h index 41885f4..c1621c6 100644 --- a/include/sound/asound.h +++ b/include/sound/asound.h @@ -688,7 +688,7 @@ struct snd_timer_tread { * * ****************************************************************************/ -#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 3) +#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 4) struct snd_ctl_card_info { int card; /* card number */ @@ -727,10 +727,15 @@ #define SNDRV_CTL_ELEM_ACCESS_READ (1<< #define SNDRV_CTL_ELEM_ACCESS_WRITE (1<<1) #define SNDRV_CTL_ELEM_ACCESS_READWRITE (SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE) #define SNDRV_CTL_ELEM_ACCESS_VOLATILE (1<<2) /* control value may be changed without a notification */ -#define SNDRV_CTL_ELEM_ACCESS_TIMESTAMP (1<<2) /* when was control changed */ +#define SNDRV_CTL_ELEM_ACCESS_TIMESTAMP (1<<3) /* when was control changed */ +#define SNDRV_CTL_ELEM_ACCESS_TLV_READ (1<<4) /* TLV read is possible */ +#define SNDRV_CTL_ELEM_ACCESS_TLV_WRITE (1<<5) /* TLV write is possible */ +#define SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE (SNDRV_CTL_ELEM_ACCESS_TLV_READ|SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) +#define SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND (1<<6) /* TLV command is possible */ #define SNDRV_CTL_ELEM_ACCESS_INACTIVE (1<<8) /* control does actually nothing, but may be updated */ #define SNDRV_CTL_ELEM_ACCESS_LOCK (1<<9) /* write lock */ #define SNDRV_CTL_ELEM_ACCESS_OWNER (1<<10) /* write lock owner */ +#define SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK (1<<28) /* kernel use a TLV callback */ #define SNDRV_CTL_ELEM_ACCESS_USER (1<<29) /* user space element */ #define SNDRV_CTL_ELEM_ACCESS_DINDIRECT (1<<30) /* indirect access for matrix dimensions in the info structure */ #define SNDRV_CTL_ELEM_ACCESS_INDIRECT (1<<31) /* indirect access for element value in the value structure */ @@ -818,6 +823,12 @@ struct snd_ctl_elem_value { unsigned char reserved[128-sizeof(struct timespec)]; }; +struct snd_ctl_tlv { + unsigned int numid; /* control element numeric identification */ + unsigned int length; /* in bytes aligned to 4 */ + unsigned int tlv[0]; /* first TLV */ +}; + enum { SNDRV_CTL_IOCTL_PVERSION = _IOR('U', 0x00, int), SNDRV_CTL_IOCTL_CARD_INFO = _IOR('U', 0x01, struct snd_ctl_card_info), @@ -831,6 +842,9 @@ enum { SNDRV_CTL_IOCTL_ELEM_ADD = _IOWR('U', 0x17, struct snd_ctl_elem_info), SNDRV_CTL_IOCTL_ELEM_REPLACE = _IOWR('U', 0x18, struct snd_ctl_elem_info), SNDRV_CTL_IOCTL_ELEM_REMOVE = _IOWR('U', 0x19, struct snd_ctl_elem_id), + SNDRV_CTL_IOCTL_TLV_READ = _IOWR('U', 0x1a, struct snd_ctl_tlv), + SNDRV_CTL_IOCTL_TLV_WRITE = _IOWR('U', 0x1b, struct snd_ctl_tlv), + SNDRV_CTL_IOCTL_TLV_COMMAND = _IOWR('U', 0x1c, struct snd_ctl_tlv), SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE = _IOWR('U', 0x20, int), SNDRV_CTL_IOCTL_HWDEP_INFO = _IOR('U', 0x21, struct snd_hwdep_info), SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE = _IOR('U', 0x30, int), @@ -855,6 +869,7 @@ enum sndrv_ctl_event_type { #define SNDRV_CTL_EVENT_MASK_VALUE (1<<0) /* element value was changed */ #define SNDRV_CTL_EVENT_MASK_INFO (1<<1) /* element info was changed */ #define SNDRV_CTL_EVENT_MASK_ADD (1<<2) /* element was added */ +#define SNDRV_CTL_EVENT_MASK_TLV (1<<3) /* element TLV tree was changed */ #define SNDRV_CTL_EVENT_MASK_REMOVE (~0U) /* element was removed */ struct snd_ctl_event { diff --git a/include/sound/control.h b/include/sound/control.h index 2489b1e..1de148b 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -30,6 +30,11 @@ struct snd_kcontrol; typedef int (snd_kcontrol_info_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_info * uinfo); typedef int (snd_kcontrol_get_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol); typedef int (snd_kcontrol_put_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol); +typedef int (snd_kcontrol_tlv_rw_t)(struct snd_kcontrol *kcontrol, + int op_flag, /* 0=read,1=write,-1=command */ + unsigned int size, + unsigned int __user *tlv); + struct snd_kcontrol_new { snd_ctl_elem_iface_t iface; /* interface identifier */ @@ -42,6 +47,10 @@ struct snd_kcontrol_new { snd_kcontrol_info_t *info; snd_kcontrol_get_t *get; snd_kcontrol_put_t *put; + union { + snd_kcontrol_tlv_rw_t *c; + unsigned int *p; + } tlv; unsigned long private_value; }; @@ -58,6 +67,10 @@ struct snd_kcontrol { snd_kcontrol_info_t *info; snd_kcontrol_get_t *get; snd_kcontrol_put_t *put; + union { + snd_kcontrol_tlv_rw_t *c; + unsigned int *p; + } tlv; unsigned long private_value; void *private_data; void (*private_free)(struct snd_kcontrol *kcontrol); diff --git a/include/sound/core.h b/include/sound/core.h index bab3ff4..1359c53 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -25,7 +25,6 @@ #define __SOUND_CORE_H #include /* wake_up() */ #include /* struct mutex */ #include /* struct rw_semaphore */ -#include /* struct workqueue_struct */ #include /* pm_message_t */ /* forward declarations */ @@ -71,7 +70,6 @@ struct snd_device_ops { int (*dev_free)(struct snd_device *dev); int (*dev_register)(struct snd_device *dev); int (*dev_disconnect)(struct snd_device *dev); - int (*dev_unregister)(struct snd_device *dev); }; struct snd_device { @@ -131,8 +129,8 @@ struct snd_card { state */ spinlock_t files_lock; /* lock the files for this card */ int shutdown; /* this card is going down */ + int free_on_last_close; /* free in context of file_release */ wait_queue_head_t shutdown_sleep; - struct work_struct free_workq; /* for free in workqueue */ struct device *dev; #ifdef CONFIG_PM @@ -244,7 +242,7 @@ struct snd_card *snd_card_new(int idx, c struct module *module, int extra_size); int snd_card_disconnect(struct snd_card *card); int snd_card_free(struct snd_card *card); -int snd_card_free_in_thread(struct snd_card *card); +int snd_card_free_when_closed(struct snd_card *card); int snd_card_register(struct snd_card *card); int snd_card_info_init(void); int snd_card_info_done(void); diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 884bbf5..892e310 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -1524,6 +1524,10 @@ struct snd_emu10k1_fx8010_control_gpr { unsigned int value[32]; /* initial values */ unsigned int min; /* minimum range */ unsigned int max; /* maximum range */ + union { + snd_kcontrol_tlv_rw_t *c; + unsigned int *p; + } tlv; unsigned int translation; /* translation type (EMU10K1_GPR_TRANSLATION*) */ }; diff --git a/include/sound/info.h b/include/sound/info.h index 74f6996..97ffc4f 100644 --- a/include/sound/info.h +++ b/include/sound/info.h @@ -71,7 +71,6 @@ struct snd_info_entry { mode_t mode; long size; unsigned short content; - unsigned short disconnected: 1; union { struct snd_info_entry_text text; struct snd_info_entry_ops *ops; @@ -83,6 +82,8 @@ struct snd_info_entry { void (*private_free)(struct snd_info_entry *entry); struct proc_dir_entry *p; struct mutex access; + struct list_head children; + struct list_head list; }; #if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS) @@ -122,8 +123,8 @@ int snd_info_restore_text(struct snd_inf int snd_info_card_create(struct snd_card * card); int snd_info_card_register(struct snd_card * card); int snd_info_card_free(struct snd_card * card); +void snd_info_card_disconnect(struct snd_card * card); int snd_info_register(struct snd_info_entry * entry); -int snd_info_unregister(struct snd_info_entry * entry); /* for card drivers */ int snd_card_proc_new(struct snd_card *card, const char *name, struct snd_info_entry **entryp); @@ -156,8 +157,8 @@ static inline void snd_info_free_entry(s static inline int snd_info_card_create(struct snd_card * card) { return 0; } static inline int snd_info_card_register(struct snd_card * card) { return 0; } static inline int snd_info_card_free(struct snd_card * card) { return 0; } +static inline void snd_info_card_disconnect(struct snd_card * card) { } static inline int snd_info_register(struct snd_info_entry * entry) { return 0; } -static inline int snd_info_unregister(struct snd_info_entry * entry) { return 0; } static inline int snd_card_proc_new(struct snd_card *card, const char *name, struct snd_info_entry **entryp) { return -EINVAL; } diff --git a/include/sound/pcm.h b/include/sound/pcm.h index f84d849..60d40b3 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -190,7 +190,7 @@ #endif struct snd_pcm_file { struct snd_pcm_substream *substream; - struct snd_pcm_file *next; + int no_compat_mmap; }; struct snd_pcm_hw_rule; @@ -384,7 +384,6 @@ #ifdef CONFIG_SND_VERBOSE_PROCFS struct snd_info_entry *proc_prealloc_entry; #endif /* misc flags */ - unsigned int no_mmap_ctrl: 1; unsigned int hw_opened: 1; }; @@ -402,7 +401,6 @@ #if defined(CONFIG_SND_PCM_OSS) || defin /* -- OSS things -- */ struct snd_pcm_oss_stream oss; #endif - struct snd_pcm_file *files; #ifdef CONFIG_SND_VERBOSE_PROCFS struct snd_info_entry *proc_root; struct snd_info_entry *proc_info_entry; diff --git a/include/sound/timer.h b/include/sound/timer.h index 5ece2bf..d42c083 100644 --- a/include/sound/timer.h +++ b/include/sound/timer.h @@ -129,7 +129,6 @@ void snd_timer_notify(struct snd_timer * int snd_timer_global_new(char *id, int device, struct snd_timer **rtimer); int snd_timer_global_free(struct snd_timer *timer); int snd_timer_global_register(struct snd_timer *timer); -int snd_timer_global_unregister(struct snd_timer *timer); int snd_timer_open(struct snd_timer_instance **ti, char *owner, struct snd_timer_id *tid, unsigned int slave_id); int snd_timer_close(struct snd_timer_instance *timeri); diff --git a/include/sound/tlv.h b/include/sound/tlv.h new file mode 100644 index 0000000..b826e1d --- /dev/null +++ b/include/sound/tlv.h @@ -0,0 +1,43 @@ +#ifndef __SOUND_TLV_H +#define __SOUND_TLV_H + +/* + * Advanced Linux Sound Architecture - ALSA - Driver + * Copyright (c) 2006 by Jaroslav Kysela + * + * + * 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 + * + */ + +/* + * TLV structure is right behind the struct snd_ctl_tlv: + * unsigned int type - see SNDRV_CTL_TLVT_* + * unsigned int length + * .... data aligned to sizeof(unsigned int), use + * block_length = (length + (sizeof(unsigned int) - 1)) & + * ~(sizeof(unsigned int) - 1)) .... + */ + +#define SNDRV_CTL_TLVT_CONTAINER 0 /* one level down - group of TLVs */ +#define SNDRV_CTL_TLVT_DB_SCALE 1 /* dB scale */ + +#define DECLARE_TLV_DB_SCALE(name, min, step, mute) \ +unsigned int name[] = { \ + SNDRV_CTL_TLVT_DB_SCALE, 2 * sizeof(unsigned int), \ + (min), ((step) & 0xffff) | ((mute) ? 0x10000 : 0) \ +} + +#endif /* __SOUND_TLV_H */ diff --git a/include/sound/version.h b/include/sound/version.h index 2ee849d..e983db3 100644 --- a/include/sound/version.h +++ b/include/sound/version.h @@ -1,3 +1,3 @@ -/* include/version.h. Generated by configure. */ +/* include/version.h. Generated from version.h.in by configure. */ #define CONFIG_SND_VERSION "1.0.12rc1" #define CONFIG_SND_DATE " (Thu Jun 22 13:55:50 2006 UTC)" diff --git a/sound/aoa/codecs/Kconfig b/sound/aoa/codecs/Kconfig index 90cf58f..d5fbd60 100644 --- a/sound/aoa/codecs/Kconfig +++ b/sound/aoa/codecs/Kconfig @@ -1,6 +1,8 @@ config SND_AOA_ONYX tristate "support Onyx chip" depends on SND_AOA + select I2C + select I2C_POWERMAC ---help--- This option enables support for the Onyx (pcm3052) codec chip found in the latest Apple machines @@ -18,6 +20,8 @@ # some PowerMacs. config SND_AOA_TAS tristate "support TAS chips" depends on SND_AOA + select I2C + select I2C_POWERMAC ---help--- This option enables support for the tas chips found in a lot of Apple Machines, especially diff --git a/sound/core/control.c b/sound/core/control.c index bb397ea..ac14426 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -75,6 +75,8 @@ static int snd_ctl_open(struct inode *in init_waitqueue_head(&ctl->change_sleep); spin_lock_init(&ctl->read_lock); ctl->card = card; + ctl->prefer_pcm_subdevice = -1; + ctl->prefer_rawmidi_subdevice = -1; ctl->pid = current->pid; file->private_data = ctl; write_lock_irqsave(&card->ctl_files_rwlock, flags); @@ -236,11 +238,16 @@ struct snd_kcontrol *snd_ctl_new1(const kctl.id.index = ncontrol->index; kctl.count = ncontrol->count ? ncontrol->count : 1; access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : - (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|SNDRV_CTL_ELEM_ACCESS_INACTIVE| - SNDRV_CTL_ELEM_ACCESS_DINDIRECT|SNDRV_CTL_ELEM_ACCESS_INDIRECT)); + (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE| + SNDRV_CTL_ELEM_ACCESS_INACTIVE| + SNDRV_CTL_ELEM_ACCESS_DINDIRECT| + SNDRV_CTL_ELEM_ACCESS_INDIRECT| + SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE| + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)); kctl.info = ncontrol->info; kctl.get = ncontrol->get; kctl.put = ncontrol->put; + kctl.tlv.p = ncontrol->tlv.p; kctl.private_value = ncontrol->private_value; kctl.private_data = private_data; return snd_ctl_new(&kctl, access); @@ -882,6 +889,8 @@ struct user_element { struct snd_ctl_elem_info info; void *elem_data; /* element data */ unsigned long elem_data_size; /* size of element data in bytes */ + void *tlv_data; /* TLV data */ + unsigned long tlv_data_size; /* TLV data size */ void *priv_data; /* private data (like strings for enumerated type) */ unsigned long priv_data_size; /* size of private data in bytes */ }; @@ -916,9 +925,46 @@ static int snd_ctl_elem_user_put(struct return change; } +static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol, + int op_flag, + unsigned int size, + unsigned int __user *tlv) +{ + struct user_element *ue = kcontrol->private_data; + int change = 0; + void *new_data; + + if (op_flag > 0) { + if (size > 1024 * 128) /* sane value */ + return -EINVAL; + new_data = kmalloc(size, GFP_KERNEL); + if (new_data == NULL) + return -ENOMEM; + if (copy_from_user(new_data, tlv, size)) { + kfree(new_data); + return -EFAULT; + } + change = ue->tlv_data_size != size; + if (!change) + change = memcmp(ue->tlv_data, new_data, size); + kfree(ue->tlv_data); + ue->tlv_data = new_data; + ue->tlv_data_size = size; + } else { + if (size < ue->tlv_data_size) + return -ENOSPC; + if (copy_to_user(tlv, ue->tlv_data, ue->tlv_data_size)) + return -EFAULT; + } + return change; +} + static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol) { - kfree(kcontrol->private_data); + struct user_element *ue = kcontrol->private_data; + if (ue->tlv_data) + kfree(ue->tlv_data); + kfree(ue); } static int snd_ctl_elem_add(struct snd_ctl_file *file, @@ -937,7 +983,8 @@ static int snd_ctl_elem_add(struct snd_c return -EINVAL; access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : (info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE| - SNDRV_CTL_ELEM_ACCESS_INACTIVE)); + SNDRV_CTL_ELEM_ACCESS_INACTIVE| + SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)); info->id.numid = 0; memset(&kctl, 0, sizeof(kctl)); down_write(&card->controls_rwsem); @@ -963,6 +1010,10 @@ static int snd_ctl_elem_add(struct snd_c kctl.get = snd_ctl_elem_user_get; if (access & SNDRV_CTL_ELEM_ACCESS_WRITE) kctl.put = snd_ctl_elem_user_put; + if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) { + kctl.tlv.c = snd_ctl_elem_user_tlv; + access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; + } switch (info->type) { case SNDRV_CTL_ELEM_TYPE_BOOLEAN: private_size = sizeof(char); @@ -1067,6 +1118,67 @@ static int snd_ctl_subscribe_events(stru return 0; } +static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file, + struct snd_ctl_tlv __user *_tlv, + int op_flag) +{ + struct snd_card *card = file->card; + struct snd_ctl_tlv tlv; + struct snd_kcontrol *kctl; + struct snd_kcontrol_volatile *vd; + unsigned int len; + int err = 0; + + if (copy_from_user(&tlv, _tlv, sizeof(tlv))) + return -EFAULT; + if (tlv.length < sizeof(unsigned int) * 3) + return -EINVAL; + down_read(&card->controls_rwsem); + kctl = snd_ctl_find_numid(card, tlv.numid); + if (kctl == NULL) { + err = -ENOENT; + goto __kctl_end; + } + if (kctl->tlv.p == NULL) { + err = -ENXIO; + goto __kctl_end; + } + vd = &kctl->vd[tlv.numid - kctl->id.numid]; + if ((op_flag == 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) || + (op_flag > 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) == 0) || + (op_flag < 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) == 0)) { + err = -ENXIO; + goto __kctl_end; + } + if (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { + if (file && vd->owner != NULL && vd->owner != file) { + err = -EPERM; + goto __kctl_end; + } + err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv); + if (err > 0) { + up_read(&card->controls_rwsem); + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &kctl->id); + return 0; + } + } else { + if (op_flag) { + err = -ENXIO; + goto __kctl_end; + } + len = kctl->tlv.p[1] + 2 * sizeof(unsigned int); + if (tlv.length < len) { + err = -ENOMEM; + goto __kctl_end; + } + if (copy_to_user(_tlv->tlv, kctl->tlv.p, len)) + err = -EFAULT; + } + __kctl_end: + up_read(&card->controls_rwsem); + return err; +} + static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct snd_ctl_file *ctl; @@ -1086,11 +1198,11 @@ static long snd_ctl_ioctl(struct file *f case SNDRV_CTL_IOCTL_CARD_INFO: return snd_ctl_card_info(card, ctl, cmd, argp); case SNDRV_CTL_IOCTL_ELEM_LIST: - return snd_ctl_elem_list(ctl->card, argp); + return snd_ctl_elem_list(card, argp); case SNDRV_CTL_IOCTL_ELEM_INFO: return snd_ctl_elem_info_user(ctl, argp); case SNDRV_CTL_IOCTL_ELEM_READ: - return snd_ctl_elem_read_user(ctl->card, argp); + return snd_ctl_elem_read_user(card, argp); case SNDRV_CTL_IOCTL_ELEM_WRITE: return snd_ctl_elem_write_user(ctl, argp); case SNDRV_CTL_IOCTL_ELEM_LOCK: @@ -1105,6 +1217,12 @@ static long snd_ctl_ioctl(struct file *f return snd_ctl_elem_remove(ctl, argp); case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS: return snd_ctl_subscribe_events(ctl, ip); + case SNDRV_CTL_IOCTL_TLV_READ: + return snd_ctl_tlv_ioctl(ctl, argp, 0); + case SNDRV_CTL_IOCTL_TLV_WRITE: + return snd_ctl_tlv_ioctl(ctl, argp, 1); + case SNDRV_CTL_IOCTL_TLV_COMMAND: + return snd_ctl_tlv_ioctl(ctl, argp, -1); case SNDRV_CTL_IOCTL_POWER: return -ENOPROTOOPT; case SNDRV_CTL_IOCTL_POWER_STATE: @@ -1338,6 +1456,11 @@ static int snd_ctl_dev_disconnect(struct struct snd_card *card = device->device_data; struct list_head *flist; struct snd_ctl_file *ctl; + int err, cardnum; + + snd_assert(card != NULL, return -ENXIO); + cardnum = card->number; + snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO); down_read(&card->controls_rwsem); list_for_each(flist, &card->ctl_files) { @@ -1346,6 +1469,10 @@ static int snd_ctl_dev_disconnect(struct kill_fasync(&ctl->fasync, SIGIO, POLL_ERR); } up_read(&card->controls_rwsem); + + if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL, + card, -1)) < 0) + return err; return 0; } @@ -1367,23 +1494,6 @@ static int snd_ctl_dev_free(struct snd_d } /* - * de-registration of the control device - */ -static int snd_ctl_dev_unregister(struct snd_device *device) -{ - struct snd_card *card = device->device_data; - int err, cardnum; - - snd_assert(card != NULL, return -ENXIO); - cardnum = card->number; - snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO); - if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL, - card, -1)) < 0) - return err; - return snd_ctl_dev_free(device); -} - -/* * create control core: * called from init.c */ @@ -1393,7 +1503,6 @@ int snd_ctl_create(struct snd_card *card .dev_free = snd_ctl_dev_free, .dev_register = snd_ctl_dev_register, .dev_disconnect = snd_ctl_dev_disconnect, - .dev_unregister = snd_ctl_dev_unregister }; snd_assert(card != NULL, return -ENXIO); diff --git a/sound/core/device.c b/sound/core/device.c index 6ce4da4..ccb2581 100644 --- a/sound/core/device.c +++ b/sound/core/device.c @@ -71,7 +71,7 @@ EXPORT_SYMBOL(snd_device_new); * @device_data: the data pointer to release * * Removes the device from the list on the card and invokes the - * callback, dev_unregister or dev_free, corresponding to the state. + * callbacks, dev_disconnect and dev_free, corresponding to the state. * Then release the device. * * Returns zero if successful, or a negative error code on failure or if the @@ -90,16 +90,14 @@ int snd_device_free(struct snd_card *car continue; /* unlink */ list_del(&dev->list); - if ((dev->state == SNDRV_DEV_REGISTERED || - dev->state == SNDRV_DEV_DISCONNECTED) && - dev->ops->dev_unregister) { - if (dev->ops->dev_unregister(dev)) - snd_printk(KERN_ERR "device unregister failure\n"); - } else { - if (dev->ops->dev_free) { - if (dev->ops->dev_free(dev)) - snd_printk(KERN_ERR "device free failure\n"); - } + if (dev->state == SNDRV_DEV_REGISTERED && + dev->ops->dev_disconnect) + if (dev->ops->dev_disconnect(dev)) + snd_printk(KERN_ERR + "device disconnect failure\n"); + if (dev->ops->dev_free) { + if (dev->ops->dev_free(dev)) + snd_printk(KERN_ERR "device free failure\n"); } kfree(dev); return 0; diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index 8bd0dcc..9aa9d94 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -42,7 +42,7 @@ static DEFINE_MUTEX(register_mutex); static int snd_hwdep_free(struct snd_hwdep *hwdep); static int snd_hwdep_dev_free(struct snd_device *device); static int snd_hwdep_dev_register(struct snd_device *device); -static int snd_hwdep_dev_unregister(struct snd_device *device); +static int snd_hwdep_dev_disconnect(struct snd_device *device); static struct snd_hwdep *snd_hwdep_search(struct snd_card *card, int device) @@ -353,7 +353,7 @@ int snd_hwdep_new(struct snd_card *card, static struct snd_device_ops ops = { .dev_free = snd_hwdep_dev_free, .dev_register = snd_hwdep_dev_register, - .dev_unregister = snd_hwdep_dev_unregister + .dev_disconnect = snd_hwdep_dev_disconnect, }; snd_assert(rhwdep != NULL, return -EINVAL); @@ -439,7 +439,7 @@ #endif return 0; } -static int snd_hwdep_dev_unregister(struct snd_device *device) +static int snd_hwdep_dev_disconnect(struct snd_device *device) { struct snd_hwdep *hwdep = device->device_data; @@ -454,9 +454,9 @@ #ifdef CONFIG_SND_OSSEMUL snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device); #endif snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device); - list_del(&hwdep->list); + list_del_init(&hwdep->list); mutex_unlock(®ister_mutex); - return snd_hwdep_free(hwdep); + return 0; } #ifdef CONFIG_PROC_FS @@ -497,7 +497,7 @@ static void __init snd_hwdep_proc_init(v static void __exit snd_hwdep_proc_done(void) { - snd_info_unregister(snd_hwdep_proc_entry); + snd_info_free_entry(snd_hwdep_proc_entry); } #else /* !CONFIG_PROC_FS */ #define snd_hwdep_proc_init() diff --git a/sound/core/info.c b/sound/core/info.c index 340332c..9663b6b 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -78,6 +78,7 @@ struct snd_info_private_data { static int snd_info_version_init(void); static int snd_info_version_done(void); +static void snd_info_disconnect(struct snd_info_entry *entry); /* resize the proc r/w buffer */ @@ -304,7 +305,7 @@ static int snd_info_entry_open(struct in mutex_lock(&info_mutex); p = PDE(inode); entry = p == NULL ? NULL : (struct snd_info_entry *)p->data; - if (entry == NULL || entry->disconnected) { + if (entry == NULL || ! entry->p) { mutex_unlock(&info_mutex); return -ENODEV; } @@ -586,10 +587,10 @@ int __exit snd_info_done(void) snd_info_version_done(); if (snd_proc_root) { #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) - snd_info_unregister(snd_seq_root); + snd_info_free_entry(snd_seq_root); #endif #ifdef CONFIG_SND_OSSEMUL - snd_info_unregister(snd_oss_root); + snd_info_free_entry(snd_oss_root); #endif snd_remove_proc_entry(&proc_root, snd_proc_root); } @@ -648,17 +649,28 @@ int snd_info_card_register(struct snd_ca * de-register the card proc file * called from init.c */ -int snd_info_card_free(struct snd_card *card) +void snd_info_card_disconnect(struct snd_card *card) { - snd_assert(card != NULL, return -ENXIO); + snd_assert(card != NULL, return); + mutex_lock(&info_mutex); if (card->proc_root_link) { snd_remove_proc_entry(snd_proc_root, card->proc_root_link); card->proc_root_link = NULL; } - if (card->proc_root) { - snd_info_unregister(card->proc_root); - card->proc_root = NULL; - } + if (card->proc_root) + snd_info_disconnect(card->proc_root); + mutex_unlock(&info_mutex); +} + +/* + * release the card proc file resources + * called from init.c + */ +int snd_info_card_free(struct snd_card *card) +{ + snd_assert(card != NULL, return -ENXIO); + snd_info_free_entry(card->proc_root); + card->proc_root = NULL; return 0; } @@ -767,6 +779,8 @@ static struct snd_info_entry *snd_info_c entry->mode = S_IFREG | S_IRUGO; entry->content = SNDRV_INFO_CONTENT_TEXT; mutex_init(&entry->access); + INIT_LIST_HEAD(&entry->children); + INIT_LIST_HEAD(&entry->list); return entry; } @@ -819,30 +833,35 @@ struct snd_info_entry *snd_info_create_c EXPORT_SYMBOL(snd_info_create_card_entry); -static int snd_info_dev_free_entry(struct snd_device *device) +static void snd_info_disconnect(struct snd_info_entry *entry) { - struct snd_info_entry *entry = device->device_data; - snd_info_free_entry(entry); - return 0; -} + struct list_head *p, *n; + struct proc_dir_entry *root; -static int snd_info_dev_register_entry(struct snd_device *device) -{ - struct snd_info_entry *entry = device->device_data; - return snd_info_register(entry); + list_for_each_safe(p, n, &entry->children) { + snd_info_disconnect(list_entry(p, struct snd_info_entry, list)); + } + + if (! entry->p) + return; + list_del_init(&entry->list); + root = entry->parent == NULL ? snd_proc_root : entry->parent->p; + snd_assert(root, return); + snd_remove_proc_entry(root, entry->p); + entry->p = NULL; } -static int snd_info_dev_disconnect_entry(struct snd_device *device) +static int snd_info_dev_free_entry(struct snd_device *device) { struct snd_info_entry *entry = device->device_data; - entry->disconnected = 1; + snd_info_free_entry(entry); return 0; } -static int snd_info_dev_unregister_entry(struct snd_device *device) +static int snd_info_dev_register_entry(struct snd_device *device) { struct snd_info_entry *entry = device->device_data; - return snd_info_unregister(entry); + return snd_info_register(entry); } /** @@ -871,8 +890,7 @@ int snd_card_proc_new(struct snd_card *c static struct snd_device_ops ops = { .dev_free = snd_info_dev_free_entry, .dev_register = snd_info_dev_register_entry, - .dev_disconnect = snd_info_dev_disconnect_entry, - .dev_unregister = snd_info_dev_unregister_entry + /* disconnect is done via snd_info_card_disconnect() */ }; struct snd_info_entry *entry; int err; @@ -901,6 +919,11 @@ void snd_info_free_entry(struct snd_info { if (entry == NULL) return; + if (entry->p) { + mutex_lock(&info_mutex); + snd_info_disconnect(entry); + mutex_unlock(&info_mutex); + } kfree(entry->name); if (entry->private_free) entry->private_free(entry); @@ -935,38 +958,14 @@ int snd_info_register(struct snd_info_en p->size = entry->size; p->data = entry; entry->p = p; + if (entry->parent) + list_add_tail(&entry->list, &entry->parent->children); mutex_unlock(&info_mutex); return 0; } EXPORT_SYMBOL(snd_info_register); -/** - * snd_info_unregister - de-register the info entry - * @entry: the info entry - * - * De-registers the info entry and releases the instance. - * - * Returns zero if successful, or a negative error code on failure. - */ -int snd_info_unregister(struct snd_info_entry * entry) -{ - struct proc_dir_entry *root; - - if (! entry) - return 0; - snd_assert(entry->p != NULL, return -ENXIO); - root = entry->parent == NULL ? snd_proc_root : entry->parent->p; - snd_assert(root, return -ENXIO); - mutex_lock(&info_mutex); - snd_remove_proc_entry(root, entry->p); - mutex_unlock(&info_mutex); - snd_info_free_entry(entry); - return 0; -} - -EXPORT_SYMBOL(snd_info_unregister); - /* */ @@ -999,8 +998,7 @@ static int __init snd_info_version_init( static int __exit snd_info_version_done(void) { - if (snd_info_version_entry) - snd_info_unregister(snd_info_version_entry); + snd_info_free_entry(snd_info_version_entry); return 0; } diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c index bb2c40d..3ebc349 100644 --- a/sound/core/info_oss.c +++ b/sound/core/info_oss.c @@ -131,10 +131,8 @@ int snd_info_minor_register(void) int snd_info_minor_unregister(void) { - if (snd_sndstat_proc_entry) { - snd_info_unregister(snd_sndstat_proc_entry); - snd_sndstat_proc_entry = NULL; - } + snd_info_free_entry(snd_sndstat_proc_entry); + snd_sndstat_proc_entry = NULL; return 0; } diff --git a/sound/core/init.c b/sound/core/init.c index 4d92588..d7607a2 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -81,8 +81,6 @@ #else /* !CONFIG_PROC_FS */ #define init_info_for_card(card) #endif -static void snd_card_free_thread(void * __card); - /** * snd_card_new - create and initialize a soundcard structure * @idx: card index (address) [0 ... (SNDRV_CARDS-1)] @@ -145,7 +143,6 @@ struct snd_card *snd_card_new(int idx, c INIT_LIST_HEAD(&card->ctl_files); spin_lock_init(&card->files_lock); init_waitqueue_head(&card->shutdown_sleep); - INIT_WORK(&card->free_workq, snd_card_free_thread, card); #ifdef CONFIG_PM mutex_init(&card->power_lock); init_waitqueue_head(&card->power_sleep); @@ -310,6 +307,7 @@ #endif if (err < 0) snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number); + snd_info_card_disconnect(card); return 0; } @@ -326,22 +324,10 @@ EXPORT_SYMBOL(snd_card_disconnect); * Returns zero. Frees all associated devices and frees the control * interface associated to given soundcard. */ -int snd_card_free(struct snd_card *card) +static int snd_card_do_free(struct snd_card *card) { struct snd_shutdown_f_ops *s_f_ops; - if (card == NULL) - return -EINVAL; - mutex_lock(&snd_card_mutex); - snd_cards[card->number] = NULL; - mutex_unlock(&snd_card_mutex); - -#ifdef CONFIG_PM - wake_up(&card->power_sleep); -#endif - /* wait, until all devices are ready for the free operation */ - wait_event(card->shutdown_sleep, card->files == NULL); - #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) if (snd_mixer_oss_notify_callback) snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE); @@ -360,7 +346,7 @@ #endif } if (card->private_free) card->private_free(card); - snd_info_unregister(card->proc_id); + snd_info_free_entry(card->proc_id); if (snd_info_card_free(card) < 0) { snd_printk(KERN_WARNING "unable to free card info\n"); /* Not fatal error */ @@ -370,61 +356,59 @@ #endif card->s_f_ops = s_f_ops->next; kfree(s_f_ops); } + kfree(card); + return 0; +} + +static int snd_card_free_prepare(struct snd_card *card) +{ + if (card == NULL) + return -EINVAL; + (void) snd_card_disconnect(card); mutex_lock(&snd_card_mutex); + snd_cards[card->number] = NULL; snd_cards_lock &= ~(1 << card->number); mutex_unlock(&snd_card_mutex); - kfree(card); +#ifdef CONFIG_PM + wake_up(&card->power_sleep); +#endif return 0; } -EXPORT_SYMBOL(snd_card_free); - -static void snd_card_free_thread(void * __card) +int snd_card_free_when_closed(struct snd_card *card) { - struct snd_card *card = __card; - struct module * module = card->module; - - if (!try_module_get(module)) { - snd_printk(KERN_ERR "unable to lock toplevel module for card %i in free thread\n", card->number); - module = NULL; - } + int free_now = 0; + int ret = snd_card_free_prepare(card); + if (ret) + return ret; - snd_card_free(card); + spin_lock(&card->files_lock); + if (card->files == NULL) + free_now = 1; + else + card->free_on_last_close = 1; + spin_unlock(&card->files_lock); - module_put(module); + if (free_now) + snd_card_do_free(card); + return 0; } -/** - * snd_card_free_in_thread - call snd_card_free() in thread - * @card: soundcard structure - * - * This function schedules the call of snd_card_free() function in a - * work queue. When all devices are released (non-busy), the work - * is woken up and calls snd_card_free(). - * - * When a card can be disconnected at any time by hotplug service, - * this function should be used in disconnect (or detach) callback - * instead of calling snd_card_free() directly. - * - * Returns - zero otherwise a negative error code if the start of thread failed. - */ -int snd_card_free_in_thread(struct snd_card *card) -{ - if (card->files == NULL) { - snd_card_free(card); - return 0; - } +EXPORT_SYMBOL(snd_card_free_when_closed); - if (schedule_work(&card->free_workq)) - return 0; +int snd_card_free(struct snd_card *card) +{ + int ret = snd_card_free_prepare(card); + if (ret) + return ret; - snd_printk(KERN_ERR "schedule_work() failed in snd_card_free_in_thread for card %i\n", card->number); - /* try to free the structure immediately */ - snd_card_free(card); - return -EFAULT; + /* wait, until all devices are ready for the free operation */ + wait_event(card->shutdown_sleep, card->files == NULL); + snd_card_do_free(card); + return 0; } -EXPORT_SYMBOL(snd_card_free_in_thread); +EXPORT_SYMBOL(snd_card_free); static void choose_default_id(struct snd_card *card) { @@ -625,9 +609,9 @@ #endif int __exit snd_card_info_done(void) { - snd_info_unregister(snd_card_info_entry); + snd_info_free_entry(snd_card_info_entry); #ifdef MODULE - snd_info_unregister(snd_card_module_info_entry); + snd_info_free_entry(snd_card_module_info_entry); #endif return 0; } @@ -708,15 +692,16 @@ EXPORT_SYMBOL(snd_card_file_add); * * This function removes the file formerly added to the card via * snd_card_file_add() function. - * If all files are removed and the release of the card is - * scheduled, it will wake up the the thread to call snd_card_free() - * (see snd_card_free_in_thread() function). + * If all files are removed and snd_card_free_when_closed() was + * called beforehand, it processes the pending release of + * resources. * * Returns zero or a negative error code. */ int snd_card_file_remove(struct snd_card *card, struct file *file) { struct snd_monitor_file *mfile, *pfile = NULL; + int last_close = 0; spin_lock(&card->files_lock); mfile = card->files; @@ -731,9 +716,14 @@ int snd_card_file_remove(struct snd_card pfile = mfile; mfile = mfile->next; } - spin_unlock(&card->files_lock); if (card->files == NULL) + last_close = 1; + spin_unlock(&card->files_lock); + if (last_close) { wake_up(&card->shutdown_sleep); + if (card->free_on_last_close) + snd_card_do_free(card); + } if (!mfile) { snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file); return -ENOENT; diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 75a9505..f4c6704 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -1193,10 +1193,8 @@ static void snd_mixer_oss_proc_init(stru static void snd_mixer_oss_proc_done(struct snd_mixer_oss *mixer) { - if (mixer->proc_entry) { - snd_info_unregister(mixer->proc_entry); - mixer->proc_entry = NULL; - } + snd_info_free_entry(mixer->proc_entry); + mixer->proc_entry = NULL; } #else /* !CONFIG_PROC_FS */ #define snd_mixer_oss_proc_init(mix) @@ -1312,21 +1310,19 @@ #endif card->mixer_oss = mixer; snd_mixer_oss_build(mixer); snd_mixer_oss_proc_init(mixer); - } else if (cmd == SND_MIXER_OSS_NOTIFY_DISCONNECT) { - mixer = card->mixer_oss; - if (mixer == NULL || !mixer->oss_dev_alloc) - return 0; - snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0); - mixer->oss_dev_alloc = 0; - } else { /* free */ + } else { mixer = card->mixer_oss; if (mixer == NULL) return 0; + if (mixer->oss_dev_alloc) { #ifdef SNDRV_OSS_INFO_DEV_MIXERS - snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number); + snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number); #endif - if (mixer->oss_dev_alloc) snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0); + mixer->oss_dev_alloc = 0; + } + if (cmd == SND_MIXER_OSS_NOTIFY_DISCONNECT) + return 0; snd_mixer_oss_proc_done(mixer); return snd_mixer_oss_free1(mixer); } diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 472fce0..505b23e 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -2846,11 +2846,9 @@ static void snd_pcm_oss_proc_done(struct int stream; for (stream = 0; stream < 2; ++stream) { struct snd_pcm_str *pstr = &pcm->streams[stream]; - if (pstr->oss.proc_entry) { - snd_info_unregister(pstr->oss.proc_entry); - pstr->oss.proc_entry = NULL; - snd_pcm_oss_proc_free_setup_list(pstr); - } + snd_info_free_entry(pstr->oss.proc_entry); + pstr->oss.proc_entry = NULL; + snd_pcm_oss_proc_free_setup_list(pstr); } } #else /* !CONFIG_SND_VERBOSE_PROCFS */ @@ -2931,25 +2929,23 @@ static int snd_pcm_oss_disconnect_minor( snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM, pcm->card, 1); } - } - return 0; -} - -static int snd_pcm_oss_unregister_minor(struct snd_pcm *pcm) -{ - snd_pcm_oss_disconnect_minor(pcm); - if (pcm->oss.reg) { if (dsp_map[pcm->card->number] == (int)pcm->device) { #ifdef SNDRV_OSS_INFO_DEV_AUDIO snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_AUDIO, pcm->card->number); #endif } pcm->oss.reg = 0; - snd_pcm_oss_proc_done(pcm); } return 0; } +static int snd_pcm_oss_unregister_minor(struct snd_pcm *pcm) +{ + snd_pcm_oss_disconnect_minor(pcm); + snd_pcm_oss_proc_done(pcm); + return 0; +} + static struct snd_pcm_notify snd_pcm_oss_notify = { .n_register = snd_pcm_oss_register_minor, diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 7581edd..ed3b094 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -42,7 +42,6 @@ static int snd_pcm_free(struct snd_pcm * static int snd_pcm_dev_free(struct snd_device *device); static int snd_pcm_dev_register(struct snd_device *device); static int snd_pcm_dev_disconnect(struct snd_device *device); -static int snd_pcm_dev_unregister(struct snd_device *device); static struct snd_pcm *snd_pcm_search(struct snd_card *card, int device) { @@ -494,19 +493,13 @@ #endif static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { #ifdef CONFIG_SND_PCM_XRUN_DEBUG - if (pstr->proc_xrun_debug_entry) { - snd_info_unregister(pstr->proc_xrun_debug_entry); - pstr->proc_xrun_debug_entry = NULL; - } + snd_info_free_entry(pstr->proc_xrun_debug_entry); + pstr->proc_xrun_debug_entry = NULL; #endif - if (pstr->proc_info_entry) { - snd_info_unregister(pstr->proc_info_entry); - pstr->proc_info_entry = NULL; - } - if (pstr->proc_root) { - snd_info_unregister(pstr->proc_root); - pstr->proc_root = NULL; - } + snd_info_free_entry(pstr->proc_info_entry); + pstr->proc_info_entry = NULL; + snd_info_free_entry(pstr->proc_root); + pstr->proc_root = NULL; return 0; } @@ -570,29 +563,19 @@ static int snd_pcm_substream_proc_init(s return 0; } - + static int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream) { - if (substream->proc_info_entry) { - snd_info_unregister(substream->proc_info_entry); - substream->proc_info_entry = NULL; - } - if (substream->proc_hw_params_entry) { - snd_info_unregister(substream->proc_hw_params_entry); - substream->proc_hw_params_entry = NULL; - } - if (substream->proc_sw_params_entry) { - snd_info_unregister(substream->proc_sw_params_entry); - substream->proc_sw_params_entry = NULL; - } - if (substream->proc_status_entry) { - snd_info_unregister(substream->proc_status_entry); - substream->proc_status_entry = NULL; - } - if (substream->proc_root) { - snd_info_unregister(substream->proc_root); - substream->proc_root = NULL; - } + snd_info_free_entry(substream->proc_info_entry); + substream->proc_info_entry = NULL; + snd_info_free_entry(substream->proc_hw_params_entry); + substream->proc_hw_params_entry = NULL; + snd_info_free_entry(substream->proc_sw_params_entry); + substream->proc_sw_params_entry = NULL; + snd_info_free_entry(substream->proc_status_entry); + substream->proc_status_entry = NULL; + snd_info_free_entry(substream->proc_root); + substream->proc_root = NULL; return 0; } #else /* !CONFIG_SND_VERBOSE_PROCFS */ @@ -696,7 +679,6 @@ int snd_pcm_new(struct snd_card *card, c .dev_free = snd_pcm_dev_free, .dev_register = snd_pcm_dev_register, .dev_disconnect = snd_pcm_dev_disconnect, - .dev_unregister = snd_pcm_dev_unregister }; snd_assert(rpcm != NULL, return -EINVAL); @@ -740,6 +722,7 @@ #endif substream = pstr->substream; while (substream) { substream_next = substream->next; + snd_pcm_timer_done(substream); snd_pcm_substream_proc_done(substream); kfree(substream); substream = substream_next; @@ -756,7 +739,12 @@ #endif static int snd_pcm_free(struct snd_pcm *pcm) { + struct snd_pcm_notify *notify; + snd_assert(pcm != NULL, return -ENXIO); + list_for_each_entry(notify, &snd_pcm_notify_list, list) { + notify->n_unregister(pcm); + } if (pcm->private_free) pcm->private_free(pcm); snd_pcm_lib_preallocate_free_for_all(pcm); @@ -804,7 +792,8 @@ int snd_pcm_attach_substream(struct snd_ kctl = snd_ctl_file(list); if (kctl->pid == current->pid) { prefer_subdevice = kctl->prefer_pcm_subdevice; - break; + if (prefer_subdevice != -1) + break; } } up_read(&card->controls_rwsem); @@ -971,35 +960,22 @@ static int snd_pcm_dev_register(struct s static int snd_pcm_dev_disconnect(struct snd_device *device) { struct snd_pcm *pcm = device->device_data; - struct list_head *list; + struct snd_pcm_notify *notify; struct snd_pcm_substream *substream; - int cidx; + int cidx, devtype; mutex_lock(®ister_mutex); + if (list_empty(&pcm->list)) + goto unlock; + list_del_init(&pcm->list); for (cidx = 0; cidx < 2; cidx++) for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) if (substream->runtime) substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED; - list_for_each(list, &snd_pcm_notify_list) { - struct snd_pcm_notify *notify; - notify = list_entry(list, struct snd_pcm_notify, list); + list_for_each_entry(notify, &snd_pcm_notify_list, list) { notify->n_disconnect(pcm); } - mutex_unlock(®ister_mutex); - return 0; -} - -static int snd_pcm_dev_unregister(struct snd_device *device) -{ - int cidx, devtype; - struct snd_pcm_substream *substream; - struct list_head *list; - struct snd_pcm *pcm = device->device_data; - - snd_assert(pcm != NULL, return -ENXIO); - mutex_lock(®ister_mutex); - list_del(&pcm->list); for (cidx = 0; cidx < 2; cidx++) { devtype = -1; switch (cidx) { @@ -1011,23 +987,20 @@ static int snd_pcm_dev_unregister(struct break; } snd_unregister_device(devtype, pcm->card, pcm->device); - for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) - snd_pcm_timer_done(substream); - } - list_for_each(list, &snd_pcm_notify_list) { - struct snd_pcm_notify *notify; - notify = list_entry(list, struct snd_pcm_notify, list); - notify->n_unregister(pcm); } + unlock: mutex_unlock(®ister_mutex); - return snd_pcm_free(pcm); + return 0; } int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) { struct list_head *p; - snd_assert(notify != NULL && notify->n_register != NULL && notify->n_unregister != NULL, return -EINVAL); + snd_assert(notify != NULL && + notify->n_register != NULL && + notify->n_unregister != NULL && + notify->n_disconnect, return -EINVAL); mutex_lock(®ister_mutex); if (nfree) { list_del(¬ify->list); @@ -1090,8 +1063,7 @@ static void snd_pcm_proc_init(void) static void snd_pcm_proc_done(void) { - if (snd_pcm_proc_entry) - snd_info_unregister(snd_pcm_proc_entry); + snd_info_free_entry(snd_pcm_proc_entry); } #else /* !CONFIG_PROC_FS */ diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index 2b8aab6..2b53979 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -478,7 +478,7 @@ static long snd_pcm_ioctl_compat(struct * mmap of PCM status/control records because of the size * incompatibility. */ - substream->no_mmap_ctrl = 1; + pcm_file->no_compat_mmap = 1; switch (cmd) { case SNDRV_PCM_IOCTL_PVERSION: diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index 067d205..be030cb 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -101,7 +101,7 @@ int snd_pcm_lib_preallocate_free(struct { snd_pcm_lib_preallocate_dma_free(substream); #ifdef CONFIG_SND_VERBOSE_PROCFS - snd_info_unregister(substream->proc_prealloc_entry); + snd_info_free_entry(substream->proc_prealloc_entry); substream->proc_prealloc_entry = NULL; #endif return 0; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 439f047..0224c70 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -1992,35 +1992,9 @@ int snd_pcm_hw_constraints_complete(stru return 0; } -static void snd_pcm_add_file(struct snd_pcm_str *str, - struct snd_pcm_file *pcm_file) -{ - pcm_file->next = str->files; - str->files = pcm_file; -} - -static void snd_pcm_remove_file(struct snd_pcm_str *str, - struct snd_pcm_file *pcm_file) -{ - struct snd_pcm_file * pcm_file1; - if (str->files == pcm_file) { - str->files = pcm_file->next; - } else { - pcm_file1 = str->files; - while (pcm_file1 && pcm_file1->next != pcm_file) - pcm_file1 = pcm_file1->next; - if (pcm_file1 != NULL) - pcm_file1->next = pcm_file->next; - } -} - static void pcm_release_private(struct snd_pcm_substream *substream) { - struct snd_pcm_file *pcm_file = substream->file; - snd_pcm_unlink(substream); - snd_pcm_remove_file(substream->pstr, pcm_file); - kfree(pcm_file); } void snd_pcm_release_substream(struct snd_pcm_substream *substream) @@ -2060,7 +2034,6 @@ int snd_pcm_open_substream(struct snd_pc return 0; } - substream->no_mmap_ctrl = 0; err = snd_pcm_hw_constraints_init(substream); if (err < 0) { snd_printd("snd_pcm_hw_constraints_init failed\n"); @@ -2105,19 +2078,16 @@ static int snd_pcm_open_file(struct file if (err < 0) return err; - if (substream->ref_count > 1) - pcm_file = substream->file; - else { - pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL); - if (pcm_file == NULL) { - snd_pcm_release_substream(substream); - return -ENOMEM; - } + pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL); + if (pcm_file == NULL) { + snd_pcm_release_substream(substream); + return -ENOMEM; + } + pcm_file->substream = substream; + if (substream->ref_count == 1) { str = substream->pstr; substream->file = pcm_file; substream->pcm_release = pcm_release_private; - pcm_file->substream = substream; - snd_pcm_add_file(str, pcm_file); } file->private_data = pcm_file; *rpcm_file = pcm_file; @@ -2209,6 +2179,7 @@ static int snd_pcm_release(struct inode fasync_helper(-1, file, 0, &substream->runtime->fasync); mutex_lock(&pcm->open_mutex); snd_pcm_release_substream(substream); + kfree(pcm_file); mutex_unlock(&pcm->open_mutex); wake_up(&pcm->open_wait); module_put(pcm->card->module); @@ -3270,11 +3241,11 @@ static int snd_pcm_mmap(struct file *fil offset = area->vm_pgoff << PAGE_SHIFT; switch (offset) { case SNDRV_PCM_MMAP_OFFSET_STATUS: - if (substream->no_mmap_ctrl) + if (pcm_file->no_compat_mmap) return -ENXIO; return snd_pcm_mmap_status(substream, file, area); case SNDRV_PCM_MMAP_OFFSET_CONTROL: - if (substream->no_mmap_ctrl) + if (pcm_file->no_compat_mmap) return -ENXIO; return snd_pcm_mmap_control(substream, file, area); default: diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 8c15c66..269c467 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -55,7 +55,6 @@ static int snd_rawmidi_free(struct snd_r static int snd_rawmidi_dev_free(struct snd_device *device); static int snd_rawmidi_dev_register(struct snd_device *device); static int snd_rawmidi_dev_disconnect(struct snd_device *device); -static int snd_rawmidi_dev_unregister(struct snd_device *device); static LIST_HEAD(snd_rawmidi_devices); static DEFINE_MUTEX(register_mutex); @@ -431,7 +430,8 @@ #endif kctl = snd_ctl_file(list); if (kctl->pid == current->pid) { subdevice = kctl->prefer_rawmidi_subdevice; - break; + if (subdevice != -1) + break; } } up_read(&card->controls_rwsem); @@ -1426,7 +1426,6 @@ int snd_rawmidi_new(struct snd_card *car .dev_free = snd_rawmidi_dev_free, .dev_register = snd_rawmidi_dev_register, .dev_disconnect = snd_rawmidi_dev_disconnect, - .dev_unregister = snd_rawmidi_dev_unregister }; snd_assert(rrawmidi != NULL, return -EINVAL); @@ -1479,6 +1478,14 @@ static void snd_rawmidi_free_substreams( static int snd_rawmidi_free(struct snd_rawmidi *rmidi) { snd_assert(rmidi != NULL, return -ENXIO); + + snd_info_free_entry(rmidi->proc_entry); + rmidi->proc_entry = NULL; + mutex_lock(®ister_mutex); + if (rmidi->ops && rmidi->ops->dev_unregister) + rmidi->ops->dev_unregister(rmidi); + mutex_unlock(®ister_mutex); + snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]); snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]); if (rmidi->private_free) @@ -1587,21 +1594,6 @@ static int snd_rawmidi_dev_disconnect(st mutex_lock(®ister_mutex); list_del_init(&rmidi->list); - mutex_unlock(®ister_mutex); - return 0; -} - -static int snd_rawmidi_dev_unregister(struct snd_device *device) -{ - struct snd_rawmidi *rmidi = device->device_data; - - snd_assert(rmidi != NULL, return -ENXIO); - mutex_lock(®ister_mutex); - list_del(&rmidi->list); - if (rmidi->proc_entry) { - snd_info_unregister(rmidi->proc_entry); - rmidi->proc_entry = NULL; - } #ifdef CONFIG_SND_OSSEMUL if (rmidi->ossreg) { if ((int)rmidi->device == midi_map[rmidi->card->number]) { @@ -1615,17 +1607,9 @@ #endif rmidi->ossreg = 0; } #endif /* CONFIG_SND_OSSEMUL */ - if (rmidi->ops && rmidi->ops->dev_unregister) - rmidi->ops->dev_unregister(rmidi); snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device); mutex_unlock(®ister_mutex); -#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) - if (rmidi->seq_dev) { - snd_device_free(rmidi->card, rmidi->seq_dev); - rmidi->seq_dev = NULL; - } -#endif - return snd_rawmidi_free(rmidi); + return 0; } /** diff --git a/sound/core/rtctimer.c b/sound/core/rtctimer.c index 84704cc..412dd62 100644 --- a/sound/core/rtctimer.c +++ b/sound/core/rtctimer.c @@ -156,7 +156,7 @@ static int __init rtctimer_init(void) static void __exit rtctimer_exit(void) { if (rtctimer) { - snd_timer_global_unregister(rtctimer); + snd_timer_global_free(rtctimer); rtctimer = NULL; } } diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c index e723413..92858cf 100644 --- a/sound/core/seq/oss/seq_oss.c +++ b/sound/core/seq/oss/seq_oss.c @@ -303,8 +303,7 @@ register_proc(void) static void unregister_proc(void) { - if (info_entry) - snd_info_unregister(info_entry); + snd_info_free_entry(info_entry); info_entry = NULL; } #endif /* CONFIG_PROC_FS */ diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index 102ff54..b79d011 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c @@ -90,7 +90,6 @@ static int snd_seq_device_free(struct sn static int snd_seq_device_dev_free(struct snd_device *device); static int snd_seq_device_dev_register(struct snd_device *device); static int snd_seq_device_dev_disconnect(struct snd_device *device); -static int snd_seq_device_dev_unregister(struct snd_device *device); static int init_device(struct snd_seq_device *dev, struct ops_list *ops); static int free_device(struct snd_seq_device *dev, struct ops_list *ops); @@ -189,7 +188,6 @@ int snd_seq_device_new(struct snd_card * .dev_free = snd_seq_device_dev_free, .dev_register = snd_seq_device_dev_register, .dev_disconnect = snd_seq_device_dev_disconnect, - .dev_unregister = snd_seq_device_dev_unregister }; if (result) @@ -309,15 +307,6 @@ static int snd_seq_device_dev_disconnect } /* - * unregister the existing device - */ -static int snd_seq_device_dev_unregister(struct snd_device *device) -{ - struct snd_seq_device *dev = device->device_data; - return snd_seq_device_free(dev); -} - -/* * register device driver * id = driver id * entry = driver operators - duplicated to each instance @@ -573,7 +562,7 @@ static void __exit alsa_seq_device_exit( { remove_drivers(); #ifdef CONFIG_PROC_FS - snd_info_unregister(info_entry); + snd_info_free_entry(info_entry); #endif if (num_ops) snd_printk(KERN_ERR "drivers not released (%d)\n", num_ops); diff --git a/sound/core/seq/seq_info.c b/sound/core/seq/seq_info.c index 142e9e6..8a7fe5c 100644 --- a/sound/core/seq/seq_info.c +++ b/sound/core/seq/seq_info.c @@ -64,9 +64,9 @@ int __init snd_seq_info_init(void) int __exit snd_seq_info_done(void) { - snd_info_unregister(queues_entry); - snd_info_unregister(clients_entry); - snd_info_unregister(timer_entry); + snd_info_free_entry(queues_entry); + snd_info_free_entry(clients_entry); + snd_info_free_entry(timer_entry); return 0; } #endif diff --git a/sound/core/sound.c b/sound/core/sound.c index 7edd1fc..b4430db 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -387,8 +387,7 @@ int __init snd_minor_info_init(void) int __exit snd_minor_info_done(void) { - if (snd_minor_info_entry) - snd_info_unregister(snd_minor_info_entry); + snd_info_free_entry(snd_minor_info_entry); return 0; } #endif /* CONFIG_PROC_FS */ diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c index 74f0fe5..b2fc40a 100644 --- a/sound/core/sound_oss.c +++ b/sound/core/sound_oss.c @@ -270,8 +270,7 @@ int __init snd_minor_info_oss_init(void) int __exit snd_minor_info_oss_done(void) { - if (snd_minor_info_oss_entry) - snd_info_unregister(snd_minor_info_oss_entry); + snd_info_free_entry(snd_minor_info_oss_entry); return 0; } #endif /* CONFIG_PROC_FS */ diff --git a/sound/core/timer.c b/sound/core/timer.c index 0a984e8..10a79ae 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -88,7 +88,7 @@ static DEFINE_MUTEX(register_mutex); static int snd_timer_free(struct snd_timer *timer); static int snd_timer_dev_free(struct snd_device *device); static int snd_timer_dev_register(struct snd_device *device); -static int snd_timer_dev_unregister(struct snd_device *device); +static int snd_timer_dev_disconnect(struct snd_device *device); static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left); @@ -718,7 +718,7 @@ void snd_timer_interrupt(struct snd_time } } if (timer->flags & SNDRV_TIMER_FLG_RESCHED) - snd_timer_reschedule(timer, ticks_left); + snd_timer_reschedule(timer, timer->sticks); if (timer->running) { if (timer->hw.flags & SNDRV_TIMER_HW_STOP) { timer->hw.stop(timer); @@ -773,7 +773,7 @@ int snd_timer_new(struct snd_card *card, static struct snd_device_ops ops = { .dev_free = snd_timer_dev_free, .dev_register = snd_timer_dev_register, - .dev_unregister = snd_timer_dev_unregister + .dev_disconnect = snd_timer_dev_disconnect, }; snd_assert(tid != NULL, return -EINVAL); @@ -813,6 +813,21 @@ int snd_timer_new(struct snd_card *card, static int snd_timer_free(struct snd_timer *timer) { snd_assert(timer != NULL, return -ENXIO); + + mutex_lock(®ister_mutex); + if (! list_empty(&timer->open_list_head)) { + struct list_head *p, *n; + struct snd_timer_instance *ti; + snd_printk(KERN_WARNING "timer %p is busy?\n", timer); + list_for_each_safe(p, n, &timer->open_list_head) { + list_del_init(p); + ti = list_entry(p, struct snd_timer_instance, open_list); + ti->timer = NULL; + } + } + list_del(&timer->device_list); + mutex_unlock(®ister_mutex); + if (timer->private_free) timer->private_free(timer); kfree(timer); @@ -867,30 +882,13 @@ static int snd_timer_dev_register(struct return 0; } -static int snd_timer_unregister(struct snd_timer *timer) +static int snd_timer_dev_disconnect(struct snd_device *device) { - struct list_head *p, *n; - struct snd_timer_instance *ti; - - snd_assert(timer != NULL, return -ENXIO); + struct snd_timer *timer = device->device_data; mutex_lock(®ister_mutex); - if (! list_empty(&timer->open_list_head)) { - snd_printk(KERN_WARNING "timer 0x%lx is busy?\n", (long)timer); - list_for_each_safe(p, n, &timer->open_list_head) { - list_del_init(p); - ti = list_entry(p, struct snd_timer_instance, open_list); - ti->timer = NULL; - } - } - list_del(&timer->device_list); + list_del_init(&timer->device_list); mutex_unlock(®ister_mutex); - return snd_timer_free(timer); -} - -static int snd_timer_dev_unregister(struct snd_device *device) -{ - struct snd_timer *timer = device->device_data; - return snd_timer_unregister(timer); + return 0; } void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstamp) @@ -955,18 +953,12 @@ int snd_timer_global_register(struct snd return snd_timer_dev_register(&dev); } -int snd_timer_global_unregister(struct snd_timer *timer) -{ - return snd_timer_unregister(timer); -} - /* * System timer */ struct snd_timer_system_private { struct timer_list tlist; - struct timer * timer; unsigned long last_expires; unsigned long last_jiffies; unsigned long correction; @@ -978,7 +970,7 @@ static void snd_timer_s_function(unsigne struct snd_timer_system_private *priv = timer->private_data; unsigned long jiff = jiffies; if (time_after(jiff, priv->last_expires)) - priv->correction = (long)jiff - (long)priv->last_expires; + priv->correction += (long)jiff - (long)priv->last_expires; snd_timer_interrupt(timer, (long)jiff - (long)priv->last_jiffies); } @@ -994,7 +986,7 @@ static int snd_timer_s_start(struct snd_ njiff++; } else { njiff += timer->sticks - priv->correction; - priv->correction -= timer->sticks; + priv->correction = 0; } priv->last_expires = priv->tlist.expires = njiff; add_timer(&priv->tlist); @@ -1013,6 +1005,7 @@ static int snd_timer_s_stop(struct snd_t timer->sticks = priv->last_expires - jiff; else timer->sticks = 1; + priv->correction = 0; return 0; } @@ -1126,7 +1119,7 @@ static void __init snd_timer_proc_init(v static void __exit snd_timer_proc_done(void) { - snd_info_unregister(snd_timer_proc_entry); + snd_info_free_entry(snd_timer_proc_entry); } #else /* !CONFIG_PROC_FS */ #define snd_timer_proc_init() @@ -1982,7 +1975,7 @@ static void __exit alsa_timer_exit(void) /* unregister the system timer */ list_for_each_safe(p, n, &snd_timer_list) { struct snd_timer *timer = list_entry(p, struct snd_timer, device_list); - snd_timer_unregister(timer); + snd_timer_free(timer); } snd_timer_proc_done(); #ifdef SNDRV_OSS_INFO_DEV_TIMERS @@ -2005,5 +1998,4 @@ EXPORT_SYMBOL(snd_timer_notify); EXPORT_SYMBOL(snd_timer_global_new); EXPORT_SYMBOL(snd_timer_global_free); EXPORT_SYMBOL(snd_timer_global_register); -EXPORT_SYMBOL(snd_timer_global_unregister); EXPORT_SYMBOL(snd_timer_interrupt); diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig index 395c4ef..952c7f1 100644 --- a/sound/drivers/Kconfig +++ b/sound/drivers/Kconfig @@ -73,6 +73,19 @@ config SND_MTPAV To compile this driver as a module, choose M here: the module will be called snd-mtpav. +config SND_MTS64 + tristate "ESI Miditerminal 4140 driver" + depends on SND && PARPORT + select SND_RAWMIDI + help + The ESI Miditerminal 4140 is a 4 In 4 Out MIDI Interface with + additional SMPTE Timecode capabilities for the parallel port. + + Say 'Y' to include support for this device. + + To compile this driver as a module, chose 'M' here: the module + will be called snd-mts64. + config SND_SERIAL_U16550 tristate "UART16550 serial MIDI driver" depends on SND @@ -100,4 +113,17 @@ config SND_MPU401 To compile this driver as a module, choose M here: the module will be called snd-mpu401. +config SND_AC97_POWER_SAVE + bool "AC97 Power-Saving Mode" + depends on SND_AC97_CODEC && EXPERIMENTAL + default n + help + Say Y here to enable the aggressive power-saving support of + AC97 codecs. In this mode, the power-mode is dynamically + controlled at each open/close. + + The mode is activated by passing power_save=1 option to + snd-ac97-codec driver. You can toggle it dynamically over + sysfs, too. + endmenu diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile index cb98c3d..c9bad6d 100644 --- a/sound/drivers/Makefile +++ b/sound/drivers/Makefile @@ -5,6 +5,7 @@ # snd-dummy-objs := dummy.o snd-mtpav-objs := mtpav.o +snd-mts64-objs := mts64.o snd-serial-u16550-objs := serial-u16550.o snd-virmidi-objs := virmidi.o @@ -13,5 +14,6 @@ obj-$(CONFIG_SND_DUMMY) += snd-dummy.o obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o +obj-$(CONFIG_SND_MTS64) += snd-mts64.o obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index ffeafaf..73b1613 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -285,7 +285,7 @@ static struct snd_pcm_hardware snd_card_ .channels_max = USE_CHANNELS_MAX, .buffer_bytes_max = MAX_BUFFER_SIZE, .period_bytes_min = 64, - .period_bytes_max = MAX_BUFFER_SIZE, + .period_bytes_max = MAX_PERIOD_SIZE, .periods_min = USE_PERIODS_MIN, .periods_max = USE_PERIODS_MAX, .fifo_size = 0, @@ -547,13 +547,13 @@ static struct snd_kcontrol_new snd_dummy DUMMY_VOLUME("Master Volume", 0, MIXER_ADDR_MASTER), DUMMY_CAPSRC("Master Capture Switch", 0, MIXER_ADDR_MASTER), DUMMY_VOLUME("Synth Volume", 0, MIXER_ADDR_SYNTH), -DUMMY_CAPSRC("Synth Capture Switch", 0, MIXER_ADDR_MASTER), +DUMMY_CAPSRC("Synth Capture Switch", 0, MIXER_ADDR_SYNTH), DUMMY_VOLUME("Line Volume", 0, MIXER_ADDR_LINE), -DUMMY_CAPSRC("Line Capture Switch", 0, MIXER_ADDR_MASTER), +DUMMY_CAPSRC("Line Capture Switch", 0, MIXER_ADDR_LINE), DUMMY_VOLUME("Mic Volume", 0, MIXER_ADDR_MIC), -DUMMY_CAPSRC("Mic Capture Switch", 0, MIXER_ADDR_MASTER), +DUMMY_CAPSRC("Mic Capture Switch", 0, MIXER_ADDR_MIC), DUMMY_VOLUME("CD Volume", 0, MIXER_ADDR_CD), -DUMMY_CAPSRC("CD Capture Switch", 0, MIXER_ADDR_MASTER) +DUMMY_CAPSRC("CD Capture Switch", 0, MIXER_ADDR_CD) }; static int __init snd_card_dummy_new_mixer(struct snd_dummy *dummy) diff --git a/sound/drivers/mpu401/mpu401.c b/sound/drivers/mpu401/mpu401.c index 17cc105..2de181a 100644 --- a/sound/drivers/mpu401/mpu401.c +++ b/sound/drivers/mpu401/mpu401.c @@ -211,7 +211,7 @@ static void __devexit snd_mpu401_pnp_rem struct snd_card *card = (struct snd_card *) pnp_get_drvdata(dev); snd_card_disconnect(card); - snd_card_free_in_thread(card); + snd_card_free_when_closed(card); } static struct pnp_driver snd_mpu401_pnp_driver = { diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c new file mode 100644 index 0000000..1699873 --- /dev/null +++ b/sound/drivers/mts64.c @@ -0,0 +1,1091 @@ +/* + * ALSA Driver for Ego Systems Inc. (ESI) Miditerminal 4140 + * Copyright (c) 2006 by Matthias König + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CARD_NAME "Miditerminal 4140" +#define DRIVER_NAME "MTS64" +#define PLATFORM_DRIVER "snd_mts64" + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; + +static struct platform_device *platform_devices[SNDRV_CARDS]; +static int device_count; + +module_param_array(index, int, NULL, S_IRUGO); +MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); +module_param_array(id, charp, NULL, S_IRUGO); +MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); +module_param_array(enable, bool, NULL, S_IRUGO); +MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); + +MODULE_AUTHOR("Matthias Koenig "); +MODULE_DESCRIPTION("ESI Miditerminal 4140"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{ESI,Miditerminal 4140}}"); + +/********************************************************************* + * Chip specific + *********************************************************************/ +#define MTS64_NUM_INPUT_PORTS 5 +#define MTS64_NUM_OUTPUT_PORTS 4 +#define MTS64_SMPTE_SUBSTREAM 4 + +struct mts64 { + spinlock_t lock; + struct snd_card *card; + struct snd_rawmidi *rmidi; + struct pardevice *pardev; + int pardev_claimed; + + int open_count; + int current_midi_output_port; + int current_midi_input_port; + u8 mode[MTS64_NUM_INPUT_PORTS]; + struct snd_rawmidi_substream *midi_input_substream[MTS64_NUM_INPUT_PORTS]; + int smpte_switch; + u8 time[4]; /* [0]=hh, [1]=mm, [2]=ss, [3]=ff */ + u8 fps; +}; + +static int snd_mts64_free(struct mts64 *mts) +{ + kfree(mts); + return 0; +} + +static int __devinit snd_mts64_create(struct snd_card *card, + struct pardevice *pardev, + struct mts64 **rchip) +{ + struct mts64 *mts; + + *rchip = NULL; + + mts = kzalloc(sizeof(struct mts64), GFP_KERNEL); + if (mts == NULL) + return -ENOMEM; + + /* Init chip specific data */ + spin_lock_init(&mts->lock); + mts->card = card; + mts->pardev = pardev; + mts->current_midi_output_port = -1; + mts->current_midi_input_port = -1; + + *rchip = mts; + + return 0; +} + +/********************************************************************* + * HW register related constants + *********************************************************************/ + +/* Status Bits */ +#define MTS64_STAT_BSY 0x80 +#define MTS64_STAT_BIT_SET 0x20 /* readout process, bit is set */ +#define MTS64_STAT_PORT 0x10 /* read byte is a port number */ + +/* Control Bits */ +#define MTS64_CTL_READOUT 0x08 /* enable readout */ +#define MTS64_CTL_WRITE_CMD 0x06 +#define MTS64_CTL_WRITE_DATA 0x02 +#define MTS64_CTL_STROBE 0x01 + +/* Command */ +#define MTS64_CMD_RESET 0xfe +#define MTS64_CMD_PROBE 0x8f /* Used in probing procedure */ +#define MTS64_CMD_SMPTE_SET_TIME 0xe8 +#define MTS64_CMD_SMPTE_SET_FPS 0xee +#define MTS64_CMD_SMPTE_STOP 0xef +#define MTS64_CMD_SMPTE_FPS_24 0xe3 +#define MTS64_CMD_SMPTE_FPS_25 0xe2 +#define MTS64_CMD_SMPTE_FPS_2997 0xe4 +#define MTS64_CMD_SMPTE_FPS_30D 0xe1 +#define MTS64_CMD_SMPTE_FPS_30 0xe0 +#define MTS64_CMD_COM_OPEN 0xf8 /* setting the communication mode */ +#define MTS64_CMD_COM_CLOSE1 0xff /* clearing communication mode */ +#define MTS64_CMD_COM_CLOSE2 0xf5 + +/********************************************************************* + * Hardware specific functions + *********************************************************************/ +static void mts64_enable_readout(struct parport *p); +static void mts64_disable_readout(struct parport *p); +static int mts64_device_ready(struct parport *p); +static int mts64_device_init(struct parport *p); +static int mts64_device_open(struct mts64 *mts); +static int mts64_device_close(struct mts64 *mts); +static u8 mts64_map_midi_input(u8 c); +static int mts64_probe(struct parport *p); +static u16 mts64_read(struct parport *p); +static u8 mts64_read_char(struct parport *p); +static void mts64_smpte_start(struct parport *p, + u8 hours, u8 minutes, + u8 seconds, u8 frames, + u8 idx); +static void mts64_smpte_stop(struct parport *p); +static void mts64_write_command(struct parport *p, u8 c); +static void mts64_write_data(struct parport *p, u8 c); +static void mts64_write_midi(struct mts64 *mts, u8 c, int midiport); + + +/* Enables the readout procedure + * + * Before we can read a midi byte from the device, we have to set + * bit 3 of control port. + */ +static void mts64_enable_readout(struct parport *p) +{ + u8 c; + + c = parport_read_control(p); + c |= MTS64_CTL_READOUT; + parport_write_control(p, c); +} + +/* Disables readout + * + * Readout is disabled by clearing bit 3 of control + */ +static void mts64_disable_readout(struct parport *p) +{ + u8 c; + + c = parport_read_control(p); + c &= ~MTS64_CTL_READOUT; + parport_write_control(p, c); +} + +/* waits for device ready + * + * Checks if BUSY (Bit 7 of status) is clear + * 1 device ready + * 0 failure + */ +static int mts64_device_ready(struct parport *p) +{ + int i; + u8 c; + + for (i = 0; i < 0xffff; ++i) { + c = parport_read_status(p); + c &= MTS64_STAT_BSY; + if (c != 0) + return 1; + } + + return 0; +} + +/* Init device (LED blinking startup magic) + * + * Returns: + * 0 init ok + * -EIO failure + */ +static int __devinit mts64_device_init(struct parport *p) +{ + int i; + + mts64_write_command(p, MTS64_CMD_RESET); + + for (i = 0; i < 64; ++i) { + msleep(100); + + if (mts64_probe(p) == 0) { + /* success */ + mts64_disable_readout(p); + return 0; + } + } + mts64_disable_readout(p); + + return -EIO; +} + +/* + * Opens the device (set communication mode) + */ +static int mts64_device_open(struct mts64 *mts) +{ + int i; + struct parport *p = mts->pardev->port; + + for (i = 0; i < 5; ++i) + mts64_write_command(p, MTS64_CMD_COM_OPEN); + + return 0; +} + +/* + * Close device (clear communication mode) + */ +static int mts64_device_close(struct mts64 *mts) +{ + int i; + struct parport *p = mts->pardev->port; + + for (i = 0; i < 5; ++i) { + mts64_write_command(p, MTS64_CMD_COM_CLOSE1); + mts64_write_command(p, MTS64_CMD_COM_CLOSE2); + } + + return 0; +} + +/* map hardware port to substream number + * + * When reading a byte from the device, the device tells us + * on what port the byte is. This HW port has to be mapped to + * the midiport (substream number). + * substream 0-3 are Midiports 1-4 + * substream 4 is SMPTE Timecode + * The mapping is done by the table: + * HW | 0 | 1 | 2 | 3 | 4 + * SW | 0 | 1 | 4 | 2 | 3 + */ +static u8 mts64_map_midi_input(u8 c) +{ + static u8 map[] = { 0, 1, 4, 2, 3 }; + + return map[c]; +} + + +/* Probe parport for device + * + * Do we have a Miditerminal 4140 on parport? + * Returns: + * 0 device found + * -ENODEV no device + */ +static int __devinit mts64_probe(struct parport *p) +{ + u8 c; + + mts64_smpte_stop(p); + mts64_write_command(p, MTS64_CMD_PROBE); + + msleep(50); + + c = mts64_read(p); + + c &= 0x00ff; + if (c != MTS64_CMD_PROBE) + return -ENODEV; + else + return 0; + +} + +/* Read byte incl. status from device + * + * Returns: + * data in lower 8 bits and status in upper 8 bits + */ +static u16 mts64_read(struct parport *p) +{ + u8 data, status; + + mts64_device_ready(p); + mts64_enable_readout(p); + status = parport_read_status(p); + data = mts64_read_char(p); + mts64_disable_readout(p); + + return (status << 8) | data; +} + +/* Read a byte from device + * + * Note, that readout mode has to be enabled. + * readout procedure is as follows: + * - Write number of the Bit to read to DATA + * - Read STATUS + * - Bit 5 of STATUS indicates if Bit is set + * + * Returns: + * Byte read from device + */ +static u8 mts64_read_char(struct parport *p) +{ + u8 c = 0; + u8 status; + u8 i; + + for (i = 0; i < 8; ++i) { + parport_write_data(p, i); + c >>= 1; + status = parport_read_status(p); + if (status & MTS64_STAT_BIT_SET) + c |= 0x80; + } + + return c; +} + +/* Starts SMPTE Timecode generation + * + * The device creates SMPTE Timecode by hardware. + * 0 24 fps + * 1 25 fps + * 2 29.97 fps + * 3 30 fps (Drop-frame) + * 4 30 fps + */ +static void mts64_smpte_start(struct parport *p, + u8 hours, u8 minutes, + u8 seconds, u8 frames, + u8 idx) +{ + static u8 fps[5] = { MTS64_CMD_SMPTE_FPS_24, + MTS64_CMD_SMPTE_FPS_25, + MTS64_CMD_SMPTE_FPS_2997, + MTS64_CMD_SMPTE_FPS_30D, + MTS64_CMD_SMPTE_FPS_30 }; + + mts64_write_command(p, MTS64_CMD_SMPTE_SET_TIME); + mts64_write_command(p, frames); + mts64_write_command(p, seconds); + mts64_write_command(p, minutes); + mts64_write_command(p, hours); + + mts64_write_command(p, MTS64_CMD_SMPTE_SET_FPS); + mts64_write_command(p, fps[idx]); +} + +/* Stops SMPTE Timecode generation + */ +static void mts64_smpte_stop(struct parport *p) +{ + mts64_write_command(p, MTS64_CMD_SMPTE_STOP); +} + +/* Write a command byte to device + */ +static void mts64_write_command(struct parport *p, u8 c) +{ + mts64_device_ready(p); + + parport_write_data(p, c); + + parport_write_control(p, MTS64_CTL_WRITE_CMD); + parport_write_control(p, MTS64_CTL_WRITE_CMD | MTS64_CTL_STROBE); + parport_write_control(p, MTS64_CTL_WRITE_CMD); +} + +/* Write a data byte to device + */ +static void mts64_write_data(struct parport *p, u8 c) +{ + mts64_device_ready(p); + + parport_write_data(p, c); + + parport_write_control(p, MTS64_CTL_WRITE_DATA); + parport_write_control(p, MTS64_CTL_WRITE_DATA | MTS64_CTL_STROBE); + parport_write_control(p, MTS64_CTL_WRITE_DATA); +} + +/* Write a MIDI byte to midiport + * + * midiport ranges from 0-3 and maps to Ports 1-4 + * assumptions: communication mode is on + */ +static void mts64_write_midi(struct mts64 *mts, u8 c, + int midiport) +{ + struct parport *p = mts->pardev->port; + + /* check current midiport */ + if (mts->current_midi_output_port != midiport) + mts64_write_command(p, midiport); + + /* write midi byte */ + mts64_write_data(p, c); +} + +/********************************************************************* + * Control elements + *********************************************************************/ + +/* SMPTE Switch */ +static int snd_mts64_ctl_smpte_switch_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *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_mts64_ctl_smpte_switch_get(struct snd_kcontrol* kctl, + struct snd_ctl_elem_value *uctl) +{ + struct mts64 *mts = snd_kcontrol_chip(kctl); + + spin_lock_irq(&mts->lock); + uctl->value.integer.value[0] = mts->smpte_switch; + spin_unlock_irq(&mts->lock); + + return 0; +} + +/* smpte_switch is not accessed from IRQ handler, so we just need + to protect the HW access */ +static int snd_mts64_ctl_smpte_switch_put(struct snd_kcontrol* kctl, + struct snd_ctl_elem_value *uctl) +{ + struct mts64 *mts = snd_kcontrol_chip(kctl); + int changed = 0; + + spin_lock_irq(&mts->lock); + if (mts->smpte_switch == uctl->value.integer.value[0]) + goto __out; + + changed = 1; + mts->smpte_switch = uctl->value.integer.value[0]; + if (mts->smpte_switch) { + mts64_smpte_start(mts->pardev->port, + mts->time[0], mts->time[1], + mts->time[2], mts->time[3], + mts->fps); + } else { + mts64_smpte_stop(mts->pardev->port); + } +__out: + spin_unlock_irq(&mts->lock); + return changed; +} + +static struct snd_kcontrol_new mts64_ctl_smpte_switch __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI, + .name = "SMPTE Playback Switch", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .private_value = 0, + .info = snd_mts64_ctl_smpte_switch_info, + .get = snd_mts64_ctl_smpte_switch_get, + .put = snd_mts64_ctl_smpte_switch_put +}; + +/* Time */ +static int snd_mts64_ctl_smpte_time_h_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 23; + return 0; +} + +static int snd_mts64_ctl_smpte_time_f_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 99; + return 0; +} + +static int snd_mts64_ctl_smpte_time_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 59; + return 0; +} + +static int snd_mts64_ctl_smpte_time_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *uctl) +{ + struct mts64 *mts = snd_kcontrol_chip(kctl); + int idx = kctl->private_value; + + spin_lock_irq(&mts->lock); + uctl->value.integer.value[0] = mts->time[idx]; + spin_unlock_irq(&mts->lock); + + return 0; +} + +static int snd_mts64_ctl_smpte_time_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *uctl) +{ + struct mts64 *mts = snd_kcontrol_chip(kctl); + int idx = kctl->private_value; + int changed = 0; + + spin_lock_irq(&mts->lock); + if (mts->time[idx] != uctl->value.integer.value[0]) { + changed = 1; + mts->time[idx] = uctl->value.integer.value[0]; + } + spin_unlock_irq(&mts->lock); + + return changed; +} + +static struct snd_kcontrol_new mts64_ctl_smpte_time_hours __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI, + .name = "SMPTE Time Hours", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .private_value = 0, + .info = snd_mts64_ctl_smpte_time_h_info, + .get = snd_mts64_ctl_smpte_time_get, + .put = snd_mts64_ctl_smpte_time_put +}; + +static struct snd_kcontrol_new mts64_ctl_smpte_time_minutes __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI, + .name = "SMPTE Time Minutes", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .private_value = 1, + .info = snd_mts64_ctl_smpte_time_info, + .get = snd_mts64_ctl_smpte_time_get, + .put = snd_mts64_ctl_smpte_time_put +}; + +static struct snd_kcontrol_new mts64_ctl_smpte_time_seconds __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI, + .name = "SMPTE Time Seconds", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .private_value = 2, + .info = snd_mts64_ctl_smpte_time_info, + .get = snd_mts64_ctl_smpte_time_get, + .put = snd_mts64_ctl_smpte_time_put +}; + +static struct snd_kcontrol_new mts64_ctl_smpte_time_frames __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI, + .name = "SMPTE Time Frames", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .private_value = 3, + .info = snd_mts64_ctl_smpte_time_f_info, + .get = snd_mts64_ctl_smpte_time_get, + .put = snd_mts64_ctl_smpte_time_put +}; + +/* FPS */ +static int snd_mts64_ctl_smpte_fps_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[5] = { "24", + "25", + "29.97", + "30D", + "30" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 5; + if (uinfo->value.enumerated.item > 4) + uinfo->value.enumerated.item = 4; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int snd_mts64_ctl_smpte_fps_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *uctl) +{ + struct mts64 *mts = snd_kcontrol_chip(kctl); + + spin_lock_irq(&mts->lock); + uctl->value.enumerated.item[0] = mts->fps; + spin_unlock_irq(&mts->lock); + + return 0; +} + +static int snd_mts64_ctl_smpte_fps_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *uctl) +{ + struct mts64 *mts = snd_kcontrol_chip(kctl); + int changed = 0; + + spin_lock_irq(&mts->lock); + if (mts->fps != uctl->value.enumerated.item[0]) { + changed = 1; + mts->fps = uctl->value.enumerated.item[0]; + } + spin_unlock_irq(&mts->lock); + + return changed; +} + +static struct snd_kcontrol_new mts64_ctl_smpte_fps __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI, + .name = "SMPTE Fps", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .private_value = 0, + .info = snd_mts64_ctl_smpte_fps_info, + .get = snd_mts64_ctl_smpte_fps_get, + .put = snd_mts64_ctl_smpte_fps_put +}; + + +static int __devinit snd_mts64_ctl_create(struct snd_card *card, + struct mts64 *mts) +{ + int err, i; + static struct snd_kcontrol_new *control[] = { + &mts64_ctl_smpte_switch, + &mts64_ctl_smpte_time_hours, + &mts64_ctl_smpte_time_minutes, + &mts64_ctl_smpte_time_seconds, + &mts64_ctl_smpte_time_frames, + &mts64_ctl_smpte_fps, + 0 }; + + for (i = 0; control[i]; ++i) { + err = snd_ctl_add(card, snd_ctl_new1(control[i], mts)); + if (err < 0) { + snd_printd("Cannot create control: %s\n", + control[i]->name); + return err; + } + } + + return 0; +} + +/********************************************************************* + * Rawmidi + *********************************************************************/ +#define MTS64_MODE_INPUT_TRIGGERED 0x01 + +static int snd_mts64_rawmidi_open(struct snd_rawmidi_substream *substream) +{ + struct mts64 *mts = substream->rmidi->private_data; + + if (mts->open_count == 0) { + /* We don't need a spinlock here, because this is just called + if the device has not been opened before. + So there aren't any IRQs from the device */ + mts64_device_open(mts); + + msleep(50); + } + ++(mts->open_count); + + return 0; +} + +static int snd_mts64_rawmidi_close(struct snd_rawmidi_substream *substream) +{ + struct mts64 *mts = substream->rmidi->private_data; + unsigned long flags; + + --(mts->open_count); + if (mts->open_count == 0) { + /* We need the spinlock_irqsave here because we can still + have IRQs at this point */ + spin_lock_irqsave(&mts->lock, flags); + mts64_device_close(mts); + spin_unlock_irqrestore(&mts->lock, flags); + + msleep(500); + + } else if (mts->open_count < 0) + mts->open_count = 0; + + return 0; +} + +static void snd_mts64_rawmidi_output_trigger(struct snd_rawmidi_substream *substream, + int up) +{ + struct mts64 *mts = substream->rmidi->private_data; + u8 data; + unsigned long flags; + + spin_lock_irqsave(&mts->lock, flags); + while (snd_rawmidi_transmit_peek(substream, &data, 1) == 1) { + mts64_write_midi(mts, data, substream->number+1); + snd_rawmidi_transmit_ack(substream, 1); + } + spin_unlock_irqrestore(&mts->lock, flags); +} + +static void snd_mts64_rawmidi_input_trigger(struct snd_rawmidi_substream *substream, + int up) +{ + struct mts64 *mts = substream->rmidi->private_data; + unsigned long flags; + + spin_lock_irqsave(&mts->lock, flags); + if (up) + mts->mode[substream->number] |= MTS64_MODE_INPUT_TRIGGERED; + else + mts->mode[substream->number] &= ~MTS64_MODE_INPUT_TRIGGERED; + + spin_unlock_irqrestore(&mts->lock, flags); +} + +static struct snd_rawmidi_ops snd_mts64_rawmidi_output_ops = { + .open = snd_mts64_rawmidi_open, + .close = snd_mts64_rawmidi_close, + .trigger = snd_mts64_rawmidi_output_trigger +}; + +static struct snd_rawmidi_ops snd_mts64_rawmidi_input_ops = { + .open = snd_mts64_rawmidi_open, + .close = snd_mts64_rawmidi_close, + .trigger = snd_mts64_rawmidi_input_trigger +}; + +/* Create and initialize the rawmidi component */ +static int __devinit snd_mts64_rawmidi_create(struct snd_card *card) +{ + struct mts64 *mts = card->private_data; + struct snd_rawmidi *rmidi; + struct snd_rawmidi_substream *substream; + struct list_head *list; + int err; + + err = snd_rawmidi_new(card, CARD_NAME, 0, + MTS64_NUM_OUTPUT_PORTS, + MTS64_NUM_INPUT_PORTS, + &rmidi); + if (err < 0) + return err; + + rmidi->private_data = mts; + strcpy(rmidi->name, CARD_NAME); + rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + + mts->rmidi = rmidi; + + /* register rawmidi ops */ + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, + &snd_mts64_rawmidi_output_ops); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, + &snd_mts64_rawmidi_input_ops); + + /* name substreams */ + /* output */ + list_for_each(list, + &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) { + substream = list_entry(list, struct snd_rawmidi_substream, list); + sprintf(substream->name, + "Miditerminal %d", substream->number+1); + } + /* input */ + list_for_each(list, + &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) { + substream = list_entry(list, struct snd_rawmidi_substream, list); + mts->midi_input_substream[substream->number] = substream; + switch(substream->number) { + case MTS64_SMPTE_SUBSTREAM: + strcpy(substream->name, "Miditerminal SMPTE"); + break; + default: + sprintf(substream->name, + "Miditerminal %d", substream->number+1); + } + } + + /* controls */ + err = snd_mts64_ctl_create(card, mts); + + return err; +} + +/********************************************************************* + * parport stuff + *********************************************************************/ +static void snd_mts64_interrupt(int irq, void *private, struct pt_regs *r) +{ + struct mts64 *mts = ((struct snd_card*)private)->private_data; + u16 ret; + u8 status, data; + struct snd_rawmidi_substream *substream; + + spin_lock(&mts->lock); + ret = mts64_read(mts->pardev->port); + data = ret & 0x00ff; + status = ret >> 8; + + if (status & MTS64_STAT_PORT) { + mts->current_midi_input_port = mts64_map_midi_input(data); + } else { + if (mts->current_midi_input_port == -1) + goto __out; + substream = mts->midi_input_substream[mts->current_midi_input_port]; + if (mts->mode[substream->number] & MTS64_MODE_INPUT_TRIGGERED) + snd_rawmidi_receive(substream, &data, 1); + } +__out: + spin_unlock(&mts->lock); +} + +static int __devinit snd_mts64_probe_port(struct parport *p) +{ + struct pardevice *pardev; + int res; + + pardev = parport_register_device(p, DRIVER_NAME, + NULL, NULL, NULL, + 0, NULL); + if (!pardev) + return -EIO; + + if (parport_claim(pardev)) { + parport_unregister_device(pardev); + return -EIO; + } + + res = mts64_probe(p); + + parport_release(pardev); + parport_unregister_device(pardev); + + return res; +} + +static void __devinit snd_mts64_attach(struct parport *p) +{ + struct platform_device *device; + + device = platform_device_alloc(PLATFORM_DRIVER, device_count); + if (!device) + return; + + /* Temporary assignment to forward the parport */ + platform_set_drvdata(device, p); + + if (platform_device_register(device) < 0) { + platform_device_put(device); + return; + } + + /* Since we dont get the return value of probe + * We need to check if device probing succeeded or not */ + if (!platform_get_drvdata(device)) { + platform_device_unregister(device); + return; + } + + /* register device in global table */ + platform_devices[device_count] = device; + device_count++; +} + +static void snd_mts64_detach(struct parport *p) +{ + /* nothing to do here */ +} + +static struct parport_driver mts64_parport_driver = { + .name = "mts64", + .attach = snd_mts64_attach, + .detach = snd_mts64_detach +}; + +/********************************************************************* + * platform stuff + *********************************************************************/ +static void snd_mts64_card_private_free(struct snd_card *card) +{ + struct mts64 *mts = card->private_data; + struct pardevice *pardev = mts->pardev; + + if (pardev) { + if (mts->pardev_claimed) + parport_release(pardev); + parport_unregister_device(pardev); + } + + snd_mts64_free(mts); +} + +static int __devinit snd_mts64_probe(struct platform_device *pdev) +{ + struct pardevice *pardev; + struct parport *p; + int dev = pdev->id; + struct snd_card *card = NULL; + struct mts64 *mts = NULL; + int err; + + p = platform_get_drvdata(pdev); + platform_set_drvdata(pdev, NULL); + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) + return -ENOENT; + if ((err = snd_mts64_probe_port(p)) < 0) + return err; + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) { + snd_printd("Cannot create card\n"); + return -ENOMEM; + } + strcpy(card->driver, DRIVER_NAME); + strcpy(card->shortname, "ESI " CARD_NAME); + sprintf(card->longname, "%s at 0x%lx, irq %i", + card->shortname, p->base, p->irq); + + pardev = parport_register_device(p, /* port */ + DRIVER_NAME, /* name */ + NULL, /* preempt */ + NULL, /* wakeup */ + snd_mts64_interrupt, /* ISR */ + PARPORT_DEV_EXCL, /* flags */ + (void *)card); /* private */ + if (pardev == NULL) { + snd_printd("Cannot register pardevice\n"); + err = -EIO; + goto __err; + } + + if ((err = snd_mts64_create(card, pardev, &mts)) < 0) { + snd_printd("Cannot create main component\n"); + parport_unregister_device(pardev); + goto __err; + } + card->private_data = mts; + card->private_free = snd_mts64_card_private_free; + + if ((err = snd_mts64_rawmidi_create(card)) < 0) { + snd_printd("Creating Rawmidi component failed\n"); + goto __err; + } + + /* claim parport */ + if (parport_claim(pardev)) { + snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base); + err = -EIO; + goto __err; + } + mts->pardev_claimed = 1; + + /* init device */ + if ((err = mts64_device_init(p)) < 0) + goto __err; + + platform_set_drvdata(pdev, card); + + /* At this point card will be usable */ + if ((err = snd_card_register(card)) < 0) { + snd_printd("Cannot register card\n"); + goto __err; + } + + snd_printk("ESI Miditerminal 4140 on 0x%lx\n", p->base); + return 0; + +__err: + snd_card_free(card); + return err; +} + +static int snd_mts64_remove(struct platform_device *pdev) +{ + struct snd_card *card = platform_get_drvdata(pdev); + + if (card) + snd_card_free(card); + + return 0; +} + + +static struct platform_driver snd_mts64_driver = { + .probe = snd_mts64_probe, + .remove = snd_mts64_remove, + .driver = { + .name = PLATFORM_DRIVER + } +}; + +/********************************************************************* + * module init stuff + *********************************************************************/ +static void snd_mts64_unregister_all(void) +{ + int i; + + for (i = 0; i < SNDRV_CARDS; ++i) { + if (platform_devices[i]) { + platform_device_unregister(platform_devices[i]); + platform_devices[i] = NULL; + } + } + platform_driver_unregister(&snd_mts64_driver); + parport_unregister_driver(&mts64_parport_driver); +} + +static int __init snd_mts64_module_init(void) +{ + int err; + + if ((err = platform_driver_register(&snd_mts64_driver)) < 0) + return err; + + if (parport_register_driver(&mts64_parport_driver) != 0) { + platform_driver_unregister(&snd_mts64_driver); + return -EIO; + } + + if (device_count == 0) { + snd_mts64_unregister_all(); + return -ENODEV; + } + + return 0; +} + +static void __exit snd_mts64_module_exit(void) +{ + snd_mts64_unregister_all(); +} + +module_init(snd_mts64_module_init); +module_exit(snd_mts64_module_exit); diff --git a/sound/drivers/opl4/opl4_proc.c b/sound/drivers/opl4/opl4_proc.c index e552ec3..11dd811 100644 --- a/sound/drivers/opl4/opl4_proc.c +++ b/sound/drivers/opl4/opl4_proc.c @@ -159,8 +159,7 @@ int snd_opl4_create_proc(struct snd_opl4 void snd_opl4_free_proc(struct snd_opl4 *opl4) { - if (opl4->proc_entry) - snd_info_unregister(opl4->proc_entry); + snd_info_free_entry(opl4->proc_entry); } #endif /* CONFIG_PROC_FS */ diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c index dc7cc20..0aea536 100644 --- a/sound/i2c/other/ak4xxx-adda.c +++ b/sound/i2c/other/ak4xxx-adda.c @@ -137,6 +137,8 @@ void snd_akm4xxx_reset(struct snd_akm4xx case SND_AK4381: ak4381_reset(ak, state); break; + default: + break; } } @@ -472,6 +474,57 @@ static int snd_akm4xxx_deemphasis_put(st return change; } +static int ak4xxx_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *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 ak4xxx_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol); + int chip = AK_GET_CHIP(kcontrol->private_value); + int addr = AK_GET_ADDR(kcontrol->private_value); + int shift = AK_GET_SHIFT(kcontrol->private_value); + int invert = AK_GET_INVERT(kcontrol->private_value); + unsigned char val = snd_akm4xxx_get(ak, chip, addr); + + if (invert) + val = ! val; + ucontrol->value.integer.value[0] = (val & (1<private_value); + int addr = AK_GET_ADDR(kcontrol->private_value); + int shift = AK_GET_SHIFT(kcontrol->private_value); + int invert = AK_GET_INVERT(kcontrol->private_value); + long flag = ucontrol->value.integer.value[0]; + unsigned char val, oval; + int change; + + if (invert) + flag = ! flag; + oval = snd_akm4xxx_get(ak, chip, addr); + if (flag) + val = oval | (1<type == SND_AK5365) { + memset(ctl, 0, sizeof(*ctl)); + if (ak->channel_names == NULL) + strcpy(ctl->id.name, "Capture Volume"); + else + strcpy(ctl->id.name, ak->channel_names[0]); + ctl->id.index = ak->idx_offset * 2; + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl->count = 1; + ctl->info = snd_akm4xxx_stereo_volume_info; + ctl->get = snd_akm4xxx_stereo_volume_get; + ctl->put = snd_akm4xxx_stereo_volume_put; + /* Registers 4 & 5 (see AK5365 data sheet, pages 34 and 35): + * valid values are from 0x00 (mute) to 0x98 (+12dB). */ + ctl->private_value = + AK_COMPOSE(0, 4, 0, 0x98); + ctl->private_data = ak; + err = snd_ctl_add(ak->card, + snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ| + SNDRV_CTL_ELEM_ACCESS_WRITE)); + if (err < 0) + goto __error; + + memset(ctl, 0, sizeof(*ctl)); + if (ak->channel_names == NULL) + strcpy(ctl->id.name, "Capture Switch"); + else + strcpy(ctl->id.name, ak->channel_names[1]); + ctl->id.index = ak->idx_offset * 2; + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl->count = 1; + ctl->info = ak4xxx_switch_info; + ctl->get = ak4xxx_switch_get; + ctl->put = ak4xxx_switch_put; + /* register 2, bit 0 (SMUTE): 0 = normal operation, 1 = mute */ + ctl->private_value = + AK_COMPOSE(0, 2, 0, 0) | AK_INVERT; + ctl->private_data = ak; + err = snd_ctl_add(ak->card, + snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ| + SNDRV_CTL_ELEM_ACCESS_WRITE)); + if (err < 0) + goto __error; + } + if (ak->type == SND_AK4355 || ak->type == SND_AK4358) num_emphs = 1; else @@ -630,6 +729,9 @@ int snd_akm4xxx_build_controls(struct sn case SND_AK4381: ctl->private_value = AK_COMPOSE(idx, 1, 1, 0); break; + default: + err = -EINVAL; + goto __error; } ctl->private_data = ak; err = snd_ctl_add(ak->card, diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c index 34998de..8581820 100644 --- a/sound/isa/es18xx.c +++ b/sound/isa/es18xx.c @@ -2038,7 +2038,80 @@ MODULE_PARM_DESC(dma2, "DMA 2 # for ES18 static struct platform_device *platform_devices[SNDRV_CARDS]; #ifdef CONFIG_PNP -static int pnp_registered; +static int pnp_registered, pnpc_registered; + +static struct pnp_device_id snd_audiodrive_pnpbiosids[] = { + { .id = "ESS1869" }, + { .id = "" } /* end */ +}; + +MODULE_DEVICE_TABLE(pnp, snd_audiodrive_pnpbiosids); + +/* PnP main device initialization */ +static int __devinit snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev, + struct pnp_resource_table *cfg) +{ + int err; + + pnp_init_resource_table(cfg); + if (port[dev] != SNDRV_AUTO_PORT) + pnp_resource_change(&cfg->port_resource[0], port[dev], 16); + if (fm_port[dev] != SNDRV_AUTO_PORT) + pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4); + if (mpu_port[dev] != SNDRV_AUTO_PORT) + pnp_resource_change(&cfg->port_resource[2], mpu_port[dev], 2); + if (dma1[dev] != SNDRV_AUTO_DMA) + pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1); + if (dma2[dev] != SNDRV_AUTO_DMA) + pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1); + if (irq[dev] != SNDRV_AUTO_IRQ) + pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); + if (pnp_device_is_isapnp(pdev)) { + err = pnp_manual_config_dev(pdev, cfg, 0); + if (err < 0) + snd_printk(KERN_ERR PFX "PnP manual resources are invalid, using auto config\n"); + } + err = pnp_activate_dev(pdev); + if (err < 0) { + snd_printk(KERN_ERR PFX "PnP configure failure (out of resources?)\n"); + return -EBUSY; + } + /* ok. hack using Vendor-Defined Card-Level registers */ + /* skip csn and logdev initialization - already done in isapnp_configure */ + if (pnp_device_is_isapnp(pdev)) { + isapnp_cfg_begin(isapnp_card_number(pdev), isapnp_csn_number(pdev)); + isapnp_write_byte(0x27, pnp_irq(pdev, 0)); /* Hardware Volume IRQ Number */ + if (mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_write_byte(0x28, pnp_irq(pdev, 0)); /* MPU-401 IRQ Number */ + isapnp_write_byte(0x72, pnp_irq(pdev, 0)); /* second IRQ */ + isapnp_cfg_end(); + } + port[dev] = pnp_port_start(pdev, 0); + fm_port[dev] = pnp_port_start(pdev, 1); + mpu_port[dev] = pnp_port_start(pdev, 2); + dma1[dev] = pnp_dma(pdev, 0); + dma2[dev] = pnp_dma(pdev, 1); + irq[dev] = pnp_irq(pdev, 0); + snd_printdd("PnP ES18xx: port=0x%lx, fm port=0x%lx, mpu port=0x%lx\n", port[dev], fm_port[dev], mpu_port[dev]); + snd_printdd("PnP ES18xx: dma1=%i, dma2=%i, irq=%i\n", dma1[dev], dma2[dev], irq[dev]); + return 0; +} + +static int __devinit snd_audiodrive_pnp(int dev, struct snd_audiodrive *acard, + struct pnp_dev *pdev) +{ + struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); + + if (!cfg) + return -ENOMEM; + acard->dev = pdev; + if (snd_audiodrive_pnp_init_main(dev, acard->dev, cfg) < 0) { + kfree(cfg); + return -EBUSY; + } + kfree(cfg); + return 0; +} static struct pnp_card_device_id snd_audiodrive_pnpids[] = { /* ESS 1868 (integrated on Compaq dual P-Pro motherboard and Genius 18PnP 3D) */ @@ -2061,13 +2134,11 @@ static struct pnp_card_device_id snd_aud MODULE_DEVICE_TABLE(pnp_card, snd_audiodrive_pnpids); -static int __devinit snd_audiodrive_pnp(int dev, struct snd_audiodrive *acard, +static int __devinit snd_audiodrive_pnpc(int dev, struct snd_audiodrive *acard, struct pnp_card_link *card, const struct pnp_card_device_id *id) { - struct pnp_dev *pdev; struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); - int err; if (!cfg) return -ENOMEM; @@ -2082,58 +2153,16 @@ static int __devinit snd_audiodrive_pnp( return -EBUSY; } /* Control port initialization */ - err = pnp_activate_dev(acard->devc); - if (err < 0) { + if (pnp_activate_dev(acard->devc) < 0) { snd_printk(KERN_ERR PFX "PnP control configure failure (out of resources?)\n"); - kfree(cfg); return -EAGAIN; } snd_printdd("pnp: port=0x%llx\n", (unsigned long long)pnp_port_start(acard->devc, 0)); - /* PnP initialization */ - pdev = acard->dev; - pnp_init_resource_table(cfg); - if (port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], port[dev], 16); - if (fm_port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4); - if (mpu_port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[2], mpu_port[dev], 2); - if (dma1[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1); - if (dma2[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1); - if (irq[dev] != SNDRV_AUTO_IRQ) - pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); - err = pnp_manual_config_dev(pdev, cfg, 0); - if (err < 0) - snd_printk(KERN_ERR PFX "PnP manual resources are invalid, using auto config\n"); - err = pnp_activate_dev(pdev); - if (err < 0) { - snd_printk(KERN_ERR PFX "PnP configure failure (out of resources?)\n"); + if (snd_audiodrive_pnp_init_main(dev, acard->dev, cfg) < 0) { kfree(cfg); return -EBUSY; } - /* ok. hack using Vendor-Defined Card-Level registers */ - /* skip csn and logdev initialization - already done in isapnp_configure */ - if (pnp_device_is_isapnp(pdev)) { - isapnp_cfg_begin(isapnp_card_number(pdev), isapnp_csn_number(pdev)); - isapnp_write_byte(0x27, pnp_irq(pdev, 0)); /* Hardware Volume IRQ Number */ - if (mpu_port[dev] != SNDRV_AUTO_PORT) - isapnp_write_byte(0x28, pnp_irq(pdev, 0)); /* MPU-401 IRQ Number */ - isapnp_write_byte(0x72, pnp_irq(pdev, 0)); /* second IRQ */ - isapnp_cfg_end(); - } else { - snd_printk(KERN_ERR PFX "unable to install ISA PnP hack, expect malfunction\n"); - } - port[dev] = pnp_port_start(pdev, 0); - fm_port[dev] = pnp_port_start(pdev, 1); - mpu_port[dev] = pnp_port_start(pdev, 2); - dma1[dev] = pnp_dma(pdev, 0); - dma2[dev] = pnp_dma(pdev, 1); - irq[dev] = pnp_irq(pdev, 0); - snd_printdd("PnP ES18xx: port=0x%lx, fm port=0x%lx, mpu port=0x%lx\n", port[dev], fm_port[dev], mpu_port[dev]); - snd_printdd("PnP ES18xx: dma1=%i, dma2=%i, irq=%i\n", dma1[dev], dma2[dev], irq[dev]); kfree(cfg); return 0; } @@ -2302,7 +2331,69 @@ #endif #ifdef CONFIG_PNP static unsigned int __devinitdata es18xx_pnp_devices; -static int __devinit snd_audiodrive_pnp_detect(struct pnp_card_link *pcard, +static int __devinit snd_audiodrive_pnp_detect(struct pnp_dev *pdev, + const struct pnp_device_id *id) +{ + static int dev; + int err; + struct snd_card *card; + + if (pnp_device_is_isapnp(pdev)) + return -ENOENT; /* we have another procedure - card */ + for (; dev < SNDRV_CARDS; dev++) { + if (enable[dev] && isapnp[dev]) + break; + } + if (dev >= SNDRV_CARDS) + return -ENODEV; + + card = snd_es18xx_card_new(dev); + if (! card) + return -ENOMEM; + if ((err = snd_audiodrive_pnp(dev, card->private_data, pdev)) < 0) { + snd_card_free(card); + return err; + } + snd_card_set_dev(card, &pdev->dev); + if ((err = snd_audiodrive_probe(card, dev)) < 0) { + snd_card_free(card); + return err; + } + pnp_set_drvdata(pdev, card); + dev++; + es18xx_pnp_devices++; + return 0; +} + +static void __devexit snd_audiodrive_pnp_remove(struct pnp_dev * pdev) +{ + snd_card_free(pnp_get_drvdata(pdev)); + pnp_set_drvdata(pdev, NULL); +} + +#ifdef CONFIG_PM +static int snd_audiodrive_pnp_suspend(struct pnp_dev *pdev, pm_message_t state) +{ + return snd_es18xx_suspend(pnp_get_drvdata(pdev), state); +} +static int snd_audiodrive_pnp_resume(struct pnp_dev *pdev) +{ + return snd_es18xx_resume(pnp_get_drvdata(pdev)); +} +#endif + +static struct pnp_driver es18xx_pnp_driver = { + .name = "es18xx-pnpbios", + .id_table = snd_audiodrive_pnpbiosids, + .probe = snd_audiodrive_pnp_detect, + .remove = __devexit_p(snd_audiodrive_pnp_remove), +#ifdef CONFIG_PM + .suspend = snd_audiodrive_pnp_suspend, + .resume = snd_audiodrive_pnp_resume, +#endif +}; + +static int __devinit snd_audiodrive_pnpc_detect(struct pnp_card_link *pcard, const struct pnp_card_device_id *pid) { static int dev; @@ -2320,7 +2411,7 @@ static int __devinit snd_audiodrive_pnp_ if (! card) return -ENOMEM; - if ((res = snd_audiodrive_pnp(dev, card->private_data, pcard, pid)) < 0) { + if ((res = snd_audiodrive_pnpc(dev, card->private_data, pcard, pid)) < 0) { snd_card_free(card); return res; } @@ -2336,19 +2427,19 @@ static int __devinit snd_audiodrive_pnp_ return 0; } -static void __devexit snd_audiodrive_pnp_remove(struct pnp_card_link * pcard) +static void __devexit snd_audiodrive_pnpc_remove(struct pnp_card_link * pcard) { snd_card_free(pnp_get_card_drvdata(pcard)); pnp_set_card_drvdata(pcard, NULL); } #ifdef CONFIG_PM -static int snd_audiodrive_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state) +static int snd_audiodrive_pnpc_suspend(struct pnp_card_link *pcard, pm_message_t state) { return snd_es18xx_suspend(pnp_get_card_drvdata(pcard), state); } -static int snd_audiodrive_pnp_resume(struct pnp_card_link *pcard) +static int snd_audiodrive_pnpc_resume(struct pnp_card_link *pcard) { return snd_es18xx_resume(pnp_get_card_drvdata(pcard)); } @@ -2359,11 +2450,11 @@ static struct pnp_card_driver es18xx_pnp .flags = PNP_DRIVER_RES_DISABLE, .name = "es18xx", .id_table = snd_audiodrive_pnpids, - .probe = snd_audiodrive_pnp_detect, - .remove = __devexit_p(snd_audiodrive_pnp_remove), + .probe = snd_audiodrive_pnpc_detect, + .remove = __devexit_p(snd_audiodrive_pnpc_remove), #ifdef CONFIG_PM - .suspend = snd_audiodrive_pnp_suspend, - .resume = snd_audiodrive_pnp_resume, + .suspend = snd_audiodrive_pnpc_suspend, + .resume = snd_audiodrive_pnpc_resume, #endif }; #endif /* CONFIG_PNP */ @@ -2373,8 +2464,10 @@ static void __init_or_module snd_es18xx_ int i; #ifdef CONFIG_PNP - if (pnp_registered) + if (pnpc_registered) pnp_unregister_card_driver(&es18xx_pnpc_driver); + if (pnp_registered) + pnp_unregister_driver(&es18xx_pnp_driver); #endif for (i = 0; i < ARRAY_SIZE(platform_devices); ++i) platform_device_unregister(platform_devices[i]); @@ -2405,11 +2498,13 @@ static int __init alsa_card_es18xx_init( } #ifdef CONFIG_PNP - err = pnp_register_card_driver(&es18xx_pnpc_driver); - if (!err) { + err = pnp_register_driver(&es18xx_pnp_driver); + if (!err) pnp_registered = 1; - cards += es18xx_pnp_devices; - } + err = pnp_register_card_driver(&es18xx_pnpc_driver); + if (!err) + pnpc_registered = 1; + cards += es18xx_pnp_devices; #endif if(!cards) { diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 0abf280..c414cf1 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -47,6 +47,11 @@ static int enable_loopback; module_param(enable_loopback, bool, 0444); MODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control"); +#ifdef CONFIG_SND_AC97_POWER_SAVE +static int power_save; +module_param(power_save, bool, 0644); +MODULE_PARM_DESC(power_save, "Enable AC97 power-saving control"); +#endif /* */ @@ -151,7 +156,7 @@ static const struct ac97_codec_id snd_ac { 0x4e534300, 0xffffffff, "LM4540,43,45,46,48", NULL, NULL }, // only guess --jk { 0x4e534331, 0xffffffff, "LM4549", NULL, NULL }, { 0x4e534350, 0xffffffff, "LM4550", patch_lm4550, NULL }, // volume wrap fix -{ 0x50534304, 0xffffffff, "UCB1400", NULL, NULL }, +{ 0x50534304, 0xffffffff, "UCB1400", patch_ucb1400, NULL }, { 0x53494c20, 0xffffffe0, "Si3036,8", mpatch_si3036, mpatch_si3036, AC97_MODEM_PATCH }, { 0x54524102, 0xffffffff, "TR28022", NULL, NULL }, { 0x54524106, 0xffffffff, "TR28026", NULL, NULL }, @@ -187,6 +192,8 @@ static const struct ac97_codec_id snd_ac }; +static void update_power_regs(struct snd_ac97 *ac97); + /* * I/O routines */ @@ -554,6 +561,18 @@ int snd_ac97_put_volsw(struct snd_kcontr } err = snd_ac97_update_bits(ac97, reg, val_mask, val); snd_ac97_page_restore(ac97, page_save); +#ifdef CONFIG_SND_AC97_POWER_SAVE + /* check analog mixer power-down */ + if ((val_mask & 0x8000) && + (kcontrol->private_value & (1<<30))) { + if (val & 0x8000) + ac97->power_up &= ~(1 << (reg>>1)); + else + ac97->power_up |= 1 << (reg>>1); + if (power_save) + update_power_regs(ac97); + } +#endif return err; } @@ -962,6 +981,10 @@ static int snd_ac97_bus_dev_free(struct static int snd_ac97_free(struct snd_ac97 *ac97) { if (ac97) { +#ifdef CONFIG_SND_AC97_POWER_SAVE + if (ac97->power_workq) + destroy_workqueue(ac97->power_workq); +#endif snd_ac97_proc_done(ac97); if (ac97->bus) ac97->bus->codec[ac97->num] = NULL; @@ -1117,7 +1140,9 @@ struct snd_kcontrol *snd_ac97_cnew(const /* * create mute switch(es) for normal stereo controls */ -static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg, int check_stereo, struct snd_ac97 *ac97) +static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg, + int check_stereo, int check_amix, + struct snd_ac97 *ac97) { struct snd_kcontrol *kctl; int err; @@ -1137,10 +1162,14 @@ static int snd_ac97_cmute_new_stereo(str } if (mute_mask == 0x8080) { struct snd_kcontrol_new tmp = AC97_DOUBLE(name, reg, 15, 7, 1, 1); + if (check_amix) + tmp.private_value |= (1 << 30); tmp.index = ac97->num; kctl = snd_ctl_new1(&tmp, ac97); } else { struct snd_kcontrol_new tmp = AC97_SINGLE(name, reg, 15, 1, 1); + if (check_amix) + tmp.private_value |= (1 << 30); tmp.index = ac97->num; kctl = snd_ctl_new1(&tmp, ac97); } @@ -1186,7 +1215,9 @@ static int snd_ac97_cvol_new(struct snd_ /* * create a mute-switch and a volume for normal stereo/mono controls */ -static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx, int reg, int check_stereo, struct snd_ac97 *ac97) +static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx, + int reg, int check_stereo, int check_amix, + struct snd_ac97 *ac97) { int err; char name[44]; @@ -1197,7 +1228,9 @@ static int snd_ac97_cmix_new_stereo(stru if (snd_ac97_try_bit(ac97, reg, 15)) { sprintf(name, "%s Switch", pfx); - if ((err = snd_ac97_cmute_new_stereo(card, name, reg, check_stereo, ac97)) < 0) + if ((err = snd_ac97_cmute_new_stereo(card, name, reg, + check_stereo, check_amix, + ac97)) < 0) return err; } check_volume_resolution(ac97, reg, &lo_max, &hi_max); @@ -1209,8 +1242,10 @@ static int snd_ac97_cmix_new_stereo(stru return 0; } -#define snd_ac97_cmix_new(card, pfx, reg, ac97) snd_ac97_cmix_new_stereo(card, pfx, reg, 0, ac97) -#define snd_ac97_cmute_new(card, name, reg, ac97) snd_ac97_cmute_new_stereo(card, name, reg, 0, ac97) +#define snd_ac97_cmix_new(card, pfx, reg, acheck, ac97) \ + snd_ac97_cmix_new_stereo(card, pfx, reg, 0, acheck, ac97) +#define snd_ac97_cmute_new(card, name, reg, acheck, ac97) \ + snd_ac97_cmute_new_stereo(card, name, reg, 0, acheck, ac97) static unsigned int snd_ac97_determine_spdif_rates(struct snd_ac97 *ac97); @@ -1226,9 +1261,11 @@ static int snd_ac97_mixer_build(struct s /* AD claims to remove this control from AD1887, although spec v2.2 does not allow this */ if (snd_ac97_try_volume_mix(ac97, AC97_MASTER)) { if (ac97->flags & AC97_HAS_NO_MASTER_VOL) - err = snd_ac97_cmute_new(card, "Master Playback Switch", AC97_MASTER, ac97); + err = snd_ac97_cmute_new(card, "Master Playback Switch", + AC97_MASTER, 0, ac97); else - err = snd_ac97_cmix_new(card, "Master Playback", AC97_MASTER, ac97); + err = snd_ac97_cmix_new(card, "Master Playback", + AC97_MASTER, 0, ac97); if (err < 0) return err; } @@ -1265,19 +1302,23 @@ static int snd_ac97_mixer_build(struct s if ((snd_ac97_try_volume_mix(ac97, AC97_SURROUND_MASTER)) && !(ac97->flags & AC97_AD_MULTI)) { /* Surround Master (0x38) is with stereo mutes */ - if ((err = snd_ac97_cmix_new_stereo(card, "Surround Playback", AC97_SURROUND_MASTER, 1, ac97)) < 0) + if ((err = snd_ac97_cmix_new_stereo(card, "Surround Playback", + AC97_SURROUND_MASTER, 1, 0, + ac97)) < 0) return err; } /* build headphone controls */ if (snd_ac97_try_volume_mix(ac97, AC97_HEADPHONE)) { - if ((err = snd_ac97_cmix_new(card, "Headphone Playback", AC97_HEADPHONE, ac97)) < 0) + if ((err = snd_ac97_cmix_new(card, "Headphone Playback", + AC97_HEADPHONE, 0, ac97)) < 0) return err; } /* build master mono controls */ if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_MONO)) { - if ((err = snd_ac97_cmix_new(card, "Master Mono Playback", AC97_MASTER_MONO, ac97)) < 0) + if ((err = snd_ac97_cmix_new(card, "Master Mono Playback", + AC97_MASTER_MONO, 0, ac97)) < 0) return err; } @@ -1310,7 +1351,8 @@ static int snd_ac97_mixer_build(struct s /* build Phone controls */ if (!(ac97->flags & AC97_HAS_NO_PHONE)) { if (snd_ac97_try_volume_mix(ac97, AC97_PHONE)) { - if ((err = snd_ac97_cmix_new(card, "Phone Playback", AC97_PHONE, ac97)) < 0) + if ((err = snd_ac97_cmix_new(card, "Phone Playback", + AC97_PHONE, 1, ac97)) < 0) return err; } } @@ -1318,7 +1360,8 @@ static int snd_ac97_mixer_build(struct s /* build MIC controls */ if (!(ac97->flags & AC97_HAS_NO_MIC)) { if (snd_ac97_try_volume_mix(ac97, AC97_MIC)) { - if ((err = snd_ac97_cmix_new(card, "Mic Playback", AC97_MIC, ac97)) < 0) + if ((err = snd_ac97_cmix_new(card, "Mic Playback", + AC97_MIC, 1, ac97)) < 0) return err; if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_boost, ac97))) < 0) return err; @@ -1327,14 +1370,16 @@ static int snd_ac97_mixer_build(struct s /* build Line controls */ if (snd_ac97_try_volume_mix(ac97, AC97_LINE)) { - if ((err = snd_ac97_cmix_new(card, "Line Playback", AC97_LINE, ac97)) < 0) + if ((err = snd_ac97_cmix_new(card, "Line Playback", + AC97_LINE, 1, ac97)) < 0) return err; } /* build CD controls */ if (!(ac97->flags & AC97_HAS_NO_CD)) { if (snd_ac97_try_volume_mix(ac97, AC97_CD)) { - if ((err = snd_ac97_cmix_new(card, "CD Playback", AC97_CD, ac97)) < 0) + if ((err = snd_ac97_cmix_new(card, "CD Playback", + AC97_CD, 1, ac97)) < 0) return err; } } @@ -1342,7 +1387,8 @@ static int snd_ac97_mixer_build(struct s /* build Video controls */ if (!(ac97->flags & AC97_HAS_NO_VIDEO)) { if (snd_ac97_try_volume_mix(ac97, AC97_VIDEO)) { - if ((err = snd_ac97_cmix_new(card, "Video Playback", AC97_VIDEO, ac97)) < 0) + if ((err = snd_ac97_cmix_new(card, "Video Playback", + AC97_VIDEO, 1, ac97)) < 0) return err; } } @@ -1350,7 +1396,8 @@ static int snd_ac97_mixer_build(struct s /* build Aux controls */ if (!(ac97->flags & AC97_HAS_NO_AUX)) { if (snd_ac97_try_volume_mix(ac97, AC97_AUX)) { - if ((err = snd_ac97_cmix_new(card, "Aux Playback", AC97_AUX, ac97)) < 0) + if ((err = snd_ac97_cmix_new(card, "Aux Playback", + AC97_AUX, 1, ac97)) < 0) return err; } } @@ -1385,9 +1432,12 @@ static int snd_ac97_mixer_build(struct s } else { if (!(ac97->flags & AC97_HAS_NO_STD_PCM)) { if (ac97->flags & AC97_HAS_NO_PCM_VOL) - err = snd_ac97_cmute_new(card, "PCM Playback Switch", AC97_PCM, ac97); + err = snd_ac97_cmute_new(card, + "PCM Playback Switch", + AC97_PCM, 0, ac97); else - err = snd_ac97_cmix_new(card, "PCM Playback", AC97_PCM, ac97); + err = snd_ac97_cmix_new(card, "PCM Playback", + AC97_PCM, 0, ac97); if (err < 0) return err; } @@ -1398,7 +1448,9 @@ static int snd_ac97_mixer_build(struct s if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_src, ac97))) < 0) return err; if (snd_ac97_try_bit(ac97, AC97_REC_GAIN, 15)) { - if ((err = snd_ac97_cmute_new(card, "Capture Switch", AC97_REC_GAIN, ac97)) < 0) + err = snd_ac97_cmute_new(card, "Capture Switch", + AC97_REC_GAIN, 0, ac97); + if (err < 0) return err; } if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_vol, ac97))) < 0) @@ -1817,18 +1869,25 @@ static int snd_ac97_dev_register(struct return 0; } -/* unregister ac97 codec */ -static int snd_ac97_dev_unregister(struct snd_device *device) +/* disconnect ac97 codec */ +static int snd_ac97_dev_disconnect(struct snd_device *device) { struct snd_ac97 *ac97 = device->device_data; if (ac97->dev.bus) device_unregister(&ac97->dev); - return snd_ac97_free(ac97); + return 0; } /* build_ops to do nothing */ static struct snd_ac97_build_ops null_build_ops; +#ifdef CONFIG_SND_AC97_POWER_SAVE +static void do_update_power(void *data) +{ + update_power_regs(data); +} +#endif + /** * snd_ac97_mixer - create an Codec97 component * @bus: the AC97 bus which codec is attached to @@ -1860,7 +1919,7 @@ int snd_ac97_mixer(struct snd_ac97_bus * static struct snd_device_ops ops = { .dev_free = snd_ac97_dev_free, .dev_register = snd_ac97_dev_register, - .dev_unregister = snd_ac97_dev_unregister, + .dev_disconnect = snd_ac97_dev_disconnect, }; snd_assert(rac97 != NULL, return -EINVAL); @@ -1883,6 +1942,10 @@ int snd_ac97_mixer(struct snd_ac97_bus * bus->codec[ac97->num] = ac97; mutex_init(&ac97->reg_mutex); mutex_init(&ac97->page_mutex); +#ifdef CONFIG_SND_AC97_POWER_SAVE + ac97->power_workq = create_workqueue("ac97"); + INIT_WORK(&ac97->power_work, do_update_power, ac97); +#endif #ifdef CONFIG_PCI if (ac97->pci) { @@ -2117,15 +2180,8 @@ #endif return -ENOMEM; } } - /* make sure the proper powerdown bits are cleared */ - if (ac97->scaps && ac97_is_audio(ac97)) { - reg = snd_ac97_read(ac97, AC97_EXTENDED_STATUS); - if (ac97->scaps & AC97_SCAP_SURROUND_DAC) - reg &= ~AC97_EA_PRJ; - if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) - reg &= ~(AC97_EA_PRI | AC97_EA_PRK); - snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, reg); - } + if (ac97_is_audio(ac97)) + update_power_regs(ac97); snd_ac97_proc_init(ac97); if ((err = snd_device_new(card, SNDRV_DEV_CODEC, ac97, &ops)) < 0) { snd_ac97_free(ac97); @@ -2153,22 +2209,155 @@ static void snd_ac97_powerdown(struct sn snd_ac97_write(ac97, AC97_HEADPHONE, 0x9f9f); } - power = ac97->regs[AC97_POWERDOWN] | 0x8000; /* EAPD */ - power |= 0x4000; /* Headphone amplifier powerdown */ - power |= 0x0300; /* ADC & DAC powerdown */ - snd_ac97_write(ac97, AC97_POWERDOWN, power); - udelay(100); - power |= 0x0400; /* Analog Mixer powerdown (Vref on) */ + /* surround, CLFE, mic powerdown */ + power = ac97->regs[AC97_EXTENDED_STATUS]; + if (ac97->scaps & AC97_SCAP_SURROUND_DAC) + power |= AC97_EA_PRJ; + if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) + power |= AC97_EA_PRI | AC97_EA_PRK; + power |= AC97_EA_PRL; + snd_ac97_write(ac97, AC97_EXTENDED_STATUS, power); + + /* powerdown external amplifier */ + if (ac97->scaps & AC97_SCAP_INV_EAPD) + power = ac97->regs[AC97_POWERDOWN] & ~AC97_PD_EAPD; + else if (! (ac97->scaps & AC97_SCAP_EAPD_LED)) + power = ac97->regs[AC97_POWERDOWN] | AC97_PD_EAPD; + power |= AC97_PD_PR6; /* Headphone amplifier powerdown */ + power |= AC97_PD_PR0 | AC97_PD_PR1; /* ADC & DAC powerdown */ snd_ac97_write(ac97, AC97_POWERDOWN, power); udelay(100); -#if 0 - /* FIXME: this causes click noises on some boards at resume */ - power |= 0x3800; /* AC-link powerdown, internal Clk disable */ + power |= AC97_PD_PR2 | AC97_PD_PR3; /* Analog Mixer powerdown */ snd_ac97_write(ac97, AC97_POWERDOWN, power); +#ifdef CONFIG_SND_AC97_POWER_SAVE + if (power_save) { + udelay(100); + /* AC-link powerdown, internal Clk disable */ + /* FIXME: this may cause click noises on some boards */ + power |= AC97_PD_PR4 | AC97_PD_PR5; + snd_ac97_write(ac97, AC97_POWERDOWN, power); + } #endif } +struct ac97_power_reg { + unsigned short reg; + unsigned short power_reg; + unsigned short mask; +}; + +enum { PWIDX_ADC, PWIDX_FRONT, PWIDX_CLFE, PWIDX_SURR, PWIDX_MIC, PWIDX_SIZE }; + +static struct ac97_power_reg power_regs[PWIDX_SIZE] = { + [PWIDX_ADC] = { AC97_PCM_LR_ADC_RATE, AC97_POWERDOWN, AC97_PD_PR0}, + [PWIDX_FRONT] = { AC97_PCM_FRONT_DAC_RATE, AC97_POWERDOWN, AC97_PD_PR1}, + [PWIDX_CLFE] = { AC97_PCM_LFE_DAC_RATE, AC97_EXTENDED_STATUS, + AC97_EA_PRI | AC97_EA_PRK}, + [PWIDX_SURR] = { AC97_PCM_SURR_DAC_RATE, AC97_EXTENDED_STATUS, + AC97_EA_PRJ}, + [PWIDX_MIC] = { AC97_PCM_MIC_ADC_RATE, AC97_EXTENDED_STATUS, + AC97_EA_PRL}, +}; + +#ifdef CONFIG_SND_AC97_POWER_SAVE +/** + * snd_ac97_update_power - update the powerdown register + * @ac97: the codec instance + * @reg: the rate register, e.g. AC97_PCM_FRONT_DAC_RATE + * @powerup: non-zero when power up the part + * + * Update the AC97 powerdown register bits of the given part. + */ +int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup) +{ + int i; + + if (! ac97) + return 0; + + if (reg) { + /* SPDIF requires DAC power, too */ + if (reg == AC97_SPDIF) + reg = AC97_PCM_FRONT_DAC_RATE; + for (i = 0; i < PWIDX_SIZE; i++) { + if (power_regs[i].reg == reg) { + if (powerup) + ac97->power_up |= (1 << i); + else + ac97->power_up &= ~(1 << i); + break; + } + } + } + + if (! power_save) + return 0; + + if (! powerup && ac97->power_workq) + /* adjust power-down bits after two seconds delay + * (for avoiding loud click noises for many (OSS) apps + * that open/close frequently) + */ + queue_delayed_work(ac97->power_workq, &ac97->power_work, HZ*2); + else + update_power_regs(ac97); + + return 0; +} + +EXPORT_SYMBOL(snd_ac97_update_power); +#endif /* CONFIG_SND_AC97_POWER_SAVE */ + +static void update_power_regs(struct snd_ac97 *ac97) +{ + unsigned int power_up, bits; + int i; + +#ifdef CONFIG_SND_AC97_POWER_SAVE + if (power_save) + power_up = ac97->power_up; + else { +#endif + power_up = (1 << PWIDX_FRONT) | (1 << PWIDX_ADC); + power_up |= (1 << PWIDX_MIC); + if (ac97->scaps & AC97_SCAP_SURROUND_DAC) + power_up |= (1 << PWIDX_SURR); + if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) + power_up |= (1 << PWIDX_CLFE); +#ifdef CONFIG_SND_AC97_POWER_SAVE + } +#endif + if (power_up) { + if (ac97->regs[AC97_POWERDOWN] & AC97_PD_PR2) { + /* needs power-up analog mix and vref */ + snd_ac97_update_bits(ac97, AC97_POWERDOWN, + AC97_PD_PR3, 0); + msleep(1); + snd_ac97_update_bits(ac97, AC97_POWERDOWN, + AC97_PD_PR2, 0); + } + } + for (i = 0; i < PWIDX_SIZE; i++) { + if (power_up & (1 << i)) + bits = 0; + else + bits = power_regs[i].mask; + snd_ac97_update_bits(ac97, power_regs[i].power_reg, + power_regs[i].mask, bits); + } + if (! power_up) { + if (! (ac97->regs[AC97_POWERDOWN] & AC97_PD_PR2)) { + /* power down analog mix and vref */ + snd_ac97_update_bits(ac97, AC97_POWERDOWN, + AC97_PD_PR2, AC97_PD_PR2); + snd_ac97_update_bits(ac97, AC97_POWERDOWN, + AC97_PD_PR3, AC97_PD_PR3); + } + } +} + + #ifdef CONFIG_PM /** * snd_ac97_suspend - General suspend function for AC97 codec @@ -2484,6 +2673,7 @@ static int tune_mute_led(struct snd_ac97 msw->put = master_mute_sw_put; snd_ac97_remove_ctl(ac97, "External Amplifier", NULL); snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, 0x8000); /* mute LED on */ + ac97->scaps |= AC97_SCAP_EAPD_LED; return 0; } diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 094cfc1..37c6be4 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -2208,7 +2208,8 @@ int patch_alc655(struct snd_ac97 * ac97) val &= ~(1 << 1); /* Pin 47 is spdif input pin */ else { /* ALC655 */ if (ac97->subsystem_vendor == 0x1462 && - ac97->subsystem_device == 0x0131) /* MSI S270 laptop */ + (ac97->subsystem_device == 0x0131 || /* MSI S270 laptop */ + ac97->subsystem_device == 0x0161)) /* LG K1 Express */ val &= ~(1 << 1); /* Pin 47 is EAPD (for internal speaker) */ else val |= (1 << 1); /* Pin 47 is spdif input pin */ @@ -2872,3 +2873,41 @@ int patch_lm4550(struct snd_ac97 *ac97) ac97->res_table = lm4550_restbl; return 0; } + +/* + * UCB1400 codec (http://www.semiconductors.philips.com/acrobat_download/datasheets/UCB1400-02.pdf) + */ +static const struct snd_kcontrol_new snd_ac97_controls_ucb1400[] = { +/* enable/disable headphone driver which allows direct connection to + stereo headphone without the use of external DC blocking + capacitors */ +AC97_SINGLE("Headphone Driver", 0x6a, 6, 1, 0), +/* Filter used to compensate the DC offset is added in the ADC to remove idle + tones from the audio band. */ +AC97_SINGLE("DC Filter", 0x6a, 4, 1, 0), +/* Control smart-low-power mode feature. Allows automatic power down + of unused blocks in the ADC analog front end and the PLL. */ +AC97_SINGLE("Smart Low Power Mode", 0x6c, 4, 3, 0), +}; + +static int patch_ucb1400_specific(struct snd_ac97 * ac97) +{ + int idx, err; + for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_ucb1400); idx++) + if ((err = snd_ctl_add(ac97->bus->card, snd_ctl_new1(&snd_ac97_controls_ucb1400[idx], ac97))) < 0) + return err; + return 0; +} + +static struct snd_ac97_build_ops patch_ucb1400_ops = { + .build_specific = patch_ucb1400_specific, +}; + +int patch_ucb1400(struct snd_ac97 * ac97) +{ + ac97->build_ops = &patch_ucb1400_ops; + /* enable headphone driver and smart low power mode by default */ + snd_ac97_write(ac97, 0x6a, 0x0050); + snd_ac97_write(ac97, 0x6c, 0x0030); + return 0; +} diff --git a/sound/pci/ac97/ac97_patch.h b/sound/pci/ac97/ac97_patch.h index adcaa04..7419792 100644 --- a/sound/pci/ac97/ac97_patch.h +++ b/sound/pci/ac97/ac97_patch.h @@ -58,5 +58,6 @@ int patch_cm9780(struct snd_ac97 * ac97) int patch_vt1616(struct snd_ac97 * ac97); int patch_vt1617a(struct snd_ac97 * ac97); int patch_it2646(struct snd_ac97 * ac97); +int patch_ucb1400(struct snd_ac97 * ac97); int mpatch_si3036(struct snd_ac97 * ac97); int patch_lm4550(struct snd_ac97 * ac97); diff --git a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c index f684aa2..3758d07 100644 --- a/sound/pci/ac97/ac97_pcm.c +++ b/sound/pci/ac97/ac97_pcm.c @@ -269,6 +269,7 @@ int snd_ac97_set_rate(struct snd_ac97 *a return -EINVAL; } + snd_ac97_update_power(ac97, reg, 1); switch (reg) { case AC97_PCM_MIC_ADC_RATE: if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRM) == 0) /* MIC VRA */ @@ -606,6 +607,7 @@ int snd_ac97_pcm_open(struct ac97_pcm *p goto error; } } + pcm->cur_dbl = r; spin_unlock_irq(&pcm->bus->bus_lock); for (i = 3; i < 12; i++) { if (!(slots & (1 << i))) @@ -651,6 +653,21 @@ int snd_ac97_pcm_close(struct ac97_pcm * unsigned short slots = pcm->aslots; int i, cidx; +#ifdef CONFIG_SND_AC97_POWER_SAVE + int r = pcm->cur_dbl; + for (i = 3; i < 12; i++) { + if (!(slots & (1 << i))) + continue; + for (cidx = 0; cidx < 4; cidx++) { + if (pcm->r[r].rslots[cidx] & (1 << i)) { + int reg = get_slot_reg(pcm, cidx, i, r); + snd_ac97_update_power(pcm->r[r].codec[cidx], + reg, 0); + } + } + } +#endif + bus = pcm->bus; spin_lock_irq(&pcm->bus->bus_lock); for (i = 3; i < 12; i++) { @@ -660,6 +677,7 @@ int snd_ac97_pcm_close(struct ac97_pcm * bus->used_slots[pcm->stream][cidx] &= ~(1 << i); } pcm->aslots = 0; + pcm->cur_dbl = 0; spin_unlock_irq(&pcm->bus->bus_lock); return 0; } diff --git a/sound/pci/ac97/ac97_proc.c b/sound/pci/ac97/ac97_proc.c index 2118df5..a3fdd7d 100644 --- a/sound/pci/ac97/ac97_proc.c +++ b/sound/pci/ac97/ac97_proc.c @@ -457,14 +457,10 @@ #endif void snd_ac97_proc_done(struct snd_ac97 * ac97) { - if (ac97->proc_regs) { - snd_info_unregister(ac97->proc_regs); - ac97->proc_regs = NULL; - } - if (ac97->proc) { - snd_info_unregister(ac97->proc); - ac97->proc = NULL; - } + snd_info_free_entry(ac97->proc_regs); + ac97->proc_regs = NULL; + snd_info_free_entry(ac97->proc); + ac97->proc = NULL; } void snd_ac97_bus_proc_init(struct snd_ac97_bus * bus) @@ -485,8 +481,6 @@ void snd_ac97_bus_proc_init(struct snd_a void snd_ac97_bus_proc_done(struct snd_ac97_bus * bus) { - if (bus->proc) { - snd_info_unregister(bus->proc); - bus->proc = NULL; - } + snd_info_free_entry(bus->proc); + bus->proc = NULL; } diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index 146eed7..9855f52 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -70,9 +70,13 @@ #include #include #include #include +#include #include "ca0106.h" +static DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1); +static DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1); + static int snd_ca0106_shared_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -469,18 +473,24 @@ static int snd_ca0106_i2c_volume_put(str #define CA_VOLUME(xname,chid,reg) \ { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ .info = snd_ca0106_volume_info, \ .get = snd_ca0106_volume_get, \ .put = snd_ca0106_volume_put, \ + .tlv = { .p = snd_ca0106_db_scale1 }, \ .private_value = ((chid) << 8) | (reg) \ } #define I2C_VOLUME(xname,chid) \ { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ .info = snd_ca0106_i2c_volume_info, \ .get = snd_ca0106_i2c_volume_get, \ .put = snd_ca0106_i2c_volume_put, \ + .tlv = { .p = snd_ca0106_db_scale2 }, \ .private_value = chid \ } diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c index 5c9711c..89c4027 100644 --- a/sound/pci/cs46xx/dsp_spos.c +++ b/sound/pci/cs46xx/dsp_spos.c @@ -868,35 +868,23 @@ int cs46xx_dsp_proc_done (struct snd_cs4 struct dsp_spos_instance * ins = chip->dsp_spos_instance; int i; - if (ins->proc_sym_info_entry) { - snd_info_unregister(ins->proc_sym_info_entry); - ins->proc_sym_info_entry = NULL; - } - - if (ins->proc_modules_info_entry) { - snd_info_unregister(ins->proc_modules_info_entry); - ins->proc_modules_info_entry = NULL; - } - - if (ins->proc_parameter_dump_info_entry) { - snd_info_unregister(ins->proc_parameter_dump_info_entry); - ins->proc_parameter_dump_info_entry = NULL; - } - - if (ins->proc_sample_dump_info_entry) { - snd_info_unregister(ins->proc_sample_dump_info_entry); - ins->proc_sample_dump_info_entry = NULL; - } - - if (ins->proc_scb_info_entry) { - snd_info_unregister(ins->proc_scb_info_entry); - ins->proc_scb_info_entry = NULL; - } - - if (ins->proc_task_info_entry) { - snd_info_unregister(ins->proc_task_info_entry); - ins->proc_task_info_entry = NULL; - } + snd_info_free_entry(ins->proc_sym_info_entry); + ins->proc_sym_info_entry = NULL; + + snd_info_free_entry(ins->proc_modules_info_entry); + ins->proc_modules_info_entry = NULL; + + snd_info_free_entry(ins->proc_parameter_dump_info_entry); + ins->proc_parameter_dump_info_entry = NULL; + + snd_info_free_entry(ins->proc_sample_dump_info_entry); + ins->proc_sample_dump_info_entry = NULL; + + snd_info_free_entry(ins->proc_scb_info_entry); + ins->proc_scb_info_entry = NULL; + + snd_info_free_entry(ins->proc_task_info_entry); + ins->proc_task_info_entry = NULL; mutex_lock(&chip->spos_mutex); for (i = 0; i < ins->nscb; ++i) { @@ -905,10 +893,8 @@ int cs46xx_dsp_proc_done (struct snd_cs4 } mutex_unlock(&chip->spos_mutex); - if (ins->proc_dsp_dir) { - snd_info_unregister (ins->proc_dsp_dir); - ins->proc_dsp_dir = NULL; - } + snd_info_free_entry(ins->proc_dsp_dir); + ins->proc_dsp_dir = NULL; return 0; } diff --git a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c index 232b337..343f51d 100644 --- a/sound/pci/cs46xx/dsp_spos_scb_lib.c +++ b/sound/pci/cs46xx/dsp_spos_scb_lib.c @@ -233,7 +233,7 @@ void cs46xx_dsp_proc_free_scb_desc (stru snd_printdd("cs46xx_dsp_proc_free_scb_desc: freeing %s\n",scb->scb_name); - snd_info_unregister(scb->proc_info); + snd_info_free_entry(scb->proc_info); scb->proc_info = NULL; snd_assert (scb_info != NULL, return); diff --git a/sound/pci/cs5535audio/Makefile b/sound/pci/cs5535audio/Makefile index 2911a8a..ad947b4 100644 --- a/sound/pci/cs5535audio/Makefile +++ b/sound/pci/cs5535audio/Makefile @@ -4,7 +4,7 @@ # snd-cs5535audio-objs := cs5535audio.o cs5535audio_pcm.o -ifdef CONFIG_PM +ifeq ($(CONFIG_PM),y) snd-cs5535audio-objs += cs5535audio_pm.o endif diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 79f24cd..be65d4d 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -927,6 +927,7 @@ static struct snd_emu_chip_details emu_c .ca0151_chip = 1, .spk71 = 1, .spdif_bug = 1, + .adc_1361t = 1, /* 24 bit capture instead of 16bit */ .ac97_chip = 1} , {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10051102, .driver = "Audigy2", .name = "Audigy 2 EX [1005]", diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index bda8bdf..da1610a 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -1626,12 +1626,7 @@ static struct pci_driver driver = { // initialization of the module static int __init alsa_card_emu10k1x_init(void) { - int err; - - if ((err = pci_register_driver(&driver)) > 0) - return err; - - return 0; + return pci_register_driver(&driver); } // clean up the module diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index dfba002..13cd6ce 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #if 0 /* for testing purposes - digital out -> capture */ @@ -266,6 +267,7 @@ static const u32 treble_table[41][5] = { { 0x37c4448b, 0xa45ef51d, 0x262f3267, 0x081e36dc, 0xfd8f5d14 } }; +/* dB gain = (float) 20 * log10( float(db_table_value) / 0x8000000 ) */ static const u32 db_table[101] = { 0x00000000, 0x01571f82, 0x01674b41, 0x01783a1b, 0x0189f540, 0x019c8651, 0x01aff763, 0x01c45306, 0x01d9a446, 0x01eff6b8, @@ -290,6 +292,9 @@ static const u32 db_table[101] = { 0x7fffffff, }; +/* EMU10k1/EMU10k2 DSP control db gain */ +static DECLARE_TLV_DB_SCALE(snd_emu10k1_db_scale1, -4000, 40, 1); + static const u32 onoff_table[2] = { 0x00000000, 0x00000001 }; @@ -755,6 +760,11 @@ static int snd_emu10k1_add_controls(stru knew.device = gctl->id.device; knew.subdevice = gctl->id.subdevice; knew.info = snd_emu10k1_gpr_ctl_info; + if (gctl->tlv.p) { + knew.tlv.p = gctl->tlv.p; + knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ; + } knew.get = snd_emu10k1_gpr_ctl_get; knew.put = snd_emu10k1_gpr_ctl_put; memset(nctl, 0, sizeof(*nctl)); @@ -1013,6 +1023,7 @@ snd_emu10k1_init_mono_control(struct snd ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; ctl->min = 0; ctl->max = 100; + ctl->tlv.p = snd_emu10k1_db_scale1; ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100; } @@ -1027,6 +1038,7 @@ snd_emu10k1_init_stereo_control(struct s ctl->gpr[1] = gpr + 1; ctl->value[1] = defval; ctl->min = 0; ctl->max = 100; + ctl->tlv.p = snd_emu10k1_db_scale1; ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100; } diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c index 9905651..4e0f954 100644 --- a/sound/pci/emu10k1/p16v.c +++ b/sound/pci/emu10k1/p16v.c @@ -100,6 +100,7 @@ #include #include #include #include +#include #include #include "p16v.h" @@ -784,12 +785,16 @@ static int snd_p16v_capture_channel_put( } return change; } +static DECLARE_TLV_DB_SCALE(snd_p16v_db_scale1, -5175, 25, 1); #define P16V_VOL(xname,xreg,xhl) { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ .info = snd_p16v_volume_info, \ .get = snd_p16v_volume_get, \ .put = snd_p16v_volume_put, \ + .tlv = { .p = snd_p16v_db_scale1 }, \ .private_value = ((xreg) | ((xhl) << 8)) \ } diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 13868c9..f3f2b2c 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -2,6 +2,7 @@ * The driver for the ForteMedia FM801 based soundcards * Copyright (c) by Jaroslav Kysela * + * Support FM only card by Andy Shevchenko * * 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 @@ -54,6 +55,7 @@ static int enable[SNDRV_CARDS] = SNDRV_D * 1 = MediaForte 256-PCS * 2 = MediaForte 256-PCPR * 3 = MediaForte 64-PCR + * 16 = setup tuner only (this is additional bit), i.e. SF-64-PCR FM card * High 16-bits are video (radio) device number + 1 */ static int tea575x_tuner[SNDRV_CARDS]; @@ -158,6 +160,7 @@ struct fm801 { unsigned int multichannel: 1, /* multichannel support */ secondary: 1; /* secondary codec */ unsigned char secondary_addr; /* address of the secondary codec */ + unsigned int tea575x_tuner; /* tuner flags */ unsigned short ply_ctrl; /* playback control */ unsigned short cap_ctrl; /* capture control */ @@ -318,10 +321,8 @@ static unsigned int channels[] = { 2, 4, 6 }; -#define CHANNELS sizeof(channels) / sizeof(channels[0]) - static struct snd_pcm_hw_constraint_list hw_constraints_channels = { - .count = CHANNELS, + .count = ARRAY_SIZE(channels), .list = channels, .mask = 0, }; @@ -1253,6 +1254,9 @@ static int snd_fm801_chip_init(struct fm int id; unsigned short cmdw; + if (chip->tea575x_tuner & 0x0010) + goto __ac97_ok; + /* codec cold reset + AC'97 warm reset */ outw((1<<5) | (1<<6), FM801_REG(chip, CODEC_CTRL)); inw(FM801_REG(chip, CODEC_CTRL)); /* flush posting data */ @@ -1290,6 +1294,8 @@ static int snd_fm801_chip_init(struct fm wait_for_codec(chip, 0, AC97_VENDOR_ID1, msecs_to_jiffies(750)); } + __ac97_ok: + /* init volume */ outw(0x0808, FM801_REG(chip, PCM_VOL)); outw(0x9f1f, FM801_REG(chip, FM_VOL)); @@ -1298,9 +1304,12 @@ static int snd_fm801_chip_init(struct fm /* I2S control - I2S mode */ outw(0x0003, FM801_REG(chip, I2S_MODE)); - /* interrupt setup - unmask MPU, PLAYBACK & CAPTURE */ + /* interrupt setup */ cmdw = inw(FM801_REG(chip, IRQ_MASK)); - cmdw &= ~0x0083; + if (chip->irq < 0) + cmdw |= 0x00c3; /* mask everything, no PCM nor MPU */ + else + cmdw &= ~0x0083; /* unmask MPU, PLAYBACK & CAPTURE */ outw(cmdw, FM801_REG(chip, IRQ_MASK)); /* interrupt clear */ @@ -1365,20 +1374,23 @@ static int __devinit snd_fm801_create(st chip->card = card; chip->pci = pci; chip->irq = -1; + chip->tea575x_tuner = tea575x_tuner; if ((err = pci_request_regions(pci, "FM801")) < 0) { kfree(chip); pci_disable_device(pci); return err; } chip->port = pci_resource_start(pci, 0); - if (request_irq(pci->irq, snd_fm801_interrupt, IRQF_DISABLED|IRQF_SHARED, - "FM801", chip)) { - snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->irq); - snd_fm801_free(chip); - return -EBUSY; + if ((tea575x_tuner & 0x0010) == 0) { + if (request_irq(pci->irq, snd_fm801_interrupt, IRQF_DISABLED|IRQF_SHARED, + "FM801", chip)) { + snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->irq); + snd_fm801_free(chip); + return -EBUSY; + } + chip->irq = pci->irq; + pci_set_master(pci); } - chip->irq = pci->irq; - pci_set_master(pci); pci_read_config_byte(pci, PCI_REVISION_ID, &rev); if (rev >= 0xb1) /* FM801-AU */ @@ -1394,12 +1406,12 @@ static int __devinit snd_fm801_create(st snd_card_set_dev(card, &pci->dev); #ifdef TEA575X_RADIO - if (tea575x_tuner > 0 && (tea575x_tuner & 0xffff) < 4) { + if (tea575x_tuner > 0 && (tea575x_tuner & 0x000f) < 4) { chip->tea.dev_nr = tea575x_tuner >> 16; chip->tea.card = card; chip->tea.freq_fixup = 10700; chip->tea.private_data = chip; - chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & 0xffff) - 1]; + chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & 0x000f) - 1]; snd_tea575x_init(&chip->tea); } #endif @@ -1439,6 +1451,9 @@ static int __devinit snd_card_fm801_prob sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, chip->port, chip->irq); + if (tea575x_tuner[dev] & 0x0010) + goto __fm801_tuner_only; + if ((err = snd_fm801_pcm(chip, 0, NULL)) < 0) { snd_card_free(card); return err; @@ -1465,6 +1480,7 @@ static int __devinit snd_card_fm801_prob return err; } + __fm801_tuner_only: if ((err = snd_card_register(card)) < 0) { snd_card_free(card); return err; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 23201f3..ff29d0f 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -29,6 +29,7 @@ #include #include #include "hda_codec.h" #include +#include #include #include "hda_local.h" @@ -841,6 +842,31 @@ int snd_hda_mixer_amp_volume_put(struct return change; } +int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *_tlv) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = get_amp_nid(kcontrol); + int dir = get_amp_direction(kcontrol); + u32 caps, val1, val2; + + if (size < 4 * sizeof(unsigned int)) + return -ENOMEM; + caps = query_amp_caps(codec, nid, dir); + val2 = (((caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT) + 1) * 25; + val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT); + val1 = ((int)val1) * ((int)val2); + if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv)) + return -EFAULT; + if (put_user(2 * sizeof(unsigned int), _tlv + 1)) + return -EFAULT; + if (put_user(val1, _tlv + 2)) + return -EFAULT; + if (put_user(val2, _tlv + 3)) + return -EFAULT; + return 0; +} + /* switch */ int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1916,7 +1942,7 @@ int snd_hda_multi_out_analog_prepare(str /* front */ snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 0, format); - if (mout->hp_nid) + if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT]) /* headphone out will just decode front left/right (stereo) */ snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format); /* extra outputs copied from front */ diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 40520e9..c12bc4e 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -479,7 +479,7 @@ #endif struct hda_amp_info { u32 key; /* hash key */ u32 amp_caps; /* amp capabilities */ - u16 vol[2]; /* current volume & mute*/ + u16 vol[2]; /* current volume & mute */ u16 status; /* update flag */ u16 next; /* next link */ }; diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 14e8aa2..ff24266 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -30,9 +30,13 @@ #define HDA_COMPOSE_AMP_VAL(nid,chs,idx, /* mono volume with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ .info = snd_hda_mixer_amp_volume_info, \ .get = snd_hda_mixer_amp_volume_get, \ .put = snd_hda_mixer_amp_volume_put, \ + .tlv = { .c = snd_hda_mixer_amp_tlv }, \ .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) } /* stereo volume with index */ #define HDA_CODEC_VOLUME_IDX(xname, xcidx, nid, xindex, direction) \ @@ -63,6 +67,7 @@ #define HDA_CODEC_MUTE(xname, nid, xinde int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *tlv); int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index c2f0fe8..d737f17 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -52,10 +52,9 @@ static void print_amp_caps(struct snd_in 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); + caps = snd_hda_param_read(codec, nid, + dir == HDA_OUTPUT ? + AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); if (caps == -1 || caps == 0) { snd_iprintf(buffer, "N/A\n"); return; @@ -74,10 +73,7 @@ static void print_amp_vals(struct snd_in unsigned int val; int i; - if (dir == HDA_OUTPUT) - dir = AC_AMP_GET_OUTPUT; - else - dir = AC_AMP_GET_INPUT; + dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT; for (i = 0; i < indices; i++) { snd_iprintf(buffer, " ["); if (stereo) { diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 6823f2b..71abc2a 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -488,9 +488,13 @@ static struct snd_kcontrol_new ad1986a_m { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PCM Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, .info = ad1986a_pcm_amp_vol_info, .get = ad1986a_pcm_amp_vol_get, .put = ad1986a_pcm_amp_vol_put, + .tlv = { .c = snd_hda_mixer_amp_tlv }, .private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT) }, { @@ -637,6 +641,7 @@ static struct snd_kcontrol_new ad1986a_l .info = snd_hda_mixer_amp_volume_info, .get = snd_hda_mixer_amp_volume_get, .put = ad1986a_laptop_master_vol_put, + .tlv = { .c = snd_hda_mixer_amp_tlv }, .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT), }, { @@ -791,6 +796,8 @@ static struct hda_board_config ad1986a_c .config = AD1986A_3STACK }, /* ASUS A8N-VM CSM */ { .pci_subvendor = 0x1043, .pci_subdevice = 0x81b3, .config = AD1986A_3STACK }, /* ASUS P5RD2-VM / P5GPL-X SE */ + { .pci_subvendor = 0x1043, .pci_subdevice = 0x81cb, + .config = AD1986A_3STACK }, /* ASUS M2NPV-VM */ { .modelname = "laptop", .config = AD1986A_LAPTOP }, { .pci_subvendor = 0x144d, .pci_subdevice = 0xc01e, .config = AD1986A_LAPTOP }, /* FSC V2060 */ @@ -803,6 +810,8 @@ static struct hda_board_config ad1986a_c .config = AD1986A_LAPTOP_EAPD }, /* Samsung X60 Chane */ { .pci_subvendor = 0x144d, .pci_subdevice = 0xc024, .config = AD1986A_LAPTOP_EAPD }, /* Samsung R65-T2300 Charis */ + { .pci_subvendor = 0x144d, .pci_subdevice = 0xc026, + .config = AD1986A_LAPTOP_EAPD }, /* Samsung X10-T2300 Culesa */ { .pci_subvendor = 0x1043, .pci_subdevice = 0x1153, .config = AD1986A_LAPTOP_EAPD }, /* ASUS M9 */ { .pci_subvendor = 0x1043, .pci_subdevice = 0x1213, @@ -1626,10 +1635,12 @@ static int ad198x_ch_mode_put(struct snd { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ad198x_spec *spec = codec->spec; - if (spec->need_dac_fix) + int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode, + spec->num_channel_mode, + &spec->multiout.max_channels); + if (! err && spec->need_dac_fix) spec->multiout.num_dacs = spec->multiout.max_channels / 2; - return snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode, - spec->num_channel_mode, &spec->multiout.max_channels); + return err; } /* 6-stack mode */ diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 18d1052..53aa57f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -79,6 +79,7 @@ enum { ALC262_BASIC, ALC262_FUJITSU, ALC262_HP_BPC, + ALC262_BENQ_ED8, ALC262_AUTO, ALC262_MODEL_LAST /* last tag */ }; @@ -89,6 +90,7 @@ enum { ALC660_3ST, ALC861_3ST_DIG, ALC861_6ST_DIG, + ALC861_UNIWILL_M31, ALC861_AUTO, ALC861_MODEL_LAST, }; @@ -97,6 +99,7 @@ enum { enum { ALC882_3ST_DIG, ALC882_6ST_DIG, + ALC882_ARIMA, ALC882_AUTO, ALC882_MODEL_LAST, }; @@ -153,6 +156,7 @@ struct alc_spec { /* channel model */ const struct hda_channel_mode *channel_mode; int num_channel_mode; + int need_dac_fix; /* PCM information */ struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */ @@ -190,6 +194,7 @@ struct alc_config_preset { hda_nid_t dig_in_nid; unsigned int num_channel_mode; const struct hda_channel_mode *channel_mode; + int need_dac_fix; unsigned int num_mux_defs; const struct hda_input_mux *input_mux; void (*unsol_event)(struct hda_codec *, unsigned int); @@ -262,9 +267,12 @@ static int alc_ch_mode_put(struct snd_kc { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; - return snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode, - spec->num_channel_mode, - &spec->multiout.max_channels); + int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode, + spec->num_channel_mode, + &spec->multiout.max_channels); + if (! err && spec->need_dac_fix) + spec->multiout.num_dacs = spec->multiout.max_channels / 2; + return err; } /* @@ -544,6 +552,7 @@ static void setup_preset(struct alc_spec spec->channel_mode = preset->channel_mode; spec->num_channel_mode = preset->num_channel_mode; + spec->need_dac_fix = preset->need_dac_fix; spec->multiout.max_channels = spec->channel_mode[0].channels; @@ -1348,6 +1357,10 @@ static struct hda_verb alc880_pin_clevo_ }; static struct hda_verb alc880_pin_tcl_S700_init_verbs[] = { + /* change to EAPD mode */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, + {0x20, AC_VERB_SET_PROC_COEF, 0x3060}, + /* Headphone output */ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, /* Front output*/ @@ -2130,7 +2143,10 @@ static struct hda_board_config alc880_cf { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20f, .config = ALC880_3ST }, { .pci_subvendor = 0x8086, .pci_subdevice = 0xe210, .config = ALC880_3ST }, { .pci_subvendor = 0x8086, .pci_subdevice = 0xe211, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe212, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe213, .config = ALC880_3ST }, { .pci_subvendor = 0x8086, .pci_subdevice = 0xe214, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe234, .config = ALC880_3ST }, { .pci_subvendor = 0x8086, .pci_subdevice = 0xe302, .config = ALC880_3ST }, { .pci_subvendor = 0x8086, .pci_subdevice = 0xe303, .config = ALC880_3ST }, { .pci_subvendor = 0x8086, .pci_subdevice = 0xe304, .config = ALC880_3ST }, @@ -2145,6 +2161,7 @@ static struct hda_board_config alc880_cf { .pci_subvendor = 0x107b, .pci_subdevice = 0x4040, .config = ALC880_3ST }, { .pci_subvendor = 0x107b, .pci_subdevice = 0x4041, .config = ALC880_3ST }, /* TCL S700 */ + { .modelname = "tcl", .config = ALC880_TCL_S700 }, { .pci_subvendor = 0x19db, .pci_subdevice = 0x4188, .config = ALC880_TCL_S700 }, /* Back 3 jack, front 2 jack (Internal add Aux-In) */ @@ -2156,8 +2173,13 @@ static struct hda_board_config alc880_cf { .modelname = "3stack-digout", .config = ALC880_3ST_DIG }, { .pci_subvendor = 0x8086, .pci_subdevice = 0xe308, .config = ALC880_3ST_DIG }, { .pci_subvendor = 0x1025, .pci_subdevice = 0x0070, .config = ALC880_3ST_DIG }, - /* Clevo m520G NB */ - { .pci_subvendor = 0x1558, .pci_subdevice = 0x0520, .config = ALC880_CLEVO }, + + /* Clevo laptops */ + { .modelname = "clevo", .config = ALC880_CLEVO }, + { .pci_subvendor = 0x1558, .pci_subdevice = 0x0520, + .config = ALC880_CLEVO }, /* Clevo m520G NB */ + { .pci_subvendor = 0x1558, .pci_subdevice = 0x0660, + .config = ALC880_CLEVO }, /* Clevo m665n */ /* Back 3 jack plus 1 SPDIF out jack, front 2 jack (Internal add Aux-In)*/ { .pci_subvendor = 0x8086, .pci_subdevice = 0xe305, .config = ALC880_3ST_DIG }, @@ -2222,12 +2244,16 @@ static struct hda_board_config alc880_cf { .pci_subvendor = 0x1043, .pci_subdevice = 0x1113, .config = ALC880_ASUS_DIG }, { .pci_subvendor = 0x1043, .pci_subdevice = 0x1173, .config = ALC880_ASUS_DIG }, { .pci_subvendor = 0x1043, .pci_subdevice = 0x1993, .config = ALC880_ASUS }, + { .pci_subvendor = 0x1043, .pci_subdevice = 0x10c2, .config = ALC880_ASUS_DIG }, /* Asus W6A */ { .pci_subvendor = 0x1043, .pci_subdevice = 0x10c3, .config = ALC880_ASUS_DIG }, { .pci_subvendor = 0x1043, .pci_subdevice = 0x1133, .config = ALC880_ASUS }, { .pci_subvendor = 0x1043, .pci_subdevice = 0x1123, .config = ALC880_ASUS_DIG }, { .pci_subvendor = 0x1043, .pci_subdevice = 0x1143, .config = ALC880_ASUS }, + { .modelname = "asus-w1v", .config = ALC880_ASUS_W1V }, { .pci_subvendor = 0x1043, .pci_subdevice = 0x10b3, .config = ALC880_ASUS_W1V }, + { .modelname = "asus-dig", .config = ALC880_ASUS_DIG }, { .pci_subvendor = 0x1043, .pci_subdevice = 0x8181, .config = ALC880_ASUS_DIG }, /* ASUS P4GPL-X */ + { .modelname = "asus-dig2", .config = ALC880_ASUS_DIG2 }, { .pci_subvendor = 0x1558, .pci_subdevice = 0x5401, .config = ALC880_ASUS_DIG2 }, { .modelname = "uniwill", .config = ALC880_UNIWILL_DIG }, @@ -2263,6 +2289,7 @@ static struct alc_config_preset alc880_p .dac_nids = alc880_dac_nids, .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), .channel_mode = alc880_threestack_modes, + .need_dac_fix = 1, .input_mux = &alc880_capture_source, }, [ALC880_3ST_DIG] = { @@ -2273,6 +2300,7 @@ static struct alc_config_preset alc880_p .dig_out_nid = ALC880_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), .channel_mode = alc880_threestack_modes, + .need_dac_fix = 1, .input_mux = &alc880_capture_source, }, [ALC880_TCL_S700] = { @@ -2365,6 +2393,7 @@ static struct alc_config_preset alc880_p .dac_nids = alc880_asus_dac_nids, .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), .channel_mode = alc880_asus_modes, + .need_dac_fix = 1, .input_mux = &alc880_capture_source, }, [ALC880_ASUS_DIG] = { @@ -2376,6 +2405,7 @@ static struct alc_config_preset alc880_p .dig_out_nid = ALC880_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), .channel_mode = alc880_asus_modes, + .need_dac_fix = 1, .input_mux = &alc880_capture_source, }, [ALC880_ASUS_DIG2] = { @@ -2387,6 +2417,7 @@ static struct alc_config_preset alc880_p .dig_out_nid = ALC880_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), .channel_mode = alc880_asus_modes, + .need_dac_fix = 1, .input_mux = &alc880_capture_source, }, [ALC880_ASUS_W1V] = { @@ -2398,6 +2429,7 @@ static struct alc_config_preset alc880_p .dig_out_nid = ALC880_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), .channel_mode = alc880_asus_modes, + .need_dac_fix = 1, .input_mux = &alc880_capture_source, }, [ALC880_UNIWILL_DIG] = { @@ -2408,6 +2440,7 @@ static struct alc_config_preset alc880_p .dig_out_nid = ALC880_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), .channel_mode = alc880_asus_modes, + .need_dac_fix = 1, .input_mux = &alc880_capture_source, }, [ALC880_CLEVO] = { @@ -2419,6 +2452,7 @@ static struct alc_config_preset alc880_p .hp_nid = 0x03, .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), .channel_mode = alc880_threestack_modes, + .need_dac_fix = 1, .input_mux = &alc880_capture_source, }, [ALC880_LG] = { @@ -2430,6 +2464,7 @@ static struct alc_config_preset alc880_p .dig_out_nid = ALC880_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc880_lg_ch_modes), .channel_mode = alc880_lg_ch_modes, + .need_dac_fix = 1, .input_mux = &alc880_lg_capture_source, .unsol_event = alc880_lg_unsol_event, .init_hook = alc880_lg_automute, @@ -3900,7 +3935,8 @@ static struct hda_board_config alc260_cf { .pci_subvendor = 0x152d, .pci_subdevice = 0x0729, .config = ALC260_BASIC }, /* CTL Travel Master U553W */ { .modelname = "hp", .config = ALC260_HP }, - { .pci_subvendor = 0x103c, .pci_subdevice = 0x3010, .config = ALC260_HP }, + { .modelname = "hp-3013", .config = ALC260_HP_3013 }, + { .pci_subvendor = 0x103c, .pci_subdevice = 0x3010, .config = ALC260_HP_3013 }, { .pci_subvendor = 0x103c, .pci_subdevice = 0x3011, .config = ALC260_HP }, { .pci_subvendor = 0x103c, .pci_subdevice = 0x3012, .config = ALC260_HP_3013 }, { .pci_subvendor = 0x103c, .pci_subdevice = 0x3013, .config = ALC260_HP_3013 }, @@ -4266,6 +4302,13 @@ static struct hda_verb alc882_init_verbs { } }; +static struct hda_verb alc882_eapd_verbs[] = { + /* change to EAPD mode */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, + {0x20, AC_VERB_SET_PROC_COEF, 0x3060}, + { } +}; + /* * generic initialization of ADC, input mixers and output mixers */ @@ -4397,6 +4440,9 @@ static struct hda_board_config alc882_cf .config = ALC882_6ST_DIG }, /* Foxconn */ { .pci_subvendor = 0x1019, .pci_subdevice = 0x6668, .config = ALC882_6ST_DIG }, /* ECS to Intel*/ + { .modelname = "arima", .config = ALC882_ARIMA }, + { .pci_subvendor = 0x161f, .pci_subdevice = 0x2054, + .config = ALC882_ARIMA }, /* Arima W820Di1 */ { .modelname = "auto", .config = ALC882_AUTO }, {} }; @@ -4411,6 +4457,7 @@ static struct alc_config_preset alc882_p .dig_in_nid = ALC882_DIGIN_NID, .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), .channel_mode = alc882_ch_modes, + .need_dac_fix = 1, .input_mux = &alc882_capture_source, }, [ALC882_6ST_DIG] = { @@ -4424,6 +4471,15 @@ static struct alc_config_preset alc882_p .channel_mode = alc882_sixstack_modes, .input_mux = &alc882_capture_source, }, + [ALC882_ARIMA] = { + .mixers = { alc882_base_mixer, alc882_chmode_mixer }, + .init_verbs = { alc882_init_verbs, alc882_eapd_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes), + .channel_mode = alc882_sixstack_modes, + .input_mux = &alc882_capture_source, + }, }; @@ -4999,16 +5055,20 @@ #define alc883_pcm_digital_capture alc88 */ static struct hda_board_config alc883_cfg_tbl[] = { { .modelname = "3stack-dig", .config = ALC883_3ST_2ch_DIG }, + { .modelname = "3stack-6ch-dig", .config = ALC883_3ST_6ch_DIG }, + { .pci_subvendor = 0x1019, .pci_subdevice = 0x6668, + .config = ALC883_3ST_6ch_DIG }, /* ECS to Intel*/ + { .modelname = "3stack-6ch", .config = ALC883_3ST_6ch }, + { .pci_subvendor = 0x108e, .pci_subdevice = 0x534d, + .config = ALC883_3ST_6ch }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xd601, + .config = ALC883_3ST_6ch }, /* D102GGC */ { .modelname = "6stack-dig", .config = ALC883_6ST_DIG }, - { .modelname = "6stack-dig-demo", .config = ALC888_DEMO_BOARD }, { .pci_subvendor = 0x1462, .pci_subdevice = 0x6668, .config = ALC883_6ST_DIG }, /* MSI */ { .pci_subvendor = 0x105b, .pci_subdevice = 0x6668, .config = ALC883_6ST_DIG }, /* Foxconn */ - { .pci_subvendor = 0x1019, .pci_subdevice = 0x6668, - .config = ALC883_3ST_6ch_DIG }, /* ECS to Intel*/ - { .pci_subvendor = 0x108e, .pci_subdevice = 0x534d, - .config = ALC883_3ST_6ch }, + { .modelname = "6stack-dig-demo", .config = ALC888_DEMO_BOARD }, { .modelname = "auto", .config = ALC883_AUTO }, {} }; @@ -5038,6 +5098,7 @@ static struct alc_config_preset alc883_p .dig_in_nid = ALC883_DIGIN_NID, .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), .channel_mode = alc883_3ST_6ch_modes, + .need_dac_fix = 1, .input_mux = &alc883_capture_source, }, [ALC883_3ST_6ch] = { @@ -5049,6 +5110,7 @@ static struct alc_config_preset alc883_p .adc_nids = alc883_adc_nids, .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), .channel_mode = alc883_3ST_6ch_modes, + .need_dac_fix = 1, .input_mux = &alc883_capture_source, }, [ALC883_6ST_DIG] = { @@ -5217,8 +5279,10 @@ static int patch_alc883(struct hda_codec spec->stream_digital_playback = &alc883_pcm_digital_playback; spec->stream_digital_capture = &alc883_pcm_digital_capture; - spec->adc_nids = alc883_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids); + if (! spec->adc_nids && spec->input_mux) { + spec->adc_nids = alc883_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids); + } codec->patch_ops = alc_patch_ops; if (board_config == ALC883_AUTO) @@ -5481,6 +5545,7 @@ static struct snd_kcontrol_new alc262_fu .info = snd_hda_mixer_amp_volume_info, .get = snd_hda_mixer_amp_volume_get, .put = alc262_fujitsu_master_vol_put, + .tlv = { .c = snd_hda_mixer_amp_tlv }, .private_value = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT), }, { @@ -5499,6 +5564,13 @@ static struct snd_kcontrol_new alc262_fu { } /* end */ }; +/* additional init verbs for Benq laptops */ +static struct hda_verb alc262_EAPD_verbs[] = { + {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, + {0x20, AC_VERB_SET_PROC_COEF, 0x3070}, + {} +}; + /* add playback controls from the parsed DAC table */ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg) { @@ -5769,6 +5841,7 @@ static struct hda_board_config alc262_cf { .modelname = "fujitsu", .config = ALC262_FUJITSU }, { .pci_subvendor = 0x10cf, .pci_subdevice = 0x1397, .config = ALC262_FUJITSU }, + { .modelname = "hp-bpc", .config = ALC262_HP_BPC }, { .pci_subvendor = 0x103c, .pci_subdevice = 0x208c, .config = ALC262_HP_BPC }, /* xw4400 */ { .pci_subvendor = 0x103c, .pci_subdevice = 0x3014, @@ -5777,6 +5850,9 @@ static struct hda_board_config alc262_cf .config = ALC262_HP_BPC }, /* xw8400 */ { .pci_subvendor = 0x103c, .pci_subdevice = 0x12fe, .config = ALC262_HP_BPC }, /* xw9400 */ + { .modelname = "benq", .config = ALC262_BENQ_ED8 }, + { .pci_subvendor = 0x17ff, .pci_subdevice = 0x0560, + .config = ALC262_BENQ_ED8 }, { .modelname = "auto", .config = ALC262_AUTO }, {} }; @@ -5814,6 +5890,16 @@ static struct alc_config_preset alc262_p .channel_mode = alc262_modes, .input_mux = &alc262_HP_capture_source, }, + [ALC262_BENQ_ED8] = { + .mixers = { alc262_base_mixer }, + .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + }, }; static int patch_alc262(struct hda_codec *codec) @@ -5942,6 +6028,23 @@ static struct hda_channel_mode alc861_th { 2, alc861_threestack_ch2_init }, { 6, alc861_threestack_ch6_init }, }; +/* Set mic1 as input and unmute the mixer */ +static struct hda_verb alc861_uniwill_m31_ch2_init[] = { + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/ + { } /* end */ +}; +/* Set mic1 as output and mute mixer */ +static struct hda_verb alc861_uniwill_m31_ch4_init[] = { + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/ + { } /* end */ +}; + +static struct hda_channel_mode alc861_uniwill_m31_modes[2] = { + { 2, alc861_uniwill_m31_ch2_init }, + { 4, alc861_uniwill_m31_ch4_init }, +}; /* patch-ALC861 */ @@ -6020,6 +6123,47 @@ static struct snd_kcontrol_new alc861_3S }, { } /* end */ }; +static struct snd_kcontrol_new alc861_uniwill_m31_mixer[] = { + /* output mixer control */ + HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 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", 0x04, 0x0, HDA_OUTPUT), */ + + /* Input mixer control */ + /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */ + HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT), + + /* Capture mixer control */ + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .count = 1, + .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 = alc_ch_mode_info, + .get = alc_ch_mode_get, + .put = alc_ch_mode_put, + .private_value = ARRAY_SIZE(alc861_uniwill_m31_modes), + }, + { } /* end */ +}; /* * generic initialization of ADC, input mixers and output mixers @@ -6148,6 +6292,67 @@ static struct hda_verb alc861_threestack {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, { } }; + +static struct hda_verb alc861_uniwill_m31_init_verbs[] = { + /* + * Unmute ADC0 and set the default input to mic-in + */ + /* port-A for surround (rear panel) */ + { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + /* port-B for mic-in (rear panel) with vref */ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* port-C for line-in (rear panel) */ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* port-D for Front */ + { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* port-E for HP out (front panel) */ + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, // this has to be set to VREF80 + /* route front PCM to HP */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01 }, + /* port-F for mic-in (front panel) with vref */ + { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* port-G for CLFE (rear panel) */ + { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + /* port-H for side (rear panel) */ + { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + /* CD-in */ + { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* route front mic to ADC1*/ + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + /* Unmute DAC0~3 & spdif out*/ + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* Unmute Mixer 14 (mic) 1c (Line in)*/ + {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* Unmute Stereo Mixer 15 */ + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, //Output 0~12 step + + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, // hp used DAC 3 (Front) + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + { } +}; + /* * generic initialization of ADC, input mixers and output mixers */ @@ -6477,10 +6682,14 @@ static struct hda_board_config alc861_cf { .modelname = "3stack", .config = ALC861_3ST }, { .pci_subvendor = 0x8086, .pci_subdevice = 0xd600, .config = ALC861_3ST }, + { .modelname = "3stack-660", .config = ALC660_3ST }, { .pci_subvendor = 0x1043, .pci_subdevice = 0x81e7, .config = ALC660_3ST }, { .modelname = "3stack-dig", .config = ALC861_3ST_DIG }, { .modelname = "6stack-dig", .config = ALC861_6ST_DIG }, + { .modelname = "uniwill-m31", .config = ALC861_UNIWILL_M31}, + { .pci_subvendor = 0x1584, .pci_subdevice = 0x9072, + .config = ALC861_UNIWILL_M31 }, { .modelname = "auto", .config = ALC861_AUTO }, {} }; @@ -6493,6 +6702,7 @@ static struct alc_config_preset alc861_p .dac_nids = alc861_dac_nids, .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes), .channel_mode = alc861_threestack_modes, + .need_dac_fix = 1, .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), .adc_nids = alc861_adc_nids, .input_mux = &alc861_capture_source, @@ -6505,6 +6715,7 @@ static struct alc_config_preset alc861_p .dig_out_nid = ALC861_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes), .channel_mode = alc861_threestack_modes, + .need_dac_fix = 1, .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), .adc_nids = alc861_adc_nids, .input_mux = &alc861_capture_source, @@ -6528,10 +6739,25 @@ static struct alc_config_preset alc861_p .dac_nids = alc660_dac_nids, .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes), .channel_mode = alc861_threestack_modes, + .need_dac_fix = 1, .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), .adc_nids = alc861_adc_nids, .input_mux = &alc861_capture_source, }, + [ALC861_UNIWILL_M31] = { + .mixers = { alc861_uniwill_m31_mixer }, + .init_verbs = { alc861_uniwill_m31_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861_dac_nids), + .dac_nids = alc861_dac_nids, + .dig_out_nid = ALC861_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc861_uniwill_m31_modes), + .channel_mode = alc861_uniwill_m31_modes, + .need_dac_fix = 1, + .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), + .adc_nids = alc861_adc_nids, + .input_mux = &alc861_capture_source, + }, + }; diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index ea99083..d709389 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -136,6 +136,14 @@ static hda_nid_t stac927x_mux_nids[3] = 0x15, 0x16, 0x17 }; +static hda_nid_t stac9205_adc_nids[2] = { + 0x12, 0x13 +}; + +static hda_nid_t stac9205_mux_nids[2] = { + 0x19, 0x1a +}; + static hda_nid_t stac9200_pin_nids[8] = { 0x08, 0x09, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, }; @@ -151,6 +159,13 @@ static hda_nid_t stac927x_pin_nids[14] = 0x14, 0x21, 0x22, 0x23, }; +static hda_nid_t stac9205_pin_nids[12] = { + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x14, 0x16, 0x17, 0x18, + 0x21, 0x22, + +}; + static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); @@ -214,6 +229,12 @@ static struct hda_verb stac927x_core_ini {} }; +static struct hda_verb stac9205_core_init[] = { + /* set master volume and direct control */ + { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + {} +}; + static struct snd_kcontrol_new stac9200_mixer[] = { HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT), HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT), @@ -277,6 +298,21 @@ static snd_kcontrol_new_t stac927x_mixer { } /* end */ }; +static snd_kcontrol_new_t stac9205_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Input Source", + .count = 1, + .info = stac92xx_mux_enum_info, + .get = stac92xx_mux_enum_get, + .put = stac92xx_mux_enum_put, + }, + HDA_CODEC_VOLUME("InMux Capture Volume", 0x19, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("InVol Capture Volume", 0x1b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("ADCMux Capture Switch", 0x1d, 0x0, HDA_OUTPUT), + { } /* end */ +}; + static int stac92xx_build_controls(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -415,6 +451,24 @@ static struct hda_board_config stac927x_ {} /* terminator */ }; +static unsigned int ref9205_pin_configs[12] = { + 0x40000100, 0x40000100, 0x01016011, 0x01014010, + 0x01813122, 0x01a19021, 0x40000100, 0x40000100, + 0x40000100, 0x40000100, 0x01441030, 0x01c41030 +}; + +static unsigned int *stac9205_brd_tbl[] = { + ref9205_pin_configs, +}; + +static struct hda_board_config stac9205_cfg_tbl[] = { + { .modelname = "ref", + .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2668, /* DFI LanParty */ + .config = STAC_REF }, /* SigmaTel reference board */ + {} /* terminator */ +}; + static void stac92xx_set_config_regs(struct hda_codec *codec) { int i; @@ -1089,6 +1143,8 @@ static int stac92xx_init(struct hda_code STAC_UNSOL_ENABLE); /* fake event to set up pins */ codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26); + /* enable the headphones by default. If/when unsol_event detection works, this will be ignored */ + stac92xx_auto_init_hp_out(codec); } else { stac92xx_auto_init_multi_out(codec); stac92xx_auto_init_hp_out(codec); @@ -1354,11 +1410,51 @@ static int patch_stac927x(struct hda_cod return 0; } +static int patch_stac9205(struct hda_codec *codec) +{ + struct sigmatel_spec *spec; + int err; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + spec->board_config = snd_hda_check_board_config(codec, stac9205_cfg_tbl); + if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9205, using BIOS defaults\n"); + else { + spec->num_pins = 14; + spec->pin_nids = stac9205_pin_nids; + spec->pin_configs = stac9205_brd_tbl[spec->board_config]; + stac92xx_set_config_regs(codec); + } + + spec->adc_nids = stac9205_adc_nids; + spec->mux_nids = stac9205_mux_nids; + spec->num_muxes = 3; + + spec->init = stac9205_core_init; + spec->mixer = stac9205_mixer; + + spec->multiout.dac_nids = spec->dac_nids; + + err = stac92xx_parse_auto_config(codec, 0x1f, 0x20); + if (err < 0) { + stac92xx_free(codec); + return err; + } + + codec->patch_ops = stac92xx_patch_ops; + + return 0; +} + /* - * STAC 7661(?) hack + * STAC 7661(?) and 7664 hack */ -/* static config for Sony VAIO FE550G */ +/* static config for Sony VAIO FE550G and Sony VAIO AR */ static hda_nid_t vaio_dacs[] = { 0x2 }; #define VAIO_HP_DAC 0x5 static hda_nid_t vaio_adcs[] = { 0x8 /*,0x6*/ }; @@ -1434,6 +1530,7 @@ static struct snd_kcontrol_new vaio_mixe .info = snd_hda_mixer_amp_volume_info, .get = snd_hda_mixer_amp_volume_get, .put = vaio_master_vol_put, + .tlv = { .c = snd_hda_mixer_amp_tlv }, .private_value = HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), }, { @@ -1458,7 +1555,7 @@ static struct snd_kcontrol_new vaio_mixe {} }; -static struct hda_codec_ops stac7661_patch_ops = { +static struct hda_codec_ops stac766x_patch_ops = { .build_controls = stac92xx_build_controls, .build_pcms = stac92xx_build_pcms, .init = stac92xx_init, @@ -1468,23 +1565,25 @@ #ifdef CONFIG_PM #endif }; -enum { STAC7661_VAIO }; +enum { STAC766x_VAIO }; -static struct hda_board_config stac7661_cfg_tbl[] = { - { .modelname = "vaio", .config = STAC7661_VAIO }, +static struct hda_board_config stac766x_cfg_tbl[] = { + { .modelname = "vaio", .config = STAC766x_VAIO }, { .pci_subvendor = 0x104d, .pci_subdevice = 0x81e6, - .config = STAC7661_VAIO }, + .config = STAC766x_VAIO }, { .pci_subvendor = 0x104d, .pci_subdevice = 0x81ef, - .config = STAC7661_VAIO }, + .config = STAC766x_VAIO }, + { .pci_subvendor = 0x104d, .pci_subdevice = 0x81fd, + .config = STAC766x_VAIO }, {} }; -static int patch_stac7661(struct hda_codec *codec) +static int patch_stac766x(struct hda_codec *codec) { struct sigmatel_spec *spec; int board_config; - board_config = snd_hda_check_board_config(codec, stac7661_cfg_tbl); + board_config = snd_hda_check_board_config(codec, stac766x_cfg_tbl); if (board_config < 0) /* unknown config, let generic-parser do its job... */ return snd_hda_parse_generic_codec(codec); @@ -1495,7 +1594,7 @@ static int patch_stac7661(struct hda_cod codec->spec = spec; switch (board_config) { - case STAC7661_VAIO: + case STAC766x_VAIO: spec->mixer = vaio_mixer; spec->init = vaio_init; spec->multiout.max_channels = 2; @@ -1509,7 +1608,7 @@ static int patch_stac7661(struct hda_cod break; } - codec->patch_ops = stac7661_patch_ops; + codec->patch_ops = stac766x_patch_ops; return 0; } @@ -1525,12 +1624,12 @@ struct hda_codec_preset snd_hda_preset_s { .id = 0x83847681, .name = "STAC9220D/9223D A2", .patch = patch_stac922x }, { .id = 0x83847682, .name = "STAC9221 A2", .patch = patch_stac922x }, { .id = 0x83847683, .name = "STAC9221D A2", .patch = patch_stac922x }, - { .id = 0x83847618, .name = "STAC9227", .patch = patch_stac922x }, - { .id = 0x83847619, .name = "STAC9227", .patch = patch_stac922x }, - { .id = 0x83847616, .name = "STAC9228", .patch = patch_stac922x }, - { .id = 0x83847617, .name = "STAC9228", .patch = patch_stac922x }, - { .id = 0x83847614, .name = "STAC9229", .patch = patch_stac922x }, - { .id = 0x83847615, .name = "STAC9229", .patch = patch_stac922x }, + { .id = 0x83847618, .name = "STAC9227", .patch = patch_stac927x }, + { .id = 0x83847619, .name = "STAC9227", .patch = patch_stac927x }, + { .id = 0x83847616, .name = "STAC9228", .patch = patch_stac927x }, + { .id = 0x83847617, .name = "STAC9228", .patch = patch_stac927x }, + { .id = 0x83847614, .name = "STAC9229", .patch = patch_stac927x }, + { .id = 0x83847615, .name = "STAC9229", .patch = patch_stac927x }, { .id = 0x83847620, .name = "STAC9274", .patch = patch_stac927x }, { .id = 0x83847621, .name = "STAC9274D", .patch = patch_stac927x }, { .id = 0x83847622, .name = "STAC9273X", .patch = patch_stac927x }, @@ -1541,6 +1640,15 @@ struct hda_codec_preset snd_hda_preset_s { .id = 0x83847627, .name = "STAC9271D", .patch = patch_stac927x }, { .id = 0x83847628, .name = "STAC9274X5NH", .patch = patch_stac927x }, { .id = 0x83847629, .name = "STAC9274D5NH", .patch = patch_stac927x }, - { .id = 0x83847661, .name = "STAC7661", .patch = patch_stac7661 }, + { .id = 0x83847661, .name = "STAC7661", .patch = patch_stac766x }, + { .id = 0x838476a0, .name = "STAC9205", .patch = patch_stac9205 }, + { .id = 0x838476a1, .name = "STAC9205D", .patch = patch_stac9205 }, + { .id = 0x838476a2, .name = "STAC9204", .patch = patch_stac9205 }, + { .id = 0x838476a3, .name = "STAC9204D", .patch = patch_stac9205 }, + { .id = 0x838476a4, .name = "STAC9255", .patch = patch_stac9205 }, + { .id = 0x838476a5, .name = "STAC9255D", .patch = patch_stac9205 }, + { .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 }, + { .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 }, + { .id = 0x83847664, .name = "STAC7664", .patch = patch_stac766x }, {} /* terminator */ }; diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index bf20858..9b8325d 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -1857,7 +1857,7 @@ static int snd_ice1712_pro_internal_cloc { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); static unsigned int xrate[13] = { - 8000, 9600, 11025, 12000, 1600, 22050, 24000, + 8000, 9600, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000 }; unsigned char oval; @@ -1924,7 +1924,7 @@ static int snd_ice1712_pro_internal_cloc { int val; static unsigned int xrate[13] = { - 8000, 9600, 11025, 12000, 1600, 22050, 24000, + 8000, 9600, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000 }; @@ -1941,7 +1941,7 @@ static int snd_ice1712_pro_internal_cloc struct snd_ctl_elem_value *ucontrol) { static unsigned int xrate[13] = { - 8000, 9600, 11025, 12000, 1600, 22050, 24000, + 8000, 9600, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000 }; unsigned char oval; diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c index fec9440..1134a57 100644 --- a/sound/pci/ice1712/revo.c +++ b/sound/pci/ice1712/revo.c @@ -98,6 +98,9 @@ static unsigned int revo51_num_stereo[] static char *revo51_channel_names[] = {"PCM Playback Volume", "PCM Center Playback Volume", "PCM LFE Playback Volume", "PCM Rear Playback Volume"}; +static unsigned int revo51_adc_num_stereo[] = {2}; +static char *revo51_adc_channel_names[] = {"PCM Capture Volume","PCM Capture Switch"}; + static struct snd_akm4xxx akm_revo_front __devinitdata = { .type = SND_AK4381, .num_dacs = 2, @@ -159,7 +162,26 @@ static struct snd_ak4xxx_private akm_rev .data_mask = VT1724_REVO_CDOUT, .clk_mask = VT1724_REVO_CCLK, .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2, - .cs_addr = 0, + .cs_addr = VT1724_REVO_CS1 | VT1724_REVO_CS2, + .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2, + .add_flags = VT1724_REVO_CCLK, /* high at init */ + .mask_flags = 0, +}; + +static struct snd_akm4xxx akm_revo51_adc __devinitdata = { + .type = SND_AK5365, + .num_adcs = 2, + .num_stereo = revo51_adc_num_stereo, + .channel_names = revo51_adc_channel_names +}; + +static struct snd_ak4xxx_private akm_revo51_adc_priv __devinitdata = { + .caddr = 2, + .cif = 0, + .data_mask = VT1724_REVO_CDOUT, + .clk_mask = VT1724_REVO_CCLK, + .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2, + .cs_addr = VT1724_REVO_CS0 | VT1724_REVO_CS2, .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2, .add_flags = VT1724_REVO_CCLK, /* high at init */ .mask_flags = 0, @@ -202,9 +224,13 @@ static int __devinit revo_init(struct sn snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE, VT1724_REVO_MUTE); break; case VT1724_SUBDEVICE_REVOLUTION51: - ice->akm_codecs = 1; + ice->akm_codecs = 2; if ((err = snd_ice1712_akm4xxx_init(ak, &akm_revo51, &akm_revo51_priv, ice)) < 0) return err; + err = snd_ice1712_akm4xxx_init(ak + 1, &akm_revo51_adc, + &akm_revo51_adc_priv, ice); + if (err < 0) + return err; /* unmute all codecs - needed! */ snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE, VT1724_REVO_MUTE); break; diff --git a/sound/pci/ice1712/revo.h b/sound/pci/ice1712/revo.h index dea52ea..efbb86e 100644 --- a/sound/pci/ice1712/revo.h +++ b/sound/pci/ice1712/revo.h @@ -42,7 +42,7 @@ extern struct snd_ice1712_card_info snd_ #define VT1724_REVO_CCLK 0x02 #define VT1724_REVO_CDIN 0x04 /* not used */ #define VT1724_REVO_CDOUT 0x08 -#define VT1724_REVO_CS0 0x10 /* not used */ +#define VT1724_REVO_CS0 0x10 /* AK5365 chipselect for Rev. 5.1 */ #define VT1724_REVO_CS1 0x20 /* front AKM4381 chipselect */ #define VT1724_REVO_CS2 0x40 /* surround AKM4355 chipselect */ #define VT1724_REVO_MUTE (1<<22) /* 0 = all mute, 1 = normal operation */ diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 6874263..72dbaed 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -2251,6 +2251,16 @@ static int snd_intel8x0_ich_chip_init(st /* ACLink on, 2 channels */ cnt = igetdword(chip, ICHREG(GLOB_CNT)); cnt &= ~(ICH_ACLINK | ICH_PCM_246_MASK); +#ifdef CONFIG_SND_AC97_POWER_SAVE + /* do cold reset - the full ac97 powerdown may leave the controller + * in a warm state but actually it cannot communicate with the codec. + */ + iputdword(chip, ICHREG(GLOB_CNT), cnt & ~ICH_AC97COLD); + cnt = igetdword(chip, ICHREG(GLOB_CNT)); + udelay(10); + iputdword(chip, ICHREG(GLOB_CNT), cnt | ICH_AC97COLD); + msleep(1); +#else /* finish cold or do warm reset */ cnt |= (cnt & ICH_AC97COLD) == 0 ? ICH_AC97COLD : ICH_AC97WARM; iputdword(chip, ICHREG(GLOB_CNT), cnt); @@ -2265,6 +2275,7 @@ static int snd_intel8x0_ich_chip_init(st return -EIO; __ok: +#endif if (probing) { /* wait for any codec ready status. * Once it becomes ready it should remain ready @@ -2485,7 +2496,7 @@ static int intel8x0_resume(struct pci_de card->shortname, chip); chip->irq = pci->irq; synchronize_irq(chip->irq); - snd_intel8x0_chip_init(chip, 1); + snd_intel8x0_chip_init(chip, 0); /* re-initialize mixer stuff */ if (chip->device_type == DEVICE_INTEL_ICH4) { @@ -2615,6 +2626,7 @@ static void __devinit intel8x0_measure_a /* not 48000Hz, tuning the clock.. */ chip->ac97_bus->clock = (chip->ac97_bus->clock * 48000) / pos; printk(KERN_INFO "intel8x0: clocking to %d\n", chip->ac97_bus->clock); + snd_ac97_update_power(chip->ac97[0], AC97_PCM_FRONT_DAC_RATE, 0); } #ifdef CONFIG_PROC_FS diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index f435fcd..fe210c8 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -673,9 +673,13 @@ static struct lbuspath lbus_rec_path = { #define FIRMWARE_VERSIONS 1 static union firmware_version firmware_versions[] = { { - .firmware.ASIC = 3,.firmware.CODEC = 2, - .firmware.AUXDSP = 3,.firmware.PROG = 773, - }, + .firmware = { + .ASIC = 3, + .CODEC = 2, + .AUXDSP = 3, + .PROG = 773, + }, + }, }; static u32 atoh(unsigned char *in, unsigned int len) diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 08da923..e0e3bfd 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -1277,7 +1277,18 @@ static int snd_via82xx_pcm_close(struct if (! ratep->used) ratep->rate = 0; spin_unlock_irq(&ratep->lock); - + if (! ratep->rate) { + if (! viadev->direction) { + snd_ac97_update_power(chip->ac97, + AC97_PCM_FRONT_DAC_RATE, 0); + snd_ac97_update_power(chip->ac97, + AC97_PCM_SURR_DAC_RATE, 0); + snd_ac97_update_power(chip->ac97, + AC97_PCM_LFE_DAC_RATE, 0); + } else + snd_ac97_update_power(chip->ac97, + AC97_PCM_LR_ADC_RATE, 0); + } viadev->substream = NULL; return 0; } @@ -2393,6 +2404,7 @@ static int __devinit check_dxs_list(stru { .subvendor = 0x16f3, .subdevice = 0x6405, .action = VIA_DXS_SRC }, /* Jetway K8M8MS */ { .subvendor = 0x1734, .subdevice = 0x1078, .action = VIA_DXS_SRC }, /* FSC Amilo L7300 */ { .subvendor = 0x1734, .subdevice = 0x1093, .action = VIA_DXS_SRC }, /* FSC */ + { .subvendor = 0x1734, .subdevice = 0x10ab, .action = VIA_DXS_SRC }, /* FSC */ { .subvendor = 0x1849, .subdevice = 0x3059, .action = VIA_DXS_NO_VRA }, /* ASRock K7VM2 */ { .subvendor = 0x1849, .subdevice = 0x9739, .action = VIA_DXS_SRC }, /* ASRock mobo(?) */ { .subvendor = 0x1849, .subdevice = 0x9761, .action = VIA_DXS_SRC }, /* ASRock mobo(?) */ diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.c b/sound/pcmcia/pdaudiocf/pdaudiocf.c index 1c09e5f..fd3590f 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf.c +++ b/sound/pcmcia/pdaudiocf/pdaudiocf.c @@ -206,7 +206,7 @@ static void snd_pdacf_detach(struct pcmc snd_pdacf_powerdown(chip); chip->chip_status |= PDAUDIOCF_STAT_IS_STALE; /* to be sure */ snd_card_disconnect(chip->card); - snd_card_free_in_thread(chip->card); + snd_card_free_when_closed(chip->card); } /* diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c index cafe664..76c85cf 100644 --- a/sound/pcmcia/vx/vxpocket.c +++ b/sound/pcmcia/vx/vxpocket.c @@ -65,7 +65,7 @@ static void vxpocket_release(struct pcmc } /* - * destructor, called from snd_card_free_in_thread() + * destructor, called from snd_card_free_when_closed() */ static int snd_vxpocket_dev_free(struct snd_device *device) { @@ -363,7 +363,7 @@ static void vxpocket_detach(struct pcmci chip->chip_status |= VX_STAT_IS_STALE; /* to be sure */ snd_card_disconnect(chip->card); vxpocket_release(link); - snd_card_free_in_thread(chip->card); + snd_card_free_when_closed(chip->card); } /* diff --git a/sound/ppc/beep.c b/sound/ppc/beep.c index 5fec1e5..5f38f67 100644 --- a/sound/ppc/beep.c +++ b/sound/ppc/beep.c @@ -215,15 +215,18 @@ int __init snd_pmac_attach_beep(struct s { struct pmac_beep *beep; struct input_dev *input_dev; + struct snd_kcontrol *beep_ctl; void *dmabuf; int err = -ENOMEM; beep = kzalloc(sizeof(*beep), GFP_KERNEL); + if (! beep) + return -ENOMEM; dmabuf = dma_alloc_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4, &beep->addr, GFP_KERNEL); input_dev = input_allocate_device(); - if (!beep || !dmabuf || !input_dev) - goto fail; + if (! dmabuf || ! input_dev) + goto fail1; /* FIXME: set more better values */ input_dev->name = "PowerMac Beep"; @@ -244,17 +247,24 @@ int __init snd_pmac_attach_beep(struct s beep->volume = BEEP_VOLUME; beep->running = 0; - err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_pmac_beep_mixer, chip)); + beep_ctl = snd_ctl_new1(&snd_pmac_beep_mixer, chip); + err = snd_ctl_add(chip->card, beep_ctl); if (err < 0) - goto fail; + goto fail1; + + chip->beep = beep; - chip->beep = beep; - input_register_device(beep->dev); - - return 0; - - fail: input_free_device(input_dev); - kfree(dmabuf); + err = input_register_device(beep->dev); + if (err) + goto fail2; + + return 0; + + fail2: snd_ctl_remove(chip->card, beep_ctl); + fail1: input_free_device(input_dev); + if (dmabuf) + dma_free_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4, + dmabuf, beep->addr); kfree(beep); return err; } diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c index f3ae6e2..405c603 100644 --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -34,7 +34,7 @@ * (the second one is a monitor/tee pipe, valid only for serial input). * * The mmcodec is connected via the CHI bus and needs the data & some - * parameters (volume, balance, output selection) timemultiplexed in 8 byte + * parameters (volume, output selection) timemultiplexed in 8 byte * chunks. It also has a control mode, which serves for audio format setting. * * Looking at the CS4215 data sheet it is easy to set up 2 or 4 codecs on @@ -104,17 +104,15 @@ static char *cmds[] = { #define dprintk(a, x...) if(dbri_debug & a) printk(KERN_DEBUG x) -#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | \ - (1 << 27) | \ - value) #else #define dprintk(a, x...) -#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | \ - (intr << 27) | \ - value) #endif /* DBRI_DEBUG */ +#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | \ + (intr << 27) | \ + value) + /*************************************************************************** CS4215 specific definitions and structures ****************************************************************************/ @@ -240,12 +238,6 @@ #define REG8 0x20UL /* Command Queue Po #define REG9 0x24UL /* Interrupt Queue Pointer */ #define DBRI_NO_CMDS 64 -#define DBRI_NO_INTS 1 /* Note: the value of this define was - * originally 2. The ringbuffer to store - * interrupts in dma is currently broken. - * This is a temporary fix until the ringbuffer - * is fixed. - */ #define DBRI_INT_BLK 64 #define DBRI_NO_DESCS 64 #define DBRI_NO_PIPES 32 @@ -260,8 +252,8 @@ #define DBRI_NO_STREAMS 2 /* One transmit/receive descriptor */ struct dbri_mem { volatile __u32 word1; - volatile __u32 ba; /* Transmit/Receive Buffer Address */ - volatile __u32 nda; /* Next Descriptor Address */ + __u32 ba; /* Transmit/Receive Buffer Address */ + __u32 nda; /* Next Descriptor Address */ volatile __u32 word4; }; @@ -270,7 +262,7 @@ struct dbri_mem { */ struct dbri_dma { volatile s32 cmd[DBRI_NO_CMDS]; /* Place for commands */ - volatile s32 intr[DBRI_NO_INTS * DBRI_INT_BLK]; /* Interrupt field */ + volatile s32 intr[DBRI_INT_BLK]; /* Interrupt field */ struct dbri_mem desc[DBRI_NO_DESCS]; /* Xmit/receive descriptors */ }; @@ -282,9 +274,7 @@ enum in_or_out { PIPEinput, PIPEoutput } struct dbri_pipe { u32 sdp; /* SDP command word */ - enum in_or_out direction; int nextpipe; /* Next pipe in linked list */ - int prevpipe; int cycle; /* Offset of timeslot (bits) */ int length; /* Length of timeslot (bits) */ int first_desc; /* Index of first descriptor */ @@ -308,23 +298,20 @@ struct dbri_streaminfo { int pipe; /* Data pipe used */ int left_gain; /* mixer elements */ int right_gain; - int balance; }; /* This structure holds the information for both chips (DBRI & CS4215) */ struct snd_dbri { struct snd_card *card; /* ALSA card */ - struct snd_pcm *pcm; int regs_size, irq; /* Needed for unload */ struct sbus_dev *sdev; /* SBUS device info */ spinlock_t lock; - volatile struct dbri_dma *dma; /* Pointer to our DMA block */ + struct dbri_dma *dma; /* Pointer to our DMA block */ u32 dma_dvma; /* DBRI visible DMA address */ void __iomem *regs; /* dbri HW regs */ - int dbri_version; /* 'e' and up is OK */ int dbri_irqp; /* intr queue pointer */ int wait_send; /* sequence of command buffers send */ int wait_ackd; /* sequence of command buffers acknowledged */ @@ -345,8 +332,6 @@ struct snd_dbri { #define DBRI_MAX_VOLUME 63 /* Output volume */ #define DBRI_MAX_GAIN 15 /* Input gain */ -#define DBRI_RIGHT_BALANCE 255 -#define DBRI_MID_BALANCE (DBRI_RIGHT_BALANCE >> 1) /* DBRI Reg0 - Status Control Register - defines. (Page 17) */ #define D_P (1<<15) /* Program command & queue pointer valid */ @@ -690,7 +675,6 @@ #endif static void dbri_cmdsend(struct snd_dbri * dbri, volatile s32 * cmd) { volatile s32 *ptr; - u32 reg; for (ptr = &dbri->dma->cmd[0]; ptr < cmd; ptr++) { dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr); @@ -709,9 +693,6 @@ static void dbri_cmdsend(struct snd_dbri /* Set command pointer and signal it is valid. */ sbus_writel(dbri->dma_dvma, dbri->regs + REG8); - reg = sbus_readl(dbri->regs + REG0); - reg |= D_P; - sbus_writel(reg, dbri->regs + REG0); /*spin_unlock(&dbri->lock); */ } @@ -747,18 +728,6 @@ static void dbri_initialize(struct snd_d dprintk(D_GEN, "init: cmd: %p, int: %p\n", &dbri->dma->cmd[0], &dbri->dma->intr[0]); - /* - * Initialize the interrupt ringbuffer. - */ - for (n = 0; n < DBRI_NO_INTS - 1; n++) { - dma_addr = dbri->dma_dvma; - dma_addr += dbri_dma_off(intr, ((n + 1) & DBRI_INT_BLK)); - dbri->dma->intr[n * DBRI_INT_BLK] = dma_addr; - } - dma_addr = dbri->dma_dvma + dbri_dma_off(intr, 0); - dbri->dma->intr[n * DBRI_INT_BLK] = dma_addr; - dbri->dbri_irqp = 1; - /* Initialize pipes */ for (n = 0; n < DBRI_NO_PIPES; n++) dbri->pipes[n].desc = dbri->pipes[n].first_desc = -1; @@ -771,9 +740,14 @@ static void dbri_initialize(struct snd_d sbus_writel(tmp, dbri->regs + REG0); /* - * Set up the interrupt queue + * Initialize the interrupt ringbuffer. */ dma_addr = dbri->dma_dvma + dbri_dma_off(intr, 0); + dbri->dma->intr[0] = dma_addr; + dbri->dbri_irqp = 1; + /* + * Set up the interrupt queue + */ *(cmd++) = DBRI_CMD(D_IIQ, 0, 0); *(cmd++) = dma_addr; @@ -860,10 +834,6 @@ static void setup_pipe(struct snd_dbri * dbri->pipes[pipe].sdp = sdp; dbri->pipes[pipe].desc = -1; dbri->pipes[pipe].first_desc = -1; - if (sdp & D_SDP_TO_SER) - dbri->pipes[pipe].direction = PIPEoutput; - else - dbri->pipes[pipe].direction = PIPEinput; reset_pipe(dbri, pipe); } @@ -1382,14 +1352,6 @@ static void cs4215_setdata(struct snd_db int left_gain = info->left_gain % 64; int right_gain = info->right_gain % 64; - if (info->balance < DBRI_MID_BALANCE) { - right_gain *= info->balance; - right_gain /= DBRI_MID_BALANCE; - } else { - left_gain *= DBRI_RIGHT_BALANCE - info->balance; - left_gain /= DBRI_MID_BALANCE; - } - dbri->mm.data[0] &= ~0x3f; /* Reset the volume bits */ dbri->mm.data[1] &= ~0x3f; dbri->mm.data[0] |= (DBRI_MAX_VOLUME - left_gain); @@ -1957,10 +1919,8 @@ static void dbri_process_interrupt_buffe while ((x = dbri->dma->intr[dbri->dbri_irqp]) != 0) { dbri->dma->intr[dbri->dbri_irqp] = 0; dbri->dbri_irqp++; - if (dbri->dbri_irqp == (DBRI_NO_INTS * DBRI_INT_BLK)) + if (dbri->dbri_irqp == DBRI_INT_BLK) dbri->dbri_irqp = 1; - else if ((dbri->dbri_irqp & (DBRI_INT_BLK - 1)) == 0) - dbri->dbri_irqp++; dbri_process_one_interrupt(dbri, x); } @@ -2254,7 +2214,6 @@ static int __devinit snd_dbri_pcm(struct pcm->private_data = dbri; pcm->info_flags = 0; strcpy(pcm->name, dbri->card->shortname); - dbri->pcm = pcm; if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, @@ -2473,7 +2432,6 @@ static int __init snd_dbri_mixer(struct for (idx = DBRI_REC; idx < DBRI_NO_STREAMS; idx++) { dbri->stream_info[idx].left_gain = 0; dbri->stream_info[idx].right_gain = 0; - dbri->stream_info[idx].balance = DBRI_MID_BALANCE; } return 0; @@ -2505,12 +2463,11 @@ static void dbri_debug_read(struct snd_i struct dbri_pipe *pptr = &dbri->pipes[pipe]; snd_iprintf(buffer, "Pipe %d: %s SDP=0x%x desc=%d, " - "len=%d @ %d prev: %d next %d\n", + "len=%d @ %d next %d\n", pipe, - (pptr->direction == - PIPEinput ? "input" : "output"), pptr->sdp, - pptr->desc, pptr->length, pptr->cycle, - pptr->prevpipe, pptr->nextpipe); + ((pptr->sdp & D_SDP_TO_SER) ? "output" : "input"), + pptr->sdp, pptr->desc, + pptr->length, pptr->cycle, pptr->nextpipe); } } } @@ -2549,7 +2506,6 @@ static int __init snd_dbri_create(struct dbri->card = card; dbri->sdev = sdev; dbri->irq = irq->pri; - dbri->dbri_version = sdev->prom_name[9]; dbri->dma = sbus_alloc_consistent(sdev, sizeof(struct dbri_dma), &dbri->dma_dvma); @@ -2669,7 +2625,7 @@ static int __init dbri_attach(int prom_n printk(KERN_INFO "audio%d at %p (irq %d) is DBRI(%c)+CS4215(%d)\n", dev, dbri->regs, - dbri->irq, dbri->dbri_version, dbri->mm.version); + dbri->irq, sdev->prom_name[9], dbri->mm.version); dev++; return 0; diff --git a/sound/synth/emux/emux_proc.c b/sound/synth/emux/emux_proc.c index 58b9601..59144ec 100644 --- a/sound/synth/emux/emux_proc.c +++ b/sound/synth/emux/emux_proc.c @@ -128,10 +128,8 @@ void snd_emux_proc_init(struct snd_emux void snd_emux_proc_free(struct snd_emux *emu) { - if (emu->proc) { - snd_info_unregister(emu->proc); - emu->proc = NULL; - } + snd_info_free_entry(emu->proc); + emu->proc = NULL; } #endif /* CONFIG_PROC_FS */ diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index 1b7f499..087f9b6 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -2049,7 +2049,7 @@ static struct usb_driver usb_audio_drive }; -#if defined(CONFIG_PROCFS) && defined(CONFIG_SND_VERBOSE_PROCFS) +#if defined(CONFIG_PROC_FS) && defined(CONFIG_SND_VERBOSE_PROCFS) /* * proc interface for list the supported pcm formats @@ -3499,7 +3499,7 @@ static void snd_usb_audio_disconnect(str } usb_chip[chip->index] = NULL; mutex_unlock(®ister_mutex); - snd_card_free(card); + snd_card_free_when_closed(card); } else { mutex_unlock(®ister_mutex); } diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c index 491e975..e516d6a 100644 --- a/sound/usb/usbmixer.c +++ b/sound/usb/usbmixer.c @@ -37,6 +37,7 @@ #include #include #include #include +#include #include "usbaudio.h" @@ -416,6 +417,26 @@ static inline int set_cur_mix_value(stru return set_ctl_value(cval, SET_CUR, (cval->control << 8) | channel, value); } +/* + * TLV callback for mixer volume controls + */ +static int mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *_tlv) +{ + struct usb_mixer_elem_info *cval = kcontrol->private_data; + DECLARE_TLV_DB_SCALE(scale, 0, 0, 0); + + if (size < sizeof(scale)) + return -ENOMEM; + /* USB descriptions contain the dB scale in 1/256 dB unit + * while ALSA TLV contains in 1/100 dB unit + */ + scale[2] = (convert_signed_value(cval, cval->min) * 100) / 256; + scale[3] = (convert_signed_value(cval, cval->res) * 100) / 256; + if (copy_to_user(_tlv, scale, sizeof(scale))) + return -EFAULT; + return 0; +} /* * parser routines begin here... @@ -933,6 +954,12 @@ static void build_feature_ctl(struct mix } strlcat(kctl->id.name + len, control == USB_FEATURE_MUTE ? " Switch" : " Volume", sizeof(kctl->id.name)); + if (control == USB_FEATURE_VOLUME) { + kctl->tlv.c = mixer_vol_tlv; + kctl->vd[0].access |= + SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; + } break; default: diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h index 9351846..a7e9563 100644 --- a/sound/usb/usbquirks.h +++ b/sound/usb/usbquirks.h @@ -123,6 +123,10 @@ YAMAHA_DEVICE(0x103e, NULL), YAMAHA_DEVICE(0x103f, NULL), YAMAHA_DEVICE(0x1040, NULL), YAMAHA_DEVICE(0x1041, NULL), +YAMAHA_DEVICE(0x1042, NULL), +YAMAHA_DEVICE(0x1043, NULL), +YAMAHA_DEVICE(0x1044, NULL), +YAMAHA_DEVICE(0x1045, NULL), YAMAHA_DEVICE(0x2000, "DGP-7"), YAMAHA_DEVICE(0x2001, "DGP-5"), YAMAHA_DEVICE(0x2002, NULL), @@ -141,6 +145,7 @@ YAMAHA_DEVICE(0x500b, "DME64N"), YAMAHA_DEVICE(0x500c, "DME24N"), YAMAHA_DEVICE(0x500d, NULL), YAMAHA_DEVICE(0x500e, NULL), +YAMAHA_DEVICE(0x500f, NULL), YAMAHA_DEVICE(0x7000, "DTX"), YAMAHA_DEVICE(0x7010, "UB99"), #undef YAMAHA_DEVICE