GIT 076fd0f5054fdef42528718a3ebd259c9413a063 git+ssh://master.kernel.org/pub/scm/linux/kernel/git/perex/alsa-current.git commit 076fd0f5054fdef42528718a3ebd259c9413a063 Author: Takashi Iwai Date: Wed Sep 6 14:27:46 2006 +0200 [ALSA] Add pcm_class attribute to PCM sysfs entry This patch adds a new attribute, pcm_class, to each PCM sysfs entry. It's useful to detect what kind of PCM stream is, for example, HAL can check whether it's a modem or not. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 596e46adf9fa9b77cb32c378473f607db7c099a8 Author: Takashi Iwai Date: Wed Sep 6 14:03:14 2006 +0200 [ALSA] hda-codec - Add independent headphone volume control This patch addes the support of the independent 'Headphone' volume control to the generic codec parser. Some codecs (e.g. Conexant) have separate connections to the headphone and the independent amp adjustment is needed. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 99b0c38303b56a963714b08b9ca17c3ab1ca90ea Author: Richard Fish Date: Wed Sep 6 13:58:25 2006 +0200 [ALSA] [snd-intel-hda] enable center/LFE speaker on some laptops This patch adds LFE mixer controls for laptops with a stac9200 and a mono speaker pin with amplifier. Signed-off-by: Richard Fish Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 614e11ff809c7816bdf196bfd2220e58c9874bbb Author: Takashi Iwai Date: Wed Sep 6 13:35:27 2006 +0200 [ALSA] Add dB information to es1938 driver Added the dB information to ESS Solo (es1938) driver. The new compound dB range TLVs are used for non-linear native volume controls. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 6f99f65fd4ded8c7504168981ae9fde6ec155d87 Author: Takashi Iwai Date: Wed Sep 6 12:15:34 2006 +0200 [ALSA] Add definition of TLV dB range compound Added the definition of TLV dB range compound. It contains one or more dB-range or linear-volume TLV entries with min/max ranges. Used for volume controls with non-linear curves. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 30f3112261231c709b3fe2ae7e991610d16ebf36 Author: Takashi Iwai Date: Wed Sep 6 12:13:37 2006 +0200 [ALSA] hdsp - Fix auto-updating of firmware Fixed the auto-updating of firmware if the breakout box was switched off/on. The firmware binary itself was already cached but it wasn't loaded properly. Also, request_firmware() is issued if the box was with firmware at module loading time but later it's erased. The auto-update is triggered at each PCM action (open, prepare, etc) and at opening proc files. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 687c22c8a7ea32f6b50b0e7bd82de9ff31e736a9 Author: Krzysztof Helt Date: Tue Sep 5 20:25:05 2006 +0200 [ALSA] sparc dbri: SMP fixes The dbri driver hangs when used in kernel compiled with SMP support due to inproper locking. The patch fixes it. Signed-off-by: Krzysztof Helt Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 9442d3a9642134fe477530c7422a221a4dba7a40 Author: Ville Syrjala Date: Mon Sep 4 12:28:51 2006 +0200 [ALSA] es1968: Fix hw volume Fix maestro2 hardware volume control. Tested on a Dell Inspiron 7000. Signed-off-by: Ville Syrjala Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 9ce5ec1f0920651943ce0cdcfec4115900c4ce77 Author: Ville Syrjala Date: Mon Sep 4 12:28:24 2006 +0200 [ALSA] ac97: Fix AD1819 volume range AD1819 volume registers can hold extra bits which do not affect the actual volume. Add a res_table to the codec patch to fix the problem. PCM, line and mic volume were tested. Signed-off-by: Ville Syrjala Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 77107ff13d0960f80017ba2e76f37a0d69b05eb5 Author: Tobin Davis Date: Fri Sep 1 21:03:12 2006 +0200 [ALSA] hda-codec - Add 5 stack audio support for Intel 965 systems This patch renames the 965_2112 function ids to 965_3ST, and adds functional support for 965_5ST (5 stack 7.1 surround). Signed-off-by: Tobin Davis Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit a2306e3864d1b401bd40ee29d7098beb7ec0da0d Author: Takashi Iwai Date: Fri Sep 1 17:09:44 2006 +0200 [ALSA] Add dB scale information to mixart driver Added the dB scale information to mixart driver. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 7661d1d5067be92303ce8b0335e4ba63711076ae Author: Andreas Schwab Date: Fri Sep 1 17:15:36 2006 +0200 [ALSA] [PPC,SOUND] Fix audio gpio state detection When booting with line out or headphone plugged, you won't hear anything. The problem is that after reset all channels are muted, but the actual value of the gpio port doesn't exactly match the active_val settings as expected by check_audio_gpio. For example, the line_mute port is set to 7, but check_audio_gpio would expect 0xd or 0xf, thus its return value indicates that it is not active, even though it is. AFAICS only looking at the low bit is enough to determine whether the port is active. Signed-off-by: Andreas Schwab Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 387795868944471500c670df53be626b783c6336 Author: Pavel Machek Date: Thu Aug 31 17:03:43 2006 +0200 [ALSA] sound/pci/hda/intel_hda: small cleanups Cleanup whitespace. Signed-off-by: Pavel Machek Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 346c18a9bc6fbdafca5bb045f349b9d254987070 Author: Takashi Iwai Date: Thu Aug 31 16:55:40 2006 +0200 [ALSA] hda-intel - Remove volatile Removed volatile from the position buffer pointer. Also, use synchronize_irq() instead of unreliable msleep(1) in the driver remove callback. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 192e76c50a0feff400383e4a6703244fe7fcada8 Author: Takashi Iwai Date: Wed Aug 30 16:57:37 2006 +0200 [ALSA] Add dB scale information to ice1724 driver Added the dB scale information to each board support code of ice1724 driver. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 574f3c86bb13b15eeeaf96c7e285b1d73414f9e5 Author: Takashi Iwai Date: Wed Aug 30 16:56:30 2006 +0200 [ALSA] Add dB scale information to ice1712 driver Added the dB scale information for native digital volumes of ice1712 driver. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 48a346403699ca2d8c5866ab9b2ab2d3cf76136d Author: Takashi Iwai Date: Wed Aug 30 16:49:54 2006 +0200 [ALSA] Clean up and add TLV support to AK4xxx i2c driver - Clean up the code in AK4xxx-ADDA i2c code. - Fix capture gain controls for AK5365 - Changed the static table for DAC/ADC mixer labels to use structs - Implemented TLV entries for each AK codec The volumes in AK4524, AK4528 and AK5365 are corrected with a table to be suitable for dB conversion. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 4512b43fdee9fd648ecdd6ca4d682f641cc1d167 Author: Takashi Iwai Date: Tue Aug 29 18:15:15 2006 +0200 [ALSA] Add missing dB scale information to vxpocket driver Added the missing dB scale information for Mic volume to vxpocket driver. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit ffac1fbc740bfe6d4fd79754f6e1a74c6faa2006 Author: Luke Ross Date: Tue Aug 29 10:46:32 2006 +0200 [ALSA] Support for non-standard rates in USB audio driver There's at least one USB audio chipset out there which supports only one non-standard rate (ID 0e6a:0310 supports 46875Hz). There's a few other patches for this card which are unsatisfactory because they attempt to map this rate to 44.1k leading to sound distortion. The patch below uses SNDRV_PCM_RATE_KNOT to properly support the non-standard rates where they are available. Signed-off-by: Luke Ross Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit bdb70d8949bc8b2c923299127b0e576981285600 Author: Andrey Liakhovets Date: Mon Aug 28 16:52:41 2006 +0200 [ALSA] ac97 - Fix VIA EPIA sound problem Fix the bad sound quality on VIA EPIA system using VIA VT1617A (ALSA bug#2381). Signed-off-by: Andrey Liakhovets Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit c6d37f3d597082b05517f1f074710459cfabbc66 Author: Takashi Iwai Date: Mon Aug 28 13:29:42 2006 +0200 [ALSA] ymfpci - Add TLV entries for native volume controls Added the linear volume TLV entries for YMFPCI native volume controls. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 359ea2a91bf981d76aacf1de2bdf80632b4b6e2f Author: Takashi Iwai Date: Mon Aug 28 13:20:13 2006 +0200 [ALSA] Add the definition of linear volume TLV Added the definition of linear volume TLV type. Some DSP chips and codecs (e.g. AK codec) use linear volume control. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 920ed905c75ba0e6d5d46c3311667945fc65a562 Author: Krzysztof Helt Date: Mon Aug 28 13:01:31 2006 +0200 [ALSA] sparc dbri: OSS layer fix This patch removes setting of incorrect stop_threshold value inside the driver. After the change, playback through the OSS layer works correctly. Signed-off-by: Krzysztof Helt Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 237b797a93e632d83c9122941457d9cf6e954658 Author: Krzysztof Helt Date: Mon Aug 28 13:00:45 2006 +0200 [ALSA] dbri sparc: fixes TS leak This patch fixes time slot leak in the dbri driver. Signed-off-by: Krzysztof Helt Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 48f63dadb0fa93eff8ec1a072e2ebbf38c8f0b2b Author: Krzysztof Helt Date: Mon Aug 28 12:59:23 2006 +0200 [ALSA] sparc dbri: recording is back This patch fixes sound recording after the driver convertion to ring buffered version. It also contains small clean ups to the driver. Signed-off-by: Krzysztof Helt Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 942930fc7d1c3e77bd78a8a5184d371a513313cd Author: Takashi Iwai Date: Fri Aug 25 13:11:26 2006 +0200 [ALSA] hda-codec - Use model=ref for some Dell laptops Force to choose model=ref for some Dell laptops with STAC9200 codec chip for fixing the silent mic recording problem (possibly due to a BIOS bug). Reference: ALSA bug#2038 So far, applied to Inspiron 630m, Latitude D620 and 120L. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit f1b4203225ba0e4e10096f85f1c8135656257790 Author: Takashi Iwai Date: Fri Aug 25 11:39:34 2006 +0200 [ALSA] Return error if no user TLV is defined Retrun error to user TLV_READ ioctl if no TLV is defined. (Until now, nothing was written and rerunred successfully.) Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 613dc83156ef3b72338b402b679b9e1494375bbb Author: Takashi Iwai Date: Thu Aug 24 12:36:36 2006 +0200 [ALSA] Fix errors with user TLV_WRITE Fixed the errors at checking info.access field during user TLV_WRITE call. It should have been zero-initialized. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 58ab5ed9534ba3a0f870b3f5c070cb11f370f545 Author: Takashi Iwai Date: Wed Aug 23 19:53:28 2006 +0200 [ALSA] Add dB scale information to vxpocket and vx222 drivers Added the dB scale information to vxpocket and vx222 drivers. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit e4d41f22c91481b5498459552ca0a0cb75ef5247 Author: Takashi Iwai Date: Wed Aug 23 19:53:02 2006 +0200 [ALSA] Add dB scale information to pcxhr driver Added the dB scale information to pcxhr driver. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 15edcf85744f486a55d77ebfa46bdea58d8124cc Author: Jochen Voss Date: Wed Aug 23 18:35:35 2006 +0200 [ALSA] Fix volume control for the AK4358 DAC Fix volume control for the AK4358 DAC. The attenuation control registers of the AK4358 use only 7bit for the volume, the msb is used to enable attenuation output. Without this patch there are 256 volume levels the lower 128 of which are mute. Signed-off-by: Jochen Voss Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit d0a3023782a97c0a6a26ab6a001b593a0b719614 Author: Takashi Iwai Date: Wed Aug 23 18:34:06 2006 +0200 [ALSA] hda-codec - Fix mic capture with generic parser Fixed the mic capture with generic parser of hda-codec driver - Use VREF80 for mic pins if available - Handle multiple inputs correctly on audio-input widget node. Confirmed on a conexant codec chip. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 7c8af58b5c2cb35f3baf49eb32f2d6766e802023 Author: Richard Fish Date: Wed Aug 23 18:31:34 2006 +0200 [ALSA] hda-codec - restore HDA sigmatel pin configs on resume This patch restores the Intel HDA Sigmatel codec pin configuration on resume. Most of it is dedicated to saving the BIOS pin configuration if necessary, so that even unrecognized chips can be resumed correctly. Signed-off-by: Richard Fish Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 1d57219cdaa1125a9fa464ab2a45cfc89a6c628f Author: Guillaume Munch Date: Tue Aug 22 17:15:47 2006 +0200 [ALSA] hda-codec - Support for SigmaTel 9872 - AR11M and AR11S uses the same chip hence we claim to support the AR Series. - Added commentary about STAC9225s which shares the same id as CXD9872RD. - Added entry for 7662 but won't work automatically until pci_subdevice is known. - 'vaio' model now corresponds to CXD9872RD_VAIO for backward compat. - Replaced STAC766x_VAIO with CXD9872RD_VAIO, STAC9872AK_VAIO, STAC9872K_VAIO and CXD9872AKD_VAIO - Added 'vaio-ar' model for potential future modifications. Signed-off-by: Guillaume Munch Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 0ba7925bb8edcd537db2bcc8f5e8b1244e477a6a Author: Magnus Sandin Date: Tue Aug 22 13:33:12 2006 +0200 [ALSA] ac97 - Enable S/PDIF on ASUS P5P800-VM mobo The attached patch will force building the S/PDIF controls on the PCU SSID for Asus P5P800-VM motherboard, even if the AC97_EI_SPDIF bit is not set. Signed-off-by: Magnus Sandin Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit be7f3fbfb0d93594460fb5c153b5cfd1addb4fe2 Author: Vladimir Avdonin Date: Tue Aug 22 13:31:58 2006 +0200 [ALSA] hda-codec - Fix for Acer laptops with ALC883 codec Patch enables the internal speaker on acer laptops with ALC883. Signed-off-by: Vladimir Avdonin Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 93068f4da115cb5d6093a2f2706f160c11a0ae9c Author: Takashi Iwai Date: Tue Aug 22 13:16:39 2006 +0200 [ALSA] Add dB scale information to opl3sa2 driver Added the dB scale information to opl3sa2 driver. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 4654cc889ffc84a060227ec6875e44c865ee79e5 Author: Takashi Iwai Date: Tue Aug 22 13:16:25 2006 +0200 [ALSA] Add dB scale information to ad1848 driver Added the dB scale information to ad1848 driver. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 9e1dfb4a7713d1ae2359e09d79ce8e16d367f4e5 Author: Takashi Iwai Date: Tue Aug 22 13:16:01 2006 +0200 [ALSA] Add dB scale information to ad1816a driver Added the dB scale information to ad1816a driver. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit b1477c2d643aa8227552672fb74d6bdbf7ca8e91 Author: Takashi Iwai Date: Wed Aug 23 13:07:19 2006 +0200 [ALSA] Add dB scale information to dummy driver Added the dB scale information to dummy driver. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 41e44f12fa04117e0a78869d49a06b3218832340 Author: Takashi Iwai Date: Wed Aug 23 13:01:37 2006 +0200 [ALSA] Add dB scale information to trident driver Added the dB scale information to trident driver. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 4f600126be4aac969ec90be6cdded5d4e150d289 Author: Takashi Iwai Date: Wed Aug 23 12:32:06 2006 +0200 [ALSA] Add dB scale information to fm801 driver Added the dB scale information to fm801 driver. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 32890c05d491320c628572de660489df7025ce62 Author: Takashi Iwai Date: Wed Aug 23 12:14:25 2006 +0200 [ALSA] Add dB scale information to cs4281 driver Added the dB scale information to cs4281 driver. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 6bd04e7ab65eeae356f2e3dd983891af6e5b6b89 Author: Takashi Iwai Date: Wed Aug 23 12:04:34 2006 +0200 [ALSA] Add dB scale information to ak4531 codec Added the dB scale information to ak4531 codec driver. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 8fc4e8805ba7532961fd16c1fec9b902c9d7b7fe Author: Takashi Iwai Date: Mon Aug 21 18:44:54 2006 +0200 [ALSA] Added TLV support to VIA82xx driver Added the TLV support to VIA82xx driver for addition of dB range information. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 8120df8bafcb7dd3a5cd992056c6a608a5485d09 Author: Takashi Iwai Date: Mon Aug 21 18:44:31 2006 +0200 [ALSA] Add TLV support to AC97 codec driver Added the TLV support to AC97 codec driver for addition of dB range information. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 69965f336035ff9180032337fb4c5458a5e7d343 Author: Stephen Hemminger Date: Mon Aug 21 19:17:46 2006 +0200 [ALSA] intel_hda: MSI support Simple patch to enable Message Signalled Interrupts for the HDA Intel audio controller. Tested with: Intel Corporation 82801FB/FBM/FR/FW/FRW (ICH6 Family) High Definition Audio Controller (rev 03) MSI is better because it means audio doesn't end up sharing IRQ with USB. Signed-off-by: Stephen Hemminger Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 4890f408efe149cfb1bf60c89138c770d8d81c9e Author: Adrian Bunk Date: Mon Aug 21 19:22:45 2006 +0200 [ALSA] make sound/pci/emu10k1/emu10k1.c:snd_emu10k1_resume() static This patch makes the needlessly global snd_emu10k1_resume() static. Signed-off-by: Adrian Bunk Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit e78425755d4417deaf71fe9036ec213a1603637b Author: Krzysztof Helt Date: Wed Aug 23 11:37:36 2006 +0200 [ALSA] sparc dbri: hardware constrains added This patch adds ALSA hardware constrains so stereo is possible only with 16-bit format. It contains small cleanups to ring buffered code as well. Signed-off-by: Krzysztof Helt Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 3b751bdb667b066d92b73cb874b067974e2c404f Author: Krzysztof Helt Date: Mon Aug 21 19:30:57 2006 +0200 [ALSA] sparc dbri: ring buffered version It is a complete rework of low level layer to work on ring buffers for comands and data descriptors. This removes annoying noise due to delay in data buffer switching. Signed-off-by: Krzysztof Helt Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 8d68113319d7f0ecf95e77bdacc2ae760d09db64 Author: Krzysztof Helt Date: Mon Aug 21 19:29:59 2006 +0200 [ALSA] sparc dbri: simplifed linking time slot function A simplified routines to link and unlink time slots. Signed-off-by: Krzysztof Helt Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 5e46fbcaa3a7744607b57d9b7875dfc7d2277c1a Author: Krzysztof Helt Date: Mon Aug 21 19:29:18 2006 +0200 [ALSA] sparc dbri: fixed setting of burst size after reset A proper way to set DBRI's burst size. The size must be set after each chip reset. Signed-off-by: Krzysztof Helt Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit e5c25c6f959f75b346cc7356e3f839ac8589a5a0 Author: Krzysztof Helt Date: Mon Aug 21 19:28:16 2006 +0200 [ALSA] sparc dbri: more driver cleanup A general clean up and redudant code removal. Signed-off-by: Krzysztof Helt Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit e619c62ce067226d988d05a97a5c4e07bb9d4f64 Author: Krzysztof Helt Date: Mon Aug 21 19:27:35 2006 +0200 [ALSA] sparc dbri: removal of dri_desc struct The structure is in big part redudant. Signed-off-by: Krzysztof Helt Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 76dc4c0cda17dafc627e53cafcc767de93e419ba Author: Takashi Iwai Date: Mon Aug 21 17:57:44 2006 +0200 [ALSA] hda-intel - Switch to polling mode for CORB/RIRB communication Automatically switch to polling mode for CORB/RIRB communication if the irq-driven mode seems not working well. If the polling mode still doesn't work, switch to single_cmd mode as fallback. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit 9e27a8d8ea6f8c8ff96282f4c1f5a4a3bb9232c0 Author: Tobin Davis Date: Tue Aug 22 19:44:45 2006 +0200 [ALSA] hda-codec - Add support for new Intel boards with Stac9227 codec This patch adds full 5.1 audio support for Intel boards with the SigmaTel 9227 codec chip (946, 963, 965 series). Signed-off-by: Tobin Davis Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit f9430b7a0233b0604f52c6ae366d18476c73c906 Author: Tobin Davis Date: Tue Aug 22 19:43:46 2006 +0200 [ALSA] hda-codec - add missing device ids for Intel 945 boards This patch adds missing device ids for Intel 945 motherboards. Signed-off-by: Tobin Davis Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit bbf3f9e7e4d2c2772691e922cc3b02e4c0ac6c54 Author: Takashi Iwai Date: Tue Aug 22 16:35:19 2006 +0200 [ALSA] Fix missing selection of CONFIG_VIDEO_DEV from SND_FM801_TEA575X Fixed the missing selection of CONFIG_VIDEO_DEV from SND_FM801_TEA575X. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela commit c9e115143612aebbf88b77f90d3b91ac55b7e228 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 b193f01e8fc89532bc2a86ae7b99d09b868214f6 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 ba9759d8439b5e00fa12c1107edf06a6f07e5b04 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 701828a235db2b68fff9533e8cb519357d2db34e 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 abb976c9ab029edcdf2637f11d7dd068f13b1c28 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 6893340ad7a2fc38b37c2b387ee5083afb6bd0a2 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 b65e8b0bb990c25ff21a291d7040b7ba9b79e2e2 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 cd8b7fabb6ca90f23e1a3b8e5d9b9defcd28e967 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 1ddf8050e65582465b72ceacf6b06f6532733243 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 b2c65348412c76d90dda2e632836123f7b79ed8c 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 c4e99a02ed291d60270337e4640aa35d3284fac5 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 f7b0bca5bd4a11e2e6cf9aa375710435d1a76865 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 d95f0c8f0689203c837d8aa708584e3a5814a026 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 11cd2fa48e41a607c1a2989f916ae67799d26c57 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 7b58a1dd142e5808c86536ac01e1c4dc0715b57c 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 b15cba8572aa9e6a9c8d67647d0c5f17f7cf2e4d 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 530cdd33f26e6934b3bbefb337235126963158fc 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 d1199cc7d960c67cfe927f91de6a5f51a666b7dd 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 fefae7bdbefc42eb50b6ef6f37df40bb4f1036cd 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 36268a476bbf8e3b59682298fbfd7fc73dc9624b 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 849525710f397e89755e1f188702756ed9e6ef41 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 6e94df1a4161cb1f7115767e996329222a4f000f 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 ddd7400eb46b354394f45ae40605eed883559675 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 09a384547be82b6c05dca8858312145a3e6035be 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 ecdcae8c2e8fc65bdd5d943edc4a97a8a69a625a 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 6d456be8848ab13b8acc27614a6a30ab9042afa3 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 1433d9db7f4cf36817ffb6ed726969e17712e3bb 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 9144671c49074d6b4d9a7ff03cb29a4edfe9169a 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 ff7a6b3186edc57a299c4e81653d8ca73ef12a6e 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 f3d69ecdae199ee98ff17e1d6c6999484888029b 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 41946c104af3308ac68056a39924d790448b74bf 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 b5dbb7d1f7356743abfe9851c02d5b3215a52fb8 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 b0a13cb38ec544fab53d89c32fb6f60df2435738 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 0addd0f2f540b6211382d5d3a6961dcbca9f2b30 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 775c2487cb3232613cca509d85a07aca1cd573aa 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 15b0b6076e0d17a3a8350a0371a4c5bbcdaac052 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 d8ab474ab568c63e8ab0a5cda1ef5971a62ae29e 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 5d5cbeaa6141268cd018e91bd45137deba2369d6 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 1eacd57fc97fef19af15b4c0f98f164bd9d677b5 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 17cea0d81ba0c44e183088c5f71a76837ca88ae0 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 13238db09bcce4dc3c33847cf988452fd7bb2b36 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 be10d54fcb8c7b44b67e184524b77d394d785a50 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 3e12ee5180c3942d9a30ee8ea27fd61c7655a843 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 7ea25a22d8618537ea90f0333f9cd699fcee98ea 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 f87e1d3c61ef02b7bb2b5e3fa0211ab182e1bd49 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 08483a51da20ed75743758a9ec8cffa539c7958a 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 fddecbcc5dcdb8ec43a9384e1c112c0744e05d37 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 a9ef189fd5f9d61267674e8804c62ccce9b8d0f9 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 8ac1ee0d7d3c6c87d7198fbc5cb67b80752c29a3 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 bbccf8fbb755f30dd4063f88c8009e65141bd0bd 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 60244519a88eaa0fd5816cd149fd693077de76c4 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 bb987cd0a79f59f3d235183e724c9ba7f4fced74 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 38deafdf126aad9082b9ed0c6b82a9638b778524 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 bd623d7acb0208935357d7968a89e9ff16ac73ff 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 88428a519d0a3563cd06dad2a3767f831ceed810 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 4e10d91efb2f51eb70daddd08206f30d109476af 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 46f7214bb28826930060132b00af1f51c7edb93f 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 82634fd5a641a0e4acc93ffe4f3ce5632736dbcf 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 2ba15b98b68d2b8bf99858ab39557a80d9b608d8 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 38ed8c03f4e1e3d3f43be9a91894045ea9fe9537 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 5c7084ae192209b45752d8645b4e73b8b5b1fed4 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 7cc3e373d2360b400d8f9d3243d1e88aacaa39b5 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 9b6294aad5fa9daf127180f3c0344945a92945cf 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 7e7079a839f9f904c93ed2114e4be91164412370 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 2601bca66346e98801c38efb440bb59a7c78e084 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 Documentation/sound/alsa/ALSA-Configuration.txt | 41 + .../sound/alsa/DocBook/writing-an-alsa-driver.tmpl | 5 include/sound/ac97_codec.h | 32 + include/sound/ad1848.h | 22 include/sound/ak4xxx-adda.h | 43 + include/sound/asound.h | 19 include/sound/control.h | 13 include/sound/core.h | 10 include/sound/emu10k1.h | 4 include/sound/info.h | 7 include/sound/pcm.h | 4 include/sound/timer.h | 1 include/sound/tlv.h | 60 + include/sound/version.h | 2 include/sound/vx_core.h | 1 sound/aoa/codecs/Kconfig | 4 sound/core/control.c | 160 ++- sound/core/device.c | 20 sound/core/hwdep.c | 12 sound/core/info.c | 102 +- sound/core/info_oss.c | 6 sound/core/init.c | 116 +- sound/core/oss/mixer_oss.c | 22 sound/core/oss/pcm_oss.c | 24 sound/core/pcm.c | 128 +- sound/core/pcm_compat.c | 2 sound/core/pcm_memory.c | 2 sound/core/pcm_native.c | 49 - sound/core/rawmidi.c | 38 - sound/core/rtctimer.c | 2 sound/core/seq/oss/seq_oss.c | 3 sound/core/seq/seq_device.c | 13 sound/core/seq/seq_info.c | 6 sound/core/sound.c | 59 + sound/core/sound_oss.c | 3 sound/core/timer.c | 62 - sound/drivers/Kconfig | 26 sound/drivers/Makefile | 2 sound/drivers/dummy.c | 20 sound/drivers/mpu401/mpu401.c | 2 sound/drivers/mts64.c | 1091 ++++++++++++++++++++ sound/drivers/opl4/opl4_proc.c | 3 sound/drivers/vx/vx_mixer.c | 17 sound/i2c/other/ak4xxx-adda.c | 535 +++++++--- sound/isa/ad1816a/ad1816a_lib.c | 55 + sound/isa/ad1848/ad1848_lib.c | 49 + sound/isa/es18xx.c | 219 +++- sound/isa/opl3sa2.c | 26 sound/pci/Kconfig | 1 sound/pci/ac97/ac97_codec.c | 334 +++++- sound/pci/ac97/ac97_patch.c | 96 ++ sound/pci/ac97/ac97_patch.h | 1 sound/pci/ac97/ac97_pcm.c | 18 sound/pci/ac97/ac97_proc.c | 18 sound/pci/ac97/ak4531_codec.c | 49 + sound/pci/ca0106/ca0106_mixer.c | 10 sound/pci/cs4281.c | 5 sound/pci/cs46xx/dsp_spos.c | 52 - sound/pci/cs46xx/dsp_spos_scb_lib.c | 2 sound/pci/cs5535audio/Makefile | 2 sound/pci/emu10k1/emu10k1.c | 2 sound/pci/emu10k1/emu10k1_main.c | 1 sound/pci/emu10k1/emu10k1x.c | 7 sound/pci/emu10k1/emufx.c | 12 sound/pci/emu10k1/p16v.c | 5 sound/pci/es1938.c | 103 ++ sound/pci/es1968.c | 40 - sound/pci/fm801.c | 63 + sound/pci/hda/hda_codec.c | 28 - sound/pci/hda/hda_codec.h | 2 sound/pci/hda/hda_generic.c | 199 +++- sound/pci/hda/hda_intel.c | 87 +- sound/pci/hda/hda_local.h | 5 sound/pci/hda/hda_proc.c | 12 sound/pci/hda/patch_analog.c | 17 sound/pci/hda/patch_realtek.c | 273 +++++ sound/pci/hda/patch_sigmatel.c | 659 ++++++++++-- sound/pci/ice1712/aureon.c | 104 ++ sound/pci/ice1712/ice1712.c | 14 sound/pci/ice1712/phase.c | 39 + sound/pci/ice1712/pontis.c | 9 sound/pci/ice1712/prodigy192.c | 14 sound/pci/ice1712/revo.c | 69 + sound/pci/ice1712/revo.h | 2 sound/pci/intel8x0.c | 14 sound/pci/mixart/mixart_mixer.c | 14 sound/pci/pcxhr/pcxhr_mixer.c | 16 sound/pci/riptide/riptide.c | 10 sound/pci/rme9652/hdsp.c | 48 + sound/pci/trident/trident_main.c | 10 sound/pci/via82xx.c | 23 sound/pci/vx222/vx222.c | 7 sound/pci/vx222/vx222_ops.c | 9 sound/pci/ymfpci/ymfpci_main.c | 7 sound/pcmcia/pdaudiocf/pdaudiocf.c | 2 sound/pcmcia/vx/vxp_mixer.c | 6 sound/pcmcia/vx/vxpocket.c | 9 sound/ppc/beep.c | 32 - sound/ppc/tumbler.c | 5 sound/sparc/dbri.c | 817 +++++++-------- sound/synth/emux/emux_proc.c | 6 sound/usb/usbaudio.c | 50 + sound/usb/usbmixer.c | 27 sound/usb/usbquirks.h | 5 104 files changed, 5000 insertions(+), 1613 deletions(-) diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index f61af23..a788dd7 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,32 @@ 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 + acer Acer laptops (Travelmate 3012WTMi, Aspire 5600, etc) 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 +857,21 @@ 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) + + STAC9200/9205/9220/9221/9254 + ref Reference board + 3stack D945 3stack + 5stack D945 5stack + SPDIF - STAC7661(?) + STAC9227/9228/9229/927x + ref Reference board + 3stack D965 3stack + 5stack D965 5stack + SPDIF + + STAC9872 vaio Setup for VAIO FE550G/SZ110 + vaio-ar Setup for VAIO AR If the default configuration doesn't work and one of the above matches with your device, report it together with the PCI @@ -1213,6 +1238,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/ad1848.h b/include/sound/ad1848.h index 57af1fe..c8de6f8 100644 --- a/include/sound/ad1848.h +++ b/include/sound/ad1848.h @@ -179,14 +179,13 @@ #define AD1848_MIXVAL_SINGLE(reg, shift, #define AD1848_MIXVAL_DOUBLE(left_reg, right_reg, shift_left, shift_right, mask, invert) \ ((left_reg) | ((right_reg) << 8) | ((shift_left) << 16) | ((shift_right) << 19) | ((mask) << 24) | ((invert) << 22)) -int snd_ad1848_add_ctl(struct snd_ad1848 *chip, const char *name, int index, int type, unsigned long value); - /* for ease of use */ struct ad1848_mix_elem { const char *name; int index; int type; unsigned long private_value; + unsigned int *tlv; }; #define AD1848_SINGLE(xname, xindex, reg, shift, mask, invert) \ @@ -195,15 +194,26 @@ #define AD1848_SINGLE(xname, xindex, reg .type = AD1848_MIX_SINGLE, \ .private_value = AD1848_MIXVAL_SINGLE(reg, shift, mask, invert) } +#define AD1848_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \ +{ .name = xname, \ + .index = xindex, \ + .type = AD1848_MIX_SINGLE, \ + .private_value = AD1848_MIXVAL_SINGLE(reg, shift, mask, invert), \ + .tlv = xtlv } + #define AD1848_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ { .name = xname, \ .index = xindex, \ .type = AD1848_MIX_DOUBLE, \ .private_value = AD1848_MIXVAL_DOUBLE(left_reg, right_reg, shift_left, shift_right, mask, invert) } -static inline int snd_ad1848_add_ctl_elem(struct snd_ad1848 *chip, const struct ad1848_mix_elem *c) -{ - return snd_ad1848_add_ctl(chip, c->name, c->index, c->type, c->private_value); -} +#define AD1848_DOUBLE_TLV(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert, xtlv) \ +{ .name = xname, \ + .index = xindex, \ + .type = AD1848_MIX_DOUBLE, \ + .private_value = AD1848_MIXVAL_DOUBLE(left_reg, right_reg, shift_left, shift_right, mask, invert), \ + .tlv = xtlv } + +int snd_ad1848_add_ctl_elem(struct snd_ad1848 *chip, const struct ad1848_mix_elem *c); #endif /* __SOUND_AD1848_H */ diff --git a/include/sound/ak4xxx-adda.h b/include/sound/ak4xxx-adda.h index 3d98884..026e407 100644 --- a/include/sound/ak4xxx-adda.h +++ b/include/sound/ak4xxx-adda.h @@ -39,26 +39,40 @@ struct snd_ak4xxx_ops { #define AK4XXX_IMAGE_SIZE (AK4XXX_MAX_CHIPS * 16) /* 64 bytes */ +/* DAC label and channels */ +struct snd_akm4xxx_dac_channel { + char *name; /* mixer volume name */ + unsigned int num_channels; +}; + +/* ADC labels and channels */ +struct snd_akm4xxx_adc_channel { + char *name; /* capture gain volume label */ + char *gain_name; /* IPGA */ + char *switch_name; /* capture switch */ + unsigned int num_channels; +}; + struct snd_akm4xxx { struct snd_card *card; unsigned int num_adcs; /* AK4524 or AK4528 ADCs */ unsigned int num_dacs; /* AK4524 or AK4528 DACs */ unsigned char images[AK4XXX_IMAGE_SIZE]; /* saved register image */ - unsigned char ipga_gain[AK4XXX_MAX_CHIPS][2]; /* saved register image - * for IPGA (AK4528) - */ + unsigned char volumes[AK4XXX_IMAGE_SIZE]; /* saved volume values */ unsigned long private_value[AK4XXX_MAX_CHIPS]; /* helper for driver */ void *private_data[AK4XXX_MAX_CHIPS]; /* helper for driver */ /* template should fill the following fields */ 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 - */ - char **channel_names; /* array of mixer channel names */ + + /* (array) information of combined codecs */ + struct snd_akm4xxx_dac_channel *dac_info; + struct snd_akm4xxx_adc_channel *adc_info; + struct snd_ak4xxx_ops ops; }; @@ -72,9 +86,18 @@ #define snd_akm4xxx_get(ak,chip,reg) \ (ak)->images[(chip) * 16 + (reg)] #define snd_akm4xxx_set(ak,chip,reg,val) \ ((ak)->images[(chip) * 16 + (reg)] = (val)) +#define snd_akm4xxx_get_vol(ak,chip,reg) \ + (ak)->volumes[(chip) * 16 + (reg)] +#define snd_akm4xxx_set_vol(ak,chip,reg,val) \ + ((ak)->volumes[(chip) * 16 + (reg)] = (val)) + +/* Warning: IPGA is tricky - we assume the addr + 4 is unused + * so far, it's OK for all AK codecs with IPGA: + * AK4524, AK4528 and EK5365 + */ #define snd_akm4xxx_get_ipga(ak,chip,reg) \ - (ak)->ipga_gain[chip][(reg)-4] + snd_akm4xxx_get_vol(ak, chip, (reg) + 4) #define snd_akm4xxx_set_ipga(ak,chip,reg,val) \ - ((ak)->ipga_gain[chip][(reg)-4] = (val)) + snd_akm4xxx_set_vol(ak, chip, (reg) + 4, val) #endif /* __SOUND_AK4XXX_ADDA_H */ 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..b056ea9 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -25,8 +25,8 @@ #define __SOUND_CORE_H #include /* wake_up() */ #include /* struct mutex */ #include /* struct rw_semaphore */ -#include /* struct workqueue_struct */ #include /* pm_message_t */ +#include /* forward declarations */ #ifdef CONFIG_PCI @@ -71,7 +71,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 +130,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 @@ -188,6 +187,7 @@ struct snd_minor { int device; /* device number */ const struct file_operations *f_ops; /* file operations */ void *private_data; /* private data for f_ops->open */ + struct class_device *class_dev; /* class device for sysfs */ }; /* sound.c */ @@ -202,6 +202,8 @@ int snd_register_device(int type, struct const char *name); int snd_unregister_device(int type, struct snd_card *card, int dev); void *snd_lookup_minor_data(unsigned int minor, int type); +int snd_add_device_sysfs_file(int type, struct snd_card *card, int dev, + const struct class_device_attribute *attr); #ifdef CONFIG_SND_OSSEMUL int snd_register_oss_device(int type, struct snd_card *card, int dev, @@ -244,7 +246,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..d93a96b --- /dev/null +++ b/include/sound/tlv.h @@ -0,0 +1,60 @@ +#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 SNDRV_CTL_TLVT_DB_LINEAR 2 /* linear volume */ +#define SNDRV_CTL_TLVT_DB_RANGE 3 /* dB range container */ + +#define TLV_DB_SCALE_ITEM(min, step, mute) \ + SNDRV_CTL_TLVT_DB_SCALE, 2 * sizeof(unsigned int), \ + (min), ((step) & 0xffff) | ((mute) ? 0x10000 : 0) +#define DECLARE_TLV_DB_SCALE(name, min, step, mute) \ + unsigned int name[] = { TLV_DB_SCALE_ITEM(min, step, mute) } + +/* linear volume between min_dB and max_dB (.01dB unit) */ +#define TLV_DB_LINEAR_ITEM(min_dB, max_dB) \ + SNDRV_CTL_TLVT_DB_LINEAR, 2 * sizeof(unsigned int), \ + (min_dB), (max_dB) +#define DECLARE_TLV_DB_LINEAR(name, min_dB, max_dB) \ + unsigned int name[] = { TLV_DB_LINEAR_ITEM(min_dB, max_dB) } + +/* dB range container */ +/* Each item is: */ +/* The below assumes that each item TLV is 4 words like DB_SCALE or LINEAR */ +#define TLV_DB_RANGE_HEAD(num) \ + SNDRV_CTL_TLVT_DB_RANGE, 6 * (num) * sizeof(unsigned int) + +#define TLV_DB_GAIN_MUTE -9999999 + +#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/include/sound/vx_core.h b/include/sound/vx_core.h index 9821a61..dbca141 100644 --- a/include/sound/vx_core.h +++ b/include/sound/vx_core.h @@ -128,6 +128,7 @@ struct snd_vx_hardware { unsigned int num_ins; unsigned int num_outs; unsigned int output_level_max; + unsigned int *output_level_db_scale; }; /* hwdep id string */ 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..6973a96 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,48 @@ 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 (! ue->tlv_data_size || ! ue->tlv_data) + return -ENXIO; + 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 +985,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 +1012,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); @@ -997,6 +1050,7 @@ static int snd_ctl_elem_add(struct snd_c if (ue == NULL) return -ENOMEM; ue->info = *info; + ue->info.access = 0; ue->elem_data = (char *)ue + sizeof(*ue); ue->elem_data_size = private_size; kctl.private_free = snd_ctl_elem_user_free; @@ -1067,6 +1121,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 +1201,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 +1220,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 +1459,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 +1472,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 +1497,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 +1506,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..bf8f412 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); @@ -918,6 +907,28 @@ void snd_pcm_detach_substream(struct snd substream->pstr->substream_opened--; } +static ssize_t show_pcm_class(struct class_device *class_device, char *buf) +{ + struct snd_pcm *pcm; + const char *str; + static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = { + [SNDRV_PCM_CLASS_GENERIC] = "generic", + [SNDRV_PCM_CLASS_MULTI] = "multi", + [SNDRV_PCM_CLASS_MODEM] = "modem", + [SNDRV_PCM_CLASS_DIGITIZER] = "digitizer", + }; + + if (! (pcm = class_get_devdata(class_device)) || + pcm->dev_class > SNDRV_PCM_CLASS_LAST) + str = "none"; + else + str = strs[pcm->dev_class]; + return snprintf(buf, PAGE_SIZE, "%s\n", str); +} + +static struct class_device_attribute pcm_attrs = + __ATTR(pcm_class, S_IRUGO, show_pcm_class, NULL); + static int snd_pcm_dev_register(struct snd_device *device) { int cidx, err; @@ -956,6 +967,8 @@ static int snd_pcm_dev_register(struct s mutex_unlock(®ister_mutex); return err; } + snd_add_device_sysfs_file(devtype, pcm->card, pcm->device, + &pcm_attrs); for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) snd_pcm_timer_init(substream); } @@ -971,35 +984,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 +1011,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 +1087,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..efa476c 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -268,7 +268,11 @@ #endif snd_minors[minor] = preg; if (card) device = card->dev; - class_device_create(sound_class, NULL, MKDEV(major, minor), device, "%s", name); + preg->class_dev = class_device_create(sound_class, NULL, + MKDEV(major, minor), + device, "%s", name); + if (preg->class_dev) + class_set_devdata(preg->class_dev, private_data); mutex_unlock(&sound_mutex); return 0; @@ -276,6 +280,24 @@ #endif EXPORT_SYMBOL(snd_register_device); +/* find the matching minor record + * return the index of snd_minor, or -1 if not found + */ +static int find_snd_minor(int type, struct snd_card *card, int dev) +{ + int cardnum, minor; + struct snd_minor *mptr; + + cardnum = card ? card->number : -1; + for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) + if ((mptr = snd_minors[minor]) != NULL && + mptr->type == type && + mptr->card == cardnum && + mptr->device == dev) + return minor; + return -1; +} + /** * snd_unregister_device - unregister the device on the given card * @type: the device type, SNDRV_DEVICE_TYPE_XXX @@ -289,32 +311,42 @@ EXPORT_SYMBOL(snd_register_device); */ int snd_unregister_device(int type, struct snd_card *card, int dev) { - int cardnum, minor; - struct snd_minor *mptr; + int minor; - cardnum = card ? card->number : -1; mutex_lock(&sound_mutex); - for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) - if ((mptr = snd_minors[minor]) != NULL && - mptr->type == type && - mptr->card == cardnum && - mptr->device == dev) - break; - if (minor == ARRAY_SIZE(snd_minors)) { + minor = find_snd_minor(type, card, dev); + if (minor < 0) { mutex_unlock(&sound_mutex); return -EINVAL; } class_device_destroy(sound_class, MKDEV(major, minor)); + kfree(snd_minors[minor]); snd_minors[minor] = NULL; mutex_unlock(&sound_mutex); - kfree(mptr); return 0; } EXPORT_SYMBOL(snd_unregister_device); +int snd_add_device_sysfs_file(int type, struct snd_card *card, int dev, + const struct class_device_attribute *attr) +{ + int minor, ret = -EINVAL; + struct class_device *cdev; + + mutex_lock(&sound_mutex); + minor = find_snd_minor(type, card, dev); + if (minor >= 0 && (cdev = snd_minors[minor]->class_dev) != NULL) + ret = class_device_create_file(cdev, attr); + mutex_unlock(&sound_mutex); + return ret; + +} + +EXPORT_SYMBOL(snd_add_device_sysfs_file); + #ifdef CONFIG_PROC_FS /* * INFO PART @@ -387,8 +419,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..42001ef 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -29,6 +29,7 @@ #include #include #include #include +#include #include #include #include @@ -285,7 +286,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, @@ -443,10 +444,13 @@ static int __init snd_card_dummy_pcm(str } #define DUMMY_VOLUME(xname, xindex, addr) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ + .name = xname, .index = xindex, \ .info = snd_dummy_volume_info, \ .get = snd_dummy_volume_get, .put = snd_dummy_volume_put, \ - .private_value = addr } + .private_value = addr, \ + .tlv = { .p = db_scale_dummy } } static int snd_dummy_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -497,6 +501,8 @@ static int snd_dummy_volume_put(struct s return change; } +static DECLARE_TLV_DB_SCALE(db_scale_dummy, -4500, 30, 0); + #define DUMMY_CAPSRC(xname, xindex, addr) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_dummy_capsrc_info, \ @@ -547,13 +553,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/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c index c1d7fcd..1613ed8 100644 --- a/sound/drivers/vx/vx_mixer.c +++ b/sound/drivers/vx/vx_mixer.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "vx_cmd.h" @@ -455,10 +456,13 @@ static int vx_output_level_put(struct sn static struct snd_kcontrol_new vx_control_output_level = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Master Playback Volume", .info = vx_output_level_info, .get = vx_output_level_get, .put = vx_output_level_put, + /* tlv will be filled later */ }; /* @@ -712,12 +716,17 @@ static int vx_monitor_sw_put(struct snd_ return 0; } +static DECLARE_TLV_DB_SCALE(db_scale_audio_gain, -10975, 25, 0); + static struct snd_kcontrol_new vx_control_audio_gain = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), /* name will be filled later */ .info = vx_audio_gain_info, .get = vx_audio_gain_get, - .put = vx_audio_gain_put + .put = vx_audio_gain_put, + .tlv = { .p = db_scale_audio_gain }, }; static struct snd_kcontrol_new vx_control_output_switch = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -729,9 +738,12 @@ static struct snd_kcontrol_new vx_contro static struct snd_kcontrol_new vx_control_monitor_gain = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Monitoring Volume", + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .info = vx_audio_gain_info, /* shared */ .get = vx_audio_monitor_get, - .put = vx_audio_monitor_put + .put = vx_audio_monitor_put, + .tlv = { .p = db_scale_audio_gain }, }; static struct snd_kcontrol_new vx_control_monitor_switch = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -918,6 +930,7 @@ int snd_vx_mixer_new(struct vx_core *chi for (i = 0; i < chip->hw->num_outs; i++) { temp = vx_control_output_level; temp.index = i; + temp.tlv.p = chip->hw->output_level_db_scale; if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) return err; } diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c index dc7cc20..c34cb46 100644 --- a/sound/i2c/other/ak4xxx-adda.c +++ b/sound/i2c/other/ak4xxx-adda.c @@ -28,12 +28,14 @@ #include #include #include #include +#include #include MODULE_AUTHOR("Jaroslav Kysela , Takashi Iwai "); MODULE_DESCRIPTION("Routines for control of AK452x / AK43xx AD/DA converters"); MODULE_LICENSE("GPL"); +/* write the given register and save the data to the cache */ void snd_akm4xxx_write(struct snd_akm4xxx *ak, int chip, unsigned char reg, unsigned char val) { @@ -41,15 +43,10 @@ void snd_akm4xxx_write(struct snd_akm4xx ak->ops.write(ak, chip, reg, val); /* save the data */ - if (ak->type == SND_AK4524 || ak->type == SND_AK4528) { - if ((reg != 0x04 && reg != 0x05) || (val & 0x80) == 0) - snd_akm4xxx_set(ak, chip, reg, val); - else - snd_akm4xxx_set_ipga(ak, chip, reg, val); - } else { - /* AK4529, or else */ + /* don't overwrite with IPGA data */ + if ((ak->type != SND_AK4524 && ak->type != SND_AK5365) || + (reg != 0x04 && reg != 0x05) || (val & 0x80) == 0) snd_akm4xxx_set(ak, chip, reg, val); - } ak->ops.unlock(ak, chip); } @@ -78,7 +75,7 @@ static void ak4524_reset(struct snd_akm4 /* IPGA */ for (reg = 0x04; reg < 0x06; reg++) snd_akm4xxx_write(ak, chip, reg, - snd_akm4xxx_get_ipga(ak, chip, reg)); + snd_akm4xxx_get_ipga(ak, chip, reg) | 0x80); } } @@ -137,11 +134,49 @@ void snd_akm4xxx_reset(struct snd_akm4xx case SND_AK4381: ak4381_reset(ak, state); break; + default: + break; } } EXPORT_SYMBOL(snd_akm4xxx_reset); + +/* + * Volume conversion table for non-linear volumes + * from -63.5dB (mute) to 0dB step 0.5dB + * + * Used for AK4524 input/ouput attenuation, AK4528, and + * AK5365 input attenuation + */ +static unsigned char vol_cvt_datt[128] = { + 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, + 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06, + 0x06, 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x0a, + 0x0a, 0x0b, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x0f, + 0x10, 0x10, 0x11, 0x12, 0x12, 0x13, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x17, 0x18, 0x19, 0x1a, 0x1c, + 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x23, + 0x24, 0x25, 0x26, 0x28, 0x29, 0x2a, 0x2b, 0x2d, + 0x2e, 0x30, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, + 0x37, 0x38, 0x39, 0x3b, 0x3c, 0x3e, 0x3f, 0x40, + 0x41, 0x42, 0x43, 0x44, 0x46, 0x47, 0x48, 0x4a, + 0x4b, 0x4d, 0x4e, 0x50, 0x51, 0x52, 0x53, 0x54, + 0x55, 0x56, 0x58, 0x59, 0x5b, 0x5c, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x64, 0x65, 0x66, 0x67, 0x69, + 0x6a, 0x6c, 0x6d, 0x6f, 0x70, 0x71, 0x72, 0x73, + 0x75, 0x76, 0x77, 0x79, 0x7a, 0x7c, 0x7d, 0x7f, +}; + +/* + * dB tables + */ +static DECLARE_TLV_DB_SCALE(db_scale_vol_datt, -6350, 50, 1); +static DECLARE_TLV_DB_SCALE(db_scale_8bit, -12750, 50, 1); +static DECLARE_TLV_DB_SCALE(db_scale_7bit, -6350, 50, 1); +static DECLARE_TLV_DB_LINEAR(db_scale_linear, TLV_DB_GAIN_MUTE, 0); +static DECLARE_TLV_DB_SCALE(db_scale_ipga, 0, 50, 0); + /* * initialize all the ak4xxx chips */ @@ -238,6 +273,9 @@ void snd_akm4xxx_init(struct snd_akm4xxx int chip, num_chips; unsigned char *ptr, reg, data, *inits; + memset(ak->images, 0, sizeof(ak->images)); + memset(ak->volumes, 0, sizeof(ak->volumes)); + switch (ak->type) { case SND_AK4524: inits = inits_ak4524; @@ -263,6 +301,9 @@ void snd_akm4xxx_init(struct snd_akm4xxx inits = inits_ak4381; num_chips = ak->num_dacs / 2; break; + case SND_AK5365: + /* FIXME: any init sequence? */ + return; default: snd_BUG(); return; @@ -280,14 +321,21 @@ void snd_akm4xxx_init(struct snd_akm4xxx EXPORT_SYMBOL(snd_akm4xxx_init); +/* + * Mixer callbacks + */ +#define AK_VOL_CVT (1<<21) /* need dB conversion */ +#define AK_NEEDSMSB (1<<22) /* need MSB update bit */ +#define AK_INVERT (1<<23) /* data is inverted */ #define AK_GET_CHIP(val) (((val) >> 8) & 0xff) #define AK_GET_ADDR(val) ((val) & 0xff) -#define AK_GET_SHIFT(val) (((val) >> 16) & 0x7f) +#define AK_GET_SHIFT(val) (((val) >> 16) & 0x1f) +#define AK_GET_VOL_CVT(val) (((val) >> 21) & 1) +#define AK_GET_NEEDSMSB(val) (((val) >> 22) & 1) #define AK_GET_INVERT(val) (((val) >> 23) & 1) #define AK_GET_MASK(val) (((val) >> 24) & 0xff) #define AK_COMPOSE(chip,addr,shift,mask) \ (((chip) << 8) | (addr) | ((shift) << 16) | ((mask) << 24)) -#define AK_INVERT (1<<23) static int snd_akm4xxx_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -307,31 +355,37 @@ static int snd_akm4xxx_volume_get(struct 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 invert = AK_GET_INVERT(kcontrol->private_value); - unsigned int mask = AK_GET_MASK(kcontrol->private_value); - unsigned char val = snd_akm4xxx_get(ak, chip, addr); - - ucontrol->value.integer.value[0] = invert ? mask - val : val; + + ucontrol->value.integer.value[0] = snd_akm4xxx_get_vol(ak, chip, addr); return 0; } -static int snd_akm4xxx_volume_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int put_ak_reg(struct snd_kcontrol *kcontrol, int addr, + unsigned char nval) { 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 invert = AK_GET_INVERT(kcontrol->private_value); unsigned int mask = AK_GET_MASK(kcontrol->private_value); - unsigned char nval = ucontrol->value.integer.value[0] % (mask+1); - int change; + int chip = AK_GET_CHIP(kcontrol->private_value); - if (invert) + if (snd_akm4xxx_get_vol(ak, chip, addr) == nval) + return 0; + + snd_akm4xxx_set_vol(ak, chip, addr, nval); + if (AK_GET_VOL_CVT(kcontrol->private_value)) + nval = vol_cvt_datt[nval]; + if (AK_GET_INVERT(kcontrol->private_value)) nval = mask - nval; - change = snd_akm4xxx_get(ak, chip, addr) != nval; - if (change) - snd_akm4xxx_write(ak, chip, addr, nval); - return change; + if (AK_GET_NEEDSMSB(kcontrol->private_value)) + nval |= 0x80; + snd_akm4xxx_write(ak, chip, addr, nval); + return 1; +} + +static int snd_akm4xxx_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return put_ak_reg(kcontrol, AK_GET_ADDR(kcontrol->private_value), + ucontrol->value.integer.value[0]); } static int snd_akm4xxx_stereo_volume_info(struct snd_kcontrol *kcontrol, @@ -352,55 +406,25 @@ static int snd_akm4xxx_stereo_volume_get 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 invert = AK_GET_INVERT(kcontrol->private_value); - unsigned int mask = AK_GET_MASK(kcontrol->private_value); - unsigned char val = snd_akm4xxx_get(ak, chip, addr); - - ucontrol->value.integer.value[0] = invert ? mask - val : val; - - val = snd_akm4xxx_get(ak, chip, addr+1); - ucontrol->value.integer.value[1] = invert ? mask - val : val; + ucontrol->value.integer.value[0] = snd_akm4xxx_get_vol(ak, chip, addr); + ucontrol->value.integer.value[1] = snd_akm4xxx_get_vol(ak, chip, addr+1); return 0; } static int snd_akm4xxx_stereo_volume_put(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 invert = AK_GET_INVERT(kcontrol->private_value); - unsigned int mask = AK_GET_MASK(kcontrol->private_value); - unsigned char nval = ucontrol->value.integer.value[0] % (mask+1); - int change0, change1; - - if (invert) - nval = mask - nval; - change0 = snd_akm4xxx_get(ak, chip, addr) != nval; - if (change0) - snd_akm4xxx_write(ak, chip, addr, nval); - - nval = ucontrol->value.integer.value[1] % (mask+1); - if (invert) - nval = mask - nval; - change1 = snd_akm4xxx_get(ak, chip, addr+1) != nval; - if (change1) - snd_akm4xxx_write(ak, chip, addr+1, nval); - + int change; - return change0 || change1; + change = put_ak_reg(kcontrol, addr, ucontrol->value.integer.value[0]); + change |= put_ak_reg(kcontrol, addr + 1, + ucontrol->value.integer.value[1]); + return change; } -static int snd_akm4xxx_ipga_gain_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 36; - return 0; -} +#define snd_akm4xxx_ipga_gain_info snd_akm4xxx_volume_info static int snd_akm4xxx_ipga_gain_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -408,21 +432,57 @@ static int snd_akm4xxx_ipga_gain_get(str struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol); int chip = AK_GET_CHIP(kcontrol->private_value); int addr = AK_GET_ADDR(kcontrol->private_value); + ucontrol->value.integer.value[0] = - snd_akm4xxx_get_ipga(ak, chip, addr) & 0x7f; + snd_akm4xxx_get_ipga(ak, chip, addr); return 0; } +static int put_ak_ipga(struct snd_kcontrol *kcontrol, int addr, + unsigned char nval) +{ + struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol); + int chip = AK_GET_CHIP(kcontrol->private_value); + + if (snd_akm4xxx_get_ipga(ak, chip, addr) == nval) + return 0; + snd_akm4xxx_set_ipga(ak, chip, addr, nval); + snd_akm4xxx_write(ak, chip, addr, nval | 0x80); /* need MSB */ + return 1; +} + static int snd_akm4xxx_ipga_gain_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + return put_ak_ipga(kcontrol, AK_GET_ADDR(kcontrol->private_value), + ucontrol->value.integer.value[0]); +} + +#define snd_akm4xxx_stereo_gain_info snd_akm4xxx_stereo_volume_info + +static int snd_akm4xxx_stereo_gain_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); - unsigned char nval = (ucontrol->value.integer.value[0] % 37) | 0x80; - int change = snd_akm4xxx_get_ipga(ak, chip, addr) != nval; - if (change) - snd_akm4xxx_write(ak, chip, addr, nval); + + ucontrol->value.integer.value[0] = + snd_akm4xxx_get_ipga(ak, chip, addr); + ucontrol->value.integer.value[1] = + snd_akm4xxx_get_ipga(ak, chip, addr + 1); + return 0; +} + +static int snd_akm4xxx_stereo_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int addr = AK_GET_ADDR(kcontrol->private_value); + int change; + + change = put_ak_ipga(kcontrol, addr, ucontrol->value.integer.value[0]); + change |= put_ak_ipga(kcontrol, addr + 1, + ucontrol->value.integer.value[1]); return change; } @@ -472,179 +532,302 @@ 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<num_dacs; ) { - memset(ctl, 0, sizeof(*ctl)); - if (ak->channel_names == NULL) { - strcpy(ctl->id.name, "DAC Volume"); + memset(&knew, 0, sizeof(knew)); + if (! ak->dac_info || ! ak->dac_info[mixer_ch].name) { + knew.name = "DAC Volume"; + knew.index = mixer_ch + ak->idx_offset * 2; num_stereo = 1; - ctl->id.index = mixer_ch + ak->idx_offset * 2; } else { - strcpy(ctl->id.name, ak->channel_names[mixer_ch]); - num_stereo = ak->num_stereo[mixer_ch]; - ctl->id.index = 0; + knew.name = ak->dac_info[mixer_ch].name; + num_stereo = ak->dac_info[mixer_ch].num_channels; } - ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - ctl->count = 1; + knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + knew.count = 1; + knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ; if (num_stereo == 2) { - ctl->info = snd_akm4xxx_stereo_volume_info; - ctl->get = snd_akm4xxx_stereo_volume_get; - ctl->put = snd_akm4xxx_stereo_volume_put; + knew.info = snd_akm4xxx_stereo_volume_info; + knew.get = snd_akm4xxx_stereo_volume_get; + knew.put = snd_akm4xxx_stereo_volume_put; } else { - ctl->info = snd_akm4xxx_volume_info; - ctl->get = snd_akm4xxx_volume_get; - ctl->put = snd_akm4xxx_volume_put; + knew.info = snd_akm4xxx_volume_info; + knew.get = snd_akm4xxx_volume_get; + knew.put = snd_akm4xxx_volume_put; } switch (ak->type) { case SND_AK4524: /* register 6 & 7 */ - ctl->private_value = - AK_COMPOSE(idx/2, (idx%2) + 6, 0, 127); + knew.private_value = + AK_COMPOSE(idx/2, (idx%2) + 6, 0, 127) | + AK_VOL_CVT; + knew.tlv.p = db_scale_vol_datt; break; case SND_AK4528: /* register 4 & 5 */ - ctl->private_value = - AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127); + knew.private_value = + AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127) | + AK_VOL_CVT; + knew.tlv.p = db_scale_vol_datt; break; case SND_AK4529: { /* registers 2-7 and b,c */ int val = idx < 6 ? idx + 2 : (idx - 6) + 0xb; - ctl->private_value = + knew.private_value = AK_COMPOSE(0, val, 0, 255) | AK_INVERT; + knew.tlv.p = db_scale_8bit; break; } case SND_AK4355: /* register 4-9, chip #0 only */ - ctl->private_value = AK_COMPOSE(0, idx + 4, 0, 255); + knew.private_value = AK_COMPOSE(0, idx + 4, 0, 255); + knew.tlv.p = db_scale_8bit; break; - case SND_AK4358: - if (idx >= 6) - /* register 4-9, chip #0 only */ - ctl->private_value = - AK_COMPOSE(0, idx + 5, 0, 255); - else - /* register 4-9, chip #0 only */ - ctl->private_value = - AK_COMPOSE(0, idx + 4, 0, 255); + case SND_AK4358: { + /* register 4-9 and 11-12, chip #0 only */ + int addr = idx < 6 ? idx + 4 : idx + 5; + knew.private_value = + AK_COMPOSE(0, addr, 0, 127) | AK_NEEDSMSB; + knew.tlv.p = db_scale_7bit; break; + } case SND_AK4381: /* register 3 & 4 */ - ctl->private_value = + knew.private_value = AK_COMPOSE(idx/2, (idx%2) + 3, 0, 255); + knew.tlv.p = db_scale_linear; break; default: - err = -EINVAL; - goto __error; + return -EINVAL; } - ctl->private_data = ak; - err = snd_ctl_add(ak->card, - snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ| - SNDRV_CTL_ELEM_ACCESS_WRITE)); + err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak)); if (err < 0) - goto __error; + return err; idx += num_stereo; mixer_ch++; } - for (idx = 0; idx < ak->num_adcs && ak->type == SND_AK4524; ++idx) { - memset(ctl, 0, sizeof(*ctl)); - strcpy(ctl->id.name, "ADC Volume"); - ctl->id.index = idx + ak->idx_offset * 2; - ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - ctl->count = 1; - ctl->info = snd_akm4xxx_volume_info; - ctl->get = snd_akm4xxx_volume_get; - ctl->put = snd_akm4xxx_volume_put; + return 0; +} + +static int build_adc_controls(struct snd_akm4xxx *ak) +{ + int idx, err, mixer_ch, num_stereo; + struct snd_kcontrol_new knew; + + mixer_ch = 0; + for (idx = 0; idx < ak->num_adcs;) { + memset(&knew, 0, sizeof(knew)); + if (! ak->adc_info || ! ak->adc_info[mixer_ch].name) { + knew.name = "ADC Volume"; + knew.index = mixer_ch + ak->idx_offset * 2; + num_stereo = 1; + } else { + knew.name = ak->adc_info[mixer_ch].name; + num_stereo = ak->adc_info[mixer_ch].num_channels; + } + knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + knew.count = 1; + knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ; + if (num_stereo == 2) { + knew.info = snd_akm4xxx_stereo_volume_info; + knew.get = snd_akm4xxx_stereo_volume_get; + knew.put = snd_akm4xxx_stereo_volume_put; + } else { + knew.info = snd_akm4xxx_volume_info; + knew.get = snd_akm4xxx_volume_get; + knew.put = snd_akm4xxx_volume_put; + } /* register 4 & 5 */ - ctl->private_value = - AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127); - ctl->private_data = ak; - err = snd_ctl_add(ak->card, - snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ| - SNDRV_CTL_ELEM_ACCESS_WRITE)); + knew.private_value = + AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127) | + AK_VOL_CVT; + knew.tlv.p = db_scale_vol_datt; + err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak)); if (err < 0) - goto __error; - - memset(ctl, 0, sizeof(*ctl)); - strcpy(ctl->id.name, "IPGA Analog Capture Volume"); - ctl->id.index = idx + ak->idx_offset * 2; - ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - ctl->count = 1; - ctl->info = snd_akm4xxx_ipga_gain_info; - ctl->get = snd_akm4xxx_ipga_gain_get; - ctl->put = snd_akm4xxx_ipga_gain_put; + return err; + + if (! ak->adc_info || ! ak->adc_info[mixer_ch].gain_name) + knew.name = "IPGA Analog Capture Volume"; + else + knew.name = ak->adc_info[mixer_ch].gain_name; + if (num_stereo == 2) { + knew.info = snd_akm4xxx_stereo_gain_info; + knew.get = snd_akm4xxx_stereo_gain_get; + knew.put = snd_akm4xxx_stereo_gain_put; + } else { + knew.info = snd_akm4xxx_ipga_gain_info; + knew.get = snd_akm4xxx_ipga_gain_get; + knew.put = snd_akm4xxx_ipga_gain_put; + } /* register 4 & 5 */ - ctl->private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, 0); - 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 (ak->type == SND_AK4524) + knew.private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, + 24); + else /* AK5365 */ + knew.private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, + 36); + knew.tlv.p = db_scale_ipga; + err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak)); if (err < 0) - goto __error; + return err; + + if (ak->type == SND_AK5365 && (idx % 2) == 0) { + if (! ak->adc_info || + ! ak->adc_info[mixer_ch].switch_name) + knew.name = "Capture Switch"; + else + knew.name = ak->adc_info[mixer_ch].switch_name; + knew.info = ak4xxx_switch_info; + knew.get = ak4xxx_switch_get; + knew.put = ak4xxx_switch_put; + knew.access = 0; + /* register 2, bit 0 (SMUTE): 0 = normal operation, + 1 = mute */ + knew.private_value = + AK_COMPOSE(idx/2, 2, 0, 0) | AK_INVERT; + err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak)); + if (err < 0) + return err; + } + + idx += num_stereo; + mixer_ch++; } - if (ak->type == SND_AK4355 || ak->type == SND_AK4358) - num_emphs = 1; - else - num_emphs = ak->num_dacs / 2; + return 0; +} + +static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs) +{ + int idx, err; + struct snd_kcontrol_new knew; + for (idx = 0; idx < num_emphs; idx++) { - memset(ctl, 0, sizeof(*ctl)); - strcpy(ctl->id.name, "Deemphasis"); - ctl->id.index = idx + ak->idx_offset; - ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - ctl->count = 1; - ctl->info = snd_akm4xxx_deemphasis_info; - ctl->get = snd_akm4xxx_deemphasis_get; - ctl->put = snd_akm4xxx_deemphasis_put; + memset(&knew, 0, sizeof(knew)); + knew.name = "Deemphasis"; + knew.index = idx + ak->idx_offset; + knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + knew.count = 1; + knew.info = snd_akm4xxx_deemphasis_info; + knew.get = snd_akm4xxx_deemphasis_get; + knew.put = snd_akm4xxx_deemphasis_put; switch (ak->type) { case SND_AK4524: case SND_AK4528: /* register 3 */ - ctl->private_value = AK_COMPOSE(idx, 3, 0, 0); + knew.private_value = AK_COMPOSE(idx, 3, 0, 0); break; case SND_AK4529: { int shift = idx == 3 ? 6 : (2 - idx) * 2; /* register 8 with shift */ - ctl->private_value = AK_COMPOSE(0, 8, shift, 0); + knew.private_value = AK_COMPOSE(0, 8, shift, 0); break; } case SND_AK4355: case SND_AK4358: - ctl->private_value = AK_COMPOSE(idx, 3, 0, 0); + knew.private_value = AK_COMPOSE(idx, 3, 0, 0); break; case SND_AK4381: - ctl->private_value = AK_COMPOSE(idx, 1, 1, 0); + knew.private_value = AK_COMPOSE(idx, 1, 1, 0); break; + default: + return -EINVAL; } - ctl->private_data = ak; - err = snd_ctl_add(ak->card, - snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ| - SNDRV_CTL_ELEM_ACCESS_WRITE)); + err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak)); if (err < 0) - goto __error; + return err; } - err = 0; - - __error: - kfree(ctl); - return err; + return 0; } +int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak) +{ + int err, num_emphs; + + err = build_dac_controls(ak); + if (err < 0) + return err; + + if (ak->type == SND_AK4524 || ak->type == SND_AK5365) { + err = build_adc_controls(ak); + if (err < 0) + return err; + } + + if (ak->type == SND_AK4355 || ak->type == SND_AK4358) + num_emphs = 1; + else + num_emphs = ak->num_dacs / 2; + err = build_deemphasis(ak, num_emphs); + if (err < 0) + return err; + + return 0; +} + EXPORT_SYMBOL(snd_akm4xxx_build_controls); static int __init alsa_akm4xxx_module_init(void) diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c index 8fcf2c1..fd9b61e 100644 --- a/sound/isa/ad1816a/ad1816a_lib.c +++ b/sound/isa/ad1816a/ad1816a_lib.c @@ -24,6 +24,7 @@ #include #include #include #include +#include #include #include @@ -765,6 +766,13 @@ static int snd_ad1816a_put_mux(struct sn return change; } +#define AD1816A_SINGLE_TLV(xname, reg, shift, mask, invert, xtlv) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ + .name = xname, .info = snd_ad1816a_info_single, \ + .get = snd_ad1816a_get_single, .put = snd_ad1816a_put_single, \ + .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \ + .tlv = { .p = (xtlv) } } #define AD1816A_SINGLE(xname, reg, shift, mask, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ad1816a_info_single, \ .get = snd_ad1816a_get_single, .put = snd_ad1816a_put_single, \ @@ -822,6 +830,14 @@ static int snd_ad1816a_put_single(struct return change; } +#define AD1816A_DOUBLE_TLV(xname, reg, shift_left, shift_right, mask, invert, xtlv) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ + .name = xname, .info = snd_ad1816a_info_double, \ + .get = snd_ad1816a_get_double, .put = snd_ad1816a_put_double, \ + .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24), \ + .tlv = { .p = (xtlv) } } + #define AD1816A_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ad1816a_info_double, \ .get = snd_ad1816a_get_double, .put = snd_ad1816a_put_double, \ @@ -890,28 +906,44 @@ static int snd_ad1816a_put_double(struct return change; } +static DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0); +static DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0); +static DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0); +static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0); +static DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0); + static struct snd_kcontrol_new snd_ad1816a_controls[] __devinitdata = { AD1816A_DOUBLE("Master Playback Switch", AD1816A_MASTER_ATT, 15, 7, 1, 1), -AD1816A_DOUBLE("Master Playback Volume", AD1816A_MASTER_ATT, 8, 0, 31, 1), +AD1816A_DOUBLE_TLV("Master Playback Volume", AD1816A_MASTER_ATT, 8, 0, 31, 1, + db_scale_5bit), AD1816A_DOUBLE("PCM Playback Switch", AD1816A_VOICE_ATT, 15, 7, 1, 1), -AD1816A_DOUBLE("PCM Playback Volume", AD1816A_VOICE_ATT, 8, 0, 63, 1), +AD1816A_DOUBLE_TLV("PCM Playback Volume", AD1816A_VOICE_ATT, 8, 0, 63, 1, + db_scale_6bit), AD1816A_DOUBLE("Line Playback Switch", AD1816A_LINE_GAIN_ATT, 15, 7, 1, 1), -AD1816A_DOUBLE("Line Playback Volume", AD1816A_LINE_GAIN_ATT, 8, 0, 31, 1), +AD1816A_DOUBLE_TLV("Line Playback Volume", AD1816A_LINE_GAIN_ATT, 8, 0, 31, 1, + db_scale_5bit_12db_max), AD1816A_DOUBLE("CD Playback Switch", AD1816A_CD_GAIN_ATT, 15, 7, 1, 1), -AD1816A_DOUBLE("CD Playback Volume", AD1816A_CD_GAIN_ATT, 8, 0, 31, 1), +AD1816A_DOUBLE_TLV("CD Playback Volume", AD1816A_CD_GAIN_ATT, 8, 0, 31, 1, + db_scale_5bit_12db_max), AD1816A_DOUBLE("Synth Playback Switch", AD1816A_SYNTH_GAIN_ATT, 15, 7, 1, 1), -AD1816A_DOUBLE("Synth Playback Volume", AD1816A_SYNTH_GAIN_ATT, 8, 0, 31, 1), +AD1816A_DOUBLE_TLV("Synth Playback Volume", AD1816A_SYNTH_GAIN_ATT, 8, 0, 31, 1, + db_scale_5bit_12db_max), AD1816A_DOUBLE("FM Playback Switch", AD1816A_FM_ATT, 15, 7, 1, 1), -AD1816A_DOUBLE("FM Playback Volume", AD1816A_FM_ATT, 8, 0, 63, 1), +AD1816A_DOUBLE_TLV("FM Playback Volume", AD1816A_FM_ATT, 8, 0, 63, 1, + db_scale_6bit), AD1816A_SINGLE("Mic Playback Switch", AD1816A_MIC_GAIN_ATT, 15, 1, 1), -AD1816A_SINGLE("Mic Playback Volume", AD1816A_MIC_GAIN_ATT, 8, 31, 1), +AD1816A_SINGLE_TLV("Mic Playback Volume", AD1816A_MIC_GAIN_ATT, 8, 31, 1, + db_scale_5bit_12db_max), AD1816A_SINGLE("Mic Boost", AD1816A_MIC_GAIN_ATT, 14, 1, 0), AD1816A_DOUBLE("Video Playback Switch", AD1816A_VID_GAIN_ATT, 15, 7, 1, 1), -AD1816A_DOUBLE("Video Playback Volume", AD1816A_VID_GAIN_ATT, 8, 0, 31, 1), +AD1816A_DOUBLE_TLV("Video Playback Volume", AD1816A_VID_GAIN_ATT, 8, 0, 31, 1, + db_scale_5bit_12db_max), AD1816A_SINGLE("Phone Capture Switch", AD1816A_PHONE_IN_GAIN_ATT, 15, 1, 1), -AD1816A_SINGLE("Phone Capture Volume", AD1816A_PHONE_IN_GAIN_ATT, 0, 15, 1), +AD1816A_SINGLE_TLV("Phone Capture Volume", AD1816A_PHONE_IN_GAIN_ATT, 0, 15, 1, + db_scale_4bit), AD1816A_SINGLE("Phone Playback Switch", AD1816A_PHONE_OUT_ATT, 7, 1, 1), -AD1816A_SINGLE("Phone Playback Volume", AD1816A_PHONE_OUT_ATT, 0, 31, 1), +AD1816A_SINGLE_TLV("Phone Playback Volume", AD1816A_PHONE_OUT_ATT, 0, 31, 1, + db_scale_5bit), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Capture Source", @@ -920,7 +952,8 @@ AD1816A_SINGLE("Phone Playback Volume", .put = snd_ad1816a_put_mux, }, AD1816A_DOUBLE("Capture Switch", AD1816A_ADC_PGA, 15, 7, 1, 1), -AD1816A_DOUBLE("Capture Volume", AD1816A_ADC_PGA, 8, 0, 15, 0), +AD1816A_DOUBLE_TLV("Capture Volume", AD1816A_ADC_PGA, 8, 0, 15, 0, + db_scale_rec_gain), AD1816A_SINGLE("3D Control - Switch", AD1816A_3D_PHAT_CTRL, 15, 1, 1), AD1816A_SINGLE("3D Control - Level", AD1816A_3D_PHAT_CTRL, 0, 15, 0), }; diff --git a/sound/isa/ad1848/ad1848_lib.c b/sound/isa/ad1848/ad1848_lib.c index e711f87..a6fbd5d 100644 --- a/sound/isa/ad1848/ad1848_lib.c +++ b/sound/isa/ad1848/ad1848_lib.c @@ -29,6 +29,7 @@ #include #include #include #include +#include #include #include @@ -118,6 +119,8 @@ #if 0 #endif } +EXPORT_SYMBOL(snd_ad1848_out); + static void snd_ad1848_dout(struct snd_ad1848 *chip, unsigned char reg, unsigned char value) { @@ -941,6 +944,8 @@ #endif return 0; } +EXPORT_SYMBOL(snd_ad1848_create); + static struct snd_pcm_ops snd_ad1848_playback_ops = { .open = snd_ad1848_playback_open, .close = snd_ad1848_playback_close, @@ -988,12 +993,16 @@ int snd_ad1848_pcm(struct snd_ad1848 *ch return 0; } +EXPORT_SYMBOL(snd_ad1848_pcm); + const struct snd_pcm_ops *snd_ad1848_get_pcm_ops(int direction) { return direction == SNDRV_PCM_STREAM_PLAYBACK ? &snd_ad1848_playback_ops : &snd_ad1848_capture_ops; } +EXPORT_SYMBOL(snd_ad1848_get_pcm_ops); + /* * MIXER part */ @@ -1171,7 +1180,8 @@ static int snd_ad1848_put_double(struct /* */ -int snd_ad1848_add_ctl(struct snd_ad1848 *chip, const char *name, int index, int type, unsigned long value) +int snd_ad1848_add_ctl_elem(struct snd_ad1848 *chip, + const struct ad1848_mix_elem *c) { static struct snd_kcontrol_new newctls[] = { [AD1848_MIX_SINGLE] = { @@ -1196,32 +1206,46 @@ int snd_ad1848_add_ctl(struct snd_ad1848 struct snd_kcontrol *ctl; int err; - ctl = snd_ctl_new1(&newctls[type], chip); + ctl = snd_ctl_new1(&newctls[c->type], chip); if (! ctl) return -ENOMEM; - strlcpy(ctl->id.name, name, sizeof(ctl->id.name)); - ctl->id.index = index; - ctl->private_value = value; + strlcpy(ctl->id.name, c->name, sizeof(ctl->id.name)); + ctl->id.index = c->index; + ctl->private_value = c->private_value; + if (c->tlv) { + ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; + ctl->tlv.p = c->tlv; + } if ((err = snd_ctl_add(chip->card, ctl)) < 0) return err; return 0; } +EXPORT_SYMBOL(snd_ad1848_add_ctl_elem); + +static DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0); +static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0); +static DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0); static struct ad1848_mix_elem snd_ad1848_controls[] = { AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1), -AD1848_DOUBLE("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1), +AD1848_DOUBLE_TLV("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1, + db_scale_6bit), AD1848_DOUBLE("Aux Playback Switch", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 7, 7, 1, 1), -AD1848_DOUBLE("Aux Playback Volume", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 0, 0, 31, 1), +AD1848_DOUBLE_TLV("Aux Playback Volume", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 0, 0, 31, 1, + db_scale_5bit_12db_max), AD1848_DOUBLE("Aux Playback Switch", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 7, 7, 1, 1), -AD1848_DOUBLE("Aux Playback Volume", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 0, 0, 31, 1), -AD1848_DOUBLE("Capture Volume", 0, AD1848_LEFT_INPUT, AD1848_RIGHT_INPUT, 0, 0, 15, 0), +AD1848_DOUBLE_TLV("Aux Playback Volume", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 0, 0, 31, 1, + db_scale_5bit_12db_max), +AD1848_DOUBLE_TLV("Capture Volume", 0, AD1848_LEFT_INPUT, AD1848_RIGHT_INPUT, 0, 0, 15, 0, + db_scale_rec_gain), { .name = "Capture Source", .type = AD1848_MIX_CAPTURE, }, AD1848_SINGLE("Loopback Capture Switch", 0, AD1848_LOOPBACK, 0, 1, 0), -AD1848_SINGLE("Loopback Capture Volume", 0, AD1848_LOOPBACK, 1, 63, 0) +AD1848_SINGLE_TLV("Loopback Capture Volume", 0, AD1848_LOOPBACK, 1, 63, 0, + db_scale_6bit), }; int snd_ad1848_mixer(struct snd_ad1848 *chip) @@ -1245,12 +1269,7 @@ int snd_ad1848_mixer(struct snd_ad1848 * return 0; } -EXPORT_SYMBOL(snd_ad1848_out); -EXPORT_SYMBOL(snd_ad1848_create); -EXPORT_SYMBOL(snd_ad1848_pcm); -EXPORT_SYMBOL(snd_ad1848_get_pcm_ops); EXPORT_SYMBOL(snd_ad1848_mixer); -EXPORT_SYMBOL(snd_ad1848_add_ctl); /* * INIT part 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/isa/opl3sa2.c b/sound/isa/opl3sa2.c index 4031b61..da92bf6 100644 --- a/sound/isa/opl3sa2.c +++ b/sound/isa/opl3sa2.c @@ -33,6 +33,7 @@ #include #include #include #include +#include #include @@ -337,6 +338,14 @@ #define OPL3SA2_SINGLE(xname, xindex, re .info = snd_opl3sa2_info_single, \ .get = snd_opl3sa2_get_single, .put = snd_opl3sa2_put_single, \ .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } +#define OPL3SA2_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ + .name = xname, .index = xindex, \ + .info = snd_opl3sa2_info_single, \ + .get = snd_opl3sa2_get_single, .put = snd_opl3sa2_put_single, \ + .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \ + .tlv = { .p = (xtlv) } } static int snd_opl3sa2_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -395,6 +404,14 @@ #define OPL3SA2_DOUBLE(xname, xindex, le .info = snd_opl3sa2_info_double, \ .get = snd_opl3sa2_get_double, .put = snd_opl3sa2_put_double, \ .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } +#define OPL3SA2_DOUBLE_TLV(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert, xtlv) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ + .name = xname, .index = xindex, \ + .info = snd_opl3sa2_info_double, \ + .get = snd_opl3sa2_get_double, .put = snd_opl3sa2_put_double, \ + .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22), \ + .tlv = { .p = (xtlv) } } static int snd_opl3sa2_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -469,11 +486,16 @@ static int snd_opl3sa2_put_double(struct return change; } +static DECLARE_TLV_DB_SCALE(db_scale_master, -3000, 200, 0); +static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0); + static struct snd_kcontrol_new snd_opl3sa2_controls[] = { OPL3SA2_DOUBLE("Master Playback Switch", 0, 0x07, 0x08, 7, 7, 1, 1), -OPL3SA2_DOUBLE("Master Playback Volume", 0, 0x07, 0x08, 0, 0, 15, 1), +OPL3SA2_DOUBLE_TLV("Master Playback Volume", 0, 0x07, 0x08, 0, 0, 15, 1, + db_scale_master), OPL3SA2_SINGLE("Mic Playback Switch", 0, 0x09, 7, 1, 1), -OPL3SA2_SINGLE("Mic Playback Volume", 0, 0x09, 0, 31, 1) +OPL3SA2_SINGLE_TLV("Mic Playback Volume", 0, 0x09, 0, 31, 1, + db_scale_5bit_12db_max), }; static struct snd_kcontrol_new snd_opl3sa2_tone_controls[] = { diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index e49c0fe..dffb6be 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -475,6 +475,7 @@ config SND_FM801_TEA575X depends on SND_FM801_TEA575X_BOOL default SND_FM801 select VIDEO_V4L1 + select VIDEO_DEV config SND_HDA_INTEL tristate "Intel HD Audio" diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 51e83d7..a79e918 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -31,6 +31,7 @@ #include #include #include #include +#include #include #include #include @@ -47,6 +48,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 +157,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 +193,8 @@ static const struct ac97_codec_id snd_ac }; +static void update_power_regs(struct snd_ac97 *ac97); + /* * I/O routines */ @@ -554,6 +562,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 +982,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 +1141,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 +1163,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); } @@ -1153,6 +1183,32 @@ static int snd_ac97_cmute_new_stereo(str } /* + * set dB information + */ +static DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0); +static DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0); +static DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0); +static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0); +static DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0); + +static unsigned int *find_db_scale(unsigned int maxval) +{ + switch (maxval) { + case 0x0f: return db_scale_4bit; + case 0x1f: return db_scale_5bit; + case 0x3f: return db_scale_6bit; + } + return NULL; +} + +static void set_tlv_db_scale(struct snd_kcontrol *kctl, unsigned int *tlv) +{ + kctl->tlv.p = tlv; + if (tlv) + kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; +} + +/* * create a volume for normal stereo/mono controls */ static int snd_ac97_cvol_new(struct snd_card *card, char *name, int reg, unsigned int lo_max, @@ -1174,6 +1230,10 @@ static int snd_ac97_cvol_new(struct snd_ tmp.index = ac97->num; kctl = snd_ctl_new1(&tmp, ac97); } + if (reg >= AC97_PHONE && reg <= AC97_PCM) + set_tlv_db_scale(kctl, db_scale_5bit_12db_max); + else + set_tlv_db_scale(kctl, find_db_scale(lo_max)); err = snd_ctl_add(card, kctl); if (err < 0) return err; @@ -1186,7 +1246,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 +1259,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 +1273,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 +1292,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; } @@ -1245,6 +1313,7 @@ static int snd_ac97_mixer_build(struct s snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 0, &max); kctl->private_value &= ~(0xff << 16); kctl->private_value |= (int)max << 16; + set_tlv_db_scale(kctl, find_db_scale(max)); snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max); } @@ -1258,6 +1327,7 @@ static int snd_ac97_mixer_build(struct s snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 8, &max); kctl->private_value &= ~(0xff << 16); kctl->private_value |= (int)max << 16; + set_tlv_db_scale(kctl, find_db_scale(max)); snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max << 8); } @@ -1265,19 +1335,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; } @@ -1301,8 +1375,9 @@ static int snd_ac97_mixer_build(struct s ((ac97->flags & AC97_HAS_PC_BEEP) || snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP))) { for (idx = 0; idx < 2; idx++) - if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0) + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0) return err; + set_tlv_db_scale(kctl, db_scale_4bit); snd_ac97_write_cache(ac97, AC97_PC_BEEP, snd_ac97_read(ac97, AC97_PC_BEEP) | 0x801e); } @@ -1310,7 +1385,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 +1394,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 +1404,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 +1421,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 +1430,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; } } @@ -1363,31 +1444,38 @@ static int snd_ac97_mixer_build(struct s else init_val = 0x9f1f; for (idx = 0; idx < 2; idx++) - if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_pcm[idx], ac97))) < 0) + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_pcm[idx], ac97))) < 0) return err; + set_tlv_db_scale(kctl, db_scale_5bit); ac97->spec.ad18xx.pcmreg[0] = init_val; if (ac97->scaps & AC97_SCAP_SURROUND_DAC) { for (idx = 0; idx < 2; idx++) - if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_surround[idx], ac97))) < 0) + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_surround[idx], ac97))) < 0) return err; + set_tlv_db_scale(kctl, db_scale_5bit); ac97->spec.ad18xx.pcmreg[1] = init_val; } if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) { for (idx = 0; idx < 2; idx++) - if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_center[idx], ac97))) < 0) + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_center[idx], ac97))) < 0) return err; + set_tlv_db_scale(kctl, db_scale_5bit); for (idx = 0; idx < 2; idx++) - if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_lfe[idx], ac97))) < 0) + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_lfe[idx], ac97))) < 0) return err; + set_tlv_db_scale(kctl, db_scale_5bit); ac97->spec.ad18xx.pcmreg[2] = init_val; } snd_ac97_write_cache(ac97, AC97_PCM, init_val); } 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,19 +1486,23 @@ 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) + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_control_capture_vol, ac97))) < 0) return err; + set_tlv_db_scale(kctl, db_scale_rec_gain); snd_ac97_write_cache(ac97, AC97_REC_SEL, 0x0000); snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x0000); } /* build MIC Capture controls */ if (snd_ac97_try_volume_mix(ac97, AC97_REC_GAIN_MIC)) { for (idx = 0; idx < 2; idx++) - if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_capture[idx], ac97))) < 0) + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_mic_capture[idx], ac97))) < 0) return err; + set_tlv_db_scale(kctl, db_scale_rec_gain); snd_ac97_write_cache(ac97, AC97_REC_GAIN_MIC, 0x0000); } @@ -1481,6 +1573,12 @@ static int snd_ac97_mixer_build(struct s } /* build S/PDIF controls */ + + /* Hack for ASUS P5P800-VM, which does not indicate S/PDIF capability */ + if (ac97->subsystem_vendor == 0x1043 && + ac97->subsystem_device == 0x810f) + ac97->ext_id |= AC97_EI_SPDIF; + if ((ac97->ext_id & AC97_EI_SPDIF) && !(ac97->scaps & AC97_SCAP_NO_SPDIF)) { if (ac97->build_ops->build_spdif) { if ((err = ac97->build_ops->build_spdif(ac97)) < 0) @@ -1817,18 +1915,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 +1965,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 +1988,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 +2226,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,19 +2255,152 @@ 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); + } + } } @@ -2484,6 +2719,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..9be4ceb 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -32,6 +32,7 @@ #include #include #include #include +#include #include #include "ac97_patch.h" #include "ac97_id.h" @@ -51,6 +52,20 @@ static int patch_build_controls(struct s return 0; } +/* replace with a new TLV */ +static void reset_tlv(struct snd_ac97 *ac97, const char *name, + unsigned int *tlv) +{ + struct snd_ctl_elem_id sid; + struct snd_kcontrol *kctl; + memset(&sid, 0, sizeof(sid)); + strcpy(sid.name, name); + sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kctl = snd_ctl_find_id(ac97->bus->card, &sid); + if (kctl && kctl->tlv.p) + kctl->tlv.p = tlv; +} + /* set to the page, update bits and restore the page */ static int ac97_update_bits_page(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value, unsigned short page) { @@ -1380,6 +1395,17 @@ static void ad1888_resume(struct snd_ac9 #endif +static const struct snd_ac97_res_table ad1819_restbl[] = { + { AC97_PHONE, 0x9f1f }, + { AC97_MIC, 0x9f1f }, + { AC97_LINE, 0x9f1f }, + { AC97_CD, 0x9f1f }, + { AC97_VIDEO, 0x9f1f }, + { AC97_AUX, 0x9f1f }, + { AC97_PCM, 0x9f1f }, + { } /* terminator */ +}; + int patch_ad1819(struct snd_ac97 * ac97) { unsigned short scfg; @@ -1387,6 +1413,7 @@ int patch_ad1819(struct snd_ac97 * ac97) // patch for Analog Devices scfg = snd_ac97_read(ac97, AC97_AD_SERIAL_CFG); snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, scfg | 0x7000); /* select all codecs */ + ac97->res_table = ad1819_restbl; return 0; } @@ -1522,12 +1549,16 @@ static const struct snd_kcontrol_new snd AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 8, 1, 1), /* inverted */ }; +static DECLARE_TLV_DB_SCALE(db_scale_6bit_6db_max, -8850, 150, 0); + static int patch_ad1885_specific(struct snd_ac97 * ac97) { int err; if ((err = patch_build_controls(ac97, snd_ac97_controls_ad1885, ARRAY_SIZE(snd_ac97_controls_ad1885))) < 0) return err; + reset_tlv(ac97, "Headphone Playback Volume", + db_scale_6bit_6db_max); return 0; } @@ -1551,12 +1582,27 @@ int patch_ad1885(struct snd_ac97 * ac97) return 0; } +static int patch_ad1886_specific(struct snd_ac97 * ac97) +{ + reset_tlv(ac97, "Headphone Playback Volume", + db_scale_6bit_6db_max); + return 0; +} + +static struct snd_ac97_build_ops patch_ad1886_build_ops = { + .build_specific = &patch_ad1886_specific, +#ifdef CONFIG_PM + .resume = ad18xx_resume +#endif +}; + int patch_ad1886(struct snd_ac97 * ac97) { patch_ad1881(ac97); /* Presario700 workaround */ /* for Jack Sense/SPDIF Register misetting causing */ snd_ac97_write_cache(ac97, AC97_AD_JACK_SPDIF, 0x0010); + ac97->build_ops = &patch_ad1886_build_ops; return 0; } @@ -2015,6 +2061,8 @@ static const struct snd_kcontrol_new snd /* AC97_SINGLE("IEC958 Input Monitor", AC97_ALC650_MULTICH, 13, 1, 0), */ }; +static DECLARE_TLV_DB_SCALE(db_scale_5bit_3db_max, -4350, 150, 0); + static int patch_alc650_specific(struct snd_ac97 * ac97) { int err; @@ -2025,6 +2073,9 @@ static int patch_alc650_specific(struct if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc650, ARRAY_SIZE(snd_ac97_spdif_controls_alc650))) < 0) return err; } + if (ac97->id != AC97_ID_ALC650F) + reset_tlv(ac97, "Master Playback Volume", + db_scale_5bit_3db_max); return 0; } @@ -2208,7 +2259,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 */ @@ -2759,6 +2811,10 @@ int patch_vt1616(struct snd_ac97 * ac97) */ int patch_vt1617a(struct snd_ac97 * ac97) { + /* bring analog power consumption to normal, like WinXP driver + * for EPIA SP + */ + snd_ac97_write_cache(ac97, 0x5c, 0x20); ac97->ext_id |= AC97_EI_SPDIF; /* force the detection of spdif */ ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000; return 0; @@ -2872,3 +2928,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/ac97/ak4531_codec.c b/sound/pci/ac97/ak4531_codec.c index 94c26ec..c153cb7 100644 --- a/sound/pci/ac97/ak4531_codec.c +++ b/sound/pci/ac97/ak4531_codec.c @@ -27,6 +27,7 @@ #include #include #include +#include MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Universal routines for AK4531 codec"); @@ -63,6 +64,14 @@ #define AK4531_SINGLE(xname, xindex, reg .info = snd_ak4531_info_single, \ .get = snd_ak4531_get_single, .put = snd_ak4531_put_single, \ .private_value = reg | (shift << 16) | (mask << 24) | (invert << 22) } +#define AK4531_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ + .name = xname, .index = xindex, \ + .info = snd_ak4531_info_single, \ + .get = snd_ak4531_get_single, .put = snd_ak4531_put_single, \ + .private_value = reg | (shift << 16) | (mask << 24) | (invert << 22), \ + .tlv = { .p = (xtlv) } } static int snd_ak4531_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -122,6 +131,14 @@ #define AK4531_DOUBLE(xname, xindex, lef .info = snd_ak4531_info_double, \ .get = snd_ak4531_get_double, .put = snd_ak4531_put_double, \ .private_value = left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) | (invert << 22) } +#define AK4531_DOUBLE_TLV(xname, xindex, left_reg, right_reg, left_shift, right_shift, mask, invert, xtlv) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ + .name = xname, .index = xindex, \ + .info = snd_ak4531_info_double, \ + .get = snd_ak4531_get_double, .put = snd_ak4531_put_double, \ + .private_value = left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) | (invert << 22), \ + .tlv = { .p = (xtlv) } } static int snd_ak4531_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -250,50 +267,62 @@ static int snd_ak4531_put_input_sw(struc return change; } +static DECLARE_TLV_DB_SCALE(db_scale_master, -6200, 200, 0); +static DECLARE_TLV_DB_SCALE(db_scale_mono, -2800, 400, 0); +static DECLARE_TLV_DB_SCALE(db_scale_input, -5000, 200, 0); + static struct snd_kcontrol_new snd_ak4531_controls[] = { -AK4531_DOUBLE("Master Playback Switch", 0, AK4531_LMASTER, AK4531_RMASTER, 7, 7, 1, 1), +AK4531_DOUBLE_TLV("Master Playback Switch", 0, + AK4531_LMASTER, AK4531_RMASTER, 7, 7, 1, 1, + db_scale_master), AK4531_DOUBLE("Master Playback Volume", 0, AK4531_LMASTER, AK4531_RMASTER, 0, 0, 0x1f, 1), -AK4531_SINGLE("Master Mono Playback Switch", 0, AK4531_MONO_OUT, 7, 1, 1), +AK4531_SINGLE_TLV("Master Mono Playback Switch", 0, AK4531_MONO_OUT, 7, 1, 1, + db_scale_mono), AK4531_SINGLE("Master Mono Playback Volume", 0, AK4531_MONO_OUT, 0, 0x07, 1), AK4531_DOUBLE("PCM Switch", 0, AK4531_LVOICE, AK4531_RVOICE, 7, 7, 1, 1), -AK4531_DOUBLE("PCM Volume", 0, AK4531_LVOICE, AK4531_RVOICE, 0, 0, 0x1f, 1), +AK4531_DOUBLE_TLV("PCM Volume", 0, AK4531_LVOICE, AK4531_RVOICE, 0, 0, 0x1f, 1, + db_scale_input), AK4531_DOUBLE("PCM Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 3, 2, 1, 0), AK4531_DOUBLE("PCM Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 2, 2, 1, 0), AK4531_DOUBLE("PCM Switch", 1, AK4531_LFM, AK4531_RFM, 7, 7, 1, 1), -AK4531_DOUBLE("PCM Volume", 1, AK4531_LFM, AK4531_RFM, 0, 0, 0x1f, 1), +AK4531_DOUBLE_TLV("PCM Volume", 1, AK4531_LFM, AK4531_RFM, 0, 0, 0x1f, 1, + db_scale_input), AK4531_DOUBLE("PCM Playback Switch", 1, AK4531_OUT_SW1, AK4531_OUT_SW1, 6, 5, 1, 0), AK4531_INPUT_SW("PCM Capture Route", 1, AK4531_LIN_SW1, AK4531_RIN_SW1, 6, 5), AK4531_DOUBLE("CD Switch", 0, AK4531_LCD, AK4531_RCD, 7, 7, 1, 1), -AK4531_DOUBLE("CD Volume", 0, AK4531_LCD, AK4531_RCD, 0, 0, 0x1f, 1), +AK4531_DOUBLE_TLV("CD Volume", 0, AK4531_LCD, AK4531_RCD, 0, 0, 0x1f, 1, + db_scale_input), AK4531_DOUBLE("CD Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 2, 1, 1, 0), AK4531_INPUT_SW("CD Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 2, 1), AK4531_DOUBLE("Line Switch", 0, AK4531_LLINE, AK4531_RLINE, 7, 7, 1, 1), -AK4531_DOUBLE("Line Volume", 0, AK4531_LLINE, AK4531_RLINE, 0, 0, 0x1f, 1), +AK4531_DOUBLE_TLV("Line Volume", 0, AK4531_LLINE, AK4531_RLINE, 0, 0, 0x1f, 1, + db_scale_input), AK4531_DOUBLE("Line Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 4, 3, 1, 0), AK4531_INPUT_SW("Line Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 4, 3), AK4531_DOUBLE("Aux Switch", 0, AK4531_LAUXA, AK4531_RAUXA, 7, 7, 1, 1), -AK4531_DOUBLE("Aux Volume", 0, AK4531_LAUXA, AK4531_RAUXA, 0, 0, 0x1f, 1), +AK4531_DOUBLE_TLV("Aux Volume", 0, AK4531_LAUXA, AK4531_RAUXA, 0, 0, 0x1f, 1, + db_scale_input), AK4531_DOUBLE("Aux Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 5, 4, 1, 0), AK4531_INPUT_SW("Aux Capture Route", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 4, 3), AK4531_SINGLE("Mono Switch", 0, AK4531_MONO1, 7, 1, 1), -AK4531_SINGLE("Mono Volume", 0, AK4531_MONO1, 0, 0x1f, 1), +AK4531_SINGLE_TLV("Mono Volume", 0, AK4531_MONO1, 0, 0x1f, 1, db_scale_input), AK4531_SINGLE("Mono Playback Switch", 0, AK4531_OUT_SW2, 0, 1, 0), AK4531_DOUBLE("Mono Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 0, 0, 1, 0), AK4531_SINGLE("Mono Switch", 1, AK4531_MONO2, 7, 1, 1), -AK4531_SINGLE("Mono Volume", 1, AK4531_MONO2, 0, 0x1f, 1), +AK4531_SINGLE_TLV("Mono Volume", 1, AK4531_MONO2, 0, 0x1f, 1, db_scale_input), AK4531_SINGLE("Mono Playback Switch", 1, AK4531_OUT_SW2, 1, 1, 0), AK4531_DOUBLE("Mono Capture Switch", 1, AK4531_LIN_SW2, AK4531_RIN_SW2, 1, 1, 1, 0), -AK4531_SINGLE("Mic Volume", 0, AK4531_MIC, 0, 0x1f, 1), +AK4531_SINGLE_TLV("Mic Volume", 0, AK4531_MIC, 0, 0x1f, 1, db_scale_input), AK4531_SINGLE("Mic Switch", 0, AK4531_MIC, 7, 1, 1), AK4531_SINGLE("Mic Playback Switch", 0, AK4531_OUT_SW1, 0, 1, 0), AK4531_DOUBLE("Mic Capture Switch", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 0, 0, 1, 0), 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/cs4281.c b/sound/pci/cs4281.c index 9631456..1990430 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -33,6 +33,7 @@ #include #include #include #include +#include #include #include @@ -1054,6 +1055,8 @@ static int snd_cs4281_put_volume(struct return change; } +static DECLARE_TLV_DB_SCALE(db_scale_dsp, -4650, 150, 0); + static struct snd_kcontrol_new snd_cs4281_fm_vol = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1062,6 +1065,7 @@ static struct snd_kcontrol_new snd_cs428 .get = snd_cs4281_get_volume, .put = snd_cs4281_put_volume, .private_value = ((BA0_FMLVC << 16) | BA0_FMRVC), + .tlv = { .p = db_scale_dsp }, }; static struct snd_kcontrol_new snd_cs4281_pcm_vol = @@ -1072,6 +1076,7 @@ static struct snd_kcontrol_new snd_cs428 .get = snd_cs4281_get_volume, .put = snd_cs4281_put_volume, .private_value = ((BA0_PPLVC << 16) | BA0_PPRVC), + .tlv = { .p = db_scale_dsp }, }; static void snd_cs4281_mixer_free_ac97_bus(struct snd_ac97_bus *bus) 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.c b/sound/pci/emu10k1/emu10k1.c index 289bcd9..493ec08 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -232,7 +232,7 @@ static int snd_emu10k1_suspend(struct pc return 0; } -int snd_emu10k1_resume(struct pci_dev *pci) +static int snd_emu10k1_resume(struct pci_dev *pci) { struct snd_card *card = pci_get_drvdata(pci); struct snd_emu10k1 *emu = card->private_data; 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/es1938.c b/sound/pci/es1938.c index cc0f34f..3784088 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -62,6 +62,7 @@ #include #include #include #include +#include #include @@ -1164,6 +1165,14 @@ static int snd_es1938_reg_read(struct es return snd_es1938_read(chip, reg); } +#define ES1938_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,\ + .name = xname, .index = xindex, \ + .info = snd_es1938_info_single, \ + .get = snd_es1938_get_single, .put = snd_es1938_put_single, \ + .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \ + .tlv = { .p = xtlv } } #define ES1938_SINGLE(xname, xindex, reg, shift, mask, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_es1938_info_single, \ @@ -1217,6 +1226,14 @@ static int snd_es1938_put_single(struct return snd_es1938_reg_bits(chip, reg, mask, val) != val; } +#define ES1938_DOUBLE_TLV(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert, xtlv) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,\ + .name = xname, .index = xindex, \ + .info = snd_es1938_info_double, \ + .get = snd_es1938_get_double, .put = snd_es1938_put_double, \ + .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22), \ + .tlv = { .p = xtlv } } #define ES1938_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_es1938_info_double, \ @@ -1297,8 +1314,41 @@ static int snd_es1938_put_double(struct return change; } +static unsigned int db_scale_master[] = { + TLV_DB_RANGE_HEAD(2), + 0, 54, TLV_DB_SCALE_ITEM(-3600, 50, 1), + 54, 63, TLV_DB_SCALE_ITEM(-900, 100, 0), +}; + +static unsigned int db_scale_audio1[] = { + TLV_DB_RANGE_HEAD(2), + 0, 8, TLV_DB_SCALE_ITEM(-3300, 300, 1), + 8, 15, TLV_DB_SCALE_ITEM(-900, 150, 0), +}; + +static unsigned int db_scale_audio2[] = { + TLV_DB_RANGE_HEAD(2), + 0, 8, TLV_DB_SCALE_ITEM(-3450, 300, 1), + 8, 15, TLV_DB_SCALE_ITEM(-1050, 150, 0), +}; + +static unsigned int db_scale_mic[] = { + TLV_DB_RANGE_HEAD(2), + 0, 8, TLV_DB_SCALE_ITEM(-2400, 300, 1), + 8, 15, TLV_DB_SCALE_ITEM(0, 150, 0), +}; + +static unsigned int db_scale_line[] = { + TLV_DB_RANGE_HEAD(2), + 0, 8, TLV_DB_SCALE_ITEM(-3150, 300, 1), + 8, 15, TLV_DB_SCALE_ITEM(-750, 150, 0), +}; + +static DECLARE_TLV_DB_SCALE(db_scale_capture, 0, 150, 0); + static struct snd_kcontrol_new snd_es1938_controls[] = { -ES1938_DOUBLE("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0), +ES1938_DOUBLE_TLV("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0, + db_scale_master), ES1938_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1309,19 +1359,28 @@ ES1938_DOUBLE("Master Playback Switch", }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Hardware Master Playback Switch", .access = SNDRV_CTL_ELEM_ACCESS_READ, .info = snd_es1938_info_hw_switch, .get = snd_es1938_get_hw_switch, + .tlv = { .p = db_scale_master }, }, ES1938_SINGLE("Hardware Volume Split", 0, 0x64, 7, 1, 0), -ES1938_DOUBLE("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0), +ES1938_DOUBLE_TLV("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0, + db_scale_line), ES1938_DOUBLE("CD Playback Volume", 0, 0x38, 0x38, 4, 0, 15, 0), -ES1938_DOUBLE("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0), -ES1938_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0), -ES1938_DOUBLE("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0), -ES1938_DOUBLE("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0), -ES1938_DOUBLE("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0), +ES1938_DOUBLE_TLV("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0, + db_scale_mic), +ES1938_DOUBLE_TLV("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0, + db_scale_line), +ES1938_DOUBLE_TLV("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0, + db_scale_mic), +ES1938_DOUBLE_TLV("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0, + db_scale_line), +ES1938_DOUBLE_TLV("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0, + db_scale_capture), ES1938_SINGLE("PC Speaker Volume", 0, 0x3c, 0, 7, 0), ES1938_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0), ES1938_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1), @@ -1332,16 +1391,26 @@ ES1938_SINGLE("Capture Switch", 0, 0x1c, .get = snd_es1938_get_mux, .put = snd_es1938_put_mux, }, -ES1938_DOUBLE("Mono Input Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0), -ES1938_DOUBLE("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0), -ES1938_DOUBLE("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0), -ES1938_DOUBLE("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0), -ES1938_DOUBLE("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0), -ES1938_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0), -ES1938_DOUBLE("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0), -ES1938_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0), -ES1938_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0), -ES1938_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0), +ES1938_DOUBLE_TLV("Mono Input Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0, + db_scale_line), +ES1938_DOUBLE_TLV("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0, + db_scale_audio2), +ES1938_DOUBLE_TLV("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0, + db_scale_mic), +ES1938_DOUBLE_TLV("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0, + db_scale_line), +ES1938_DOUBLE_TLV("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0, + db_scale_mic), +ES1938_DOUBLE_TLV("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0, + db_scale_line), +ES1938_DOUBLE_TLV("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0, + db_scale_line), +ES1938_DOUBLE_TLV("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0, + db_scale_line), +ES1938_DOUBLE_TLV("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0, + db_scale_audio2), +ES1938_DOUBLE_TLV("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0, + db_scale_audio1), ES1938_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 3c5ab7c..f3c4038 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -1905,7 +1905,7 @@ static void es1968_update_hw_volume(unsi /* Figure out which volume control button was pushed, based on differences from the default register values. */ - x = inb(chip->io_port + 0x1c); + x = inb(chip->io_port + 0x1c) & 0xee; /* Reset the volume control registers. */ outb(0x88, chip->io_port + 0x1c); outb(0x88, chip->io_port + 0x1d); @@ -1921,7 +1921,8 @@ static void es1968_update_hw_volume(unsi /* FIXME: we can't call snd_ac97_* functions since here is in tasklet. */ spin_lock_irqsave(&chip->ac97_lock, flags); val = chip->ac97->regs[AC97_MASTER]; - if (x & 1) { + switch (x) { + case 0x88: /* mute */ val ^= 0x8000; chip->ac97->regs[AC97_MASTER] = val; @@ -1929,26 +1930,31 @@ static void es1968_update_hw_volume(unsi outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX); snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id); - } else { - val &= 0x7fff; - if (((x>>1) & 7) > 4) { - /* volume up */ - if ((val & 0xff) > 0) - val--; - if ((val & 0xff00) > 0) - val -= 0x0100; - } else { - /* volume down */ - if ((val & 0xff) < 0x1f) - val++; - if ((val & 0xff00) < 0x1f00) - val += 0x0100; - } + break; + case 0xaa: + /* volume up */ + if ((val & 0x7f) > 0) + val--; + if ((val & 0x7f00) > 0) + val -= 0x0100; + chip->ac97->regs[AC97_MASTER] = val; + outw(val, chip->io_port + ESM_AC97_DATA); + outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->master_volume->id); + break; + case 0x66: + /* volume down */ + if ((val & 0x7f) < 0x1f) + val++; + if ((val & 0x7f00) < 0x1f00) + val += 0x0100; chip->ac97->regs[AC97_MASTER] = val; outw(val, chip->io_port + ESM_AC97_DATA); outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX); snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id); + break; } spin_unlock_irqrestore(&chip->ac97_lock, flags); } diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 13868c9..bdfda19 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 @@ -28,6 +29,7 @@ #include #include #include #include +#include #include #include #include @@ -54,6 +56,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 +161,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 +322,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, }; @@ -1052,6 +1054,13 @@ #define FM801_DOUBLE(xname, reg, shift_l { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_fm801_info_double, \ .get = snd_fm801_get_double, .put = snd_fm801_put_double, \ .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) } +#define FM801_DOUBLE_TLV(xname, reg, shift_left, shift_right, mask, invert, xtlv) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ + .name = xname, .info = snd_fm801_info_double, \ + .get = snd_fm801_get_double, .put = snd_fm801_put_double, \ + .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24), \ + .tlv = { .p = (xtlv) } } static int snd_fm801_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -1148,14 +1157,19 @@ static int snd_fm801_put_mux(struct snd_ return snd_fm801_update_bits(chip, FM801_REC_SRC, 7, val); } +static DECLARE_TLV_DB_SCALE(db_scale_dsp, -3450, 150, 0); + #define FM801_CONTROLS ARRAY_SIZE(snd_fm801_controls) static struct snd_kcontrol_new snd_fm801_controls[] __devinitdata = { -FM801_DOUBLE("Wave Playback Volume", FM801_PCM_VOL, 0, 8, 31, 1), +FM801_DOUBLE_TLV("Wave Playback Volume", FM801_PCM_VOL, 0, 8, 31, 1, + db_scale_dsp), FM801_SINGLE("Wave Playback Switch", FM801_PCM_VOL, 15, 1, 1), -FM801_DOUBLE("I2S Playback Volume", FM801_I2S_VOL, 0, 8, 31, 1), +FM801_DOUBLE_TLV("I2S Playback Volume", FM801_I2S_VOL, 0, 8, 31, 1, + db_scale_dsp), FM801_SINGLE("I2S Playback Switch", FM801_I2S_VOL, 15, 1, 1), -FM801_DOUBLE("FM Playback Volume", FM801_FM_VOL, 0, 8, 31, 1), +FM801_DOUBLE_TLV("FM Playback Volume", FM801_FM_VOL, 0, 8, 31, 1, + db_scale_dsp), FM801_SINGLE("FM Playback Switch", FM801_FM_VOL, 15, 1, 1), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1253,6 +1267,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 +1307,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 +1317,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 +1387,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 +1419,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 +1464,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 +1493,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_generic.c b/sound/pci/hda/hda_generic.c index 85ad164..97e9af1 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -46,11 +46,18 @@ struct hda_gnode { }; /* patch-specific record */ + +#define MAX_PCM_VOLS 2 +struct pcm_vol { + struct hda_gnode *node; /* Node for PCM volume */ + unsigned int index; /* connection of PCM volume */ +}; + struct hda_gspec { struct hda_gnode *dac_node[2]; /* DAC node */ struct hda_gnode *out_pin_node[2]; /* Output pin (Line-Out) node */ - struct hda_gnode *pcm_vol_node[2]; /* Node for PCM volume */ - unsigned int pcm_vol_index[2]; /* connection of PCM volume */ + struct pcm_vol pcm_vol[MAX_PCM_VOLS]; /* PCM volumes */ + unsigned int pcm_vol_nodes; /* number of PCM volumes */ struct hda_gnode *adc_node; /* ADC node */ struct hda_gnode *cap_vol_node; /* Node for capture volume */ @@ -285,9 +292,11 @@ static int parse_output_path(struct hda_ return node == spec->dac_node[dac_idx]; } spec->dac_node[dac_idx] = node; - if (node->wid_caps & AC_WCAP_OUT_AMP) { - spec->pcm_vol_node[dac_idx] = node; - spec->pcm_vol_index[dac_idx] = 0; + if ((node->wid_caps & AC_WCAP_OUT_AMP) && + spec->pcm_vol_nodes < MAX_PCM_VOLS) { + spec->pcm_vol[spec->pcm_vol_nodes].node = node; + spec->pcm_vol[spec->pcm_vol_nodes].index = 0; + spec->pcm_vol_nodes++; } return 1; /* found */ } @@ -307,13 +316,16 @@ static int parse_output_path(struct hda_ select_input_connection(codec, node, i); unmute_input(codec, node, i); unmute_output(codec, node); - if (! spec->pcm_vol_node[dac_idx]) { - if (node->wid_caps & AC_WCAP_IN_AMP) { - spec->pcm_vol_node[dac_idx] = node; - spec->pcm_vol_index[dac_idx] = i; - } else if (node->wid_caps & AC_WCAP_OUT_AMP) { - spec->pcm_vol_node[dac_idx] = node; - spec->pcm_vol_index[dac_idx] = 0; + if (spec->dac_node[dac_idx] && + spec->pcm_vol_nodes < MAX_PCM_VOLS && + !(spec->dac_node[dac_idx]->wid_caps & + AC_WCAP_OUT_AMP)) { + if ((node->wid_caps & AC_WCAP_IN_AMP) || + (node->wid_caps & AC_WCAP_OUT_AMP)) { + int n = spec->pcm_vol_nodes; + spec->pcm_vol[n].node = node; + spec->pcm_vol[n].index = i; + spec->pcm_vol_nodes++; } } return 1; @@ -370,7 +382,9 @@ static struct hda_gnode *parse_output_ja /* set PIN-Out enable */ snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN); + AC_PINCTL_OUT_EN | + ((node->pin_caps & AC_PINCAP_HP_DRV) ? + AC_PINCTL_HP_EN : 0)); return node; } } @@ -461,14 +475,19 @@ static const char *get_input_type(struct return "Front Line"; return "Line"; case AC_JACK_CD: +#if 0 if (pinctl) *pinctl |= AC_PINCTL_VREF_GRD; +#endif return "CD"; case AC_JACK_AUX: if ((location & 0x0f) == AC_JACK_LOC_FRONT) return "Front Aux"; return "Aux"; case AC_JACK_MIC_IN: + if (node->pin_caps & + (AC_PINCAP_VREF_80 << AC_PINCAP_VREF_SHIFT)) + *pinctl |= AC_PINCTL_VREF_80; if ((location & 0x0f) == AC_JACK_LOC_FRONT) return "Front Mic"; return "Mic"; @@ -556,6 +575,29 @@ static int parse_adc_sub_nodes(struct hd return 1; /* found */ } +/* add a capture source element */ +static void add_cap_src(struct hda_gspec *spec, int idx) +{ + struct hda_input_mux_item *csrc; + char *buf; + int num, ocap; + + num = spec->input_mux.num_items; + csrc = &spec->input_mux.items[num]; + buf = spec->cap_labels[num]; + for (ocap = 0; ocap < num; ocap++) { + if (! strcmp(buf, spec->cap_labels[ocap])) { + /* same label already exists, + * put the index number to be unique + */ + sprintf(buf, "%s %d", spec->cap_labels[ocap], num); + break; + } + } + csrc->index = idx; + spec->input_mux.num_items++; +} + /* * parse input */ @@ -576,28 +618,26 @@ static int parse_input_path(struct hda_c * if it reaches to a proper input PIN, add the path as the * input path. */ + /* first, check the direct connections to PIN widgets */ for (i = 0; i < adc_node->nconns; i++) { node = hda_get_node(spec, adc_node->conn_list[i]); - if (! node) - continue; - err = parse_adc_sub_nodes(codec, spec, node); - if (err < 0) - return err; - else if (err > 0) { - struct hda_input_mux_item *csrc = &spec->input_mux.items[spec->input_mux.num_items]; - char *buf = spec->cap_labels[spec->input_mux.num_items]; - int ocap; - for (ocap = 0; ocap < spec->input_mux.num_items; ocap++) { - if (! strcmp(buf, spec->cap_labels[ocap])) { - /* same label already exists, - * put the index number to be unique - */ - sprintf(buf, "%s %d", spec->cap_labels[ocap], - spec->input_mux.num_items); - } - } - csrc->index = i; - spec->input_mux.num_items++; + if (node && node->type == AC_WID_PIN) { + err = parse_adc_sub_nodes(codec, spec, node); + if (err < 0) + return err; + else if (err > 0) + add_cap_src(spec, i); + } + } + /* ... then check the rests, more complicated connections */ + for (i = 0; i < adc_node->nconns; i++) { + node = hda_get_node(spec, adc_node->conn_list[i]); + if (node && node->type != AC_WID_PIN) { + err = parse_adc_sub_nodes(codec, spec, node); + if (err < 0) + return err; + else if (err > 0) + add_cap_src(spec, i); } } @@ -647,9 +687,6 @@ static int parse_input(struct hda_codec /* * create mixer controls if possible */ -#define DIR_OUT 0x1 -#define DIR_IN 0x2 - static int create_mixer(struct hda_codec *codec, struct hda_gnode *node, unsigned int index, const char *type, const char *dir_sfx) { @@ -722,49 +759,97 @@ static int check_existing_control(struct /* * build output mixer controls */ -static int build_output_controls(struct hda_codec *codec) +static int create_output_mixers(struct hda_codec *codec, const char **names) { struct hda_gspec *spec = codec->spec; - static const char *types[2] = { "Master", "Headphone" }; int i, err; - for (i = 0; i < 2 && spec->pcm_vol_node[i]; i++) { - err = create_mixer(codec, spec->pcm_vol_node[i], - spec->pcm_vol_index[i], - types[i], "Playback"); + for (i = 0; i < spec->pcm_vol_nodes; i++) { + err = create_mixer(codec, spec->pcm_vol[i].node, + spec->pcm_vol[i].index, + names[i], "Playback"); if (err < 0) return err; } return 0; } +static int build_output_controls(struct hda_codec *codec) +{ + struct hda_gspec *spec = codec->spec; + static const char *types_speaker[] = { "Speaker", "Headphone" }; + static const char *types_line[] = { "Front", "Headphone" }; + + switch (spec->pcm_vol_nodes) { + case 1: + return create_mixer(codec, spec->pcm_vol[0].node, + spec->pcm_vol[0].index, + "Master", "Playback"); + case 2: + if (defcfg_type(spec->out_pin_node[0]) == AC_JACK_SPEAKER) + return create_output_mixers(codec, types_speaker); + else + return create_output_mixers(codec, types_line); + } + return 0; +} + /* create capture volume/switch */ static int build_input_controls(struct hda_codec *codec) { struct hda_gspec *spec = codec->spec; struct hda_gnode *adc_node = spec->adc_node; - int err; - - if (! adc_node) + int i, err; + static struct snd_kcontrol_new cap_sel = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = capture_source_info, + .get = capture_source_get, + .put = capture_source_put, + }; + + if (! adc_node || ! spec->input_mux.num_items) return 0; /* not found */ + spec->cur_cap_src = 0; + select_input_connection(codec, adc_node, + spec->input_mux.items[0].index); + /* create capture volume and switch controls if the ADC has an amp */ - err = create_mixer(codec, adc_node, 0, NULL, "Capture"); + /* do we have only a single item? */ + if (spec->input_mux.num_items == 1) { + err = create_mixer(codec, adc_node, + spec->input_mux.items[0].index, + NULL, "Capture"); + if (err < 0) + return err; + return 0; + } /* create input MUX if multiple sources are available */ - if (spec->input_mux.num_items > 1) { - static struct snd_kcontrol_new cap_sel = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = capture_source_info, - .get = capture_source_get, - .put = capture_source_put, - }; - if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&cap_sel, codec))) < 0) + if ((err = snd_ctl_add(codec->bus->card, + snd_ctl_new1(&cap_sel, codec))) < 0) + return err; + + /* no volume control? */ + if (! (adc_node->wid_caps & AC_WCAP_IN_AMP) || + ! (adc_node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) + return 0; + + for (i = 0; i < spec->input_mux.num_items; i++) { + struct snd_kcontrol_new knew; + char name[32]; + sprintf(name, "%s Capture Volume", + spec->input_mux.items[i].label); + knew = (struct snd_kcontrol_new) + HDA_CODEC_VOLUME(name, adc_node->nid, + spec->input_mux.items[i].index, + HDA_INPUT); + if ((err = snd_ctl_add(codec->bus->card, + snd_ctl_new1(&knew, codec))) < 0) return err; - spec->cur_cap_src = 0; - select_input_connection(codec, adc_node, spec->input_mux.items[0].index); } + return 0; } diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 79d63c9..cc50d13 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -55,6 +55,7 @@ static char *model; static int position_fix; static int probe_mask = -1; static int single_cmd; +static int disable_msi; module_param(index, int, 0444); MODULE_PARM_DESC(index, "Index value for Intel HD audio interface."); @@ -68,6 +69,8 @@ module_param(probe_mask, int, 0444); MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1)."); module_param(single_cmd, bool, 0444); MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs (for debugging only)."); +module_param(disable_msi, int, 0); +MODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)"); /* just for backward compatibility */ @@ -252,7 +255,7 @@ #define NVIDIA_HDA_ENABLE_COHBITS 0x struct azx_dev { u32 *bdl; /* virtual address of the BDL */ dma_addr_t bdl_addr; /* physical address of the BDL */ - volatile u32 *posbuf; /* position buffer pointer */ + u32 *posbuf; /* position buffer pointer */ unsigned int bufsize; /* size of the play buffer in bytes */ unsigned int fragsize; /* size of each period in bytes */ @@ -271,8 +274,8 @@ struct azx_dev { /* for sanity check of position buffer */ unsigned int period_intr; - unsigned int opened: 1; - unsigned int running: 1; + unsigned int opened :1; + unsigned int running :1; }; /* CORB/RIRB */ @@ -330,8 +333,9 @@ struct azx { /* flags */ int position_fix; - unsigned int initialized: 1; - unsigned int single_cmd: 1; + unsigned int initialized :1; + unsigned int single_cmd :1; + unsigned int polling_mode :1; }; /* driver types */ @@ -518,8 +522,23 @@ static unsigned int azx_rirb_get_respons struct azx *chip = codec->bus->private_data; int timeout = 50; - while (chip->rirb.cmds) { + for (;;) { + if (chip->polling_mode) { + spin_lock_irq(&chip->reg_lock); + azx_update_rirb(chip); + spin_unlock_irq(&chip->reg_lock); + } + if (! chip->rirb.cmds) + break; if (! --timeout) { + if (! chip->polling_mode) { + snd_printk(KERN_WARNING "hda_intel: " + "azx_get_response timeout, " + "switching to polling mode...\n"); + chip->polling_mode = 1; + timeout = 50; + continue; + } snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, " "switching to single_cmd mode...\n"); @@ -642,14 +661,14 @@ static int azx_reset(struct azx *chip) azx_writeb(chip, GCTL, azx_readb(chip, GCTL) | ICH6_GCTL_RESET); count = 50; - while (! azx_readb(chip, GCTL) && --count) + while (!azx_readb(chip, GCTL) && --count) msleep(1); - /* Brent Chartrand said to wait >= 540us for codecs to intialize */ + /* Brent Chartrand said to wait >= 540us for codecs to initialize */ msleep(1); /* check to see if controller is ready */ - if (! azx_readb(chip, GCTL)) { + if (!azx_readb(chip, GCTL)) { snd_printd("azx_reset: controller not ready!\n"); return -EBUSY; } @@ -658,7 +677,7 @@ static int azx_reset(struct azx *chip) azx_writel(chip, GCTL, azx_readl(chip, GCTL) | ICH6_GCTL_UREN); /* detect codecs */ - if (! chip->codec_mask) { + if (!chip->codec_mask) { chip->codec_mask = azx_readw(chip, STATESTS); snd_printdd("codec_mask = 0x%x\n", chip->codec_mask); } @@ -766,7 +785,7 @@ static void azx_init_chip(struct azx *ch azx_int_enable(chip); /* initialize the codec command I/O */ - if (! chip->single_cmd) + if (!chip->single_cmd) azx_init_cmd_io(chip); /* program the position buffer */ @@ -794,7 +813,7 @@ static void azx_init_chip(struct azx *ch /* * interrupt handler */ -static irqreturn_t azx_interrupt(int irq, void* dev_id, struct pt_regs *regs) +static irqreturn_t azx_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct azx *chip = dev_id; struct azx_dev *azx_dev; @@ -999,8 +1018,9 @@ static struct snd_pcm_hardware azx_pcm_h .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE /*|*/ - /*SNDRV_PCM_INFO_RESUME*/), + /* No full-resume yet implemented */ + /* SNDRV_PCM_INFO_RESUME |*/ + SNDRV_PCM_INFO_PAUSE), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_48000, .rate_min = 48000, @@ -1178,7 +1198,7 @@ static snd_pcm_uframes_t azx_pcm_pointer if (chip->position_fix == POS_FIX_POSBUF || chip->position_fix == POS_FIX_AUTO) { /* use the position buffer */ - pos = *azx_dev->posbuf; + pos = le32_to_cpu(*azx_dev->posbuf); if (chip->position_fix == POS_FIX_AUTO && azx_dev->period_intr == 1 && ! pos) { printk(KERN_WARNING @@ -1326,7 +1346,7 @@ static int __devinit azx_init_stream(str struct azx_dev *azx_dev = &chip->azx_dev[i]; azx_dev->bdl = (u32 *)(chip->bdl.area + off); azx_dev->bdl_addr = chip->bdl.addr + off; - azx_dev->posbuf = (volatile u32 *)(chip->posbuf.area + i * 8); + azx_dev->posbuf = (u32 __iomem *)(chip->posbuf.area + i * 8); /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80); /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */ @@ -1398,12 +1418,13 @@ static int azx_free(struct azx *chip) azx_writel(chip, DPLBASE, 0); azx_writel(chip, DPUBASE, 0); - /* wait a little for interrupts to finish */ - msleep(1); + synchronize_irq(chip->irq); } - if (chip->irq >= 0) + if (chip->irq >= 0) { + pci_disable_msi(chip->pci); free_irq(chip->irq, (void*)chip); + } if (chip->remap_addr) iounmap(chip->remap_addr); @@ -1434,19 +1455,19 @@ static int __devinit azx_create(struct s struct azx **rchip) { struct azx *chip; - int err = 0; + int err; static struct snd_device_ops ops = { .dev_free = azx_dev_free, }; *rchip = NULL; - if ((err = pci_enable_device(pci)) < 0) + err = pci_enable_device(pci); + if (err < 0) return err; chip = kzalloc(sizeof(*chip), GFP_KERNEL); - - if (NULL == chip) { + if (!chip) { snd_printk(KERN_ERR SFX "cannot allocate chip\n"); pci_disable_device(pci); return -ENOMEM; @@ -1472,13 +1493,14 @@ #if BITS_PER_LONG != 64 } #endif - if ((err = pci_request_regions(pci, "ICH HD audio")) < 0) { + err = pci_request_regions(pci, "ICH HD audio"); + if (err < 0) { kfree(chip); pci_disable_device(pci); return err; } - chip->addr = pci_resource_start(pci,0); + chip->addr = pci_resource_start(pci, 0); chip->remap_addr = ioremap_nocache(chip->addr, pci_resource_len(pci,0)); if (chip->remap_addr == NULL) { snd_printk(KERN_ERR SFX "ioremap error\n"); @@ -1486,6 +1508,9 @@ #endif goto errout; } + if (!disable_msi) + pci_enable_msi(pci); + if (request_irq(pci->irq, azx_interrupt, IRQF_DISABLED|IRQF_SHARED, "HDA Intel", (void*)chip)) { snd_printk(KERN_ERR SFX "unable to grab IRQ %d\n", pci->irq); @@ -1519,7 +1544,7 @@ #endif } chip->num_streams = chip->playback_streams + chip->capture_streams; chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev), GFP_KERNEL); - if (! chip->azx_dev) { + if (!chip->azx_dev) { snd_printk(KERN_ERR "cannot malloc azx_dev\n"); goto errout; } @@ -1550,7 +1575,7 @@ #endif chip->initialized = 1; /* codec detection */ - if (! chip->codec_mask) { + if (!chip->codec_mask) { snd_printk(KERN_ERR SFX "no codecs found!\n"); err = -ENODEV; goto errout; @@ -1577,16 +1602,16 @@ static int __devinit azx_probe(struct pc { struct snd_card *card; struct azx *chip; - int err = 0; + int err; card = snd_card_new(index, id, THIS_MODULE, 0); - if (NULL == card) { + if (!card) { snd_printk(KERN_ERR SFX "Error creating card!\n"); return -ENOMEM; } - if ((err = azx_create(card, pci, pci_id->driver_data, - &chip)) < 0) { + err = azx_create(card, pci, pci_id->driver_data, &chip); + if (err < 0) { snd_card_free(card); return err; } 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..6590381 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, }; @@ -108,6 +111,7 @@ enum { ALC883_3ST_6ch, ALC883_6ST_DIG, ALC888_DEMO_BOARD, + ALC883_ACER, ALC883_AUTO, ALC883_MODEL_LAST, }; @@ -153,6 +157,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 +195,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 +268,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 +553,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 +1358,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 +2144,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 +2162,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 +2174,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 +2245,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 +2290,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 +2301,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 +2394,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 +2406,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 +2418,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 +2430,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 +2441,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 +2453,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 +2465,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 +3936,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 +4303,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 +4441,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 +4458,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 +4472,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 +5056,23 @@ #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 = "acer", .config = ALC883_ACER }, + { .pci_subvendor = 0x1025, .pci_subdevice = 0/*0x0102*/, + .config = ALC883_ACER }, { .modelname = "auto", .config = ALC883_AUTO }, {} }; @@ -5038,6 +5102,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 +5114,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] = { @@ -5077,6 +5143,23 @@ static struct alc_config_preset alc883_p .channel_mode = alc883_sixstack_modes, .input_mux = &alc883_capture_source, }, + [ALC883_ACER] = { + .mixers = { alc883_base_mixer, + alc883_chmode_mixer }, + /* On TravelMate laptops, GPIO 0 enables the internal speaker + * and the headphone jack. Turn this on and rely on the + * standard mute methods whenever the user wants to turn + * these outputs off. + */ + .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .adc_nids = alc883_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + }, }; @@ -5217,8 +5300,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 +5566,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 +5585,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 +5862,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 +5871,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 +5911,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 +6049,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 +6144,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 +6313,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 +6703,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 +6723,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 +6736,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 +6760,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..bcbbe11 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -42,9 +42,10 @@ #define STAC_REF 0 #define STAC_D945GTP3 1 #define STAC_D945GTP5 2 #define STAC_MACMINI 3 -#define STAC_D965_2112 4 -#define STAC_D965_284B 5 -#define STAC_922X_MODELS 6 /* number of 922x models */ +#define STAC_922X_MODELS 4 /* number of 922x models */ +#define STAC_D965_3ST 4 +#define STAC_D965_5ST 5 +#define STAC_927X_MODELS 6 /* number of 922x models */ struct sigmatel_spec { struct snd_kcontrol_new *mixers[4]; @@ -73,6 +74,7 @@ struct sigmatel_spec { hda_nid_t *pin_nids; unsigned int num_pins; unsigned int *pin_configs; + unsigned int *bios_pin_configs; /* codec specific stuff */ struct hda_verb *init; @@ -110,24 +112,10 @@ static hda_nid_t stac922x_adc_nids[2] = 0x06, 0x07, }; -static hda_nid_t stac9227_adc_nids[2] = { - 0x07, 0x08, -}; - -#if 0 -static hda_nid_t d965_2112_dac_nids[3] = { - 0x02, 0x03, 0x05, -}; -#endif - static hda_nid_t stac922x_mux_nids[2] = { 0x12, 0x13, }; -static hda_nid_t stac9227_mux_nids[2] = { - 0x15, 0x16, -}; - static hda_nid_t stac927x_adc_nids[3] = { 0x07, 0x08, 0x09 }; @@ -136,8 +124,17 @@ 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, + 0x08, 0x09, 0x0d, 0x0e, + 0x0f, 0x10, 0x11, 0x12, }; static hda_nid_t stac922x_pin_nids[10] = { @@ -151,6 +148,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); @@ -190,25 +194,23 @@ static struct hda_verb stac922x_core_ini {} }; -static struct hda_verb stac9227_core_init[] = { +static struct hda_verb d965_core_init[] = { /* set master volume and direct control */ - { 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, /* unmute node 0x1b */ { 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* select node 0x03 as DAC */ + { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x01}, {} }; -static struct hda_verb d965_2112_core_init[] = { +static struct hda_verb stac927x_core_init[] = { /* set master volume and direct control */ - { 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - /* unmute node 0x1b */ - { 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* select node 0x03 as DAC */ - { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, {} }; -static struct hda_verb stac927x_core_init[] = { +static struct hda_verb stac9205_core_init[] = { /* set master volume and direct control */ { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, {} @@ -277,6 +279,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; @@ -341,38 +358,67 @@ static unsigned int d945gtp5_pin_configs 0x02a19320, 0x40000100, }; -static unsigned int d965_2112_pin_configs[10] = { - 0x0221401f, 0x40000100, 0x40000100, 0x01014011, - 0x01a19021, 0x01813024, 0x01452130, 0x40000100, - 0x02a19320, 0x40000100, -}; - static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = { [STAC_REF] = ref922x_pin_configs, [STAC_D945GTP3] = d945gtp3_pin_configs, [STAC_D945GTP5] = d945gtp5_pin_configs, [STAC_MACMINI] = d945gtp5_pin_configs, - [STAC_D965_2112] = d965_2112_pin_configs, }; static struct hda_board_config stac922x_cfg_tbl[] = { + { .modelname = "5stack", .config = STAC_D945GTP5 }, + { .modelname = "3stack", .config = STAC_D945GTP3 }, { .modelname = "ref", .pci_subvendor = PCI_VENDOR_ID_INTEL, .pci_subdevice = 0x2668, /* DFI LanParty */ .config = STAC_REF }, /* SigmaTel reference board */ + /* Intel 945G based systems */ { .pci_subvendor = PCI_VENDOR_ID_INTEL, .pci_subdevice = 0x0101, .config = STAC_D945GTP3 }, /* Intel D945GTP - 3 Stack */ { .pci_subvendor = PCI_VENDOR_ID_INTEL, .pci_subdevice = 0x0202, - .config = STAC_D945GTP3 }, /* Intel D945GNT - 3 Stack, 9221 A1 */ + .config = STAC_D945GTP3 }, /* Intel D945GNT - 3 Stack */ { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x0b0b, - .config = STAC_D945GTP3 }, /* Intel D945PSN - 3 Stack, 9221 A1 */ + .pci_subdevice = 0x0606, + .config = STAC_D945GTP3 }, /* Intel D945GTP - 3 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x0601, + .config = STAC_D945GTP3 }, /* Intel D945GTP - 3 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x0111, + .config = STAC_D945GTP3 }, /* Intel D945GZP - 3 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x1115, + .config = STAC_D945GTP3 }, /* Intel D945GPM - 3 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x1116, + .config = STAC_D945GTP3 }, /* Intel D945GBO - 3 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x1117, + .config = STAC_D945GTP3 }, /* Intel D945GPM - 3 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x1118, + .config = STAC_D945GTP3 }, /* Intel D945GPM - 3 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x1119, + .config = STAC_D945GTP3 }, /* Intel D945GPM - 3 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x8826, + .config = STAC_D945GTP3 }, /* Intel D945GPM - 3 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x5049, + .config = STAC_D945GTP3 }, /* Intel D945GCZ - 3 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x5055, + .config = STAC_D945GTP3 }, /* Intel D945GCZ - 3 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x5048, + .config = STAC_D945GTP3 }, /* Intel D945GPB - 3 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x0110, + .config = STAC_D945GTP3 }, /* Intel D945GLR - 3 Stack */ { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x0707, - .config = STAC_D945GTP5 }, /* Intel D945PSV - 5 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, .pci_subdevice = 0x0404, .config = STAC_D945GTP5 }, /* Intel D945GTP - 5 Stack */ { .pci_subvendor = PCI_VENDOR_ID_INTEL, @@ -384,44 +430,214 @@ static struct hda_board_config stac922x_ { .pci_subvendor = PCI_VENDOR_ID_INTEL, .pci_subdevice = 0x0417, .config = STAC_D945GTP5 }, /* Intel D975XBK - 5 Stack */ + /* Intel 945P based systems */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x0b0b, + .config = STAC_D945GTP3 }, /* Intel D945PSN - 3 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x0112, + .config = STAC_D945GTP3 }, /* Intel D945PLN - 3 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x0d0d, + .config = STAC_D945GTP3 }, /* Intel D945PLM - 3 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x0909, + .config = STAC_D945GTP3 }, /* Intel D945PAW - 3 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x0505, + .config = STAC_D945GTP3 }, /* Intel D945PLM - 3 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x0707, + .config = STAC_D945GTP5 }, /* Intel D945PSV - 5 Stack */ + /* other systems */ { .pci_subvendor = 0x8384, .pci_subdevice = 0x7680, .config = STAC_MACMINI }, /* Apple Mac Mini (early 2006) */ + {} /* terminator */ +}; + +static unsigned int ref927x_pin_configs[14] = { + 0x02214020, 0x02a19080, 0x0181304e, 0x01014010, + 0x01a19040, 0x01011012, 0x01016011, 0x0101201f, + 0x183301f0, 0x18a001f0, 0x18a001f0, 0x01442070, + 0x01c42190, 0x40000100, +}; + +static unsigned int d965_3st_pin_configs[14] = { + 0x0221401f, 0x02a19120, 0x40000100, 0x01014011, + 0x01a19021, 0x01813024, 0x40000100, 0x40000100, + 0x40000100, 0x40000100, 0x40000100, 0x40000100, + 0x40000100, 0x40000100 +}; + +static unsigned int d965_5st_pin_configs[14] = { + 0x02214020, 0x02a19080, 0x0181304e, 0x01014010, + 0x01a19040, 0x01011012, 0x01016011, 0x40000100, + 0x40000100, 0x40000100, 0x40000100, 0x01442070, + 0x40000100, 0x40000100 +}; + +static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = { + [STAC_REF] = ref927x_pin_configs, + [STAC_D965_3ST] = d965_3st_pin_configs, + [STAC_D965_5ST] = d965_5st_pin_configs, +}; + +static struct hda_board_config stac927x_cfg_tbl[] = { + { .modelname = "5stack", .config = STAC_D965_5ST }, + { .modelname = "3stack", .config = STAC_D965_3ST }, + { .modelname = "ref", + .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2668, /* DFI LanParty */ + .config = STAC_REF }, /* SigmaTel reference board */ + /* Intel 946 based systems */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x3d01, + .config = STAC_D965_3ST }, /* D946 configuration */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0xa301, + .config = STAC_D965_3ST }, /* Intel D946GZT - 3 stack */ + /* 965 based 3 stack systems */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2116, + .config = STAC_D965_3ST }, /* Intel D965 3Stack config */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2115, + .config = STAC_D965_3ST }, /* Intel DQ965WC - 3 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2114, + .config = STAC_D965_3ST }, /* Intel D965 3Stack config */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2113, + .config = STAC_D965_3ST }, /* Intel D965 3Stack config */ { .pci_subvendor = PCI_VENDOR_ID_INTEL, .pci_subdevice = 0x2112, - .config = STAC_D965_2112 }, + .config = STAC_D965_3ST }, /* Intel DG965MS - 3 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2111, + .config = STAC_D965_3ST }, /* Intel D965 3Stack config */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2110, + .config = STAC_D965_3ST }, /* Intel D965 3Stack config */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2009, + .config = STAC_D965_3ST }, /* Intel D965 3Stack config */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2008, + .config = STAC_D965_3ST }, /* Intel DQ965GF - 3 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2007, + .config = STAC_D965_3ST }, /* Intel D965 3Stack config */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2006, + .config = STAC_D965_3ST }, /* Intel D965 3Stack config */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2005, + .config = STAC_D965_3ST }, /* Intel D965 3Stack config */ { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x284b, - .config = STAC_D965_284B }, + .pci_subdevice = 0x2004, + .config = STAC_D965_3ST }, /* Intel D965 3Stack config */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2003, + .config = STAC_D965_3ST }, /* Intel D965 3Stack config */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2002, + .config = STAC_D965_3ST }, /* Intel D965 3Stack config */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2001, + .config = STAC_D965_3ST }, /* Intel DQ965GF - 3 Stack */ + /* 965 based 5 stack systems */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2301, + .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2302, + .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2303, + .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2304, + .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2305, + .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2501, + .config = STAC_D965_5ST }, /* Intel DG965MQ - 5 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2502, + .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2503, + .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2504, + .config = STAC_D965_5ST }, /* Intel DQ965GF - 5 Stack */ {} /* terminator */ }; -static unsigned int ref927x_pin_configs[14] = { - 0x01813122, 0x01a19021, 0x01014010, 0x01016011, - 0x01012012, 0x01011014, 0x40000100, 0x40000100, - 0x40000100, 0x40000100, 0x40000100, 0x01441030, - 0x01c41030, 0x40000100, +static unsigned int ref9205_pin_configs[12] = { + 0x40000100, 0x40000100, 0x01016011, 0x01014010, + 0x01813122, 0x01a19021, 0x40000100, 0x40000100, + 0x40000100, 0x40000100, 0x01441030, 0x01c41030 }; -static unsigned int *stac927x_brd_tbl[] = { - ref927x_pin_configs, +static unsigned int *stac9205_brd_tbl[] = { + ref9205_pin_configs, }; -static struct hda_board_config stac927x_cfg_tbl[] = { +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 */ + /* Dell laptops have BIOS problem */ + { .pci_subvendor = PCI_VENDOR_ID_DELL, .pci_subdevice = 0x01b5, + .config = STAC_REF }, /* Dell Inspiron 630m */ + { .pci_subvendor = PCI_VENDOR_ID_DELL, .pci_subdevice = 0x01c2, + .config = STAC_REF }, /* Dell Latitude D620 */ + { .pci_subvendor = PCI_VENDOR_ID_DELL, .pci_subdevice = 0x01cb, + .config = STAC_REF }, /* Dell Latitude 120L */ {} /* terminator */ }; +static int stac92xx_save_bios_config_regs(struct hda_codec *codec) +{ + int i; + struct sigmatel_spec *spec = codec->spec; + + if (! spec->bios_pin_configs) { + spec->bios_pin_configs = kcalloc(spec->num_pins, + sizeof(*spec->bios_pin_configs), GFP_KERNEL); + if (! spec->bios_pin_configs) + return -ENOMEM; + } + + for (i = 0; i < spec->num_pins; i++) { + hda_nid_t nid = spec->pin_nids[i]; + unsigned int pin_cfg; + + pin_cfg = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONFIG_DEFAULT, 0x00); + snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x bios pin config %8.8x\n", + nid, pin_cfg); + spec->bios_pin_configs[i] = pin_cfg; + } + + return 0; +} + static void stac92xx_set_config_regs(struct hda_codec *codec) { int i; struct sigmatel_spec *spec = codec->spec; unsigned int pin_cfg; - for (i=0; i < spec->num_pins; i++) { + if (! spec->pin_nids || ! spec->pin_configs) + return; + + for (i = 0; i < spec->num_pins; i++) { snd_hda_codec_write(codec, spec->pin_nids[i], 0, AC_VERB_SET_CONFIG_DEFAULT_BYTES_0, spec->pin_configs[i] & 0x000000ff); @@ -1007,6 +1223,66 @@ static int stac9200_auto_create_hp_ctls( return 0; } +/* add playback controls for LFE output */ +static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec, + struct auto_pin_cfg *cfg) +{ + struct sigmatel_spec *spec = codec->spec; + int err; + hda_nid_t lfe_pin = 0x0; + int i; + + /* + * search speaker outs and line outs for a mono speaker pin + * with an amp. If one is found, add LFE controls + * for it. + */ + for (i = 0; i < spec->autocfg.speaker_outs && lfe_pin == 0x0; i++) { + hda_nid_t pin = spec->autocfg.speaker_pins[i]; + unsigned long wcaps = get_wcaps(codec, pin); + wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP); + if (wcaps == AC_WCAP_OUT_AMP) + /* found a mono speaker with an amp, must be lfe */ + lfe_pin = pin; + } + + /* if speaker_outs is 0, then speakers may be in line_outs */ + if (lfe_pin == 0 && spec->autocfg.speaker_outs == 0) { + for (i = 0; i < spec->autocfg.line_outs && lfe_pin == 0x0; i++) { + hda_nid_t pin = spec->autocfg.line_out_pins[i]; + unsigned long cfg; + cfg = snd_hda_codec_read(codec, pin, 0, + AC_VERB_GET_CONFIG_DEFAULT, + 0x00); + if (get_defcfg_device(cfg) == AC_JACK_SPEAKER) { + unsigned long wcaps = get_wcaps(codec, pin); + wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP); + if (wcaps == AC_WCAP_OUT_AMP) + /* found a mono speaker with an amp, + must be lfe */ + lfe_pin = pin; + } + } + } + + if (lfe_pin) { + err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, + "LFE Playback Volume", + HDA_COMPOSE_AMP_VAL(lfe_pin, 1, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, + "LFE Playback Switch", + HDA_COMPOSE_AMP_VAL(lfe_pin, 1, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } + + return 0; +} + static int stac9200_parse_auto_config(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -1021,6 +1297,9 @@ static int stac9200_parse_auto_config(st if ((err = stac9200_auto_create_hp_ctls(codec, &spec->autocfg)) < 0) return err; + if ((err = stac9200_auto_create_lfe_ctls(codec, &spec->autocfg)) < 0) + return err; + if (spec->autocfg.dig_out_pin) spec->multiout.dig_out_nid = 0x05; if (spec->autocfg.dig_in_pin) @@ -1089,6 +1368,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); @@ -1131,6 +1412,9 @@ static void stac92xx_free(struct hda_cod kfree(spec->kctl_alloc); } + if (spec->bios_pin_configs) + kfree(spec->bios_pin_configs); + kfree(spec); } @@ -1188,6 +1472,7 @@ static int stac92xx_resume(struct hda_co int i; stac92xx_init(codec); + stac92xx_set_config_regs(codec); for (i = 0; i < spec->num_mixers; i++) snd_hda_resume_ctls(codec, spec->mixers[i]); if (spec->multiout.dig_out_nid) @@ -1220,12 +1505,18 @@ static int patch_stac9200(struct hda_cod return -ENOMEM; codec->spec = spec; + spec->num_pins = 8; + spec->pin_nids = stac9200_pin_nids; spec->board_config = snd_hda_check_board_config(codec, stac9200_cfg_tbl); - if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9200, using BIOS defaults\n"); - else { - spec->num_pins = 8; - spec->pin_nids = stac9200_pin_nids; + if (spec->board_config < 0) { + snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9200, using BIOS defaults\n"); + err = stac92xx_save_bios_config_regs(codec); + if (err < 0) { + stac92xx_free(codec); + return err; + } + spec->pin_configs = spec->bios_pin_configs; + } else { spec->pin_configs = stac9200_brd_tbl[spec->board_config]; stac92xx_set_config_regs(codec); } @@ -1261,13 +1552,19 @@ static int patch_stac922x(struct hda_cod return -ENOMEM; codec->spec = spec; + spec->num_pins = 10; + spec->pin_nids = stac922x_pin_nids; spec->board_config = snd_hda_check_board_config(codec, stac922x_cfg_tbl); - if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, " - "using BIOS defaults\n"); - else if (stac922x_brd_tbl[spec->board_config] != NULL) { - spec->num_pins = 10; - spec->pin_nids = stac922x_pin_nids; + if (spec->board_config < 0) { + snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, " + "using BIOS defaults\n"); + err = stac92xx_save_bios_config_regs(codec); + if (err < 0) { + stac92xx_free(codec); + return err; + } + spec->pin_configs = spec->bios_pin_configs; + } else if (stac922x_brd_tbl[spec->board_config] != NULL) { spec->pin_configs = stac922x_brd_tbl[spec->board_config]; stac92xx_set_config_regs(codec); } @@ -1281,25 +1578,6 @@ static int patch_stac922x(struct hda_cod spec->multiout.dac_nids = spec->dac_nids; - switch (spec->board_config) { - case STAC_D965_2112: - spec->adc_nids = stac9227_adc_nids; - spec->mux_nids = stac9227_mux_nids; -#if 0 - spec->multiout.dac_nids = d965_2112_dac_nids; - spec->multiout.num_dacs = ARRAY_SIZE(d965_2112_dac_nids); -#endif - spec->init = d965_2112_core_init; - spec->mixer = stac9227_mixer; - break; - case STAC_D965_284B: - spec->adc_nids = stac9227_adc_nids; - spec->mux_nids = stac9227_mux_nids; - spec->init = stac9227_core_init; - spec->mixer = stac9227_mixer; - break; - } - err = stac92xx_parse_auto_config(codec, 0x08, 0x09); if (err < 0) { stac92xx_free(codec); @@ -1324,26 +1602,94 @@ static int patch_stac927x(struct hda_cod return -ENOMEM; codec->spec = spec; + spec->num_pins = 14; + spec->pin_nids = stac927x_pin_nids; spec->board_config = snd_hda_check_board_config(codec, stac927x_cfg_tbl); - if (spec->board_config < 0) + if (spec->board_config < 0) { snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC927x, using BIOS defaults\n"); - else { - spec->num_pins = 14; - spec->pin_nids = stac927x_pin_nids; + err = stac92xx_save_bios_config_regs(codec); + if (err < 0) { + stac92xx_free(codec); + return err; + } + spec->pin_configs = spec->bios_pin_configs; + } else if (stac927x_brd_tbl[spec->board_config] != NULL) { spec->pin_configs = stac927x_brd_tbl[spec->board_config]; stac92xx_set_config_regs(codec); } - spec->adc_nids = stac927x_adc_nids; - spec->mux_nids = stac927x_mux_nids; + switch (spec->board_config) { + case STAC_D965_3ST: + spec->adc_nids = stac927x_adc_nids; + spec->mux_nids = stac927x_mux_nids; + spec->num_muxes = 3; + spec->init = d965_core_init; + spec->mixer = stac9227_mixer; + break; + case STAC_D965_5ST: + spec->adc_nids = stac927x_adc_nids; + spec->mux_nids = stac927x_mux_nids; + spec->num_muxes = 3; + spec->init = d965_core_init; + spec->mixer = stac9227_mixer; + break; + default: + spec->adc_nids = stac927x_adc_nids; + spec->mux_nids = stac927x_mux_nids; + spec->num_muxes = 3; + spec->init = stac927x_core_init; + spec->mixer = stac927x_mixer; + } + + spec->multiout.dac_nids = spec->dac_nids; + + err = stac92xx_parse_auto_config(codec, 0x1e, 0x20); + if (err < 0) { + stac92xx_free(codec); + return err; + } + + codec->patch_ops = stac92xx_patch_ops; + + 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->num_pins = 14; + spec->pin_nids = stac9205_pin_nids; + 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"); + err = stac92xx_save_bios_config_regs(codec); + if (err < 0) { + stac92xx_free(codec); + return err; + } + spec->pin_configs = spec->bios_pin_configs; + } else { + 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 = stac927x_core_init; - spec->mixer = stac927x_mixer; + spec->init = stac9205_core_init; + spec->mixer = stac9205_mixer; spec->multiout.dac_nids = spec->dac_nids; - err = stac92xx_parse_auto_config(codec, 0x1e, 0x20); + err = stac92xx_parse_auto_config(codec, 0x1f, 0x20); if (err < 0) { stac92xx_free(codec); return err; @@ -1355,10 +1701,10 @@ static int patch_stac927x(struct hda_cod } /* - * STAC 7661(?) hack + * STAC9872 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*/ }; @@ -1389,6 +1735,23 @@ static struct hda_verb vaio_init[] = { {} }; +static struct hda_verb vaio_ar_init[] = { + {0x0a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP <- 0x2 */ + {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Speaker <- 0x5 */ + {0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */ + {0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */ +/* {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },*/ /* Optical Out */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x2}, /* mic-sel: 0a,0d,14,02 */ + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */ + {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */ +/* {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},*/ /* Optical Out */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* CD-in -> 0x6 */ + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Mic-in -> 0x9 */ + {} +}; + /* bind volumes of both NID 0x02 and 0x05 */ static int vaio_master_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1434,6 +1797,38 @@ 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), + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = snd_hda_mixer_amp_switch_info, + .get = snd_hda_mixer_amp_switch_get, + .put = vaio_master_sw_put, + .private_value = HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), + }, + /* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */ + HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .count = 1, + .info = stac92xx_mux_enum_info, + .get = stac92xx_mux_enum_get, + .put = stac92xx_mux_enum_put, + }, + {} +}; + +static struct snd_kcontrol_new vaio_ar_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Volume", + .info = snd_hda_mixer_amp_volume_info, + .get = snd_hda_mixer_amp_volume_get, + .put = vaio_master_vol_put, .private_value = HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), }, { @@ -1447,6 +1842,8 @@ static struct snd_kcontrol_new vaio_mixe /* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */ HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT), + /*HDA_CODEC_MUTE("Optical Out Switch", 0x10, 0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Optical Out Volume", 0x10, 0, HDA_OUTPUT),*/ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Capture Source", @@ -1458,7 +1855,7 @@ static struct snd_kcontrol_new vaio_mixe {} }; -static struct hda_codec_ops stac7661_patch_ops = { +static struct hda_codec_ops stac9872_patch_ops = { .build_controls = stac92xx_build_controls, .build_pcms = stac92xx_build_pcms, .init = stac92xx_init, @@ -1468,23 +1865,34 @@ #ifdef CONFIG_PM #endif }; -enum { STAC7661_VAIO }; - -static struct hda_board_config stac7661_cfg_tbl[] = { - { .modelname = "vaio", .config = STAC7661_VAIO }, +enum { /* FE and SZ series. id=0x83847661 and subsys=0x104D0700 or 104D1000. */ + CXD9872RD_VAIO, + /* Unknown. id=0x83847662 and subsys=0x104D1200 or 104D1000. */ + STAC9872AK_VAIO, + /* Unknown. id=0x83847661 and subsys=0x104D1200. */ + STAC9872K_VAIO, + /* AR Series. id=0x83847664 and subsys=104D1300 */ + CXD9872AKD_VAIO + }; + +static struct hda_board_config stac9872_cfg_tbl[] = { + { .modelname = "vaio", .config = CXD9872RD_VAIO }, + { .modelname = "vaio-ar", .config = CXD9872AKD_VAIO }, { .pci_subvendor = 0x104d, .pci_subdevice = 0x81e6, - .config = STAC7661_VAIO }, + .config = CXD9872RD_VAIO }, { .pci_subvendor = 0x104d, .pci_subdevice = 0x81ef, - .config = STAC7661_VAIO }, + .config = CXD9872RD_VAIO }, + { .pci_subvendor = 0x104d, .pci_subdevice = 0x81fd, + .config = CXD9872AKD_VAIO }, {} }; -static int patch_stac7661(struct hda_codec *codec) +static int patch_stac9872(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, stac9872_cfg_tbl); if (board_config < 0) /* unknown config, let generic-parser do its job... */ return snd_hda_parse_generic_codec(codec); @@ -1495,7 +1903,9 @@ static int patch_stac7661(struct hda_cod codec->spec = spec; switch (board_config) { - case STAC7661_VAIO: + case CXD9872RD_VAIO: + case STAC9872AK_VAIO: + case STAC9872K_VAIO: spec->mixer = vaio_mixer; spec->init = vaio_init; spec->multiout.max_channels = 2; @@ -1507,9 +1917,22 @@ static int patch_stac7661(struct hda_cod spec->input_mux = &vaio_mux; spec->mux_nids = vaio_mux_nids; break; + + case CXD9872AKD_VAIO: + spec->mixer = vaio_ar_mixer; + spec->init = vaio_ar_init; + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = ARRAY_SIZE(vaio_dacs); + spec->multiout.dac_nids = vaio_dacs; + spec->multiout.hp_nid = VAIO_HP_DAC; + spec->num_adcs = ARRAY_SIZE(vaio_adcs); + spec->adc_nids = vaio_adcs; + spec->input_mux = &vaio_mux; + spec->mux_nids = vaio_mux_nids; + break; } - codec->patch_ops = stac7661_patch_ops; + codec->patch_ops = stac9872_patch_ops; return 0; } @@ -1525,12 +1948,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 +1964,20 @@ 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 }, + /* The following does not take into account .id=0x83847661 when subsys = + * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are + * currently not fully supported. + */ + { .id = 0x83847661, .name = "CXD9872RD/K", .patch = patch_stac9872 }, + { .id = 0x83847662, .name = "STAC9872AK", .patch = patch_stac9872 }, + { .id = 0x83847664, .name = "CXD9872AKD", .patch = patch_stac9872 }, + { .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 }, {} /* terminator */ }; diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c index 9492f3d..9e76ceb 100644 --- a/sound/pci/ice1712/aureon.c +++ b/sound/pci/ice1712/aureon.c @@ -60,6 +60,7 @@ #include #include "ice1712.h" #include "envy24ht.h" #include "aureon.h" +#include /* WM8770 registers */ #define WM_DAC_ATTEN 0x00 /* DAC1-8 analog attenuation */ @@ -660,6 +661,12 @@ static int aureon_ac97_mmute_put(struct return change; } +static DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1); +static DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1); +static DECLARE_TLV_DB_SCALE(db_scale_wm_adc, -1200, 100, 0); +static DECLARE_TLV_DB_SCALE(db_scale_ac97_master, -4650, 150, 0); +static DECLARE_TLV_DB_SCALE(db_scale_ac97_gain, -3450, 150, 0); + /* * Logarithmic volume values for WM8770 * Computed as 20 * Log10(255 / x) @@ -1409,10 +1416,13 @@ static struct snd_kcontrol_new aureon_da }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Master Playback Volume", .info = wm_master_vol_info, .get = wm_master_vol_get, - .put = wm_master_vol_put + .put = wm_master_vol_put, + .tlv = { .p = db_scale_wm_dac } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1424,11 +1434,14 @@ static struct snd_kcontrol_new aureon_da }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Front Playback Volume", .info = wm_vol_info, .get = wm_vol_get, .put = wm_vol_put, - .private_value = (2 << 8) | 0 + .private_value = (2 << 8) | 0, + .tlv = { .p = db_scale_wm_dac } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1440,11 +1453,14 @@ static struct snd_kcontrol_new aureon_da }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Rear Playback Volume", .info = wm_vol_info, .get = wm_vol_get, .put = wm_vol_put, - .private_value = (2 << 8) | 2 + .private_value = (2 << 8) | 2, + .tlv = { .p = db_scale_wm_dac } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1456,11 +1472,14 @@ static struct snd_kcontrol_new aureon_da }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Center Playback Volume", .info = wm_vol_info, .get = wm_vol_get, .put = wm_vol_put, - .private_value = (1 << 8) | 4 + .private_value = (1 << 8) | 4, + .tlv = { .p = db_scale_wm_dac } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1472,11 +1491,14 @@ static struct snd_kcontrol_new aureon_da }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "LFE Playback Volume", .info = wm_vol_info, .get = wm_vol_get, .put = wm_vol_put, - .private_value = (1 << 8) | 5 + .private_value = (1 << 8) | 5, + .tlv = { .p = db_scale_wm_dac } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1488,11 +1510,14 @@ static struct snd_kcontrol_new aureon_da }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Side Playback Volume", .info = wm_vol_info, .get = wm_vol_get, .put = wm_vol_put, - .private_value = (2 << 8) | 6 + .private_value = (2 << 8) | 6, + .tlv = { .p = db_scale_wm_dac } } }; @@ -1506,10 +1531,13 @@ static struct snd_kcontrol_new wm_contro }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "PCM Playback Volume", .info = wm_pcm_vol_info, .get = wm_pcm_vol_get, - .put = wm_pcm_vol_put + .put = wm_pcm_vol_put, + .tlv = { .p = db_scale_wm_pcm } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1520,10 +1548,13 @@ static struct snd_kcontrol_new wm_contro }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Capture Volume", .info = wm_adc_vol_info, .get = wm_adc_vol_get, - .put = wm_adc_vol_put + .put = wm_adc_vol_put, + .tlv = { .p = db_scale_wm_adc } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1567,11 +1598,14 @@ static struct snd_kcontrol_new ac97_cont }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "AC97 Playback Volume", .info = aureon_ac97_vol_info, .get = aureon_ac97_vol_get, .put = aureon_ac97_vol_put, - .private_value = AC97_MASTER|AUREON_AC97_STEREO + .private_value = AC97_MASTER|AUREON_AC97_STEREO, + .tlv = { .p = db_scale_ac97_master } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1583,11 +1617,14 @@ static struct snd_kcontrol_new ac97_cont }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "CD Playback Volume", .info = aureon_ac97_vol_info, .get = aureon_ac97_vol_get, .put = aureon_ac97_vol_put, - .private_value = AC97_CD|AUREON_AC97_STEREO + .private_value = AC97_CD|AUREON_AC97_STEREO, + .tlv = { .p = db_scale_ac97_gain } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1599,11 +1636,14 @@ static struct snd_kcontrol_new ac97_cont }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Aux Playback Volume", .info = aureon_ac97_vol_info, .get = aureon_ac97_vol_get, .put = aureon_ac97_vol_put, - .private_value = AC97_AUX|AUREON_AC97_STEREO + .private_value = AC97_AUX|AUREON_AC97_STEREO, + .tlv = { .p = db_scale_ac97_gain } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1615,11 +1655,14 @@ static struct snd_kcontrol_new ac97_cont }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Line Playback Volume", .info = aureon_ac97_vol_info, .get = aureon_ac97_vol_get, .put = aureon_ac97_vol_put, - .private_value = AC97_LINE|AUREON_AC97_STEREO + .private_value = AC97_LINE|AUREON_AC97_STEREO, + .tlv = { .p = db_scale_ac97_gain } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1631,11 +1674,14 @@ static struct snd_kcontrol_new ac97_cont }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Mic Playback Volume", .info = aureon_ac97_vol_info, .get = aureon_ac97_vol_get, .put = aureon_ac97_vol_put, - .private_value = AC97_MIC + .private_value = AC97_MIC, + .tlv = { .p = db_scale_ac97_gain } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1657,11 +1703,14 @@ static struct snd_kcontrol_new universe_ }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "AC97 Playback Volume", .info = aureon_ac97_vol_info, .get = aureon_ac97_vol_get, .put = aureon_ac97_vol_put, - .private_value = AC97_MASTER|AUREON_AC97_STEREO + .private_value = AC97_MASTER|AUREON_AC97_STEREO, + .tlv = { .p = db_scale_ac97_master } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1673,11 +1722,14 @@ static struct snd_kcontrol_new universe_ }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "CD Playback Volume", .info = aureon_ac97_vol_info, .get = aureon_ac97_vol_get, .put = aureon_ac97_vol_put, - .private_value = AC97_AUX|AUREON_AC97_STEREO + .private_value = AC97_AUX|AUREON_AC97_STEREO, + .tlv = { .p = db_scale_ac97_gain } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1685,15 +1737,18 @@ static struct snd_kcontrol_new universe_ .info = aureon_ac97_mute_info, .get = aureon_ac97_mute_get, .put = aureon_ac97_mute_put, - .private_value = AC97_CD, + .private_value = AC97_CD }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Phono Playback Volume", .info = aureon_ac97_vol_info, .get = aureon_ac97_vol_get, .put = aureon_ac97_vol_put, - .private_value = AC97_CD|AUREON_AC97_STEREO + .private_value = AC97_CD|AUREON_AC97_STEREO, + .tlv = { .p = db_scale_ac97_gain } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1705,11 +1760,14 @@ static struct snd_kcontrol_new universe_ }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Line Playback Volume", .info = aureon_ac97_vol_info, .get = aureon_ac97_vol_get, .put = aureon_ac97_vol_put, - .private_value = AC97_LINE|AUREON_AC97_STEREO + .private_value = AC97_LINE|AUREON_AC97_STEREO, + .tlv = { .p = db_scale_ac97_gain } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1721,11 +1779,14 @@ static struct snd_kcontrol_new universe_ }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Mic Playback Volume", .info = aureon_ac97_vol_info, .get = aureon_ac97_vol_get, .put = aureon_ac97_vol_put, - .private_value = AC97_MIC + .private_value = AC97_MIC, + .tlv = { .p = db_scale_ac97_gain } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1744,11 +1805,14 @@ static struct snd_kcontrol_new universe_ }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Aux Playback Volume", .info = aureon_ac97_vol_info, .get = aureon_ac97_vol_get, .put = aureon_ac97_vol_put, - .private_value = AC97_VIDEO|AUREON_AC97_STEREO + .private_value = AC97_VIDEO|AUREON_AC97_STEREO, + .tlv = { .p = db_scale_ac97_gain } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index bf20858..dc69392 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -62,6 +62,7 @@ #include #include #include #include +#include #include @@ -1377,6 +1378,7 @@ static int snd_ice1712_pro_mixer_volume_ return change; } +static DECLARE_TLV_DB_SCALE(db_scale_playback, -14400, 150, 0); static struct snd_kcontrol_new snd_ice1712_multi_playback_ctrls[] __devinitdata = { { @@ -1390,12 +1392,15 @@ static struct snd_kcontrol_new snd_ice17 }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Multi Playback Volume", .info = snd_ice1712_pro_mixer_volume_info, .get = snd_ice1712_pro_mixer_volume_get, .put = snd_ice1712_pro_mixer_volume_put, .private_value = 0, .count = 10, + .tlv = { .p = db_scale_playback } }, }; @@ -1420,11 +1425,14 @@ static struct snd_kcontrol_new snd_ice17 static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_volume __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "H/W Multi Capture Volume", .info = snd_ice1712_pro_mixer_volume_info, .get = snd_ice1712_pro_mixer_volume_get, .put = snd_ice1712_pro_mixer_volume_put, .private_value = 10, + .tlv = { .p = db_scale_playback } }; static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_volume __devinitdata = { @@ -1857,7 +1865,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 +1932,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 +1949,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/phase.c b/sound/pci/ice1712/phase.c index 502da1c..e08d73f 100644 --- a/sound/pci/ice1712/phase.c +++ b/sound/pci/ice1712/phase.c @@ -46,6 +46,7 @@ #include #include "ice1712.h" #include "envy24ht.h" #include "phase.h" +#include /* WM8770 registers */ #define WM_DAC_ATTEN 0x00 /* DAC1-8 analog attenuation */ @@ -696,6 +697,9 @@ static int phase28_oversampling_put(stru return 0; } +static DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1); +static DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1); + static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -706,10 +710,13 @@ static struct snd_kcontrol_new phase28_d }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Master Playback Volume", .info = wm_master_vol_info, .get = wm_master_vol_get, - .put = wm_master_vol_put + .put = wm_master_vol_put, + .tlv = { .p = db_scale_wm_dac } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -721,11 +728,14 @@ static struct snd_kcontrol_new phase28_d }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Front Playback Volume", .info = wm_vol_info, .get = wm_vol_get, .put = wm_vol_put, - .private_value = (2 << 8) | 0 + .private_value = (2 << 8) | 0, + .tlv = { .p = db_scale_wm_dac } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -737,11 +747,14 @@ static struct snd_kcontrol_new phase28_d }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Rear Playback Volume", .info = wm_vol_info, .get = wm_vol_get, .put = wm_vol_put, - .private_value = (2 << 8) | 2 + .private_value = (2 << 8) | 2, + .tlv = { .p = db_scale_wm_dac } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -753,11 +766,14 @@ static struct snd_kcontrol_new phase28_d }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Center Playback Volume", .info = wm_vol_info, .get = wm_vol_get, .put = wm_vol_put, - .private_value = (1 << 8) | 4 + .private_value = (1 << 8) | 4, + .tlv = { .p = db_scale_wm_dac } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -769,11 +785,14 @@ static struct snd_kcontrol_new phase28_d }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "LFE Playback Volume", .info = wm_vol_info, .get = wm_vol_get, .put = wm_vol_put, - .private_value = (1 << 8) | 5 + .private_value = (1 << 8) | 5, + .tlv = { .p = db_scale_wm_dac } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -785,11 +804,14 @@ static struct snd_kcontrol_new phase28_d }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Side Playback Volume", .info = wm_vol_info, .get = wm_vol_get, .put = wm_vol_put, - .private_value = (2 << 8) | 6 + .private_value = (2 << 8) | 6, + .tlv = { .p = db_scale_wm_dac } } }; @@ -803,10 +825,13 @@ static struct snd_kcontrol_new wm_contro }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "PCM Playback Volume", .info = wm_pcm_vol_info, .get = wm_pcm_vol_get, - .put = wm_pcm_vol_put + .put = wm_pcm_vol_put, + .tlv = { .p = db_scale_wm_pcm } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c index 0efcad9..6c74c2d 100644 --- a/sound/pci/ice1712/pontis.c +++ b/sound/pci/ice1712/pontis.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "ice1712.h" #include "envy24ht.h" @@ -564,6 +565,8 @@ static int pontis_gpio_data_put(struct s return changed; } +static DECLARE_TLV_DB_SCALE(db_scale_volume, -6400, 50, 1); + /* * mixers */ @@ -571,17 +574,23 @@ static int pontis_gpio_data_put(struct s static struct snd_kcontrol_new pontis_controls[] __devinitdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "PCM Playback Volume", .info = wm_dac_vol_info, .get = wm_dac_vol_get, .put = wm_dac_vol_put, + .tlv = { .p = db_scale_volume }, }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Capture Volume", .info = wm_adc_vol_info, .get = wm_adc_vol_get, .put = wm_adc_vol_put, + .tlv = { .p = db_scale_volume }, }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c index fdb5cb8..41b2605 100644 --- a/sound/pci/ice1712/prodigy192.c +++ b/sound/pci/ice1712/prodigy192.c @@ -35,6 +35,7 @@ #include "ice1712.h" #include "envy24ht.h" #include "prodigy192.h" #include "stac946x.h" +#include static inline void stac9460_put(struct snd_ice1712 *ice, int reg, unsigned char val) { @@ -356,6 +357,9 @@ static int aureon_oversampling_put(struc } #endif +static DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0); +static DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0); + /* * mixers */ @@ -368,14 +372,18 @@ static struct snd_kcontrol_new stac_cont .get = stac9460_dac_mute_get, .put = stac9460_dac_mute_put, .private_value = 1, + .tlv = { .p = db_scale_dac } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Master Playback Volume", .info = stac9460_dac_vol_info, .get = stac9460_dac_vol_get, .put = stac9460_dac_vol_put, .private_value = 1, + .tlv = { .p = db_scale_dac } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -387,11 +395,14 @@ static struct snd_kcontrol_new stac_cont }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "DAC Volume", .count = 6, .info = stac9460_dac_vol_info, .get = stac9460_dac_vol_get, .put = stac9460_dac_vol_put, + .tlv = { .p = db_scale_dac } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -404,11 +415,14 @@ static struct snd_kcontrol_new stac_cont }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "ADC Volume", .count = 1, .info = stac9460_adc_vol_info, .get = stac9460_adc_vol_get, .put = stac9460_adc_vol_put, + .tlv = { .p = db_scale_adc } }, #if 0 { diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c index fec9440..c9eefa9 100644 --- a/sound/pci/ice1712/revo.c +++ b/sound/pci/ice1712/revo.c @@ -87,16 +87,34 @@ static void revo_set_rate_val(struct snd * initialize the chips on M-Audio Revolution cards */ -static unsigned int revo71_num_stereo_front[] = {2}; -static char *revo71_channel_names_front[] = {"PCM Playback Volume"}; +#define AK_DAC(xname,xch) { .name = xname, .num_channels = xch } -static unsigned int revo71_num_stereo_surround[] = {1, 1, 2, 2}; -static char *revo71_channel_names_surround[] = {"PCM Center Playback Volume", "PCM LFE Playback Volume", - "PCM Side Playback Volume", "PCM Rear Playback Volume"}; +static struct snd_akm4xxx_dac_channel revo71_front[] = { + AK_DAC("PCM Playback Volume", 2) +}; + +static struct snd_akm4xxx_dac_channel revo71_surround[] = { + AK_DAC("PCM Center Playback Volume", 1), + AK_DAC("PCM LFE Playback Volume", 1), + AK_DAC("PCM Side Playback Volume", 2), + AK_DAC("PCM Rear Playback Volume", 2), +}; -static unsigned int revo51_num_stereo[] = {2, 1, 1, 2}; -static char *revo51_channel_names[] = {"PCM Playback Volume", "PCM Center Playback Volume", - "PCM LFE Playback Volume", "PCM Rear Playback Volume"}; +static struct snd_akm4xxx_dac_channel revo51_dac[] = { + AK_DAC("PCM Playback Volume", 2), + AK_DAC("PCM Center Playback Volume", 1), + AK_DAC("PCM LFE Playback Volume", 1), + AK_DAC("PCM Rear Playback Volume", 2), +}; + +static struct snd_akm4xxx_adc_channel revo51_adc[] = { + { + .name = "PCM Capture Volume", + .gain_name = "PCM Capture Gain Volume", + .switch_name = "PCM Capture Switch", + .num_channels = 2 + }, +}; static struct snd_akm4xxx akm_revo_front __devinitdata = { .type = SND_AK4381, @@ -104,8 +122,7 @@ static struct snd_akm4xxx akm_revo_front .ops = { .set_rate_val = revo_set_rate_val }, - .num_stereo = revo71_num_stereo_front, - .channel_names = revo71_channel_names_front + .dac_info = revo71_front, }; static struct snd_ak4xxx_private akm_revo_front_priv __devinitdata = { @@ -127,8 +144,7 @@ static struct snd_akm4xxx akm_revo_surro .ops = { .set_rate_val = revo_set_rate_val }, - .num_stereo = revo71_num_stereo_surround, - .channel_names = revo71_channel_names_surround + .dac_info = revo71_surround, }; static struct snd_ak4xxx_private akm_revo_surround_priv __devinitdata = { @@ -149,8 +165,7 @@ static struct snd_akm4xxx akm_revo51 __d .ops = { .set_rate_val = revo_set_rate_val }, - .num_stereo = revo51_num_stereo, - .channel_names = revo51_channel_names + .dac_info = revo51_dac, }; static struct snd_ak4xxx_private akm_revo51_priv __devinitdata = { @@ -159,7 +174,25 @@ 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, + .adc_info = revo51_adc, +}; + +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 +235,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/mixart/mixart_mixer.c b/sound/pci/mixart/mixart_mixer.c index ed47b73..13de0f7 100644 --- a/sound/pci/mixart/mixart_mixer.c +++ b/sound/pci/mixart/mixart_mixer.c @@ -31,6 +31,7 @@ #include "mixart.h" #include "mixart_core.h" #include "mixart_hwdep.h" #include +#include #include "mixart_mixer.h" static u32 mixart_analog_level[256] = { @@ -388,12 +389,17 @@ static int mixart_analog_vol_put(struct return changed; } +static DECLARE_TLV_DB_SCALE(db_scale_analog, -9600, 50, 0); + static struct snd_kcontrol_new mixart_control_analog_level = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), /* name will be filled later */ .info = mixart_analog_vol_info, .get = mixart_analog_vol_get, .put = mixart_analog_vol_put, + .tlv = { .p = db_scale_analog }, }; /* shared */ @@ -866,14 +872,19 @@ static int mixart_pcm_vol_put(struct snd return changed; } +static DECLARE_TLV_DB_SCALE(db_scale_digital, -10950, 50, 0); + static struct snd_kcontrol_new snd_mixart_pcm_vol = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), /* name will be filled later */ /* count will be filled later */ .info = mixart_digital_vol_info, /* shared */ .get = mixart_pcm_vol_get, .put = mixart_pcm_vol_put, + .tlv = { .p = db_scale_digital }, }; @@ -984,10 +995,13 @@ static int mixart_monitor_vol_put(struct static struct snd_kcontrol_new mixart_control_monitor_vol = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Monitoring Volume", .info = mixart_digital_vol_info, /* shared */ .get = mixart_monitor_vol_get, .put = mixart_monitor_vol_put, + .tlv = { .p = db_scale_digital }, }; /* diff --git a/sound/pci/pcxhr/pcxhr_mixer.c b/sound/pci/pcxhr/pcxhr_mixer.c index 94e63a1..b133ad9 100644 --- a/sound/pci/pcxhr/pcxhr_mixer.c +++ b/sound/pci/pcxhr/pcxhr_mixer.c @@ -31,6 +31,7 @@ #include "pcxhr.h" #include "pcxhr_hwdep.h" #include "pcxhr_core.h" #include +#include #include #include "pcxhr_mixer.h" @@ -43,6 +44,9 @@ #define PCXHR_ANALOG_PLAYBACK_LEVEL_MIN #define PCXHR_ANALOG_PLAYBACK_LEVEL_MAX 128 /* 0.0 dB */ #define PCXHR_ANALOG_PLAYBACK_ZERO_LEVEL 104 /* -24.0 dB ( 0.0 dB - fix level +24.0 dB ) */ +static DECLARE_TLV_DB_SCALE(db_scale_analog_capture, -9600, 50, 0); +static DECLARE_TLV_DB_SCALE(db_scale_analog_playback, -12800, 100, 0); + static int pcxhr_update_analog_audio_level(struct snd_pcxhr *chip, int is_capture, int channel) { int err, vol; @@ -130,10 +134,13 @@ static int pcxhr_analog_vol_put(struct s static struct snd_kcontrol_new pcxhr_control_analog_level = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), /* name will be filled later */ .info = pcxhr_analog_vol_info, .get = pcxhr_analog_vol_get, .put = pcxhr_analog_vol_put, + /* tlv will be filled later */ }; /* shared */ @@ -188,6 +195,7 @@ #define PCXHR_DIGITAL_LEVEL_MIN 0x000 / #define PCXHR_DIGITAL_LEVEL_MAX 0x1ff /* +18 dB */ #define PCXHR_DIGITAL_ZERO_LEVEL 0x1b7 /* 0 dB */ +static DECLARE_TLV_DB_SCALE(db_scale_digital, -10950, 50, 0); #define MORE_THAN_ONE_STREAM_LEVEL 0x000001 #define VALID_STREAM_PAN_LEVEL_MASK 0x800000 @@ -343,11 +351,14 @@ static int pcxhr_pcm_vol_put(struct snd_ static struct snd_kcontrol_new snd_pcxhr_pcm_vol = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), /* name will be filled later */ /* count will be filled later */ .info = pcxhr_digital_vol_info, /* shared */ .get = pcxhr_pcm_vol_get, .put = pcxhr_pcm_vol_put, + .tlv = { .p = db_scale_digital }, }; @@ -433,10 +444,13 @@ static int pcxhr_monitor_vol_put(struct static struct snd_kcontrol_new pcxhr_control_monitor_vol = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Monitoring Volume", .info = pcxhr_digital_vol_info, /* shared */ .get = pcxhr_monitor_vol_get, .put = pcxhr_monitor_vol_put, + .tlv = { .p = db_scale_digital }, }; /* @@ -928,6 +942,7 @@ int pcxhr_create_mixer(struct pcxhr_mgr temp = pcxhr_control_analog_level; temp.name = "Master Playback Volume"; temp.private_value = 0; /* playback */ + temp.tlv.p = db_scale_analog_playback; if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0) return err; /* output mute controls */ @@ -963,6 +978,7 @@ int pcxhr_create_mixer(struct pcxhr_mgr temp = pcxhr_control_analog_level; temp.name = "Master Capture Volume"; temp.private_value = 1; /* capture */ + temp.tlv.p = db_scale_analog_capture; if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0) return err; 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/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index e5a52da..d3e07de 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -726,22 +726,36 @@ static int hdsp_get_iobox_version (struc } -static int hdsp_check_for_firmware (struct hdsp *hdsp, int show_err) +#ifdef HDSP_FW_LOADER +static int __devinit hdsp_request_fw_loader(struct hdsp *hdsp); +#endif + +static int hdsp_check_for_firmware (struct hdsp *hdsp, int load_on_demand) { - if (hdsp->io_type == H9652 || hdsp->io_type == H9632) return 0; + if (hdsp->io_type == H9652 || hdsp->io_type == H9632) + return 0; if ((hdsp_read (hdsp, HDSP_statusRegister) & HDSP_DllError) != 0) { - snd_printk(KERN_ERR "Hammerfall-DSP: firmware not present.\n"); hdsp->state &= ~HDSP_FirmwareLoaded; - if (! show_err) + if (! load_on_demand) return -EIO; + snd_printk(KERN_ERR "Hammerfall-DSP: firmware not present.\n"); /* try to load firmware */ - if (hdsp->state & HDSP_FirmwareCached) { - if (snd_hdsp_load_firmware_from_cache(hdsp) != 0) - snd_printk(KERN_ERR "Hammerfall-DSP: Firmware loading from cache failed, please upload manually.\n"); - } else { - snd_printk(KERN_ERR "Hammerfall-DSP: No firmware loaded nor cached, please upload firmware.\n"); + if (! (hdsp->state & HDSP_FirmwareCached)) { +#ifdef HDSP_FW_LOADER + if (! hdsp_request_fw_loader(hdsp)) + return 0; +#endif + snd_printk(KERN_ERR + "Hammerfall-DSP: No firmware loaded nor " + "cached, please upload firmware.\n"); + return -EIO; + } + if (snd_hdsp_load_firmware_from_cache(hdsp) != 0) { + snd_printk(KERN_ERR + "Hammerfall-DSP: Firmware loading from " + "cache failed, please upload manually.\n"); + return -EIO; } - return -EIO; } return 0; } @@ -3181,8 +3195,16 @@ snd_hdsp_proc_read(struct snd_info_entry return; } } else { - snd_iprintf(buffer, "No firmware loaded nor cached, please upload firmware.\n"); - return; + int err = -EINVAL; +#ifdef HDSP_FW_LOADER + err = hdsp_request_fw_loader(hdsp); +#endif + if (err < 0) { + snd_iprintf(buffer, + "No firmware loaded nor cached, " + "please upload firmware.\n"); + return; + } } } @@ -3851,7 +3873,7 @@ static int snd_hdsp_trigger(struct snd_p if (hdsp_check_for_iobox (hdsp)) return -EIO; - if (hdsp_check_for_firmware(hdsp, 1)) + if (hdsp_check_for_firmware(hdsp, 0)) /* no auto-loading in trigger */ return -EIO; spin_lock(&hdsp->lock); diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index 4930cc6..ebbe12d 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -40,6 +40,7 @@ #include #include #include #include +#include #include #include @@ -2627,6 +2628,8 @@ static int snd_trident_vol_control_get(s return 0; } +static DECLARE_TLV_DB_SCALE(db_scale_gvol, -6375, 25, 0); + static int snd_trident_vol_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2653,6 +2656,7 @@ static struct snd_kcontrol_new snd_tride .get = snd_trident_vol_control_get, .put = snd_trident_vol_control_put, .private_value = 16, + .tlv = { .p = db_scale_gvol }, }; static struct snd_kcontrol_new snd_trident_vol_wave_control __devinitdata = @@ -2663,6 +2667,7 @@ static struct snd_kcontrol_new snd_tride .get = snd_trident_vol_control_get, .put = snd_trident_vol_control_put, .private_value = 0, + .tlv = { .p = db_scale_gvol }, }; /*--------------------------------------------------------------------------- @@ -2730,6 +2735,7 @@ static struct snd_kcontrol_new snd_tride .info = snd_trident_pcm_vol_control_info, .get = snd_trident_pcm_vol_control_get, .put = snd_trident_pcm_vol_control_put, + /* FIXME: no tlv yet */ }; /*--------------------------------------------------------------------------- @@ -2839,6 +2845,8 @@ static int snd_trident_pcm_rvol_control_ return change; } +static DECLARE_TLV_DB_SCALE(db_scale_crvol, -3175, 25, 1); + static struct snd_kcontrol_new snd_trident_pcm_rvol_control __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -2848,6 +2856,7 @@ static struct snd_kcontrol_new snd_tride .info = snd_trident_pcm_rvol_control_info, .get = snd_trident_pcm_rvol_control_get, .put = snd_trident_pcm_rvol_control_put, + .tlv = { .p = db_scale_crvol }, }; /*--------------------------------------------------------------------------- @@ -2903,6 +2912,7 @@ static struct snd_kcontrol_new snd_tride .info = snd_trident_pcm_cvol_control_info, .get = snd_trident_pcm_cvol_control_get, .put = snd_trident_pcm_cvol_control_put, + .tlv = { .p = db_scale_crvol }, }; static void snd_trident_notify_pcm_change1(struct snd_card *card, diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 08da923..6db3d4c 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -59,6 +59,7 @@ #include #include #include #include +#include #include #include #include @@ -1277,7 +1278,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; } @@ -1687,21 +1699,29 @@ static int snd_via8233_pcmdxs_volume_put return change; } +static DECLARE_TLV_DB_SCALE(db_scale_dxs, -9450, 150, 1); + static struct snd_kcontrol_new snd_via8233_pcmdxs_volume_control __devinitdata = { .name = "PCM Playback Volume", .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .info = snd_via8233_dxs_volume_info, .get = snd_via8233_pcmdxs_volume_get, .put = snd_via8233_pcmdxs_volume_put, + .tlv = { .p = db_scale_dxs } }; static struct snd_kcontrol_new snd_via8233_dxs_volume_control __devinitdata = { .name = "VIA DXS Playback Volume", .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .count = 4, .info = snd_via8233_dxs_volume_info, .get = snd_via8233_dxs_volume_get, .put = snd_via8233_dxs_volume_put, + .tlv = { .p = db_scale_dxs } }; /* @@ -2393,6 +2413,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/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c index 9c03c6b..e7cd8ac 100644 --- a/sound/pci/vx222/vx222.c +++ b/sound/pci/vx222/vx222.c @@ -26,6 +26,7 @@ #include #include #include #include +#include #include "vx222.h" #define CARD_NAME "VX222" @@ -72,6 +73,9 @@ MODULE_DEVICE_TABLE(pci, snd_vx222_ids); /* */ +static DECLARE_TLV_DB_SCALE(db_scale_old_vol, -11350, 50, 0); +static DECLARE_TLV_DB_SCALE(db_scale_akm, -7350, 50, 0); + static struct snd_vx_hardware vx222_old_hw = { .name = "VX222/Old", @@ -81,6 +85,7 @@ static struct snd_vx_hardware vx222_old_ .num_ins = 1, .num_outs = 1, .output_level_max = VX_ANALOG_OUT_LEVEL_MAX, + .output_level_db_scale = db_scale_old_vol, }; static struct snd_vx_hardware vx222_v2_hw = { @@ -92,6 +97,7 @@ static struct snd_vx_hardware vx222_v2_h .num_ins = 1, .num_outs = 1, .output_level_max = VX2_AKM_LEVEL_MAX, + .output_level_db_scale = db_scale_akm, }; static struct snd_vx_hardware vx222_mic_hw = { @@ -103,6 +109,7 @@ static struct snd_vx_hardware vx222_mic_ .num_ins = 1, .num_outs = 1, .output_level_max = VX2_AKM_LEVEL_MAX, + .output_level_db_scale = db_scale_akm, }; diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c index 9b6d345..5e51950 100644 --- a/sound/pci/vx222/vx222_ops.c +++ b/sound/pci/vx222/vx222_ops.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "vx222.h" @@ -845,6 +846,8 @@ static void vx2_set_input_level(struct s #define MIC_LEVEL_MAX 0xff +static DECLARE_TLV_DB_SCALE(db_scale_mic, -6450, 50, 0); + /* * controls API for input levels */ @@ -922,18 +925,24 @@ static int vx_mic_level_put(struct snd_k static struct snd_kcontrol_new vx_control_input_level = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Capture Volume", .info = vx_input_level_info, .get = vx_input_level_get, .put = vx_input_level_put, + .tlv = { .p = db_scale_mic }, }; static struct snd_kcontrol_new vx_control_mic_level = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Mic Capture Volume", .info = vx_mic_level_info, .get = vx_mic_level_get, .put = vx_mic_level_put, + .tlv = { .p = db_scale_mic }, }; /* diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index a55b5fd..24f6fc5 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -36,6 +36,7 @@ #include #include #include #include +#include #include #include #include @@ -1477,11 +1478,15 @@ static int snd_ymfpci_put_single(struct return change; } +static DECLARE_TLV_DB_LINEAR(db_scale_native, TLV_DB_GAIN_MUTE, 0); + #define YMFPCI_DOUBLE(xname, xindex, reg) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ .info = snd_ymfpci_info_double, \ .get = snd_ymfpci_get_double, .put = snd_ymfpci_put_double, \ - .private_value = reg } + .private_value = reg, \ + .tlv = { .p = db_scale_native } } static int snd_ymfpci_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { 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/vxp_mixer.c b/sound/pcmcia/vx/vxp_mixer.c index e237f6c..bced7b6 100644 --- a/sound/pcmcia/vx/vxp_mixer.c +++ b/sound/pcmcia/vx/vxp_mixer.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "vxpocket.h" #define MIC_LEVEL_MIN 0 @@ -63,12 +64,17 @@ static int vx_mic_level_put(struct snd_k return 0; } +static DECLARE_TLV_DB_SCALE(db_scale_mic, -21, 3, 0); + static struct snd_kcontrol_new vx_control_mic_level = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Mic Capture Volume", .info = vx_mic_level_info, .get = vx_mic_level_get, .put = vx_mic_level_put, + .tlv = { .p = db_scale_mic }, }; /* diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c index cafe664..3089fcc 100644 --- a/sound/pcmcia/vx/vxpocket.c +++ b/sound/pcmcia/vx/vxpocket.c @@ -27,6 +27,7 @@ #include "vxpocket.h" #include #include #include +#include /* */ @@ -65,7 +66,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) { @@ -90,6 +91,8 @@ static int snd_vxpocket_dev_free(struct * Only output levels can be modified */ +static DECLARE_TLV_DB_SCALE(db_scale_old_vol, -11350, 50, 0); + static struct snd_vx_hardware vxpocket_hw = { .name = "VXPocket", .type = VX_TYPE_VXPOCKET, @@ -99,6 +102,7 @@ static struct snd_vx_hardware vxpocket_h .num_ins = 1, .num_outs = 1, .output_level_max = VX_ANALOG_OUT_LEVEL_MAX, + .output_level_db_scale = db_scale_old_vol, }; /* VX-pocket 440 @@ -120,6 +124,7 @@ static struct snd_vx_hardware vxp440_hw .num_ins = 2, .num_outs = 2, .output_level_max = VX_ANALOG_OUT_LEVEL_MAX, + .output_level_db_scale = db_scale_old_vol, }; @@ -363,7 +368,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/ppc/tumbler.c b/sound/ppc/tumbler.c index 84f6b19..f2bfa1e 100644 --- a/sound/ppc/tumbler.c +++ b/sound/ppc/tumbler.c @@ -190,7 +190,7 @@ static int check_audio_gpio(struct pmac_ ret = do_gpio_read(gp); - return (ret & 0xd) == (gp->active_val & 0xd); + return (ret & 0x1) == (gp->active_val & 0x1); } static int read_audio_gpio(struct pmac_gpio *gp) @@ -198,7 +198,8 @@ static int read_audio_gpio(struct pmac_g int ret; if (! gp->addr) return 0; - ret = ((do_gpio_read(gp) & 0x02) !=0); + ret = do_gpio_read(gp); + ret = (ret & 0x02) !=0; return ret == gp->active_state; } diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c index f3ae6e2..e4935fc 100644 --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -2,6 +2,8 @@ * Driver for DBRI sound chip found on Sparcs. * Copyright (C) 2004, 2005 Martin Habets (mhabets@users.sourceforge.net) * + * Converted to ring buffered version by Krzysztof Helt (krzysztof.h1@wp.pl) + * * Based entirely upon drivers/sbus/audio/dbri.c which is: * Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de) * Copyright (C) 1998, 1999 Brent Baccala (baccala@freesoft.org) @@ -34,7 +36,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 @@ -83,7 +85,7 @@ MODULE_PARM_DESC(id, "ID string for Sun module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable Sun DBRI soundcard."); -#define DBRI_DEBUG +#undef DBRI_DEBUG #define D_INT (1<<0) #define D_GEN (1<<1) @@ -104,17 +106,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 dprintk(a, x...) do { } while (0) -#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 ****************************************************************************/ @@ -160,7 +160,7 @@ static struct { /* { NA, (1 << 4), (5 << 3) }, */ { 48000, (1 << 4), (6 << 3) }, { 9600, (1 << 4), (7 << 3) }, - { 5513, (2 << 4), (0 << 3) }, /* Actually 5512.5 */ + { 5512, (2 << 4), (0 << 3) }, /* Actually 5512.5 */ { 11025, (2 << 4), (1 << 3) }, { 18900, (2 << 4), (2 << 3) }, { 22050, (2 << 4), (3 << 3) }, @@ -240,28 +240,21 @@ #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 - -#define DBRI_MM_ONB 1 -#define DBRI_MM_SB 2 +#define DBRI_MAX_PIPE (DBRI_NO_PIPES - 1) #define DBRI_REC 0 #define DBRI_PLAY 1 #define DBRI_NO_STREAMS 2 /* One transmit/receive descriptor */ +/* When ba != 0 descriptor is used */ 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; }; @@ -269,8 +262,8 @@ struct dbri_mem { * the CPU and the DBRI */ struct dbri_dma { - volatile s32 cmd[DBRI_NO_CMDS]; /* Place for commands */ - volatile s32 intr[DBRI_NO_INTS * DBRI_INT_BLK]; /* Interrupt field */ + s32 cmd[DBRI_NO_CMDS]; /* Place for commands */ + volatile s32 intr[DBRI_INT_BLK]; /* Interrupt field */ struct dbri_mem desc[DBRI_NO_DESCS]; /* Xmit/receive descriptors */ }; @@ -282,58 +275,43 @@ 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 */ int desc; /* Index of active descriptor */ volatile __u32 *recv_fixed_ptr; /* Ptr to receive fixed data */ }; -struct dbri_desc { - int inuse; /* Boolean flag */ - int next; /* Index of next desc, or -1 */ - unsigned int len; -}; - /* Per stream (playback or record) information */ struct dbri_streaminfo { struct snd_pcm_substream *substream; u32 dvma_buffer; /* Device view of Alsa DMA buffer */ - int left; /* # of bytes left in DMA buffer */ int size; /* Size of DMA buffer */ size_t offset; /* offset in user buffer */ 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 */ struct dbri_pipe pipes[DBRI_NO_PIPES]; /* DBRI's 32 data pipes */ - struct dbri_desc descs[DBRI_NO_DESCS]; + int next_desc[DBRI_NO_DESCS]; /* Index of next desc, or -1 */ + spinlock_t cmdlock; /* Protects cmd queue accesses */ + s32 *cmdptr; /* Pointer to the last queued cmd */ - int chi_in_pipe; - int chi_out_pipe; int chi_bpf; struct cs4215 mm; /* mmcodec special info */ @@ -345,8 +323,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 */ @@ -569,7 +545,7 @@ #define DBRI_TD_ABT (1<<2) /* Abort: fra #define DBRI_TD_TBC (1<<0) /* Transmit buffer Complete */ #define DBRI_TD_STATUS(v) ((v)&0xff) /* Transmit status */ /* Maximum buffer size per TD: almost 8Kb */ -#define DBRI_TD_MAXCNT ((1 << 13) - 1) +#define DBRI_TD_MAXCNT ((1 << 13) - 4) /* Receive descriptor defines */ #define DBRI_RD_F (1<<31) /* End of Frame */ @@ -633,93 +609,124 @@ The list is terminated with a WAIT comma CPU interrupt to signal completion. Since the DBRI can run in parallel with the CPU, several means of -synchronization present themselves. The method implemented here is close -to the original scheme (Rudolf's), and uses 2 counters (wait_send and -wait_ackd) to synchronize the command buffer between the CPU and the DBRI. +synchronization present themselves. The method implemented here is only +use of the dbri_cmdwait() to wait for execution of batch of sent commands. -A more sophisticated scheme might involve a circular command buffer -or an array of command buffers. A routine could fill one with -commands and link it onto a list. When a interrupt signaled -completion of the current command buffer, look on the list for -the next one. +A circular command buffer is used here. A new command is being added +while another can be executed. The scheme works by adding two WAIT commands +after each sent batch of commands. When the next batch is prepared it is +added after the WAIT commands then the WAITs are replaced with single JUMP +command to the new batch. The the DBRI is forced to reread the last WAIT +command (replaced by the JUMP by then). If the DBRI is still executing +previous commands the request to reread the WAIT command is ignored. Every time a routine wants to write commands to the DBRI, it must -first call dbri_cmdlock() and get an initial pointer into dbri->dma->cmd -in return. dbri_cmdlock() will block if the previous commands have not -been completed yet. After this the commands can be written to the buffer, -and dbri_cmdsend() is called with the final pointer value to send them -to the DBRI. +first call dbri_cmdlock() and get pointer to a free space in +dbri->dma->cmd buffer. After this, the commands can be written to +the buffer, and dbri_cmdsend() is called with the final pointer value +to send them to the DBRI. */ -static void dbri_process_interrupt_buffer(struct snd_dbri * dbri); - -enum dbri_lock { NoGetLock, GetLock }; -#define MAXLOOPS 10 - -static volatile s32 *dbri_cmdlock(struct snd_dbri * dbri, enum dbri_lock get) +#define MAXLOOPS 20 +/* + * Wait for the current command string to execute + */ +static void dbri_cmdwait(struct snd_dbri *dbri) { int maxloops = MAXLOOPS; - -#ifndef SMP - if ((get == GetLock) && spin_is_locked(&dbri->lock)) { - printk(KERN_ERR "DBRI: cmdlock called while in spinlock."); - } -#endif + unsigned long flags; /* Delay if previous commands are still being processed */ - while ((--maxloops) > 0 && (dbri->wait_send != dbri->wait_ackd)) { + spin_lock_irqsave(&dbri->lock, flags); + while ((--maxloops) > 0 && (sbus_readl(dbri->regs + REG0) & D_P)) { + spin_unlock_irqrestore(&dbri->lock, flags); msleep_interruptible(1); - /* If dbri_cmdlock() got called from inside the - * interrupt handler, this will do the processing. - */ - dbri_process_interrupt_buffer(dbri); + spin_lock_irqsave(&dbri->lock, flags); } + spin_unlock_irqrestore(&dbri->lock, flags); + if (maxloops == 0) { - printk(KERN_ERR "DBRI: Chip never completed command buffer %d\n", - dbri->wait_send); + printk(KERN_ERR "DBRI: Chip never completed command buffer\n"); } else { dprintk(D_CMD, "Chip completed command buffer (%d)\n", MAXLOOPS - maxloops - 1); } +} +/* + * Lock the command queue and returns pointer to a space for len cmd words + * It locks the cmdlock spinlock. + */ +static s32 *dbri_cmdlock(struct snd_dbri * dbri, int len) +{ + /* Space for 2 WAIT cmds (replaced later by 1 JUMP cmd) */ + len += 2; + spin_lock(&dbri->cmdlock); + if (dbri->cmdptr - dbri->dma->cmd + len < DBRI_NO_CMDS - 2) + return dbri->cmdptr + 2; + else if (len < sbus_readl(dbri->regs + REG8) - dbri->dma_dvma) + return dbri->dma->cmd; + else + printk(KERN_ERR "DBRI: no space for commands."); - /*if (get == GetLock) spin_lock(&dbri->lock); */ - return &dbri->dma->cmd[0]; + return 0; } -static void dbri_cmdsend(struct snd_dbri * dbri, volatile s32 * cmd) +/* + * Send prepared cmd string. It works by writting a JUMP cmd into + * the last WAIT cmd and force DBRI to reread the cmd. + * The JUMP cmd points to the new cmd string. + * It also releases the cmdlock spinlock. + * + * Lock must not be held before calling this. + */ +static void dbri_cmdsend(struct snd_dbri * dbri, s32 * cmd,int len) { - volatile s32 *ptr; - u32 reg; + s32 tmp, addr; + static int wait_id = 0; - for (ptr = &dbri->dma->cmd[0]; ptr < cmd; ptr++) { - dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr); - } + wait_id++; + wait_id &= 0xffff; /* restrict it to a 16 bit counter. */ + *(cmd) = DBRI_CMD(D_WAIT, 1, wait_id); + *(cmd+1) = DBRI_CMD(D_WAIT, 1, wait_id); - if ((cmd - &dbri->dma->cmd[0]) >= DBRI_NO_CMDS - 1) { - printk(KERN_ERR "DBRI: Command buffer overflow! (bug in driver)\n"); - /* Ignore the last part. */ - cmd = &dbri->dma->cmd[DBRI_NO_CMDS - 3]; - } + /* Replace the last command with JUMP */ + addr = dbri->dma_dvma + (cmd - len - dbri->dma->cmd) * sizeof(s32); + *(dbri->cmdptr+1) = addr; + *(dbri->cmdptr) = DBRI_CMD(D_JUMP, 0, 0); - dbri->wait_send++; - dbri->wait_send &= 0xffff; /* restrict it to a 16 bit counter. */ - *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0); - *(cmd++) = DBRI_CMD(D_WAIT, 1, dbri->wait_send); +#ifdef DBRI_DEBUG + if (cmd > dbri->cmdptr) { + s32 *ptr; + + for (ptr = dbri->cmdptr; ptr < cmd+2; ptr++) + dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr); + } else { + s32 *ptr = dbri->cmdptr; + + dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr); + ptr++; + dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr); + for (ptr = dbri->dma->cmd; ptr < cmd+2; ptr++) { + dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr); + } + } +#endif - /* 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); + /* Reread the last command */ + tmp = sbus_readl(dbri->regs + REG0); + tmp |= D_P; + sbus_writel(tmp, dbri->regs + REG0); - /*spin_unlock(&dbri->lock); */ + dbri->cmdptr = cmd; + spin_unlock(&dbri->cmdlock); } /* Lock must be held when calling this */ static void dbri_reset(struct snd_dbri * dbri) { int i; + u32 tmp; dprintk(D_GEN, "reset 0:%x 2:%x 8:%x 9:%x\n", sbus_readl(dbri->regs + REG0), @@ -729,13 +736,20 @@ static void dbri_reset(struct snd_dbri * sbus_writel(D_R, dbri->regs + REG0); /* Soft Reset */ for (i = 0; (sbus_readl(dbri->regs + REG0) & D_R) && i < 64; i++) udelay(10); + + /* A brute approach - DBRI falls back to working burst size by itself + * On SS20 D_S does not work, so do not try so high. */ + tmp = sbus_readl(dbri->regs + REG0); + tmp |= D_G | D_E; + tmp &= ~D_S; + sbus_writel(tmp, dbri->regs + REG0); } /* Lock must not be held before calling this */ static void dbri_initialize(struct snd_dbri * dbri) { - volatile s32 *cmd; - u32 dma_addr, tmp; + s32 *cmd; + u32 dma_addr; unsigned long flags; int n; @@ -743,42 +757,34 @@ static void dbri_initialize(struct snd_d dbri_reset(dbri); - cmd = dbri_cmdlock(dbri, NoGetLock); - dprintk(D_GEN, "init: cmd: %p, int: %p\n", - &dbri->dma->cmd[0], &dbri->dma->intr[0]); + /* Initialize pipes */ + for (n = 0; n < DBRI_NO_PIPES; n++) + dbri->pipes[n].desc = dbri->pipes[n].first_desc = -1; + spin_lock_init(&dbri->cmdlock); /* * 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->dma->intr[0] = 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; - - /* A brute approach - DBRI falls back to working burst size by itself - * On SS20 D_S does not work, so do not try so high. */ - tmp = sbus_readl(dbri->regs + REG0); - tmp |= D_G | D_E; - tmp &= ~D_S; - sbus_writel(tmp, dbri->regs + REG0); - /* * Set up the interrupt queue */ - dma_addr = dbri->dma_dvma + dbri_dma_off(intr, 0); + spin_lock(&dbri->cmdlock); + cmd = dbri->cmdptr = dbri->dma->cmd; *(cmd++) = DBRI_CMD(D_IIQ, 0, 0); *(cmd++) = dma_addr; + *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0); + dbri->cmdptr = cmd; + *(cmd++) = DBRI_CMD(D_WAIT, 1, 0); + *(cmd++) = DBRI_CMD(D_WAIT, 1, 0); + dma_addr = dbri->dma_dvma + dbri_dma_off(cmd, 0); + sbus_writel(dma_addr, dbri->regs + REG8); + spin_unlock(&dbri->cmdlock); - dbri_cmdsend(dbri, cmd); spin_unlock_irqrestore(&dbri->lock, flags); + dbri_cmdwait(dbri); } /* @@ -809,9 +815,9 @@ static void reset_pipe(struct snd_dbri * { int sdp; int desc; - volatile int *cmd; + s32 *cmd; - if (pipe < 0 || pipe > 31) { + if (pipe < 0 || pipe > DBRI_MAX_PIPE) { printk(KERN_ERR "DBRI: reset_pipe called with illegal pipe number\n"); return; } @@ -822,25 +828,29 @@ static void reset_pipe(struct snd_dbri * return; } - cmd = dbri_cmdlock(dbri, NoGetLock); + cmd = dbri_cmdlock(dbri, 3); *(cmd++) = DBRI_CMD(D_SDP, 0, sdp | D_SDP_C | D_SDP_P); *(cmd++) = 0; - dbri_cmdsend(dbri, cmd); + *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0); + dbri_cmdsend(dbri, cmd, 3); desc = dbri->pipes[pipe].first_desc; - while (desc != -1) { - dbri->descs[desc].inuse = 0; - desc = dbri->descs[desc].next; - } + if ( desc >= 0) + do { + dbri->dma->desc[desc].nda = dbri->dma->desc[desc].ba = 0; + desc = dbri->next_desc[desc]; + } while (desc != -1 && desc != dbri->pipes[pipe].first_desc); dbri->pipes[pipe].desc = -1; dbri->pipes[pipe].first_desc = -1; } -/* FIXME: direction as an argument? */ +/* + * Lock must be held before calling this. + */ static void setup_pipe(struct snd_dbri * dbri, int pipe, int sdp) { - if (pipe < 0 || pipe > 31) { + if (pipe < 0 || pipe > DBRI_MAX_PIPE) { printk(KERN_ERR "DBRI: setup_pipe called with illegal pipe number\n"); return; } @@ -860,119 +870,87 @@ 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); } -/* FIXME: direction not needed */ +/* + * Lock must be held before calling this. + */ static void link_time_slot(struct snd_dbri * dbri, int pipe, - enum in_or_out direction, int basepipe, + int prevpipe, int nextpipe, int length, int cycle) { - volatile s32 *cmd; + s32 *cmd; int val; - int prevpipe; - int nextpipe; - if (pipe < 0 || pipe > 31 || basepipe < 0 || basepipe > 31) { + if (pipe < 0 || pipe > DBRI_MAX_PIPE + || prevpipe < 0 || prevpipe > DBRI_MAX_PIPE + || nextpipe < 0 || nextpipe > DBRI_MAX_PIPE) { printk(KERN_ERR "DBRI: link_time_slot called with illegal pipe number\n"); return; } - if (dbri->pipes[pipe].sdp == 0 || dbri->pipes[basepipe].sdp == 0) { + if (dbri->pipes[pipe].sdp == 0 + || dbri->pipes[prevpipe].sdp == 0 + || dbri->pipes[nextpipe].sdp == 0) { printk(KERN_ERR "DBRI: link_time_slot called on uninitialized pipe\n"); return; } - /* Deal with CHI special case: - * "If transmission on edges 0 or 1 is desired, then cycle n - * (where n = # of bit times per frame...) must be used." - * - DBRI data sheet, page 11 - */ - if (basepipe == 16 && direction == PIPEoutput && cycle == 0) - cycle = dbri->chi_bpf; - - if (basepipe == pipe) { - prevpipe = pipe; - nextpipe = pipe; - } else { - /* We're not initializing a new linked list (basepipe != pipe), - * so run through the linked list and find where this pipe - * should be sloted in, based on its cycle. CHI confuses - * things a bit, since it has a single anchor for both its - * transmit and receive lists. - */ - if (basepipe == 16) { - if (direction == PIPEinput) { - prevpipe = dbri->chi_in_pipe; - } else { - prevpipe = dbri->chi_out_pipe; - } - } else { - prevpipe = basepipe; - } - - nextpipe = dbri->pipes[prevpipe].nextpipe; - - while (dbri->pipes[nextpipe].cycle < cycle - && dbri->pipes[nextpipe].nextpipe != basepipe) { - prevpipe = nextpipe; - nextpipe = dbri->pipes[nextpipe].nextpipe; - } - } - - if (prevpipe == 16) { - if (direction == PIPEinput) { - dbri->chi_in_pipe = pipe; - } else { - dbri->chi_out_pipe = pipe; - } - } else { - dbri->pipes[prevpipe].nextpipe = pipe; - } - + dbri->pipes[prevpipe].nextpipe = pipe; dbri->pipes[pipe].nextpipe = nextpipe; - dbri->pipes[pipe].cycle = cycle; dbri->pipes[pipe].length = length; - cmd = dbri_cmdlock(dbri, NoGetLock); + cmd = dbri_cmdlock(dbri, 4); - if (direction == PIPEinput) { - val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(prevpipe) | pipe; + if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) { + /* Deal with CHI special case: + * "If transmission on edges 0 or 1 is desired, then cycle n + * (where n = # of bit times per frame...) must be used." + * - DBRI data sheet, page 11 + */ + if (prevpipe == 16 && cycle == 0) + cycle = dbri->chi_bpf; + + val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(prevpipe) | pipe; *(cmd++) = DBRI_CMD(D_DTS, 0, val); + *(cmd++) = 0; *(cmd++) = D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe); - *(cmd++) = 0; } else { - val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(prevpipe) | pipe; + val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(prevpipe) | pipe; *(cmd++) = DBRI_CMD(D_DTS, 0, val); - *(cmd++) = 0; *(cmd++) = D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe); + *(cmd++) = 0; } + *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0); - dbri_cmdsend(dbri, cmd); + dbri_cmdsend(dbri, cmd, 4); } +#if 0 +/* + * Lock must be held before calling this. + */ static void unlink_time_slot(struct snd_dbri * dbri, int pipe, enum in_or_out direction, int prevpipe, int nextpipe) { - volatile s32 *cmd; + s32 *cmd; int val; - if (pipe < 0 || pipe > 31 || prevpipe < 0 || prevpipe > 31) { + if (pipe < 0 || pipe > DBRI_MAX_PIPE + || prevpipe < 0 || prevpipe > DBRI_MAX_PIPE + || nextpipe < 0 || nextpipe > DBRI_MAX_PIPE) { printk(KERN_ERR "DBRI: unlink_time_slot called with illegal pipe number\n"); return; } - cmd = dbri_cmdlock(dbri, NoGetLock); + cmd = dbri_cmdlock(dbri, 4); if (direction == PIPEinput) { val = D_DTS_VI | D_DTS_DEL | D_DTS_PRVIN(prevpipe) | pipe; @@ -985,9 +963,11 @@ static void unlink_time_slot(struct snd_ *(cmd++) = 0; *(cmd++) = D_TS_NEXT(nextpipe); } + *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0); - dbri_cmdsend(dbri, cmd); + dbri_cmdsend(dbri, cmd, 4); } +#endif /* xmit_fixed() / recv_fixed() * @@ -1001,13 +981,16 @@ static void unlink_time_slot(struct snd_ * the actual time slot is. The interrupt handler takes care of bit * ordering and alignment. An 8-bit time slot will always end up * in the low-order 8 bits, filled either MSB-first or LSB-first, - * depending on the settings passed to setup_pipe() + * depending on the settings passed to setup_pipe(). + * + * Lock must not be held before calling it. */ static void xmit_fixed(struct snd_dbri * dbri, int pipe, unsigned int data) { - volatile s32 *cmd; + s32 *cmd; + unsigned long flags; - if (pipe < 16 || pipe > 31) { + if (pipe < 16 || pipe > DBRI_MAX_PIPE) { printk(KERN_ERR "DBRI: xmit_fixed: Illegal pipe number\n"); return; } @@ -1032,17 +1015,22 @@ static void xmit_fixed(struct snd_dbri * if (dbri->pipes[pipe].sdp & D_SDP_MSB) data = reverse_bytes(data, dbri->pipes[pipe].length); - cmd = dbri_cmdlock(dbri, GetLock); + cmd = dbri_cmdlock(dbri, 3); *(cmd++) = DBRI_CMD(D_SSP, 0, pipe); *(cmd++) = data; + *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0); + + spin_lock_irqsave(&dbri->lock, flags); + dbri_cmdsend(dbri, cmd, 3); + spin_unlock_irqrestore(&dbri->lock, flags); + dbri_cmdwait(dbri); - dbri_cmdsend(dbri, cmd); } static void recv_fixed(struct snd_dbri * dbri, int pipe, volatile __u32 * ptr) { - if (pipe < 16 || pipe > 31) { + if (pipe < 16 || pipe > DBRI_MAX_PIPE) { printk(KERN_ERR "DBRI: recv_fixed called with illegal pipe number\n"); return; } @@ -1071,12 +1059,16 @@ static void recv_fixed(struct snd_dbri * * and work by building chains of descriptors which identify the * data buffers. Buffers too large for a single descriptor will * be spread across multiple descriptors. + * + * All descriptors create a ring buffer. + * + * Lock must be held before calling this. */ static int setup_descs(struct snd_dbri * dbri, int streamno, unsigned int period) { struct dbri_streaminfo *info = &dbri->stream_info[streamno]; __u32 dvma_buffer; - int desc = 0; + int desc; int len; int first_desc = -1; int last_desc = -1; @@ -1119,11 +1111,23 @@ static int setup_descs(struct snd_dbri * len &= ~3; } + /* Free descriptors if pipe has any */ + desc = dbri->pipes[info->pipe].first_desc; + if ( desc >= 0) + do { + dbri->dma->desc[desc].nda = dbri->dma->desc[desc].ba = 0; + desc = dbri->next_desc[desc]; + } while (desc != -1 && desc != dbri->pipes[info->pipe].first_desc); + + dbri->pipes[info->pipe].desc = -1; + dbri->pipes[info->pipe].first_desc = -1; + + desc = 0; while (len > 0) { int mylen; for (; desc < DBRI_NO_DESCS; desc++) { - if (!dbri->descs[desc].inuse) + if (!dbri->dma->desc[desc].ba) break; } if (desc == DBRI_NO_DESCS) { @@ -1131,37 +1135,33 @@ static int setup_descs(struct snd_dbri * return -1; } - if (len > DBRI_TD_MAXCNT) { - mylen = DBRI_TD_MAXCNT; /* 8KB - 1 */ - } else { + if (len > DBRI_TD_MAXCNT) + mylen = DBRI_TD_MAXCNT; /* 8KB - 4 */ + else mylen = len; - } - if (mylen > period) { + + if (mylen > period) mylen = period; - } - dbri->descs[desc].inuse = 1; - dbri->descs[desc].next = -1; + dbri->next_desc[desc] = -1; dbri->dma->desc[desc].ba = dvma_buffer; dbri->dma->desc[desc].nda = 0; if (streamno == DBRI_PLAY) { - dbri->descs[desc].len = mylen; dbri->dma->desc[desc].word1 = DBRI_TD_CNT(mylen); dbri->dma->desc[desc].word4 = 0; - if (first_desc != -1) - dbri->dma->desc[desc].word1 |= DBRI_TD_M; + dbri->dma->desc[desc].word1 |= + DBRI_TD_F | DBRI_TD_B; } else { - dbri->descs[desc].len = 0; dbri->dma->desc[desc].word1 = 0; dbri->dma->desc[desc].word4 = DBRI_RD_B | DBRI_RD_BCNT(mylen); } - if (first_desc == -1) { + if (first_desc == -1) first_desc = desc; - } else { - dbri->descs[last_desc].next = desc; + else { + dbri->next_desc[last_desc] = desc; dbri->dma->desc[last_desc].nda = dbri->dma_dvma + dbri_dma_off(desc, desc); } @@ -1176,21 +1176,24 @@ static int setup_descs(struct snd_dbri * return -1; } - dbri->dma->desc[last_desc].word1 &= ~DBRI_TD_M; - if (streamno == DBRI_PLAY) { - dbri->dma->desc[last_desc].word1 |= - DBRI_TD_I | DBRI_TD_F | DBRI_TD_B; - } + dbri->dma->desc[last_desc].nda = + dbri->dma_dvma + dbri_dma_off(desc, first_desc); + dbri->next_desc[last_desc] = first_desc; dbri->pipes[info->pipe].first_desc = first_desc; dbri->pipes[info->pipe].desc = first_desc; - for (desc = first_desc; desc != -1; desc = dbri->descs[desc].next) { +#ifdef DBRI_DEBUG + for (desc = first_desc; desc != -1; ) { dprintk(D_DESC, "DESC %d: %08x %08x %08x %08x\n", desc, dbri->dma->desc[desc].word1, dbri->dma->desc[desc].ba, dbri->dma->desc[desc].nda, dbri->dma->desc[desc].word4); + desc = dbri->next_desc[desc]; + if ( desc == first_desc ) + break; } +#endif return 0; } @@ -1207,56 +1210,30 @@ (give clock/frame sync) or slave (take c enum master_or_slave { CHImaster, CHIslave }; +/* + * Lock must not be held before calling it. + */ static void reset_chi(struct snd_dbri * dbri, enum master_or_slave master_or_slave, int bits_per_frame) { - volatile s32 *cmd; + s32 *cmd; int val; - static int chi_initialized = 0; /* FIXME: mutex? */ - - if (!chi_initialized) { - - cmd = dbri_cmdlock(dbri, GetLock); - - /* Set CHI Anchor: Pipe 16 */ - - val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(16) | D_PIPE(16); - *(cmd++) = DBRI_CMD(D_DTS, 0, val); - *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16); - *(cmd++) = 0; - - val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(16) | D_PIPE(16); - *(cmd++) = DBRI_CMD(D_DTS, 0, val); - *(cmd++) = 0; - *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16); - dbri->pipes[16].sdp = 1; - dbri->pipes[16].nextpipe = 16; - dbri->chi_in_pipe = 16; - dbri->chi_out_pipe = 16; - -#if 0 - chi_initialized++; -#endif - } else { - int pipe; + /* Set CHI Anchor: Pipe 16 */ - for (pipe = dbri->chi_in_pipe; - pipe != 16; pipe = dbri->pipes[pipe].nextpipe) { - unlink_time_slot(dbri, pipe, PIPEinput, - 16, dbri->pipes[pipe].nextpipe); - } - for (pipe = dbri->chi_out_pipe; - pipe != 16; pipe = dbri->pipes[pipe].nextpipe) { - unlink_time_slot(dbri, pipe, PIPEoutput, - 16, dbri->pipes[pipe].nextpipe); - } + cmd = dbri_cmdlock(dbri, 4); + val = D_DTS_VO | D_DTS_VI | D_DTS_INS + | D_DTS_PRVIN(16) | D_PIPE(16) | D_DTS_PRVOUT(16); + *(cmd++) = DBRI_CMD(D_DTS, 0, val); + *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16); + *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16); + *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0); + dbri_cmdsend(dbri, cmd, 4); - dbri->chi_in_pipe = 16; - dbri->chi_out_pipe = 16; + dbri->pipes[16].sdp = 1; + dbri->pipes[16].nextpipe = 16; - cmd = dbri_cmdlock(dbri, GetLock); - } + cmd = dbri_cmdlock(dbri, 4); if (master_or_slave == CHIslave) { /* Setup DBRI for CHI Slave - receive clock, frame sync (FS) @@ -1295,8 +1272,9 @@ #endif *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0); *(cmd++) = DBRI_CMD(D_CDM, 0, D_CDM_XCE | D_CDM_XEN | D_CDM_REN); + *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0); - dbri_cmdsend(dbri, cmd); + dbri_cmdsend(dbri, cmd, 4); } /* @@ -1307,9 +1285,14 @@ #endif In the standard SPARC audio configuration, the CS4215 codec is attached to the DBRI via the CHI interface and few of the DBRI's PIO pins. + * Lock must not be held before calling it. + */ static void cs4215_setup_pipes(struct snd_dbri * dbri) { + unsigned long flags; + + spin_lock_irqsave(&dbri->lock, flags); /* * Data mode: * Pipe 4: Send timeslots 1-4 (audio data) @@ -1333,6 +1316,9 @@ static void cs4215_setup_pipes(struct sn setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB); setup_pipe(dbri, 18, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB); setup_pipe(dbri, 19, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB); + spin_unlock_irqrestore(&dbri->lock, flags); + + dbri_cmdwait(dbri); } static int cs4215_init_data(struct cs4215 *mm) @@ -1364,7 +1350,7 @@ static int cs4215_init_data(struct cs421 mm->status = 0; mm->version = 0xff; mm->precision = 8; /* For ULAW */ - mm->channels = 2; + mm->channels = 1; return 0; } @@ -1379,16 +1365,8 @@ static void cs4215_setdata(struct snd_db } else { /* Start by setting the playback attenuation. */ struct dbri_streaminfo *info = &dbri->stream_info[DBRI_PLAY]; - 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; - } + int left_gain = info->left_gain & 0x3f; + int right_gain = info->right_gain & 0x3f; dbri->mm.data[0] &= ~0x3f; /* Reset the volume bits */ dbri->mm.data[1] &= ~0x3f; @@ -1397,8 +1375,8 @@ static void cs4215_setdata(struct snd_db /* Now set the recording gain. */ info = &dbri->stream_info[DBRI_REC]; - left_gain = info->left_gain % 16; - right_gain = info->right_gain % 16; + left_gain = info->left_gain & 0xf; + right_gain = info->right_gain & 0xf; dbri->mm.data[2] |= CS4215_LG(left_gain); dbri->mm.data[3] |= CS4215_RG(right_gain); } @@ -1413,6 +1391,7 @@ static void cs4215_open(struct snd_dbri { int data_width; u32 tmp; + unsigned long flags; dprintk(D_MM, "cs4215_open: %d channels, %d bits\n", dbri->mm.channels, dbri->mm.precision); @@ -1437,6 +1416,7 @@ static void cs4215_open(struct snd_dbri * bits. The CS4215, it seems, observes TSIN (the delayed signal) * even if it's the CHI master. Don't ask me... */ + spin_lock_irqsave(&dbri->lock, flags); tmp = sbus_readl(dbri->regs + REG0); tmp &= ~(D_C); /* Disable CHI */ sbus_writel(tmp, dbri->regs + REG0); @@ -1455,15 +1435,16 @@ static void cs4215_open(struct snd_dbri */ data_width = dbri->mm.channels * dbri->mm.precision; - link_time_slot(dbri, 20, PIPEoutput, 16, 32, dbri->mm.offset + 32); - link_time_slot(dbri, 4, PIPEoutput, 16, data_width, dbri->mm.offset); - link_time_slot(dbri, 6, PIPEinput, 16, data_width, dbri->mm.offset); - link_time_slot(dbri, 21, PIPEinput, 16, 16, dbri->mm.offset + 40); + link_time_slot(dbri, 4, 16, 16, data_width, dbri->mm.offset); + link_time_slot(dbri, 20, 4, 16, 32, dbri->mm.offset + 32); + link_time_slot(dbri, 6, 16, 16, data_width, dbri->mm.offset); + link_time_slot(dbri, 21, 6, 16, 16, dbri->mm.offset + 40); /* FIXME: enable CHI after _setdata? */ tmp = sbus_readl(dbri->regs + REG0); tmp |= D_C; /* Enable CHI */ sbus_writel(tmp, dbri->regs + REG0); + spin_unlock_irqrestore(&dbri->lock, flags); cs4215_setdata(dbri, 0); } @@ -1475,6 +1456,7 @@ static int cs4215_setctrl(struct snd_dbr { int i, val; u32 tmp; + unsigned long flags; /* FIXME - let the CPU do something useful during these delays */ @@ -1511,6 +1493,7 @@ static int cs4215_setctrl(struct snd_dbr * done in hardware by a TI 248 that delays the DBRI->4215 * frame sync signal by eight clock cycles. Anybody know why? */ + spin_lock_irqsave(&dbri->lock, flags); tmp = sbus_readl(dbri->regs + REG0); tmp &= ~D_C; /* Disable CHI */ sbus_writel(tmp, dbri->regs + REG0); @@ -1524,17 +1507,20 @@ static int cs4215_setctrl(struct snd_dbr * Pipe 19: Receive timeslot 7 (version). */ - link_time_slot(dbri, 17, PIPEoutput, 16, 32, dbri->mm.offset); - link_time_slot(dbri, 18, PIPEinput, 16, 8, dbri->mm.offset); - link_time_slot(dbri, 19, PIPEinput, 16, 8, dbri->mm.offset + 48); + link_time_slot(dbri, 17, 16, 16, 32, dbri->mm.offset); + link_time_slot(dbri, 18, 16, 16, 8, dbri->mm.offset); + link_time_slot(dbri, 19, 18, 16, 8, dbri->mm.offset + 48); + spin_unlock_irqrestore(&dbri->lock, flags); /* Wait for the chip to echo back CLB (Control Latch Bit) as zero */ dbri->mm.ctrl[0] &= ~CS4215_CLB; xmit_fixed(dbri, 17, *(int *)dbri->mm.ctrl); + spin_lock_irqsave(&dbri->lock, flags); tmp = sbus_readl(dbri->regs + REG0); tmp |= D_C; /* Enable CHI */ sbus_writel(tmp, dbri->regs + REG0); + spin_unlock_irqrestore(&dbri->lock, flags); for (i = 10; ((dbri->mm.status & 0xe4) != 0x20); --i) { msleep_interruptible(1); @@ -1614,8 +1600,7 @@ static int cs4215_prepare(struct snd_dbr CS4215_BSEL_128 | CS4215_FREQ[freq_idx].xtal; dbri->mm.channels = channels; - /* Stereo bit: 8 bit stereo not working yet. */ - if ((channels > 1) && (dbri->mm.precision == 16)) + if (channels == 2) dbri->mm.ctrl[1] |= CS4215_DFR_STEREO; ret = cs4215_setctrl(dbri); @@ -1655,7 +1640,6 @@ static int cs4215_init(struct snd_dbri * } cs4215_setup_pipes(dbri); - cs4215_init_data(&dbri->mm); /* Enable capture of the status & version timeslots. */ @@ -1684,88 +1668,71 @@ buffer and calls dbri_process_one_interr Complicated interrupts are handled by dedicated functions (which appear first in this file). Any pending interrupts can be serviced by calling dbri_process_interrupt_buffer(), which works even if the CPU's -interrupts are disabled. This function is used by dbri_cmdlock() -to make sure we're synced up with the chip before each command sequence, -even if we're running cli'ed. +interrupts are disabled. */ /* xmit_descs() * - * Transmit the current TD's for recording/playing, if needed. + * Starts transmiting the current TD's for recording/playing. * For playback, ALSA has filled the DMA memory with new data (we hope). */ -static void xmit_descs(unsigned long data) +static void xmit_descs(struct snd_dbri *dbri) { - struct snd_dbri *dbri = (struct snd_dbri *) data; struct dbri_streaminfo *info; - volatile s32 *cmd; + s32 *cmd; unsigned long flags; int first_td; if (dbri == NULL) return; /* Disabled */ - /* First check the recording stream for buffer overflow */ info = &dbri->stream_info[DBRI_REC]; spin_lock_irqsave(&dbri->lock, flags); - if ((info->left >= info->size) && (info->pipe >= 0)) { + if (info->pipe >= 0) { first_td = dbri->pipes[info->pipe].first_desc; dprintk(D_DESC, "xmit_descs rec @ TD %d\n", first_td); /* Stream could be closed by the time we run. */ - if (first_td < 0) { - goto play; - } - - cmd = dbri_cmdlock(dbri, NoGetLock); - *(cmd++) = DBRI_CMD(D_SDP, 0, - dbri->pipes[info->pipe].sdp - | D_SDP_P | D_SDP_EVERY | D_SDP_C); - *(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, first_td); - dbri_cmdsend(dbri, cmd); + if (first_td >= 0) { + cmd = dbri_cmdlock(dbri, 2); + *(cmd++) = DBRI_CMD(D_SDP, 0, + dbri->pipes[info->pipe].sdp + | D_SDP_P | D_SDP_EVERY | D_SDP_C); + *(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, first_td); + dbri_cmdsend(dbri, cmd, 2); - /* Reset our admin of the pipe & bytes read. */ - dbri->pipes[info->pipe].desc = first_td; - info->left = 0; + /* Reset our admin of the pipe. */ + dbri->pipes[info->pipe].desc = first_td; + } } -play: - spin_unlock_irqrestore(&dbri->lock, flags); - - /* Now check the playback stream for buffer underflow */ info = &dbri->stream_info[DBRI_PLAY]; - spin_lock_irqsave(&dbri->lock, flags); - if ((info->left <= 0) && (info->pipe >= 0)) { + if (info->pipe >= 0) { first_td = dbri->pipes[info->pipe].first_desc; dprintk(D_DESC, "xmit_descs play @ TD %d\n", first_td); /* Stream could be closed by the time we run. */ - if (first_td < 0) { - spin_unlock_irqrestore(&dbri->lock, flags); - return; - } - - cmd = dbri_cmdlock(dbri, NoGetLock); - *(cmd++) = DBRI_CMD(D_SDP, 0, - dbri->pipes[info->pipe].sdp - | D_SDP_P | D_SDP_EVERY | D_SDP_C); - *(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, first_td); - dbri_cmdsend(dbri, cmd); + if (first_td >= 0) { + cmd = dbri_cmdlock(dbri, 2); + *(cmd++) = DBRI_CMD(D_SDP, 0, + dbri->pipes[info->pipe].sdp + | D_SDP_P | D_SDP_EVERY | D_SDP_C); + *(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, first_td); + dbri_cmdsend(dbri, cmd, 2); - /* Reset our admin of the pipe & bytes written. */ - dbri->pipes[info->pipe].desc = first_td; - info->left = info->size; + /* Reset our admin of the pipe. */ + dbri->pipes[info->pipe].desc = first_td; + } } + spin_unlock_irqrestore(&dbri->lock, flags); } -static DECLARE_TASKLET(xmit_descs_task, xmit_descs, 0); - /* transmission_complete_intr() * * Called by main interrupt handler when DBRI signals transmission complete @@ -1775,9 +1742,9 @@ static DECLARE_TASKLET(xmit_descs_task, * them as available. Stops when the first descriptor is found without * TBC (Transmit Buffer Complete) set, or we've run through them all. * - * The DMA buffers are not released, but re-used. Since the transmit buffer - * descriptors are not clobbered, they can be re-submitted as is. This is - * done by the xmit_descs() tasklet above since that could take longer. + * The DMA buffers are not released. They form a ring buffer and + * they are filled by ALSA while others are transmitted by DMA. + * */ static void transmission_complete_intr(struct snd_dbri * dbri, int pipe) @@ -1803,21 +1770,9 @@ static void transmission_complete_intr(s dprintk(D_INT, "TD %d, status 0x%02x\n", td, status); dbri->dma->desc[td].word4 = 0; /* Reset it for next time. */ - info->offset += dbri->descs[td].len; - info->left -= dbri->descs[td].len; - - /* On the last TD, transmit them all again. */ - if (dbri->descs[td].next == -1) { - if (info->left > 0) { - printk(KERN_WARNING - "%d bytes left after last transfer.\n", - info->left); - info->left = 0; - } - tasklet_schedule(&xmit_descs_task); - } + info->offset += DBRI_RD_CNT(dbri->dma->desc[td].word1); - td = dbri->descs[td].next; + td = dbri->next_desc[td]; dbri->pipes[pipe].desc = td; } @@ -1841,30 +1796,18 @@ static void reception_complete_intr(stru return; } - dbri->descs[rd].inuse = 0; - dbri->pipes[pipe].desc = dbri->descs[rd].next; + dbri->pipes[pipe].desc = dbri->next_desc[rd]; status = dbri->dma->desc[rd].word1; dbri->dma->desc[rd].word1 = 0; /* Reset it for next time. */ info = &dbri->stream_info[DBRI_REC]; info->offset += DBRI_RD_CNT(status); - info->left += DBRI_RD_CNT(status); /* FIXME: Check status */ dprintk(D_INT, "Recv RD %d, status 0x%02x, len %d\n", rd, DBRI_RD_STATUS(status), DBRI_RD_CNT(status)); - /* On the last TD, transmit them all again. */ - if (dbri->descs[rd].next == -1) { - if (info->left > info->size) { - printk(KERN_WARNING - "%d bytes recorded in %d size buffer.\n", - info->left, info->size); - } - tasklet_schedule(&xmit_descs_task); - } - /* Notify ALSA */ if (spin_is_locked(&dbri->lock)) { spin_unlock(&dbri->lock); @@ -1892,16 +1835,11 @@ #endif channel, code, rval); } - if (channel == D_INTR_CMD && command == D_WAIT) { - dbri->wait_ackd = val; - if (dbri->wait_send != val) { - printk(KERN_ERR "Processing wait command %d when %d was send.\n", - val, dbri->wait_send); - } - return; - } - switch (code) { + case D_INTR_CMDI: + if (command != D_WAIT) + printk(KERN_ERR "DBRI: Command read interrupt\n"); + break; case D_INTR_BRDY: reception_complete_intr(dbri, channel); break; @@ -1914,8 +1852,10 @@ #endif * resend SDP command with clear pipe bit (C) set */ { - volatile s32 *cmd; - + /* FIXME: do something useful in case of underrun */ + printk(KERN_ERR "DBRI: Underrun error\n"); +#if 0 + s32 *cmd; int pipe = channel; int td = dbri->pipes[pipe].desc; @@ -1926,6 +1866,7 @@ #endif | D_SDP_P | D_SDP_C | D_SDP_2SAME); *(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, td); dbri_cmdsend(dbri, cmd); +#endif } break; case D_INTR_FXDT: @@ -1946,9 +1887,7 @@ #endif /* dbri_process_interrupt_buffer advances through the DBRI's interrupt * buffer until it finds a zero word (indicating nothing more to do * right now). Non-zero words require processing and are handed off - * to dbri_process_one_interrupt AFTER advancing the pointer. This - * order is important since we might recurse back into this function - * and need to make sure the pointer has been advanced first. + * to dbri_process_one_interrupt AFTER advancing the pointer. */ static void dbri_process_interrupt_buffer(struct snd_dbri * dbri) { @@ -1957,10 +1896,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); } @@ -2020,8 +1957,6 @@ static irqreturn_t snd_dbri_interrupt(in dbri_process_interrupt_buffer(dbri); - /* FIXME: Write 0 into regs to ACK interrupt */ - spin_unlock(&dbri->lock); return IRQ_HANDLED; @@ -2039,8 +1974,8 @@ static struct snd_pcm_hardware snd_dbri_ SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_BE, - .rates = SNDRV_PCM_RATE_8000_48000, - .rate_min = 8000, + .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_5512, + .rate_min = 5512, .rate_max = 48000, .channels_min = 1, .channels_max = 2, @@ -2051,6 +1986,39 @@ static struct snd_pcm_hardware snd_dbri_ .periods_max = 1024, }; +static int snd_hw_rule_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *c = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + struct snd_mask fmt; + + snd_mask_any(&fmt); + if (c->min > 1) { + fmt.bits[0] &= SNDRV_PCM_FMTBIT_S16_BE; + return snd_mask_refine(f, &fmt); + } + return 0; +} + +static int snd_hw_rule_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *c = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + struct snd_interval ch; + + snd_interval_any(&ch); + if (!(f->bits[0] & SNDRV_PCM_FMTBIT_S16_BE)) { + ch.min = ch.max = 1; + ch.integer = 1; + return snd_interval_refine(c, &ch); + } + return 0; +} + static int snd_dbri_open(struct snd_pcm_substream *substream) { struct snd_dbri *dbri = snd_pcm_substream_chip(substream); @@ -2063,12 +2031,19 @@ static int snd_dbri_open(struct snd_pcm_ spin_lock_irqsave(&dbri->lock, flags); info->substream = substream; - info->left = 0; info->offset = 0; info->dvma_buffer = 0; info->pipe = -1; spin_unlock_irqrestore(&dbri->lock, flags); + snd_pcm_hw_rule_add(runtime,0,SNDRV_PCM_HW_PARAM_CHANNELS, + snd_hw_rule_format, 0, SNDRV_PCM_HW_PARAM_FORMAT, + -1); + snd_pcm_hw_rule_add(runtime,0,SNDRV_PCM_HW_PARAM_FORMAT, + snd_hw_rule_channels, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + -1); + cs4215_open(dbri); return 0; @@ -2081,7 +2056,6 @@ static int snd_dbri_close(struct snd_pcm dprintk(D_USR, "close audio output.\n"); info->substream = NULL; - info->left = 0; info->offset = 0; return 0; @@ -2134,6 +2108,7 @@ static int snd_dbri_hw_free(struct snd_p struct snd_dbri *dbri = snd_pcm_substream_chip(substream); struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream); int direction; + dprintk(D_USR, "hw_free.\n"); /* hw_free can get called multiple times. Only unmap the DMA once. @@ -2148,7 +2123,10 @@ static int snd_dbri_hw_free(struct snd_p substream->runtime->buffer_size, direction); info->dvma_buffer = 0; } - info->pipe = -1; + if (info->pipe != -1) { + reset_pipe(dbri, info->pipe); + info->pipe = -1; + } return snd_pcm_lib_free_pages(substream); } @@ -2157,18 +2135,16 @@ static int snd_dbri_prepare(struct snd_p { struct snd_dbri *dbri = snd_pcm_substream_chip(substream); struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream); - struct snd_pcm_runtime *runtime = substream->runtime; int ret; info->size = snd_pcm_lib_buffer_bytes(substream); if (DBRI_STREAMNO(substream) == DBRI_PLAY) info->pipe = 4; /* Send pipe */ - else { + else info->pipe = 6; /* Receive pipe */ - info->left = info->size; /* To trigger submittal */ - } spin_lock_irq(&dbri->lock); + info->offset = 0; /* Setup the all the transmit/receive desciptors to cover the * whole DMA buffer. @@ -2176,8 +2152,6 @@ static int snd_dbri_prepare(struct snd_p ret = setup_descs(dbri, DBRI_STREAMNO(substream), snd_pcm_lib_period_bytes(substream)); - runtime->stop_threshold = DBRI_TD_MAXCNT / runtime->channels; - spin_unlock_irq(&dbri->lock); dprintk(D_USR, "prepare audio output. %d bytes\n", info->size); @@ -2194,14 +2168,11 @@ static int snd_dbri_trigger(struct snd_p case SNDRV_PCM_TRIGGER_START: dprintk(D_USR, "start audio, period is %d bytes\n", (int)snd_pcm_lib_period_bytes(substream)); - /* Enable & schedule the tasklet that re-submits the TDs. */ - xmit_descs_task.data = (unsigned long)dbri; - tasklet_schedule(&xmit_descs_task); + /* Re-submit the TDs. */ + xmit_descs(dbri); break; case SNDRV_PCM_TRIGGER_STOP: dprintk(D_USR, "stop audio.\n"); - /* Make the tasklet bail out immediately. */ - xmit_descs_task.data = 0; reset_pipe(dbri, info->pipe); break; default: @@ -2219,8 +2190,8 @@ static snd_pcm_uframes_t snd_dbri_pointe ret = bytes_to_frames(substream->runtime, info->offset) % substream->runtime->buffer_size; - dprintk(D_USR, "I/O pointer: %ld frames, %d bytes left.\n", - ret, info->left); + dprintk(D_USR, "I/O pointer: %ld frames of %ld.\n", + ret, substream->runtime->buffer_size); return ret; } @@ -2254,7 +2225,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, @@ -2303,7 +2273,6 @@ static int snd_cs4215_put_volume(struct { struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol); struct dbri_streaminfo *info = &dbri->stream_info[kcontrol->private_value]; - unsigned long flags; int changed = 0; if (info->left_gain != ucontrol->value.integer.value[0]) { @@ -2318,13 +2287,9 @@ static int snd_cs4215_put_volume(struct /* First mute outputs, and wait 1/8000 sec (125 us) * to make sure this takes. This avoids clicking noises. */ - spin_lock_irqsave(&dbri->lock, flags); - cs4215_setdata(dbri, 1); udelay(125); cs4215_setdata(dbri, 0); - - spin_unlock_irqrestore(&dbri->lock, flags); } return changed; } @@ -2371,7 +2336,6 @@ static int snd_cs4215_put_single(struct struct snd_ctl_elem_value *ucontrol) { struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol); - unsigned long flags; int elem = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff; int mask = (kcontrol->private_value >> 16) & 0xff; @@ -2404,13 +2368,9 @@ static int snd_cs4215_put_single(struct /* First mute outputs, and wait 1/8000 sec (125 us) * to make sure this takes. This avoids clicking noises. */ - spin_lock_irqsave(&dbri->lock, flags); - cs4215_setdata(dbri, 1); udelay(125); cs4215_setdata(dbri, 0); - - spin_unlock_irqrestore(&dbri->lock, flags); } return changed; } @@ -2473,7 +2433,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 +2464,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 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->nextpipe); } } } @@ -2549,7 +2507,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 +2626,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..664dd4c 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -123,6 +123,7 @@ struct audioformat { unsigned int rate_min, rate_max; /* min/max rates */ unsigned int nr_rates; /* number of rate table entries */ unsigned int *rate_table; /* rate table */ + unsigned int needs_knot; /* any unusual rates? */ }; struct snd_usb_substream; @@ -1759,6 +1760,9 @@ static int check_hw_params_convention(st } channels[f->format] |= (1 << f->channels); rates[f->format] |= f->rates; + /* needs knot? */ + if (f->needs_knot) + goto __out; } /* check whether channels and rates match for all formats */ cmaster = rmaster = 0; @@ -1799,6 +1803,38 @@ static int check_hw_params_convention(st return err; } +/* + * If the device supports unusual bit rates, does the request meet these? + */ +static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime, + struct snd_usb_substream *subs) +{ + struct list_head *p; + struct snd_pcm_hw_constraint_list constraints_rates; + int err; + + list_for_each(p, &subs->fmt_list) { + struct audioformat *fp; + fp = list_entry(p, struct audioformat, list); + + if (!fp->needs_knot) + continue; + + constraints_rates.count = fp->nr_rates; + constraints_rates.list = fp->rate_table; + constraints_rates.mask = 0; + + err = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_rates); + + if (err < 0) + return err; + } + + return 0; +} + /* * set up the runtime hardware information. @@ -1861,6 +1897,8 @@ static int setup_hw_info(struct snd_pcm_ SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0) return err; + if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0) + return err; } return 0; } @@ -2049,7 +2087,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 @@ -2406,6 +2444,7 @@ static int parse_audio_format_rates(stru unsigned char *fmt, int offset) { int nr_rates = fmt[offset]; + int found; if (fmt[0] < offset + 1 + 3 * (nr_rates ? nr_rates : 2)) { snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n", chip->dev->devnum, fp->iface, fp->altsetting); @@ -2428,6 +2467,7 @@ static int parse_audio_format_rates(stru return -1; } + fp->needs_knot = 0; fp->nr_rates = nr_rates; fp->rate_min = fp->rate_max = combine_triple(&fmt[8]); for (r = 0, idx = offset + 1; r < nr_rates; r++, idx += 3) { @@ -2436,13 +2476,19 @@ static int parse_audio_format_rates(stru fp->rate_min = rate; else if (rate > fp->rate_max) fp->rate_max = rate; + found = 0; for (c = 0; c < (int)ARRAY_SIZE(conv_rates); c++) { if (rate == conv_rates[c]) { + found = 1; fp->rates |= (1 << c); break; } } + if (!found) + fp->needs_knot = 1; } + if (fp->needs_knot) + fp->rates |= SNDRV_PCM_RATE_KNOT; } else { /* continuous rates */ fp->rates = SNDRV_PCM_RATE_CONTINUOUS; @@ -3499,7 +3545,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