GIT e8c68195cd1a827829326a784e2dbd5f9b29109e git+ssh://master.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6.git e8c68195cd1a827829326a784e2dbd5f9b29109e commit e8c68195cd1a827829326a784e2dbd5f9b29109e Author: Pavel Machek Date: Tue Apr 1 15:33:22 2008 +0200 [ALSA] fix comments in sound/core.h Two sentences seem to be spliced into one in comment, fix that and fix english. Also fix codingstyle. Signed-off-by: Pavel Machek Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai commit 2399a3e3a4eb3cd31c4647a138ec00bd8521acd6 Author: Clemens Ladisch Date: Tue Apr 1 10:02:18 2008 +0200 [ALSA] oxygen: fix line-in recording selection (now for real) On C-Media cards, the GPIO pin 0 of the CM9780 must be handled exactly like on Xonar cards, so move the Xonar code to the common mixer code. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai commit 4c17d1d22dfcb9ceaf7b72539ee5e531011ecad0 Author: Herton Ronaldo Krzesinski Date: Sat Mar 22 10:26:05 2008 +0100 [ALSA] hda-codec - Support mic automute for Clevo M720R/SR Add support for mic automute in clevo-m720r ALC883 model, and rename it to more generic clevo-m720. Also change model entry in ALSA-Configuration.txt accordingly. Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: Takashi Iwai commit 22ded9ecccc8c70041a42dbe77fbf69dc8e401c5 Author: Herton Ronaldo Krzesinski Date: Sat Mar 22 10:25:30 2008 +0100 [ALSA] hda-codec - Map clevo-m720r ALC883 model for Clevo M720SR Map clevo-m720r ALC883 model for Clevo M720SR. Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: Takashi Iwai commit 2917e9a73597f98a24f1faf040d59bbbf29dedaf Author: Stas Sergeev Date: Sat Mar 22 10:12:37 2008 +0100 [ALSA] pcsp: remove downsampling pcsp: remove S16->U8 downsampling as dmix now supports U8 natively. Signed-off-by: Stas Sergeev Signed-off-by: Takashi Iwai commit ebf5a77c892d7b820367ab7a1b7eb112504c3eff Author: Takashi Iwai Date: Sat Mar 22 10:11:08 2008 +0100 [ALSA] ymfpci - Fix race at removal free_irq() must be called first to avoid races at removal. Signed-off-by: Takashi Iwai commit bc0a36f68689176b861b199a7da421d255624822 Author: Takashi Iwai Date: Thu Mar 20 12:30:36 2008 +0100 [ALSA] hda-codec - Add missing models in ALSA-Configuration.txt Signed-off-by: Takashi Iwai commit 4fe9ddb2ae24e0318bd79d448653d5ae30d1402c Author: Herton Ronaldo Krzesinski Date: Thu Mar 20 12:14:59 2008 +0100 [ALSA] hda-codec - Use common 3stack-6ch mixer for 3stack-hp model Forgot one more: 3stack-hp model also have now the same mixer as 3stack-6ch (after DAC assignment fix in ALC883), so use it avoiding duplicating the same mixer definition. Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: Takashi Iwai commit 15c234eb3cd22b520b1d8f9f263dda43081b569e Author: Herton Ronaldo Krzesinski Date: Thu Mar 20 12:14:28 2008 +0100 [ALSA] hda-codec - Use base ALC883 mixer for 6stack-dell model After DAC assignment fix in ALC883, alc888_6st_dell_mixer is now the same as alc883_base_mixer. Avoid duplicated code and use alc883_base_mixer in 6stack-dell model, removing alc888_6st_dell_mixer definition. Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: Takashi Iwai commit f4853a7cdff5554e334ea405a7068219680a17cc Author: Herton Ronaldo Krzesinski Date: Thu Mar 20 12:13:46 2008 +0100 [ALSA] hda-codec - Remove now uneeded 6stack-hp model from ALC883 After DAC assignment fix in ALC883, the 6stack-hp model is now the same as 6stack-dig. So just remove 6stack-hp model and replace its use with 6stack-dig. Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: Takashi Iwai commit 0879a794efe119965334f675cfb5e63e010c16f6 Author: Jiang zhe Date: Thu Mar 20 12:12:39 2008 +0100 [ALSA] hda-codec - model for alc262 to support Lenovo 3000 This model is to support the Lenovo 3000 y410. ALSA bug#3856: https://bugtrack.alsa-project.org/alsa-bug/view.php?id=3856 Signed-off-by: Jiang zhe Signed-off-by: Takashi Iwai commit 78447af0ad9da1908ccb56ae061d69a31359c19a Author: Matthew Ranostay Date: Thu Mar 20 12:10:57 2008 +0100 [ALSA] hda: 92hd71bxxx DMIC nid Added missing DMIC verb to dell_4_1_pin_configs[]. Signed-off-by: Matthew Ranostay Signed-off-by: Takashi Iwai commit 1a3e5b9b663534233f575438f26c8f5e77df16fa Author: Pavel Hofman Date: Thu Mar 20 12:10:27 2008 +0100 [ALSA] ice1724 - Improved the Juli rate setting * moving most of clock-specific code to card-specific routines * support for ESI Juli * to-be-researched - monitoring of analog/digital inputs Signed-off-by: Pavel Hofman Signed-off-by: Takashi Iwai commit 77bc3b8162cc53943082acdfd0a10e5a0b99703a Author: Andrew Morton Date: Thu Mar 20 12:07:31 2008 +0100 [ALSA] sound/pci/pcxhr/pcxhr.c: fix warnings sparc64: sound/pci/pcxhr/pcxhr.c: In function `pcxhr_update_r_buffer': sound/pci/pcxhr/pcxhr.c:459: warning: cast to pointer from integer of different size sound/pci/pcxhr/pcxhr.c: In function `pcxhr_trigger_tasklet': sound/pci/pcxhr/pcxhr.c:628: warning: long int format, different type arg (arg 4) Signed-off-by: Andrew Morton Signed-off-by: Takashi Iwai commit 80a4ec32c091120f9a838331665691469ef50e5e Author: Andrew Morton Date: Thu Mar 20 12:05:33 2008 +0100 [ALSA] sound/pci/pcxhr/pcxhr_core.c: fix printk warning sound/pci/pcxhr/pcxhr_core.c: In function `pcxhr_set_pipe_state': sound/pci/pcxhr/pcxhr_core.c:899: warning: long int format, different type arg (arg 4) suseconds_t is int on sparc64. Signed-off-by: Andrew Morton Signed-off-by: Takashi Iwai commit 5019e8a8da773c1a1069b64928e29a5738f029d9 Author: Andrew Morton Date: Thu Mar 20 12:04:46 2008 +0100 [ALSA] sound/pci/aw2/aw2-alsa.c needs dma-mapping.h sparc32: sound/pci/aw2/aw2-alsa.c: In function 'snd_aw2_create': sound/pci/aw2/aw2-alsa.c:282: error: 'DMA_32BIT_MASK' undeclared (first use in this function) sound/pci/aw2/aw2-alsa.c:282: error: (Each undeclared identifier is reported only once sound/pci/aw2/aw2-alsa.c:282: error: for each function it appears in.) Signed-off-by: Andrew Morton Signed-off-by: Takashi Iwai commit daab78f172cd1cd642326d7a126d06daef122a7e Author: Clemens Ladisch Date: Wed Mar 19 08:21:32 2008 +0100 [ALSA] oxygen: disable clock of unused I2S inputs Disable the master clock outputs of any unused I2S inputs. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai commit 90d9869ce78d2c1ac28783f5d7a50d6e678cf81a Author: Clemens Ladisch Date: Wed Mar 19 08:20:59 2008 +0100 [ALSA] oxygen: move MIDI flag to model struct Put the flag that enables the MIDI port into the model structure instead of passing it as a separate parameter to oxygen_pci_probe(). Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai commit d0b398c940b0663c576f8b229741c91a21d25c67 Author: Clemens Ladisch Date: Wed Mar 19 08:20:13 2008 +0100 [ALSA] oxygen: make SPI/2-wire configuration model-specific Allow the model drivers to specify if the codec communication goes over SPI or a 2-wire bus. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai commit dd88e4e9d07d7712b935bb74cf83b80dd5865c3d Author: Clemens Ladisch Date: Wed Mar 19 08:19:41 2008 +0100 [ALSA] oxygen: change model-specific PCM device configuration When specifying which PCM devices to use, model drivers now use flags that also specify the routing between PCM devices and DMA channels instead of just DMA channel bits. This simplifies some code that checks for these flags. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai commit d95f271932ec4cc5e8e5c7b3c3b01d16ac4f2c8b Author: Clemens Ladisch Date: Wed Mar 19 08:17:33 2008 +0100 [ALSA] oxygen: add monitor controls Add controls to enable monitoring of the analog and digital inputs. To allow monitoring after loading the driver when nothing has been played back or recorded yet, the I2S input and outputs are initialized to a valid configuration. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai commit 845cc0351e8e51383de8beeb64aed36645c9fb58 Author: Clemens Ladisch Date: Wed Mar 19 08:16:40 2008 +0100 [ALSA] virtuoso: move PCM1796 symbols to a header file Move the PCM1796 register symbol definitions to their own header file. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai commit 8766f3012f00072a6a761933e56ef62b02638ce3 Author: Clemens Ladisch Date: Wed Mar 19 08:14:01 2008 +0100 [ALSA] oxygen: move WM8785 symbols to a header file Move the WM8786 register symbol definitions to their own header file. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai commit 5fe649abe0f2055890b3abec258086ba0b4d623e Author: Timur Tabi Date: Tue Mar 18 17:18:18 2008 +0100 [ALSA] Removed deprecated sound/driver.h from Freescale MPC8610 drivers With commit 9004acc70e8c49c50c4c7b652f906f1e0ed5709d, include/sound/driver.h is deprecated. This patch removes the #include from fsl_ssi.c and fsl_dma.c. Signed-off-by: Timur Tabi Signed-off-by: Takashi Iwai commit c74ed718a1ebbe7714ba8df73d3c515a1e66f44f Author: Takashi Iwai Date: Tue Mar 18 17:11:05 2008 +0100 [ALSA] hda-intel - Add sync support Addded the support of sync streams to hda-intel driver. Signed-off-by: Takashi Iwai commit 490cfbaef17ee12cc4d4695fed69c3c3b0b16be5 Author: Takashi Iwai Date: Tue Mar 18 12:13:03 2008 +0100 [ALSA] hda-codec - Support of Lenovo Thinkpad X300 Added the model thinkpad for Lenovo Thinkpad X300 with AD1984A codec. Signed-off-by: Takashi Iwai commit 4c51bf53b02ea98ddc7128d8f3b551b512b5e3bc Author: Robert Jarzmik Date: Tue Mar 18 12:08:35 2008 +0100 [ALSA] soc - Add missing audio path between Mono Mixer and Mic PGAs Signed-off-by: Robert Jarzmik Signed-off-by: Mark Brown Signed-off-by: Takashi Iwai commit 66fb89a75697c983035f8871bcbd138672d5076e Author: Takashi Iwai Date: Tue Mar 18 09:57:50 2008 +0100 [ALSA] hda-codec - keep the format verb at closing PCM streams Keep the format verb at closing PCM streams. Introduced snd_hda_codec_cleanup_stream() for the parcicular purpose. Signed-off-by: Takashi Iwai commit d92324a7bd1689a7892ff276d9bf739da6c7b197 Author: Takashi Iwai Date: Tue Mar 18 09:53:23 2008 +0100 [ALSA] hda-codec - Fix spekaer output of Panasonic CF-74 Add a new model "panasonic" for Panasonic CF-74 with STAC9200 codec to fix the speaker output. Signed-off-by: Takashi Iwai commit 714e3e89d0146f79c651d3ab5f3befa1a888d9dd Author: Takashi Iwai Date: Tue Mar 18 09:47:06 2008 +0100 [ALSA] hda-intel - Add barrier Add proper barriers in the RIRB communication code. Signed-off-by: Takashi Iwai commit fda94fa3427920ee3758125315abbcedafa281b0 Author: Herton Ronaldo Krzesinski Date: Tue Mar 18 09:27:59 2008 +0100 [ALSA] hda-codec - Map 3stack-6ch-dig ALC883 model for MSI 945GCM5 V2 (MSI-7267) Map 3stack-6ch-dig ALC883 model for MSI 945GCM5 V2 (MSI-7267). Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: Takashi Iwai commit f13203d61f57d63ba386e57e7c2db63716d743e8 Author: Herton Ronaldo Krzesinski Date: Tue Mar 18 09:27:08 2008 +0100 [ALSA] hda-codec - Fix DAC assignment order in ALC883 Actually clfe and surround DACs are inverted in alc883_dac_nids array (see ALC883 datasheet). I discovered this while testing multi-channel setup (using 3stack-6ch-dig model) on MSI 945GCM5 V2 motherboard that has an ALC883 codec. Simply Rear Left/Right and Center/LFE were swapped in 6 channel mode (also in 4 channel mode you didn't get rear left/right output). Other models also were affected by this bug, as can be seen by the mixer layouts that "workaround" this (the real bug was not noticed, and some other models simply played with mixer and initial verbs). Thus along with fixing the order of dac nids, also change the models that relied on previous dac ordering properly. Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: Takashi Iwai commit 3d7d0bff091572fd8e0bb6925e99c23cd247d933 Author: Mariusz Kozlowski Date: Tue Mar 18 09:03:03 2008 +0100 [ALSA] sound/drivers/pcsp/pcsp.c build fix sound/drivers/pcsp/pcsp.c: In function 'snd_pcsp_create': sound/drivers/pcsp/pcsp.c:54: error: 'loops_per_jiffy' undeclared (first use in\ this function) sound/drivers/pcsp/pcsp.c:54: error: (Each undeclared identifier is reported on\ ly once sound/drivers/pcsp/pcsp.c:54: error: for each function it appears in.) Signed-off-by: Mariusz Kozlowski Signed-off-by: Andrew Morton Signed-off-by: Takashi Iwai commit 189baa461b141d9451be3f11e51f210ab388dc10 Author: Atsushi Nemoto Date: Mon Mar 17 14:36:24 2008 +0100 [ALSA] at73c213: Add constraints for periods value The interrupt handler always provide runtime->period_size data, so it works correctly only if buffer_size was a multiple of period_size. This patch fixes periodic click noise. Signed-off-by: Atsushi Nemoto Signed-off-by: Takashi Iwai commit 4d3ad5b88279cd50509dcad35ae339216296eed7 Author: Julia Lawall Date: Mon Mar 17 10:23:35 2008 +0100 [ALSA] sound/pci: remove unused variable The variable is_capture is initialized but never used otherwise. The semantic patch that makes this change is as follows: (http://www.emn.fr/x-info/coccinelle/) // @@ type T; identifier i; constant C; @@ ( extern T i; | - T i; <+... when != i - i = C; ...+> ) // Signed-off-by: Julia Lawall Signed-off-by: Takashi Iwai commit 6c9a662423e302aef682fb6f11335ccd9c2ef5e5 Author: Takashi Iwai Date: Mon Mar 17 10:16:37 2008 +0100 [ALSA] ice1724 - Fix return codes in some pointis callbacks Fixed the return codes (1 for changed values) in put callbacks of pontis. Signed-off-by: Takashi Iwai commit c47e2e0e7059dda1dc4d168b806a442b8a6d35a4 Author: Takashi Iwai Date: Mon Mar 17 09:59:32 2008 +0100 [ALSA] usb-audio - Add a proper error check The error in check_hw_params_convention() has to be checked and handled properly. Signed-off-by: Takashi Iwai commit 8eb1b41e0400997d1f13c205be20bdee6a4917d8 Author: Pavel Hofman Date: Mon Mar 17 08:45:33 2008 +0100 [ALSA] some fixes and cleanup for ICE1724 cards * removing the hack with NON_AKM ak4xxx type * support for card-specific flags in ak4114_stats * definition of the flags for corresponding cards Signed-off-by: Pavel Hofman Signed-off-by: Takashi Iwai commit 8b361169e3d268ac2f823ba776f170295e99d7fa Author: Joachim Foerster Date: Mon Mar 17 08:40:12 2008 +0100 [ALSA] [ML403-AC97CR] Remove duplicate snd_card_set_dev() We want to have snd_card_set_dev() in _probe(), but not a second one in snd_ml403_ac97cr_create(). Signed-off-by: Joachim Foerster Signed-off-by: Takashi Iwai commit 5443c7eddb5706bb6dbb4c8a128986b5ec6749fb Author: Takashi Iwai Date: Fri Mar 14 17:17:09 2008 +0100 [ALSA] ice1724 - Fix the SPDIF input sample-rate on Juli@ AK4114 on Juli@ has the SPDIF input sample rate detection and causes errors when an incompatible sample rate is chosen. The patch adds the open hook to check the current rate and limit the hw constraints. Signed-off-by: Takashi Iwai commit 236f0fb32d052b75a988cfe5155eff286498f7fa Author: Tony Vroon Date: Fri Mar 14 17:09:18 2008 +0100 [ALSA] hda-codec - Fujitsu Lifebook port replicator/dock headphone jack sense The docking station headphone output had no audio and jack sense was not considered. Jack information from the laptop itself and the dock are combined, as the dock does not obscure the connector. Signed-off-by: Tony Vroon Signed-off-by: Takashi Iwai commit 20b5efd474e573e45ecb106fecbbe31ff38a2af6 Author: Takashi Iwai Date: Fri Mar 14 15:52:20 2008 +0100 [ALSA] hda-intel - Fix power-off hang on ASUS P5AD2 The hda-intel driver has a problem at power-off on ASUS P5AD2. It's caused when the position-buffer is enabled -- most likely a hardware-specific problem. This patch adds a quirk to avoid the unnecessary enablement of position-buffer. Signed-off-by: Takashi Iwai commit 19675681e30022e7747d10a8fda9faa466150589 Author: Herton Ronaldo Krzesinski Date: Fri Mar 14 12:52:59 2008 +0100 [ALSA] hda-codec - Map 3stack-6ch-dig ALC662 model for Asus P5GC-MX Map 3stack-6ch-dig ALC662 model for Asus P5GC-MX. Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: Takashi Iwai commit c3bc9a1d27f90e46fb5f42fd6a3b0c032407bf55 Author: Herton Ronaldo Krzesinski Date: Fri Mar 14 12:52:20 2008 +0100 [ALSA] hda-codec - Fix ALC662 DAC mixer mutes Currently ALC662 doesn't suport amp mute for AmpOut in nids 0x02, 0x03, 0x04 (see block diagram in ALC662 datasheet page 3, does M correspond to mute?). The result is that currently mute for "Front Playback Switch", "Surround Playback Switch", "Center Playback Switch" and "LFE Playback Switch" mixer items doesn't work (tested on Asus P5GC-MX motherboard with 3stack-6ch model). The solution I found for this is to mute the proper inputs in 0x0c, 0x0d, 0x0e audio mixers. Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: Takashi Iwai commit c5ac9c1f1e0efa0687a540869a5a8f7d569c5579 Author: Takashi Iwai Date: Fri Mar 14 09:18:32 2008 +0100 [ALSA] hda-codec - Fix orphan Headphone controls in STAC codecs Currently, the headphone controls are created as Master wrongly in some cases, and this prevents the virtual master controls. The patch fixes the problem by simply using "Headphone" always for headphone controls. Signed-off-by: Takashi Iwai commit 47f2d534c2fb2db592c1ec68ce6d56c7a3dd66ac Author: Matthew Ranostay Date: Fri Mar 14 08:46:51 2008 +0100 [ALSA] hda: 92HD73xxx distortion fix Fixed issue on some laptops that if the Master mixer and DAC mixers are turned all the way up that will cause distortion. This is fixed by limiting the max volume with the volume knob nid. Signed-off-by: Matthew Ranostay Signed-off-by: Takashi Iwai commit 58c532e55b351b6f5827b400c2dd487f2eee8c67 Author: Stas Sergeev Date: Wed Mar 12 13:12:15 2008 +0100 [ALSA] pcsp: locking fix pcsp: locking fix. Signed-off-by: Stas Sergeev Signed-off-by: Takashi Iwai commit 94568b7d77b132434aa58a519cc99cdba17928bc Author: Takashi Iwai Date: Wed Mar 12 12:51:09 2008 +0100 [ALSA] hda-codec - Improve ALC262 ultra model Improved ALC262 ultra model for Samsung Q1 Ultra series. - clean up mixers - support of input from HP jack as a mic - add quirk for Q1 EL Signed-off-by: Takashi Iwai commit 936b2a0a94fa9433503700023a9e1b480abb4a3e Author: Atsushi Nemoto Date: Tue Mar 11 08:15:30 2008 +0100 [ALSA] at73c213: remove redundant private_free routine snd_pcm_lib_preallocate_free_for_all() is called from snd_pcm_free() just after calling the private_free routine. So there should be no need to call it in driver's private_free routine. Signed-off-by: Atsushi Nemoto Signed-off-by: Andrew Morton Signed-off-by: Takashi Iwai commit 67ac42ebf6e6fc56a86d02c552532fa6aa8091a0 Author: Takashi Iwai Date: Mon Mar 10 12:19:12 2008 +0100 [ALSA] aw2 - Rename aw2-tsl.h to aw2-tsl.c aw2-tsl.h should be rather a C file to be included since it's referred only in aw2-saa6146.c and includes a table data. Signed-off-by: Takashi Iwai commit d6788051324258a47903454a26c4d0851758dcb0 Author: Michael Gruber Date: Mon Mar 10 11:30:59 2008 +0100 [ALSA] hda-intel - Fix microphone capture with ALC880 F1734 model The default capture source should be the mic which is 0x01 on this model. In addition to that the change to VREF50 allows for higher capture volume. Signed-off-by: Michael Gruber Signed-off-by: Takashi Iwai commit aa08b076a12348711f89be68dda271164b97e409 Author: Matthew Ranostay Date: Mon Mar 10 11:30:04 2008 +0100 [ALSA] hda: Reorganized DAC outputs Changed so that internal speakers point to the Front mixer instead of Surround. Signed-off-by: Matthew Ranostay Signed-off-by: Takashi Iwai commit dd9d89b5d2f048407b11f1c0beab7d273540df56 Author: Takashi Iwai Date: Mon Mar 10 11:21:30 2008 +0100 [ALSA] release 1.0.16 Signed-off-by: Takashi Iwai commit c8782091c9c201d35449bdbcac7b87a7477e99ab Author: Atsushi Nemoto Date: Sat Mar 8 11:08:32 2008 +0100 [ALSA] at73c213: monaural support Add support for monaural playback to at73c213 driver. The sound will be apear on L-channel. Tested on AT91SAM9260-EK. Signed-off-by: Atsushi Nemoto Signed-off-by: Andrew Morton Signed-off-by: Takashi Iwai commit 932dd114705946e08bf91ef7a4de311441943735 Author: Atsushi Nemoto Date: Sat Mar 8 11:07:26 2008 +0100 [ALSA] at73c213: fix error checking for clk API The clk_round_rate() and clk_set_rate() will return int, so not store thier return value to unsigned long variable. This bug hides real error on these API. Signed-off-by: Atsushi Nemoto Signed-off-by: Andrew Morton Signed-off-by: Takashi Iwai commit 7b6cfd6f56f8e31a7d77aff7365ca80b02ec2479 Author: Tobin Davis Date: Fri Mar 7 11:57:51 2008 +0100 [ALSA] HDA Codecs: add support for Toshiba Equium L30 This patch adds support for the Toshiba Equium L30 laptop and renames the mixer controls to match Laptop usages. Signed-off-by: Tobin Davis Signed-off-by: Takashi Iwai commit 40d8f3e2b033b3962841bd81766cd831e80f9804 Author: Julia Lawall Date: Tue Mar 4 15:07:24 2008 -0800 sound: Use BUG_ON if (...) BUG(); should be replaced with BUG_ON(...) when the test has no side-effects to allow a definition of BUG_ON that drops the code completely. The semantic patch that makes this change is as follows: (http://www.emn.fr/x-info/coccinelle/) // @ disable unlikely @ expression E,f; @@ ( if (<... f(...) ...>) { BUG(); } | - if (unlikely(E)) { BUG(); } + BUG_ON(E); ) @@ expression E,f; @@ ( if (<... f(...) ...>) { BUG(); } | - if (E) { BUG(); } + BUG_ON(E); ) // Signed-off-by: Julia Lawall Signed-off-by: Andrew Morton commit 6323a3abe71c56a51af637f23e3be0b52eae5d4e Author: Takashi Iwai Date: Thu Mar 6 16:58:35 2008 +0100 [ALSA] hda-codec - Add internal mic item for ALC268 acer model Added the internal mic as a capture source item for ALC268 acer model. Signed-off-by: Takashi Iwai Signed-off-by: Takashi Iwai commit 084f6e9585380be10753b589f3dda5583f2e467a Author: Takashi Iwai Date: Thu Mar 6 16:58:17 2008 +0100 [ALSA] hda-codec - Fix dmics on ALC268 in auto configuration Fixed the handling of dmics on ALC268 in the auto-configuration mode. Signed-off-by: Takashi Iwai commit 58de6592051bfe90559f6ccceba43c0aa40bd907 Author: Peer Chen Date: Thu Mar 6 15:15:11 2008 +0100 [ALSA] hda_intel: Add the DIDs of nvidia MCP79 HD audio controller to hda_intel.c Add the Device IDs of nvidia MCP79 HD audio controller to hda_intel.c Signed-off-by: Peer Chen Signed-off-by: Takashi Iwai commit 8f2ac2da90427f947d42f95fc810018285c0c734 Author: Jiang zhe Date: Thu Mar 6 11:09:09 2008 +0100 [ALSA] hda-codec - model for cx20549 to support laptop HP530 Currently the model laptop-hpsense use the 0x12 as ExtMic, and use 0x14 as Internal IntMic. But the hp530 only have one ExtMic, the Pin widget is 0x14. In this patch, I changed the mixer item for them. I still reserved the IntMic item, it will be helpful if other machine may use this model. ALSA bug#3821. Signed-off-by: Jiang zhe Signed-off-by: Takashi Iwai commit 65f22221aae3f35ac8239127a760955eeef31276 Author: Jiang zhe Date: Thu Mar 6 11:07:11 2008 +0100 [ALSA] hda-codec - model for alc883 to support FUJITSU Pi2515 There is no suitable model for Pi2515. This model is to support it. ALSA bug#3800. Signed-off-by: Jiang zhe Signed-off-by: Takashi Iwai commit 929af0da5c4f3c9969461fd962460e0740f29340 Author: Stas Sergeev Date: Thu Mar 6 11:01:44 2008 +0100 [ALSA] pcsp: add description update ALSA-Configuration.txt Signed-off-by: Stas Sergeev Signed-off-by: Takashi Iwai commit e7ce7b55edef82afc668b4cb8421c61474abac78 Author: Stas Sergeev Date: Thu Mar 6 11:01:16 2008 +0100 [ALSA] pcsp: improve "enable" option handling Simplify init code. Signed-off-by: Stas Sergeev Signed-off-by: Takashi Iwai commit 57981dcf6d9fb3fcfe1d541e73b223dc70e3984f Author: Pascal Terjan Date: Tue Mar 4 11:33:28 2008 +0100 [ALSA] ALC288 - Add NEC S970 to the quirk table NEC S970 has no sound in the internal speakers when autodetection is used. With targa-dig model, there is sound in the speakers and it gets correctly muted when pluging headphones. Signed-off-by: Takashi Iwai commit de3dca3dac19a987d06cecfd7b94715475fc96d1 Author: Stas Sergeev Date: Tue Mar 4 11:28:43 2008 +0100 [ALSA] pcsp - clean ups - make pcsp_start_timer_tasklet static - remove redundant includes. is not available on all platforms. Signed-off-by: Stas Sergeev Signed-off-by: Takashi Iwai commit dd80f6cb8e30876e4ebea6732c312d0a1aa28f86 Author: Jiang zhe Date: Tue Mar 4 11:20:33 2008 +0100 [ALSA] hda-codec - model for alc883 to support M720R There is no suitable model for M720R (ALSA bug#3781). This patch is to support HP jack-sensing and mixer. Signed-off-by: Jiang zhe Signed-off-by: Takashi Iwai commit c820ebfe8dfef8ae065e948a1e7166bc1127ae4a Author: Takashi Iwai Date: Tue Mar 4 11:06:26 2008 +0100 [ALSA] aw2 - Remove endian dependency Removed unnecessary dependency on the little-endianess. Signed-off-by: Takashi Iwai commit ac7973c77406bf131bdec96b38349868b5acd78f Author: Harvey Harrison Date: Mon Mar 3 15:32:18 2008 -0800 [ALSA] sound: replace remaining __FUNCTION__ occurences __FUNCTION__ is gcc-specific, use __func__ Signed-off-by: Harvey Harrison Signed-off-by: Takashi Iwai commit 5d63d8054bb75235512d85fb7213bf05439d1a28 Author: Andrew Morton Date: Tue Mar 4 10:08:58 2008 +0100 [ALSA] hda_intel needs dma-mapping.h sparc32: sound/pci/hda/hda_intel.c: In function 'azx_create': sound/pci/hda/hda_intel.c:1838: error: 'DMA_64BIT_MASK' undeclared (first use in this function) sound/pci/hda/hda_intel.c:1838: error: (Each undeclared identifier is reported only once sound/pci/hda/hda_intel.c:1838: error: for each function it appears in.) Signed-off-by: Andrew Morton Signed-off-by: Takashi Iwai commit 724b32f588f21728e73fba4c686699850d3e2958 Author: Graeme Gregory Date: Mon Mar 3 17:19:45 2008 +0100 [ALSA] soc - Add Invert Switch for ROUT2 GTA02 device has a speaker between LOUT2 & ROUT2 and in this mode ROUT2 needs to be inverted. This patch adds a mixer control for this. Signed-off-by: Graeme Gregory Signed-off-by: Mark Brown Signed-off-by: Takashi Iwai commit a54e4cad7c0623a1d5cc0dc258cdcba9a73574b6 Author: Stas Sergeev Date: Mon Mar 3 10:53:54 2008 +0100 [ALSA] Add PC-speaker sound driver Added PC-speaker sound driver (snd-pcsp). Signed-off-by: Stas Sergeev Signed-off-by: Takashi Iwai commit c57df2258b994fa893a71dde7576d9030f94d389 Author: Takashi Iwai Date: Fri Feb 29 14:16:17 2008 +0100 [ALSA] hda-codec - Fix the array over-range access with stac92hd71bxx codec Add the check of the array range for dac_nids to prevent the over-range access. Signed-off-by: Takashi Iwai commit 0557d40044dd2ecfc131fd86b49f69fa023002f9 Author: Pawel MOLL Date: Fri Feb 29 12:41:31 2008 +0100 [ALSA] IEC958 definitions for consumer status channel, byte 4 Added definition for byte 4 of SPDIF channel status, according to second edition of IEC 60958-3 (consumer) spec. Signed-off-by: Pawel MOLL Signed-off-by: Takashi Iwai commit 3f83f51e7d23cd194ceeddc1bd18cf6bbbb7617a Author: Matthew Ranostay Date: Fri Feb 29 12:08:20 2008 +0100 [ALSA] hda: add verbs for 92hd73xxx laptops Added core_init[] for several 92hd73xxx laptops. Signed-off-by: Matthew Ranostay Signed-off-by: Takashi Iwai commit d0843aab030d4aacc36bdde07e01eb7c9b7a8298 Author: Matthew Ranostay Date: Fri Feb 29 12:07:43 2008 +0100 [ALSA] hda: disable power management on fixed ports Power management can't be enabled on fixed ports, since the presence will always return false and prevent output. Signed-off-by: Matthew Ranostay Signed-off-by: Takashi Iwai commit babd2204a73a525d0d6b80d7582d710724979776 Author: Harvey Harrison Date: Fri Feb 29 11:59:26 2008 +0100 [ALSA] sound: hda: missing includes of hda_patch.h Move the array declaration to hda_codec.c where it is used and add includes where the individual presets are declared. Fixes the following sparse warnings: sound/pci/hda/patch_realtek.c:13744:25: warning: symbol 'snd_hda_preset_realtek' was not declared. Should it be static? sound/pci/hda/patch_cmedia.c:729:25: warning: symbol 'snd_hda_preset_cmedia' was not declared. Should it be static? sound/pci/hda/patch_analog.c:3656:25: warning: symbol 'snd_hda_preset_analog' was not declared. Should it be static? sound/pci/hda/patch_sigmatel.c:3995:25: warning: symbol 'snd_hda_preset_sigmatel' was not declared. Should it be static? sound/pci/hda/patch_si3054.c:286:25: warning: symbol 'snd_hda_preset_si3054' was not declared. Should it be static? sound/pci/hda/patch_atihdmi.c:156:25: warning: symbol 'snd_hda_preset_atihdmi' was not declared. Should it be static? sound/pci/hda/patch_conexant.c:1721:25: warning: symbol 'snd_hda_preset_conexant' was not declared. Should it be static? sound/pci/hda/patch_via.c:1962:25: warning: symbol 'snd_hda_preset_via' was not declared. Should it be static? Signed-off-by: Harvey Harrison Signed-off-by: Takashi Iwai commit 427e87d10ae531a1444ae4664b7b07a88187cb61 Author: Takashi Iwai Date: Fri Feb 29 11:57:53 2008 +0100 [ALSA] hda-codec - Use int instead of long in patch_sigmatel.c The HD-audio parameters are at most 32bit int. Signed-off-by: Takashi Iwai commit 739a2cf3414501d61d2c87bfe320879d0e2098eb Author: Harvey Harrison Date: Fri Feb 29 11:56:48 2008 +0100 [ALSA] sound: patch_sigmatel.c fix shadowed variable warning Temp variable in the loop shadows the second argument (which is otherwise unused in this function). Change this to defcfg as it is used to hold the default config. sound/pci/hda/patch_sigmatel.c:2759:18: warning: symbol 'cfg' shadows an earlier one sound/pci/hda/patch_sigmatel.c:2734:26: originally declared here Signed-off-by: Harvey Harrison Signed-off-by: Takashi Iwai commit 835955638bb9b9f122ccbe996049525babe4dba5 Author: Harvey Harrison Date: Fri Feb 29 11:54:49 2008 +0100 [ALSA] sound: hdspm.c fix returning void expression warnings Just drop the returns. sound/pci/rme9652/hdspm.c:1031:3: warning: returning void-valued expression sound/pci/rme9652/hdspm.c:1033:3: warning: returning void-valued expression Signed-off-by: Harvey Harrison Signed-off-by: Takashi Iwai commit 00465ce1f0280712a46df3ccb550050462712837 Author: Harvey Harrison Date: Fri Feb 29 11:54:26 2008 +0100 [ALSA] sound: riptide.c fix shadowed variable warnings In both cases we are passing around the substream number, use sub_num for this. sound/pci/riptide/riptide.c:1633:6: warning: symbol 'index' shadows an earlier one sound/pci/riptide/riptide.c:121:12: originally declared here sound/pci/riptide/riptide.c:1673:6: warning: symbol 'index' shadows an earlier one sound/pci/riptide/riptide.c:121:12: originally declared here Signed-off-by: Harvey Harrison Signed-off-by: Takashi Iwai commit 3be67f3b7a37286ec5e7a4ae776e3b0ff3d7f141 Author: Harvey Harrison Date: Fri Feb 29 11:53:59 2008 +0100 [ALSA] sound: pcxhr_core.c fix shadowed variable warning Inner loop redeclares err with u32 rather than int, stupid fix here is to change the inner err to err2. sound/pci/pcxhr/pcxhr_core.c:1008:8: warning: symbol 'err' shadows an earlier one sound/pci/pcxhr/pcxhr_core.c:983:6: originally declared here Signed-off-by: Harvey Harrison Signed-off-by: Takashi Iwai commit 711bf3a99213502149d1f4c9be8f65fbd6f38891 Author: Harvey Harrison Date: Fri Feb 29 11:46:57 2008 +0100 [ALSA] sound: virtuoso.c fix shadowed variable warning Use priv_idx as an identifier. sound/pci/oxygen/virtuoso.c:277:15: warning: symbol 'index' shadows an earlier one sound/pci/oxygen/virtuoso.c:56:12: originally declared here Signed-off-by: Harvey Harrison Signed-off-by: Takashi Iwai commit c6319da56a4f905564e2b023ed1eca77cfeb57c6 Author: Harvey Harrison Date: Fri Feb 29 11:52:50 2008 +0100 [ALSA] sound: ice1712.c fix shadowed variable warnings In all four case, adding a private value to the iooff index, call it priv_idx. sound/pci/ice1712/ice1712.c:1300:6: warning: symbol 'index' shadows an earlier one sound/pci/ice1712/ice1712.c:85:12: originally declared here sound/pci/ice1712/ice1712.c:1312:6: warning: symbol 'index' shadows an earlier one sound/pci/ice1712/ice1712.c:85:12: originally declared here sound/pci/ice1712/ice1712.c:1338:6: warning: symbol 'index' shadows an earlier one sound/pci/ice1712/ice1712.c:85:12: originally declared here sound/pci/ice1712/ice1712.c:1350:6: warning: symbol 'index' shadows an earlier one sound/pci/ice1712/ice1712.c:85:12: originally declared here [tiwai - fixed coding issues as well] Signed-off-by: Harvey Harrison Signed-off-by: Takashi Iwai commit 20d8d60b9a2650aebe1352ef65d4336de9224318 Author: Harvey Harrison Date: Fri Feb 29 11:44:57 2008 +0100 [ALSA] sound: emu10k1x.c fix shadowed variable warnings enable in these contexts refers specifically to intr enable, as per the two functions it is found in. Use intr_enable instead. sound/pci/emu10k1/emu10k1x.c:330:15: warning: symbol 'enable' shadows an earlier one sound/pci/emu10k1/emu10k1x.c:53:12: originally declared here sound/pci/emu10k1/emu10k1x.c:341:15: warning: symbol 'enable' shadows an earlier one sound/pci/emu10k1/emu10k1x.c:53:12: originally declared here instead of shadowing, use cap_voice as we test for the capture voice in this statement. sound/pci/emu10k1/emu10k1x.c:798:25: warning: symbol 'pvoice' shadows an earlier one sound/pci/emu10k1/emu10k1x.c:787:24: originally declared here Signed-off-by: Harvey Harrison Signed-off-by: Takashi Iwai commit 3174e1ef2716ade612442d692c69b6e4d9c8b2c0 Author: Harvey Harrison Date: Fri Feb 29 11:44:26 2008 +0100 [ALSA] sound: emuproc.c fix signedness warning Reading regs from the fpga into an int instead of a u32, trivial fix. sound/pci/emu10k1/emuproc.c:422:34: warning: incorrect type in argument 3 (different signedness) sound/pci/emu10k1/emuproc.c:422:34: expected unsigned int [usertype] *value sound/pci/emu10k1/emuproc.c:422:34: got int * Signed-off-by: Harvey Harrison Signed-off-by: Takashi Iwai commit f5d855c2f22afd51734bd66a64b62cc4637d948c Author: Harvey Harrison Date: Fri Feb 29 11:41:56 2008 +0100 [ALSA] sound: au88x0_pcm.c fix integer as NULL pointer warning sound/pci/au88x0/au88x0_pcm.c:508:15: warning: Using plain integer as NULL pointer Also some small codingstyle fixes. Signed-off-by: Harvey Harrison Signed-off-by: Takashi Iwai commit d829de44a0fba2c44168c43408ae95a354c09173 Author: Ahmet İnan Date: Thu Feb 28 12:46:32 2008 +0100 [ALSA] snd-dummy - better realtime app support when the time interval for a period is smaller than kernel HZ, then snd-aloop and snd-dummy cannot call snd_pcm_period_elapsed as fast enough annymore. this happens for example with games. but the app still needs to see, that the buffer actually did go further, which is provided by these patches. Signed-off-by: Ahmet İnan mathematik.uni-freiburg.de> Signed-off-by: Takashi Iwai commit b7960004f1f1efbc7d6e69a2508acd59c9deb9a1 Author: Harvey Harrison Date: Thu Feb 28 12:02:56 2008 +0100 [ALSA] sound: ca0106_mixer.c fix shadowed variable warnings Change the variable err to _err within the ADD_CTLS macro to avoid shadowing the local variable. sound/pci/ca0106/ca0106_mixer.c:710:2: warning: symbol 'err' shadows an earlier one sound/pci/ca0106/ca0106_mixer.c:663:6: originally declared here sound/pci/ca0106/ca0106_mixer.c:712:3: warning: symbol 'err' shadows an earlier one sound/pci/ca0106/ca0106_mixer.c:663:6: originally declared here sound/pci/ca0106/ca0106_mixer.c:721:3: warning: symbol 'err' shadows an earlier one sound/pci/ca0106/ca0106_mixer.c:663:6: originally declared here Signed-off-by: Harvey Harrison Signed-off-by: Takashi Iwai commit 723f49e3d43379b226304d881f6f64a4fce6f461 Author: Harvey Harrison Date: Thu Feb 28 12:02:22 2008 +0100 [ALSA] sound: ca0106_main.c fix shadowed variable warnings change to intr_enable as per the two functions it is defined in. sound/pci/ca0106/ca0106_main.c:438:15: warning: symbol 'enable' shadows an earlier one sound/pci/ca0106/ca0106_main.c:159:12: originally declared here sound/pci/ca0106/ca0106_main.c:449:15: warning: symbol 'enable' shadows an earlier one sound/pci/ca0106/ca0106_main.c:159:12: originally declared here Signed-off-by: Harvey Harrison Signed-off-by: Takashi Iwai commit fcc02602790a7022221f3651d357a45edbbe56d6 Author: Harvey Harrison Date: Thu Feb 28 12:00:48 2008 +0100 [ALSA] sound: ali5451.c fix shadowed variable warnings enable is used to test for whether or not spdif should be enabled, change to spdif_enable. sound/pci/ali5451/ali5451.c:1812:15: warning: symbol 'enable' shadows an earlier one sound/pci/ali5451/ali5451.c:63:12: originally declared here sound/pci/ali5451/ali5451.c:1840:27: warning: symbol 'enable' shadows an earlier one sound/pci/ali5451/ali5451.c:63:12: originally declared here Signed-off-by: Harvey Harrison Signed-off-by: Takashi Iwai commit d95824c0e55af75a9fa489bfe8c492648c0360b4 Author: Harvey Harrison Date: Thu Feb 28 11:58:18 2008 +0100 [ALSA] sound: ac97_pcm.c fix shadowed variable warning err is always assigned before it is used, no need to declare another inside the if statement. sound/pci/ac97/ac97_pcm.c:577:7: warning: symbol 'err' shadows an earlier one sound/pci/ac97/ac97_pcm.c:572:6: originally declared here Signed-off-by: Harvey Harrison Signed-off-by: Takashi Iwai commit 46d8011663478db677ac5051d9194af93df30215 Author: Harvey Harrison Date: Thu Feb 28 11:57:47 2008 +0100 [ALSA] sound: rme96.c fix integer as NULL pointer warning kernel style does assignment outside of if() block sound/pci/rme96.c:1562:71: warning: Using plain integer as NULL pointer Signed-off-by: Harvey Harrison Signed-off-by: Takashi Iwai commit 37839490f63324c0a7e38180e1e1b39ba48e01de Author: Harvey Harrison Date: Thu Feb 28 11:57:23 2008 +0100 [ALSA] sound: rme32.c fix integer as NULL pointer warning kernel style does assignment outside of if() statements. sound/pci/rme32.c:1353:71: warning: Using plain integer as NULL pointer Signed-off-by: Harvey Harrison Signed-off-by: Takashi Iwai commit 2b474704573b00430b98b75f9917b14c086b8d68 Author: Harvey Harrison Date: Thu Feb 28 11:56:37 2008 +0100 [ALSA] sound: maestro3.c fix shadowed variable warnings change id to elem_id as it is used to initialize each mixer element sound/pci/maestro3.c:2071:25: warning: symbol 'id' shadows an earlier one sound/pci/maestro3.c:67:13: originally declared here index is used in each of these places to count over the dsp's memory, change to the name dsp_index sound/pci/maestro3.c:2572:9: warning: symbol 'index' shadows an earlier one sound/pci/maestro3.c:66:12: originally declared here sound/pci/maestro3.c:2604:9: warning: symbol 'index' shadows an earlier one sound/pci/maestro3.c:66:12: originally declared here [tiwai - fixed coding style issues as well] Signed-off-by: Harvey Harrison Signed-off-by: Takashi Iwai commit a44daff9d0e2770a48c8bf89018ec8be8b286ad1 Author: Harvey Harrison Date: Thu Feb 28 11:55:07 2008 +0100 [ALSA] sound: fm801.c fix shadowed variable warning id was only used as a counter in a for loop, move the declaration to where it is used and change it to i. sound/pci/fm801.c:1288:6: warning: symbol 'id' shadows an earlier one sound/pci/fm801.c:51:13: originally declared here [tiwai - fixed a coding style issue as well] Signed-off-by: Harvey Harrison Signed-off-by: Takashi Iwai commit c0139155d39d8ed1900fd56b8b7ca85638c72594 Author: Harvey Harrison Date: Thu Feb 28 11:53:41 2008 +0100 [ALSA] sound: es1968.c fox shadowed variable warning id is used when initializing the mixer elements, use elem_id here instead. sound/pci/es1968.c:1963:25: warning: symbol 'id' shadows an earlier one sound/pci/es1968.c:129:13: originally declared here Signed-off-by: Harvey Harrison Signed-off-by: Takashi Iwai commit 41c8812ca7ee783793277d3a5efc9391f3fc90b2 Author: Harvey Harrison Date: Thu Feb 28 11:53:07 2008 +0100 [ALSA] sound: ens1370.c fix shadowed variable warning index is incremented only when AC97_EI_SPDIF and then assigned to the index field. Change the temporary name to is_spdif. sound/pci/ens1370.c:1638:10: warning: symbol 'index' shadows an earlier one sound/pci/ens1370.c:84:12: originally declared here Signed-off-by: Harvey Harrison Signed-off-by: Takashi Iwai commit 33bf8eb1c3c652e9a5852cb6fe546099da22369f Author: Harvey Harrison Date: Thu Feb 28 11:52:17 2008 +0100 [ALSA] sound: cmipci.c fix shadowed variable warning A temporary variable for each mixer element is used in an initialization loop, use the name elem_id. sound/pci/cmipci.c:2747:26: warning: symbol 'id' shadows an earlier one sound/pci/cmipci.c:56:13: originally declared here [tiwai - fixed a coding style issue as well] Signed-off-by: Harvey Harrison Signed-off-by: Takashi Iwai commit ab3d590a28cb22b5f221f98008fcef6dfd497015 Author: Mark Brown Date: Tue Feb 26 13:16:08 2008 +0100 [ALSA] soc - Report errors from snd_soc_dapm_set_endpoint() Signed-off-by: Mark Brown Signed-off-by: Takashi Iwai commit 9aad583cd5559637c290c19e0c9ec5c8d5ca42e7 Author: Takashi Iwai Date: Tue Feb 26 11:56:35 2008 +0100 [ALSA] hda-codec - Add docking-station mic input for Thinkpad X61 Added the docking-stationc mic input to the capture source list for Thinkpad X61. Signed-off-by: Takashi Iwai commit e8c1a95ce6c6f2f683a2f4e3a45eda9205c794d2 Author: Takashi Iwai Date: Mon Feb 25 18:26:41 2008 +0100 [ALSA] hda-codec - Fix initial DAC numbers of 92HD71bxx codecs Fix the initial num_dacs of 92HD71bxx codecs. Signed-off-by: Takashi Iwai commit 4ed88c85a7a8e69b29b759bb2c74a3e26954f8d5 Author: Clemens Ladisch Date: Mon Feb 25 11:04:19 2008 +0100 [ALSA] usb-audio: sort quirks list Move some entries to their proper position so that the list is again sorted by vendor/product ID. Signed-off-by: Clemens Ladisch commit b45915eecfda8e9b5233698db994149d257bd1c2 Author: Clemens Ladisch Date: Mon Feb 25 10:59:52 2008 +0100 [ALSA] mpu401: reduce tx loop timeout Reduce the number of times to check for a non-empty Tx FIFO from 100 to 2 because there is no MPU-401 implementation that needs more than one or two reads to determine the actual FIFO status. Signed-off-by: Clemens Ladisch commit 60d731770fe6711998de916d6a0ebc829507c064 Author: Remy Bruno Date: Fri Feb 22 17:57:02 2008 +0100 [ALSA] hdsp - RME 9632 fix at 192kHz The bits indicating SPDIF frequency in the status register are not the same for the 9632 than for the other cards, because it also supports 192kHz. A specific bitmask has thus been added (used in hdsp_spdif_sample_rate()). The 9632 does not seem to report external sample rates greater than 96kHz. In this case, the best seems to report spdif rate when autosync reference is spdif. This also required to move function hdsp_spdif_sample_rate(). Signed-off-by: Remy Bruno Signed-off-by: Takashi Iwai commit 3211b9b156c503b77ee5bbfb18d35b2f96864f47 Author: Matthew Ranostay Date: Fri Feb 22 17:55:05 2008 +0100 [ALSA] hda: Mic as output fix Added logic to check if AUTO_PIN_FRONT_MIC is available for output switch, if AUTO_PIN_MIC isn't. Signed-off-by: Matthew Ranostay Signed-off-by: Takashi Iwai commit a142bcece1a1d19899828f2edec8b699f48d9415 Author: Takashi Iwai Date: Thu Feb 21 14:11:09 2008 +0100 [ALSA] hda-codec - Add missing descriptions for STAC codec models Added the missing descriptions for STAC codec models. Signed-off-by: Takashi Iwai commit 3192a0ed2e46e3aa711683343febee9ad02ee09f Author: Takashi Iwai Date: Thu Feb 21 12:40:00 2008 +0100 [ALSA] seq-oss - Remove invalid BUG() Removed invalid BUG() - the driver should handle the error case properly rather than issuing BUG(). Signed-off-by: Takashi Iwai commit 42bd615bf337d54e9866f5ab1de06f0512e0dd77 Author: Takashi Iwai Date: Thu Feb 21 08:13:11 2008 +0100 [ALSA] hda-intel - Use PCI_DEVICE() macro Clean up the pci id table using PCI_DEVICE() macro. Signed-off-by: Takashi Iwai commit f001d156fa444f58ebee9b1c713cc839b83d9062 Author: Ahmet İnan Date: Thu Feb 21 07:55:30 2008 +0100 [ALSA] snd-dummy - improved timing, silence on prepare Signed-off-by: Ahmet İnan mathematik.uni-freiburg.de> Signed-off-by: Takashi Iwai commit f7e7b6cacc329d0f35edc1c048671a3cdb63b407 Author: Matthew Ranostay Date: Thu Feb 21 07:51:46 2008 +0100 [ALSA] hda: STAC927x analog mic Some laptops have a internal analog microphone that is not setup by the BIOS. Signed-off-by: Matthew Ranostay Signed-off-by: Takashi Iwai commit 0ae6a1e3cca6d7d04ddfc7dcfd4dd5ef98bccecf Author: Matthew Ranostay Date: Thu Feb 21 07:51:14 2008 +0100 [ALSA] hda: 92HDxxxx PCI Quirks Added PCI_QUIRKS for laptop that have the 92HDxxx family of codecs. Signed-off-by: Matthew Ranostay Signed-off-by: Takashi Iwai commit b7348791bb6ae8183aa121fe3e16df1a094351eb Author: Matthew Ranostay Date: Thu Feb 21 07:50:12 2008 +0100 [ALSA] hda: STAC927x invalid association value STAC_DELL_BIOS quirks were setting the association value wrong for port 0x0f, which prevented it from being included in hp_outs[]. Signed-off-by: Matthew Ranostay Signed-off-by: Takashi Iwai commit 989378df1fd64b277dde2a8c23db6a6b54facef4 Author: Matthew Ranostay Date: Thu Feb 21 07:49:31 2008 +0100 [ALSA] hda: fix STAC927x power management Fix issue on STAC927x codecs that first DAC was getting powered down even if was being used. Signed-off-by: Matthew Ranostay Signed-off-by: Takashi Iwai commit 80123b12f1b196eda0a4d6f66793ea38b0793918 Author: Jarkko Nikula Date: Wed Feb 20 17:13:44 2008 +0100 [ALSA] ASoC: Add support for 12 MHz MCLK in TLV320AIC3X Signed-off-by: Jarkko Nikula Signed-off-by: Takashi Iwai commit 726dae80b1b725b97e234382ac4a6f7f4460e7fb Author: Takashi Iwai Date: Wed Feb 20 12:13:29 2008 +0100 [ALSA] Add description of aw2 driver Added a brief description of aw2 driver to ALSA-Configuration.txt. Signed-off-by: Takashi Iwai commit 524c07bf8f19bb0c2b4b9e490448c13935284545 Author: Takashi Iwai Date: Wed Feb 20 12:12:58 2008 +0100 [ALSA] aw2 - Add missing module parameters Added the missing declarations for module parameters. Signed-off-by: Takashi Iwai commit 5c6a719700af957d33238a3c76fd5e943f9c85e1 Author: Cedric Bregardis Date: Wed Feb 20 12:05:13 2008 +0100 [ALSA] Emagic Audiowerk 2 ALSA driver. Signed-off-by: Cedric Bregardis Signed-off-by: Jean-Christian Hassler Signed-off-by: Takashi Iwai commit d59701ed85ddc56aa8deffa55a0d4556ce7fa089 Author: Takashi Iwai Date: Tue Feb 19 15:03:57 2008 +0100 [ALSA] hda-codec - Don't create multiple capture streams for single inputs When the device has only one input source, it makes no sense to have multiple capture streams. Signed-off-by: Takashi Iwai commit 9de8378de420343533005ae85dbf5b1621b2144f Author: Takashi Iwai Date: Tue Feb 19 15:00:15 2008 +0100 [ALSA] hda-codec - Fix ALC268 capture source Initialize the capture source properly for auto model. It's especially important for cases that only mic is detected. Signed-off-by: Takashi Iwai commit 40f6af19deeee06ec160c458df1ba85a8a43f1c9 Author: Takashi Iwai Date: Tue Feb 19 13:16:41 2008 +0100 [ALSA] hda-codec - Add beep volume control to ALC268 Added the beep volume control to ALC268 codec support code. Since the codec doesn't return the correct AMP caps, we need to override the value. Signed-off-by: Takashi Iwai commit 1b52c3e77b5c343a283259023b4829abcd0b818b Author: Kailang Yang Date: Tue Feb 19 11:38:05 2008 +0100 [ALSA] hda-codec - Fix ALC662 recording Fixed ALC662 recording issue. Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai commit d0d459a585b3c7839dcefe53b33f997566c9a730 Author: Takashi Iwai Date: Tue Feb 19 11:36:35 2008 +0100 [ALSA] hda-intel - Clean up stream definitions Clean up the code to define playback/capture streams. Signed-off-by: Takashi Iwai commit cea028a93367701680899d1b8c5a3bc304742e0c Author: Takashi Iwai Date: Mon Feb 18 13:06:49 2008 +0100 [ALSA] ca0106 - Add master volume controls Added master volume and switch controls for ca0106 using vmaster. Signed-off-by: Takashi Iwai commit 9c495fedae550beadb2c9601b2dd6a2fb676a7b9 Author: Takashi Iwai Date: Mon Feb 18 13:05:50 2008 +0100 [ALSA] Keep private TLV entry in vmaster itself Use a private array for TLV entries of virtual master controls instead of (supposed) static array. This cleans up the existing codes. Also, now vmaster assumes the simple dB-range TLV that is the only type it can handle. Signed-off-by: Takashi Iwai commit 9aede90361dfbfe2a9fba54e7961570c1377c098 Author: Takashi Iwai Date: Mon Feb 18 13:03:13 2008 +0100 [ALSA] Move vmaster code to sound core Move the codes for virtual master controls to sound core part so that not only hda-intel drivers can use it. Signed-off-by: Takashi Iwai commit 9022e1a478ffc582c964a4b761c812ccb0ffbdd3 Author: Takashi Iwai Date: Mon Feb 18 12:23:13 2008 +0100 [ALSA] intel8x0 - Add support of 8 channel sound Added the support of 8 channel sound for codecs that are known to work. So far, only ALC850 is marked as a 8ch-support codec. This fix is a modified version of the patch on ALSA BTS#2097 by Martin Ellis: https://bugtrack.alsa-project.org/alsa-bug/view.php?id=2097 Signed-off-by: Takashi Iwai commit 39de6da5e5f86833843680a3fe1d98c1b2d89e5f Author: Hans-Christian Egtvedt Date: Mon Feb 18 11:44:56 2008 +0100 [ALSA] Add __devinit macro to at73c213 sound driver probe functions This patch adds __devinit to the functions used when probing. Will also reduce the memory footprint a bit if CONFIG_HOTPLUG is not enabled. Signed-off-by: Hans-Christian Egtvedt Signed-off-by: Takashi Iwai commit e68448db20ffb6850696713a25d64c5b34238af3 Author: Vladimir Barinov Date: Mon Feb 18 11:40:22 2008 +0100 [ALSA] Davinci ASoC support Add ASoC support for the TI Davinci SoC and the Davicni-EVM reference board. It includes: - ASoC Davinci DMA driver - ASoC Davinci I2S (Davinci McBSP module based) driver - ASoC Davinci-EVM reference board Signed-off-by: Vladimir Barinov Signed-off-by: Takashi Iwai commit d5a528808116ff3747513ed48feb208d9450f4cc Author: Takashi Iwai Date: Sat Feb 16 09:44:56 2008 +0100 [ALSA] hda-codec - Add model=mobile for AD1884A & co Added the new model mobile for AD1884A and compatible codecs. It's a reduced version of model=laptop. Signed-off-by: Takashi Iwai commit 2d5e342d8faa4b0e697fde6e494786b24bee8acd Author: Takashi Iwai Date: Sat Feb 16 09:43:56 2008 +0100 [ALSA] hda-codec - Add support of AD1883/1884A/1984A/1984B Added the support of new AD codecs: AD1883, AD1884A, AD1984A and AD1984B. These are almost compatible except for additional digital pins, etc. Signed-off-by: Takashi Iwai commit 60adf416ae4f3b245bfa44d99a1cd4d22a3667b0 Author: Liam Girdwood Date: Fri Feb 15 16:43:11 2008 +0100 [ALSA] ASoC: WM9713 driver This patch adds an ASoC driver for the WM9713 AC97 codec. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown Signed-off-by: Takashi Iwai commit 2308e4f672da4a4aa8590fa537916e55d85b3d33 Author: Takashi Iwai Date: Thu Feb 14 17:27:17 2008 +0100 [ALSA] hda-codec - Fix missing capsrc_nids for ALC262 ALC262 must have capsrc_nids defined as well as in ALC882. Also, add a NULL check in alc882_mux_enum_put to avoid Oops. Signed-off-by: Takashi Iwai commit d47dcbec0c4bceb57f3ac2dc1692570a9ffee1bc Author: Libin Yang Date: Thu Feb 14 12:55:13 2008 +0100 [ALSA] HDA-Intel - Patch to support RV7xx HDMI Audio This patch is to add R7xx HDMI audio support. Signed-off-by: Libin Yang Signed-off-by: Takashi Iwai commit 8317c04936b09774ac2ef3929f8d5aecf58406b1 Author: Takashi Iwai Date: Wed Feb 13 17:19:35 2008 +0100 [ALSA] hda-codec - Fix breakage of resume in auto-config of realtek codecs The last patch for fixing the auto-config pin setting breaks the resume due to a wrong use of snd_hda_codec_amp_stereo(). The code in the init hook shouldn't touch the amp cache. Signed-off-by: Takashi Iwai commit 8523ebcd4c8660730c93a749424f25bb9f142af0 Author: Takashi Iwai Date: Wed Feb 13 16:59:29 2008 +0100 [ALSA] hda-codec - Add more names to vendor list Added more known names to the vendor id list. Signed-off-by: Takashi Iwai commit 41894134bfdf0d02b50608bdfdd8337fdd157df6 Author: Takashi Iwai Date: Tue Feb 12 18:37:26 2008 +0100 [ALSA] hda-codec - Add "IEC958 Default PCM" switch Added a new mixer switch to enable/disable the sharing of the default PCM stream with analog and SPDIF outputs. When "IEC958 Default PCM" switch is on, the PCM stream is routed both to analog and SPDIF outputs. This is the behavior in the earlier version. Turning this switch off has a merit for some codecs, though. Some codec chips don't support 24bit formats for SPDIF but only for analog outputs. In this case, you can use 24bit format by disabling this switch. Signed-off-by: Takashi Iwai commit 330999c9cf2b34209ce8f78618bd2ddb2b4b2841 Author: Takashi Iwai Date: Tue Feb 12 18:32:23 2008 +0100 [ALSA] hda-codec - Fix auto-configuration of Realtek codecs This patch fixes some bugs in the auto-configurator of Realtek codecs: - add missing pin set-up for speaker pins - fix the speaker auto-mute function not to conflict with the existing "Speaker" mixer switch Signed-off-by: Takashi Iwai commit aa211652951cbd2759e1ce62d49c2076e7e30fe1 Author: Takashi Iwai Date: Tue Feb 12 18:30:12 2008 +0100 [ALSA] hda-codec - More fix-up for auto-configuration In some cases, the BIOS sets up only the HP pins with different assoc and sequence numbers, e.g. on FSC Esprimo with ALC262. This patch adds a fix-up for such a case. When multiple HPs are defined and no line-outs is found, the configurator tries to re-assign some pins from HP list to line-out, judging from the sequence number. Signed-off-by: Takashi Iwai commit 74b22e0dd6cdfdb3fd5dc790abc89c4c93a90aff Author: Takashi Iwai Date: Tue Feb 12 12:11:36 2008 +0100 [ALSA] hda-codec - Implement auto-mic jack sensing on Samsung laptops Implemented the auto-mic jack sensing for Samsung laptops with AD1986A codec chip (model=laptop-eapd). The hardware uses pin 0x1d and 0x1f for the internal and external mics, respectively. Signed-off-by: Takashi Iwai commit bd1bb09c7f18a92451454329d8332bad652fb864 Author: Takashi Iwai Date: Mon Feb 11 18:32:32 2008 +0100 [ALSA] hda-codec - Clean up capture source selection of Realtek codecs Clean up the codes of the capture source selection for Realtek codecs. Now using common helper functions with the new capsrc_nids field. Signed-off-by: Takashi Iwai commit db3c660f3c6244a337642b3f113ad8f468f5f2e7 Author: Takashi Iwai Date: Mon Feb 11 15:54:34 2008 +0100 [ALSA] hda-codec - Fix automute of AD1981HD hp model Reprogram the speaker-pin setting at each HP pin plug to make sure the spekaer auto-muting on AD1981HD hp model. Signed-off-by: Takashi Iwai commit da5f1b56d1c003255be36e76331a11d929f30781 Author: Takashi Iwai Date: Mon Feb 11 14:52:36 2008 +0100 [ALSA] hda-codec - Fix ALC880 F1734 model Fixed some issues with ALC880 F1734 model - fix capture via mic - enable volume-wheel control Signed-off-by: Takashi Iwai commit 204298acba83d98982711df0792eafbb1cb9cf66 Author: Pavel Hofman Date: Mon Feb 11 14:48:06 2008 +0100 [ALSA] AK4114 - listing regs in proc A simple patch for listing AK4114 regs in proc. Signed-off-by: Pavel Hofman Signed-off-by: Takashi Iwai commit d955a4406f533bec3c806db1fa51053bd2f3a8a2 Author: Jonathan Woithe Date: Fri Feb 8 12:44:17 2008 +0100 [ALSA] hda-codec - remove duplicate controls in alc268 test mixer I've just noticed that there are a handful of duplicate controls in the ALC268 test model mixer. This patch (against alsa-driver 1.0.16) removes them. Signed-off-by: Jonathan Woithe Signed-off-by: Takashi Iwai commit 005a9cc8097b3dfaa11da7d59f853ed6f71e221c Author: Takashi Iwai Date: Thu Feb 7 17:12:01 2008 +0100 [ALSA] hda-codec - Correct HDMI transmitter names Give better names to the new HDMI transmitter chips. Signed-off-by: Takashi Iwai commit 0e929872b2e9db3335dfccb87b8923fb8154840f Author: Takashi Iwai Date: Thu Feb 7 12:06:32 2008 +0100 [ALSA] hda-intel - Fix a compile error with CONFIG_SND_DEBUG_DETECT=y Forgot to get rid of the obsolete fragsize field from a debug print. Signed-off-by: Takashi Iwai commit baa21a4190f05e3e3c4a6e91f38b9882cbbe9d47 Author: Jaroslav Kysela Date: Wed Feb 6 20:04:49 2008 +0100 [ALSA] ice1712 - added support for M-Audio Delta 66E See ALSA bug#3327 for more details. Experimental. Also fix support for M-Audio Delta 1010E - subdevice check. Signed-off-by: Jaroslav Kysela commit 36f77684f57a69283736441affd4fd4cf17f1452 Author: Jaroslav Kysela Date: Wed Feb 6 15:48:06 2008 +0100 [ALSA] Added support for Delta1010E (newer revisions of Delta1010) For more details, see ALSA bug#3327 . Signed-off-by: Jaroslav Kysela commit 40a623f4f20b1d5de5b9ca4ef95bbcf4a05dd297 Author: Takashi Iwai Date: Wed Feb 6 15:05:57 2008 +0100 [ALSA] hda-intel - Support 64bit buffer allocation The HD-audio hardware usually supports 64bit address for DMA and other buffers. The patch enables the feature if supported. Signed-off-by: Takashi Iwai commit 9fa88a0adc64e2a94bcff6e079ccc4a89a307e31 Author: Takashi Iwai Date: Wed Feb 6 14:50:19 2008 +0100 [ALSA] hda-intel - Use SG buffer Use SG buffers for the HD-audio instead of linear buffers. Signed-off-by: Takashi Iwai commit ad79aeb6d75398e1c0de575857c1f2b689f3fb8a Author: Matthew Ranostay Date: Wed Feb 6 14:49:44 2008 +0100 [ALSA] hda: STAC927x power down inactive DACs On several laptops that have STAC9228 codecs have unused DACs, this powers them down to a D3 state. Signed-off-by: Matthew Ranostay Signed-off-by: Takashi Iwai commit cd7553720d5a42b909acb69a657fd8aa8598dce1 Author: Alan Horstmann Date: Wed Feb 6 14:43:54 2008 +0100 [ALSA] ice1712 - Fix hoontech MIDI input Fixes the problems with Midi In on Hoontech/STA dsp24 cards, for example with DSP2000 box, without restricting the box configurations available. Also adds mpu_401 name strings. Signed-off-by: Alan Horstmann Signed-off-by: Takashi Iwai commit 95ef2079456a380e2e34c21ddda9a57d3ea31b07 Author: Takashi Iwai Date: Wed Feb 6 14:41:59 2008 +0100 [ALSA] hda-codec - Add ID for an unknown HDMI codec chip Added the ID for an unknown HDMI codec chip on Jetway J9F2. Signed-off-by: Takashi Iwai commit be920ce65bea9c0c102a2954429c8011a6728aaa Author: Takashi Iwai Date: Wed Feb 6 14:03:20 2008 +0100 [ALSA] hda-intel - Fix PCM device number assignment In the current scheme, PCM device numbers are assigned incrementally in the order of codecs. This causes problems when the codec number is irregular, e.g. codec #0 for HDMI and codec #1 for analog. Then the HDMI becomes the first PCM, which is picked up as the default output device. Unfortuantely this doesn't work well with normal setups. This patch introduced the fixed device numbers for the PCM types, namely, analog, SPDIF, HDMI and modem. The PCM devices are assigned according to the corresponding PCM type. After this patch, HDMI will be always assigned to PCM #3, SPDIF to PCM #1, and the first analog to PCM #0, etc. Signed-off-by: Takashi Iwai commit c04334fa690ed3cf882417fbbf11b0ab9c5cfd62 Author: Takashi Iwai Date: Mon Feb 4 12:44:11 2008 +0100 [ALSA] Add more fallbacks to OSS PHONEOUT mixer map Added more fallbacks to OSS PHONEOUT mixer mapping. This corresponds to the speaker output in general, so now "Mono" and "Speaker" are assigned. Signed-off-by: Takashi Iwai commit 8cbb61d4fe7cff64c9b7fbedc438969e9c37e008 Author: Takashi Iwai Date: Mon Feb 4 12:36:32 2008 +0100 [ALSA] ice1724 - Add ADC setup in set_rate callback for Audiophile192 Added the missing GPIO setup for the AK5385A ADC codec on Audiophile192. Signed-off-by: Takashi Iwai commit 7dbfb462d6de24ffca4cf1c15e743f77dd75064b Author: Takashi Iwai Date: Mon Feb 4 12:34:59 2008 +0100 [ALSA] ice1724 - Enable AK4114 support for Audiophile192 Fixed and enabled the support of AK4114 chip on Audiophile192. Signed-off-by: Takashi Iwai commit b88b52f9d923b4e3087edb14a34c17f13bcd3b63 Author: Mirco Tischler Date: Mon Feb 4 12:33:59 2008 +0100 [ALSA] hda-codec - Add support of Zepto laptops Adds support for zepto laptops with alc268 intel_hda codec. Signed-off-by: Mirco Tischler Signed-off-by: Takashi Iwai commit 9e3ed41515fdeaaa950b78d546bed116498ba1df Author: Takashi Iwai Date: Mon Feb 4 12:32:20 2008 +0100 [ALSA] hda-codec - Add SI HDMI codec support Added the support of SI HDMI codec, found in ASUS machines. ALSA bug#3654 https://bugtrack.alsa-project.org/alsa-bug/view.php?id=3654 Signed-off-by: Takashi Iwai commit f0ca1002891fdc8677546e94018f937aca8c9650 Author: Takashi Iwai Date: Mon Feb 4 12:31:13 2008 +0100 [ALSA] hda-codec - Allow multiple SPDIF devices The current code doesn't allow multiple SPDIF devices, and causes errors when multiple SPDIF devices are found (e.g. SPDIF out and HDMI). This patch allows multiple SPDIF devices by incrementing the index automatically. Signed-off-by: Takashi Iwai commit 5af38a04de5f264fd0f9c9dc9270304d1244538c Author: Tobin Davis Date: Sun Feb 3 20:31:47 2008 +0100 [ALSA] HDA - Add support for the OQO Model 2 This patch adds support for the OQO Model 2 Ultra Mobile PC. Signed-off-by: Tobin Davis Signed-off-by: Takashi Iwai commit b913e78c14838c6ce12ba038b152a2b4c76876f4 Author: Kristoffer Ericson Date: Fri Feb 1 13:16:10 2008 +0100 [ALSA] Add SUPERH depends to sound/soc/sh/Kconfig Currently you will see an empty "SoC Audio support for SuperH" menu when building for other archs (example pxa). This patch adds "depends on SUPERH" to remove that empty menu. Signed-off-by: Kristoffer Ericson Signed-off-by: Takashi Iwai commit a34f76c0773ec4c15717f392977b3e03a3a7d735 Author: Mike Montour Date: Fri Feb 1 13:12:12 2008 +0100 [ALSA] soc - Mono voice playback volume for WM8753 Voice playback volume is in register bits 0:2, not 4:6. From: Mike Montour Signed-off-by: Mark Brown Cc: Werner Almesberger Signed-off-by: Takashi Iwai Documentation/sound/alsa/ALSA-Configuration.txt | 42 +- include/sound/ac97_codec.h | 1 + include/sound/ak4114.h | 1 + include/sound/ak4xxx-adda.h | 2 +- include/sound/asoundef.h | 8 + include/sound/control.h | 7 + include/sound/core.h | 6 +- include/sound/version.h | 4 +- sound/arm/pxa2xx-ac97.c | 8 +- sound/core/Kconfig | 4 + sound/core/Makefile | 1 + sound/core/init.c | 2 +- sound/core/oss/mixer_oss.c | 2 + sound/core/seq/oss/seq_oss_synth.c | 9 +- sound/core/vmaster.c | 371 +++++++ sound/drivers/Kconfig | 17 + sound/drivers/Makefile | 2 +- sound/drivers/dummy.c | 28 +- sound/drivers/ml403-ac97cr.c | 2 - sound/drivers/mpu401/mpu401_uart.c | 13 +- sound/drivers/pcsp/Makefile | 2 + sound/drivers/pcsp/pcsp.c | 239 +++++ sound/drivers/pcsp/pcsp.h | 82 ++ sound/drivers/pcsp/pcsp_input.c | 116 ++ sound/drivers/pcsp/pcsp_input.h | 14 + sound/drivers/pcsp/pcsp_lib.c | 338 ++++++ sound/drivers/pcsp/pcsp_mixer.c | 143 +++ sound/i2c/other/ak4114.c | 24 +- sound/i2c/other/ak4xxx-adda.c | 16 +- sound/isa/sb/sb16_csp.c | 28 +- sound/isa/sb/sb_common.c | 6 +- sound/oss/trident.c | 12 +- sound/oss/trident.h | 2 +- sound/oss/vwsnd.c | 6 +- sound/pci/Kconfig | 18 + sound/pci/Makefile | 1 + sound/pci/ac97/ac97_patch.c | 46 +- sound/pci/ac97/ac97_pcm.c | 1 - sound/pci/ad1889.c | 4 +- sound/pci/ali5451/ali5451.c | 28 +- sound/pci/als300.c | 4 +- sound/pci/au88x0/au88x0_pcm.c | 10 +- sound/pci/aw2/Makefile | 3 + sound/pci/aw2/aw2-alsa.c | 795 ++++++++++++++ sound/pci/aw2/aw2-saa7146.c | 465 ++++++++ sound/pci/aw2/aw2-saa7146.h | 105 ++ sound/pci/aw2/aw2-tsl.c | 110 ++ sound/pci/aw2/saa7146.h | 168 +++ sound/pci/azt3328.c | 4 +- sound/pci/ca0106/ca0106_main.c | 16 +- sound/pci/ca0106/ca0106_mixer.c | 59 +- sound/pci/cmipci.c | 11 +- sound/pci/emu10k1/emu10k1x.c | 22 +- sound/pci/emu10k1/emuproc.c | 2 +- sound/pci/ens1370.c | 6 +- sound/pci/es1968.c | 18 +- sound/pci/fm801.c | 8 +- sound/pci/hda/Makefile | 2 +- sound/pci/hda/hda_codec.c | 201 ++++- sound/pci/hda/hda_codec.h | 13 +- sound/pci/hda/hda_generic.c | 4 +- sound/pci/hda/hda_intel.c | 454 +++++--- sound/pci/hda/hda_local.h | 20 +- sound/pci/hda/hda_patch.h | 28 - sound/pci/hda/patch_analog.c | 550 ++++++++++- sound/pci/hda/patch_atihdmi.c | 8 + sound/pci/hda/patch_cmedia.c | 12 +- sound/pci/hda/patch_conexant.c | 68 ++- sound/pci/hda/patch_realtek.c | 1153 ++++++++++++--------- sound/pci/hda/patch_si3054.c | 4 +- sound/pci/hda/patch_sigmatel.c | 301 +++++- sound/pci/hda/patch_via.c | 14 +- sound/pci/hda/vmaster.c | 364 ------- sound/pci/ice1712/delta.c | 22 +- sound/pci/ice1712/delta.h | 2 + sound/pci/ice1712/hoontech.c | 21 +- sound/pci/ice1712/ice1712.c | 40 +- sound/pci/ice1712/ice1712.h | 9 + sound/pci/ice1712/ice1724.c | 321 +++--- sound/pci/ice1712/juli.c | 486 ++++++++- sound/pci/ice1712/pontis.c | 4 +- sound/pci/ice1712/prodigy192.c | 33 +- sound/pci/ice1712/revo.c | 55 +- sound/pci/intel8x0.c | 30 +- sound/pci/maestro3.c | 34 +- sound/pci/oxygen/hifier.c | 10 +- sound/pci/oxygen/oxygen.c | 106 +-- sound/pci/oxygen/oxygen.h | 17 +- sound/pci/oxygen/oxygen_lib.c | 57 +- sound/pci/oxygen/oxygen_mixer.c | 195 ++++- sound/pci/oxygen/oxygen_pcm.c | 78 +- sound/pci/oxygen/pcm1796.h | 58 + sound/pci/oxygen/virtuoso.c | 122 +-- sound/pci/oxygen/wm8785.h | 45 + sound/pci/pcxhr/pcxhr.c | 7 +- sound/pci/pcxhr/pcxhr_core.c | 33 +- sound/pci/riptide/riptide.c | 14 +- sound/pci/rme32.c | 3 +- sound/pci/rme96.c | 3 +- sound/pci/rme9652/hdsp.c | 54 +- sound/pci/rme9652/hdspm.c | 4 +- sound/pci/ymfpci/ymfpci_main.c | 4 +- sound/soc/Kconfig | 1 + sound/soc/Makefile | 2 +- sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/tlv320aic3x.c | 11 + sound/soc/codecs/wm8753.c | 5 +- sound/soc/codecs/wm9713.c | 1291 +++++++++++++++++++++++ sound/soc/codecs/wm9713.h | 53 + sound/soc/davinci/Kconfig | 19 + sound/soc/davinci/Makefile | 11 + sound/soc/davinci/davinci-evm.c | 208 ++++ sound/soc/davinci/davinci-i2s.c | 407 +++++++ sound/soc/davinci/davinci-i2s.h | 17 + sound/soc/davinci/davinci-pcm.c | 389 +++++++ sound/soc/davinci/davinci-pcm.h | 29 + sound/soc/fsl/fsl_dma.c | 1 - sound/soc/fsl/fsl_ssi.c | 1 - sound/soc/pxa/pxa2xx-ac97.c | 8 +- sound/soc/s3c24xx/s3c24xx-i2s.c | 20 +- sound/soc/s3c24xx/s3c24xx-pcm.c | 28 +- sound/soc/sh/Kconfig | 1 + sound/soc/soc-dapm.c | 5 +- sound/spi/at73c213.c | 44 +- sound/usb/usbaudio.c | 9 +- sound/usb/usbquirks.h | 75 +- 127 files changed, 9151 insertions(+), 2015 deletions(-) diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index e985cf5..08256fc 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -284,6 +284,13 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. control correctly. If you have problems regarding this, try another ALSA compliant mixer (alsamixer works). + Module snd-aw2 + -------------- + + Module for Audiowerk2 sound card + + This module supports multiple cards. + Module snd-azt2320 ------------------ @@ -818,6 +825,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. hippo_1 Hippo (Benq) with jack detection sony-assamd Sony ASSAMD ultra Samsung Q1 Ultra Vista model + lenovo-3000 Lenovo 3000 y410 basic fixed pin assignment w/o SPDIF auto auto-config reading BIOS (default) @@ -826,11 +834,15 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. toshiba Toshiba A205 acer Acer laptops dell Dell OEM laptops (Vostro 1200) + zepto Zepto laptops test for testing/debugging purpose, almost all controls can adjusted. Appearing only when compiled with $CONFIG_SND_DEBUG=y auto auto-config reading BIOS (default) + ALC269 + basic Basic preset + ALC662 3stack-dig 3-stack (2-channel) with SPDIF 3stack-6ch 3-stack (6-channel) @@ -871,10 +883,11 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. lenovo-nb0763 Lenovo NB0763 lenovo-ms7195-dig Lenovo MS7195 haier-w66 Haier W66 - 6stack-hp HP machines with 6stack (Nettle boards) 3stack-hp HP machines with 3stack (Lucknow, Samba boards) 6stack-dell Dell machines with 6stack (Inspiron 530) mitac Mitac 8252D + clevo-m720 Clevo M720 laptop series + fujitsu-pi2515 Fujitsu AMILO Pi2515 auto auto-config reading BIOS (default) ALC861/660 @@ -911,6 +924,12 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. 3stack 3-stack mode (default) 6stack 6-stack mode + AD1884A / AD1883 / AD1984A / AD1984B + desktop 3-stack desktop (default) + laptop laptop with HP jack sensing + mobile mobile devices with HP jack sensing + thinkpad Lenovo Thinkpad X300 + AD1884 N/A @@ -979,6 +998,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. dell-m26 Dell Inspiron 1501 dell-m27 Dell Inspiron E1705/9400 gateway Gateway laptops with EAPD control + panasonic Panasonic CF-74 STAC9205/9254 ref Reference board @@ -1017,6 +1037,16 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. 3stack D965 3stack 5stack D965 5stack + SPDIF dell-3stack Dell Dimension E520 + dell-bios Fixes with Dell BIOS setup + + STAC92HD71B* + ref Reference board + dell-m4-1 Dell desktops + dell-m4-2 Dell desktops + + STAC92HD73* + ref Reference board + dell-m6 Dell desktops STAC9872 vaio Setup for VAIO FE550G/SZ110 @@ -1590,6 +1620,16 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. Power management is _not_ supported. + Module snd-pcsp + ----------------- + + Module for internal PC-Speaker. + + nforce_wa - enable NForce chipset workaround. Expect bad sound. + + This module supports system beeps, some kind of PCM playback and + even a few mixer controls. + Module snd-pcxhr ---------------- diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h index 0148058..049edc5 100644 --- a/include/sound/ac97_codec.h +++ b/include/sound/ac97_codec.h @@ -397,6 +397,7 @@ #define AC97_HAS_NO_TONE (1<<16) /* no Tone volume */ #define AC97_HAS_NO_STD_PCM (1<<17) /* no standard AC97 PCM volume and mute */ #define AC97_HAS_NO_AUX (1<<18) /* no standard AC97 AUX volume and mute */ +#define AC97_HAS_8CH (1<<19) /* supports 8-channel output */ /* rates indexes */ #define AC97_RATES_FRONT_DAC 0 diff --git a/include/sound/ak4114.h b/include/sound/ak4114.h index 4e80d3f..d293d36 100644 --- a/include/sound/ak4114.h +++ b/include/sound/ak4114.h @@ -182,6 +182,7 @@ struct ak4114 { unsigned char rcs0; unsigned char rcs1; struct delayed_work work; + unsigned int check_flags; void *change_callback_private; void (*change_callback)(struct ak4114 *ak4114, unsigned char c0, unsigned char c1); }; diff --git a/include/sound/ak4xxx-adda.h b/include/sound/ak4xxx-adda.h index 6153b91..891cf1a 100644 --- a/include/sound/ak4xxx-adda.h +++ b/include/sound/ak4xxx-adda.h @@ -68,7 +68,7 @@ struct snd_akm4xxx { enum { SND_AK4524, SND_AK4528, SND_AK4529, SND_AK4355, SND_AK4358, SND_AK4381, - SND_AK5365, NON_AKM + SND_AK5365 } type; /* (array) information of combined codecs */ diff --git a/include/sound/asoundef.h b/include/sound/asoundef.h index 024ce62..a6e0fac 100644 --- a/include/sound/asoundef.h +++ b/include/sound/asoundef.h @@ -112,6 +112,14 @@ #define IEC958_AES3_CON_CLOCK_1000PPM (0<<4) /* 1000 ppm */ #define IEC958_AES3_CON_CLOCK_50PPM (1<<4) /* 50 ppm */ #define IEC958_AES3_CON_CLOCK_VARIABLE (2<<4) /* variable pitch */ +#define IEC958_AES4_CON_MAX_WORDLEN_24 (1<<0) /* 0 = 20-bit, 1 = 24-bit */ +#define IEC958_AES4_CON_WORDLEN (7<<1) /* mask - sample word length */ +#define IEC958_AES4_CON_WORDLEN_NOTID (0<<1) /* not indicated */ +#define IEC958_AES4_CON_WORDLEN_20_16 (1<<1) /* 20-bit or 16-bit */ +#define IEC958_AES4_CON_WORDLEN_22_18 (2<<1) /* 22-bit or 18-bit */ +#define IEC958_AES4_CON_WORDLEN_23_19 (4<<1) /* 23-bit or 19-bit */ +#define IEC958_AES4_CON_WORDLEN_24_20 (5<<1) /* 24-bit or 20-bit */ +#define IEC958_AES4_CON_WORDLEN_21_17 (6<<1) /* 21-bit or 17-bit */ /***************************************************************************** * * diff --git a/include/sound/control.h b/include/sound/control.h index e79baa6..3dc1291 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -169,4 +169,11 @@ int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol, int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); +/* + * virtual master control + */ +struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, + const unsigned int *tlv); +int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave); + #endif /* __SOUND_CONTROL_H */ diff --git a/include/sound/core.h b/include/sound/core.h index 4fc0235..356659d 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -310,7 +310,7 @@ int snd_card_file_add(struct snd_card *card, struct file *file); int snd_card_file_remove(struct snd_card *card, struct file *file); #ifndef snd_card_set_dev -#define snd_card_set_dev(card,devptr) ((card)->dev = (devptr)) +#define snd_card_set_dev(card, devptr) ((card)->dev = (devptr)) #endif /* device.c */ @@ -373,7 +373,7 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...) * snd_printd - debug printk * @fmt: format string * - * Compiled only when Works like snd_printk() for debugging purpose. + * Works like snd_printk() for debugging purposes. * Ignored when CONFIG_SND_DEBUG is not set. */ #define snd_printd(fmt, args...) \ @@ -417,7 +417,7 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...) * snd_printdd - debug printk * @format: format string * - * Compiled only when Works like snd_printk() for debugging purpose. + * Works like snd_printk() for debugging purposes. * Ignored when CONFIG_SND_DEBUG_DETECT is not set. */ #define snd_printdd(format, args...) snd_printk(format, ##args) diff --git a/include/sound/version.h b/include/sound/version.h index fac66c4..ed6fb2e 100644 --- a/include/sound/version.h +++ b/include/sound/version.h @@ -1,3 +1,3 @@ /* include/version.h. Generated by alsa/ksync script. */ -#define CONFIG_SND_VERSION "1.0.16rc2" -#define CONFIG_SND_DATE " (Thu Jan 31 16:40:16 2008 UTC)" +#define CONFIG_SND_VERSION "1.0.16" +#define CONFIG_SND_DATE "" diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c index 5d86e68..5910275 100644 --- a/sound/arm/pxa2xx-ac97.c +++ b/sound/arm/pxa2xx-ac97.c @@ -66,7 +66,7 @@ static unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1) <= 0 && !((GSR | gsr_bits) & GSR_SDONE)) { printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n", - __FUNCTION__, reg, GSR | gsr_bits); + __func__, reg, GSR | gsr_bits); val = -1; goto out; } @@ -98,7 +98,7 @@ static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigne if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1) <= 0 && !((GSR | gsr_bits) & GSR_CDONE)) printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n", - __FUNCTION__, reg, GSR | gsr_bits); + __func__, reg, GSR | gsr_bits); mutex_unlock(&car_mutex); } @@ -125,7 +125,7 @@ static void pxa2xx_ac97_reset(struct snd_ac97 *ac97) if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) { printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n", - __FUNCTION__, gsr_bits); + __func__, gsr_bits); /* let's try warm reset */ gsr_bits = 0; @@ -144,7 +144,7 @@ static void pxa2xx_ac97_reset(struct snd_ac97 *ac97) if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n", - __FUNCTION__, gsr_bits); + __func__, gsr_bits); } GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); diff --git a/sound/core/Kconfig b/sound/core/Kconfig index 829ca38..a8d71c6 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -181,3 +181,7 @@ config SND_PCM_XRUN_DEBUG It is usually not required, but if you have trouble with sound clicking when system is loaded, it may help to determine the process or driver which causes the scheduling gaps. + +config SND_VMASTER + bool + depends on SND diff --git a/sound/core/Makefile b/sound/core/Makefile index 267039a..da8e685 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -6,6 +6,7 @@ snd-y := sound.o init.o memory.o info.o control.o misc.o device.o snd-$(CONFIG_ISA_DMA_API) += isadma.o snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o info_oss.o +snd-$(CONFIG_SND_VMASTER) += vmaster.o snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ pcm_memory.o diff --git a/sound/core/init.c b/sound/core/init.c index e3338d6..f045f7d 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -254,7 +254,7 @@ static int snd_disconnect_release(struct inode *inode, struct file *file) if (likely(df)) return df->disconnected_f_op->release(inode, file); - panic("%s(%p, %p) failed!", __FUNCTION__, inode, file); + panic("%s(%p, %p) failed!", __func__, inode, file); } static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait) diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 75daed2..581aa2c 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -1257,6 +1257,8 @@ static void snd_mixer_oss_build(struct snd_mixer_oss *mixer) { SOUND_MIXER_DIGITAL3, "Digital", 2 }, { SOUND_MIXER_PHONEIN, "Phone", 0 }, { SOUND_MIXER_PHONEOUT, "Master Mono", 0 }, + { SOUND_MIXER_PHONEOUT, "Speaker", 0 }, /*fallback*/ + { SOUND_MIXER_PHONEOUT, "Mono", 0 }, /*fallback*/ { SOUND_MIXER_PHONEOUT, "Phone", 0 }, /* fallback */ { SOUND_MIXER_VIDEO, "Video", 0 }, { SOUND_MIXER_RADIO, "Radio", 0 }, diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c index ab570a0..558dadb 100644 --- a/sound/core/seq/oss/seq_oss_synth.c +++ b/sound/core/seq/oss/seq_oss_synth.c @@ -245,8 +245,13 @@ snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp) info->nr_voices = rec->nr_voices; if (info->nr_voices > 0) { info->ch = kcalloc(info->nr_voices, sizeof(struct seq_oss_chinfo), GFP_KERNEL); - if (!info->ch) - BUG(); + if (!info->ch) { + snd_printk(KERN_ERR "Cannot malloc\n"); + rec->oper.close(&info->arg); + module_put(rec->oper.owner); + snd_use_lock_free(&rec->use_lock); + continue; + } reset_channels(info); } debug_printk(("synth %d assigned\n", i)); diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c new file mode 100644 index 0000000..4cc57f9 --- /dev/null +++ b/sound/core/vmaster.c @@ -0,0 +1,371 @@ +/* + * Virtual master and slave controls + * + * Copyright (c) 2008 by Takashi Iwai + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include +#include +#include +#include + +/* + * a subset of information returned via ctl info callback + */ +struct link_ctl_info { + int type; /* value type */ + int count; /* item count */ + int min_val, max_val; /* min, max values */ +}; + +/* + * link master - this contains a list of slave controls that are + * identical types, i.e. info returns the same value type and value + * ranges, but may have different number of counts. + * + * The master control is so far only mono volume/switch for simplicity. + * The same value will be applied to all slaves. + */ +struct link_master { + struct list_head slaves; + struct link_ctl_info info; + int val; /* the master value */ + unsigned int tlv[4]; +}; + +/* + * link slave - this contains a slave control element + * + * It fakes the control callbacsk with additional attenuation by the + * master control. A slave may have either one or two channels. + */ + +struct link_slave { + struct list_head list; + struct link_master *master; + struct link_ctl_info info; + int vals[2]; /* current values */ + struct snd_kcontrol slave; /* the copy of original control entry */ +}; + +/* get the slave ctl info and save the initial values */ +static int slave_init(struct link_slave *slave) +{ + struct snd_ctl_elem_info *uinfo; + struct snd_ctl_elem_value *uctl; + int err, ch; + + if (slave->info.count) + return 0; /* already initialized */ + + uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL); + if (!uinfo) + return -ENOMEM; + uinfo->id = slave->slave.id; + err = slave->slave.info(&slave->slave, uinfo); + if (err < 0) { + kfree(uinfo); + return err; + } + slave->info.type = uinfo->type; + slave->info.count = uinfo->count; + if (slave->info.count > 2 || + (slave->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER && + slave->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) { + snd_printk(KERN_ERR "invalid slave element\n"); + kfree(uinfo); + return -EINVAL; + } + slave->info.min_val = uinfo->value.integer.min; + slave->info.max_val = uinfo->value.integer.max; + kfree(uinfo); + + uctl = kmalloc(sizeof(*uctl), GFP_KERNEL); + if (!uctl) + return -ENOMEM; + uctl->id = slave->slave.id; + err = slave->slave.get(&slave->slave, uctl); + for (ch = 0; ch < slave->info.count; ch++) + slave->vals[ch] = uctl->value.integer.value[ch]; + kfree(uctl); + return 0; +} + +/* initialize master volume */ +static int master_init(struct link_master *master) +{ + struct link_slave *slave; + + if (master->info.count) + return 0; /* already initialized */ + + list_for_each_entry(slave, &master->slaves, list) { + int err = slave_init(slave); + if (err < 0) + return err; + master->info = slave->info; + master->info.count = 1; /* always mono */ + /* set full volume as default (= no attenuation) */ + master->val = master->info.max_val; + return 0; + } + return -ENOENT; +} + +static int slave_get_val(struct link_slave *slave, + struct snd_ctl_elem_value *ucontrol) +{ + int err, ch; + + err = slave_init(slave); + if (err < 0) + return err; + for (ch = 0; ch < slave->info.count; ch++) + ucontrol->value.integer.value[ch] = slave->vals[ch]; + return 0; +} + +static int slave_put_val(struct link_slave *slave, + struct snd_ctl_elem_value *ucontrol) +{ + int err, ch, vol; + + err = master_init(slave->master); + if (err < 0) + return err; + + switch (slave->info.type) { + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: + for (ch = 0; ch < slave->info.count; ch++) + ucontrol->value.integer.value[ch] &= + !!slave->master->val; + break; + case SNDRV_CTL_ELEM_TYPE_INTEGER: + for (ch = 0; ch < slave->info.count; ch++) { + /* max master volume is supposed to be 0 dB */ + vol = ucontrol->value.integer.value[ch]; + vol += slave->master->val - slave->master->info.max_val; + if (vol < slave->info.min_val) + vol = slave->info.min_val; + else if (vol > slave->info.max_val) + vol = slave->info.max_val; + ucontrol->value.integer.value[ch] = vol; + } + break; + } + return slave->slave.put(&slave->slave, ucontrol); +} + +/* + * ctl callbacks for slaves + */ +static int slave_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct link_slave *slave = snd_kcontrol_chip(kcontrol); + return slave->slave.info(&slave->slave, uinfo); +} + +static int slave_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct link_slave *slave = snd_kcontrol_chip(kcontrol); + return slave_get_val(slave, ucontrol); +} + +static int slave_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct link_slave *slave = snd_kcontrol_chip(kcontrol); + int err, ch, changed = 0; + + err = slave_init(slave); + if (err < 0) + return err; + for (ch = 0; ch < slave->info.count; ch++) { + if (slave->vals[ch] != ucontrol->value.integer.value[ch]) { + changed = 1; + slave->vals[ch] = ucontrol->value.integer.value[ch]; + } + } + if (!changed) + return 0; + return slave_put_val(slave, ucontrol); +} + +static int slave_tlv_cmd(struct snd_kcontrol *kcontrol, + int op_flag, unsigned int size, + unsigned int __user *tlv) +{ + struct link_slave *slave = snd_kcontrol_chip(kcontrol); + /* FIXME: this assumes that the max volume is 0 dB */ + return slave->slave.tlv.c(&slave->slave, op_flag, size, tlv); +} + +static void slave_free(struct snd_kcontrol *kcontrol) +{ + struct link_slave *slave = snd_kcontrol_chip(kcontrol); + if (slave->slave.private_free) + slave->slave.private_free(&slave->slave); + if (slave->master) + list_del(&slave->list); + kfree(slave); +} + +/* + * Add a slave control to the group with the given master control + * + * All slaves must be the same type (returning the same information + * via info callback). The fucntion doesn't check it, so it's your + * responsibility. + * + * Also, some additional limitations: + * - at most two channels + * - logarithmic volume control (dB level), no linear volume + * - master can only attenuate the volume, no gain + */ +int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave) +{ + struct link_master *master_link = snd_kcontrol_chip(master); + struct link_slave *srec; + + srec = kzalloc(sizeof(*srec) + + slave->count * sizeof(*slave->vd), GFP_KERNEL); + if (!srec) + return -ENOMEM; + srec->slave = *slave; + memcpy(srec->slave.vd, slave->vd, slave->count * sizeof(*slave->vd)); + srec->master = master_link; + + /* override callbacks */ + slave->info = slave_info; + slave->get = slave_get; + slave->put = slave_put; + if (slave->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) + slave->tlv.c = slave_tlv_cmd; + slave->private_data = srec; + slave->private_free = slave_free; + + list_add_tail(&srec->list, &master_link->slaves); + return 0; +} + +EXPORT_SYMBOL(snd_ctl_add_slave); + +/* + * ctl callbacks for master controls + */ +static int master_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct link_master *master = snd_kcontrol_chip(kcontrol); + int ret; + + ret = master_init(master); + if (ret < 0) + return ret; + uinfo->type = master->info.type; + uinfo->count = master->info.count; + uinfo->value.integer.min = master->info.min_val; + uinfo->value.integer.max = master->info.max_val; + return 0; +} + +static int master_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct link_master *master = snd_kcontrol_chip(kcontrol); + int err = master_init(master); + if (err < 0) + return err; + ucontrol->value.integer.value[0] = master->val; + return 0; +} + +static int master_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct link_master *master = snd_kcontrol_chip(kcontrol); + struct link_slave *slave; + struct snd_ctl_elem_value *uval; + int err, old_val; + + err = master_init(master); + if (err < 0) + return err; + old_val = master->val; + if (ucontrol->value.integer.value[0] == old_val) + return 0; + + uval = kmalloc(sizeof(*uval), GFP_KERNEL); + if (!uval) + return -ENOMEM; + list_for_each_entry(slave, &master->slaves, list) { + master->val = old_val; + uval->id = slave->slave.id; + slave_get_val(slave, uval); + master->val = ucontrol->value.integer.value[0]; + slave_put_val(slave, uval); + } + kfree(uval); + return 1; +} + +static void master_free(struct snd_kcontrol *kcontrol) +{ + struct link_master *master = snd_kcontrol_chip(kcontrol); + struct link_slave *slave; + + list_for_each_entry(slave, &master->slaves, list) + slave->master = NULL; + kfree(master); +} + + +/* + * Create a virtual master control with the given name + */ +struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, + const unsigned int *tlv) +{ + struct link_master *master; + struct snd_kcontrol *kctl; + struct snd_kcontrol_new knew; + + memset(&knew, 0, sizeof(knew)); + knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + knew.name = name; + knew.info = master_info; + + master = kzalloc(sizeof(*master), GFP_KERNEL); + if (!master) + return NULL; + INIT_LIST_HEAD(&master->slaves); + + kctl = snd_ctl_new1(&knew, master); + if (!kctl) { + kfree(master); + return NULL; + } + /* override some callbacks */ + kctl->info = master_info; + kctl->get = master_get; + kctl->put = master_put; + kctl->private_free = master_free; + + /* additional (constant) TLV read */ + if (tlv && tlv[0] == SNDRV_CTL_TLVT_DB_SCALE) { + kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; + memcpy(master->tlv, tlv, sizeof(master->tlv)); + kctl->tlv.p = master->tlv; + } + + return kctl; +} + +EXPORT_SYMBOL(snd_ctl_make_virtual_master); diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig index 75d4fe0..78648c4 100644 --- a/sound/drivers/Kconfig +++ b/sound/drivers/Kconfig @@ -4,6 +4,23 @@ menu "Generic devices" depends on SND!=n +config SND_PCSP + tristate "Internal PC speaker support" + depends on X86_PC && HIGH_RES_TIMERS + help + If you don't have a sound card in your computer, you can include a + driver for the PC speaker which allows it to act like a primitive + sound card. + This driver also replaces the pcspkr driver for beeps. + + You can compile this as a module which will be called snd-pcsp. + + You don't need this driver if you only want your pc-speaker to beep. + You don't need this driver if you have a tablet piezo beeper + in your PC instead of the real speaker. + + It should not hurt to say Y or M here in all other cases. + config SND_MPU401_UART tristate select SND_RAWMIDI diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile index 8e55300..d4a07f9 100644 --- a/sound/drivers/Makefile +++ b/sound/drivers/Makefile @@ -20,4 +20,4 @@ obj-$(CONFIG_SND_MTS64) += snd-mts64.o obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o obj-$(CONFIG_SND_ML403_AC97CR) += snd-ml403-ac97cr.o -obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ +obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ pcsp/ diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index a240eae..83ac498 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -181,10 +181,10 @@ struct snd_dummy_pcm { struct snd_dummy *dummy; spinlock_t lock; struct timer_list timer; - unsigned int pcm_size; - unsigned int pcm_count; + unsigned int pcm_buffer_size; + unsigned int pcm_period_size; unsigned int pcm_bps; /* bytes per second */ - unsigned int pcm_jiffie; /* bytes per one jiffie */ + unsigned int pcm_hz; /* HZ */ unsigned int pcm_irq_pos; /* IRQ position */ unsigned int pcm_buf_pos; /* position in buffer */ struct snd_pcm_substream *substream; @@ -238,11 +238,15 @@ static int snd_card_dummy_pcm_prepare(struct snd_pcm_substream *substream) if (bps <= 0) return -EINVAL; dpcm->pcm_bps = bps; - dpcm->pcm_jiffie = bps / HZ; - dpcm->pcm_size = snd_pcm_lib_buffer_bytes(substream); - dpcm->pcm_count = snd_pcm_lib_period_bytes(substream); + dpcm->pcm_hz = HZ; + dpcm->pcm_buffer_size = snd_pcm_lib_buffer_bytes(substream); + dpcm->pcm_period_size = snd_pcm_lib_period_bytes(substream); dpcm->pcm_irq_pos = 0; dpcm->pcm_buf_pos = 0; + + snd_pcm_format_set_silence(runtime->format, runtime->dma_area, + bytes_to_samples(runtime, runtime->dma_bytes)); + return 0; } @@ -254,11 +258,11 @@ static void snd_card_dummy_pcm_timer_function(unsigned long data) spin_lock_irqsave(&dpcm->lock, flags); dpcm->timer.expires = 1 + jiffies; add_timer(&dpcm->timer); - dpcm->pcm_irq_pos += dpcm->pcm_jiffie; - dpcm->pcm_buf_pos += dpcm->pcm_jiffie; - dpcm->pcm_buf_pos %= dpcm->pcm_size; - if (dpcm->pcm_irq_pos >= dpcm->pcm_count) { - dpcm->pcm_irq_pos %= dpcm->pcm_count; + dpcm->pcm_irq_pos += dpcm->pcm_bps; + dpcm->pcm_buf_pos += dpcm->pcm_bps; + dpcm->pcm_buf_pos %= dpcm->pcm_buffer_size * dpcm->pcm_hz; + if (dpcm->pcm_irq_pos >= dpcm->pcm_period_size * dpcm->pcm_hz) { + dpcm->pcm_irq_pos %= dpcm->pcm_period_size * dpcm->pcm_hz; spin_unlock_irqrestore(&dpcm->lock, flags); snd_pcm_period_elapsed(dpcm->substream); } else @@ -270,7 +274,7 @@ static snd_pcm_uframes_t snd_card_dummy_pcm_pointer(struct snd_pcm_substream *su struct snd_pcm_runtime *runtime = substream->runtime; struct snd_dummy_pcm *dpcm = runtime->private_data; - return bytes_to_frames(runtime, dpcm->pcm_buf_pos); + return bytes_to_frames(runtime, dpcm->pcm_buf_pos / dpcm->pcm_hz); } static struct snd_pcm_hardware snd_card_dummy_playback = diff --git a/sound/drivers/ml403-ac97cr.c b/sound/drivers/ml403-ac97cr.c index 05a871a..b654007 100644 --- a/sound/drivers/ml403-ac97cr.c +++ b/sound/drivers/ml403-ac97cr.c @@ -1191,8 +1191,6 @@ snd_ml403_ac97cr_create(struct snd_card *card, struct platform_device *pfdev, return err; } - snd_card_set_dev(card, &pfdev->dev); - *rml403_ac97cr = ml403_ac97cr; return 0; } diff --git a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c index 5993864..dd6ec42 100644 --- a/sound/drivers/mpu401/mpu401_uart.c +++ b/sound/drivers/mpu401/mpu401_uart.c @@ -425,16 +425,17 @@ static void snd_mpu401_uart_input_read(struct snd_mpu401 * mpu) static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu) { unsigned char byte; - int max = 256, timeout; + int max = 256; do { if (snd_rawmidi_transmit_peek(mpu->substream_output, &byte, 1) == 1) { - for (timeout = 100; timeout > 0; timeout--) { - if (snd_mpu401_output_ready(mpu)) - break; - } - if (timeout == 0) + /* + * Try twice because there is hardware that insists on + * setting the output busy bit after each write. + */ + if (!snd_mpu401_output_ready(mpu) && + !snd_mpu401_output_ready(mpu)) break; /* Tx FIFO full - try again later */ mpu->write(mpu, byte, MPU401D(mpu)); snd_rawmidi_transmit_ack(mpu->substream_output, 1); diff --git a/sound/drivers/pcsp/Makefile b/sound/drivers/pcsp/Makefile new file mode 100644 index 0000000..b19555b --- /dev/null +++ b/sound/drivers/pcsp/Makefile @@ -0,0 +1,2 @@ +snd-pcsp-objs := pcsp.o pcsp_lib.o pcsp_mixer.o pcsp_input.o +obj-$(CONFIG_SND_PCSP) += snd-pcsp.o diff --git a/sound/drivers/pcsp/pcsp.c b/sound/drivers/pcsp/pcsp.c new file mode 100644 index 0000000..d8f9621 --- /dev/null +++ b/sound/drivers/pcsp/pcsp.c @@ -0,0 +1,239 @@ +/* + * PC-Speaker driver for Linux + * + * Copyright (C) 1997-2001 David Woodhouse + * Copyright (C) 2001-2008 Stas Sergeev + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pcsp_input.h" +#include "pcsp.h" + +MODULE_AUTHOR("Stas Sergeev "); +MODULE_DESCRIPTION("PC-Speaker driver"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{PC-Speaker, pcsp}}"); +MODULE_ALIAS("platform:pcspkr"); + +static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ +static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ +static int enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */ + +module_param(index, int, 0444); +MODULE_PARM_DESC(index, "Index value for pcsp soundcard."); +module_param(id, charp, 0444); +MODULE_PARM_DESC(id, "ID string for pcsp soundcard."); +module_param(enable, bool, 0444); +MODULE_PARM_DESC(enable, "Enable PC-Speaker sound."); + +struct snd_pcsp pcsp_chip; + +static int __devinit snd_pcsp_create(struct snd_card *card) +{ + static struct snd_device_ops ops = { }; + struct timespec tp; + int err; + int div, min_div, order; + + hrtimer_get_res(CLOCK_MONOTONIC, &tp); + if (tp.tv_sec || tp.tv_nsec > PCSP_MAX_PERIOD_NS) { + printk(KERN_ERR "PCSP: Timer resolution is not sufficient " + "(%linS)\n", tp.tv_nsec); + printk(KERN_ERR "PCSP: Make sure you have HPET and ACPI " + "enabled.\n"); + return -EIO; + } + + if (loops_per_jiffy >= PCSP_MIN_LPJ && tp.tv_nsec <= PCSP_MIN_PERIOD_NS) + min_div = MIN_DIV; + else + min_div = MAX_DIV; +#if PCSP_DEBUG + printk("PCSP: lpj=%li, min_div=%i, res=%li\n", + loops_per_jiffy, min_div, tp.tv_nsec); +#endif + + div = MAX_DIV / min_div; + order = fls(div) - 1; + + pcsp_chip.max_treble = min(order, PCSP_MAX_TREBLE); + pcsp_chip.treble = min(pcsp_chip.max_treble, PCSP_DEFAULT_TREBLE); + pcsp_chip.playback_ptr = 0; + pcsp_chip.period_ptr = 0; + atomic_set(&pcsp_chip.timer_active, 0); + pcsp_chip.enable = 1; + pcsp_chip.pcspkr = 1; + + spin_lock_init(&pcsp_chip.substream_lock); + + pcsp_chip.card = card; + pcsp_chip.port = 0x61; + pcsp_chip.irq = -1; + pcsp_chip.dma = -1; + + /* Register device */ + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, &pcsp_chip, &ops); + if (err < 0) + return err; + + return 0; +} + +static int __devinit snd_card_pcsp_probe(int devnum, struct device *dev) +{ + struct snd_card *card; + int err; + + if (devnum != 0) + return -EINVAL; + + hrtimer_init(&pcsp_chip.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + pcsp_chip.timer.cb_mode = HRTIMER_CB_IRQSAFE; + pcsp_chip.timer.function = pcsp_do_timer; + + card = snd_card_new(index, id, THIS_MODULE, 0); + if (!card) + return -ENOMEM; + + err = snd_pcsp_create(card); + if (err < 0) { + snd_card_free(card); + return err; + } + err = snd_pcsp_new_pcm(&pcsp_chip); + if (err < 0) { + snd_card_free(card); + return err; + } + err = snd_pcsp_new_mixer(&pcsp_chip); + if (err < 0) { + snd_card_free(card); + return err; + } + + snd_card_set_dev(pcsp_chip.card, dev); + + strcpy(card->driver, "PC-Speaker"); + strcpy(card->shortname, "pcsp"); + sprintf(card->longname, "Internal PC-Speaker at port 0x%x", + pcsp_chip.port); + + err = snd_card_register(card); + if (err < 0) { + snd_card_free(card); + return err; + } + + return 0; +} + +static int __devinit alsa_card_pcsp_init(struct device *dev) +{ + int err; + + err = snd_card_pcsp_probe(0, dev); + if (err) { + printk(KERN_ERR "PC-Speaker initialization failed.\n"); + return err; + } + +#ifdef CONFIG_DEBUG_PAGEALLOC + /* Well, CONFIG_DEBUG_PAGEALLOC makes the sound horrible. Lets alert */ + printk(KERN_WARNING + "PCSP: Warning, CONFIG_DEBUG_PAGEALLOC is enabled!\n" + "You have to disable it if you want to use the PC-Speaker " + "driver.\n" + "Unless it is disabled, enjoy the horrible, distorted " + "and crackling noise.\n"); +#endif + + return 0; +} + +static void __devexit alsa_card_pcsp_exit(struct snd_pcsp *chip) +{ + snd_card_free(chip->card); +} + +static int __devinit pcsp_probe(struct platform_device *dev) +{ + int err; + + err = pcspkr_input_init(&pcsp_chip.input_dev, &dev->dev); + if (err < 0) + return err; + + err = alsa_card_pcsp_init(&dev->dev); + if (err < 0) { + pcspkr_input_remove(pcsp_chip.input_dev); + return err; + } + + platform_set_drvdata(dev, &pcsp_chip); + return 0; +} + +static int __devexit pcsp_remove(struct platform_device *dev) +{ + struct snd_pcsp *chip = platform_get_drvdata(dev); + alsa_card_pcsp_exit(chip); + pcspkr_input_remove(chip->input_dev); + platform_set_drvdata(dev, NULL); + return 0; +} + +static void pcsp_stop_beep(struct snd_pcsp *chip) +{ + spin_lock_irq(&chip->substream_lock); + if (!chip->playback_substream) + pcspkr_stop_sound(); + spin_unlock_irq(&chip->substream_lock); +} + +static int pcsp_suspend(struct platform_device *dev, pm_message_t state) +{ + struct snd_pcsp *chip = platform_get_drvdata(dev); + pcsp_stop_beep(chip); + snd_pcm_suspend_all(chip->pcm); + return 0; +} + +static void pcsp_shutdown(struct platform_device *dev) +{ + struct snd_pcsp *chip = platform_get_drvdata(dev); + pcsp_stop_beep(chip); +} + +static struct platform_driver pcsp_platform_driver = { + .driver = { + .name = "pcspkr", + .owner = THIS_MODULE, + }, + .probe = pcsp_probe, + .remove = __devexit_p(pcsp_remove), + .suspend = pcsp_suspend, + .shutdown = pcsp_shutdown, +}; + +static int __init pcsp_init(void) +{ + if (!enable) + return -ENODEV; + return platform_driver_register(&pcsp_platform_driver); +} + +static void __exit pcsp_exit(void) +{ + platform_driver_unregister(&pcsp_platform_driver); +} + +module_init(pcsp_init); +module_exit(pcsp_exit); diff --git a/sound/drivers/pcsp/pcsp.h b/sound/drivers/pcsp/pcsp.h new file mode 100644 index 0000000..f07cc1e --- /dev/null +++ b/sound/drivers/pcsp/pcsp.h @@ -0,0 +1,82 @@ +/* + * PC-Speaker driver for Linux + * + * Copyright (C) 1993-1997 Michael Beck + * Copyright (C) 1997-2001 David Woodhouse + * Copyright (C) 2001-2008 Stas Sergeev + */ + +#ifndef __PCSP_H__ +#define __PCSP_H__ + +#include +#if defined(CONFIG_MIPS) || defined(CONFIG_X86) +/* Use the global PIT lock ! */ +#include +#else +#include +static DEFINE_SPINLOCK(i8253_lock); +#endif + +#define PCSP_SOUND_VERSION 0x400 /* read 4.00 */ +#define PCSP_DEBUG 0 + +/* default timer freq for PC-Speaker: 18643 Hz */ +#define DIV_18KHZ 64 +#define MAX_DIV DIV_18KHZ +#define CUR_DIV() (MAX_DIV >> chip->treble) +#define PCSP_MAX_TREBLE 1 + +/* unfortunately, with hrtimers 37KHz does not work very well :( */ +#define PCSP_DEFAULT_TREBLE 0 +#define MIN_DIV (MAX_DIV >> PCSP_MAX_TREBLE) + +/* wild guess */ +#define PCSP_MIN_LPJ 1000000 +#define PCSP_DEFAULT_SDIV (DIV_18KHZ >> 1) +#define PCSP_DEFAULT_SRATE (PIT_TICK_RATE / PCSP_DEFAULT_SDIV) +#define PCSP_INDEX_INC() (1 << (PCSP_MAX_TREBLE - chip->treble)) +#define PCSP_RATE() (PIT_TICK_RATE / CUR_DIV()) +#define PCSP_MIN_RATE__1 MAX_DIV/PIT_TICK_RATE +#define PCSP_MAX_RATE__1 MIN_DIV/PIT_TICK_RATE +#define PCSP_MAX_PERIOD_NS (1000000000ULL * PCSP_MIN_RATE__1) +#define PCSP_MIN_PERIOD_NS (1000000000ULL * PCSP_MAX_RATE__1) +#define PCSP_CALC_NS(div) ({ \ + u64 __val = 1000000000ULL * (div); \ + do_div(__val, PIT_TICK_RATE); \ + __val; \ +}) +#define PCSP_PERIOD_NS() PCSP_CALC_NS(CUR_DIV()) + +#define PCSP_MAX_PERIOD_SIZE (64*1024) +#define PCSP_MAX_PERIODS 512 +#define PCSP_BUFFER_SIZE (128*1024) + +struct snd_pcsp { + struct snd_card *card; + struct snd_pcm *pcm; + struct input_dev *input_dev; + struct hrtimer timer; + unsigned short port, irq, dma; + spinlock_t substream_lock; + struct snd_pcm_substream *playback_substream; + size_t playback_ptr; + size_t period_ptr; + atomic_t timer_active; + int thalf; + u64 ns_rem; + unsigned char val61; + int enable; + int max_treble; + int treble; + int pcspkr; +}; + +extern struct snd_pcsp pcsp_chip; + +extern enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle); + +extern int snd_pcsp_new_pcm(struct snd_pcsp *chip); +extern int snd_pcsp_new_mixer(struct snd_pcsp *chip); + +#endif diff --git a/sound/drivers/pcsp/pcsp_input.c b/sound/drivers/pcsp/pcsp_input.c new file mode 100644 index 0000000..cd9b83e --- /dev/null +++ b/sound/drivers/pcsp/pcsp_input.c @@ -0,0 +1,116 @@ +/* + * PC Speaker beeper driver for Linux + * + * Copyright (c) 2002 Vojtech Pavlik + * Copyright (c) 1992 Orest Zborowski + * + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation + */ + +#include +#include +#include +#include "pcsp.h" + +static void pcspkr_do_sound(unsigned int count) +{ + unsigned long flags; + + spin_lock_irqsave(&i8253_lock, flags); + + if (count) { + /* enable counter 2 */ + outb_p(inb_p(0x61) | 3, 0x61); + /* set command for counter 2, 2 byte write */ + outb_p(0xB6, 0x43); + /* select desired HZ */ + outb_p(count & 0xff, 0x42); + outb((count >> 8) & 0xff, 0x42); + } else { + /* disable counter 2 */ + outb(inb_p(0x61) & 0xFC, 0x61); + } + + spin_unlock_irqrestore(&i8253_lock, flags); +} + +void pcspkr_stop_sound(void) +{ + pcspkr_do_sound(0); +} + +static int pcspkr_input_event(struct input_dev *dev, unsigned int type, + unsigned int code, int value) +{ + unsigned int count = 0; + + if (atomic_read(&pcsp_chip.timer_active) || !pcsp_chip.pcspkr) + return 0; + + switch (type) { + case EV_SND: + switch (code) { + case SND_BELL: + if (value) + value = 1000; + case SND_TONE: + break; + default: + return -1; + } + break; + + default: + return -1; + } + + if (value > 20 && value < 32767) + count = PIT_TICK_RATE / value; + + pcspkr_do_sound(count); + + return 0; +} + +int __devinit pcspkr_input_init(struct input_dev **rdev, struct device *dev) +{ + int err; + + struct input_dev *input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + input_dev->name = "PC Speaker"; + input_dev->phys = "isa0061/input0"; + input_dev->id.bustype = BUS_ISA; + input_dev->id.vendor = 0x001f; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + input_dev->dev.parent = dev; + + input_dev->evbit[0] = BIT(EV_SND); + input_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE); + input_dev->event = pcspkr_input_event; + + err = input_register_device(input_dev); + if (err) { + input_free_device(input_dev); + return err; + } + + *rdev = input_dev; + return 0; +} + +int pcspkr_input_remove(struct input_dev *dev) +{ + pcspkr_stop_sound(); + input_unregister_device(dev); /* this also does kfree() */ + + return 0; +} diff --git a/sound/drivers/pcsp/pcsp_input.h b/sound/drivers/pcsp/pcsp_input.h new file mode 100644 index 0000000..e66738c --- /dev/null +++ b/sound/drivers/pcsp/pcsp_input.h @@ -0,0 +1,14 @@ +/* + * PC-Speaker driver for Linux + * + * Copyright (C) 2001-2008 Stas Sergeev + */ + +#ifndef __PCSP_INPUT_H__ +#define __PCSP_INPUT_H__ + +int __devinit pcspkr_input_init(struct input_dev **rdev, struct device *dev); +int pcspkr_input_remove(struct input_dev *dev); +void pcspkr_stop_sound(void); + +#endif diff --git a/sound/drivers/pcsp/pcsp_lib.c b/sound/drivers/pcsp/pcsp_lib.c new file mode 100644 index 0000000..ac6238e --- /dev/null +++ b/sound/drivers/pcsp/pcsp_lib.c @@ -0,0 +1,338 @@ +/* + * PC-Speaker driver for Linux + * + * Copyright (C) 1993-1997 Michael Beck + * Copyright (C) 1997-2001 David Woodhouse + * Copyright (C) 2001-2008 Stas Sergeev + */ + +#include +#include +#include +#include +#include +#include "pcsp.h" + +static int nforce_wa; +module_param(nforce_wa, bool, 0444); +MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround " + "(expect bad sound)"); + +static void pcsp_start_timer(unsigned long dummy) +{ + hrtimer_start(&pcsp_chip.timer, ktime_set(0, 0), HRTIMER_MODE_REL); +} + +/* + * We need the hrtimer_start as a tasklet to avoid + * the nasty locking problem. :( + * The problem: + * - The timer handler is called with the cpu_base->lock + * already held by hrtimer code. + * - snd_pcm_period_elapsed() takes the + * substream->self_group.lock. + * So far so good. + * But the snd_pcsp_trigger() is called with the + * substream->self_group.lock held, and it calls + * hrtimer_start(), which takes the cpu_base->lock. + * You see the problem. We have the code pathes + * which take two locks in a reverse order. This + * can deadlock and the lock validator complains. + * The only solution I could find was to move the + * hrtimer_start() into a tasklet. -stsp + */ +static DECLARE_TASKLET(pcsp_start_timer_tasklet, pcsp_start_timer, 0); + +enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) +{ + unsigned long flags; + unsigned char timer_cnt, val; + int periods_elapsed; + u64 ns; + size_t period_bytes, buffer_bytes; + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; + struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer); + + if (chip->thalf) { + outb(chip->val61, 0x61); + chip->thalf = 0; + if (!atomic_read(&chip->timer_active)) + return HRTIMER_NORESTART; + hrtimer_forward(&chip->timer, chip->timer.expires, + ktime_set(0, chip->ns_rem)); + return HRTIMER_RESTART; + } + + /* hrtimer calls us from both hardirq and softirq contexts, + * so irqsave :( */ + spin_lock_irqsave(&chip->substream_lock, flags); + /* Takashi Iwai says regarding this extra lock: + + If the irq handler handles some data on the DMA buffer, it should + do snd_pcm_stream_lock(). + That protects basically against all races among PCM callbacks, yes. + However, there are two remaining issues: + 1. The substream pointer you try to lock isn't protected _before_ + this lock yet. + 2. snd_pcm_period_elapsed() itself acquires the lock. + The requirement of another lock is because of 1. When you get + chip->playback_substream, it's not protected. + Keeping this lock while snd_pcm_period_elapsed() assures the substream + is still protected (at least, not released). And the other status is + handled properly inside snd_pcm_stream_lock() in + snd_pcm_period_elapsed(). + + */ + if (!chip->playback_substream) + goto exit_nr_unlock1; + substream = chip->playback_substream; + snd_pcm_stream_lock(substream); + if (!atomic_read(&chip->timer_active)) + goto exit_nr_unlock2; + + runtime = substream->runtime; + /* assume it is u8 mono */ + val = runtime->dma_area[chip->playback_ptr]; + timer_cnt = val * CUR_DIV() / 256; + + if (timer_cnt && chip->enable) { + spin_lock(&i8253_lock); + if (!nforce_wa) { + outb_p(chip->val61, 0x61); + outb_p(timer_cnt, 0x42); + outb(chip->val61 ^ 1, 0x61); + } else { + outb(chip->val61 ^ 2, 0x61); + chip->thalf = 1; + } + spin_unlock(&i8253_lock); + } + + period_bytes = snd_pcm_lib_period_bytes(substream); + buffer_bytes = snd_pcm_lib_buffer_bytes(substream); + chip->playback_ptr += PCSP_INDEX_INC(); + periods_elapsed = chip->playback_ptr - chip->period_ptr; + if (periods_elapsed < 0) { + printk(KERN_WARNING "PCSP: playback_ptr inconsistent " + "(%zi %zi %zi)\n", + chip->playback_ptr, period_bytes, buffer_bytes); + periods_elapsed += buffer_bytes; + } + periods_elapsed /= period_bytes; + /* wrap the pointer _before_ calling snd_pcm_period_elapsed(), + * or ALSA will BUG on us. */ + chip->playback_ptr %= buffer_bytes; + + snd_pcm_stream_unlock(substream); + + if (periods_elapsed) { + snd_pcm_period_elapsed(substream); + chip->period_ptr += periods_elapsed * period_bytes; + chip->period_ptr %= buffer_bytes; + } + + spin_unlock_irqrestore(&chip->substream_lock, flags); + + if (!atomic_read(&chip->timer_active)) + return HRTIMER_NORESTART; + + chip->ns_rem = PCSP_PERIOD_NS(); + ns = (chip->thalf ? PCSP_CALC_NS(timer_cnt) : chip->ns_rem); + chip->ns_rem -= ns; + hrtimer_forward(&chip->timer, chip->timer.expires, ktime_set(0, ns)); + return HRTIMER_RESTART; + +exit_nr_unlock2: + snd_pcm_stream_unlock(substream); +exit_nr_unlock1: + spin_unlock_irqrestore(&chip->substream_lock, flags); + return HRTIMER_NORESTART; +} + +static void pcsp_start_playing(struct snd_pcsp *chip) +{ +#if PCSP_DEBUG + printk(KERN_INFO "PCSP: start_playing called\n"); +#endif + if (atomic_read(&chip->timer_active)) { + printk(KERN_ERR "PCSP: Timer already active\n"); + return; + } + + spin_lock(&i8253_lock); + chip->val61 = inb(0x61) | 0x03; + outb_p(0x92, 0x43); /* binary, mode 1, LSB only, ch 2 */ + spin_unlock(&i8253_lock); + atomic_set(&chip->timer_active, 1); + chip->thalf = 0; + + tasklet_schedule(&pcsp_start_timer_tasklet); +} + +static void pcsp_stop_playing(struct snd_pcsp *chip) +{ +#if PCSP_DEBUG + printk(KERN_INFO "PCSP: stop_playing called\n"); +#endif + if (!atomic_read(&chip->timer_active)) + return; + + atomic_set(&chip->timer_active, 0); + spin_lock(&i8253_lock); + /* restore the timer */ + outb_p(0xb6, 0x43); /* binary, mode 3, LSB/MSB, ch 2 */ + outb(chip->val61 & 0xFC, 0x61); + spin_unlock(&i8253_lock); +} + +static int snd_pcsp_playback_close(struct snd_pcm_substream *substream) +{ + struct snd_pcsp *chip = snd_pcm_substream_chip(substream); +#if PCSP_DEBUG + printk(KERN_INFO "PCSP: close called\n"); +#endif + if (atomic_read(&chip->timer_active)) { + printk(KERN_ERR "PCSP: timer still active\n"); + pcsp_stop_playing(chip); + } + spin_lock_irq(&chip->substream_lock); + chip->playback_substream = NULL; + spin_unlock_irq(&chip->substream_lock); + return 0; +} + +static int snd_pcsp_playback_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + int err; + err = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + return err; + return 0; +} + +static int snd_pcsp_playback_hw_free(struct snd_pcm_substream *substream) +{ +#if PCSP_DEBUG + printk(KERN_INFO "PCSP: hw_free called\n"); +#endif + return snd_pcm_lib_free_pages(substream); +} + +static int snd_pcsp_playback_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcsp *chip = snd_pcm_substream_chip(substream); +#if PCSP_DEBUG + printk(KERN_INFO "PCSP: prepare called, " + "size=%zi psize=%zi f=%zi f1=%i\n", + snd_pcm_lib_buffer_bytes(substream), + snd_pcm_lib_period_bytes(substream), + snd_pcm_lib_buffer_bytes(substream) / + snd_pcm_lib_period_bytes(substream), + substream->runtime->periods); +#endif + chip->playback_ptr = 0; + chip->period_ptr = 0; + return 0; +} + +static int snd_pcsp_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcsp *chip = snd_pcm_substream_chip(substream); +#if PCSP_DEBUG + printk(KERN_INFO "PCSP: trigger called\n"); +#endif + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + pcsp_start_playing(chip); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + pcsp_stop_playing(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +static snd_pcm_uframes_t snd_pcsp_playback_pointer(struct snd_pcm_substream + *substream) +{ + struct snd_pcsp *chip = snd_pcm_substream_chip(substream); + return bytes_to_frames(substream->runtime, chip->playback_ptr); +} + +static struct snd_pcm_hardware snd_pcsp_playback = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_HALF_DUPLEX | + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_U8, + .rates = SNDRV_PCM_RATE_KNOT, + .rate_min = PCSP_DEFAULT_SRATE, + .rate_max = PCSP_DEFAULT_SRATE, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = PCSP_BUFFER_SIZE, + .period_bytes_min = 64, + .period_bytes_max = PCSP_MAX_PERIOD_SIZE, + .periods_min = 2, + .periods_max = PCSP_MAX_PERIODS, + .fifo_size = 0, +}; + +static int snd_pcsp_playback_open(struct snd_pcm_substream *substream) +{ + struct snd_pcsp *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; +#if PCSP_DEBUG + printk(KERN_INFO "PCSP: open called\n"); +#endif + if (atomic_read(&chip->timer_active)) { + printk(KERN_ERR "PCSP: still active!!\n"); + return -EBUSY; + } + runtime->hw = snd_pcsp_playback; + spin_lock_irq(&chip->substream_lock); + chip->playback_substream = substream; + spin_unlock_irq(&chip->substream_lock); + return 0; +} + +static struct snd_pcm_ops snd_pcsp_playback_ops = { + .open = snd_pcsp_playback_open, + .close = snd_pcsp_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_pcsp_playback_hw_params, + .hw_free = snd_pcsp_playback_hw_free, + .prepare = snd_pcsp_playback_prepare, + .trigger = snd_pcsp_trigger, + .pointer = snd_pcsp_playback_pointer, +}; + +int __devinit snd_pcsp_new_pcm(struct snd_pcsp *chip) +{ + int err; + + err = snd_pcm_new(chip->card, "pcspeaker", 0, 1, 0, &chip->pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(chip->pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_pcsp_playback_ops); + + chip->pcm->private_data = chip; + chip->pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; + strcpy(chip->pcm->name, "pcsp"); + + snd_pcm_lib_preallocate_pages_for_all(chip->pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data + (GFP_KERNEL), PCSP_BUFFER_SIZE, + PCSP_BUFFER_SIZE); + + return 0; +} diff --git a/sound/drivers/pcsp/pcsp_mixer.c b/sound/drivers/pcsp/pcsp_mixer.c new file mode 100644 index 0000000..64a695f --- /dev/null +++ b/sound/drivers/pcsp/pcsp_mixer.c @@ -0,0 +1,143 @@ +/* + * PC-Speaker driver for Linux + * + * Mixer implementation. + * Copyright (C) 2001-2008 Stas Sergeev + */ + +#include +#include +#include "pcsp.h" + + +static int pcsp_enable_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 pcsp_enable_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = chip->enable; + return 0; +} + +static int pcsp_enable_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol); + int changed = 0; + int enab = ucontrol->value.integer.value[0]; + if (enab != chip->enable) { + chip->enable = enab; + changed = 1; + } + return changed; +} + +static int pcsp_treble_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = chip->max_treble + 1; + if (uinfo->value.enumerated.item > chip->max_treble) + uinfo->value.enumerated.item = chip->max_treble; + sprintf(uinfo->value.enumerated.name, "%d", PCSP_RATE()); + return 0; +} + +static int pcsp_treble_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.enumerated.item[0] = chip->treble; + return 0; +} + +static int pcsp_treble_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol); + int changed = 0; + int treble = ucontrol->value.enumerated.item[0]; + if (treble != chip->treble) { + chip->treble = treble; +#if PCSP_DEBUG + printk(KERN_INFO "PCSP: rate set to %i\n", PCSP_RATE()); +#endif + changed = 1; + } + return changed; +} + +static int pcsp_pcspkr_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 pcsp_pcspkr_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = chip->pcspkr; + return 0; +} + +static int pcsp_pcspkr_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol); + int changed = 0; + int spkr = ucontrol->value.integer.value[0]; + if (spkr != chip->pcspkr) { + chip->pcspkr = spkr; + changed = 1; + } + return changed; +} + +#define PCSP_MIXER_CONTROL(ctl_type, ctl_name) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = ctl_name, \ + .info = pcsp_##ctl_type##_info, \ + .get = pcsp_##ctl_type##_get, \ + .put = pcsp_##ctl_type##_put, \ +} + +static struct snd_kcontrol_new __devinitdata snd_pcsp_controls[] = { + PCSP_MIXER_CONTROL(enable, "Master Playback Switch"), + PCSP_MIXER_CONTROL(treble, "BaseFRQ Playback Volume"), + PCSP_MIXER_CONTROL(pcspkr, "PC Speaker Playback Switch"), +}; + +int __devinit snd_pcsp_new_mixer(struct snd_pcsp *chip) +{ + struct snd_card *card = chip->card; + int i, err; + + for (i = 0; i < ARRAY_SIZE(snd_pcsp_controls); i++) { + err = snd_ctl_add(card, + snd_ctl_new1(snd_pcsp_controls + i, + chip)); + if (err < 0) + return err; + } + + strcpy(card->mixername, "PC-Speaker"); + + return 0; +} diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c index 15061bd..d20d893 100644 --- a/sound/i2c/other/ak4114.c +++ b/sound/i2c/other/ak4114.c @@ -27,6 +27,7 @@ #include #include #include +#include MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("AK4114 IEC958 (S/PDIF) receiver by Asahi Kasei"); @@ -446,6 +447,26 @@ static struct snd_kcontrol_new snd_ak4114_iec958_controls[] = { } }; + +static void snd_ak4114_proc_regs_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct ak4114 *ak4114 = entry->private_data; + int reg, val; + /* all ak4114 registers 0x00 - 0x1f */ + for (reg = 0; reg < 0x20; reg++) { + val = reg_read(ak4114, reg); + snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val); + } +} + +static void snd_ak4114_proc_init(struct ak4114 *ak4114) +{ + struct snd_info_entry *entry; + if (!snd_card_proc_new(ak4114->card, "ak4114", &entry)) + snd_info_set_text_ops(entry, ak4114, snd_ak4114_proc_regs_read); +} + int snd_ak4114_build(struct ak4114 *ak4114, struct snd_pcm_substream *ply_substream, struct snd_pcm_substream *cap_substream) @@ -478,6 +499,7 @@ int snd_ak4114_build(struct ak4114 *ak4114, return err; ak4114->kctls[idx] = kctl; } + snd_ak4114_proc_init(ak4114); /* trigger workq */ schedule_delayed_work(&ak4114->work, HZ / 10); return 0; @@ -590,7 +612,7 @@ static void ak4114_stats(struct work_struct *work) struct ak4114 *chip = container_of(work, struct ak4114, work.work); if (!chip->init) - snd_ak4114_check_rate_and_errors(chip, 0); + snd_ak4114_check_rate_and_errors(chip, chip->check_flags); schedule_delayed_work(&chip->work, HZ / 10); } diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c index 35fbbf2..288926d 100644 --- a/sound/i2c/other/ak4xxx-adda.c +++ b/sound/i2c/other/ak4xxx-adda.c @@ -70,7 +70,8 @@ static void ak4524_reset(struct snd_akm4xxx *ak, int state) } /* reset procedure for AK4355 and AK4358 */ -static void ak4355_reset(struct snd_akm4xxx *ak, int state) +static void ak435X_reset(struct snd_akm4xxx *ak, int state, + unsigned char total_regs) { unsigned char reg; @@ -78,7 +79,7 @@ static void ak4355_reset(struct snd_akm4xxx *ak, int state) snd_akm4xxx_write(ak, 0, 0x01, 0x02); /* reset and soft-mute */ return; } - for (reg = 0x00; reg < 0x0b; reg++) + for (reg = 0x00; reg < total_regs; reg++) if (reg != 0x01) snd_akm4xxx_write(ak, 0, reg, snd_akm4xxx_get(ak, 0, reg)); @@ -118,8 +119,10 @@ void snd_akm4xxx_reset(struct snd_akm4xxx *ak, int state) /* FIXME: needed for ak4529? */ break; case SND_AK4355: + ak435X_reset(ak, state, 0x0b); + break; case SND_AK4358: - ak4355_reset(ak, state); + ak435X_reset(ak, state, 0x10); break; case SND_AK4381: ak4381_reset(ak, state); @@ -292,11 +295,6 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak) case SND_AK5365: /* FIXME: any init sequence? */ return; - case NON_AKM: - /* fake value for non-akm codecs using akm infrastructure - * (e.g. of ice1724) - certainly FIXME - */ - return; default: snd_BUG(); return; @@ -374,6 +372,8 @@ static int put_ak_reg(struct snd_kcontrol *kcontrol, int addr, nval = mask - nval; if (AK_GET_NEEDSMSB(kcontrol->private_value)) nval |= 0x80; + /* printk(KERN_DEBUG "DEBUG - AK writing reg: chip %x addr %x, + nval %x\n", chip, addr, nval); */ snd_akm4xxx_write(ak, chip, addr, nval); return 1; } diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c index bed29ca..f3fd7b4 100644 --- a/sound/isa/sb/sb16_csp.c +++ b/sound/isa/sb/sb16_csp.c @@ -331,7 +331,7 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p, return -EFAULT; if ((file_h.name != RIFF_HEADER) || (le32_to_cpu(file_h.len) >= SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE - sizeof(file_h))) { - snd_printd("%s: Invalid RIFF header\n", __FUNCTION__); + snd_printd("%s: Invalid RIFF header\n", __func__); return -EINVAL; } data_ptr += sizeof(file_h); @@ -340,7 +340,7 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p, if (copy_from_user(&item_type, data_ptr, sizeof(item_type))) return -EFAULT; if (item_type != CSP__HEADER) { - snd_printd("%s: Invalid RIFF file type\n", __FUNCTION__); + snd_printd("%s: Invalid RIFF file type\n", __func__); return -EINVAL; } data_ptr += sizeof (item_type); @@ -395,7 +395,7 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p, return -EFAULT; if (code_h.name != MAIN_HEADER) { - snd_printd("%s: Missing 'main' microcode\n", __FUNCTION__); + snd_printd("%s: Missing 'main' microcode\n", __func__); return -EINVAL; } data_ptr += sizeof(code_h); @@ -439,7 +439,7 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p, p->acc_format = p->acc_width = p->acc_rates = 0; p->mode = 0; snd_printd("%s: Unsupported CSP codec type: 0x%04x\n", - __FUNCTION__, + __func__, le16_to_cpu(funcdesc_h.VOC_type)); return -EINVAL; } @@ -458,7 +458,7 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p, return 0; } } - snd_printd("%s: Function #%d not found\n", __FUNCTION__, info.func_req); + snd_printd("%s: Function #%d not found\n", __func__, info.func_req); return -EINVAL; } @@ -612,7 +612,7 @@ static int get_version(struct snd_sb *chip) static int snd_sb_csp_check_version(struct snd_sb_csp * p) { if (p->version < 0x10 || p->version > 0x1f) { - snd_printd("%s: Invalid CSP version: 0x%x\n", __FUNCTION__, p->version); + snd_printd("%s: Invalid CSP version: 0x%x\n", __func__, p->version); return 1; } return 0; @@ -631,7 +631,7 @@ static int snd_sb_csp_load(struct snd_sb_csp * p, const unsigned char *buf, int spin_lock_irqsave(&p->chip->reg_lock, flags); snd_sbdsp_command(p->chip, 0x01); /* CSP download command */ if (snd_sbdsp_get_byte(p->chip)) { - snd_printd("%s: Download command failed\n", __FUNCTION__); + snd_printd("%s: Download command failed\n", __func__); goto __fail; } /* Send CSP low byte (size - 1) */ @@ -658,7 +658,7 @@ static int snd_sb_csp_load(struct snd_sb_csp * p, const unsigned char *buf, int udelay (10); } if (status != 0x55) { - snd_printd("%s: Microcode initialization failed\n", __FUNCTION__); + snd_printd("%s: Microcode initialization failed\n", __func__); goto __fail; } } else { @@ -824,19 +824,19 @@ static int snd_sb_csp_start(struct snd_sb_csp * p, int sample_width, int channel unsigned long flags; if (!(p->running & (SNDRV_SB_CSP_ST_LOADED | SNDRV_SB_CSP_ST_AUTO))) { - snd_printd("%s: Microcode not loaded\n", __FUNCTION__); + snd_printd("%s: Microcode not loaded\n", __func__); return -ENXIO; } if (p->running & SNDRV_SB_CSP_ST_RUNNING) { - snd_printd("%s: CSP already running\n", __FUNCTION__); + snd_printd("%s: CSP already running\n", __func__); return -EBUSY; } if (!(sample_width & p->acc_width)) { - snd_printd("%s: Unsupported PCM sample width\n", __FUNCTION__); + snd_printd("%s: Unsupported PCM sample width\n", __func__); return -EINVAL; } if (!(channels & p->acc_channels)) { - snd_printd("%s: Invalid number of channels\n", __FUNCTION__); + snd_printd("%s: Invalid number of channels\n", __func__); return -EINVAL; } @@ -858,11 +858,11 @@ static int snd_sb_csp_start(struct snd_sb_csp * p, int sample_width, int channel s_type |= 0x22; /* 00dX 00dX (d = 1 if 8 bit samples) */ if (set_codec_parameter(p->chip, 0x81, s_type)) { - snd_printd("%s: Set sample type command failed\n", __FUNCTION__); + snd_printd("%s: Set sample type command failed\n", __func__); goto __fail; } if (set_codec_parameter(p->chip, 0x80, 0x00)) { - snd_printd("%s: Codec start command failed\n", __FUNCTION__); + snd_printd("%s: Codec start command failed\n", __func__); goto __fail; } p->run_width = sample_width; diff --git a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c index d63c1af..b432d9a 100644 --- a/sound/isa/sb/sb_common.c +++ b/sound/isa/sb/sb_common.c @@ -51,7 +51,7 @@ int snd_sbdsp_command(struct snd_sb *chip, unsigned char val) outb(val, SBP(chip, COMMAND)); return 1; } - snd_printd("%s [0x%lx]: timeout (0x%x)\n", __FUNCTION__, chip->port, val); + snd_printd("%s [0x%lx]: timeout (0x%x)\n", __func__, chip->port, val); return 0; } @@ -68,7 +68,7 @@ int snd_sbdsp_get_byte(struct snd_sb *chip) return val; } } - snd_printd("%s [0x%lx]: timeout\n", __FUNCTION__, chip->port); + snd_printd("%s [0x%lx]: timeout\n", __func__, chip->port); return -ENODEV; } @@ -87,7 +87,7 @@ int snd_sbdsp_reset(struct snd_sb *chip) else break; } - snd_printdd("%s [0x%lx] failed...\n", __FUNCTION__, chip->port); + snd_printdd("%s [0x%lx] failed...\n", __func__, chip->port); return -ENODEV; } diff --git a/sound/oss/trident.c b/sound/oss/trident.c index d6af906..f43f91e 100644 --- a/sound/oss/trident.c +++ b/sound/oss/trident.c @@ -3076,8 +3076,7 @@ ali_ac97_get(struct trident_card *card, int secondary, u8 reg) u16 wcontrol; unsigned long flags; - if (!card) - BUG(); + BUG_ON(!card); address = ALI_AC97_READ; if (card->revision == ALI_5451_V02) { @@ -3148,8 +3147,7 @@ ali_ac97_set(struct trident_card *card, int secondary, u8 reg, u16 val) data = ((u32) val) << 16; - if (!card) - BUG(); + BUG_ON(!card); address = ALI_AC97_WRITE; mask = ALI_AC97_WRITE_ACTION | ALI_AC97_AUDIO_BUSY; @@ -3213,8 +3211,7 @@ ali_ac97_read(struct ac97_codec *codec, u8 reg) struct trident_card *card = NULL; /* Added by Matt Wu */ - if (!codec) - BUG(); + BUG_ON(!codec); card = (struct trident_card *) codec->private_data; @@ -3240,8 +3237,7 @@ ali_ac97_write(struct ac97_codec *codec, u8 reg, u16 val) struct trident_card *card; /* Added by Matt Wu */ - if (!codec) - BUG(); + BUG_ON(!codec); card = (struct trident_card *) codec->private_data; diff --git a/sound/oss/trident.h b/sound/oss/trident.h index 4713b49..ff30a1d 100644 --- a/sound/oss/trident.h +++ b/sound/oss/trident.h @@ -322,7 +322,7 @@ enum miscint_bits { #define VALIDATE_MAGIC(FOO,MAG) \ ({ \ if (!(FOO) || (FOO)->magic != MAG) { \ - printk(invalid_magic,__FUNCTION__); \ + printk(invalid_magic,__func__); \ return -ENXIO; \ } \ }) diff --git a/sound/oss/vwsnd.c b/sound/oss/vwsnd.c index d25249a..2c5aaa5 100644 --- a/sound/oss/vwsnd.c +++ b/sound/oss/vwsnd.c @@ -194,11 +194,11 @@ static void dbgassert(const char *fcn, int line, const char *expr) * DBGRV - debug print function return when verbose */ -#define ASSERT(e) ((e) ? (void) 0 : dbgassert(__FUNCTION__, __LINE__, #e)) +#define ASSERT(e) ((e) ? (void) 0 : dbgassert(__func__, __LINE__, #e)) #define DBGDO(x) x #define DBGX(fmt, args...) (in_interrupt() ? 0 : printk(KERN_ERR fmt, ##args)) -#define DBGP(fmt, args...) (DBGX("%s: " fmt, __FUNCTION__ , ##args)) -#define DBGE(fmt, args...) (DBGX("%s" fmt, __FUNCTION__ , ##args)) +#define DBGP(fmt, args...) (DBGX("%s: " fmt, __func__ , ##args)) +#define DBGE(fmt, args...) (DBGX("%s" fmt, __func__ , ##args)) #define DBGC(rtn) (DBGP("calling %s\n", rtn)) #define DBGR() (DBGP("returning\n")) #define DBGXV(fmt, args...) (shut_up ? 0 : DBGX(fmt, ##args)) diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 812085d..b5903ee 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -122,6 +122,21 @@ config SND_AU8830 To compile this driver as a module, choose M here: the module will be called snd-au8830. +config SND_AW2 + tristate "Emagic Audiowerk 2" + depends on SND + help + Say Y here to include support for Emagic Audiowerk 2 soundcards. + + Supported features: Analog and SPDIF output. Analog or SPDIF input. + Note: Switch between analog and digital input does not always work. + It can produce continuous noise. The workaround is to switch again + (and again) between digital and analog input until it works. + + To compile this driver as a module, choose M here: the module + will be called snd-aw2. + + config SND_AZT3328 tristate "Aztech AZF3328 / PCI168 (EXPERIMENTAL)" depends on SND && EXPERIMENTAL @@ -162,6 +177,7 @@ config SND_CA0106 depends on SND select SND_AC97_CODEC select SND_RAWMIDI + select SND_VMASTER help Say Y here to include support for the Sound Blaster Audigy LS and Live 24bit. @@ -517,6 +533,7 @@ config SND_HDA_INTEL tristate "Intel HD Audio" depends on SND select SND_PCM + select SND_VMASTER help Say Y here to include support for Intel "High Definition Audio" (Azalia) motherboard devices. @@ -680,6 +697,7 @@ config SND_ICE1724 depends on SND select SND_MPU401_UART select SND_AC97_CODEC + select SND_VMASTER help Say Y here to include support for soundcards based on ICE/VT1724/1720 (Envy24HT/PT) chips. diff --git a/sound/pci/Makefile b/sound/pci/Makefile index 2d42fd2..85ef14b 100644 --- a/sound/pci/Makefile +++ b/sound/pci/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_SND) += \ ac97/ \ ali5451/ \ au88x0/ \ + aw2/ \ ca0106/ \ cs46xx/ \ cs5535audio/ \ diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 50c637e..39198e5 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -114,10 +114,9 @@ static int ac97_surround_jack_mode_put(struct snd_kcontrol *kcontrol, struct snd static int ac97_channel_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static const char *texts[] = { "2ch", "4ch", "6ch" }; - if (kcontrol->private_value) - return ac97_enum_text_info(kcontrol, uinfo, texts, 2); /* 4ch only */ - return ac97_enum_text_info(kcontrol, uinfo, texts, 3); + static const char *texts[] = { "2ch", "4ch", "6ch", "8ch" }; + return ac97_enum_text_info(kcontrol, uinfo, texts, + kcontrol->private_value); } static int ac97_channel_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -133,13 +132,8 @@ static int ac97_channel_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); unsigned char mode = ucontrol->value.enumerated.item[0]; - if (kcontrol->private_value) { - if (mode >= 2) - return -EINVAL; - } else { - if (mode >= 3) - return -EINVAL; - } + if (mode >= kcontrol->private_value) + return -EINVAL; if (mode != ac97->channel_mode) { ac97->channel_mode = mode; @@ -158,6 +152,7 @@ static int ac97_channel_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e .get = ac97_surround_jack_mode_get, \ .put = ac97_surround_jack_mode_put, \ } +/* 6ch */ #define AC97_CHANNEL_MODE_CTL \ { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ @@ -165,7 +160,9 @@ static int ac97_channel_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e .info = ac97_channel_mode_info, \ .get = ac97_channel_mode_get, \ .put = ac97_channel_mode_put, \ + .private_value = 3, \ } +/* 4ch */ #define AC97_CHANNEL_MODE_4CH_CTL \ { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ @@ -173,7 +170,17 @@ static int ac97_channel_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e .info = ac97_channel_mode_info, \ .get = ac97_channel_mode_get, \ .put = ac97_channel_mode_put, \ - .private_value = 1, \ + .private_value = 2, \ + } +/* 8ch */ +#define AC97_CHANNEL_MODE_8CH_CTL \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = "Channel Mode", \ + .info = ac97_channel_mode_info, \ + .get = ac97_channel_mode_get, \ + .put = ac97_channel_mode_put, \ + .private_value = 4, \ } static inline int is_surround_on(struct snd_ac97 *ac97) @@ -210,6 +217,10 @@ static inline int is_shared_micin(struct snd_ac97 *ac97) return !ac97->indep_surround && !is_clfe_on(ac97); } +static inline int alc850_is_aux_back_surround(struct snd_ac97 *ac97) +{ + return is_surround_on(ac97); +} /* The following snd_ac97_ymf753_... items added by David Shust (dshust@shustring.com) */ /* Modified for YMF743 by Keita Maehara */ @@ -2816,10 +2827,12 @@ static int patch_alc655(struct snd_ac97 * ac97) #define AC97_ALC850_JACK_SELECT 0x76 #define AC97_ALC850_MISC1 0x7a +#define AC97_ALC850_MULTICH 0x6a static void alc850_update_jacks(struct snd_ac97 *ac97) { int shared; + int aux_is_back_surround; /* shared Line-In / Surround Out */ shared = is_shared_surrout(ac97); @@ -2837,13 +2850,18 @@ static void alc850_update_jacks(struct snd_ac97 *ac97) /* MIC-IN = 1, CENTER-LFE = 5 */ snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 4, shared ? (5<<4) : (1<<4)); + + aux_is_back_surround = alc850_is_aux_back_surround(ac97); + /* Aux is Back Surround */ + snd_ac97_update_bits(ac97, AC97_ALC850_MULTICH, 1 << 10, + aux_is_back_surround ? (1<<10) : (0<<10)); } static const struct snd_kcontrol_new snd_ac97_controls_alc850[] = { AC97_PAGE_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0, 0), AC97_SINGLE("Mic Front Input Switch", AC97_ALC850_JACK_SELECT, 15, 1, 1), AC97_SURROUND_JACK_MODE_CTL, - AC97_CHANNEL_MODE_CTL, + AC97_CHANNEL_MODE_8CH_CTL, }; static int patch_alc850_specific(struct snd_ac97 *ac97) @@ -2869,6 +2887,7 @@ static int patch_alc850(struct snd_ac97 *ac97) ac97->build_ops = &patch_alc850_ops; ac97->spec.dev_flags = 0; /* for IEC958 playback route - ALC655 compatible */ + ac97->flags |= AC97_HAS_8CH; /* assume only page 0 for writing cache */ snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, AC97_PAGE_VENDOR); @@ -2878,6 +2897,7 @@ static int patch_alc850(struct snd_ac97 *ac97) spdif-in monitor off, spdif-in PCM off center on mic off, surround on line-in off duplicate front off + NB default bit 10=0 = Aux is Capture, not Back Surround */ snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, 1<<15); /* SURR_OUT: on, Surr 1kOhm: on, Surr Amp: off, Front 1kOhm: off diff --git a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c index 3674f35..48cbda9 100644 --- a/sound/pci/ac97/ac97_pcm.c +++ b/sound/pci/ac97/ac97_pcm.c @@ -574,7 +574,6 @@ int snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate, r = rate > 48000; bus = pcm->bus; if (cfg == AC97_PCM_CFG_SPDIF) { - int err; for (cidx = 0; cidx < 4; cidx++) if (bus->codec[cidx] && (bus->codec[cidx]->ext_id & AC97_EI_SPDIF)) { err = set_spdif_rate(bus->codec[cidx], rate); diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index a66d515..1edb644 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -264,10 +264,10 @@ snd_ad1889_ac97_ready(struct snd_ad1889 *chip) mdelay(1); if (!retry) { snd_printk(KERN_ERR PFX "[%s] Link is not ready.\n", - __FUNCTION__); + __func__); return -EIO; } - ad1889_debug("[%s] ready after %d ms\n", __FUNCTION__, 400 - retry); + ad1889_debug("[%s] ready after %d ms\n", __func__, 400 - retry); return 0; } diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index 6a905ed..fc04d3d 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -1809,26 +1809,26 @@ static int snd_ali5451_spdif_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ali *codec = kcontrol->private_data; - unsigned int enable; + unsigned int spdif_enable; - enable = ucontrol->value.integer.value[0] ? 1 : 0; + spdif_enable = ucontrol->value.integer.value[0] ? 1 : 0; spin_lock_irq(&codec->reg_lock); switch (kcontrol->private_value) { case 0: - enable = (codec->spdif_mask & 0x02) ? 1 : 0; + spdif_enable = (codec->spdif_mask & 0x02) ? 1 : 0; break; case 1: - enable = ((codec->spdif_mask & 0x02) && + spdif_enable = ((codec->spdif_mask & 0x02) && (codec->spdif_mask & 0x04)) ? 1 : 0; break; case 2: - enable = (codec->spdif_mask & 0x01) ? 1 : 0; + spdif_enable = (codec->spdif_mask & 0x01) ? 1 : 0; break; default: break; } - ucontrol->value.integer.value[0] = enable; + ucontrol->value.integer.value[0] = spdif_enable; spin_unlock_irq(&codec->reg_lock); return 0; } @@ -1837,17 +1837,17 @@ static int snd_ali5451_spdif_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ali *codec = kcontrol->private_data; - unsigned int change = 0, enable = 0; + unsigned int change = 0, spdif_enable = 0; - enable = ucontrol->value.integer.value[0] ? 1 : 0; + spdif_enable = ucontrol->value.integer.value[0] ? 1 : 0; spin_lock_irq(&codec->reg_lock); switch (kcontrol->private_value) { case 0: change = (codec->spdif_mask & 0x02) ? 1 : 0; - change = change ^ enable; + change = change ^ spdif_enable; if (change) { - if (enable) { + if (spdif_enable) { codec->spdif_mask |= 0x02; snd_ali_enable_spdif_out(codec); } else { @@ -1859,9 +1859,9 @@ static int snd_ali5451_spdif_put(struct snd_kcontrol *kcontrol, break; case 1: change = (codec->spdif_mask & 0x04) ? 1 : 0; - change = change ^ enable; + change = change ^ spdif_enable; if (change && (codec->spdif_mask & 0x02)) { - if (enable) { + if (spdif_enable) { codec->spdif_mask |= 0x04; snd_ali_enable_spdif_chnout(codec); } else { @@ -1872,9 +1872,9 @@ static int snd_ali5451_spdif_put(struct snd_kcontrol *kcontrol, break; case 2: change = (codec->spdif_mask & 0x01) ? 1 : 0; - change = change ^ enable; + change = change ^ spdif_enable; if (change) { - if (enable) { + if (spdif_enable) { codec->spdif_mask |= 0x01; snd_ali_enable_spdif_in(codec); } else { diff --git a/sound/pci/als300.c b/sound/pci/als300.c index 0e990a7..8df6824 100644 --- a/sound/pci/als300.c +++ b/sound/pci/als300.c @@ -92,8 +92,8 @@ #if DEBUG_CALLS #define snd_als300_dbgcalls(format, args...) printk(format, ##args) -#define snd_als300_dbgcallenter() printk(KERN_ERR "--> %s\n", __FUNCTION__) -#define snd_als300_dbgcallleave() printk(KERN_ERR "<-- %s\n", __FUNCTION__) +#define snd_als300_dbgcallenter() printk(KERN_ERR "--> %s\n", __func__) +#define snd_als300_dbgcallleave() printk(KERN_ERR "<-- %s\n", __func__) #else #define snd_als300_dbgcalls(format, args...) #define snd_als300_dbgcallenter() diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c index 526c6c5..f9a58b4 100644 --- a/sound/pci/au88x0/au88x0_pcm.c +++ b/sound/pci/au88x0/au88x0_pcm.c @@ -498,14 +498,14 @@ static struct snd_kcontrol_new snd_vortex_mixer_spdif[] __devinitdata = { }; /* create a pcm device */ -static int __devinit snd_vortex_new_pcm(vortex_t * chip, int idx, int nr) +static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr) { struct snd_pcm *pcm; struct snd_kcontrol *kctl; int i; int err, nr_capt; - if ((chip == 0) || (idx < 0) || (idx >= VORTEX_PCM_LAST)) + if (!chip || idx < 0 || idx >= VORTEX_PCM_LAST) return -ENODEV; /* idx indicates which kind of PCM device. ADB, SPDIF, I2S and A3D share the @@ -514,9 +514,9 @@ static int __devinit snd_vortex_new_pcm(vortex_t * chip, int idx, int nr) nr_capt = nr; else nr_capt = 0; - if ((err = - snd_pcm_new(chip->card, vortex_pcm_prettyname[idx], idx, nr, - nr_capt, &pcm)) < 0) + err = snd_pcm_new(chip->card, vortex_pcm_prettyname[idx], idx, nr, + nr_capt, &pcm); + if (err < 0) return err; strcpy(pcm->name, vortex_pcm_name[idx]); chip->pcm[idx] = pcm; diff --git a/sound/pci/aw2/Makefile b/sound/pci/aw2/Makefile new file mode 100644 index 0000000..842335d --- /dev/null +++ b/sound/pci/aw2/Makefile @@ -0,0 +1,3 @@ +snd-aw2-objs := aw2-alsa.o aw2-saa7146.o + +obj-$(CONFIG_SND_AW2) += snd-aw2.o diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c new file mode 100644 index 0000000..12d01c9 --- /dev/null +++ b/sound/pci/aw2/aw2-alsa.c @@ -0,0 +1,795 @@ +/***************************************************************************** + * + * Copyright (C) 2008 Cedric Bregardis and + * Jean-Christian Hassler + * + * This file is part of the Audiowerk2 ALSA driver + * + * The Audiowerk2 ALSA driver is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2. + * + * The Audiowerk2 ALSA driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Audiowerk2 ALSA driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + *****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "saa7146.h" +#include "aw2-saa7146.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Cedric Bregardis , " + "Jean-Christian Hassler "); +MODULE_DESCRIPTION("Emagic Audiowerk 2 sound driver"); +MODULE_LICENSE("GPL"); + +/********************************* + * DEFINES + ********************************/ +#define PCI_VENDOR_ID_SAA7146 0x1131 +#define PCI_DEVICE_ID_SAA7146 0x7146 + +#define CTL_ROUTE_ANALOG 0 +#define CTL_ROUTE_DIGITAL 1 + +/********************************* + * TYPEDEFS + ********************************/ + /* hardware definition */ +static struct snd_pcm_hardware snd_aw2_playback_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_44100, + .rate_min = 44100, + .rate_max = 44100, + .channels_min = 2, + .channels_max = 4, + .buffer_bytes_max = 32768, + .period_bytes_min = 4096, + .period_bytes_max = 32768, + .periods_min = 1, + .periods_max = 1024, +}; + +static struct snd_pcm_hardware snd_aw2_capture_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_44100, + .rate_min = 44100, + .rate_max = 44100, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 32768, + .period_bytes_min = 4096, + .period_bytes_max = 32768, + .periods_min = 1, + .periods_max = 1024, +}; + +struct aw2_pcm_device { + struct snd_pcm *pcm; + unsigned int stream_number; + struct aw2 *chip; +}; + +struct aw2 { + struct snd_aw2_saa7146 saa7146; + + struct pci_dev *pci; + int irq; + spinlock_t reg_lock; + struct mutex mtx; + + unsigned long iobase_phys; + void __iomem *iobase_virt; + + struct snd_card *card; + + struct aw2_pcm_device device_playback[NB_STREAM_PLAYBACK]; + struct aw2_pcm_device device_capture[NB_STREAM_CAPTURE]; +}; + +/********************************* + * FUNCTION DECLARATIONS + ********************************/ +static int __init alsa_card_aw2_init(void); +static void __exit alsa_card_aw2_exit(void); +static int snd_aw2_dev_free(struct snd_device *device); +static int __devinit snd_aw2_create(struct snd_card *card, + struct pci_dev *pci, struct aw2 **rchip); +static int __devinit snd_aw2_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id); +static void __devexit snd_aw2_remove(struct pci_dev *pci); +static int snd_aw2_pcm_playback_open(struct snd_pcm_substream *substream); +static int snd_aw2_pcm_playback_close(struct snd_pcm_substream *substream); +static int snd_aw2_pcm_capture_open(struct snd_pcm_substream *substream); +static int snd_aw2_pcm_capture_close(struct snd_pcm_substream *substream); +static int snd_aw2_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params); +static int snd_aw2_pcm_hw_free(struct snd_pcm_substream *substream); +static int snd_aw2_pcm_prepare_playback(struct snd_pcm_substream *substream); +static int snd_aw2_pcm_prepare_capture(struct snd_pcm_substream *substream); +static int snd_aw2_pcm_trigger_playback(struct snd_pcm_substream *substream, + int cmd); +static int snd_aw2_pcm_trigger_capture(struct snd_pcm_substream *substream, + int cmd); +static snd_pcm_uframes_t snd_aw2_pcm_pointer_playback(struct snd_pcm_substream + *substream); +static snd_pcm_uframes_t snd_aw2_pcm_pointer_capture(struct snd_pcm_substream + *substream); +static int __devinit snd_aw2_new_pcm(struct aw2 *chip); + +static int snd_aw2_control_switch_capture_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +static int snd_aw2_control_switch_capture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value + *ucontrol); +static int snd_aw2_control_switch_capture_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value + *ucontrol); + +/********************************* + * VARIABLES + ********************************/ +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for Audiowerk2 soundcard."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for the Audiowerk2 soundcard."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable Audiowerk2 soundcard."); + +static struct pci_device_id snd_aw2_ids[] = { + {PCI_VENDOR_ID_SAA7146, PCI_DEVICE_ID_SAA7146, PCI_ANY_ID, PCI_ANY_ID, + 0, 0, 0}, + {0} +}; + +MODULE_DEVICE_TABLE(pci, snd_aw2_ids); + +/* pci_driver definition */ +static struct pci_driver driver = { + .name = "Emagic Audiowerk 2", + .id_table = snd_aw2_ids, + .probe = snd_aw2_probe, + .remove = __devexit_p(snd_aw2_remove), +}; + +/* operators for playback PCM alsa interface */ +static struct snd_pcm_ops snd_aw2_playback_ops = { + .open = snd_aw2_pcm_playback_open, + .close = snd_aw2_pcm_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_aw2_pcm_hw_params, + .hw_free = snd_aw2_pcm_hw_free, + .prepare = snd_aw2_pcm_prepare_playback, + .trigger = snd_aw2_pcm_trigger_playback, + .pointer = snd_aw2_pcm_pointer_playback, +}; + +/* operators for capture PCM alsa interface */ +static struct snd_pcm_ops snd_aw2_capture_ops = { + .open = snd_aw2_pcm_capture_open, + .close = snd_aw2_pcm_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_aw2_pcm_hw_params, + .hw_free = snd_aw2_pcm_hw_free, + .prepare = snd_aw2_pcm_prepare_capture, + .trigger = snd_aw2_pcm_trigger_capture, + .pointer = snd_aw2_pcm_pointer_capture, +}; + +static struct snd_kcontrol_new aw2_control __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Capture Route", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .private_value = 0xffff, + .info = snd_aw2_control_switch_capture_info, + .get = snd_aw2_control_switch_capture_get, + .put = snd_aw2_control_switch_capture_put +}; + +/********************************* + * FUNCTION IMPLEMENTATIONS + ********************************/ + +/* initialization of the module */ +static int __init alsa_card_aw2_init(void) +{ + snd_printdd(KERN_DEBUG "aw2: Load aw2 module\n"); + return pci_register_driver(&driver); +} + +/* clean up the module */ +static void __exit alsa_card_aw2_exit(void) +{ + snd_printdd(KERN_DEBUG "aw2: Unload aw2 module\n"); + pci_unregister_driver(&driver); +} + +module_init(alsa_card_aw2_init); +module_exit(alsa_card_aw2_exit); + +/* component-destructor */ +static int snd_aw2_dev_free(struct snd_device *device) +{ + struct aw2 *chip = device->device_data; + + /* Free hardware */ + snd_aw2_saa7146_free(&chip->saa7146); + + /* release the irq */ + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + /* release the i/o ports & memory */ + if (chip->iobase_virt) + iounmap(chip->iobase_virt); + + pci_release_regions(chip->pci); + /* disable the PCI entry */ + pci_disable_device(chip->pci); + /* release the data */ + kfree(chip); + + return 0; +} + +/* chip-specific constructor */ +static int __devinit snd_aw2_create(struct snd_card *card, + struct pci_dev *pci, struct aw2 **rchip) +{ + struct aw2 *chip; + int err; + static struct snd_device_ops ops = { + .dev_free = snd_aw2_dev_free, + }; + + *rchip = NULL; + + /* initialize the PCI entry */ + err = pci_enable_device(pci); + if (err < 0) + return err; + pci_set_master(pci); + + /* check PCI availability (32bit DMA) */ + if ((pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0) || + (pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK) < 0)) { + printk(KERN_ERR "aw2: Impossible to set 32bit mask DMA\n"); + pci_disable_device(pci); + return -ENXIO; + } + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (chip == NULL) { + pci_disable_device(pci); + return -ENOMEM; + } + + /* initialize the stuff */ + chip->card = card; + chip->pci = pci; + chip->irq = -1; + + /* (1) PCI resource allocation */ + err = pci_request_regions(pci, "Audiowerk2"); + if (err < 0) { + pci_disable_device(pci); + kfree(chip); + return err; + } + chip->iobase_phys = pci_resource_start(pci, 0); + chip->iobase_virt = + ioremap_nocache(chip->iobase_phys, + pci_resource_len(pci, 0)); + + if (chip->iobase_virt == NULL) { + printk(KERN_ERR "aw2: unable to remap memory region"); + pci_release_regions(pci); + pci_disable_device(pci); + kfree(chip); + return -ENOMEM; + } + + + if (request_irq(pci->irq, snd_aw2_saa7146_interrupt, + IRQF_SHARED, "Audiowerk2", chip)) { + printk(KERN_ERR "aw2: Cannot grab irq %d\n", pci->irq); + + iounmap(chip->iobase_virt); + pci_release_regions(chip->pci); + pci_disable_device(chip->pci); + kfree(chip); + return -EBUSY; + } + chip->irq = pci->irq; + + /* (2) initialization of the chip hardware */ + snd_aw2_saa7146_setup(&chip->saa7146, chip->iobase_virt); + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); + if (err < 0) { + free_irq(chip->irq, (void *)chip); + iounmap(chip->iobase_virt); + pci_release_regions(chip->pci); + pci_disable_device(chip->pci); + kfree(chip); + return err; + } + + snd_card_set_dev(card, &pci->dev); + *rchip = chip; + + printk(KERN_INFO + "Audiowerk 2 sound card (saa7146 chipset) detected and " + "managed\n"); + return 0; +} + +/* constructor */ +static int __devinit snd_aw2_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + struct snd_card *card; + struct aw2 *chip; + int err; + + /* (1) Continue if device is not enabled, else inc dev */ + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + /* (2) Create card instance */ + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + /* (3) Create main component */ + err = snd_aw2_create(card, pci, &chip); + if (err < 0) { + snd_card_free(card); + return err; + } + + /* initialize mutex */ + mutex_init(&chip->mtx); + /* init spinlock */ + spin_lock_init(&chip->reg_lock); + /* (4) Define driver ID and name string */ + strcpy(card->driver, "aw2"); + strcpy(card->shortname, "Audiowerk2"); + + sprintf(card->longname, "%s with SAA7146 irq %i", + card->shortname, chip->irq); + + /* (5) Create other components */ + snd_aw2_new_pcm(chip); + + /* (6) Register card instance */ + err = snd_card_register(card); + if (err < 0) { + snd_card_free(card); + return err; + } + + /* (7) Set PCI driver data */ + pci_set_drvdata(pci, card); + + dev++; + return 0; +} + +/* destructor */ +static void __devexit snd_aw2_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +/* open callback */ +static int snd_aw2_pcm_playback_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + snd_printdd(KERN_DEBUG "aw2: Playback_open \n"); + runtime->hw = snd_aw2_playback_hw; + return 0; +} + +/* close callback */ +static int snd_aw2_pcm_playback_close(struct snd_pcm_substream *substream) +{ + return 0; + +} + +static int snd_aw2_pcm_capture_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + snd_printdd(KERN_DEBUG "aw2: Capture_open \n"); + runtime->hw = snd_aw2_capture_hw; + return 0; +} + +/* close callback */ +static int snd_aw2_pcm_capture_close(struct snd_pcm_substream *substream) +{ + /* TODO: something to do ? */ + return 0; +} + + /* hw_params callback */ +static int snd_aw2_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); +} + +/* hw_free callback */ +static int snd_aw2_pcm_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +/* prepare callback for playback */ +static int snd_aw2_pcm_prepare_playback(struct snd_pcm_substream *substream) +{ + struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream); + struct aw2 *chip = pcm_device->chip; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned long period_size, buffer_size; + + mutex_lock(&chip->mtx); + + period_size = snd_pcm_lib_period_bytes(substream); + buffer_size = snd_pcm_lib_buffer_bytes(substream); + + snd_aw2_saa7146_pcm_init_playback(&chip->saa7146, + pcm_device->stream_number, + runtime->dma_addr, period_size, + buffer_size); + + /* Define Interrupt callback */ + snd_aw2_saa7146_define_it_playback_callback(pcm_device->stream_number, + (snd_aw2_saa7146_it_cb) + snd_pcm_period_elapsed, + (void *)substream); + + mutex_unlock(&chip->mtx); + + return 0; +} + +/* prepare callback for capture */ +static int snd_aw2_pcm_prepare_capture(struct snd_pcm_substream *substream) +{ + struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream); + struct aw2 *chip = pcm_device->chip; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned long period_size, buffer_size; + + mutex_lock(&chip->mtx); + + period_size = snd_pcm_lib_period_bytes(substream); + buffer_size = snd_pcm_lib_buffer_bytes(substream); + + snd_aw2_saa7146_pcm_init_capture(&chip->saa7146, + pcm_device->stream_number, + runtime->dma_addr, period_size, + buffer_size); + + /* Define Interrupt callback */ + snd_aw2_saa7146_define_it_capture_callback(pcm_device->stream_number, + (snd_aw2_saa7146_it_cb) + snd_pcm_period_elapsed, + (void *)substream); + + mutex_unlock(&chip->mtx); + + return 0; +} + +/* playback trigger callback */ +static int snd_aw2_pcm_trigger_playback(struct snd_pcm_substream *substream, + int cmd) +{ + int status = 0; + struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream); + struct aw2 *chip = pcm_device->chip; + spin_lock(&chip->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + snd_aw2_saa7146_pcm_trigger_start_playback(&chip->saa7146, + pcm_device-> + stream_number); + break; + case SNDRV_PCM_TRIGGER_STOP: + snd_aw2_saa7146_pcm_trigger_stop_playback(&chip->saa7146, + pcm_device-> + stream_number); + break; + default: + status = -EINVAL; + } + spin_unlock(&chip->reg_lock); + return status; +} + +/* capture trigger callback */ +static int snd_aw2_pcm_trigger_capture(struct snd_pcm_substream *substream, + int cmd) +{ + int status = 0; + struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream); + struct aw2 *chip = pcm_device->chip; + spin_lock(&chip->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + snd_aw2_saa7146_pcm_trigger_start_capture(&chip->saa7146, + pcm_device-> + stream_number); + break; + case SNDRV_PCM_TRIGGER_STOP: + snd_aw2_saa7146_pcm_trigger_stop_capture(&chip->saa7146, + pcm_device-> + stream_number); + break; + default: + status = -EINVAL; + } + spin_unlock(&chip->reg_lock); + return status; +} + +/* playback pointer callback */ +static snd_pcm_uframes_t snd_aw2_pcm_pointer_playback(struct snd_pcm_substream + *substream) +{ + struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream); + struct aw2 *chip = pcm_device->chip; + unsigned int current_ptr; + + /* get the current hardware pointer */ + struct snd_pcm_runtime *runtime = substream->runtime; + current_ptr = + snd_aw2_saa7146_get_hw_ptr_playback(&chip->saa7146, + pcm_device->stream_number, + runtime->dma_area, + runtime->buffer_size); + + return bytes_to_frames(substream->runtime, current_ptr); +} + +/* capture pointer callback */ +static snd_pcm_uframes_t snd_aw2_pcm_pointer_capture(struct snd_pcm_substream + *substream) +{ + struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream); + struct aw2 *chip = pcm_device->chip; + unsigned int current_ptr; + + /* get the current hardware pointer */ + struct snd_pcm_runtime *runtime = substream->runtime; + current_ptr = + snd_aw2_saa7146_get_hw_ptr_capture(&chip->saa7146, + pcm_device->stream_number, + runtime->dma_area, + runtime->buffer_size); + + return bytes_to_frames(substream->runtime, current_ptr); +} + +/* create a pcm device */ +static int __devinit snd_aw2_new_pcm(struct aw2 *chip) +{ + struct snd_pcm *pcm_playback_ana; + struct snd_pcm *pcm_playback_num; + struct snd_pcm *pcm_capture; + struct aw2_pcm_device *pcm_device; + int err = 0; + + /* Create new Alsa PCM device */ + + err = snd_pcm_new(chip->card, "Audiowerk2 analog playback", 0, 1, 0, + &pcm_playback_ana); + if (err < 0) { + printk(KERN_ERR "aw2: snd_pcm_new error (0x%X)\n", err); + return err; + } + + /* Creation ok */ + pcm_device = &chip->device_playback[NUM_STREAM_PLAYBACK_ANA]; + + /* Set PCM device name */ + strcpy(pcm_playback_ana->name, "Analog playback"); + /* Associate private data to PCM device */ + pcm_playback_ana->private_data = pcm_device; + /* set operators of PCM device */ + snd_pcm_set_ops(pcm_playback_ana, SNDRV_PCM_STREAM_PLAYBACK, + &snd_aw2_playback_ops); + /* store PCM device */ + pcm_device->pcm = pcm_playback_ana; + /* give base chip pointer to our internal pcm device + structure */ + pcm_device->chip = chip; + /* Give stream number to PCM device */ + pcm_device->stream_number = NUM_STREAM_PLAYBACK_ANA; + + /* pre-allocation of buffers */ + /* Preallocate continuous pages. */ + err = snd_pcm_lib_preallocate_pages_for_all(pcm_playback_ana, + SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data + (chip->pci), + 64 * 1024, 64 * 1024); + if (err) + printk(KERN_ERR "aw2: snd_pcm_lib_preallocate_pages_for_all " + "error (0x%X)\n", err); + + err = snd_pcm_new(chip->card, "Audiowerk2 digital playback", 1, 1, 0, + &pcm_playback_num); + + if (err < 0) { + printk(KERN_ERR "aw2: snd_pcm_new error (0x%X)\n", err); + return err; + } + /* Creation ok */ + pcm_device = &chip->device_playback[NUM_STREAM_PLAYBACK_DIG]; + + /* Set PCM device name */ + strcpy(pcm_playback_num->name, "Digital playback"); + /* Associate private data to PCM device */ + pcm_playback_num->private_data = pcm_device; + /* set operators of PCM device */ + snd_pcm_set_ops(pcm_playback_num, SNDRV_PCM_STREAM_PLAYBACK, + &snd_aw2_playback_ops); + /* store PCM device */ + pcm_device->pcm = pcm_playback_num; + /* give base chip pointer to our internal pcm device + structure */ + pcm_device->chip = chip; + /* Give stream number to PCM device */ + pcm_device->stream_number = NUM_STREAM_PLAYBACK_DIG; + + /* pre-allocation of buffers */ + /* Preallocate continuous pages. */ + err = snd_pcm_lib_preallocate_pages_for_all(pcm_playback_num, + SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data + (chip->pci), + 64 * 1024, 64 * 1024); + if (err) + printk(KERN_ERR + "aw2: snd_pcm_lib_preallocate_pages_for_all error " + "(0x%X)\n", err); + + + + err = snd_pcm_new(chip->card, "Audiowerk2 capture", 2, 0, 1, + &pcm_capture); + + if (err < 0) { + printk(KERN_ERR "aw2: snd_pcm_new error (0x%X)\n", err); + return err; + } + + /* Creation ok */ + pcm_device = &chip->device_capture[NUM_STREAM_CAPTURE_ANA]; + + /* Set PCM device name */ + strcpy(pcm_capture->name, "Capture"); + /* Associate private data to PCM device */ + pcm_capture->private_data = pcm_device; + /* set operators of PCM device */ + snd_pcm_set_ops(pcm_capture, SNDRV_PCM_STREAM_CAPTURE, + &snd_aw2_capture_ops); + /* store PCM device */ + pcm_device->pcm = pcm_capture; + /* give base chip pointer to our internal pcm device + structure */ + pcm_device->chip = chip; + /* Give stream number to PCM device */ + pcm_device->stream_number = NUM_STREAM_CAPTURE_ANA; + + /* pre-allocation of buffers */ + /* Preallocate continuous pages. */ + err = snd_pcm_lib_preallocate_pages_for_all(pcm_capture, + SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data + (chip->pci), + 64 * 1024, 64 * 1024); + if (err) + printk(KERN_ERR + "aw2: snd_pcm_lib_preallocate_pages_for_all error " + "(0x%X)\n", err); + + + /* Create control */ + err = snd_ctl_add(chip->card, snd_ctl_new1(&aw2_control, chip)); + if (err < 0) { + printk(KERN_ERR "aw2: snd_ctl_add error (0x%X)\n", err); + return err; + } + + return 0; +} + +static int snd_aw2_control_switch_capture_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[2] = { + "Analog", "Digital" + }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) { + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + } + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_aw2_control_switch_capture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value + *ucontrol) +{ + struct aw2 *chip = snd_kcontrol_chip(kcontrol); + if (snd_aw2_saa7146_is_using_digital_input(&chip->saa7146)) + ucontrol->value.enumerated.item[0] = CTL_ROUTE_DIGITAL; + else + ucontrol->value.enumerated.item[0] = CTL_ROUTE_ANALOG; + return 0; +} + +static int snd_aw2_control_switch_capture_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value + *ucontrol) +{ + struct aw2 *chip = snd_kcontrol_chip(kcontrol); + int changed = 0; + int is_disgital = + snd_aw2_saa7146_is_using_digital_input(&chip->saa7146); + + if (((ucontrol->value.integer.value[0] == CTL_ROUTE_DIGITAL) + && !is_disgital) + || ((ucontrol->value.integer.value[0] == CTL_ROUTE_ANALOG) + && is_disgital)) { + snd_aw2_saa7146_use_digital_input(&chip->saa7146, !is_disgital); + changed = 1; + } + return changed; +} diff --git a/sound/pci/aw2/aw2-saa7146.c b/sound/pci/aw2/aw2-saa7146.c new file mode 100644 index 0000000..6a3891a --- /dev/null +++ b/sound/pci/aw2/aw2-saa7146.c @@ -0,0 +1,465 @@ +/***************************************************************************** + * + * Copyright (C) 2008 Cedric Bregardis and + * Jean-Christian Hassler + * + * This file is part of the Audiowerk2 ALSA driver + * + * The Audiowerk2 ALSA driver is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2. + * + * The Audiowerk2 ALSA driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Audiowerk2 ALSA driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + *****************************************************************************/ + +#define AW2_SAA7146_M + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "saa7146.h" +#include "aw2-saa7146.h" + +#include "aw2-tsl.c" + +#define WRITEREG(value, addr) writel((value), chip->base_addr + (addr)) +#define READREG(addr) readl(chip->base_addr + (addr)) + +static struct snd_aw2_saa7146_cb_param + arr_substream_it_playback_cb[NB_STREAM_PLAYBACK]; +static struct snd_aw2_saa7146_cb_param + arr_substream_it_capture_cb[NB_STREAM_CAPTURE]; + +static int snd_aw2_saa7146_get_limit(int size); + +/* chip-specific destructor */ +int snd_aw2_saa7146_free(struct snd_aw2_saa7146 *chip) +{ + /* disable all irqs */ + WRITEREG(0, IER); + + /* reset saa7146 */ + WRITEREG((MRST_N << 16), MC1); + + /* Unset base addr */ + chip->base_addr = NULL; + + return 0; +} + +void snd_aw2_saa7146_setup(struct snd_aw2_saa7146 *chip, + void __iomem *pci_base_addr) +{ + /* set PCI burst/threshold + + Burst length definition + VALUE BURST LENGTH + 000 1 Dword + 001 2 Dwords + 010 4 Dwords + 011 8 Dwords + 100 16 Dwords + 101 32 Dwords + 110 64 Dwords + 111 128 Dwords + + Threshold definition + VALUE WRITE MODE READ MODE + 00 1 Dword of valid data 1 empty Dword + 01 4 Dwords of valid data 4 empty Dwords + 10 8 Dwords of valid data 8 empty Dwords + 11 16 Dwords of valid data 16 empty Dwords */ + + unsigned int acon2; + unsigned int acon1 = 0; + int i; + + /* Set base addr */ + chip->base_addr = pci_base_addr; + + /* disable all irqs */ + WRITEREG(0, IER); + + /* reset saa7146 */ + WRITEREG((MRST_N << 16), MC1); + + /* enable audio interface */ +#ifdef __BIG_ENDIAN + acon1 |= A1_SWAP; + acon1 |= A2_SWAP; +#endif + /* WS0_CTRL, WS0_SYNC: input TSL1, I2S */ + + /* At initialization WS1 and WS2 are disbaled (configured as input */ + acon1 |= 0 * WS1_CTRL; + acon1 |= 0 * WS2_CTRL; + + /* WS4 is not used. So it must not restart A2. + This is why it is configured as output (force to low) */ + acon1 |= 3 * WS4_CTRL; + + /* WS3_CTRL, WS3_SYNC: output TSL2, I2S */ + acon1 |= 2 * WS3_CTRL; + + /* A1 and A2 are active and asynchronous */ + acon1 |= 3 * AUDIO_MODE; + WRITEREG(acon1, ACON1); + + /* The following comes from original windows driver. + It is needed to have a correct behavior of input and output + simultenously, but I don't know why ! */ + WRITEREG(3 * (BurstA1_in) + 3 * (ThreshA1_in) + + 3 * (BurstA1_out) + 3 * (ThreshA1_out) + + 3 * (BurstA2_out) + 3 * (ThreshA2_out), PCI_BT_A); + + /* enable audio port pins */ + WRITEREG((EAP << 16) | EAP, MC1); + + /* enable I2C */ + WRITEREG((EI2C << 16) | EI2C, MC1); + /* enable interrupts */ + WRITEREG(A1_out | A2_out | A1_in | IIC_S | IIC_E, IER); + + /* audio configuration */ + acon2 = A2_CLKSRC | BCLK1_OEN; + WRITEREG(acon2, ACON2); + + /* By default use analog input */ + snd_aw2_saa7146_use_digital_input(chip, 0); + + /* TSL setup */ + for (i = 0; i < 8; ++i) { + WRITEREG(tsl1[i], TSL1 + (i * 4)); + WRITEREG(tsl2[i], TSL2 + (i * 4)); + } + +} + +void snd_aw2_saa7146_pcm_init_playback(struct snd_aw2_saa7146 *chip, + int stream_number, + unsigned long dma_addr, + unsigned long period_size, + unsigned long buffer_size) +{ + unsigned long dw_page, dw_limit; + + /* Configure DMA for substream + Configuration informations: ALSA has allocated continuous memory + pages. So we don't need to use MMU of saa7146. + */ + + /* No MMU -> nothing to do with PageA1, we only configure the limit of + PageAx_out register */ + /* Disable MMU */ + dw_page = (0L << 11); + + /* Configure Limit for DMA access. + The limit register defines an address limit, which generates + an interrupt if passed by the actual PCI address pointer. + '0001' means an interrupt will be generated if the lower + 6 bits (64 bytes) of the PCI address are zero. '0010' + defines a limit of 128 bytes, '0011' one of 256 bytes, and + so on up to 1 Mbyte defined by '1111'. This interrupt range + can be calculated as follows: + Range = 2^(5 + Limit) bytes. + */ + dw_limit = snd_aw2_saa7146_get_limit(period_size); + dw_page |= (dw_limit << 4); + + if (stream_number == 0) { + WRITEREG(dw_page, PageA2_out); + + /* Base address for DMA transfert. */ + /* This address has been reserved by ALSA. */ + /* This is a physical address */ + WRITEREG(dma_addr, BaseA2_out); + + /* Define upper limit for DMA access */ + WRITEREG(dma_addr + buffer_size, ProtA2_out); + + } else if (stream_number == 1) { + WRITEREG(dw_page, PageA1_out); + + /* Base address for DMA transfert. */ + /* This address has been reserved by ALSA. */ + /* This is a physical address */ + WRITEREG(dma_addr, BaseA1_out); + + /* Define upper limit for DMA access */ + WRITEREG(dma_addr + buffer_size, ProtA1_out); + } else { + printk(KERN_ERR + "aw2: snd_aw2_saa7146_pcm_init_playback: " + "Substream number is not 0 or 1 -> not managed\n"); + } +} + +void snd_aw2_saa7146_pcm_init_capture(struct snd_aw2_saa7146 *chip, + int stream_number, unsigned long dma_addr, + unsigned long period_size, + unsigned long buffer_size) +{ + unsigned long dw_page, dw_limit; + + /* Configure DMA for substream + Configuration informations: ALSA has allocated continuous memory + pages. So we don't need to use MMU of saa7146. + */ + + /* No MMU -> nothing to do with PageA1, we only configure the limit of + PageAx_out register */ + /* Disable MMU */ + dw_page = (0L << 11); + + /* Configure Limit for DMA access. + The limit register defines an address limit, which generates + an interrupt if passed by the actual PCI address pointer. + '0001' means an interrupt will be generated if the lower + 6 bits (64 bytes) of the PCI address are zero. '0010' + defines a limit of 128 bytes, '0011' one of 256 bytes, and + so on up to 1 Mbyte defined by '1111'. This interrupt range + can be calculated as follows: + Range = 2^(5 + Limit) bytes. + */ + dw_limit = snd_aw2_saa7146_get_limit(period_size); + dw_page |= (dw_limit << 4); + + if (stream_number == 0) { + WRITEREG(dw_page, PageA1_in); + + /* Base address for DMA transfert. */ + /* This address has been reserved by ALSA. */ + /* This is a physical address */ + WRITEREG(dma_addr, BaseA1_in); + + /* Define upper limit for DMA access */ + WRITEREG(dma_addr + buffer_size, ProtA1_in); + } else { + printk(KERN_ERR + "aw2: snd_aw2_saa7146_pcm_init_capture: " + "Substream number is not 0 -> not managed\n"); + } +} + +void snd_aw2_saa7146_define_it_playback_callback(unsigned int stream_number, + snd_aw2_saa7146_it_cb + p_it_callback, + void *p_callback_param) +{ + if (stream_number < NB_STREAM_PLAYBACK) { + arr_substream_it_playback_cb[stream_number].p_it_callback = + (snd_aw2_saa7146_it_cb) p_it_callback; + arr_substream_it_playback_cb[stream_number].p_callback_param = + (void *)p_callback_param; + } +} + +void snd_aw2_saa7146_define_it_capture_callback(unsigned int stream_number, + snd_aw2_saa7146_it_cb + p_it_callback, + void *p_callback_param) +{ + if (stream_number < NB_STREAM_CAPTURE) { + arr_substream_it_capture_cb[stream_number].p_it_callback = + (snd_aw2_saa7146_it_cb) p_it_callback; + arr_substream_it_capture_cb[stream_number].p_callback_param = + (void *)p_callback_param; + } +} + +void snd_aw2_saa7146_pcm_trigger_start_playback(struct snd_aw2_saa7146 *chip, + int stream_number) +{ + unsigned int acon1 = 0; + /* In aw8 driver, dma transfert is always active. It is + started and stopped in a larger "space" */ + acon1 = READREG(ACON1); + if (stream_number == 0) { + WRITEREG((TR_E_A2_OUT << 16) | TR_E_A2_OUT, MC1); + + /* WS2_CTRL, WS2_SYNC: output TSL2, I2S */ + acon1 |= 2 * WS2_CTRL; + WRITEREG(acon1, ACON1); + + } else if (stream_number == 1) { + WRITEREG((TR_E_A1_OUT << 16) | TR_E_A1_OUT, MC1); + + /* WS1_CTRL, WS1_SYNC: output TSL1, I2S */ + acon1 |= 1 * WS1_CTRL; + WRITEREG(acon1, ACON1); + } +} + +void snd_aw2_saa7146_pcm_trigger_stop_playback(struct snd_aw2_saa7146 *chip, + int stream_number) +{ + unsigned int acon1 = 0; + acon1 = READREG(ACON1); + if (stream_number == 0) { + /* WS2_CTRL, WS2_SYNC: output TSL2, I2S */ + acon1 &= ~(3 * WS2_CTRL); + WRITEREG(acon1, ACON1); + + WRITEREG((TR_E_A2_OUT << 16), MC1); + } else if (stream_number == 1) { + /* WS1_CTRL, WS1_SYNC: output TSL1, I2S */ + acon1 &= ~(3 * WS1_CTRL); + WRITEREG(acon1, ACON1); + + WRITEREG((TR_E_A1_OUT << 16), MC1); + } +} + +void snd_aw2_saa7146_pcm_trigger_start_capture(struct snd_aw2_saa7146 *chip, + int stream_number) +{ + /* In aw8 driver, dma transfert is always active. It is + started and stopped in a larger "space" */ + if (stream_number == 0) + WRITEREG((TR_E_A1_IN << 16) | TR_E_A1_IN, MC1); +} + +void snd_aw2_saa7146_pcm_trigger_stop_capture(struct snd_aw2_saa7146 *chip, + int stream_number) +{ + if (stream_number == 0) + WRITEREG((TR_E_A1_IN << 16), MC1); +} + +irqreturn_t snd_aw2_saa7146_interrupt(int irq, void *dev_id) +{ + unsigned int isr; + unsigned int iicsta; + struct snd_aw2_saa7146 *chip = dev_id; + + isr = READREG(ISR); + if (!isr) + return IRQ_NONE; + + WRITEREG(isr, ISR); + + if (isr & (IIC_S | IIC_E)) { + iicsta = READREG(IICSTA); + WRITEREG(0x100, IICSTA); + } + + if (isr & A1_out) { + if (arr_substream_it_playback_cb[1].p_it_callback != NULL) { + arr_substream_it_playback_cb[1]. + p_it_callback(arr_substream_it_playback_cb[1]. + p_callback_param); + } + } + if (isr & A2_out) { + if (arr_substream_it_playback_cb[0].p_it_callback != NULL) { + arr_substream_it_playback_cb[0]. + p_it_callback(arr_substream_it_playback_cb[0]. + p_callback_param); + } + + } + if (isr & A1_in) { + if (arr_substream_it_capture_cb[0].p_it_callback != NULL) { + arr_substream_it_capture_cb[0]. + p_it_callback(arr_substream_it_capture_cb[0]. + p_callback_param); + } + } + return IRQ_HANDLED; +} + +unsigned int snd_aw2_saa7146_get_hw_ptr_playback(struct snd_aw2_saa7146 *chip, + int stream_number, + unsigned char *start_addr, + unsigned int buffer_size) +{ + long pci_adp = 0; + size_t ptr = 0; + + if (stream_number == 0) { + pci_adp = READREG(PCI_ADP3); + ptr = pci_adp - (long)start_addr; + + if (ptr == buffer_size) + ptr = 0; + } + if (stream_number == 1) { + pci_adp = READREG(PCI_ADP1); + ptr = pci_adp - (size_t) start_addr; + + if (ptr == buffer_size) + ptr = 0; + } + return ptr; +} + +unsigned int snd_aw2_saa7146_get_hw_ptr_capture(struct snd_aw2_saa7146 *chip, + int stream_number, + unsigned char *start_addr, + unsigned int buffer_size) +{ + size_t pci_adp = 0; + size_t ptr = 0; + if (stream_number == 0) { + pci_adp = READREG(PCI_ADP2); + ptr = pci_adp - (size_t) start_addr; + + if (ptr == buffer_size) + ptr = 0; + } + return ptr; +} + +void snd_aw2_saa7146_use_digital_input(struct snd_aw2_saa7146 *chip, + int use_digital) +{ + /* FIXME: switch between analog and digital input does not always work. + It can produce a kind of white noise. It seams that received data + are inverted sometime (endian inversion). Why ? I don't know, maybe + a problem of synchronization... However for the time being I have + not found the problem. Workaround: switch again (and again) between + digital and analog input until it works. */ + if (use_digital) + WRITEREG(0x40, GPIO_CTRL); + else + WRITEREG(0x50, GPIO_CTRL); +} + +int snd_aw2_saa7146_is_using_digital_input(struct snd_aw2_saa7146 *chip) +{ + unsigned int reg_val = READREG(GPIO_CTRL); + if ((reg_val & 0xFF) == 0x40) + return 1; + else + return 0; +} + + +static int snd_aw2_saa7146_get_limit(int size) +{ + int limitsize = 32; + int limit = 0; + while (limitsize < size) { + limitsize *= 2; + limit++; + } + return limit; +} diff --git a/sound/pci/aw2/aw2-saa7146.h b/sound/pci/aw2/aw2-saa7146.h new file mode 100644 index 0000000..5b35e35 --- /dev/null +++ b/sound/pci/aw2/aw2-saa7146.h @@ -0,0 +1,105 @@ +/***************************************************************************** + * + * Copyright (C) 2008 Cedric Bregardis and + * Jean-Christian Hassler + * + * This file is part of the Audiowerk2 ALSA driver + * + * The Audiowerk2 ALSA driver is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2. + * + * The Audiowerk2 ALSA driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Audiowerk2 ALSA driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + *****************************************************************************/ + +#ifndef AW2_SAA7146_H +#define AW2_SAA7146_H + +#define NB_STREAM_PLAYBACK 2 +#define NB_STREAM_CAPTURE 1 + +#define NUM_STREAM_PLAYBACK_ANA 0 +#define NUM_STREAM_PLAYBACK_DIG 1 + +#define NUM_STREAM_CAPTURE_ANA 0 + +typedef void (*snd_aw2_saa7146_it_cb) (void *); + +struct snd_aw2_saa7146_cb_param { + snd_aw2_saa7146_it_cb p_it_callback; + void *p_callback_param; +}; + +/* definition of the chip-specific record */ + +struct snd_aw2_saa7146 { + void __iomem *base_addr; +}; + +extern void snd_aw2_saa7146_setup(struct snd_aw2_saa7146 *chip, + void __iomem *pci_base_addr); +extern int snd_aw2_saa7146_free(struct snd_aw2_saa7146 *chip); + +extern void snd_aw2_saa7146_pcm_init_playback(struct snd_aw2_saa7146 *chip, + int stream_number, + unsigned long dma_addr, + unsigned long period_size, + unsigned long buffer_size); +extern void snd_aw2_saa7146_pcm_init_capture(struct snd_aw2_saa7146 *chip, + int stream_number, + unsigned long dma_addr, + unsigned long period_size, + unsigned long buffer_size); +extern void snd_aw2_saa7146_define_it_playback_callback(unsigned int + stream_number, + snd_aw2_saa7146_it_cb + p_it_callback, + void *p_callback_param); +extern void snd_aw2_saa7146_define_it_capture_callback(unsigned int + stream_number, + snd_aw2_saa7146_it_cb + p_it_callback, + void *p_callback_param); +extern void snd_aw2_saa7146_pcm_trigger_start_capture(struct snd_aw2_saa7146 + *chip, int stream_number); +extern void snd_aw2_saa7146_pcm_trigger_stop_capture(struct snd_aw2_saa7146 + *chip, int stream_number); + +extern void snd_aw2_saa7146_pcm_trigger_start_playback(struct snd_aw2_saa7146 + *chip, + int stream_number); +extern void snd_aw2_saa7146_pcm_trigger_stop_playback(struct snd_aw2_saa7146 + *chip, int stream_number); + +extern irqreturn_t snd_aw2_saa7146_interrupt(int irq, void *dev_id); +extern unsigned int snd_aw2_saa7146_get_hw_ptr_playback(struct snd_aw2_saa7146 + *chip, + int stream_number, + unsigned char + *start_addr, + unsigned int + buffer_size); +extern unsigned int snd_aw2_saa7146_get_hw_ptr_capture(struct snd_aw2_saa7146 + *chip, + int stream_number, + unsigned char + *start_addr, + unsigned int + buffer_size); + +extern void snd_aw2_saa7146_use_digital_input(struct snd_aw2_saa7146 *chip, + int use_digital); + +extern int snd_aw2_saa7146_is_using_digital_input(struct snd_aw2_saa7146 + *chip); + +#endif diff --git a/sound/pci/aw2/aw2-tsl.c b/sound/pci/aw2/aw2-tsl.c new file mode 100644 index 0000000..459b031 --- /dev/null +++ b/sound/pci/aw2/aw2-tsl.c @@ -0,0 +1,110 @@ +/***************************************************************************** + * + * Copyright (C) 2008 Cedric Bregardis and + * Jean-Christian Hassler + * Copyright 1998 Emagic Soft- und Hardware GmbH + * Copyright 2002 Martijn Sipkema + * + * This file is part of the Audiowerk2 ALSA driver + * + * The Audiowerk2 ALSA driver is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2. + * + * The Audiowerk2 ALSA driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Audiowerk2 ALSA driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + *****************************************************************************/ + +#define TSL_WS0 (1UL << 31) +#define TSL_WS1 (1UL << 30) +#define TSL_WS2 (1UL << 29) +#define TSL_WS3 (1UL << 28) +#define TSL_WS4 (1UL << 27) +#define TSL_DIS_A1 (1UL << 24) +#define TSL_SDW_A1 (1UL << 23) +#define TSL_SIB_A1 (1UL << 22) +#define TSL_SF_A1 (1UL << 21) +#define TSL_LF_A1 (1UL << 20) +#define TSL_BSEL_A1 (1UL << 17) +#define TSL_DOD_A1 (1UL << 15) +#define TSL_LOW_A1 (1UL << 14) +#define TSL_DIS_A2 (1UL << 11) +#define TSL_SDW_A2 (1UL << 10) +#define TSL_SIB_A2 (1UL << 9) +#define TSL_SF_A2 (1UL << 8) +#define TSL_LF_A2 (1UL << 7) +#define TSL_BSEL_A2 (1UL << 4) +#define TSL_DOD_A2 (1UL << 2) +#define TSL_LOW_A2 (1UL << 1) +#define TSL_EOS (1UL << 0) + + /* Audiowerk8 hardware setup: */ + /* WS0, SD4, TSL1 - Analog/ digital in */ + /* WS1, SD0, TSL1 - Analog out #1, digital out */ + /* WS2, SD2, TSL1 - Analog out #2 */ + /* WS3, SD1, TSL2 - Analog out #3 */ + /* WS4, SD3, TSL2 - Analog out #4 */ + + /* Audiowerk8 timing: */ + /* Timeslot: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | ... */ + + /* A1_INPUT: */ + /* SD4: <_ADC-L_>-------<_ADC-R_>-------< */ + /* WS0: _______________/---------------\_ */ + + /* A1_OUTPUT: */ + /* SD0: <_1-L___>-------<_1-R___>-------< */ + /* WS1: _______________/---------------\_ */ + /* SD2: >-------<_2-L___>-------<_2-R___> */ + /* WS2: -------\_______________/--------- */ + + /* A2_OUTPUT: */ + /* SD1: <_3-L___>-------<_3-R___>-------< */ + /* WS3: _______________/---------------\_ */ + /* SD3: >-------<_4-L___>-------<_4-R___> */ + /* WS4: -------\_______________/--------- */ + +static int tsl1[8] = { + 1 * TSL_SDW_A1 | 3 * TSL_BSEL_A1 | + 0 * TSL_DIS_A1 | 0 * TSL_DOD_A1 | TSL_LF_A1, + + 1 * TSL_SDW_A1 | 2 * TSL_BSEL_A1 | + 0 * TSL_DIS_A1 | 0 * TSL_DOD_A1, + + 0 * TSL_SDW_A1 | 3 * TSL_BSEL_A1 | + 0 * TSL_DIS_A1 | 0 * TSL_DOD_A1, + + 0 * TSL_SDW_A1 | 2 * TSL_BSEL_A1 | + 0 * TSL_DIS_A1 | 0 * TSL_DOD_A1, + + 1 * TSL_SDW_A1 | 1 * TSL_BSEL_A1 | + 0 * TSL_DIS_A1 | 0 * TSL_DOD_A1 | TSL_WS1 | TSL_WS0, + + 1 * TSL_SDW_A1 | 0 * TSL_BSEL_A1 | + 0 * TSL_DIS_A1 | 0 * TSL_DOD_A1 | TSL_WS1 | TSL_WS0, + + 0 * TSL_SDW_A1 | 1 * TSL_BSEL_A1 | + 0 * TSL_DIS_A1 | 0 * TSL_DOD_A1 | TSL_WS1 | TSL_WS0, + + 0 * TSL_SDW_A1 | 0 * TSL_BSEL_A1 | 0 * TSL_DIS_A1 | + 0 * TSL_DOD_A1 | TSL_WS1 | TSL_WS0 | TSL_SF_A1 | TSL_EOS, +}; + +static int tsl2[8] = { + 0 * TSL_SDW_A2 | 3 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_LF_A2, + 0 * TSL_SDW_A2 | 2 * TSL_BSEL_A2 | 2 * TSL_DOD_A2, + 0 * TSL_SDW_A2 | 3 * TSL_BSEL_A2 | 2 * TSL_DOD_A2, + 0 * TSL_SDW_A2 | 2 * TSL_BSEL_A2 | 2 * TSL_DOD_A2, + 0 * TSL_SDW_A2 | 1 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_WS2, + 0 * TSL_SDW_A2 | 0 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_WS2, + 0 * TSL_SDW_A2 | 1 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_WS2, + 0 * TSL_SDW_A2 | 0 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_WS2 | TSL_EOS +}; diff --git a/sound/pci/aw2/saa7146.h b/sound/pci/aw2/saa7146.h new file mode 100644 index 0000000..ce0ab5f --- /dev/null +++ b/sound/pci/aw2/saa7146.h @@ -0,0 +1,168 @@ +/***************************************************************************** + * + * Copyright (C) 2008 Cedric Bregardis and + * Jean-Christian Hassler + * + * This file is part of the Audiowerk2 ALSA driver + * + * The Audiowerk2 ALSA driver is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2. + * + * The Audiowerk2 ALSA driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Audiowerk2 ALSA driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + *****************************************************************************/ + +/* SAA7146 registers */ +#define PCI_BT_A 0x4C +#define IICTFR 0x8C +#define IICSTA 0x90 +#define BaseA1_in 0x94 +#define ProtA1_in 0x98 +#define PageA1_in 0x9C +#define BaseA1_out 0xA0 +#define ProtA1_out 0xA4 +#define PageA1_out 0xA8 +#define BaseA2_in 0xAC +#define ProtA2_in 0xB0 +#define PageA2_in 0xB4 +#define BaseA2_out 0xB8 +#define ProtA2_out 0xBC +#define PageA2_out 0xC0 +#define IER 0xDC +#define GPIO_CTRL 0xE0 +#define ACON1 0xF4 +#define ACON2 0xF8 +#define MC1 0xFC +#define MC2 0x100 +#define ISR 0x10C +#define PSR 0x110 +#define SSR 0x114 +#define PCI_ADP1 0x12C +#define PCI_ADP2 0x130 +#define PCI_ADP3 0x134 +#define PCI_ADP4 0x138 +#define LEVEL_REP 0x140 +#define FB_BUFFER1 0x144 +#define FB_BUFFER2 0x148 +#define TSL1 0x180 +#define TSL2 0x1C0 + +#define ME (1UL << 11) +#define LIMIT (1UL << 4) +#define PV (1UL << 3) + +/* PSR/ISR/IER */ +#define PPEF (1UL << 31) +#define PABO (1UL << 30) +#define IIC_S (1UL << 17) +#define IIC_E (1UL << 16) +#define A2_in (1UL << 15) +#define A2_out (1UL << 14) +#define A1_in (1UL << 13) +#define A1_out (1UL << 12) +#define AFOU (1UL << 11) +#define PIN3 (1UL << 6) +#define PIN2 (1UL << 5) +#define PIN1 (1UL << 4) +#define PIN0 (1UL << 3) +#define ECS (1UL << 2) +#define EC3S (1UL << 1) +#define EC0S (1UL << 0) + +/* SSR */ +#define PRQ (1UL << 31) +#define PMA (1UL << 30) +#define IIC_EA (1UL << 21) +#define IIC_EW (1UL << 20) +#define IIC_ER (1UL << 19) +#define IIC_EL (1UL << 18) +#define IIC_EF (1UL << 17) +#define AF2_in (1UL << 10) +#define AF2_out (1UL << 9) +#define AF1_in (1UL << 8) +#define AF1_out (1UL << 7) +#define EC5S (1UL << 3) +#define EC4S (1UL << 2) +#define EC2S (1UL << 1) +#define EC1S (1UL << 0) + +/* PCI_BT_A */ +#define BurstA1_in (1UL << 26) +#define ThreshA1_in (1UL << 24) +#define BurstA1_out (1UL << 18) +#define ThreshA1_out (1UL << 16) +#define BurstA2_in (1UL << 10) +#define ThreshA2_in (1UL << 8) +#define BurstA2_out (1UL << 2) +#define ThreshA2_out (1UL << 0) + +/* MC1 */ +#define MRST_N (1UL << 15) +#define EAP (1UL << 9) +#define EI2C (1UL << 8) +#define TR_E_A2_OUT (1UL << 3) +#define TR_E_A2_IN (1UL << 2) +#define TR_E_A1_OUT (1UL << 1) +#define TR_E_A1_IN (1UL << 0) + +/* MC2 */ +#define UPLD_IIC (1UL << 0) + +/* ACON1 */ +#define AUDIO_MODE (1UL << 29) +#define MAXLEVEL (1UL << 22) +#define A1_SWAP (1UL << 21) +#define A2_SWAP (1UL << 20) +#define WS0_CTRL (1UL << 18) +#define WS0_SYNC (1UL << 16) +#define WS1_CTRL (1UL << 14) +#define WS1_SYNC (1UL << 12) +#define WS2_CTRL (1UL << 10) +#define WS2_SYNC (1UL << 8) +#define WS3_CTRL (1UL << 6) +#define WS3_SYNC (1UL << 4) +#define WS4_CTRL (1UL << 2) +#define WS4_SYNC (1UL << 0) + +/* ACON2 */ +#define A1_CLKSRC (1UL << 27) +#define A2_CLKSRC (1UL << 22) +#define INVERT_BCLK1 (1UL << 21) +#define INVERT_BCLK2 (1UL << 20) +#define BCLK1_OEN (1UL << 19) +#define BCLK2_OEN (1UL << 18) + +/* IICSTA */ +#define IICCC (1UL << 8) +#define ABORT (1UL << 7) +#define SPERR (1UL << 6) +#define APERR (1UL << 5) +#define DTERR (1UL << 4) +#define DRERR (1UL << 3) +#define AL (1UL << 2) +#define ERR (1UL << 1) +#define BUSY (1UL << 0) + +/* IICTFR */ +#define BYTE2 (1UL << 24) +#define BYTE1 (1UL << 16) +#define BYTE0 (1UL << 8) +#define ATRR2 (1UL << 6) +#define ATRR1 (1UL << 4) +#define ATRR0 (1UL << 2) +#define ERR (1UL << 1) +#define BUSY (1UL << 0) + +#define START 3 +#define CONT 2 +#define STOP 1 +#define NOP 0 diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 4e71a55..be87d31 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -157,8 +157,8 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}"); #if DEBUG_CALLS #define snd_azf3328_dbgcalls(format, args...) printk(format, ##args) -#define snd_azf3328_dbgcallenter() printk(KERN_ERR "--> %s\n", __FUNCTION__) -#define snd_azf3328_dbgcallleave() printk(KERN_ERR "<-- %s\n", __FUNCTION__) +#define snd_azf3328_dbgcallenter() printk(KERN_ERR "--> %s\n", __func__) +#define snd_azf3328_dbgcallleave() printk(KERN_ERR "<-- %s\n", __func__) #else #define snd_azf3328_dbgcalls(format, args...) #define snd_azf3328_dbgcallenter() diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index 176e0f0..3818249 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -435,22 +435,22 @@ int snd_ca0106_i2c_write(struct snd_ca0106 *emu, static void snd_ca0106_intr_enable(struct snd_ca0106 *emu, unsigned int intrenb) { unsigned long flags; - unsigned int enable; - + unsigned int intr_enable; + spin_lock_irqsave(&emu->emu_lock, flags); - enable = inl(emu->port + INTE) | intrenb; - outl(enable, emu->port + INTE); + intr_enable = inl(emu->port + INTE) | intrenb; + outl(intr_enable, emu->port + INTE); spin_unlock_irqrestore(&emu->emu_lock, flags); } static void snd_ca0106_intr_disable(struct snd_ca0106 *emu, unsigned int intrenb) { unsigned long flags; - unsigned int enable; - + unsigned int intr_enable; + spin_lock_irqsave(&emu->emu_lock, flags); - enable = inl(emu->port + INTE) & ~intrenb; - outl(enable, emu->port + INTE); + intr_enable = inl(emu->port + INTE) & ~intrenb; + outl(intr_enable, emu->port + INTE); spin_unlock_irqrestore(&emu->emu_lock, flags); } diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index af73686..3025ed1 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -650,19 +650,55 @@ static int __devinit rename_ctl(struct snd_card *card, const char *src, const ch #define ADD_CTLS(emu, ctls) \ do { \ - int i, err; \ + int i, _err; \ for (i = 0; i < ARRAY_SIZE(ctls); i++) { \ - err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \ - if (err < 0) \ - return err; \ + _err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \ + if (_err < 0) \ + return _err; \ } \ } while (0) +static __devinitdata +DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 50, 1); + +static char *slave_vols[] __devinitdata = { + "Analog Front Playback Volume", + "Analog Rear Playback Volume", + "Analog Center/LFE Playback Volume", + "Analog Side Playback Volume", + "IEC958 Front Playback Volume", + "IEC958 Rear Playback Volume", + "IEC958 Center/LFE Playback Volume", + "IEC958 Unknown Playback Volume", + "CAPTURE feedback Playback Volume", + NULL +}; + +static char *slave_sws[] __devinitdata = { + "Analog Front Playback Switch", + "Analog Rear Playback Switch", + "Analog Center/LFE Playback Switch", + "Analog Side Playback Switch", + "IEC958 Playback Switch", + NULL +}; + +static void __devinit add_slaves(struct snd_card *card, + struct snd_kcontrol *master, char **list) +{ + for (; *list; list++) { + struct snd_kcontrol *slave = ctl_find(card, *list); + if (slave) + snd_ctl_add_slave(master, slave); + } +} + int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu) { int err; struct snd_card *card = emu->card; char **c; + struct snd_kcontrol *vmaster; static char *ca0106_remove_ctls[] = { "Master Mono Playback Switch", "Master Mono Playback Volume", @@ -719,6 +755,21 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu) } if (emu->details->spi_dac == 1) ADD_CTLS(emu, snd_ca0106_volume_spi_dac_ctls); + + /* Create virtual master controls */ + vmaster = snd_ctl_make_virtual_master("Master Playback Volume", + snd_ca0106_master_db_scale); + if (!vmaster) + return -ENOMEM; + add_slaves(card, vmaster, slave_vols); + + if (emu->details->spi_dac == 1) { + vmaster = snd_ctl_make_virtual_master("Master Playback Switch", + NULL); + if (!vmaster) + return -ENOMEM; + add_slaves(card, vmaster, slave_sws); + } return 0; } diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 135f308..4074584 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -2744,12 +2744,13 @@ static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_devic } for (idx = 0; idx < CM_SAVED_MIXERS; idx++) { - struct snd_ctl_elem_id id; + struct snd_ctl_elem_id elem_id; struct snd_kcontrol *ctl; - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(id.name, cm_saved_mixer[idx].name); - if ((ctl = snd_ctl_find_id(cm->card, &id)) != NULL) + memset(&elem_id, 0, sizeof(elem_id)); + elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(elem_id.name, cm_saved_mixer[idx].name); + ctl = snd_ctl_find_id(cm->card, &elem_id); + if (ctl) cm->mixer_res_ctl[idx] = ctl; } diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index 5512abd..341f34e 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -327,22 +327,22 @@ static void snd_emu10k1x_ptr_write(struct emu10k1x *emu, static void snd_emu10k1x_intr_enable(struct emu10k1x *emu, unsigned int intrenb) { unsigned long flags; - unsigned int enable; - + unsigned int intr_enable; + spin_lock_irqsave(&emu->emu_lock, flags); - enable = inl(emu->port + INTE) | intrenb; - outl(enable, emu->port + INTE); + intr_enable = inl(emu->port + INTE) | intrenb; + outl(intr_enable, emu->port + INTE); spin_unlock_irqrestore(&emu->emu_lock, flags); } static void snd_emu10k1x_intr_disable(struct emu10k1x *emu, unsigned int intrenb) { unsigned long flags; - unsigned int enable; - + unsigned int intr_enable; + spin_lock_irqsave(&emu->emu_lock, flags); - enable = inl(emu->port + INTE) & ~intrenb; - outl(enable, emu->port + INTE); + intr_enable = inl(emu->port + INTE) & ~intrenb; + outl(intr_enable, emu->port + INTE); spin_unlock_irqrestore(&emu->emu_lock, flags); } @@ -795,9 +795,9 @@ static irqreturn_t snd_emu10k1x_interrupt(int irq, void *dev_id) // capture interrupt if (status & (IPR_CAP_0_LOOP | IPR_CAP_0_HALF_LOOP)) { - struct emu10k1x_voice *pvoice = &chip->capture_voice; - if (pvoice->use) - snd_emu10k1x_pcm_interrupt(chip, pvoice); + struct emu10k1x_voice *cap_voice = &chip->capture_voice; + if (cap_voice->use) + snd_emu10k1x_pcm_interrupt(chip, cap_voice); else snd_emu10k1x_intr_disable(chip, INTE_CAP_0_LOOP | diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c index f3caa3f..216f974 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -412,7 +412,7 @@ static void snd_emu_proc_emu1010_reg_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_emu10k1 *emu = entry->private_data; - int value; + u32 value; unsigned long flags; int i; snd_iprintf(buffer, "EMU1010 Registers:\n\n"); diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 72d85a5..52fae4a 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -1635,20 +1635,20 @@ static int __devinit snd_ensoniq_1371_mixer(struct ensoniq *ensoniq, if (has_spdif > 0 || (!has_spdif && es1371_quirk_lookup(ensoniq, es1371_spdif_present))) { struct snd_kcontrol *kctl; - int i, index = 0; + int i, is_spdif = 0; ensoniq->spdif_default = ensoniq->spdif_stream = SNDRV_PCM_DEFAULT_CON_SPDIF; outl(ensoniq->spdif_default, ES_REG(ensoniq, CHANNEL_STATUS)); if (ensoniq->u.es1371.ac97->ext_id & AC97_EI_SPDIF) - index++; + is_spdif++; for (i = 0; i < ARRAY_SIZE(snd_es1371_mixer_spdif); i++) { kctl = snd_ctl_new1(&snd_es1371_mixer_spdif[i], ensoniq); if (!kctl) return -ENOMEM; - kctl->id.index = index; + kctl->id.index = is_spdif; err = snd_ctl_add(card, kctl); if (err < 0) return err; diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 25ccfce..db02d40 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -1960,7 +1960,7 @@ snd_es1968_mixer(struct es1968 *chip) { struct snd_ac97_bus *pbus; struct snd_ac97_template ac97; - struct snd_ctl_elem_id id; + struct snd_ctl_elem_id elem_id; int err; static struct snd_ac97_bus_ops ops = { .write = snd_es1968_ac97_write, @@ -1977,14 +1977,14 @@ snd_es1968_mixer(struct es1968 *chip) return err; /* attach master switch / volumes for h/w volume control */ - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(id.name, "Master Playback Switch"); - chip->master_switch = snd_ctl_find_id(chip->card, &id); - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(id.name, "Master Playback Volume"); - chip->master_volume = snd_ctl_find_id(chip->card, &id); + memset(&elem_id, 0, sizeof(elem_id)); + elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(elem_id.name, "Master Playback Switch"); + chip->master_switch = snd_ctl_find_id(chip->card, &elem_id); + memset(&elem_id, 0, sizeof(elem_id)); + elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(elem_id.name, "Master Playback Volume"); + chip->master_volume = snd_ctl_find_id(chip->card, &elem_id); return 0; } diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 4c300e6..c129f9e 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -1285,7 +1285,6 @@ static int wait_for_codec(struct fm801 *chip, unsigned int codec_id, static int snd_fm801_chip_init(struct fm801 *chip, int resume) { - int id; unsigned short cmdw; if (chip->tea575x_tuner & 0x0010) @@ -1310,13 +1309,14 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume) } else { /* my card has the secondary codec */ /* at address #3, so the loop is inverted */ - for (id = 3; id > 0; id--) { - if (! wait_for_codec(chip, id, AC97_VENDOR_ID1, + int i; + for (i = 3; i > 0; i--) { + if (!wait_for_codec(chip, i, AC97_VENDOR_ID1, msecs_to_jiffies(50))) { cmdw = inw(FM801_REG(chip, AC97_DATA)); if (cmdw != 0xffff && cmdw != 0) { chip->secondary = 1; - chip->secondary_addr = id; + chip->secondary_addr = i; break; } } diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 9e0d8a1..ab0c726 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -2,7 +2,7 @@ snd-hda-intel-y := hda_intel.o # since snd-hda-intel is the only driver using hda-codec, # merge it into a single module although it was originally # designed to be individual modules -snd-hda-intel-y += hda_codec.o vmaster.o +snd-hda-intel-y += hda_codec.o snd-hda-intel-$(CONFIG_PROC_FS) += hda_proc.o snd-hda-intel-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o snd-hda-intel-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 37c4139..689d177 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -31,6 +31,7 @@ #include #include "hda_local.h" #include +#include "hda_patch.h" /* codec presets */ #ifdef CONFIG_SND_HDA_POWER_SAVE /* define this option here to hide as static */ @@ -51,21 +52,50 @@ struct hda_vendor_id { /* codec vendor labels */ static struct hda_vendor_id hda_vendor_ids[] = { - { 0x10ec, "Realtek" }, + { 0x1002, "ATI" }, { 0x1057, "Motorola" }, + { 0x1095, "Silicon Image" }, + { 0x10ec, "Realtek" }, { 0x1106, "VIA" }, { 0x111d, "IDT" }, + { 0x11c1, "LSI" }, { 0x11d4, "Analog Devices" }, { 0x13f6, "C-Media" }, { 0x14f1, "Conexant" }, + { 0x17e8, "Chrontel" }, + { 0x1854, "LG" }, { 0x434d, "C-Media" }, { 0x8384, "SigmaTel" }, {} /* terminator */ }; -/* codec presets */ -#include "hda_patch.h" - +static const struct hda_codec_preset *hda_preset_tables[] = { +#ifdef CONFIG_SND_HDA_CODEC_REALTEK + snd_hda_preset_realtek, +#endif +#ifdef CONFIG_SND_HDA_CODEC_CMEDIA + snd_hda_preset_cmedia, +#endif +#ifdef CONFIG_SND_HDA_CODEC_ANALOG + snd_hda_preset_analog, +#endif +#ifdef CONFIG_SND_HDA_CODEC_SIGMATEL + snd_hda_preset_sigmatel, +#endif +#ifdef CONFIG_SND_HDA_CODEC_SI3054 + snd_hda_preset_si3054, +#endif +#ifdef CONFIG_SND_HDA_CODEC_ATIHDMI + snd_hda_preset_atihdmi, +#endif +#ifdef CONFIG_SND_HDA_CODEC_CONEXANT + snd_hda_preset_conexant, +#endif +#ifdef CONFIG_SND_HDA_CODEC_VIA + snd_hda_preset_via, +#endif + NULL +}; #ifdef CONFIG_SND_HDA_POWER_SAVE static void hda_power_work(struct work_struct *work); @@ -690,6 +720,19 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format); } +void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid) +{ + if (!nid) + return; + + snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0); +#if 0 /* keep the format */ + msleep(1); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0); +#endif +} + /* * amp access functions */ @@ -1037,16 +1080,24 @@ void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, } /* find a mixer control element with the given name */ -struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, - const char *name) +static struct snd_kcontrol * +_snd_hda_find_mixer_ctl(struct hda_codec *codec, + const char *name, int idx) { struct snd_ctl_elem_id id; memset(&id, 0, sizeof(id)); id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + id.index = idx; strcpy(id.name, name); return snd_ctl_find_id(codec->bus->card, &id); } +struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, + const char *name) +{ + return _snd_hda_find_mixer_ctl(codec, name, 0); +} + /* create a virtual master control and add slaves */ int snd_hda_add_vmaster(struct hda_codec *codec, char *name, unsigned int *tlv, const char **slaves) @@ -1481,6 +1532,8 @@ static struct snd_kcontrol_new dig_mixes[] = { { } /* end */ }; +#define SPDIF_MAX_IDX 4 /* 4 instances should be enough to probe */ + /** * snd_hda_create_spdif_out_ctls - create Output SPDIF-related controls * @codec: the HDA codec @@ -1496,9 +1549,20 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) int err; struct snd_kcontrol *kctl; struct snd_kcontrol_new *dig_mix; + int idx; + for (idx = 0; idx < SPDIF_MAX_IDX; idx++) { + if (!_snd_hda_find_mixer_ctl(codec, "IEC958 Playback Switch", + idx)) + break; + } + if (idx >= SPDIF_MAX_IDX) { + printk(KERN_ERR "hda_codec: too many IEC958 outputs\n"); + return -EBUSY; + } for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) { kctl = snd_ctl_new1(dig_mix, codec); + kctl->id.index = idx; kctl->private_value = nid; err = snd_ctl_add(codec->bus->card, kctl); if (err < 0) @@ -1512,6 +1576,43 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) } /* + * SPDIF sharing with analog output + */ +static int spdif_share_sw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = mout->share_spdif; + return 0; +} + +static int spdif_share_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol); + mout->share_spdif = !!ucontrol->value.integer.value[0]; + return 0; +} + +static struct snd_kcontrol_new spdif_share_sw = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "IEC958 Default PCM Playback Switch", + .info = snd_ctl_boolean_mono_info, + .get = spdif_share_sw_get, + .put = spdif_share_sw_put, +}; + +int snd_hda_create_spdif_share_sw(struct hda_codec *codec, + struct hda_multi_out *mout) +{ + if (!mout->dig_out_nid) + return 0; + /* ATTENTION: here mout is passed as private_data, instead of codec */ + return snd_ctl_add(codec->bus->card, + snd_ctl_new1(&spdif_share_sw, mout)); +} + +/* * SPDIF input */ @@ -1595,7 +1696,17 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) int err; struct snd_kcontrol *kctl; struct snd_kcontrol_new *dig_mix; + int idx; + for (idx = 0; idx < SPDIF_MAX_IDX; idx++) { + if (!_snd_hda_find_mixer_ctl(codec, "IEC958 Capture Switch", + idx)) + break; + } + if (idx >= SPDIF_MAX_IDX) { + printk(KERN_ERR "hda_codec: too many IEC958 inputs\n"); + return -EBUSY; + } for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) { kctl = snd_ctl_new1(dig_mix, codec); kctl->private_value = nid; @@ -2106,7 +2217,7 @@ static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { - snd_hda_codec_setup_stream(codec, hinfo->nid, 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, hinfo->nid); return 0; } @@ -2491,7 +2602,7 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec, mutex_lock(&codec->spdif_mutex); if (mout->dig_out_used == HDA_DIG_ANALOG_DUP) /* already opened as analog dup; reset it once */ - snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid); mout->dig_out_used = HDA_DIG_EXCLUSIVE; mutex_unlock(&codec->spdif_mutex); return 0; @@ -2526,9 +2637,36 @@ int snd_hda_multi_out_dig_close(struct hda_codec *codec, */ int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_multi_out *mout, - struct snd_pcm_substream *substream) -{ - substream->runtime->hw.channels_max = mout->max_channels; + struct snd_pcm_substream *substream, + struct hda_pcm_stream *hinfo) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + runtime->hw.channels_max = mout->max_channels; + if (mout->dig_out_nid) { + if (!mout->analog_rates) { + mout->analog_rates = hinfo->rates; + mout->analog_formats = hinfo->formats; + mout->analog_maxbps = hinfo->maxbps; + } else { + runtime->hw.rates = mout->analog_rates; + runtime->hw.formats = mout->analog_formats; + hinfo->maxbps = mout->analog_maxbps; + } + if (!mout->spdif_rates) { + snd_hda_query_supported_pcm(codec, mout->dig_out_nid, + &mout->spdif_rates, + &mout->spdif_formats, + &mout->spdif_maxbps); + } + mutex_lock(&codec->spdif_mutex); + if (mout->share_spdif) { + runtime->hw.rates &= mout->spdif_rates; + runtime->hw.formats &= mout->spdif_formats; + if (mout->spdif_maxbps < hinfo->maxbps) + hinfo->maxbps = mout->spdif_maxbps; + } + } + mutex_unlock(&codec->spdif_mutex); return snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 2); } @@ -2548,7 +2686,8 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, int i; mutex_lock(&codec->spdif_mutex); - if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { + if (mout->dig_out_nid && mout->share_spdif && + mout->dig_out_used != HDA_DIG_EXCLUSIVE) { if (chs == 2 && snd_hda_is_supported_format(codec, mout->dig_out_nid, format) && @@ -2558,8 +2697,7 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, stream_tag, format); } else { mout->dig_out_used = 0; - snd_hda_codec_setup_stream(codec, mout->dig_out_nid, - 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid); } } mutex_unlock(&codec->spdif_mutex); @@ -2601,17 +2739,16 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, int i; for (i = 0; i < mout->num_dacs; i++) - snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, nids[i]); if (mout->hp_nid) - snd_hda_codec_setup_stream(codec, mout->hp_nid, 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, mout->hp_nid); for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) if (mout->extra_out_nid[i]) - snd_hda_codec_setup_stream(codec, - mout->extra_out_nid[i], - 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, + mout->extra_out_nid[i]); mutex_lock(&codec->spdif_mutex); if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) { - snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid); mout->dig_out_used = 0; } mutex_unlock(&codec->spdif_mutex); @@ -2790,6 +2927,30 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, } } + /* FIX-UP: + * If no line-out is defined but multiple HPs are found, + * some of them might be the real line-outs. + */ + if (!cfg->line_outs && cfg->hp_outs > 1) { + int i = 0; + while (i < cfg->hp_outs) { + /* The real HPs should have the sequence 0x0f */ + if ((sequences_hp[i] & 0x0f) == 0x0f) { + i++; + continue; + } + /* Move it to the line-out table */ + cfg->line_out_pins[cfg->line_outs] = cfg->hp_pins[i]; + sequences_line_out[cfg->line_outs] = sequences_hp[i]; + cfg->line_outs++; + cfg->hp_outs--; + memmove(cfg->hp_pins + i, cfg->hp_pins + i + 1, + sizeof(cfg->hp_pins[0]) * (cfg->hp_outs - i)); + memmove(sequences_hp + i - 1, sequences_hp + i, + sizeof(sequences_hp[0]) * (cfg->hp_outs - i)); + } + } + /* sort by sequence */ sort_pins_by_sequence(cfg->line_out_pins, sequences_line_out, cfg->line_outs); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index f148711..dcd390b 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -590,11 +590,21 @@ struct hda_pcm_stream { struct hda_pcm_ops ops; }; +/* PCM types */ +enum { + HDA_PCM_TYPE_AUDIO, + HDA_PCM_TYPE_SPDIF, + HDA_PCM_TYPE_HDMI, + HDA_PCM_TYPE_MODEM, + HDA_PCM_NTYPES +}; + /* for PCM creation */ struct hda_pcm { char *name; struct hda_pcm_stream stream[2]; - unsigned int is_modem; /* modem codec? */ + unsigned int pcm_type; /* HDA_PCM_TYPE_XXX */ + int device; /* assigned device number */ }; /* codec information */ @@ -712,6 +722,7 @@ int snd_hda_build_pcms(struct hda_bus *bus); void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stream_tag, int channel_id, int format); +void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid); unsigned int snd_hda_calc_stream_format(unsigned int rate, unsigned int channels, unsigned int format, diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f9de7c4..59e4389 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1007,8 +1007,8 @@ static int generic_pcm2_cleanup(struct hda_pcm_stream *hinfo, { struct hda_gspec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, hinfo->nid, 0, 0, 0); - snd_hda_codec_setup_stream(codec, spec->dac_node[1]->nid, 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, hinfo->nid); + snd_hda_codec_cleanup_stream(codec, spec->dac_node[1]->nid); return 0; } diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 4be36c8..bc3867e 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -185,35 +186,28 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; /* max number of SDs */ /* ICH, ATI and VIA have 4 playback and 4 capture */ -#define ICH6_CAPTURE_INDEX 0 #define ICH6_NUM_CAPTURE 4 -#define ICH6_PLAYBACK_INDEX 4 #define ICH6_NUM_PLAYBACK 4 /* ULI has 6 playback and 5 capture */ -#define ULI_CAPTURE_INDEX 0 #define ULI_NUM_CAPTURE 5 -#define ULI_PLAYBACK_INDEX 5 #define ULI_NUM_PLAYBACK 6 /* ATI HDMI has 1 playback and 0 capture */ -#define ATIHDMI_CAPTURE_INDEX 0 #define ATIHDMI_NUM_CAPTURE 0 -#define ATIHDMI_PLAYBACK_INDEX 0 #define ATIHDMI_NUM_PLAYBACK 1 /* this number is statically defined for simplicity */ #define MAX_AZX_DEV 16 /* max number of fragments - we may use more if allocating more pages for BDL */ -#define BDL_SIZE PAGE_ALIGN(8192) -#define AZX_MAX_FRAG (BDL_SIZE / (MAX_AZX_DEV * 16)) +#define BDL_SIZE 4096 +#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16) +#define AZX_MAX_FRAG 32 /* max buffer size - no h/w limit, you can increase as you like */ #define AZX_MAX_BUF_SIZE (1024*1024*1024) /* max number of PCM devics per card */ -#define AZX_MAX_AUDIO_PCMS 6 -#define AZX_MAX_MODEM_PCMS 2 -#define AZX_MAX_PCMS (AZX_MAX_AUDIO_PCMS + AZX_MAX_MODEM_PCMS) +#define AZX_MAX_PCMS 8 /* RIRB int mask: overrun[2], response[0] */ #define RIRB_INT_RESPONSE 0x01 @@ -227,6 +221,9 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; /* SD_CTL bits */ #define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */ #define SD_CTL_DMA_START 0x02 /* stream DMA start bit */ +#define SD_CTL_STRIPE (3 << 16) /* stripe control */ +#define SD_CTL_TRAFFIC_PRIO (1 << 18) /* traffic priority */ +#define SD_CTL_DIR (1 << 19) /* bi-directional stream */ #define SD_CTL_STREAM_TAG_MASK (0xf << 20) #define SD_CTL_STREAM_TAG_SHIFT 20 @@ -284,12 +281,10 @@ enum { */ struct azx_dev { - u32 *bdl; /* virtual address of the BDL */ - dma_addr_t bdl_addr; /* physical address of the BDL */ + struct snd_dma_buffer bdl; /* BDL buffer */ u32 *posbuf; /* position buffer pointer */ unsigned int bufsize; /* size of the play buffer in bytes */ - unsigned int fragsize; /* size of each period in bytes */ unsigned int frags; /* number for period in the play buffer */ unsigned int fifo_size; /* FIFO size */ @@ -350,7 +345,6 @@ struct azx { struct azx_dev *azx_dev; /* PCM */ - unsigned int pcm_devs; struct snd_pcm *pcm[AZX_MAX_PCMS]; /* HD codec */ @@ -361,8 +355,7 @@ struct azx { struct azx_rb corb; struct azx_rb rirb; - /* BDL, CORB/RIRB and position buffers */ - struct snd_dma_buffer bdl; + /* CORB/RIRB and position buffers */ struct snd_dma_buffer rb; struct snd_dma_buffer posbuf; @@ -546,8 +539,9 @@ static void azx_update_rirb(struct azx *chip) if (res_ex & ICH6_RIRB_EX_UNSOL_EV) snd_hda_queue_unsol_event(chip->bus, res, res_ex); else if (chip->rirb.cmds) { - chip->rirb.cmds--; chip->rirb.res = res; + smp_wmb(); + chip->rirb.cmds--; } } } @@ -566,8 +560,10 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec) azx_update_rirb(chip); spin_unlock_irq(&chip->reg_lock); } - if (!chip->rirb.cmds) + if (!chip->rirb.cmds) { + smp_rmb(); return chip->rirb.res; /* the last value */ + } if (time_after(jiffies, timeout)) break; if (codec->bus->needs_damn_long_delay) @@ -965,30 +961,57 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id) /* * set up BDL entries */ -static void azx_setup_periods(struct azx_dev *azx_dev) +static int azx_setup_periods(struct snd_pcm_substream *substream, + struct azx_dev *azx_dev) { - u32 *bdl = azx_dev->bdl; - dma_addr_t dma_addr = azx_dev->substream->runtime->dma_addr; - int idx; + struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream); + u32 *bdl; + int i, ofs, periods, period_bytes; /* reset BDL address */ azx_sd_writel(azx_dev, SD_BDLPL, 0); azx_sd_writel(azx_dev, SD_BDLPU, 0); + period_bytes = snd_pcm_lib_period_bytes(substream); + periods = azx_dev->bufsize / period_bytes; + /* program the initial BDL entries */ - for (idx = 0; idx < azx_dev->frags; idx++) { - unsigned int off = idx << 2; /* 4 dword step */ - dma_addr_t addr = dma_addr + idx * azx_dev->fragsize; - /* program the address field of the BDL entry */ - bdl[off] = cpu_to_le32((u32)addr); - bdl[off+1] = cpu_to_le32(upper_32bit(addr)); - - /* program the size field of the BDL entry */ - bdl[off+2] = cpu_to_le32(azx_dev->fragsize); - - /* program the IOC to enable interrupt when buffer completes */ - bdl[off+3] = cpu_to_le32(0x01); + bdl = (u32 *)azx_dev->bdl.area; + ofs = 0; + azx_dev->frags = 0; + for (i = 0; i < periods; i++) { + int size, rest; + if (i >= AZX_MAX_BDL_ENTRIES) { + snd_printk(KERN_ERR "Too many BDL entries: " + "buffer=%d, period=%d\n", + azx_dev->bufsize, period_bytes); + /* reset */ + azx_sd_writel(azx_dev, SD_BDLPL, 0); + azx_sd_writel(azx_dev, SD_BDLPU, 0); + return -EINVAL; + } + rest = period_bytes; + do { + dma_addr_t addr = snd_pcm_sgbuf_get_addr(sgbuf, ofs); + /* program the address field of the BDL entry */ + bdl[0] = cpu_to_le32((u32)addr); + bdl[1] = cpu_to_le32(upper_32bit(addr)); + /* program the size field of the BDL entry */ + size = PAGE_SIZE - (ofs % PAGE_SIZE); + if (rest < size) + size = rest; + bdl[2] = cpu_to_le32(size); + /* program the IOC to enable interrupt + * only when the whole fragment is processed + */ + rest -= size; + bdl[3] = rest ? 0 : cpu_to_le32(0x01); + bdl += 4; + azx_dev->frags++; + ofs += size; + } while (rest > 0); } + return 0; } /* @@ -1037,14 +1060,17 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev) /* program the BDL address */ /* lower BDL address */ - azx_sd_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl_addr); + azx_sd_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr); /* upper BDL address */ - azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl_addr)); + azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl.addr)); /* enable the position buffer */ - if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE)) - azx_writel(chip, DPLBASE, - (u32)chip->posbuf.addr |ICH6_DPLBASE_ENABLE); + if (chip->position_fix == POS_FIX_POSBUF || + chip->position_fix == POS_FIX_AUTO) { + if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE)) + azx_writel(chip, DPLBASE, + (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE); + } /* set the interrupt enable bits in the descriptor control register */ azx_sd_writel(azx_dev, SD_CTL, @@ -1157,7 +1183,8 @@ static struct snd_pcm_hardware azx_pcm_hw = { SNDRV_PCM_INFO_MMAP_VALID | /* No full-resume yet implemented */ /* SNDRV_PCM_INFO_RESUME |*/ - SNDRV_PCM_INFO_PAUSE), + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_48000, .rate_min = 48000, @@ -1219,6 +1246,7 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) spin_unlock_irqrestore(&chip->reg_lock, flags); runtime->private_data = azx_dev; + snd_pcm_set_sync(substream); mutex_unlock(&chip->open_mutex); return 0; } @@ -1275,8 +1303,6 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; azx_dev->bufsize = snd_pcm_lib_buffer_bytes(substream); - azx_dev->fragsize = snd_pcm_lib_period_bytes(substream); - azx_dev->frags = azx_dev->bufsize / azx_dev->fragsize; azx_dev->format_val = snd_hda_calc_stream_format(runtime->rate, runtime->channels, runtime->format, @@ -1288,10 +1314,10 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) return -EINVAL; } - snd_printdd("azx_pcm_prepare: bufsize=0x%x, fragsize=0x%x, " - "format=0x%x\n", - azx_dev->bufsize, azx_dev->fragsize, azx_dev->format_val); - azx_setup_periods(azx_dev); + snd_printdd("azx_pcm_prepare: bufsize=0x%x, format=0x%x\n", + azx_dev->bufsize, azx_dev->format_val); + if (azx_setup_periods(substream, azx_dev) < 0) + return -EINVAL; azx_setup_controller(chip, azx_dev); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1; @@ -1305,37 +1331,94 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct azx_dev *azx_dev = get_azx_dev(substream); struct azx *chip = apcm->chip; - int err = 0; + struct azx_dev *azx_dev; + struct snd_pcm_substream *s; + int start, nsync = 0, sbits = 0; + int nwait, timeout; - spin_lock(&chip->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_START: - azx_stream_start(chip, azx_dev); - azx_dev->running = 1; + start = 1; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: - azx_stream_stop(chip, azx_dev); - azx_dev->running = 0; + start = 0; break; default: - err = -EINVAL; + return -EINVAL; + } + + snd_pcm_group_for_each_entry(s, substream) { + if (s->pcm->card != substream->pcm->card) + continue; + azx_dev = get_azx_dev(s); + sbits |= 1 << azx_dev->index; + nsync++; + snd_pcm_trigger_done(s, substream); + } + + spin_lock(&chip->reg_lock); + if (nsync > 1) { + /* first, set SYNC bits of corresponding streams */ + azx_writel(chip, SYNC, azx_readl(chip, SYNC) | sbits); + } + snd_pcm_group_for_each_entry(s, substream) { + if (s->pcm->card != substream->pcm->card) + continue; + azx_dev = get_azx_dev(s); + if (start) + azx_stream_start(chip, azx_dev); + else + azx_stream_stop(chip, azx_dev); + azx_dev->running = start; } spin_unlock(&chip->reg_lock); - if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH || - cmd == SNDRV_PCM_TRIGGER_SUSPEND || - cmd == SNDRV_PCM_TRIGGER_STOP) { - int timeout = 5000; - while ((azx_sd_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START) && - --timeout) - ; + if (start) { + if (nsync == 1) + return 0; + /* wait until all FIFOs get ready */ + for (timeout = 5000; timeout; timeout--) { + nwait = 0; + snd_pcm_group_for_each_entry(s, substream) { + if (s->pcm->card != substream->pcm->card) + continue; + azx_dev = get_azx_dev(s); + if (!(azx_sd_readb(azx_dev, SD_STS) & + SD_STS_FIFO_READY)) + nwait++; + } + if (!nwait) + break; + cpu_relax(); + } + } else { + /* wait until all RUN bits are cleared */ + for (timeout = 5000; timeout; timeout--) { + nwait = 0; + snd_pcm_group_for_each_entry(s, substream) { + if (s->pcm->card != substream->pcm->card) + continue; + azx_dev = get_azx_dev(s); + if (azx_sd_readb(azx_dev, SD_CTL) & + SD_CTL_DMA_START) + nwait++; + } + if (!nwait) + break; + cpu_relax(); + } } - return err; + if (nsync > 1) { + spin_lock(&chip->reg_lock); + /* reset SYNC bits */ + azx_writel(chip, SYNC, azx_readl(chip, SYNC) & ~sbits); + spin_unlock(&chip->reg_lock); + } + return 0; } static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream) @@ -1378,6 +1461,7 @@ static struct snd_pcm_ops azx_pcm_ops = { .prepare = azx_pcm_prepare, .trigger = azx_pcm_trigger, .pointer = azx_pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, }; static void azx_pcm_free(struct snd_pcm *pcm) @@ -1386,7 +1470,7 @@ static void azx_pcm_free(struct snd_pcm *pcm) } static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec, - struct hda_pcm *cpcm, int pcm_dev) + struct hda_pcm *cpcm) { int err; struct snd_pcm *pcm; @@ -1400,7 +1484,7 @@ static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec, snd_assert(cpcm->name, return -EINVAL); - err = snd_pcm_new(chip->card, cpcm->name, pcm_dev, + err = snd_pcm_new(chip->card, cpcm->name, cpcm->device, cpcm->stream[0].substreams, cpcm->stream[1].substreams, &pcm); @@ -1420,62 +1504,70 @@ static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec, snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &azx_pcm_ops); if (cpcm->stream[1].substreams) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops); - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(chip->pci), 1024 * 64, 1024 * 1024); - chip->pcm[pcm_dev] = pcm; - if (chip->pcm_devs < pcm_dev + 1) - chip->pcm_devs = pcm_dev + 1; - + chip->pcm[cpcm->device] = pcm; return 0; } static int __devinit azx_pcm_create(struct azx *chip) { + static const char *dev_name[HDA_PCM_NTYPES] = { + "Audio", "SPDIF", "HDMI", "Modem" + }; + /* starting device index for each PCM type */ + static int dev_idx[HDA_PCM_NTYPES] = { + [HDA_PCM_TYPE_AUDIO] = 0, + [HDA_PCM_TYPE_SPDIF] = 1, + [HDA_PCM_TYPE_HDMI] = 3, + [HDA_PCM_TYPE_MODEM] = 6 + }; + /* normal audio device indices; not linear to keep compatibility */ + static int audio_idx[4] = { 0, 2, 4, 5 }; struct hda_codec *codec; int c, err; - int pcm_dev; + int num_devs[HDA_PCM_NTYPES]; err = snd_hda_build_pcms(chip->bus); if (err < 0) return err; /* create audio PCMs */ - pcm_dev = 0; + memset(num_devs, 0, sizeof(num_devs)); list_for_each_entry(codec, &chip->bus->codec_list, list) { for (c = 0; c < codec->num_pcms; c++) { - if (codec->pcm_info[c].is_modem) - continue; /* create later */ - if (pcm_dev >= AZX_MAX_AUDIO_PCMS) { - snd_printk(KERN_ERR SFX - "Too many audio PCMs\n"); - return -EINVAL; - } - err = create_codec_pcm(chip, codec, - &codec->pcm_info[c], pcm_dev); - if (err < 0) - return err; - pcm_dev++; - } - } - - /* create modem PCMs */ - pcm_dev = AZX_MAX_AUDIO_PCMS; - list_for_each_entry(codec, &chip->bus->codec_list, list) { - for (c = 0; c < codec->num_pcms; c++) { - if (!codec->pcm_info[c].is_modem) - continue; /* already created */ - if (pcm_dev >= AZX_MAX_PCMS) { - snd_printk(KERN_ERR SFX - "Too many modem PCMs\n"); - return -EINVAL; + struct hda_pcm *cpcm = &codec->pcm_info[c]; + int type = cpcm->pcm_type; + switch (type) { + case HDA_PCM_TYPE_AUDIO: + if (num_devs[type] >= ARRAY_SIZE(audio_idx)) { + snd_printk(KERN_WARNING + "Too many audio devices\n"); + continue; + } + cpcm->device = audio_idx[num_devs[type]]; + break; + case HDA_PCM_TYPE_SPDIF: + case HDA_PCM_TYPE_HDMI: + case HDA_PCM_TYPE_MODEM: + if (num_devs[type]) { + snd_printk(KERN_WARNING + "%s already defined\n", + dev_name[type]); + continue; + } + cpcm->device = dev_idx[type]; + break; + default: + snd_printk(KERN_WARNING + "Invalid PCM type %d\n", type); + continue; } - err = create_codec_pcm(chip, codec, - &codec->pcm_info[c], pcm_dev); + num_devs[type]++; + err = create_codec_pcm(chip, codec, cpcm); if (err < 0) return err; - chip->pcm[pcm_dev]->dev_class = SNDRV_PCM_CLASS_MODEM; - pcm_dev++; } } return 0; @@ -1502,10 +1594,7 @@ static int __devinit azx_init_stream(struct azx *chip) * and initialize */ for (i = 0; i < chip->num_streams; i++) { - unsigned int off = sizeof(u32) * (i * AZX_MAX_FRAG * 4); 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 = (u32 __iomem *)(chip->posbuf.area + i * 8); /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80); @@ -1587,7 +1676,7 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state) int i; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - for (i = 0; i < chip->pcm_devs; i++) + for (i = 0; i < AZX_MAX_PCMS; i++) snd_pcm_suspend_all(chip->pcm[i]); if (chip->initialized) snd_hda_suspend(chip->bus, state); @@ -1641,8 +1730,9 @@ static int azx_resume(struct pci_dev *pci) */ static int azx_free(struct azx *chip) { + int i; + if (chip->initialized) { - int i; for (i = 0; i < chip->num_streams; i++) azx_stream_stop(chip, &chip->azx_dev[i]); azx_stop_chip(chip); @@ -1657,8 +1747,11 @@ static int azx_free(struct azx *chip) if (chip->remap_addr) iounmap(chip->remap_addr); - if (chip->bdl.area) - snd_dma_free_pages(&chip->bdl); + if (chip->azx_dev) { + for (i = 0; i < chip->num_streams; i++) + if (chip->azx_dev[i].bdl.area) + snd_dma_free_pages(&chip->azx_dev[i].bdl); + } if (chip->rb.area) snd_dma_free_pages(&chip->rb); if (chip->posbuf.area) @@ -1682,6 +1775,7 @@ static int azx_dev_free(struct snd_device *device) static struct snd_pci_quirk position_fix_list[] __devinitdata = { SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_NONE), SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_NONE), + SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_NONE), {} }; @@ -1740,7 +1834,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, struct azx **rchip) { struct azx *chip; - int err; + int i, err; unsigned short gcap; static struct snd_device_ops ops = { .dev_free = azx_dev_free, @@ -1812,38 +1906,35 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, gcap = azx_readw(chip, GCAP); snd_printdd("chipset global capabilities = 0x%x\n", gcap); - if (gcap) { - /* read number of streams from GCAP register instead of using - * hardcoded value - */ - chip->playback_streams = (gcap & (0xF << 12)) >> 12; - chip->capture_streams = (gcap & (0xF << 8)) >> 8; - chip->playback_index_offset = chip->capture_streams; - chip->capture_index_offset = 0; - } else { + /* allow 64bit DMA address if supported by H/W */ + if ((gcap & 0x01) && !pci_set_dma_mask(pci, DMA_64BIT_MASK)) + pci_set_consistent_dma_mask(pci, DMA_64BIT_MASK); + + /* read number of streams from GCAP register instead of using + * hardcoded value + */ + chip->capture_streams = (gcap >> 8) & 0x0f; + chip->playback_streams = (gcap >> 12) & 0x0f; + if (!chip->playback_streams && !chip->capture_streams) { /* gcap didn't give any info, switching to old method */ switch (chip->driver_type) { case AZX_DRIVER_ULI: chip->playback_streams = ULI_NUM_PLAYBACK; chip->capture_streams = ULI_NUM_CAPTURE; - chip->playback_index_offset = ULI_PLAYBACK_INDEX; - chip->capture_index_offset = ULI_CAPTURE_INDEX; break; case AZX_DRIVER_ATIHDMI: chip->playback_streams = ATIHDMI_NUM_PLAYBACK; chip->capture_streams = ATIHDMI_NUM_CAPTURE; - chip->playback_index_offset = ATIHDMI_PLAYBACK_INDEX; - chip->capture_index_offset = ATIHDMI_CAPTURE_INDEX; break; default: chip->playback_streams = ICH6_NUM_PLAYBACK; chip->capture_streams = ICH6_NUM_CAPTURE; - chip->playback_index_offset = ICH6_PLAYBACK_INDEX; - chip->capture_index_offset = ICH6_CAPTURE_INDEX; break; } } + chip->capture_index_offset = 0; + chip->playback_index_offset = chip->capture_streams; chip->num_streams = chip->playback_streams + chip->capture_streams; chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev), GFP_KERNEL); @@ -1852,13 +1943,15 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, goto errout; } - /* allocate memory for the BDL for each stream */ - err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data(chip->pci), - BDL_SIZE, &chip->bdl); - if (err < 0) { - snd_printk(KERN_ERR SFX "cannot allocate BDL\n"); - goto errout; + for (i = 0; i < chip->num_streams; i++) { + /* allocate memory for the BDL for each stream */ + err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + BDL_SIZE, &chip->azx_dev[i].bdl); + if (err < 0) { + snd_printk(KERN_ERR SFX "cannot allocate BDL\n"); + goto errout; + } } /* allocate memory for the position buffer */ err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, @@ -1994,48 +2087,63 @@ static void __devexit azx_remove(struct pci_dev *pci) /* PCI IDs */ static struct pci_device_id azx_ids[] = { - { 0x8086, 0x2668, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH6 */ - { 0x8086, 0x27d8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH7 */ - { 0x8086, 0x269a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ESB2 */ - { 0x8086, 0x284b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH8 */ - { 0x8086, 0x293e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH9 */ - { 0x8086, 0x293f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH9 */ - { 0x8086, 0x3a3e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH10 */ - { 0x8086, 0x3a6e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH10 */ - { 0x8086, 0x811b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SCH }, /* SCH*/ - { 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB450 */ - { 0x1002, 0x4383, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB600 */ - { 0x1002, 0x793b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS600 HDMI */ - { 0x1002, 0x7919, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS690 HDMI */ - { 0x1002, 0x960f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS780 HDMI */ - { 0x1002, 0xaa00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI R600 HDMI */ - { 0x1002, 0xaa08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV630 HDMI */ - { 0x1002, 0xaa10, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV610 HDMI */ - { 0x1002, 0xaa18, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV670 HDMI */ - { 0x1002, 0xaa20, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV635 HDMI */ - { 0x1002, 0xaa28, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV620 HDMI */ - { 0x1002, 0xaa30, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV770 HDMI */ - { 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_VIA }, /* VIA VT8251/VT8237A */ - { 0x1039, 0x7502, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SIS }, /* SIS966 */ - { 0x10b9, 0x5461, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ULI }, /* ULI M5461 */ - { 0x10de, 0x026c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP51 */ - { 0x10de, 0x0371, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP55 */ - { 0x10de, 0x03e4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP61 */ - { 0x10de, 0x03f0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP61 */ - { 0x10de, 0x044a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP65 */ - { 0x10de, 0x044b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP65 */ - { 0x10de, 0x055c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */ - { 0x10de, 0x055d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */ - { 0x10de, 0x07fc, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP73 */ - { 0x10de, 0x07fd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP73 */ - { 0x10de, 0x0774, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */ - { 0x10de, 0x0775, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */ - { 0x10de, 0x0776, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */ - { 0x10de, 0x0777, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */ - { 0x10de, 0x0ac0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP79 */ - { 0x10de, 0x0ac1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP79 */ - { 0x10de, 0x0ac2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP79 */ - { 0x10de, 0x0ac3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP79 */ + /* ICH 6..10 */ + { PCI_DEVICE(0x8086, 0x2668), .driver_data = AZX_DRIVER_ICH }, + { PCI_DEVICE(0x8086, 0x27d8), .driver_data = AZX_DRIVER_ICH }, + { PCI_DEVICE(0x8086, 0x269a), .driver_data = AZX_DRIVER_ICH }, + { PCI_DEVICE(0x8086, 0x284b), .driver_data = AZX_DRIVER_ICH }, + { PCI_DEVICE(0x8086, 0x293e), .driver_data = AZX_DRIVER_ICH }, + { PCI_DEVICE(0x8086, 0x293f), .driver_data = AZX_DRIVER_ICH }, + { PCI_DEVICE(0x8086, 0x3a3e), .driver_data = AZX_DRIVER_ICH }, + { PCI_DEVICE(0x8086, 0x3a6e), .driver_data = AZX_DRIVER_ICH }, + /* SCH */ + { PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH }, + /* ATI SB 450/600 */ + { PCI_DEVICE(0x1002, 0x437b), .driver_data = AZX_DRIVER_ATI }, + { PCI_DEVICE(0x1002, 0x4383), .driver_data = AZX_DRIVER_ATI }, + /* ATI HDMI */ + { PCI_DEVICE(0x1002, 0x793b), .driver_data = AZX_DRIVER_ATIHDMI }, + { PCI_DEVICE(0x1002, 0x7919), .driver_data = AZX_DRIVER_ATIHDMI }, + { PCI_DEVICE(0x1002, 0x960f), .driver_data = AZX_DRIVER_ATIHDMI }, + { PCI_DEVICE(0x1002, 0xaa00), .driver_data = AZX_DRIVER_ATIHDMI }, + { PCI_DEVICE(0x1002, 0xaa08), .driver_data = AZX_DRIVER_ATIHDMI }, + { PCI_DEVICE(0x1002, 0xaa10), .driver_data = AZX_DRIVER_ATIHDMI }, + { PCI_DEVICE(0x1002, 0xaa18), .driver_data = AZX_DRIVER_ATIHDMI }, + { PCI_DEVICE(0x1002, 0xaa20), .driver_data = AZX_DRIVER_ATIHDMI }, + { PCI_DEVICE(0x1002, 0xaa28), .driver_data = AZX_DRIVER_ATIHDMI }, + { PCI_DEVICE(0x1002, 0xaa30), .driver_data = AZX_DRIVER_ATIHDMI }, + { PCI_DEVICE(0x1002, 0xaa38), .driver_data = AZX_DRIVER_ATIHDMI }, + { PCI_DEVICE(0x1002, 0xaa40), .driver_data = AZX_DRIVER_ATIHDMI }, + { PCI_DEVICE(0x1002, 0xaa48), .driver_data = AZX_DRIVER_ATIHDMI }, + /* VIA VT8251/VT8237A */ + { PCI_DEVICE(0x1106, 0x3288), .driver_data = AZX_DRIVER_VIA }, + /* SIS966 */ + { PCI_DEVICE(0x1039, 0x7502), .driver_data = AZX_DRIVER_SIS }, + /* ULI M5461 */ + { PCI_DEVICE(0x10b9, 0x5461), .driver_data = AZX_DRIVER_ULI }, + /* NVIDIA MCP */ + { PCI_DEVICE(0x10de, 0x026c), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0371), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x03e4), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x03f0), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x044a), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x044b), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x055c), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x055d), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0774), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0775), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0776), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0777), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x07fc), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x07fd), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0ac0), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0ac1), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0ac2), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0ac3), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0bd4), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0bd5), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0bd6), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0bd7), .driver_data = AZX_DRIVER_NVIDIA }, { 0, } }; MODULE_DEVICE_TABLE(pci, azx_ids); diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index ad0014a..5c9e578 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -228,8 +228,18 @@ struct hda_multi_out { int max_channels; /* currently supported analog channels */ int dig_out_used; /* current usage of digital out (HDA_DIG_XXX) */ int no_share_stream; /* don't share a stream with multiple pins */ + int share_spdif; /* share SPDIF pin */ + /* PCM information for both analog and SPDIF DACs */ + unsigned int analog_rates; + unsigned int analog_maxbps; + u64 analog_formats; + unsigned int spdif_rates; + unsigned int spdif_maxbps; + u64 spdif_formats; }; +int snd_hda_create_spdif_share_sw(struct hda_codec *codec, + struct hda_multi_out *mout); int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mout); int snd_hda_multi_out_dig_close(struct hda_codec *codec, @@ -241,7 +251,8 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec, struct snd_pcm_substream *substream); int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_multi_out *mout, - struct snd_pcm_substream *substream); + struct snd_pcm_substream *substream, + struct hda_pcm_stream *hinfo); int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_out *mout, unsigned int stream_tag, @@ -407,11 +418,4 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, hda_nid_t nid); #endif /* CONFIG_SND_HDA_POWER_SAVE */ -/* - * virtual master control - */ -struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, - const unsigned int *tlv); -int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave); - #endif /* __SOUND_HDA_LOCAL_H */ diff --git a/sound/pci/hda/hda_patch.h b/sound/pci/hda/hda_patch.h index f5c23bb..2fdf235 100644 --- a/sound/pci/hda/hda_patch.h +++ b/sound/pci/hda/hda_patch.h @@ -18,31 +18,3 @@ extern struct hda_codec_preset snd_hda_preset_atihdmi[]; extern struct hda_codec_preset snd_hda_preset_conexant[]; /* VIA codecs */ extern struct hda_codec_preset snd_hda_preset_via[]; - -static const struct hda_codec_preset *hda_preset_tables[] = { -#ifdef CONFIG_SND_HDA_CODEC_REALTEK - snd_hda_preset_realtek, -#endif -#ifdef CONFIG_SND_HDA_CODEC_CMEDIA - snd_hda_preset_cmedia, -#endif -#ifdef CONFIG_SND_HDA_CODEC_ANALOG - snd_hda_preset_analog, -#endif -#ifdef CONFIG_SND_HDA_CODEC_SIGMATEL - snd_hda_preset_sigmatel, -#endif -#ifdef CONFIG_SND_HDA_CODEC_SI3054 - snd_hda_preset_si3054, -#endif -#ifdef CONFIG_SND_HDA_CODEC_ATIHDMI - snd_hda_preset_atihdmi, -#endif -#ifdef CONFIG_SND_HDA_CODEC_CONEXANT - snd_hda_preset_conexant, -#endif -#ifdef CONFIG_SND_HDA_CODEC_VIA - snd_hda_preset_via, -#endif - NULL -}; diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index c864928..2befeeb 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -28,6 +28,7 @@ #include #include "hda_codec.h" #include "hda_local.h" +#include "hda_patch.h" struct ad198x_spec { struct snd_kcontrol_new *mixers[5]; @@ -80,7 +81,6 @@ struct ad198x_spec { #endif /* for virtual master */ hda_nid_t vmaster_nid; - u32 vmaster_tlv[4]; const char **slave_vols; const char **slave_sws; }; @@ -171,6 +171,11 @@ static int ad198x_build_controls(struct hda_codec *codec) err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); if (err < 0) return err; + err = snd_hda_create_spdif_share_sw(codec, + &spec->multiout); + if (err < 0) + return err; + spec->multiout.share_spdif = 1; } if (spec->dig_in_nid) { err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); @@ -180,10 +185,11 @@ static int ad198x_build_controls(struct hda_codec *codec) /* if we have no master control, let's create it */ if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { + unsigned int vmaster_tlv[4]; snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, - HDA_OUTPUT, spec->vmaster_tlv); + HDA_OUTPUT, vmaster_tlv); err = snd_hda_add_vmaster(codec, "Master Playback Volume", - spec->vmaster_tlv, + vmaster_tlv, (spec->slave_vols ? spec->slave_vols : ad_slave_vols)); if (err < 0) @@ -217,7 +223,8 @@ static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct ad198x_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream); + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); } static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -289,8 +296,7 @@ static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct ad198x_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], - 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); return 0; } @@ -359,6 +365,7 @@ static int ad198x_build_pcms(struct hda_codec *codec) info++; codec->num_pcms++; info->name = "AD198x Digital"; + info->pcm_type = HDA_PCM_TYPE_SPDIF; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; if (spec->dig_in_nid) { @@ -611,13 +618,19 @@ static struct hda_input_mux ad1986a_laptop_eapd_capture_source = { }, }; +static struct hda_input_mux ad1986a_automic_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x0 }, + { "Mix", 0x5 }, + }, +}; + static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = { HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol), HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw), HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT), @@ -641,6 +654,33 @@ static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = { { } /* end */ }; +/* re-connect the mic boost input according to the jack sensing */ +static void ad1986a_automic(struct hda_codec *codec) +{ + unsigned int present; + present = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_PIN_SENSE, 0); + /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */ + snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL, + (present & AC_PINSENSE_PRESENCE) ? 0 : 2); +} + +#define AD1986A_MIC_EVENT 0x36 + +static void ad1986a_automic_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) != AD1986A_MIC_EVENT) + return; + ad1986a_automic(codec); +} + +static int ad1986a_automic_init(struct hda_codec *codec) +{ + ad198x_init(codec); + ad1986a_automic(codec); + return 0; +} + /* laptop-automute - 2ch only */ static void ad1986a_update_hp(struct hda_codec *codec) @@ -844,6 +884,15 @@ static struct hda_verb ad1986a_eapd_init_verbs[] = { {} }; +static struct hda_verb ad1986a_automic_verbs[] = { + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/ + {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0}, + {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT}, + {} +}; + /* Ultra initialization */ static struct hda_verb ad1986a_ultra_init[] = { /* eapd initialization */ @@ -986,14 +1035,17 @@ static int patch_ad1986a(struct hda_codec *codec) break; case AD1986A_LAPTOP_EAPD: spec->mixers[0] = ad1986a_laptop_eapd_mixers; - spec->num_init_verbs = 2; + spec->num_init_verbs = 3; spec->init_verbs[1] = ad1986a_eapd_init_verbs; + spec->init_verbs[2] = ad1986a_automic_verbs; spec->multiout.max_channels = 2; spec->multiout.num_dacs = 1; spec->multiout.dac_nids = ad1986a_laptop_dac_nids; if (!is_jack_available(codec, 0x25)) spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1986a_laptop_eapd_capture_source; + spec->input_mux = &ad1986a_automic_capture_source; + codec->patch_ops.unsol_event = ad1986a_automic_unsol_event; + codec->patch_ops.init = ad1986a_automic_init; break; case AD1986A_LAPTOP_AUTOMUTE: spec->mixers[0] = ad1986a_laptop_automute_mixers; @@ -1365,7 +1417,10 @@ static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol, if (! ad198x_eapd_put(kcontrol, ucontrol)) return 0; - + /* change speaker pin appropriately */ + snd_hda_codec_write(codec, 0x05, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + spec->cur_eapd ? PIN_OUT : 0); /* toggle HP mute appropriately */ snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0, HDA_AMP_MUTE, @@ -3133,11 +3188,12 @@ static int patch_ad1884(struct hda_codec *codec) * Lenovo Thinkpad T61/X61 */ static struct hda_input_mux ad1984_thinkpad_capture_source = { - .num_items = 3, + .num_items = 4, .items = { { "Mic", 0x0 }, { "Internal Mic", 0x1 }, { "Mix", 0x3 }, + { "Docking-Station", 0x4 }, }, }; @@ -3268,8 +3324,7 @@ static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { - snd_hda_codec_setup_stream(codec, 0x05 + substream->number, - 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number); return 0; } @@ -3356,6 +3411,469 @@ static int patch_ad1984(struct hda_codec *codec) /* + * AD1883 / AD1884A / AD1984A / AD1984B + * + * port-B (0x14) - front mic-in + * port-E (0x1c) - rear mic-in + * port-F (0x16) - CD / ext out + * port-C (0x15) - rear line-in + * port-D (0x12) - rear line-out + * port-A (0x11) - front hp-out + * + * AD1984A = AD1884A + digital-mic + * AD1883 = equivalent with AD1984A + * AD1984B = AD1984A + extra SPDIF-out + * + * FIXME: + * We share the single DAC for both HP and line-outs (see AD1884/1984). + */ + +static hda_nid_t ad1884a_dac_nids[1] = { + 0x03, +}; + +#define ad1884a_adc_nids ad1884_adc_nids +#define ad1884a_capsrc_nids ad1884_capsrc_nids + +#define AD1884A_SPDIF_OUT 0x02 + +static struct hda_input_mux ad1884a_capture_source = { + .num_items = 5, + .items = { + { "Front Mic", 0x0 }, + { "Mic", 0x4 }, + { "Line", 0x1 }, + { "CD", 0x2 }, + { "Mix", 0x3 }, + }, +}; + +static struct snd_kcontrol_new ad1884a_base_mixers[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = ad198x_mux_enum_info, + .get = ad198x_mux_enum_get, + .put = ad198x_mux_enum_put, + }, + /* SPDIF controls */ + HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", + /* identical with ad1983 */ + .info = ad1983_spdif_route_info, + .get = ad1983_spdif_route_get, + .put = ad1983_spdif_route_put, + }, + { } /* end */ +}; + +/* + * initialization verbs + */ +static struct hda_verb ad1884a_init_verbs[] = { + /* DACs; unmute as default */ + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ + /* Port-A (HP) mixer - route only from analog mixer */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Port-A pin */ + {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Port-D (Line-out) mixer - route only from analog mixer */ + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Port-D pin */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Mono-out mixer - route only from analog mixer */ + {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Mono-out pin */ + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Port-B (front mic) pin */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Port-C (rear line-in) pin */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Port-E (rear mic) pin */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */ + /* Port-F (CD) pin */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Analog mixer; mute as default */ + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */ + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, + /* Analog Mix output amp */ + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* capture sources */ + {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* SPDIF output amp */ + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ + { } /* end */ +}; + +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list ad1884a_loopbacks[] = { + { 0x20, HDA_INPUT, 0 }, /* Front Mic */ + { 0x20, HDA_INPUT, 1 }, /* Mic */ + { 0x20, HDA_INPUT, 2 }, /* CD */ + { 0x20, HDA_INPUT, 4 }, /* Docking */ + { } /* end */ +}; +#endif + +/* + * Laptop model + * + * Port A: Headphone jack + * Port B: MIC jack + * Port C: Internal MIC + * Port D: Dock Line Out (if enabled) + * Port E: Dock Line In (if enabled) + * Port F: Internal speakers + */ + +static struct hda_input_mux ad1884a_laptop_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, /* port-B */ + { "Internal Mic", 0x1 }, /* port-C */ + { "Dock Mic", 0x4 }, /* port-E */ + { "Mix", 0x3 }, + }, +}; + +static struct snd_kcontrol_new ad1884a_laptop_mixers[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = ad198x_mux_enum_info, + .get = ad198x_mux_enum_get, + .put = ad198x_mux_enum_put, + }, + { } /* end */ +}; + +static struct hda_input_mux ad1884a_mobile_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x1 }, /* port-C */ + { "Mix", 0x3 }, + }, +}; + +static struct snd_kcontrol_new ad1884a_mobile_mixers[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = ad198x_mux_enum_info, + .get = ad198x_mux_enum_get, + .put = ad198x_mux_enum_put, + }, + { } /* end */ +}; + +/* mute internal speaker if HP is plugged */ +static void ad1884a_hp_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x11, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE, + present ? 0x00 : 0x02); +} + +#define AD1884A_HP_EVENT 0x37 + +/* unsolicited event for HP jack sensing */ +static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res) +{ + if ((res >> 26) != AD1884A_HP_EVENT) + return; + ad1884a_hp_automute(codec); +} + +/* initialize jack-sensing, too */ +static int ad1884a_hp_init(struct hda_codec *codec) +{ + ad198x_init(codec); + ad1884a_hp_automute(codec); + return 0; +} + +/* additional verbs for laptop model */ +static struct hda_verb ad1884a_laptop_verbs[] = { + /* Port-A (HP) pin - always unmuted */ + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Port-F (int speaker) mixer - route only from analog mixer */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Port-F pin */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* analog mix */ + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* unsolicited event for pin-sense */ + {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, + { } /* end */ +}; + +/* + * Thinkpad X300 + * 0x11 - HP + * 0x12 - speaker + * 0x14 - mic-in + * 0x17 - built-in mic + */ + +static struct hda_verb ad1984a_thinkpad_verbs[] = { + /* HP unmute */ + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* analog mix */ + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* turn on EAPD */ + {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, + /* unsolicited event for pin-sense */ + {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, + /* internal mic - dmic */ + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + { } /* end */ +}; + +static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = ad198x_mux_enum_info, + .get = ad198x_mux_enum_get, + .put = ad198x_mux_enum_put, + }, + { } /* end */ +}; + +static struct hda_input_mux ad1984a_thinkpad_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Internal Mic", 0x5 }, + { "Mix", 0x3 }, + }, +}; + +/* mute internal speaker if HP is plugged */ +static void ad1984a_thinkpad_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) + & AC_PINSENSE_PRESENCE; + snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); +} + +/* unsolicited event for HP jack sensing */ +static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) != AD1884A_HP_EVENT) + return; + ad1984a_thinkpad_automute(codec); +} + +/* initialize jack-sensing, too */ +static int ad1984a_thinkpad_init(struct hda_codec *codec) +{ + ad198x_init(codec); + ad1984a_thinkpad_automute(codec); + return 0; +} + +/* + */ + +enum { + AD1884A_DESKTOP, + AD1884A_LAPTOP, + AD1884A_MOBILE, + AD1884A_THINKPAD, + AD1884A_MODELS +}; + +static const char *ad1884a_models[AD1884A_MODELS] = { + [AD1884A_DESKTOP] = "desktop", + [AD1884A_LAPTOP] = "laptop", + [AD1884A_MOBILE] = "mobile", + [AD1884A_THINKPAD] = "thinkpad", +}; + +static struct snd_pci_quirk ad1884a_cfg_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE), + SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD), + {} +}; + +static int patch_ad1884a(struct hda_codec *codec) +{ + struct ad198x_spec *spec; + int board_config; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + mutex_init(&spec->amp_mutex); + codec->spec = spec; + + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids); + spec->multiout.dac_nids = ad1884a_dac_nids; + spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT; + spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids); + spec->adc_nids = ad1884a_adc_nids; + spec->capsrc_nids = ad1884a_capsrc_nids; + spec->input_mux = &ad1884a_capture_source; + spec->num_mixers = 1; + spec->mixers[0] = ad1884a_base_mixers; + spec->num_init_verbs = 1; + spec->init_verbs[0] = ad1884a_init_verbs; + spec->spdif_route = 0; +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = ad1884a_loopbacks; +#endif + codec->patch_ops = ad198x_patch_ops; + + /* override some parameters */ + board_config = snd_hda_check_board_config(codec, AD1884A_MODELS, + ad1884a_models, + ad1884a_cfg_tbl); + switch (board_config) { + case AD1884A_LAPTOP: + spec->mixers[0] = ad1884a_laptop_mixers; + spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs; + spec->multiout.dig_out_nid = 0; + spec->input_mux = &ad1884a_laptop_capture_source; + codec->patch_ops.unsol_event = ad1884a_hp_unsol_event; + codec->patch_ops.init = ad1884a_hp_init; + break; + case AD1884A_MOBILE: + spec->mixers[0] = ad1884a_mobile_mixers; + spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs; + spec->multiout.dig_out_nid = 0; + spec->input_mux = &ad1884a_mobile_capture_source; + codec->patch_ops.unsol_event = ad1884a_hp_unsol_event; + codec->patch_ops.init = ad1884a_hp_init; + break; + case AD1884A_THINKPAD: + spec->mixers[0] = ad1984a_thinkpad_mixers; + spec->init_verbs[spec->num_init_verbs++] = + ad1984a_thinkpad_verbs; + spec->multiout.dig_out_nid = 0; + spec->input_mux = &ad1984a_thinkpad_capture_source; + codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event; + codec->patch_ops.init = ad1984a_thinkpad_init; + break; + } + + return 0; +} + + +/* * AD1882 * * port-A - front hp-out @@ -3654,8 +4172,12 @@ static int patch_ad1882(struct hda_codec *codec) * patch entries */ struct hda_codec_preset snd_hda_preset_analog[] = { + { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a }, { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 }, + { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a }, { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 }, + { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a }, + { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a }, { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 }, { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 }, { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 }, diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c index 9a8bb4c..1227250 100644 --- a/sound/pci/hda/patch_atihdmi.c +++ b/sound/pci/hda/patch_atihdmi.c @@ -27,6 +27,7 @@ #include #include "hda_codec.h" #include "hda_local.h" +#include "hda_patch.h" struct atihdmi_spec { struct hda_multi_out multiout; @@ -58,6 +59,10 @@ static int atihdmi_build_controls(struct hda_codec *codec) static int atihdmi_init(struct hda_codec *codec) { snd_hda_sequence_write(codec, atihdmi_basic_init); + /* SI codec requires to unmute the pin */ + if (get_wcaps(codec, 0x03) & AC_WCAP_OUT_AMP) + snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); return 0; } @@ -112,6 +117,7 @@ static int atihdmi_build_pcms(struct hda_codec *codec) codec->pcm_info = info; info->name = "ATI HDMI"; + info->pcm_type = HDA_PCM_TYPE_HDMI; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = atihdmi_pcm_digital_playback; return 0; @@ -158,5 +164,7 @@ struct hda_codec_preset snd_hda_preset_atihdmi[] = { { .id = 0x10027919, .name = "ATI RS600 HDMI", .patch = patch_atihdmi }, { .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi }, { .id = 0x1002aa01, .name = "ATI R6xx HDMI", .patch = patch_atihdmi }, + { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_atihdmi }, + { .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_atihdmi }, {} /* terminator */ }; diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index 3d6097b..8d14285 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -28,6 +28,7 @@ #include #include "hda_codec.h" #include "hda_local.h" +#include "hda_patch.h" #define NUM_PINS 11 @@ -329,6 +330,11 @@ static int cmi9880_build_controls(struct hda_codec *codec) err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); if (err < 0) return err; + err = snd_hda_create_spdif_share_sw(codec, + &spec->multiout); + if (err < 0) + return err; + spec->multiout.share_spdif = 1; } if (spec->dig_in_nid) { err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); @@ -432,7 +438,8 @@ static int cmi9880_playback_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct cmi_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream); + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); } static int cmi9880_playback_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -506,7 +513,7 @@ static int cmi9880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, { struct cmi_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); return 0; } @@ -571,6 +578,7 @@ static int cmi9880_build_pcms(struct hda_codec *codec) codec->num_pcms++; info++; info->name = "CMI9880 Digital"; + info->pcm_type = HDA_PCM_TYPE_SPDIF; if (spec->multiout.dig_out_nid) { info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cmi9880_pcm_digital_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 7206b30..36fd852 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -27,6 +27,7 @@ #include #include "hda_codec.h" #include "hda_local.h" +#include "hda_patch.h" #define CXT_PIN_DIR_IN 0x00 #define CXT_PIN_DIR_OUT 0x01 @@ -98,7 +99,8 @@ static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct conexant_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream); + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); } static int conexant_playback_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -172,8 +174,7 @@ static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct conexant_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], - 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); return 0; } @@ -241,7 +242,7 @@ static int cx5051_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct conexant_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->cur_adc, 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, spec->cur_adc); spec->cur_adc = 0; return 0; } @@ -284,6 +285,7 @@ static int conexant_build_pcms(struct hda_codec *codec) info++; codec->num_pcms++; info->name = "Conexant Digital"; + info->pcm_type = HDA_PCM_TYPE_SPDIF; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = conexant_pcm_digital_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = @@ -371,6 +373,11 @@ static int conexant_build_controls(struct hda_codec *codec) spec->multiout.dig_out_nid); if (err < 0) return err; + err = snd_hda_create_spdif_share_sw(codec, + &spec->multiout); + if (err < 0) + return err; + spec->multiout.share_spdif = 1; } if (spec->dig_in_nid) { err = snd_hda_create_spdif_in_ctls(codec,spec->dig_in_nid); @@ -511,6 +518,14 @@ static struct hda_input_mux cxt5045_capture_source_benq = { } }; +static struct hda_input_mux cxt5045_capture_source_hp530 = { + .num_items = 2, + .items = { + { "ExtMic", 0x1 }, + { "IntMic", 0x2 }, + } +}; + /* turn on/off EAPD (+ mute HP) as a master switch */ static int cxt5045_hp_master_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -639,6 +654,37 @@ static struct snd_kcontrol_new cxt5045_benq_mixers[] = { {} }; +static struct snd_kcontrol_new cxt5045_mixers_hp530[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = conexant_mux_enum_info, + .get = conexant_mux_enum_get, + .put = conexant_mux_enum_put + }, + HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Ext Mic Capture Switch", 0x1a, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x17, 0x2, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Playback Switch", 0x17, 0x2, HDA_INPUT), + HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x17, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x17, 0x1, HDA_INPUT), + HDA_BIND_VOL("Master Playback Volume", &cxt5045_hp_bind_master_vol), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = cxt_eapd_info, + .get = cxt_eapd_get, + .put = cxt5045_hp_master_sw_put, + .private_value = 0x10, + }, + + {} +}; + static struct hda_verb cxt5045_init_verbs[] = { /* Line in, Mic */ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, @@ -833,6 +879,7 @@ enum { CXT5045_LAPTOP_MICSENSE, CXT5045_LAPTOP_HPMICSENSE, CXT5045_BENQ, + CXT5045_LAPTOP_HP530, #ifdef CONFIG_SND_DEBUG CXT5045_TEST, #endif @@ -844,6 +891,7 @@ static const char *cxt5045_models[CXT5045_MODELS] = { [CXT5045_LAPTOP_MICSENSE] = "laptop-micsense", [CXT5045_LAPTOP_HPMICSENSE] = "laptop-hpmicsense", [CXT5045_BENQ] = "benq", + [CXT5045_LAPTOP_HP530] = "laptop-hp530", #ifdef CONFIG_SND_DEBUG [CXT5045_TEST] = "test", #endif @@ -857,7 +905,7 @@ static struct snd_pci_quirk cxt5045_cfg_tbl[] = { SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP_HPSENSE), SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP_HPSENSE), SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV9533EG", CXT5045_LAPTOP_HPSENSE), - SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530), SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP_HPSENSE), SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ), SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE), @@ -941,6 +989,14 @@ static int patch_cxt5045(struct hda_codec *codec) spec->num_mixers = 2; codec->patch_ops.init = cxt5045_init; break; + case CXT5045_LAPTOP_HP530: + codec->patch_ops.unsol_event = cxt5045_hp_unsol_event; + spec->input_mux = &cxt5045_capture_source_hp530; + spec->num_init_verbs = 2; + spec->init_verbs[1] = cxt5045_hp_sense_init_verbs; + spec->mixers[0] = cxt5045_mixers_hp530; + codec->patch_ops.init = cxt5045_init; + break; #ifdef CONFIG_SND_DEBUG case CXT5045_TEST: spec->input_mux = &cxt5045_test_capture_source; @@ -1537,7 +1593,7 @@ static void cxt5051_portc_automic(struct hda_codec *codec) new_adc = spec->adc_nids[spec->cur_adc_idx]; if (spec->cur_adc && spec->cur_adc != new_adc) { /* stream is running, let's swap the current ADC */ - snd_hda_codec_setup_stream(codec, spec->cur_adc, 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, spec->cur_adc); spec->cur_adc = new_adc; snd_hda_codec_setup_stream(codec, new_adc, spec->cur_adc_stream_tag, 0, diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 33282f9..e5d97c1 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -30,6 +30,7 @@ #include #include "hda_codec.h" #include "hda_local.h" +#include "hda_patch.h" #define ALC880_FRONT_EVENT 0x01 #define ALC880_DCVOL_EVENT 0x02 @@ -97,6 +98,7 @@ enum { ALC262_SONY_ASSAMD, ALC262_BENQ_T31, ALC262_ULTRA, + ALC262_LENOVO_3000, ALC262_AUTO, ALC262_MODEL_LAST /* last tag */ }; @@ -107,6 +109,7 @@ enum { ALC268_TOSHIBA, ALC268_ACER, ALC268_DELL, + ALC268_ZEPTO, #ifdef CONFIG_SND_DEBUG ALC268_TEST, #endif @@ -195,10 +198,11 @@ enum { ALC883_LENOVO_NB0763, ALC888_LENOVO_MS7195_DIG, ALC883_HAIER_W66, - ALC888_6ST_HP, ALC888_3ST_HP, ALC888_6ST_DELL, ALC883_MITAC, + ALC883_CLEVO_M720, + ALC883_FUJITSU_PI2515, ALC883_AUTO, ALC883_MODEL_LAST, }; @@ -237,6 +241,7 @@ struct alc_spec { /* capture */ unsigned int num_adc_nids; hda_nid_t *adc_nids; + hda_nid_t *capsrc_nids; hda_nid_t dig_in_nid; /* digital-in NID; optional */ /* capture source */ @@ -270,7 +275,6 @@ struct alc_spec { /* for virtual master */ hda_nid_t vmaster_nid; - u32 vmaster_tlv[4]; #ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_loopback_check loopback; #endif @@ -290,6 +294,7 @@ struct alc_config_preset { hda_nid_t hp_nid; /* optional */ unsigned int num_adc_nids; hda_nid_t *adc_nids; + hda_nid_t *capsrc_nids; hda_nid_t dig_in_nid; unsigned int num_channel_mode; const struct hda_channel_mode *channel_mode; @@ -336,9 +341,10 @@ static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, struct alc_spec *spec = codec->spec; unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); unsigned int mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx; + hda_nid_t nid = spec->capsrc_nids ? + spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx]; return snd_hda_input_mux_put(codec, &spec->input_mux[mux_idx], ucontrol, - spec->adc_nids[adc_idx], - &spec->cur_mux[adc_idx]); + nid, &spec->cur_mux[adc_idx]); } @@ -707,6 +713,7 @@ static void setup_preset(struct alc_spec *spec, spec->num_adc_nids = preset->num_adc_nids; spec->adc_nids = preset->adc_nids; + spec->capsrc_nids = preset->capsrc_nids; spec->dig_in_nid = preset->dig_in_nid; spec->unsol_event = preset->unsol_event; @@ -741,7 +748,6 @@ static struct hda_verb alc_gpio3_init_verbs[] = { static void alc_sku_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int mute; unsigned int present; unsigned int hp_nid = spec->autocfg.hp_pins[0]; unsigned int sp_nid = spec->autocfg.speaker_pins[0]; @@ -751,16 +757,8 @@ static void alc_sku_automute(struct hda_codec *codec) present = snd_hda_codec_read(codec, hp_nid, 0, AC_VERB_GET_PIN_SENSE, 0); spec->jack_present = (present & 0x80000000) != 0; - if (spec->jack_present) { - /* mute internal speaker */ - snd_hda_codec_amp_stereo(codec, sp_nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, HDA_AMP_MUTE); - } else { - /* unmute internal speaker if necessary */ - mute = snd_hda_codec_amp_read(codec, hp_nid, 0, HDA_OUTPUT, 0); - snd_hda_codec_amp_stereo(codec, sp_nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute); - } + snd_hda_codec_write(codec, sp_nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + spec->jack_present ? 0 : PIN_OUT); } /* unsolicited event for HP jack sensing */ @@ -1319,11 +1317,19 @@ static struct snd_kcontrol_new alc880_f1734_mixer[] = { HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), { } /* end */ }; +static struct hda_input_mux alc880_f1734_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x1 }, + { "CD", 0x4 }, + }, +}; + /* * ALC880 ASUS model @@ -1516,6 +1522,11 @@ static int alc_build_controls(struct hda_codec *codec) spec->multiout.dig_out_nid); if (err < 0) return err; + err = snd_hda_create_spdif_share_sw(codec, + &spec->multiout); + if (err < 0) + return err; + spec->multiout.share_spdif = 1; } if (spec->dig_in_nid) { err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); @@ -1525,10 +1536,11 @@ static int alc_build_controls(struct hda_codec *codec) /* if we have no master control, let's create it */ if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { + unsigned int vmaster_tlv[4]; snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, - HDA_OUTPUT, spec->vmaster_tlv); + HDA_OUTPUT, vmaster_tlv); err = snd_hda_add_vmaster(codec, "Master Playback Volume", - spec->vmaster_tlv, alc_slave_vols); + vmaster_tlv, alc_slave_vols); if (err < 0) return err; } @@ -1915,6 +1927,7 @@ static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec, * HP = 0x14, speaker-out = 0x15, mic = 0x18 */ static struct hda_verb alc880_pin_f1734_init_verbs[] = { + {0x07, AC_VERB_SET_CONNECT_SEL, 0x01}, {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, {0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, @@ -1927,7 +1940,7 @@ static struct hda_verb alc880_pin_f1734_init_verbs[] = { {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, @@ -1935,6 +1948,9 @@ static struct hda_verb alc880_pin_f1734_init_verbs[] = { {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_DCVOL_EVENT}, + { } }; @@ -2318,7 +2334,8 @@ static int alc880_playback_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream); + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); } static int alc880_playback_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -2392,8 +2409,8 @@ static int alc880_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, { struct alc_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1], - 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, + spec->adc_nids[substream->number + 1]); return 0; } @@ -2498,6 +2515,7 @@ static int alc_build_pcms(struct hda_codec *codec) codec->num_pcms = 2; info = spec->pcm_rec + 1; info->name = spec->stream_name_digital; + info->pcm_type = HDA_PCM_TYPE_SPDIF; if (spec->multiout.dig_out_nid && spec->stream_digital_playback) { info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_digital_playback); @@ -3057,7 +3075,9 @@ static struct alc_config_preset alc880_presets[] = { .hp_nid = 0x02, .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), .channel_mode = alc880_2_jack_modes, - .input_mux = &alc880_capture_source, + .input_mux = &alc880_f1734_capture_source, + .unsol_event = alc880_uniwill_p53_unsol_event, + .init_hook = alc880_uniwill_p53_hp_automute, }, [ALC880_ASUS] = { .mixers = { alc880_asus_mixer }, @@ -3467,15 +3487,21 @@ static int alc880_auto_create_analog_input_ctls(struct alc_spec *spec, return 0; } -static void alc880_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type, - int dac_idx) +static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid, + unsigned int pin_type) { - /* set as output */ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); + /* unmute pin */ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); +} + +static void alc880_auto_set_output_and_unmute(struct hda_codec *codec, + hda_nid_t nid, int pin_type, + int dac_idx) +{ + alc_set_pin_output(codec, nid, pin_type); /* need the manual connection? */ if (alc880_is_multi_pin(nid)) { struct alc_spec *spec = codec->spec; @@ -3597,9 +3623,12 @@ static int alc880_parse_auto_config(struct hda_codec *codec) /* additional initialization for auto-configuration model */ static void alc880_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc880_auto_init_multi_out(codec); alc880_auto_init_extra_out(codec); alc880_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } /* @@ -4795,11 +4824,7 @@ static void alc260_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, int sel_idx) { - /* set as output */ - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_type); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); + alc_set_pin_output(codec, nid, pin_type); /* need the manual connection? */ if (nid >= 0x12) { int idx = nid - 0x12; @@ -4929,7 +4954,7 @@ static int alc260_parse_auto_config(struct hda_codec *codec) /* check whether NID 0x04 is valid */ wcap = get_wcaps(codec, 0x04); wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */ - if (wcap != AC_WID_AUD_IN) { + if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) { spec->adc_nids = alc260_adc_nids_alt; spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt); spec->mixers[spec->num_mixers] = alc260_capture_alt_mixer; @@ -4946,8 +4971,11 @@ static int alc260_parse_auto_config(struct hda_codec *codec) /* additional initialization for auto-configuration model */ static void alc260_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc260_auto_init_multi_out(codec); alc260_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -5204,6 +5232,9 @@ static hda_nid_t alc882_dac_nids[4] = { #define alc882_adc_nids alc880_adc_nids #define alc882_adc_nids_alt alc880_adc_nids_alt +static hda_nid_t alc882_capsrc_nids[3] = { 0x24, 0x23, 0x22 }; +static hda_nid_t alc882_capsrc_nids_alt[2] = { 0x23, 0x22 }; + /* input MUX */ /* FIXME: should be a matrix-type input source selection */ @@ -5226,15 +5257,11 @@ static int alc882_mux_enum_put(struct snd_kcontrol *kcontrol, struct alc_spec *spec = codec->spec; const struct hda_input_mux *imux = spec->input_mux; unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - static hda_nid_t capture_mixers[3] = { 0x24, 0x23, 0x22 }; - hda_nid_t nid; + hda_nid_t nid = spec->capsrc_nids ? + spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx]; unsigned int *cur_val = &spec->cur_mux[adc_idx]; unsigned int i, idx; - if (spec->num_adc_nids < 3) - nid = capture_mixers[adc_idx + 1]; - else - nid = capture_mixers[adc_idx]; idx = ucontrol->value.enumerated.item[0]; if (idx >= imux->num_items) idx = imux->num_items - 1; @@ -6111,6 +6138,7 @@ static struct alc_config_preset alc882_presets[] = { .dig_out_nid = ALC882_DIGOUT_NID, .num_adc_nids = ARRAY_SIZE(alc882_adc_nids), .adc_nids = alc882_adc_nids, + .capsrc_nids = alc882_capsrc_nids, .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes), .channel_mode = alc882_3ST_6ch_modes, .need_dac_fix = 1, @@ -6127,6 +6155,7 @@ static struct alc_config_preset alc882_presets[] = { .dig_out_nid = ALC882_DIGOUT_NID, .num_adc_nids = ARRAY_SIZE(alc882_adc_nids), .adc_nids = alc882_adc_nids, + .capsrc_nids = alc882_capsrc_nids, .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes), .channel_mode = alc882_3ST_6ch_modes, .need_dac_fix = 1, @@ -6182,15 +6211,11 @@ static void alc882_auto_set_output_and_unmute(struct hda_codec *codec, struct alc_spec *spec = codec->spec; int idx; + alc_set_pin_output(codec, nid, pin_type); if (spec->multiout.dac_nids[dac_idx] == 0x25) idx = 4; else idx = spec->multiout.dac_nids[dac_idx] - 2; - - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_type); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); } @@ -6219,6 +6244,9 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec) if (pin) /* connect to front */ /* use dac 0 */ alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); + pin = spec->autocfg.speaker_pins[0]; + if (pin) + alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); } #define alc882_is_input_pin(nid) alc880_is_input_pin(nid) @@ -6231,16 +6259,21 @@ static void alc882_auto_init_analog_input(struct hda_codec *codec) for (i = 0; i < AUTO_PIN_LAST; i++) { hda_nid_t nid = spec->autocfg.input_pins[i]; - if (alc882_is_input_pin(nid)) { - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - i <= AUTO_PIN_FRONT_MIC ? - PIN_VREF80 : PIN_IN); - if (nid != ALC882_PIN_CD_NID) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); + unsigned int vref; + if (!nid) + continue; + vref = PIN_IN; + if (1 /*i <= AUTO_PIN_FRONT_MIC*/) { + if (snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP) & + AC_PINCAP_VREF_80) + vref = PIN_VREF80; } + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, vref); + if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE); } } @@ -6294,9 +6327,12 @@ static int alc882_parse_auto_config(struct hda_codec *codec) /* additional initialization for auto-configuration model */ static void alc882_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc882_auto_init_multi_out(codec); alc882_auto_init_hp_out(codec); alc882_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } static int patch_alc882(struct hda_codec *codec) @@ -6372,12 +6408,14 @@ static int patch_alc882(struct hda_codec *codec) if (wcap != AC_WID_AUD_IN) { spec->adc_nids = alc882_adc_nids_alt; spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids_alt); + spec->capsrc_nids = alc882_capsrc_nids_alt; spec->mixers[spec->num_mixers] = alc882_capture_alt_mixer; spec->num_mixers++; } else { spec->adc_nids = alc882_adc_nids; spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids); + spec->capsrc_nids = alc882_capsrc_nids; spec->mixers[spec->num_mixers] = alc882_capture_mixer; spec->num_mixers++; } @@ -6412,7 +6450,7 @@ static int patch_alc882(struct hda_codec *codec) static hda_nid_t alc883_dac_nids[4] = { /* front, rear, clfe, rear_surr */ - 0x02, 0x04, 0x03, 0x05 + 0x02, 0x03, 0x04, 0x05 }; static hda_nid_t alc883_adc_nids[2] = { @@ -6420,6 +6458,8 @@ static hda_nid_t alc883_adc_nids[2] = { 0x08, 0x09, }; +static hda_nid_t alc883_capsrc_nids[2] = { 0x23, 0x22 }; + /* input MUX */ /* FIXME: should be a matrix-type input source selection */ @@ -6451,35 +6491,18 @@ static struct hda_input_mux alc883_lenovo_nb0763_capture_source = { }, }; +static struct hda_input_mux alc883_fujitsu_pi2515_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x0 }, + { "Int Mic", 0x1 }, + }, +}; + #define alc883_mux_enum_info alc_mux_enum_info #define alc883_mux_enum_get alc_mux_enum_get - -static int alc883_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - const struct hda_input_mux *imux = spec->input_mux; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - static hda_nid_t capture_mixers[2] = { 0x23, 0x22 }; - hda_nid_t nid = capture_mixers[adc_idx]; - unsigned int *cur_val = &spec->cur_mux[adc_idx]; - unsigned int i, idx; - - idx = ucontrol->value.enumerated.item[0]; - if (idx >= imux->num_items) - idx = imux->num_items - 1; - if (*cur_val == idx) - return 0; - for (i = 0; i < imux->num_items; i++) { - unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE; - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, - imux->items[i].index, - HDA_AMP_MUTE, v); - } - *cur_val = idx; - return 1; -} +/* ALC883 has the ALC882-type input selection */ +#define alc883_mux_enum_put alc882_mux_enum_put /* * 2ch mode @@ -6638,6 +6661,60 @@ static struct snd_kcontrol_new alc883_mitac_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new alc883_clevo_m720_mixer[] = { + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc883_mux_enum_info, + .get = alc883_mux_enum_get, + .put = alc883_mux_enum_put, + }, + { } /* end */ +}; + +static struct snd_kcontrol_new alc883_2ch_fujitsu_pi2515_mixer[] = { + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc883_mux_enum_info, + .get = alc883_mux_enum_get, + .put = alc883_mux_enum_put, + }, + { } /* end */ +}; + static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), @@ -6878,124 +6955,6 @@ static struct snd_kcontrol_new alc883_medion_md2_mixer[] = { { } /* end */ }; -static struct snd_kcontrol_new alc888_6st_hp_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, - }, - { } /* end */ -}; - -static struct snd_kcontrol_new alc888_3st_hp_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, - }, - { } /* end */ -}; - -static struct snd_kcontrol_new alc888_6st_dell_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, - }, - { } /* end */ -}; - static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), @@ -7171,6 +7130,35 @@ static struct hda_verb alc883_mitac_verbs[] = { { } /* end */ }; +static struct hda_verb alc883_clevo_m720_verbs[] = { + /* HP */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Int speaker */ + {0x14, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + /* enable unsolicited event */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, + + { } /* end */ +}; + +static struct hda_verb alc883_2ch_fujitsu_pi2515_verbs[] = { + /* HP */ + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Subwoofer */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + /* enable unsolicited event */ + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + + { } /* end */ +}; + static struct hda_verb alc883_tagra_verbs[] = { {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, @@ -7227,26 +7215,14 @@ static struct hda_verb alc883_haier_w66_verbs[] = { { } /* end */ }; -static struct hda_verb alc888_6st_hp_verbs[] = { - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x02}, /* Rear : output 2 (0x0e) */ - {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* CLFE : output 1 (0x0d) */ - {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, /* Side : output 3 (0x0f) */ - { } -}; - static struct hda_verb alc888_3st_hp_verbs[] = { {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */ - {0x18, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Rear : output 1 (0x0d) */ - {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, /* CLFE : output 2 (0x0e) */ + {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Rear : output 1 (0x0d) */ + {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* CLFE : output 2 (0x0e) */ { } }; static struct hda_verb alc888_6st_dell_verbs[] = { - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x02}, /* Rear : output 1 (0x0e) */ - {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* CLFE : output 2 (0x0d) */ - {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, /* Side : output 3 (0x0f) */ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, { } }; @@ -7354,6 +7330,68 @@ static void alc883_tagra_unsol_event(struct hda_codec *codec, unsigned int res) alc883_tagra_automute(codec); } +/* toggle speaker-output according to the hp-jack state */ +static void alc883_clevo_m720_hp_automute(struct hda_codec *codec) +{ + unsigned int present; + unsigned char bits; + + present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0) + & AC_PINSENSE_PRESENCE; + bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); +} + +static void alc883_clevo_m720_mic_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x18, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); +} + +static void alc883_clevo_m720_automute(struct hda_codec *codec) +{ + alc883_clevo_m720_hp_automute(codec); + alc883_clevo_m720_mic_automute(codec); +} + +static void alc883_clevo_m720_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case ALC880_HP_EVENT: + alc883_clevo_m720_hp_automute(codec); + break; + case ALC880_MIC_EVENT: + alc883_clevo_m720_mic_automute(codec); + break; + } +} + +/* toggle speaker-output according to the hp-jack state */ +static void alc883_2ch_fujitsu_pi2515_automute(struct hda_codec *codec) +{ + unsigned int present; + unsigned char bits; + + present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) + & AC_PINSENSE_PRESENCE; + bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); +} + +static void alc883_2ch_fujitsu_pi2515_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) == ALC880_HP_EVENT) + alc883_2ch_fujitsu_pi2515_automute(codec); +} + static void alc883_haier_w66_automute(struct hda_codec *codec) { unsigned int present; @@ -7587,10 +7625,11 @@ static const char *alc883_models[ALC883_MODEL_LAST] = { [ALC883_LENOVO_NB0763] = "lenovo-nb0763", [ALC888_LENOVO_MS7195_DIG] = "lenovo-ms7195-dig", [ALC883_HAIER_W66] = "haier-w66", - [ALC888_6ST_HP] = "6stack-hp", [ALC888_3ST_HP] = "3stack-hp", [ALC888_6ST_DELL] = "6stack-dell", [ALC883_MITAC] = "mitac", + [ALC883_CLEVO_M720] = "clevo-m720", + [ALC883_FUJITSU_PI2515] = "fujitsu-pi2515", [ALC883_AUTO] = "auto", }; @@ -7604,7 +7643,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG), SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP), SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP), - SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC888_6ST_HP), + SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG), SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC), @@ -7615,6 +7654,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x3783, "NEC S970", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x3fc1, "MSI", ALC883_TARGA_DIG), @@ -7627,13 +7667,17 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7267, "MSI", ALC883_3ST_6ch_DIG), SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1558, 0x0721, "Clevo laptop M720R", ALC883_CLEVO_M720), + SND_PCI_QUIRK(0x1558, 0x0722, "Clevo laptop M720SR", ALC883_CLEVO_M720), SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD), SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch), SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION), + SND_PCI_QUIRK(0x1734, 0x1108, "Fujitsu AMILO Pi2515", ALC883_FUJITSU_PI2515), SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch), SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763), SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763), @@ -7652,8 +7696,6 @@ static struct alc_config_preset alc883_presets[] = { .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, .dig_out_nid = ALC883_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .dig_in_nid = ALC883_DIGIN_NID, .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, @@ -7665,8 +7707,6 @@ static struct alc_config_preset alc883_presets[] = { .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, .dig_out_nid = ALC883_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .dig_in_nid = ALC883_DIGIN_NID, .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), .channel_mode = alc883_3ST_6ch_modes, @@ -7678,8 +7718,6 @@ static struct alc_config_preset alc883_presets[] = { .init_verbs = { alc883_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_6ch_modes), .channel_mode = alc883_3ST_6ch_modes, .need_dac_fix = 1, @@ -7691,8 +7729,6 @@ static struct alc_config_preset alc883_presets[] = { .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, .dig_out_nid = ALC883_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .dig_in_nid = ALC883_DIGIN_NID, .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), .channel_mode = alc883_sixstack_modes, @@ -7704,8 +7740,6 @@ static struct alc_config_preset alc883_presets[] = { .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, .dig_out_nid = ALC883_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), .channel_mode = alc883_3ST_6ch_modes, .need_dac_fix = 1, @@ -7719,8 +7753,6 @@ static struct alc_config_preset alc883_presets[] = { .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, .dig_out_nid = ALC883_DIGOUT_NID, - .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, @@ -7737,8 +7769,6 @@ static struct alc_config_preset alc883_presets[] = { .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, @@ -7749,8 +7779,6 @@ static struct alc_config_preset alc883_presets[] = { .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, .dig_out_nid = ALC883_DIGOUT_NID, - .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, @@ -7764,8 +7792,6 @@ static struct alc_config_preset alc883_presets[] = { alc883_medion_eapd_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_sixstack_modes), .channel_mode = alc883_sixstack_modes, .input_mux = &alc883_capture_source, @@ -7776,8 +7802,6 @@ static struct alc_config_preset alc883_presets[] = { .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, .dig_out_nid = ALC883_DIGOUT_NID, - .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, @@ -7789,19 +7813,27 @@ static struct alc_config_preset alc883_presets[] = { .init_verbs = { alc883_init_verbs, alc882_eapd_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, }, + [ALC883_CLEVO_M720] = { + .mixers = { alc883_clevo_m720_mixer }, + .init_verbs = { alc883_init_verbs, alc883_clevo_m720_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc883_clevo_m720_unsol_event, + .init_hook = alc883_clevo_m720_automute, + }, [ALC883_LENOVO_101E_2ch] = { .mixers = { alc883_lenovo_101e_2ch_mixer}, .init_verbs = { alc883_init_verbs, alc883_lenovo_101e_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_lenovo_101e_capture_source, @@ -7813,8 +7845,6 @@ static struct alc_config_preset alc883_presets[] = { .init_verbs = { alc883_init_verbs, alc883_lenovo_nb0763_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, .need_dac_fix = 1, @@ -7828,8 +7858,6 @@ static struct alc_config_preset alc883_presets[] = { .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, .dig_out_nid = ALC883_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), .channel_mode = alc883_3ST_6ch_modes, .need_dac_fix = 1, @@ -7843,47 +7871,28 @@ static struct alc_config_preset alc883_presets[] = { .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, .dig_out_nid = ALC883_DIGOUT_NID, - .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, .unsol_event = alc883_haier_w66_unsol_event, .init_hook = alc883_haier_w66_automute, - }, - [ALC888_6ST_HP] = { - .mixers = { alc888_6st_hp_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc888_6st_hp_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, - .dig_in_nid = ALC883_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), - .channel_mode = alc883_sixstack_modes, - .input_mux = &alc883_capture_source, }, [ALC888_3ST_HP] = { - .mixers = { alc888_3st_hp_mixer, alc883_chmode_mixer }, + .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, .init_verbs = { alc883_init_verbs, alc888_3st_hp_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(alc888_3st_hp_modes), .channel_mode = alc888_3st_hp_modes, .need_dac_fix = 1, .input_mux = &alc883_capture_source, }, [ALC888_6ST_DELL] = { - .mixers = { alc888_6st_dell_mixer, alc883_chmode_mixer }, + .mixers = { alc883_base_mixer, alc883_chmode_mixer }, .init_verbs = { alc883_init_verbs, alc888_6st_dell_verbs }, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, .dig_out_nid = ALC883_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .dig_in_nid = ALC883_DIGIN_NID, .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), .channel_mode = alc883_sixstack_modes, @@ -7896,14 +7905,25 @@ static struct alc_config_preset alc883_presets[] = { .init_verbs = { alc883_init_verbs, alc883_mitac_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, .unsol_event = alc883_mitac_unsol_event, .init_hook = alc883_mitac_automute, }, + [ALC883_FUJITSU_PI2515] = { + .mixers = { alc883_2ch_fujitsu_pi2515_mixer }, + .init_verbs = { alc883_init_verbs, + alc883_2ch_fujitsu_pi2515_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_fujitsu_pi2515_capture_source, + .unsol_event = alc883_2ch_fujitsu_pi2515_unsol_event, + .init_hook = alc883_2ch_fujitsu_pi2515_automute, + }, }; @@ -7918,15 +7938,11 @@ static void alc883_auto_set_output_and_unmute(struct hda_codec *codec, struct alc_spec *spec = codec->spec; int idx; + alc_set_pin_output(codec, nid, pin_type); if (spec->multiout.dac_nids[dac_idx] == 0x25) idx = 4; else idx = spec->multiout.dac_nids[dac_idx] - 2; - - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_type); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); } @@ -7955,6 +7971,9 @@ static void alc883_auto_init_hp_out(struct hda_codec *codec) if (pin) /* connect to front */ /* use dac 0 */ alc883_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); + pin = spec->autocfg.speaker_pins[0]; + if (pin) + alc883_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); } #define alc883_is_input_pin(nid) alc880_is_input_pin(nid) @@ -8006,9 +8025,12 @@ static int alc883_parse_auto_config(struct hda_codec *codec) /* additional initialization for auto-configuration model */ static void alc883_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc883_auto_init_multi_out(codec); alc883_auto_init_hp_out(codec); alc883_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } static int patch_alc883(struct hda_codec *codec) @@ -8057,10 +8079,9 @@ static int patch_alc883(struct hda_codec *codec) spec->stream_digital_playback = &alc883_pcm_digital_playback; spec->stream_digital_capture = &alc883_pcm_digital_capture; - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = alc883_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids); - } + spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids); + spec->adc_nids = alc883_adc_nids; + spec->capsrc_nids = alc883_capsrc_nids; spec->vmaster_nid = 0x0c; @@ -8085,6 +8106,8 @@ static int patch_alc883(struct hda_codec *codec) #define alc262_dac_nids alc260_dac_nids #define alc262_adc_nids alc882_adc_nids #define alc262_adc_nids_alt alc882_adc_nids_alt +#define alc262_capsrc_nids alc882_capsrc_nids +#define alc262_capsrc_nids_alt alc882_capsrc_nids_alt #define alc262_modes alc260_modes #define alc262_capture_source alc882_capture_source @@ -8585,7 +8608,8 @@ static void alc262_hippo1_unsol_event(struct hda_codec *codec, /* * fujitsu model - * 0x14 = headphone/spdif-out, 0x15 = internal speaker + * 0x14 = headphone/spdif-out, 0x15 = internal speaker, + * 0x1b = port replicator headphone out */ #define ALC_HP_EVENT 0x37 @@ -8593,6 +8617,14 @@ static void alc262_hippo1_unsol_event(struct hda_codec *codec, static struct hda_verb alc262_fujitsu_unsol_verbs[] = { {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {} +}; + +static struct hda_verb alc262_lenovo_3000_unsol_verbs[] = { + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, {} }; @@ -8633,12 +8665,16 @@ static void alc262_fujitsu_automute(struct hda_codec *codec, int force) unsigned int mute; if (force || !spec->sense_updated) { - unsigned int present; + unsigned int present_int_hp, present_dock_hp; /* need to execute and sync at first */ snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0); - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = (present & 0x80000000) != 0; + present_int_hp = snd_hda_codec_read(codec, 0x14, 0, + AC_VERB_GET_PIN_SENSE, 0); + snd_hda_codec_read(codec, 0x1B, 0, AC_VERB_SET_PIN_SENSE, 0); + present_dock_hp = snd_hda_codec_read(codec, 0x1b, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = (present_int_hp & 0x80000000) != 0; + spec->jack_present |= (present_dock_hp & 0x80000000) != 0; spec->sense_updated = 1; } if (spec->jack_present) { @@ -8672,6 +8708,46 @@ static struct hda_bind_ctls alc262_fujitsu_bind_master_vol = { }, }; +/* mute/unmute internal speaker according to the hp jack and mute state */ +static void alc262_lenovo_3000_automute(struct hda_codec *codec, int force) +{ + struct alc_spec *spec = codec->spec; + unsigned int mute; + + if (force || !spec->sense_updated) { + unsigned int present_int_hp; + /* need to execute and sync at first */ + snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0); + present_int_hp = snd_hda_codec_read(codec, 0x1b, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = (present_int_hp & 0x80000000) != 0; + spec->sense_updated = 1; + } + if (spec->jack_present) { + /* mute internal speaker */ + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, HDA_AMP_MUTE); + snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, + HDA_AMP_MUTE, HDA_AMP_MUTE); + } else { + /* unmute internal speaker if necessary */ + mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0); + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute); + snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute); + } +} + +/* unsolicited event for HP jack sensing */ +static void alc262_lenovo_3000_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) != ALC_HP_EVENT) + return; + alc262_lenovo_3000_automute(codec, 1); +} + /* bind hp and internal speaker mute (with plug check) */ static int alc262_fujitsu_master_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -8680,12 +8756,13 @@ static int alc262_fujitsu_master_sw_put(struct snd_kcontrol *kcontrol, long *valp = ucontrol->value.integer.value; int change; - change = snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0, - HDA_AMP_MUTE, - valp[0] ? 0 : HDA_AMP_MUTE); - change |= snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0, - HDA_AMP_MUTE, - valp[1] ? 0 : HDA_AMP_MUTE); + change = snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, + valp ? 0 : HDA_AMP_MUTE); + change |= snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, + HDA_AMP_MUTE, + valp ? 0 : HDA_AMP_MUTE); + if (change) alc262_fujitsu_automute(codec, 0); return change; @@ -8712,6 +8789,44 @@ static struct snd_kcontrol_new alc262_fujitsu_mixer[] = { { } /* end */ }; +/* bind hp and internal speaker mute (with plug check) */ +static int alc262_lenovo_3000_master_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + long *valp = ucontrol->value.integer.value; + int change; + + change = snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, + HDA_AMP_MUTE, + valp ? 0 : HDA_AMP_MUTE); + + if (change) + alc262_lenovo_3000_automute(codec, 0); + return change; +} + +static struct snd_kcontrol_new alc262_lenovo_3000_mixer[] = { + HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol), + { + .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 = alc262_lenovo_3000_master_sw_put, + .private_value = HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), + }, + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + /* additional init verbs for Benq laptops */ static struct hda_verb alc262_EAPD_verbs[] = { {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, @@ -8730,59 +8845,72 @@ static struct hda_verb alc262_benq_t31_EAPD_verbs[] = { /* Samsung Q1 Ultra Vista model setup */ static struct snd_kcontrol_new alc262_ultra_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Mic Boost", 0x15, 0, HDA_INPUT), { } /* end */ }; static struct hda_verb alc262_ultra_verbs[] = { - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + /* output mixer */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + /* speaker */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* HP */ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - /* Mic is on Node 0x19 */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - {0x22, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x23, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x24, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + /* internal mic */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* ADC, choose mic */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(8)}, {} }; -static struct hda_input_mux alc262_ultra_capture_source = { - .num_items = 1, - .items = { - { "Mic", 0x1 }, - }, -}; - /* mute/unmute internal speaker according to the hp jack and mute state */ static void alc262_ultra_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; unsigned int mute; - unsigned int present; - /* need to execute and sync at first */ - snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0); - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = (present & 0x80000000) != 0; - if (spec->jack_present) { - /* mute internal speaker */ - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, HDA_AMP_MUTE); - } else { - /* unmute internal speaker if necessary */ - mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0); - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute); + mute = 0; + /* auto-mute only when HP is used as HP */ + if (!spec->cur_mux[0]) { + unsigned int present; + /* need to execute and sync at first */ + snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0); + present = snd_hda_codec_read(codec, 0x15, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; + if (spec->jack_present) + mute = HDA_AMP_MUTE; } + /* mute/unmute internal speaker */ + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute); + /* mute/unmute HP */ + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute ? 0 : HDA_AMP_MUTE); } /* unsolicited event for HP jack sensing */ @@ -8794,6 +8922,45 @@ static void alc262_ultra_unsol_event(struct hda_codec *codec, alc262_ultra_automute(codec); } +static struct hda_input_mux alc262_ultra_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x1 }, + { "Headphone", 0x7 }, + }, +}; + +static int alc262_ultra_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + int ret; + + ret = alc882_mux_enum_put(kcontrol, ucontrol); + if (!ret) + return 0; + /* reprogram the HP pin as mic or HP according to the input source */ + snd_hda_codec_write_cache(codec, 0x15, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + spec->cur_mux[0] ? PIN_VREF80 : PIN_HP); + alc262_ultra_automute(codec); /* mute/unmute HP */ + return ret; +} + +static struct snd_kcontrol_new alc262_ultra_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = alc882_mux_enum_info, + .get = alc882_mux_enum_get, + .put = alc262_ultra_mux_enum_put, + }, + { } /* end */ +}; + /* 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) @@ -9185,9 +9352,12 @@ static int alc262_parse_auto_config(struct hda_codec *codec) /* init callback for auto-configuration model -- overriding the default init */ static void alc262_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc262_auto_init_multi_out(codec); alc262_auto_init_hp_out(codec); alc262_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } /* @@ -9206,6 +9376,7 @@ static const char *alc262_models[ALC262_MODEL_LAST] = { [ALC262_BENQ_T31] = "benq-t31", [ALC262_SONY_ASSAMD] = "sony-assamd", [ALC262_ULTRA] = "ultra", + [ALC262_LENOVO_3000] = "lenovo-3000", [ALC262_AUTO] = "auto", }; @@ -9241,6 +9412,8 @@ static struct snd_pci_quirk alc262_cfg_tbl[] = { SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU), SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FUJITSU), SND_PCI_QUIRK(0x144d, 0xc032, "Samsung Q1 Ultra", ALC262_ULTRA), + SND_PCI_QUIRK(0x144d, 0xc039, "Samsung Q1U EL", ALC262_ULTRA), + SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000 y410", ALC262_LENOVO_3000), SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8), SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_BENQ_T31), SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1), @@ -9390,18 +9563,32 @@ static struct alc_config_preset alc262_presets[] = { .init_hook = alc262_hippo_automute, }, [ALC262_ULTRA] = { - .mixers = { alc262_ultra_mixer }, - .init_verbs = { alc262_init_verbs, alc262_ultra_verbs }, + .mixers = { alc262_ultra_mixer, alc262_ultra_capture_mixer }, + .init_verbs = { alc262_ultra_verbs }, .num_dacs = ARRAY_SIZE(alc262_dac_nids), .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .dig_out_nid = ALC262_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc262_modes), .channel_mode = alc262_modes, .input_mux = &alc262_ultra_capture_source, + .adc_nids = alc262_adc_nids, /* ADC0 */ + .capsrc_nids = alc262_capsrc_nids, + .num_adc_nids = 1, /* single ADC */ .unsol_event = alc262_ultra_unsol_event, .init_hook = alc262_ultra_automute, }, + [ALC262_LENOVO_3000] = { + .mixers = { alc262_lenovo_3000_mixer }, + .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs, + alc262_lenovo_3000_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .dig_out_nid = ALC262_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_fujitsu_capture_source, + .unsol_event = alc262_lenovo_3000_unsol_event, + }, }; static int patch_alc262(struct hda_codec *codec) @@ -9472,12 +9659,14 @@ static int patch_alc262(struct hda_codec *codec) if (wcap != AC_WID_AUD_IN) { spec->adc_nids = alc262_adc_nids_alt; spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids_alt); + spec->capsrc_nids = alc262_capsrc_nids_alt; spec->mixers[spec->num_mixers] = alc262_capture_alt_mixer; spec->num_mixers++; } else { spec->adc_nids = alc262_adc_nids; spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids); + spec->capsrc_nids = alc262_capsrc_nids; spec->mixers[spec->num_mixers] = alc262_capture_mixer; spec->num_mixers++; } @@ -9517,6 +9706,8 @@ static hda_nid_t alc268_adc_nids_alt[1] = { 0x08 }; +static hda_nid_t alc268_capsrc_nids[2] = { 0x23, 0x24 }; + static struct snd_kcontrol_new alc268_base_mixer[] = { /* output mixer control */ HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), @@ -9529,6 +9720,22 @@ static struct snd_kcontrol_new alc268_base_mixer[] = { { } }; +/* bind Beep switches of both NID 0x0f and 0x10 */ +static struct hda_bind_ctls alc268_bind_beep_sw = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x0f, 3, 1, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x10, 3, 1, HDA_INPUT), + 0 + }, +}; + +static struct snd_kcontrol_new alc268_beep_mixer[] = { + HDA_CODEC_VOLUME("Beep Playback Volume", 0x1d, 0x0, HDA_INPUT), + HDA_BIND_SW("Beep Playback Switch", &alc268_bind_beep_sw), + { } +}; + static struct hda_verb alc268_eapd_verbs[] = { {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, @@ -9613,8 +9820,12 @@ static struct snd_kcontrol_new alc268_acer_mixer[] = { }; static struct hda_verb alc268_acer_verbs[] = { + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* internal dmic? */ + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, { } @@ -9725,7 +9936,11 @@ static struct hda_verb alc268_base_init_verbs[] = { {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + + /* set PCBEEP vol = 0, mute connections */ + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* Unmute Selector 23h,24h and set the default input to mic-in */ @@ -9764,29 +9979,17 @@ static struct hda_verb alc268_volume_init_verbs[] = { {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* set PCBEEP vol = 0 */ - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, (0xb000 | (0x00 << 8))}, + /* set PCBEEP vol = 0, mute connections */ + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, { } }; #define alc268_mux_enum_info alc_mux_enum_info #define alc268_mux_enum_get alc_mux_enum_get - -static int alc268_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - static hda_nid_t capture_mixers[3] = { 0x23, 0x24 }; - hda_nid_t nid = capture_mixers[adc_idx]; - - return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, - nid, - &spec->cur_mux[adc_idx]); -} +#define alc268_mux_enum_put alc_mux_enum_put static struct snd_kcontrol_new alc268_capture_alt_mixer[] = { HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT), @@ -9836,13 +10039,17 @@ static struct hda_input_mux alc268_capture_source = { }, }; +static struct hda_input_mux alc268_acer_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Internal Mic", 0x6 }, + { "Line", 0x2 }, + }, +}; + #ifdef CONFIG_SND_DEBUG static struct snd_kcontrol_new alc268_test_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - /* Volume widgets */ HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x02, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x03, 0x0, HDA_OUTPUT), @@ -9981,6 +10188,10 @@ static int alc268_auto_create_analog_input_ctls(struct alc_spec *spec, case 0x1c: idx1 = 3; /* CD */ break; + case 0x12: + case 0x13: + idx1 = 6; /* digital mics */ + break; default: continue; } @@ -10073,6 +10284,9 @@ static int alc268_parse_auto_config(struct hda_codec *codec) if (spec->kctl_alloc) spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + if (spec->autocfg.speaker_pins[0] != 0x1d) + spec->mixers[spec->num_mixers++] = alc268_beep_mixer; + spec->init_verbs[spec->num_init_verbs++] = alc268_volume_init_verbs; spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux; @@ -10091,10 +10305,13 @@ static int alc268_parse_auto_config(struct hda_codec *codec) /* init callback for auto-configuration model -- overriding the default init */ static void alc268_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc268_auto_init_multi_out(codec); alc268_auto_init_hp_out(codec); alc268_auto_init_mono_speaker_out(codec); alc268_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } /* @@ -10105,6 +10322,7 @@ static const char *alc268_models[ALC268_MODEL_LAST] = { [ALC268_TOSHIBA] = "toshiba", [ALC268_ACER] = "acer", [ALC268_DELL] = "dell", + [ALC268_ZEPTO] = "zepto", #ifdef CONFIG_SND_DEBUG [ALC268_TEST] = "test", #endif @@ -10122,17 +10340,20 @@ static struct snd_pci_quirk alc268_cfg_tbl[] = { SND_PCI_QUIRK(0x1179, 0xff10, "TOSHIBA A205", ALC268_TOSHIBA), SND_PCI_QUIRK(0x1179, 0xff50, "TOSHIBA A305", ALC268_TOSHIBA), SND_PCI_QUIRK(0x152d, 0x0763, "Diverse (CPR2000)", ALC268_ACER), + SND_PCI_QUIRK(0x1170, 0x0040, "ZEPTO", ALC268_ZEPTO), {} }; static struct alc_config_preset alc268_presets[] = { [ALC268_3ST] = { - .mixers = { alc268_base_mixer, alc268_capture_alt_mixer }, + .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, + alc268_beep_mixer }, .init_verbs = { alc268_base_init_verbs }, .num_dacs = ARRAY_SIZE(alc268_dac_nids), .dac_nids = alc268_dac_nids, .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, .hp_nid = 0x03, .dig_out_nid = ALC268_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc268_modes), @@ -10140,13 +10361,15 @@ static struct alc_config_preset alc268_presets[] = { .input_mux = &alc268_capture_source, }, [ALC268_TOSHIBA] = { - .mixers = { alc268_base_mixer, alc268_capture_alt_mixer }, + .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, + alc268_beep_mixer }, .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, alc268_toshiba_verbs }, .num_dacs = ARRAY_SIZE(alc268_dac_nids), .dac_nids = alc268_dac_nids, .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, .hp_nid = 0x03, .num_channel_mode = ARRAY_SIZE(alc268_modes), .channel_mode = alc268_modes, @@ -10155,22 +10378,24 @@ static struct alc_config_preset alc268_presets[] = { .init_hook = alc268_toshiba_automute, }, [ALC268_ACER] = { - .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer }, + .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer, + alc268_beep_mixer }, .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, alc268_acer_verbs }, .num_dacs = ARRAY_SIZE(alc268_dac_nids), .dac_nids = alc268_dac_nids, .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, .hp_nid = 0x02, .num_channel_mode = ARRAY_SIZE(alc268_modes), .channel_mode = alc268_modes, - .input_mux = &alc268_capture_source, + .input_mux = &alc268_acer_capture_source, .unsol_event = alc268_acer_unsol_event, .init_hook = alc268_acer_init_hook, }, [ALC268_DELL] = { - .mixers = { alc268_dell_mixer }, + .mixers = { alc268_dell_mixer, alc268_beep_mixer }, .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, alc268_dell_verbs }, .num_dacs = ARRAY_SIZE(alc268_dac_nids), @@ -10182,6 +10407,24 @@ static struct alc_config_preset alc268_presets[] = { .init_hook = alc268_dell_init_hook, .input_mux = &alc268_capture_source, }, + [ALC268_ZEPTO] = { + .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, + alc268_beep_mixer }, + .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, + alc268_toshiba_verbs }, + .num_dacs = ARRAY_SIZE(alc268_dac_nids), + .dac_nids = alc268_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), + .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, + .hp_nid = 0x03, + .dig_out_nid = ALC268_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .input_mux = &alc268_capture_source, + .unsol_event = alc268_toshiba_unsol_event, + .init_hook = alc268_toshiba_automute + }, #ifdef CONFIG_SND_DEBUG [ALC268_TEST] = { .mixers = { alc268_test_mixer, alc268_capture_mixer }, @@ -10191,6 +10434,7 @@ static struct alc_config_preset alc268_presets[] = { .dac_nids = alc268_dac_nids, .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, .hp_nid = 0x03, .dig_out_nid = ALC268_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc268_modes), @@ -10247,13 +10491,22 @@ static int patch_alc268(struct hda_codec *codec) spec->stream_name_digital = "ALC268 Digital"; spec->stream_digital_playback = &alc268_pcm_digital_playback; + if (!query_amp_caps(codec, 0x1d, HDA_INPUT)) + /* override the amp caps for beep generator */ + snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT, + (0x0c << AC_AMPCAP_OFFSET_SHIFT) | + (0x0c << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x07 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (0 << AC_AMPCAP_MUTE_SHIFT)); + if (!spec->adc_nids && spec->input_mux) { /* check whether NID 0x07 is valid */ unsigned int wcap = get_wcaps(codec, 0x07); + int i; /* get type */ wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; - if (wcap != AC_WID_AUD_IN) { + if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) { spec->adc_nids = alc268_adc_nids_alt; spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt); spec->mixers[spec->num_mixers] = @@ -10266,6 +10519,12 @@ static int patch_alc268(struct hda_codec *codec) alc268_capture_mixer; spec->num_mixers++; } + spec->capsrc_nids = alc268_capsrc_nids; + /* set default input source */ + for (i = 0; i < spec->num_adc_nids; i++) + snd_hda_codec_write_cache(codec, alc268_capsrc_nids[i], + 0, AC_VERB_SET_CONNECT_SEL, + spec->input_mux->items[0].index); } spec->vmaster_nid = 0x02; @@ -10539,9 +10798,12 @@ static int alc269_parse_auto_config(struct hda_codec *codec) /* init callback for auto-configuration model -- overriding the default init */ static void alc269_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc269_auto_init_multi_out(codec); alc269_auto_init_hp_out(codec); alc269_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } /* @@ -11463,13 +11725,7 @@ static void alc861_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, int dac_idx) { - /* set as output */ - - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_type); - snd_hda_codec_write(codec, dac_idx, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - + alc_set_pin_output(codec, nid, pin_type); } static void alc861_auto_init_multi_out(struct hda_codec *codec) @@ -11496,6 +11752,9 @@ static void alc861_auto_init_hp_out(struct hda_codec *codec) if (pin) /* connect to front */ alc861_auto_set_output_and_unmute(codec, pin, PIN_HP, spec->multiout.dac_nids[0]); + pin = spec->autocfg.speaker_pins[0]; + if (pin) + alc861_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); } static void alc861_auto_init_analog_input(struct hda_codec *codec) @@ -11568,9 +11827,12 @@ static int alc861_parse_auto_config(struct hda_codec *codec) /* additional initialization for auto-configuration model */ static void alc861_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc861_auto_init_multi_out(codec); alc861_auto_init_hp_out(codec); alc861_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -11822,6 +12084,8 @@ static hda_nid_t alc861vd_adc_nids[1] = { 0x09, }; +static hda_nid_t alc861vd_capsrc_nids[1] = { 0x22 }; + /* input MUX */ /* FIXME: should be a matrix-type input source selection */ static struct hda_input_mux alc861vd_capture_source = { @@ -11835,11 +12099,10 @@ static struct hda_input_mux alc861vd_capture_source = { }; static struct hda_input_mux alc861vd_dallas_capture_source = { - .num_items = 3, + .num_items = 2, .items = { - { "Front Mic", 0x0 }, - { "ATAPI Mic", 0x1 }, - { "Line In", 0x5 }, + { "Ext Mic", 0x0 }, + { "Int Mic", 0x1 }, }, }; @@ -11853,33 +12116,8 @@ static struct hda_input_mux alc861vd_hp_capture_source = { #define alc861vd_mux_enum_info alc_mux_enum_info #define alc861vd_mux_enum_get alc_mux_enum_get - -static int alc861vd_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - const struct hda_input_mux *imux = spec->input_mux; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - static hda_nid_t capture_mixers[1] = { 0x22 }; - hda_nid_t nid = capture_mixers[adc_idx]; - unsigned int *cur_val = &spec->cur_mux[adc_idx]; - unsigned int i, idx; - - idx = ucontrol->value.enumerated.item[0]; - if (idx >= imux->num_items) - idx = imux->num_items - 1; - if (*cur_val == idx) - return 0; - for (i = 0; i < imux->num_items; i++) { - unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE; - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, - imux->items[i].index, - HDA_AMP_MUTE, v); - } - *cur_val = idx; - return 1; -} +/* ALC861VD has the ALC882-type input selection (but has only one ADC) */ +#define alc861vd_mux_enum_put alc882_mux_enum_put /* * 2ch mode @@ -12034,20 +12272,22 @@ static struct snd_kcontrol_new alc861vd_lenovo_mixer[] = { { } /* end */ }; -/* Pin assignment: Front=0x14, HP = 0x15, - * Front Mic=0x18, ATAPI Mic = 0x19, Line In = 0x1d +/* Pin assignment: Speaker=0x14, HP = 0x15, + * Ext Mic=0x18, Int Mic = 0x19, CD = 0x1c, PC Beep = 0x1d */ static struct snd_kcontrol_new alc861vd_dallas_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 2, HDA_INPUT), HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_VOLUME("Ext Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("PC Beep Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Beep Switch", 0x0b, 0x05, HDA_INPUT), { } /* end */ }; @@ -12348,6 +12588,7 @@ static struct snd_pci_quirk alc861vd_cfg_tbl[] = { /*SND_PCI_QUIRK(0x1179, 0xff00, "DALLAS", ALC861VD_DALLAS),*/ /*lenovo*/ SND_PCI_QUIRK(0x1179, 0xff01, "DALLAS", ALC861VD_DALLAS), SND_PCI_QUIRK(0x1179, 0xff03, "Toshiba P205", ALC861VD_LENOVO), + SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_DALLAS), SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG), SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO), SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO), @@ -12362,8 +12603,6 @@ static struct alc_config_preset alc861vd_presets[] = { alc861vd_3stack_init_verbs }, .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), .dac_nids = alc660vd_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids), - .adc_nids = alc861vd_adc_nids, .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), .channel_mode = alc861vd_3stack_2ch_modes, .input_mux = &alc861vd_capture_source, @@ -12375,8 +12614,6 @@ static struct alc_config_preset alc861vd_presets[] = { .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), .dac_nids = alc660vd_dac_nids, .dig_out_nid = ALC861VD_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids), - .adc_nids = alc861vd_adc_nids, .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), .channel_mode = alc861vd_3stack_2ch_modes, .input_mux = &alc861vd_capture_source, @@ -12421,8 +12658,6 @@ static struct alc_config_preset alc861vd_presets[] = { alc861vd_lenovo_unsol_verbs }, .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), .dac_nids = alc660vd_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids), - .adc_nids = alc861vd_adc_nids, .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), .channel_mode = alc861vd_3stack_2ch_modes, .input_mux = &alc861vd_capture_source, @@ -12434,8 +12669,6 @@ static struct alc_config_preset alc861vd_presets[] = { .init_verbs = { alc861vd_dallas_verbs }, .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), .dac_nids = alc861vd_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids), - .adc_nids = alc861vd_adc_nids, .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), .channel_mode = alc861vd_3stack_2ch_modes, .input_mux = &alc861vd_dallas_capture_source, @@ -12447,9 +12680,7 @@ static struct alc_config_preset alc861vd_presets[] = { .init_verbs = { alc861vd_dallas_verbs, alc861vd_eapd_verbs }, .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), .dac_nids = alc861vd_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids), .dig_out_nid = ALC861VD_DIGOUT_NID, - .adc_nids = alc861vd_adc_nids, .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), .channel_mode = alc861vd_3stack_2ch_modes, .input_mux = &alc861vd_hp_capture_source, @@ -12464,11 +12695,7 @@ static struct alc_config_preset alc861vd_presets[] = { static void alc861vd_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, int dac_idx) { - /* set as output */ - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + alc_set_pin_output(codec, nid, pin_type); } static void alc861vd_auto_init_multi_out(struct hda_codec *codec) @@ -12495,6 +12722,9 @@ static void alc861vd_auto_init_hp_out(struct hda_codec *codec) pin = spec->autocfg.hp_pins[0]; if (pin) /* connect to front and use dac 0 */ alc861vd_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); + pin = spec->autocfg.speaker_pins[0]; + if (pin) + alc861vd_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); } #define alc861vd_is_input_pin(nid) alc880_is_input_pin(nid) @@ -12698,9 +12928,12 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec) /* additional initialization for auto-configuration model */ static void alc861vd_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc861vd_auto_init_multi_out(codec); alc861vd_auto_init_hp_out(codec); alc861vd_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } static int patch_alc861vd(struct hda_codec *codec) @@ -12751,6 +12984,7 @@ static int patch_alc861vd(struct hda_codec *codec) spec->adc_nids = alc861vd_adc_nids; spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids); + spec->capsrc_nids = alc861vd_capsrc_nids; spec->mixers[spec->num_mixers] = alc861vd_capture_mixer; spec->num_mixers++; @@ -12792,9 +13026,11 @@ static hda_nid_t alc662_adc_nids[1] = { /* ADC1-2 */ 0x09, }; + +static hda_nid_t alc662_capsrc_nids[1] = { 0x22 }; + /* input MUX */ /* FIXME: should be a matrix-type input source selection */ - static struct hda_input_mux alc662_capture_source = { .num_items = 4, .items = { @@ -12823,33 +13059,8 @@ static struct hda_input_mux alc662_eeepc_capture_source = { #define alc662_mux_enum_info alc_mux_enum_info #define alc662_mux_enum_get alc_mux_enum_get +#define alc662_mux_enum_put alc882_mux_enum_put -static int alc662_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - const struct hda_input_mux *imux = spec->input_mux; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - static hda_nid_t capture_mixers[2] = { 0x23, 0x22 }; - hda_nid_t nid = capture_mixers[adc_idx]; - unsigned int *cur_val = &spec->cur_mux[adc_idx]; - unsigned int i, idx; - - idx = ucontrol->value.enumerated.item[0]; - if (idx >= imux->num_items) - idx = imux->num_items - 1; - if (*cur_val == idx) - return 0; - for (i = 0; i < imux->num_items; i++) { - unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE; - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, - imux->items[i].index, - HDA_AMP_MUTE, v); - } - *cur_val = idx; - return 1; -} /* * 2ch mode */ @@ -12918,13 +13129,13 @@ static struct hda_channel_mode alc662_5stack_modes[2] = { static struct snd_kcontrol_new alc662_base_mixer[] = { /* output mixer control */ HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Surround Playback Volume", 0x3, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Surround Playback Switch", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x0d, 0x0, HDA_INPUT), HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x04, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x04, 2, 2, HDA_INPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), /*Input mixer control */ @@ -12941,7 +13152,7 @@ static struct snd_kcontrol_new alc662_base_mixer[] = { static struct snd_kcontrol_new alc662_3ST_2ch_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), @@ -12958,13 +13169,13 @@ static struct snd_kcontrol_new alc662_3ST_2ch_mixer[] = { static struct snd_kcontrol_new alc662_3ST_6ch_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x03, 2, HDA_INPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x0d, 0x0, HDA_INPUT), HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x04, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x04, 2, 2, HDA_INPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), @@ -13313,6 +13524,7 @@ static const char *alc662_models[ALC662_MODEL_LAST] = { }; static struct snd_pci_quirk alc662_cfg_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x8290, "ASUS P5GC-MX", ALC662_3ST_6ch_DIG), SND_PCI_QUIRK(0x1043, 0x82a1, "ASUS Eeepc", ALC662_ASUS_EEEPC_P701), SND_PCI_QUIRK(0x1043, 0x82d1, "ASUS Eeepc EP20", ALC662_ASUS_EEEPC_EP20), SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E), @@ -13326,8 +13538,6 @@ static struct alc_config_preset alc662_presets[] = { .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, .dig_out_nid = ALC662_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), - .adc_nids = alc662_adc_nids, .dig_in_nid = ALC662_DIGIN_NID, .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), .channel_mode = alc662_3ST_2ch_modes, @@ -13340,8 +13550,6 @@ static struct alc_config_preset alc662_presets[] = { .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, .dig_out_nid = ALC662_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), - .adc_nids = alc662_adc_nids, .dig_in_nid = ALC662_DIGIN_NID, .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), .channel_mode = alc662_3ST_6ch_modes, @@ -13354,8 +13562,6 @@ static struct alc_config_preset alc662_presets[] = { .init_verbs = { alc662_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), - .adc_nids = alc662_adc_nids, .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), .channel_mode = alc662_3ST_6ch_modes, .need_dac_fix = 1, @@ -13368,8 +13574,6 @@ static struct alc_config_preset alc662_presets[] = { .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, .dig_out_nid = ALC662_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), - .adc_nids = alc662_adc_nids, .dig_in_nid = ALC662_DIGIN_NID, .num_channel_mode = ARRAY_SIZE(alc662_5stack_modes), .channel_mode = alc662_5stack_modes, @@ -13380,8 +13584,6 @@ static struct alc_config_preset alc662_presets[] = { .init_verbs = { alc662_init_verbs, alc662_sue_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), - .adc_nids = alc662_adc_nids, .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), .channel_mode = alc662_3ST_2ch_modes, .input_mux = &alc662_lenovo_101e_capture_source, @@ -13394,8 +13596,6 @@ static struct alc_config_preset alc662_presets[] = { alc662_eeepc_sue_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids), - .adc_nids = alc662_adc_nids, .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), .channel_mode = alc662_3ST_2ch_modes, .input_mux = &alc662_eeepc_capture_source, @@ -13409,8 +13609,6 @@ static struct alc_config_preset alc662_presets[] = { alc662_eeepc_ep20_sue_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), - .adc_nids = alc662_adc_nids, .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), .channel_mode = alc662_3ST_6ch_modes, .input_mux = &alc662_lenovo_101e_capture_source, @@ -13556,11 +13754,7 @@ static void alc662_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, int dac_idx) { - /* set as output */ - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + alc_set_pin_output(codec, nid, pin_type); /* need the manual connection? */ if (alc880_is_multi_pin(nid)) { struct alc_spec *spec = codec->spec; @@ -13595,6 +13789,9 @@ static void alc662_auto_init_hp_out(struct hda_codec *codec) if (pin) /* connect to front */ /* use dac 0 */ alc662_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); + pin = spec->autocfg.speaker_pins[0]; + if (pin) + alc662_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); } #define alc662_is_input_pin(nid) alc880_is_input_pin(nid) @@ -13672,9 +13869,12 @@ static int alc662_parse_auto_config(struct hda_codec *codec) /* additional initialization for auto-configuration model */ static void alc662_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc662_auto_init_multi_out(codec); alc662_auto_init_hp_out(codec); alc662_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } static int patch_alc662(struct hda_codec *codec) @@ -13722,10 +13922,9 @@ static int patch_alc662(struct hda_codec *codec) spec->stream_digital_playback = &alc662_pcm_digital_playback; spec->stream_digital_capture = &alc662_pcm_digital_capture; - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = alc662_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids); - } + spec->adc_nids = alc662_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids); + spec->capsrc_nids = alc662_capsrc_nids; spec->vmaster_nid = 0x02; diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c index d22f5a6..9332b63 100644 --- a/sound/pci/hda/patch_si3054.c +++ b/sound/pci/hda/patch_si3054.c @@ -28,7 +28,7 @@ #include #include "hda_codec.h" #include "hda_local.h" - +#include "hda_patch.h" /* si3054 verbs */ #define SI3054_VERB_READ_NODE 0x900 @@ -206,7 +206,7 @@ static int si3054_build_pcms(struct hda_codec *codec) info->name = "Si3054 Modem"; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = si3054_pcm; info->stream[SNDRV_PCM_STREAM_CAPTURE] = si3054_pcm; - info->is_modem = 1; + info->pcm_type = HDA_PCM_TYPE_MODEM; return 0; } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index caf48ed..69cd3b2 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -32,6 +32,7 @@ #include #include "hda_codec.h" #include "hda_local.h" +#include "hda_patch.h" #define NUM_CONTROL_ALLOC 32 #define STAC_PWR_EVENT 0x20 @@ -39,6 +40,7 @@ enum { STAC_REF, + STAC_9200_OQO, STAC_9200_DELL_D21, STAC_9200_DELL_D22, STAC_9200_DELL_D23, @@ -50,6 +52,7 @@ enum { STAC_9200_DELL_M26, STAC_9200_DELL_M27, STAC_9200_GATEWAY, + STAC_9200_PANASONIC, STAC_9200_MODELS }; @@ -63,11 +66,14 @@ enum { enum { STAC_92HD73XX_REF, + STAC_DELL_M6, STAC_92HD73XX_MODELS }; enum { STAC_92HD71BXX_REF, + STAC_DELL_M4_1, + STAC_DELL_M4_2, STAC_92HD71BXX_MODELS }; @@ -135,6 +141,7 @@ struct sigmatel_spec { /* power management */ unsigned int num_pwrs; hda_nid_t *pwr_nids; + hda_nid_t *dac_list; /* playback */ struct hda_input_mux *mono_mux; @@ -184,9 +191,6 @@ struct sigmatel_spec { struct hda_input_mux private_dimux; struct hda_input_mux private_imux; struct hda_input_mux private_mono_mux; - - /* virtual master */ - unsigned int vmaster_tlv[4]; }; static hda_nid_t stac9200_adc_nids[1] = { @@ -244,7 +248,7 @@ static hda_nid_t stac92hd71bxx_dmux_nids[1] = { 0x1c, }; -static hda_nid_t stac92hd71bxx_dac_nids[2] = { +static hda_nid_t stac92hd71bxx_dac_nids[1] = { 0x10, /*0x11, */ }; @@ -290,6 +294,10 @@ static hda_nid_t stac927x_mux_nids[3] = { 0x15, 0x16, 0x17 }; +static hda_nid_t stac927x_dac_nids[6] = { + 0x02, 0x03, 0x04, 0x05, 0x06, 0 +}; + static hda_nid_t stac927x_dmux_nids[1] = { 0x1b, }; @@ -331,10 +339,10 @@ static hda_nid_t stac922x_pin_nids[10] = { 0x0f, 0x10, 0x11, 0x15, 0x1b, }; -static hda_nid_t stac92hd73xx_pin_nids[12] = { +static hda_nid_t stac92hd73xx_pin_nids[13] = { 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, - 0x14, 0x22 + 0x14, 0x1e, 0x22 }; static hda_nid_t stac92hd71bxx_pin_nids[10] = { @@ -527,6 +535,43 @@ static struct hda_verb stac92hd73xx_6ch_core_init[] = { {} }; +static struct hda_verb dell_eq_core_init[] = { + /* set master volume to max value without distortion + * and direct control */ + { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec}, + /* setup audio connections */ + { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00}, + { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* setup adcs to point to mixer */ + { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, + /* setup import muxs */ + { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00}, + {} +}; + +static struct hda_verb dell_m6_core_init[] = { + /* set master volume and direct control */ + { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* setup audio connections */ + { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00}, + { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* setup adcs to point to mixer */ + { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, + /* setup import muxs */ + { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00}, + {} +}; + static struct hda_verb stac92hd73xx_8ch_core_init[] = { /* set master volume and direct control */ { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, @@ -910,6 +955,11 @@ static int stac92xx_build_controls(struct hda_codec *codec) err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); if (err < 0) return err; + err = snd_hda_create_spdif_share_sw(codec, + &spec->multiout); + if (err < 0) + return err; + spec->multiout.share_spdif = 1; } if (spec->dig_in_nid) { err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); @@ -919,10 +969,11 @@ static int stac92xx_build_controls(struct hda_codec *codec) /* if we have no master control, let's create it */ if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { + unsigned int vmaster_tlv[4]; snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0], - HDA_OUTPUT, spec->vmaster_tlv); + HDA_OUTPUT, vmaster_tlv); err = snd_hda_add_vmaster(codec, "Master Playback Volume", - spec->vmaster_tlv, slave_vols); + vmaster_tlv, slave_vols); if (err < 0) return err; } @@ -1052,9 +1103,15 @@ static unsigned int dell9200_m27_pin_configs[8] = { 0x90170310, 0x04a11020, 0x90170310, 0x40f003fc, }; +static unsigned int oqo9200_pin_configs[8] = { + 0x40c000f0, 0x404000f1, 0x0221121f, 0x02211210, + 0x90170111, 0x90a70120, 0x400000f2, 0x400000f3, +}; + static unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = { [STAC_REF] = ref9200_pin_configs, + [STAC_9200_OQO] = oqo9200_pin_configs, [STAC_9200_DELL_D21] = dell9200_d21_pin_configs, [STAC_9200_DELL_D22] = dell9200_d22_pin_configs, [STAC_9200_DELL_D23] = dell9200_d23_pin_configs, @@ -1065,10 +1122,12 @@ static unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = { [STAC_9200_DELL_M25] = dell9200_m25_pin_configs, [STAC_9200_DELL_M26] = dell9200_m26_pin_configs, [STAC_9200_DELL_M27] = dell9200_m27_pin_configs, + [STAC_9200_PANASONIC] = ref9200_pin_configs, }; static const char *stac9200_models[STAC_9200_MODELS] = { [STAC_REF] = "ref", + [STAC_9200_OQO] = "oqo", [STAC_9200_DELL_D21] = "dell-d21", [STAC_9200_DELL_D22] = "dell-d22", [STAC_9200_DELL_D23] = "dell-d23", @@ -1080,6 +1139,7 @@ static const char *stac9200_models[STAC_9200_MODELS] = { [STAC_9200_DELL_M26] = "dell-m26", [STAC_9200_DELL_M27] = "dell-m27", [STAC_9200_GATEWAY] = "gateway", + [STAC_9200_PANASONIC] = "panasonic", }; static struct snd_pci_quirk stac9200_cfg_tbl[] = { @@ -1146,13 +1206,15 @@ static struct snd_pci_quirk stac9200_cfg_tbl[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f6, "unknown Dell", STAC_9200_DELL_M26), /* Panasonic */ - SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-74", STAC_REF), + SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-74", STAC_9200_PANASONIC), /* Gateway machines needs EAPD to be set on resume */ SND_PCI_QUIRK(0x107b, 0x0205, "Gateway S-7110M", STAC_9200_GATEWAY), SND_PCI_QUIRK(0x107b, 0x0317, "Gateway MT3423, MX341*", STAC_9200_GATEWAY), SND_PCI_QUIRK(0x107b, 0x0318, "Gateway ML3019, MT3707", STAC_9200_GATEWAY), + /* OQO Mobile */ + SND_PCI_QUIRK(0x1106, 0x3288, "OQO Model 2", STAC_9200_OQO), {} /* terminator */ }; @@ -1202,24 +1264,48 @@ static struct snd_pci_quirk stac925x_cfg_tbl[] = { {} /* terminator */ }; -static unsigned int ref92hd73xx_pin_configs[12] = { +static unsigned int ref92hd73xx_pin_configs[13] = { 0x02214030, 0x02a19040, 0x01a19020, 0x02214030, 0x0181302e, 0x01014010, 0x01014020, 0x01014030, 0x02319040, 0x90a000f0, 0x90a000f0, 0x01452050, + 0x01452050, +}; + +static unsigned int dell_m6_pin_configs[13] = { + 0x0321101f, 0x4f00000f, 0x4f0000f0, 0x90170110, + 0x03a11020, 0x03011050, 0x4f0000f0, 0x4f0000f0, + 0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0, + 0x4f0000f0, }; static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = { - [STAC_92HD73XX_REF] = ref92hd73xx_pin_configs, + [STAC_92HD73XX_REF] = ref92hd73xx_pin_configs, + [STAC_DELL_M6] = dell_m6_pin_configs, }; static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = { [STAC_92HD73XX_REF] = "ref", + [STAC_DELL_M6] = "dell-m6", }; static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, - "DFI LanParty", STAC_92HD73XX_REF), + "DFI LanParty", STAC_92HD73XX_REF), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0254, + "unknown Dell", STAC_DELL_M6), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0255, + "unknown Dell", STAC_DELL_M6), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0256, + "unknown Dell", STAC_DELL_M6), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0257, + "unknown Dell", STAC_DELL_M6), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x025e, + "unknown Dell", STAC_DELL_M6), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x025f, + "unknown Dell", STAC_DELL_M6), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0271, + "unknown Dell", STAC_DELL_M6), {} /* terminator */ }; @@ -1229,18 +1315,56 @@ static unsigned int ref92hd71bxx_pin_configs[10] = { 0x90a000f0, 0x01452050, }; +static unsigned int dell_m4_1_pin_configs[13] = { + 0x0421101f, 0x04a11221, 0x40f000f0, 0x90170110, + 0x23a1902e, 0x23014250, 0x40f000f0, 0x90a000f0, + 0x40f000f0, 0x4f0000f0, +}; + +static unsigned int dell_m4_2_pin_configs[13] = { + 0x0421101f, 0x04a11221, 0x90a70330, 0x90170110, + 0x23a1902e, 0x23014250, 0x40f000f0, 0x40f000f0, + 0x40f000f0, 0x044413b0, +}; + static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = { [STAC_92HD71BXX_REF] = ref92hd71bxx_pin_configs, + [STAC_DELL_M4_1] = dell_m4_1_pin_configs, + [STAC_DELL_M4_2] = dell_m4_2_pin_configs, }; static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = { [STAC_92HD71BXX_REF] = "ref", + [STAC_DELL_M4_1] = "dell-m4-1", + [STAC_DELL_M4_2] = "dell-m4-2", }; static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_92HD71BXX_REF), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233, + "unknown Dell", STAC_DELL_M4_1), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234, + "unknown Dell", STAC_DELL_M4_1), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0250, + "unknown Dell", STAC_DELL_M4_1), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x024f, + "unknown Dell", STAC_DELL_M4_1), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x024d, + "unknown Dell", STAC_DELL_M4_1), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0251, + "unknown Dell", STAC_DELL_M4_1), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0277, + "unknown Dell", STAC_DELL_M4_1), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0263, + "unknown Dell", STAC_DELL_M4_2), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0265, + "unknown Dell", STAC_DELL_M4_2), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0262, + "unknown Dell", STAC_DELL_M4_2), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0264, + "unknown Dell", STAC_DELL_M4_2), {} /* terminator */ }; @@ -1733,7 +1857,8 @@ static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct sigmatel_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream); + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); } static int stac92xx_playback_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -1807,7 +1932,7 @@ static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, { struct sigmatel_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); return 0; } @@ -1889,6 +2014,7 @@ static int stac92xx_build_pcms(struct hda_codec *codec) codec->num_pcms++; info++; info->name = "STAC92xx Digital"; + info->pcm_type = HDA_PCM_TYPE_SPDIF; if (spec->multiout.dig_out_nid) { info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_digital_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; @@ -2222,6 +2348,29 @@ static int create_controls(struct sigmatel_spec *spec, const char *pfx, hda_nid_ return 0; } +static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid) +{ + if (!spec->multiout.hp_nid) + spec->multiout.hp_nid = nid; + else if (spec->multiout.num_dacs > 4) { + printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid); + return 1; + } else { + spec->multiout.dac_nids[spec->multiout.num_dacs] = nid; + spec->multiout.num_dacs++; + } + return 0; +} + +static int check_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid) +{ + if (is_in_dac_nids(spec, nid)) + return 1; + if (spec->multiout.hp_nid == nid) + return 1; + return 0; +} + /* add playback controls from the parsed DAC table */ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) @@ -2236,7 +2385,7 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, unsigned int wid_caps, pincap; - for (i = 0; i < cfg->line_outs; i++) { + for (i = 0; i < cfg->line_outs && i < spec->multiout.num_dacs; i++) { if (!spec->multiout.dac_nids[i]) continue; @@ -2284,10 +2433,11 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, if (spec->mic_switch) { unsigned int def_conf; - nid = cfg->input_pins[AUTO_PIN_MIC]; + unsigned int mic_pin = AUTO_PIN_MIC; +again: + nid = cfg->input_pins[mic_pin]; def_conf = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); - /* some laptops have an internal analog microphone * which can't be used as a output */ if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) { @@ -2297,38 +2447,22 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, "Mic as Output Switch", (nid << 8) | 1); + nid = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONNECT_LIST, 0) & 0xff; + if (!check_in_dac_nids(spec, nid)) + add_spec_dacs(spec, nid); if (err < 0) return err; } + } else if (mic_pin == AUTO_PIN_MIC) { + mic_pin = AUTO_PIN_FRONT_MIC; + goto again; } } return 0; } -static int check_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid) -{ - if (is_in_dac_nids(spec, nid)) - return 1; - if (spec->multiout.hp_nid == nid) - return 1; - return 0; -} - -static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid) -{ - if (!spec->multiout.hp_nid) - spec->multiout.hp_nid = nid; - else if (spec->multiout.num_dacs > 4) { - printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid); - return 1; - } else { - spec->multiout.dac_nids[spec->multiout.num_dacs] = nid; - spec->multiout.num_dacs++; - } - return 0; -} - /* add playback controls for Speaker and HP outputs */ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec, struct auto_pin_cfg *cfg) @@ -2378,12 +2512,8 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec, return err; } if (spec->multiout.hp_nid) { - const char *pfx; - if (old_num_dacs == spec->multiout.num_dacs) - pfx = "Master"; - else - pfx = "Headphone"; - err = create_controls(spec, pfx, spec->multiout.hp_nid, 3); + err = create_controls(spec, "Headphone", + spec->multiout.hp_nid, 3); if (err < 0) return err; } @@ -2745,7 +2875,7 @@ static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec, */ 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); + unsigned int 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 */ @@ -2756,12 +2886,12 @@ static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec, 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, + unsigned int defcfg; + defcfg = 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); + if (get_defcfg_device(defcfg) == AC_JACK_SPEAKER) { + unsigned int 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, @@ -2866,6 +2996,19 @@ static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid) return 0; /* nid is not a HP-Out */ }; +static void stac92xx_power_down(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + + /* power down inactive DACs */ + hda_nid_t *dac; + for (dac = spec->dac_list; *dac; dac++) + if (!is_in_dac_nids(spec, *dac) && + spec->multiout.hp_nid != *dac) + snd_hda_codec_write_cache(codec, *dac, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); +} + static int stac92xx_init(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -2909,16 +3052,21 @@ static int stac92xx_init(struct hda_codec *codec) ? STAC_HP_EVENT : STAC_PWR_EVENT; int pinctl = snd_hda_codec_read(codec, spec->pwr_nids[i], 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + int def_conf = snd_hda_codec_read(codec, spec->pwr_nids[i], + 0, AC_VERB_GET_CONFIG_DEFAULT, 0); /* outputs are only ports capable of power management * any attempts on powering down a input port cause the * referenced VREF to act quirky. */ if (pinctl & AC_PINCTL_IN_EN) continue; + if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) + continue; enable_pin_detect(codec, spec->pwr_nids[i], event | i); codec->patch_ops.unsol_event(codec, (event | i) << 26); } - + if (spec->dac_list) + stac92xx_power_down(codec); if (cfg->dig_out_pin) stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin, AC_PINCTL_OUT_EN); @@ -3091,6 +3239,9 @@ static int stac92xx_resume(struct hda_codec *codec) spec->gpio_dir, spec->gpio_data); snd_hda_codec_resume_amp(codec); snd_hda_codec_resume_cache(codec); + /* power down inactive DACs */ + if (spec->dac_list) + stac92xx_power_down(codec); /* invoke unsolicited event to reset the HP state */ if (spec->hp_detect) codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26); @@ -3147,12 +3298,18 @@ static int patch_stac9200(struct hda_codec *codec) spec->num_adcs = 1; spec->num_pwrs = 0; - if (spec->board_config == STAC_9200_GATEWAY) + if (spec->board_config == STAC_9200_GATEWAY || + spec->board_config == STAC_9200_OQO) spec->init = stac9200_eapd_init; else spec->init = stac9200_core_init; spec->mixer = stac9200_mixer; + if (spec->board_config == STAC_9200_PANASONIC) { + spec->gpio_mask = spec->gpio_dir = 0x09; + spec->gpio_data = 0x00; + } + err = stac9200_parse_auto_config(codec); if (err < 0) { stac92xx_free(codec); @@ -3318,13 +3475,42 @@ again: spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids); spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids); - spec->num_dmics = STAC92HD73XX_NUM_DMICS; spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids); spec->dinput_mux = &stac92hd73xx_dmux; /* GPIO0 High = Enable EAPD */ spec->gpio_mask = spec->gpio_dir = 0x1; spec->gpio_data = 0x01; + switch (spec->board_config) { + case STAC_DELL_M6: + spec->init = dell_eq_core_init; + switch (codec->subsystem_id) { + case 0x1028025e: /* Analog Mics */ + case 0x1028025f: + stac92xx_set_config_reg(codec, 0x0b, 0x90A70170); + spec->num_dmics = 0; + break; + case 0x10280271: /* Digital Mics */ + case 0x10280272: + spec->init = dell_m6_core_init; + /* fall-through */ + case 0x10280254: + case 0x10280255: + stac92xx_set_config_reg(codec, 0x13, 0x90A60160); + spec->num_dmics = 1; + break; + case 0x10280256: /* Both */ + case 0x10280057: + stac92xx_set_config_reg(codec, 0x0b, 0x90A70170); + stac92xx_set_config_reg(codec, 0x13, 0x90A60160); + spec->num_dmics = 1; + break; + } + break; + default: + spec->num_dmics = STAC92HD73XX_NUM_DMICS; + } + spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids); spec->pwr_nids = stac92hd73xx_pwr_nids; @@ -3413,7 +3599,7 @@ again: spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids); spec->pwr_nids = stac92hd71bxx_pwr_nids; - spec->multiout.num_dacs = 2; + spec->multiout.num_dacs = 1; spec->multiout.hp_nid = 0x11; spec->multiout.dac_nids = stac92hd71bxx_dac_nids; @@ -3577,6 +3763,7 @@ static int patch_stac927x(struct hda_codec *codec) spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids); spec->mux_nids = stac927x_mux_nids; spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids); + spec->dac_list = stac927x_dac_nids; spec->multiout.dac_nids = spec->dac_nids; switch (spec->board_config) { @@ -3591,8 +3778,10 @@ static int patch_stac927x(struct hda_codec *codec) spec->mixer = stac927x_mixer; break; case STAC_DELL_BIOS: + /* configure the analog microphone on some laptops */ + stac92xx_set_config_reg(codec, 0x0c, 0x90a79130); /* correct the front output jack as a hp out */ - stac92xx_set_config_reg(codec, 0x0f, 0x02270110); + stac92xx_set_config_reg(codec, 0x0f, 0x0227011f); /* correct the front input jack as a mic */ stac92xx_set_config_reg(codec, 0x0e, 0x02a79130); /* fallthru */ diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 4e5dd4c..52b1d81 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -39,7 +39,7 @@ #include #include "hda_codec.h" #include "hda_local.h" - +#include "hda_patch.h" /* amp values */ #define AMP_VAL_IDX_SHIFT 19 @@ -357,7 +357,8 @@ static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream); + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); } static int via_playback_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -430,8 +431,7 @@ static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], - 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); return 0; } @@ -493,6 +493,11 @@ static int via_build_controls(struct hda_codec *codec) spec->multiout.dig_out_nid); if (err < 0) return err; + err = snd_hda_create_spdif_share_sw(codec, + &spec->multiout); + if (err < 0) + return err; + spec->multiout.share_spdif = 1; } if (spec->dig_in_nid) { err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); @@ -523,6 +528,7 @@ static int via_build_pcms(struct hda_codec *codec) codec->num_pcms++; info++; info->name = spec->stream_name_digital; + info->pcm_type = HDA_PCM_TYPE_SPDIF; if (spec->multiout.dig_out_nid) { info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_digital_playback); diff --git a/sound/pci/hda/vmaster.c b/sound/pci/hda/vmaster.c deleted file mode 100644 index 2da49d2..0000000 --- a/sound/pci/hda/vmaster.c +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Virtual master and slave controls - * - * Copyright (c) 2008 by Takashi Iwai - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#include -#include -#include - -/* - * a subset of information returned via ctl info callback - */ -struct link_ctl_info { - int type; /* value type */ - int count; /* item count */ - int min_val, max_val; /* min, max values */ -}; - -/* - * link master - this contains a list of slave controls that are - * identical types, i.e. info returns the same value type and value - * ranges, but may have different number of counts. - * - * The master control is so far only mono volume/switch for simplicity. - * The same value will be applied to all slaves. - */ -struct link_master { - struct list_head slaves; - struct link_ctl_info info; - int val; /* the master value */ -}; - -/* - * link slave - this contains a slave control element - * - * It fakes the control callbacsk with additional attenuation by the - * master control. A slave may have either one or two channels. - */ - -struct link_slave { - struct list_head list; - struct link_master *master; - struct link_ctl_info info; - int vals[2]; /* current values */ - struct snd_kcontrol slave; /* the copy of original control entry */ -}; - -/* get the slave ctl info and save the initial values */ -static int slave_init(struct link_slave *slave) -{ - struct snd_ctl_elem_info *uinfo; - struct snd_ctl_elem_value *uctl; - int err, ch; - - if (slave->info.count) - return 0; /* already initialized */ - - uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL); - if (!uinfo) - return -ENOMEM; - uinfo->id = slave->slave.id; - err = slave->slave.info(&slave->slave, uinfo); - if (err < 0) { - kfree(uinfo); - return err; - } - slave->info.type = uinfo->type; - slave->info.count = uinfo->count; - if (slave->info.count > 2 || - (slave->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER && - slave->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) { - snd_printk(KERN_ERR "invalid slave element\n"); - kfree(uinfo); - return -EINVAL; - } - slave->info.min_val = uinfo->value.integer.min; - slave->info.max_val = uinfo->value.integer.max; - kfree(uinfo); - - uctl = kmalloc(sizeof(*uctl), GFP_KERNEL); - if (!uctl) - return -ENOMEM; - uctl->id = slave->slave.id; - err = slave->slave.get(&slave->slave, uctl); - for (ch = 0; ch < slave->info.count; ch++) - slave->vals[ch] = uctl->value.integer.value[ch]; - kfree(uctl); - return 0; -} - -/* initialize master volume */ -static int master_init(struct link_master *master) -{ - struct link_slave *slave; - - if (master->info.count) - return 0; /* already initialized */ - - list_for_each_entry(slave, &master->slaves, list) { - int err = slave_init(slave); - if (err < 0) - return err; - master->info = slave->info; - master->info.count = 1; /* always mono */ - /* set full volume as default (= no attenuation) */ - master->val = master->info.max_val; - return 0; - } - return -ENOENT; -} - -static int slave_get_val(struct link_slave *slave, - struct snd_ctl_elem_value *ucontrol) -{ - int err, ch; - - err = slave_init(slave); - if (err < 0) - return err; - for (ch = 0; ch < slave->info.count; ch++) - ucontrol->value.integer.value[ch] = slave->vals[ch]; - return 0; -} - -static int slave_put_val(struct link_slave *slave, - struct snd_ctl_elem_value *ucontrol) -{ - int err, ch, vol; - - err = master_init(slave->master); - if (err < 0) - return err; - - switch (slave->info.type) { - case SNDRV_CTL_ELEM_TYPE_BOOLEAN: - for (ch = 0; ch < slave->info.count; ch++) - ucontrol->value.integer.value[ch] &= - !!slave->master->val; - break; - case SNDRV_CTL_ELEM_TYPE_INTEGER: - for (ch = 0; ch < slave->info.count; ch++) { - /* max master volume is supposed to be 0 dB */ - vol = ucontrol->value.integer.value[ch]; - vol += slave->master->val - slave->master->info.max_val; - if (vol < slave->info.min_val) - vol = slave->info.min_val; - else if (vol > slave->info.max_val) - vol = slave->info.max_val; - ucontrol->value.integer.value[ch] = vol; - } - break; - } - return slave->slave.put(&slave->slave, ucontrol); -} - -/* - * ctl callbacks for slaves - */ -static int slave_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct link_slave *slave = snd_kcontrol_chip(kcontrol); - return slave->slave.info(&slave->slave, uinfo); -} - -static int slave_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct link_slave *slave = snd_kcontrol_chip(kcontrol); - return slave_get_val(slave, ucontrol); -} - -static int slave_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct link_slave *slave = snd_kcontrol_chip(kcontrol); - int err, ch, changed = 0; - - err = slave_init(slave); - if (err < 0) - return err; - for (ch = 0; ch < slave->info.count; ch++) { - if (slave->vals[ch] != ucontrol->value.integer.value[ch]) { - changed = 1; - slave->vals[ch] = ucontrol->value.integer.value[ch]; - } - } - if (!changed) - return 0; - return slave_put_val(slave, ucontrol); -} - -static int slave_tlv_cmd(struct snd_kcontrol *kcontrol, - int op_flag, unsigned int size, - unsigned int __user *tlv) -{ - struct link_slave *slave = snd_kcontrol_chip(kcontrol); - /* FIXME: this assumes that the max volume is 0 dB */ - return slave->slave.tlv.c(&slave->slave, op_flag, size, tlv); -} - -static void slave_free(struct snd_kcontrol *kcontrol) -{ - struct link_slave *slave = snd_kcontrol_chip(kcontrol); - if (slave->slave.private_free) - slave->slave.private_free(&slave->slave); - if (slave->master) - list_del(&slave->list); - kfree(slave); -} - -/* - * Add a slave control to the group with the given master control - * - * All slaves must be the same type (returning the same information - * via info callback). The fucntion doesn't check it, so it's your - * responsibility. - * - * Also, some additional limitations: - * - at most two channels - * - logarithmic volume control (dB level), no linear volume - * - master can only attenuate the volume, no gain - */ -int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave) -{ - struct link_master *master_link = snd_kcontrol_chip(master); - struct link_slave *srec; - - srec = kzalloc(sizeof(*srec) + - slave->count * sizeof(*slave->vd), GFP_KERNEL); - if (!srec) - return -ENOMEM; - srec->slave = *slave; - memcpy(srec->slave.vd, slave->vd, slave->count * sizeof(*slave->vd)); - srec->master = master_link; - - /* override callbacks */ - slave->info = slave_info; - slave->get = slave_get; - slave->put = slave_put; - if (slave->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) - slave->tlv.c = slave_tlv_cmd; - slave->private_data = srec; - slave->private_free = slave_free; - - list_add_tail(&srec->list, &master_link->slaves); - return 0; -} - -/* - * ctl callbacks for master controls - */ -static int master_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct link_master *master = snd_kcontrol_chip(kcontrol); - int ret; - - ret = master_init(master); - if (ret < 0) - return ret; - uinfo->type = master->info.type; - uinfo->count = master->info.count; - uinfo->value.integer.min = master->info.min_val; - uinfo->value.integer.max = master->info.max_val; - return 0; -} - -static int master_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct link_master *master = snd_kcontrol_chip(kcontrol); - int err = master_init(master); - if (err < 0) - return err; - ucontrol->value.integer.value[0] = master->val; - return 0; -} - -static int master_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct link_master *master = snd_kcontrol_chip(kcontrol); - struct link_slave *slave; - struct snd_ctl_elem_value *uval; - int err, old_val; - - err = master_init(master); - if (err < 0) - return err; - old_val = master->val; - if (ucontrol->value.integer.value[0] == old_val) - return 0; - - uval = kmalloc(sizeof(*uval), GFP_KERNEL); - if (!uval) - return -ENOMEM; - list_for_each_entry(slave, &master->slaves, list) { - master->val = old_val; - uval->id = slave->slave.id; - slave_get_val(slave, uval); - master->val = ucontrol->value.integer.value[0]; - slave_put_val(slave, uval); - } - kfree(uval); - return 1; -} - -static void master_free(struct snd_kcontrol *kcontrol) -{ - struct link_master *master = snd_kcontrol_chip(kcontrol); - struct link_slave *slave; - - list_for_each_entry(slave, &master->slaves, list) - slave->master = NULL; - kfree(master); -} - - -/* - * Create a virtual master control with the given name - */ -struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, - const unsigned int *tlv) -{ - struct link_master *master; - struct snd_kcontrol *kctl; - struct snd_kcontrol_new knew; - - memset(&knew, 0, sizeof(knew)); - knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - knew.name = name; - knew.info = master_info; - - master = kzalloc(sizeof(*master), GFP_KERNEL); - if (!master) - return NULL; - INIT_LIST_HEAD(&master->slaves); - - kctl = snd_ctl_new1(&knew, master); - if (!kctl) { - kfree(master); - return NULL; - } - /* override some callbacks */ - kctl->info = master_info; - kctl->get = master_get; - kctl->put = master_put; - kctl->private_free = master_free; - - /* additional (constant) TLV read */ - if (tlv) { - /* FIXME: this assumes that the max volume is 0 dB */ - kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; - kctl->tlv.p = tlv; - } - return kctl; -} diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c index efd180b..0ed96c1 100644 --- a/sound/pci/ice1712/delta.c +++ b/sound/pci/ice1712/delta.c @@ -1,8 +1,8 @@ /* * ALSA driver for ICEnsemble ICE1712 (Envy24) * - * Lowlevel functions for M-Audio Delta 1010, 44, 66, Dio2496, Audiophile - * Digigram VX442 + * Lowlevel functions for M-Audio Delta 1010, 1010E, 44, 66, 66E, Dio2496, + * Audiophile, Digigram VX442 * * Copyright (c) 2000 Jaroslav Kysela * @@ -86,6 +86,7 @@ static unsigned char ap_cs8427_codec_select(struct snd_ice1712 *ice) unsigned char tmp; tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA); switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010E: case ICE1712_SUBDEVICE_DELTA1010LT: tmp &= ~ICE1712_DELTA_1010LT_CS; tmp |= ICE1712_DELTA_1010LT_CCLK | ICE1712_DELTA_1010LT_CS_CS8427; @@ -109,6 +110,7 @@ static unsigned char ap_cs8427_codec_select(struct snd_ice1712 *ice) static void ap_cs8427_codec_deassert(struct snd_ice1712 *ice, unsigned char tmp) { switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010E: case ICE1712_SUBDEVICE_DELTA1010LT: tmp &= ~ICE1712_DELTA_1010LT_CS; tmp |= ICE1712_DELTA_1010LT_CS_NONE; @@ -534,6 +536,14 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) int err; struct snd_akm4xxx *ak; + if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DELTA1010 && + ice->eeprom.gpiodir == 0x7b) + ice->eeprom.subvendor = ICE1712_SUBDEVICE_DELTA1010E; + + if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DELTA66 && + ice->eeprom.gpiodir == 0xfb) + ice->eeprom.subvendor = ICE1712_SUBDEVICE_DELTA66E; + /* determine I2C, DACs and ADCs */ switch (ice->eeprom.subvendor) { case ICE1712_SUBDEVICE_AUDIOPHILE: @@ -550,6 +560,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) ice->num_total_adcs = ice->omni ? 8 : 4; break; case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTA1010E: case ICE1712_SUBDEVICE_DELTA1010LT: case ICE1712_SUBDEVICE_MEDIASTATION: ice->num_total_dacs = 8; @@ -559,6 +570,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) ice->num_total_dacs = 4; /* two AK4324 codecs */ break; case ICE1712_SUBDEVICE_VX442: + case ICE1712_SUBDEVICE_DELTA66E: /* omni not suported yet */ ice->num_total_dacs = 4; ice->num_total_adcs = 4; break; @@ -568,8 +580,10 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) switch (ice->eeprom.subvendor) { case ICE1712_SUBDEVICE_AUDIOPHILE: case ICE1712_SUBDEVICE_DELTA410: + case ICE1712_SUBDEVICE_DELTA1010E: case ICE1712_SUBDEVICE_DELTA1010LT: case ICE1712_SUBDEVICE_VX442: + case ICE1712_SUBDEVICE_DELTA66E: if ((err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c)) < 0) { snd_printk(KERN_ERR "unable to create I2C bus\n"); return err; @@ -601,6 +615,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) /* no analog? */ switch (ice->eeprom.subvendor) { case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTA1010E: case ICE1712_SUBDEVICE_DELTADIO2496: case ICE1712_SUBDEVICE_MEDIASTATION: return 0; @@ -627,6 +642,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) err = snd_ice1712_akm4xxx_init(ak, &akm_delta44, &akm_delta44_priv, ice); break; case ICE1712_SUBDEVICE_VX442: + case ICE1712_SUBDEVICE_DELTA66E: err = snd_ice1712_akm4xxx_init(ak, &akm_vx442, &akm_vx442_priv, ice); break; default: @@ -674,6 +690,7 @@ static int __devinit snd_ice1712_delta_add_controls(struct snd_ice1712 *ice) if (err < 0) return err; break; + case ICE1712_SUBDEVICE_DELTA1010E: case ICE1712_SUBDEVICE_DELTA1010LT: err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010lt_wordclock_select, ice)); if (err < 0) @@ -716,6 +733,7 @@ static int __devinit snd_ice1712_delta_add_controls(struct snd_ice1712 *ice) case ICE1712_SUBDEVICE_DELTA44: case ICE1712_SUBDEVICE_DELTA66: case ICE1712_SUBDEVICE_VX442: + case ICE1712_SUBDEVICE_DELTA66E: err = snd_ice1712_akm4xxx_build_controls(ice); if (err < 0) return err; diff --git a/sound/pci/ice1712/delta.h b/sound/pci/ice1712/delta.h index 26ea05a..ea7116c 100644 --- a/sound/pci/ice1712/delta.h +++ b/sound/pci/ice1712/delta.h @@ -36,8 +36,10 @@ "{Lionstracs,Mediastation}," #define ICE1712_SUBDEVICE_DELTA1010 0x121430d6 +#define ICE1712_SUBDEVICE_DELTA1010E 0xff1430d6 #define ICE1712_SUBDEVICE_DELTADIO2496 0x121431d6 #define ICE1712_SUBDEVICE_DELTA66 0x121432d6 +#define ICE1712_SUBDEVICE_DELTA66E 0xff1432d6 #define ICE1712_SUBDEVICE_DELTA44 0x121433d6 #define ICE1712_SUBDEVICE_AUDIOPHILE 0x121434d6 #define ICE1712_SUBDEVICE_DELTA410 0x121438d6 diff --git a/sound/pci/ice1712/hoontech.c b/sound/pci/ice1712/hoontech.c index cf5c7c0..6914189 100644 --- a/sound/pci/ice1712/hoontech.c +++ b/sound/pci/ice1712/hoontech.c @@ -208,6 +208,19 @@ static int __devinit snd_ice1712_hoontech_init(struct snd_ice1712 *ice) /* ICE1712_STDSP24_MUTE | ICE1712_STDSP24_INSEL | ICE1712_STDSP24_DAREAR; */ + /* These boxconfigs have caused problems in the past. + * The code is not optimal, but should now enable a working config to + * be achieved. + * ** MIDI IN can only be configured on one box ** + * ICE1712_STDSP24_BOX_MIDI1 needs to be set for that box. + * Tests on a ADAC2000 box suggest the box config flags do not + * work as would be expected, and the inputs are crossed. + * Setting ICE1712_STDSP24_BOX_MIDI1 and ICE1712_STDSP24_BOX_MIDI2 + * on the same box connects MIDI-In to both 401 uarts; both outputs + * are then active on all boxes. + * The default config here sets up everything on the first box. + * Alan Horstmann 5.2.2008 + */ spec->boxconfig[0] = ICE1712_STDSP24_BOX_CHN1 | ICE1712_STDSP24_BOX_CHN2 | ICE1712_STDSP24_BOX_CHN3 | @@ -223,14 +236,14 @@ static int __devinit snd_ice1712_hoontech_init(struct snd_ice1712 *ice) (spec->config & ICE1712_STDSP24_MUTE) ? 1 : 0); snd_ice1712_stdsp24_insel(ice, (spec->config & ICE1712_STDSP24_INSEL) ? 1 : 0); - for (box = 0; box < 1; box++) { + for (box = 0; box < 4; box++) { if (spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI2) snd_ice1712_stdsp24_midi2(ice, 1); for (chn = 0; chn < 4; chn++) snd_ice1712_stdsp24_box_channel(ice, box, chn, (spec->boxconfig[box] & (1 << chn)) ? 1 : 0); - snd_ice1712_stdsp24_box_midi(ice, box, - (spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1) ? 1 : 0); + if (spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1) + snd_ice1712_stdsp24_box_midi(ice, box, 1); } return 0; @@ -322,6 +335,8 @@ struct snd_ice1712_card_info snd_ice1712_hoontech_cards[] __devinitdata = { .name = "Hoontech SoundTrack Audio DSP24", .model = "dsp24", .chip_init = snd_ice1712_hoontech_init, + .mpu401_1_name = "MIDI-1 Hoontech/STA DSP24", + .mpu401_2_name = "MIDI-2 Hoontech/STA DSP24", }, { .subvendor = ICE1712_SUBDEVICE_STDSP24_VALUE, /* a dummy id */ diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index df292af..38e93ca 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -1297,11 +1297,14 @@ static void snd_ice1712_update_volume(struct snd_ice1712 *ice, int index) static int snd_ice1712_pro_mixer_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - int index = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + kcontrol->private_value; + int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + + kcontrol->private_value; spin_lock_irq(&ice->reg_lock); - ucontrol->value.integer.value[0] = !((ice->pro_volumes[index] >> 15) & 1); - ucontrol->value.integer.value[1] = !((ice->pro_volumes[index] >> 31) & 1); + ucontrol->value.integer.value[0] = + !((ice->pro_volumes[priv_idx] >> 15) & 1); + ucontrol->value.integer.value[1] = + !((ice->pro_volumes[priv_idx] >> 31) & 1); spin_unlock_irq(&ice->reg_lock); return 0; } @@ -1309,16 +1312,17 @@ static int snd_ice1712_pro_mixer_switch_get(struct snd_kcontrol *kcontrol, struc static int snd_ice1712_pro_mixer_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - int index = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + kcontrol->private_value; + int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + + kcontrol->private_value; unsigned int nval, change; nval = (ucontrol->value.integer.value[0] ? 0 : 0x00008000) | (ucontrol->value.integer.value[1] ? 0 : 0x80000000); spin_lock_irq(&ice->reg_lock); - nval |= ice->pro_volumes[index] & ~0x80008000; - change = nval != ice->pro_volumes[index]; - ice->pro_volumes[index] = nval; - snd_ice1712_update_volume(ice, index); + nval |= ice->pro_volumes[priv_idx] & ~0x80008000; + change = nval != ice->pro_volumes[priv_idx]; + ice->pro_volumes[priv_idx] = nval; + snd_ice1712_update_volume(ice, priv_idx); spin_unlock_irq(&ice->reg_lock); return change; } @@ -1335,11 +1339,14 @@ static int snd_ice1712_pro_mixer_volume_info(struct snd_kcontrol *kcontrol, stru static int snd_ice1712_pro_mixer_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - int index = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + kcontrol->private_value; + int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + + kcontrol->private_value; spin_lock_irq(&ice->reg_lock); - ucontrol->value.integer.value[0] = (ice->pro_volumes[index] >> 0) & 127; - ucontrol->value.integer.value[1] = (ice->pro_volumes[index] >> 16) & 127; + ucontrol->value.integer.value[0] = + (ice->pro_volumes[priv_idx] >> 0) & 127; + ucontrol->value.integer.value[1] = + (ice->pro_volumes[priv_idx] >> 16) & 127; spin_unlock_irq(&ice->reg_lock); return 0; } @@ -1347,16 +1354,17 @@ static int snd_ice1712_pro_mixer_volume_get(struct snd_kcontrol *kcontrol, struc static int snd_ice1712_pro_mixer_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - int index = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + kcontrol->private_value; + int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + + kcontrol->private_value; unsigned int nval, change; nval = (ucontrol->value.integer.value[0] & 127) | ((ucontrol->value.integer.value[1] & 127) << 16); spin_lock_irq(&ice->reg_lock); - nval |= ice->pro_volumes[index] & ~0x007f007f; - change = nval != ice->pro_volumes[index]; - ice->pro_volumes[index] = nval; - snd_ice1712_update_volume(ice, index); + nval |= ice->pro_volumes[priv_idx] & ~0x007f007f; + change = nval != ice->pro_volumes[priv_idx]; + ice->pro_volumes[priv_idx] = nval; + snd_ice1712_update_volume(ice, priv_idx); spin_unlock_irq(&ice->reg_lock); return change; } diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h index 303cffe..a3bea22 100644 --- a/sound/pci/ice1712/ice1712.h +++ b/sound/pci/ice1712/ice1712.h @@ -367,6 +367,15 @@ struct snd_ice1712 { /* other board-specific data */ void *spec; + + /* VT172x specific */ + int pro_rate_default; + int (*is_spdif_master)(struct snd_ice1712 *ice); + unsigned int (*get_rate)(struct snd_ice1712 *ice); + void (*set_rate)(struct snd_ice1712 *ice, unsigned int rate); + unsigned char (*set_mclk)(struct snd_ice1712 *ice, unsigned int rate); + void (*set_spdif_clock)(struct snd_ice1712 *ice); + }; diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index f533850..ceac870 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -106,15 +106,19 @@ static unsigned int PRO_RATE_DEFAULT = 44100; * Basic I/O */ +/* + * default rates, default clock routines + */ + /* check whether the clock mode is spdif-in */ -static inline int is_spdif_master(struct snd_ice1712 *ice) +static inline int stdclock_is_spdif_master(struct snd_ice1712 *ice) { return (inb(ICEMT1724(ice, RATE)) & VT1724_SPDIF_MASTER) ? 1 : 0; } static inline int is_pro_rate_locked(struct snd_ice1712 *ice) { - return is_spdif_master(ice) || PRO_RATE_LOCKED; + return ice->is_spdif_master(ice) || PRO_RATE_LOCKED; } /* @@ -391,51 +395,61 @@ static int snd_vt1724_pcm_trigger(struct snd_pcm_substream *substream, int cmd) #define DMA_PAUSES (VT1724_RDMA0_PAUSE|VT1724_PDMA0_PAUSE|VT1724_RDMA1_PAUSE|\ VT1724_PDMA1_PAUSE|VT1724_PDMA2_PAUSE|VT1724_PDMA3_PAUSE|VT1724_PDMA4_PAUSE) -static int get_max_rate(struct snd_ice1712 *ice) +static const unsigned int stdclock_rate_list[16] = { + 48000, 24000, 12000, 9600, 32000, 16000, 8000, 96000, 44100, + 22050, 11025, 88200, 176400, 0, 192000, 64000 +}; + +static unsigned int stdclock_get_rate(struct snd_ice1712 *ice) { + unsigned int rate; + rate = stdclock_rate_list[inb(ICEMT1724(ice, RATE)) & 15]; + return rate; +} + +static void stdclock_set_rate(struct snd_ice1712 *ice, unsigned int rate) +{ + int i; + for (i = 0; i < ARRAY_SIZE(stdclock_rate_list); i++) { + if (stdclock_rate_list[i] == rate) { + outb(i, ICEMT1724(ice, RATE)); + return; + } + } +} + +static unsigned char stdclock_set_mclk(struct snd_ice1712 *ice, + unsigned int rate) +{ + unsigned char val, old; + /* check MT02 */ if (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S) { - if ((ice->eeprom.data[ICE_EEP2_I2S] & 0x08) && !ice->vt1720) - return 192000; + val = old = inb(ICEMT1724(ice, I2S_FORMAT)); + if (rate > 96000) + val |= VT1724_MT_I2S_MCLK_128X; /* 128x MCLK */ else - return 96000; - } else - return 48000; + val &= ~VT1724_MT_I2S_MCLK_128X; /* 256x MCLK */ + if (val != old) { + outb(val, ICEMT1724(ice, I2S_FORMAT)); + /* master clock changed */ + return 1; + } + } + /* no change in master clock */ + return 0; } static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate, int force) { unsigned long flags; - unsigned char val, old; - unsigned int i, mclk_change; + unsigned char mclk_change; + unsigned int i, old_rate; - if (rate > get_max_rate(ice)) + if (rate > ice->hw_rates->list[ice->hw_rates->count - 1]) return; - - switch (rate) { - case 8000: val = 6; break; - case 9600: val = 3; break; - case 11025: val = 10; break; - case 12000: val = 2; break; - case 16000: val = 5; break; - case 22050: val = 9; break; - case 24000: val = 1; break; - case 32000: val = 4; break; - case 44100: val = 8; break; - case 48000: val = 0; break; - case 64000: val = 15; break; - case 88200: val = 11; break; - case 96000: val = 7; break; - case 176400: val = 12; break; - case 192000: val = 14; break; - default: - snd_BUG(); - val = 0; - break; - } - spin_lock_irqsave(&ice->reg_lock, flags); - if ((inb(ICEMT1724(ice, DMA_CONTROL)) & DMA_STARTS) || + if ((inb(ICEMT1724(ice, DMA_CONTROL)) & DMA_STARTS) || (inb(ICEMT1724(ice, DMA_PAUSE)) & DMA_PAUSES)) { /* running? we cannot change the rate now... */ spin_unlock_irqrestore(&ice->reg_lock, flags); @@ -446,9 +460,9 @@ static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate, return; } - old = inb(ICEMT1724(ice, RATE)); - if (force || old != val) - outb(val, ICEMT1724(ice, RATE)); + old_rate = ice->get_rate(ice); + if (force || (old_rate != rate)) + ice->set_rate(ice, rate); else if (rate == ice->cur_rate) { spin_unlock_irqrestore(&ice->reg_lock, flags); return; @@ -456,19 +470,9 @@ static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate, ice->cur_rate = rate; - /* check MT02 */ - mclk_change = 0; - if (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S) { - val = old = inb(ICEMT1724(ice, I2S_FORMAT)); - if (rate > 96000) - val |= VT1724_MT_I2S_MCLK_128X; /* 128x MCLK */ - else - val &= ~VT1724_MT_I2S_MCLK_128X; /* 256x MCLK */ - if (val != old) { - outb(val, ICEMT1724(ice, I2S_FORMAT)); - mclk_change = 1; - } - } + /* setting master clock */ + mclk_change = ice->set_mclk(ice, rate); + spin_unlock_irqrestore(&ice->reg_lock, flags); if (mclk_change && ice->gpio.i2s_mclk_changed) @@ -727,43 +731,32 @@ static const struct snd_pcm_hardware snd_vt1724_2ch_stereo = /* * set rate constraints */ -static int set_rate_constraints(struct snd_ice1712 *ice, - struct snd_pcm_substream *substream) +static void set_std_hw_rates(struct snd_ice1712 *ice) { - struct snd_pcm_runtime *runtime = substream->runtime; - if (ice->hw_rates) { - /* hardware specific */ - runtime->hw.rate_min = ice->hw_rates->list[0]; - runtime->hw.rate_max = ice->hw_rates->list[ice->hw_rates->count - 1]; - runtime->hw.rates = SNDRV_PCM_RATE_KNOT; - return snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - ice->hw_rates); - } if (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S) { /* I2S */ /* VT1720 doesn't support more than 96kHz */ if ((ice->eeprom.data[ICE_EEP2_I2S] & 0x08) && !ice->vt1720) - return snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &hw_constraints_rates_192); - else { - runtime->hw.rates = SNDRV_PCM_RATE_KNOT | - SNDRV_PCM_RATE_8000_96000; - runtime->hw.rate_max = 96000; - return snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &hw_constraints_rates_96); - } - } else if (ice->ac97) { + ice->hw_rates = &hw_constraints_rates_192; + else + ice->hw_rates = &hw_constraints_rates_96; + } else { /* ACLINK */ - runtime->hw.rate_max = 48000; - runtime->hw.rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000; - return snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &hw_constraints_rates_48); + ice->hw_rates = &hw_constraints_rates_48; } - return 0; +} + +static int set_rate_constraints(struct snd_ice1712 *ice, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw.rate_min = ice->hw_rates->list[0]; + runtime->hw.rate_max = ice->hw_rates->list[ice->hw_rates->count - 1]; + runtime->hw.rates = SNDRV_PCM_RATE_KNOT; + return snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + ice->hw_rates); } /* multi-channel playback needs alignment 8x32bit regardless of the channels @@ -824,7 +817,7 @@ static int snd_vt1724_playback_pro_close(struct snd_pcm_substream *substream) struct snd_ice1712 *ice = snd_pcm_substream_chip(substream); if (PRO_RATE_RESET) - snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0); + snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0); ice->playback_pro_substream = NULL; return 0; @@ -835,7 +828,7 @@ static int snd_vt1724_capture_pro_close(struct snd_pcm_substream *substream) struct snd_ice1712 *ice = snd_pcm_substream_chip(substream); if (PRO_RATE_RESET) - snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0); + snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0); ice->capture_pro_substream = NULL; return 0; } @@ -970,6 +963,8 @@ static int snd_vt1724_playback_spdif_open(struct snd_pcm_substream *substream) VT1724_BUFFER_ALIGN); snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, VT1724_BUFFER_ALIGN); + if (ice->spdif.ops.open) + ice->spdif.ops.open(ice, substream); return 0; } @@ -978,8 +973,10 @@ static int snd_vt1724_playback_spdif_close(struct snd_pcm_substream *substream) struct snd_ice1712 *ice = snd_pcm_substream_chip(substream); if (PRO_RATE_RESET) - snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0); + snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0); ice->playback_con_substream = NULL; + if (ice->spdif.ops.close) + ice->spdif.ops.close(ice, substream); return 0; } @@ -1002,6 +999,8 @@ static int snd_vt1724_capture_spdif_open(struct snd_pcm_substream *substream) VT1724_BUFFER_ALIGN); snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, VT1724_BUFFER_ALIGN); + if (ice->spdif.ops.open) + ice->spdif.ops.open(ice, substream); return 0; } @@ -1010,8 +1009,10 @@ static int snd_vt1724_capture_spdif_close(struct snd_pcm_substream *substream) struct snd_ice1712 *ice = snd_pcm_substream_chip(substream); if (PRO_RATE_RESET) - snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0); + snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0); ice->capture_con_substream = NULL; + if (ice->spdif.ops.close) + ice->spdif.ops.close(ice, substream); return 0; } @@ -1154,7 +1155,7 @@ static int snd_vt1724_playback_indep_close(struct snd_pcm_substream *substream) struct snd_ice1712 *ice = snd_pcm_substream_chip(substream); if (PRO_RATE_RESET) - snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0); + snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0); ice->playback_con_substream_ds[substream->number] = NULL; ice->pcm_reserved[substream->number] = NULL; @@ -1572,50 +1573,18 @@ int snd_ice1712_gpio_put(struct snd_kcontrol *kcontrol, static int snd_vt1724_pro_internal_clock_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static const char * const texts_1724[] = { - "8000", /* 0: 6 */ - "9600", /* 1: 3 */ - "11025", /* 2: 10 */ - "12000", /* 3: 2 */ - "16000", /* 4: 5 */ - "22050", /* 5: 9 */ - "24000", /* 6: 1 */ - "32000", /* 7: 4 */ - "44100", /* 8: 8 */ - "48000", /* 9: 0 */ - "64000", /* 10: 15 */ - "88200", /* 11: 11 */ - "96000", /* 12: 7 */ - "176400", /* 13: 12 */ - "192000", /* 14: 14 */ - "IEC958 Input", /* 15: -- */ - }; - static const char * const texts_1720[] = { - "8000", /* 0: 6 */ - "9600", /* 1: 3 */ - "11025", /* 2: 10 */ - "12000", /* 3: 2 */ - "16000", /* 4: 5 */ - "22050", /* 5: 9 */ - "24000", /* 6: 1 */ - "32000", /* 7: 4 */ - "44100", /* 8: 8 */ - "48000", /* 9: 0 */ - "64000", /* 10: 15 */ - "88200", /* 11: 11 */ - "96000", /* 12: 7 */ - "IEC958 Input", /* 13: -- */ - }; struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; - uinfo->value.enumerated.items = ice->vt1720 ? 14 : 16; + uinfo->value.enumerated.items = ice->hw_rates->count + 1; if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - ice->vt1720 ? texts_1720[uinfo->value.enumerated.item] : - texts_1724[uinfo->value.enumerated.item]); + if (uinfo->value.enumerated.item == uinfo->value.enumerated.items - 1) + strcpy(uinfo->value.enumerated.name, "IEC958 Input"); + else + sprintf(uinfo->value.enumerated.name, "%d", + ice->hw_rates->list[uinfo->value.enumerated.item]); return 0; } @@ -1623,68 +1592,79 @@ static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - static const unsigned char xlate[16] = { - 9, 6, 3, 1, 7, 4, 0, 12, 8, 5, 2, 11, 13, 255, 14, 10 - }; - unsigned char val; + unsigned int i, rate; spin_lock_irq(&ice->reg_lock); - if (is_spdif_master(ice)) { - ucontrol->value.enumerated.item[0] = ice->vt1720 ? 13 : 15; + if (ice->is_spdif_master(ice)) { + ucontrol->value.enumerated.item[0] = ice->hw_rates->count; } else { - val = xlate[inb(ICEMT1724(ice, RATE)) & 15]; - if (val == 255) { - snd_BUG(); - val = 0; + rate = ice->get_rate(ice); + ucontrol->value.enumerated.item[0] = 0; + for (i = 0; i < ice->hw_rates->count; i++) { + if (ice->hw_rates->list[i] == rate) { + ucontrol->value.enumerated.item[0] = i; + break; + } } - ucontrol->value.enumerated.item[0] = val; } spin_unlock_irq(&ice->reg_lock); return 0; } +/* setting clock to external - SPDIF */ +static void stdclock_set_spdif_clock(struct snd_ice1712 *ice) +{ + unsigned char oval; + unsigned char i2s_oval; + oval = inb(ICEMT1724(ice, RATE)); + outb(oval | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE)); + /* setting 256fs */ + i2s_oval = inb(ICEMT1724(ice, I2S_FORMAT)); + outb(i2s_oval & ~VT1724_MT_I2S_MCLK_128X, ICEMT1724(ice, I2S_FORMAT)); +} + static int snd_vt1724_pro_internal_clock_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - unsigned char oval; - int rate; - int change = 0; - int spdif = ice->vt1720 ? 13 : 15; + unsigned int old_rate, new_rate; + unsigned int item = ucontrol->value.enumerated.item[0]; + unsigned int spdif = ice->hw_rates->count; + + if (item > spdif) + return -EINVAL; spin_lock_irq(&ice->reg_lock); - oval = inb(ICEMT1724(ice, RATE)); - if (ucontrol->value.enumerated.item[0] == spdif) { - unsigned char i2s_oval; - outb(oval | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE)); - /* setting 256fs */ - i2s_oval = inb(ICEMT1724(ice, I2S_FORMAT)); - outb(i2s_oval & ~VT1724_MT_I2S_MCLK_128X, - ICEMT1724(ice, I2S_FORMAT)); + if (ice->is_spdif_master(ice)) + old_rate = 0; + else + old_rate = ice->get_rate(ice); + if (item == spdif) { + /* switching to external clock via SPDIF */ + ice->set_spdif_clock(ice); + new_rate = 0; } else { - rate = rates[ucontrol->value.integer.value[0] % 15]; - if (rate <= get_max_rate(ice)) { - PRO_RATE_DEFAULT = rate; - spin_unlock_irq(&ice->reg_lock); - snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 1); - spin_lock_irq(&ice->reg_lock); - } + /* internal on-card clock */ + new_rate = ice->hw_rates->list[item]; + ice->pro_rate_default = new_rate; + spin_unlock_irq(&ice->reg_lock); + snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 1); + spin_lock_irq(&ice->reg_lock); } - change = inb(ICEMT1724(ice, RATE)) != oval; spin_unlock_irq(&ice->reg_lock); - if ((oval & VT1724_SPDIF_MASTER) != - (inb(ICEMT1724(ice, RATE)) & VT1724_SPDIF_MASTER)) { + /* the first reset to the SPDIF master mode? */ + if (old_rate != new_rate && !new_rate) { /* notify akm chips as well */ - if (is_spdif_master(ice)) { - unsigned int i; - for (i = 0; i < ice->akm_codecs; i++) { - if (ice->akm[i].ops.set_rate_val) - ice->akm[i].ops.set_rate_val(&ice->akm[i], 0); - } + unsigned int i; + if (ice->gpio.set_pro_rate) + ice->gpio.set_pro_rate(ice, 0); + for (i = 0; i < ice->akm_codecs; i++) { + if (ice->akm[i].ops.set_rate_val) + ice->akm[i].ops.set_rate_val(&ice->akm[i], 0); } } - return change; + return old_rate != new_rate; } static struct snd_kcontrol_new snd_vt1724_pro_internal_clock __devinitdata = { @@ -2335,6 +2315,19 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci, * was called so in ice1712 driver, and vt1724 driver is derived from * ice1712 driver. */ + ice->pro_rate_default = PRO_RATE_DEFAULT; + if (!ice->is_spdif_master) + ice->is_spdif_master = stdclock_is_spdif_master; + if (!ice->get_rate) + ice->get_rate = stdclock_get_rate; + if (!ice->set_rate) + ice->set_rate = stdclock_set_rate; + if (!ice->set_mclk) + ice->set_mclk = stdclock_set_mclk; + if (!ice->set_spdif_clock) + ice->set_spdif_clock = stdclock_set_spdif_clock; + if (!ice->hw_rates) + set_std_hw_rates(ice); if ((err = snd_vt1724_pcm_profi(ice, pcm_dev++)) < 0) { snd_card_free(card); diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c index e8038c0..b4e0c16 100644 --- a/sound/pci/ice1712/juli.c +++ b/sound/pci/ice1712/juli.c @@ -4,6 +4,8 @@ * Lowlevel functions for ESI Juli@ cards * * Copyright (c) 2004 Jaroslav Kysela + * 2008 Pavel Hofman + * * * 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 @@ -27,11 +29,11 @@ #include #include #include +#include #include "ice1712.h" #include "envy24ht.h" #include "juli.h" - struct juli_spec { struct ak4114 *ak4114; unsigned int analog: 1; @@ -44,6 +46,32 @@ struct juli_spec { #define AK4358_ADDR 0x22 /* DAC */ /* + * Juli does not use the standard ICE1724 clock scheme. Juli's ice1724 chip is + * supplied by external clock provided by Xilinx array and MK73-1 PLL frequency + * multiplier. Actual frequency is set by ice1724 GPIOs hooked to the Xilinx. + * + * The clock circuitry is supplied by the two ice1724 crystals. This + * arrangement allows to generate independent clock signal for AK4114's input + * rate detection circuit. As a result, Juli, unlike most other + * ice1724+ak4114-based cards, detects spdif input rate correctly. + * This fact is applied in the driver, allowing to modify PCM stream rate + * parameter according to the actual input rate. + * + * Juli uses the remaining three stereo-channels of its DAC to optionally + * monitor analog input, digital input, and digital output. The corresponding + * I2S signals are routed by Xilinx, controlled by GPIOs. + * + * The master mute is implemented using output muting transistors (GPIO) in + * combination with smuting the DAC. + * + * The card itself has no HW master volume control, implemented using the + * vmaster control. + * + * TODO: + * researching and fixing the input monitors + */ + +/* * GPIO pins */ #define GPIO_FREQ_MASK (3<<0) @@ -55,17 +83,82 @@ struct juli_spec { #define GPIO_MULTI_2X (1<<2) #define GPIO_MULTI_1X (2<<2) /* also external */ #define GPIO_MULTI_HALF (3<<2) -#define GPIO_INTERNAL_CLOCK (1<<4) +#define GPIO_INTERNAL_CLOCK (1<<4) /* 0 = external, 1 = internal */ +#define GPIO_CLOCK_MASK (1<<4) #define GPIO_ANALOG_PRESENT (1<<5) /* RO only: 0 = present */ #define GPIO_RXMCLK_SEL (1<<7) /* must be 0 */ #define GPIO_AK5385A_CKS0 (1<<8) -#define GPIO_AK5385A_DFS0 (1<<9) /* swapped with DFS1 according doc? */ -#define GPIO_AK5385A_DFS1 (1<<10) +#define GPIO_AK5385A_DFS1 (1<<9) +#define GPIO_AK5385A_DFS0 (1<<10) #define GPIO_DIGOUT_MONITOR (1<<11) /* 1 = active */ #define GPIO_DIGIN_MONITOR (1<<12) /* 1 = active */ #define GPIO_ANAIN_MONITOR (1<<13) /* 1 = active */ -#define GPIO_AK5385A_MCLK (1<<14) /* must be 0 */ -#define GPIO_MUTE_CONTROL (1<<15) /* 0 = off, 1 = on */ +#define GPIO_AK5385A_CKS1 (1<<14) /* must be 0 */ +#define GPIO_MUTE_CONTROL (1<<15) /* output mute, 1 = muted */ + +#define GPIO_RATE_MASK (GPIO_FREQ_MASK | GPIO_MULTI_MASK | \ + GPIO_CLOCK_MASK) +#define GPIO_AK5385A_MASK (GPIO_AK5385A_CKS0 | GPIO_AK5385A_DFS0 | \ + GPIO_AK5385A_DFS1 | GPIO_AK5385A_CKS1) + +#define JULI_PCM_RATE (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000) + +#define GPIO_RATE_16000 (GPIO_FREQ_32KHZ | GPIO_MULTI_HALF | \ + GPIO_INTERNAL_CLOCK) +#define GPIO_RATE_22050 (GPIO_FREQ_44KHZ | GPIO_MULTI_HALF | \ + GPIO_INTERNAL_CLOCK) +#define GPIO_RATE_24000 (GPIO_FREQ_48KHZ | GPIO_MULTI_HALF | \ + GPIO_INTERNAL_CLOCK) +#define GPIO_RATE_32000 (GPIO_FREQ_32KHZ | GPIO_MULTI_1X | \ + GPIO_INTERNAL_CLOCK) +#define GPIO_RATE_44100 (GPIO_FREQ_44KHZ | GPIO_MULTI_1X | \ + GPIO_INTERNAL_CLOCK) +#define GPIO_RATE_48000 (GPIO_FREQ_48KHZ | GPIO_MULTI_1X | \ + GPIO_INTERNAL_CLOCK) +#define GPIO_RATE_64000 (GPIO_FREQ_32KHZ | GPIO_MULTI_2X | \ + GPIO_INTERNAL_CLOCK) +#define GPIO_RATE_88200 (GPIO_FREQ_44KHZ | GPIO_MULTI_2X | \ + GPIO_INTERNAL_CLOCK) +#define GPIO_RATE_96000 (GPIO_FREQ_48KHZ | GPIO_MULTI_2X | \ + GPIO_INTERNAL_CLOCK) +#define GPIO_RATE_176400 (GPIO_FREQ_44KHZ | GPIO_MULTI_4X | \ + GPIO_INTERNAL_CLOCK) +#define GPIO_RATE_192000 (GPIO_FREQ_48KHZ | GPIO_MULTI_4X | \ + GPIO_INTERNAL_CLOCK) + +/* + * Initial setup of the conversion array GPIO <-> rate + */ +static unsigned int juli_rates[] = { + 16000, 22050, 24000, 32000, + 44100, 48000, 64000, 88200, + 96000, 176400, 192000, +}; + +static unsigned int gpio_vals[] = { + GPIO_RATE_16000, GPIO_RATE_22050, GPIO_RATE_24000, GPIO_RATE_32000, + GPIO_RATE_44100, GPIO_RATE_48000, GPIO_RATE_64000, GPIO_RATE_88200, + GPIO_RATE_96000, GPIO_RATE_176400, GPIO_RATE_192000, +}; + +static struct snd_pcm_hw_constraint_list juli_rates_info = { + .count = ARRAY_SIZE(juli_rates), + .list = juli_rates, + .mask = 0, +}; + +static int get_gpio_val(int rate) +{ + int i; + for (i = 0; i < ARRAY_SIZE(juli_rates); i++) + if (juli_rates[i] == rate) + return gpio_vals[i]; + return 0; +} static void juli_ak4114_write(void *private_data, unsigned char reg, unsigned char val) { @@ -78,6 +171,27 @@ static unsigned char juli_ak4114_read(void *private_data, unsigned char reg) } /* + * If SPDIF capture and slaved to SPDIF-IN, setting runtime rate + * to the external rate + */ +static void juli_spdif_in_open(struct snd_ice1712 *ice, + struct snd_pcm_substream *substream) +{ + struct juli_spec *spec = ice->spec; + struct snd_pcm_runtime *runtime = substream->runtime; + int rate; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || + !ice->is_spdif_master(ice)) + return; + rate = snd_ak4114_external_rate(spec->ak4114); + if (rate >= runtime->hw.rate_min && rate <= runtime->hw.rate_max) { + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } +} + +/* * AK4358 section */ @@ -99,57 +213,285 @@ static void juli_akm_write(struct snd_akm4xxx *ak, int chip, } /* - * change the rate of envy24HT, AK4358 + * change the rate of envy24HT, AK4358, AK5385 */ static void juli_akm_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate) { - unsigned char old, tmp, dfs; + unsigned char old, tmp, ak4358_dfs; + unsigned int ak5385_pins, old_gpio, new_gpio; + struct snd_ice1712 *ice = ak->private_data[0]; + struct juli_spec *spec = ice->spec; - if (rate == 0) /* no hint - S/PDIF input is master, simply return */ + if (rate == 0) /* no hint - S/PDIF input is master or the new spdif + input rate undetected, simply return */ return; - + /* adjust DFS on codecs */ - if (rate > 96000) - dfs = 2; - else if (rate > 48000) - dfs = 1; - else - dfs = 0; - + if (rate > 96000) { + ak4358_dfs = 2; + ak5385_pins = GPIO_AK5385A_DFS1 | GPIO_AK5385A_CKS0; + } else if (rate > 48000) { + ak4358_dfs = 1; + ak5385_pins = GPIO_AK5385A_DFS0; + } else { + ak4358_dfs = 0; + ak5385_pins = 0; + } + /* AK5385 first, since it requires cold reset affecting both codecs */ + old_gpio = ice->gpio.get_data(ice); + new_gpio = (old_gpio & ~GPIO_AK5385A_MASK) | ak5385_pins; + /* printk(KERN_DEBUG "JULI - ak5385 set_rate_val: new gpio 0x%x\n", + new_gpio); */ + ice->gpio.set_data(ice, new_gpio); + + /* cold reset */ + old = inb(ICEMT1724(ice, AC97_CMD)); + outb(old | VT1724_AC97_COLD, ICEMT1724(ice, AC97_CMD)); + udelay(1); + outb(old & ~VT1724_AC97_COLD, ICEMT1724(ice, AC97_CMD)); + + /* AK4358 */ + /* set new value, reset DFS */ tmp = snd_akm4xxx_get(ak, 0, 2); - old = (tmp >> 4) & 0x03; - if (old == dfs) - return; - /* reset DFS */ snd_akm4xxx_reset(ak, 1); tmp = snd_akm4xxx_get(ak, 0, 2); tmp &= ~(0x03 << 4); - tmp |= dfs << 4; + tmp |= ak4358_dfs << 4; snd_akm4xxx_set(ak, 0, 2, tmp); snd_akm4xxx_reset(ak, 0); + + /* reinit ak4114 */ + snd_ak4114_reinit(spec->ak4114); } +#define AK_DAC(xname, xch) { .name = xname, .num_channels = xch } +#define PCM_VOLUME "PCM Playback Volume" +#define MONITOR_AN_IN_VOLUME "Monitor Analog In Volume" +#define MONITOR_DIG_IN_VOLUME "Monitor Digital In Volume" +#define MONITOR_DIG_OUT_VOLUME "Monitor Digital Out Volume" + +static const struct snd_akm4xxx_dac_channel juli_dac[] = { + AK_DAC(PCM_VOLUME, 2), + AK_DAC(MONITOR_AN_IN_VOLUME, 2), + AK_DAC(MONITOR_DIG_OUT_VOLUME, 2), + AK_DAC(MONITOR_DIG_IN_VOLUME, 2), +}; + + static struct snd_akm4xxx akm_juli_dac __devinitdata = { .type = SND_AK4358, - .num_dacs = 2, + .num_dacs = 8, /* DAC1 - analog out + DAC2 - analog in monitor + DAC3 - digital out monitor + DAC4 - digital in monitor + */ .ops = { .lock = juli_akm_lock, .unlock = juli_akm_unlock, .write = juli_akm_write, .set_rate_val = juli_akm_set_rate_val + }, + .dac_info = juli_dac, +}; + +#define juli_mute_info snd_ctl_boolean_mono_info + +static int juli_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned int val; + val = ice->gpio.get_data(ice) & (unsigned int) kcontrol->private_value; + if (kcontrol->private_value == GPIO_MUTE_CONTROL) + /* val 0 = signal on */ + ucontrol->value.integer.value[0] = (val) ? 0 : 1; + else + /* val 1 = signal on */ + ucontrol->value.integer.value[0] = (val) ? 1 : 0; + return 0; +} + +static int juli_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned int old_gpio, new_gpio; + old_gpio = ice->gpio.get_data(ice); + if (ucontrol->value.integer.value[0]) { + /* unmute */ + if (kcontrol->private_value == GPIO_MUTE_CONTROL) { + /* 0 = signal on */ + new_gpio = old_gpio & ~GPIO_MUTE_CONTROL; + /* un-smuting DAC */ + snd_akm4xxx_write(ice->akm, 0, 0x01, 0x01); + } else + /* 1 = signal on */ + new_gpio = old_gpio | + (unsigned int) kcontrol->private_value; + } else { + /* mute */ + if (kcontrol->private_value == GPIO_MUTE_CONTROL) { + /* 1 = signal off */ + new_gpio = old_gpio | GPIO_MUTE_CONTROL; + /* smuting DAC */ + snd_akm4xxx_write(ice->akm, 0, 0x01, 0x03); + } else + /* 0 = signal off */ + new_gpio = old_gpio & + ~((unsigned int) kcontrol->private_value); + } + /* printk("JULI - mute/unmute: control_value: 0x%x, old_gpio: 0x%x, \ + new_gpio 0x%x\n", + (unsigned int)ucontrol->value.integer.value[0], old_gpio, + new_gpio); */ + if (old_gpio != new_gpio) { + ice->gpio.set_data(ice, new_gpio); + return 1; + } + /* no change */ + return 0; +} + +static struct snd_kcontrol_new juli_mute_controls[] __devinitdata = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = juli_mute_info, + .get = juli_mute_get, + .put = juli_mute_put, + .private_value = GPIO_MUTE_CONTROL, + }, + /* Although the following functionality respects the succint NDA'd + * documentation from the card manufacturer, and the same way of + * operation is coded in OSS Juli driver, only Digital Out monitor + * seems to work. Surprisingly, Analog input monitor outputs Digital + * output data. The two are independent, as enabling both doubles + * volume of the monitor sound. + * + * Checking traces on the board suggests the functionality described + * by the manufacturer is correct - I2S from ADC and AK4114 + * go to ICE as well as to Xilinx, I2S inputs of DAC2,3,4 (the monitor + * inputs) are fed from Xilinx. + * + * I even checked traces on board and coded a support in driver for + * an alternative possiblity - the unused I2S ICE output channels + * switched to HW-IN/SPDIF-IN and providing the monitoring signal to + * the DAC - to no avail. The I2S outputs seem to be unconnected. + * + * The windows driver supports the monitoring correctly. + */ + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Monitor Analog In Switch", + .info = juli_mute_info, + .get = juli_mute_get, + .put = juli_mute_put, + .private_value = GPIO_ANAIN_MONITOR, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Monitor Digital Out Switch", + .info = juli_mute_info, + .get = juli_mute_get, + .put = juli_mute_put, + .private_value = GPIO_DIGOUT_MONITOR, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Monitor Digital In Switch", + .info = juli_mute_info, + .get = juli_mute_get, + .put = juli_mute_put, + .private_value = GPIO_DIGIN_MONITOR, + }, +}; + + +static void ak4358_proc_regs_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data; + int reg, val; + for (reg = 0; reg <= 0xf; reg++) { + val = snd_akm4xxx_get(ice->akm, 0, reg); + snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val); } +} + +static void ak4358_proc_init(struct snd_ice1712 *ice) +{ + struct snd_info_entry *entry; + if (!snd_card_proc_new(ice->card, "ak4358_codec", &entry)) + snd_info_set_text_ops(entry, ice, ak4358_proc_regs_read); +} + +static char *slave_vols[] __devinitdata = { + PCM_VOLUME, + MONITOR_AN_IN_VOLUME, + MONITOR_DIG_IN_VOLUME, + MONITOR_DIG_OUT_VOLUME, + NULL }; +static __devinitdata +DECLARE_TLV_DB_SCALE(juli_master_db_scale, -6350, 50, 1); + +static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card, + const char *name) +{ + struct snd_ctl_elem_id sid; + memset(&sid, 0, sizeof(sid)); + /* FIXME: strcpy is bad. */ + strcpy(sid.name, name); + sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + return snd_ctl_find_id(card, &sid); +} + +static void __devinit add_slaves(struct snd_card *card, + struct snd_kcontrol *master, char **list) +{ + for (; *list; list++) { + struct snd_kcontrol *slave = ctl_find(card, *list); + /* printk(KERN_DEBUG "add_slaves - %s\n", *list); */ + if (slave) { + /* printk(KERN_DEBUG "slave %s found\n", *list); */ + snd_ctl_add_slave(master, slave); + } + } +} + static int __devinit juli_add_controls(struct snd_ice1712 *ice) { struct juli_spec *spec = ice->spec; int err; + unsigned int i; + struct snd_kcontrol *vmaster; + err = snd_ice1712_akm4xxx_build_controls(ice); if (err < 0) return err; + + for (i = 0; i < ARRAY_SIZE(juli_mute_controls); i++) { + err = snd_ctl_add(ice->card, + snd_ctl_new1(&juli_mute_controls[i], ice)); + if (err < 0) + return err; + } + /* Create virtual master control */ + vmaster = snd_ctl_make_virtual_master("Master Playback Volume", + juli_master_db_scale); + if (!vmaster) + return -ENOMEM; + add_slaves(ice->card, vmaster, slave_vols); + err = snd_ctl_add(ice->card, vmaster); + if (err < 0) + return err; + /* only capture SPDIF over AK4114 */ err = snd_ak4114_build(spec->ak4114, NULL, - ice->pcm_pro->streams[SNDRV_PCM_STREAM_CAPTURE].substream); + ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); + + ak4358_proc_init(ice); if (err < 0) return err; return 0; @@ -158,6 +500,74 @@ static int __devinit juli_add_controls(struct snd_ice1712 *ice) /* * initialize the chip */ + +static inline int juli_is_spdif_master(struct snd_ice1712 *ice) +{ + return (ice->gpio.get_data(ice) & GPIO_INTERNAL_CLOCK) ? 0 : 1; +} + +static unsigned int juli_get_rate(struct snd_ice1712 *ice) +{ + int i; + unsigned char result; + + result = ice->gpio.get_data(ice) & GPIO_RATE_MASK; + for (i = 0; i < ARRAY_SIZE(gpio_vals); i++) + if (gpio_vals[i] == result) + return juli_rates[i]; + return 0; +} + +/* setting new rate */ +static void juli_set_rate(struct snd_ice1712 *ice, unsigned int rate) +{ + unsigned int old, new; + unsigned char val; + + old = ice->gpio.get_data(ice); + new = (old & ~GPIO_RATE_MASK) | get_gpio_val(rate); + /* printk(KERN_DEBUG "JULI - set_rate: old %x, new %x\n", + old & GPIO_RATE_MASK, + new & GPIO_RATE_MASK); */ + + ice->gpio.set_data(ice, new); + /* switching to external clock - supplied by external circuits */ + val = inb(ICEMT1724(ice, RATE)); + outb(val | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE)); +} + +static inline unsigned char juli_set_mclk(struct snd_ice1712 *ice, + unsigned int rate) +{ + /* no change in master clock */ + return 0; +} + +/* setting clock to external - SPDIF */ +static void juli_set_spdif_clock(struct snd_ice1712 *ice) +{ + unsigned int old; + old = ice->gpio.get_data(ice); + /* external clock (= 0), multiply 1x, 48kHz */ + ice->gpio.set_data(ice, (old & ~GPIO_RATE_MASK) | GPIO_MULTI_1X | + GPIO_FREQ_48KHZ); +} + +/* Called when ak4114 detects change in the input SPDIF stream */ +static void juli_ak4114_change(struct ak4114 *ak4114, unsigned char c0, + unsigned char c1) +{ + struct snd_ice1712 *ice = ak4114->change_callback_private; + int rate; + if (ice->is_spdif_master(ice) && c1) { + /* only for SPDIF master mode, rate was changed */ + rate = snd_ak4114_external_rate(ak4114); + /* printk(KERN_DEBUG "ak4114 - input rate changed to %d\n", + rate); */ + juli_akm_set_rate_val(ice->akm, rate); + } +} + static int __devinit juli_init(struct snd_ice1712 *ice) { static const unsigned char ak4114_init_vals[] = { @@ -187,6 +597,11 @@ static int __devinit juli_init(struct snd_ice1712 *ice) ice, &spec->ak4114); if (err < 0) return err; + /* callback for codecs rate setting */ + spec->ak4114->change_callback = juli_ak4114_change; + spec->ak4114->change_callback_private = ice; + /* AK4114 in Juli can detect external rate correctly */ + spec->ak4114->check_flags = 0; #if 0 /* it seems that the analog doughter board detection does not work @@ -210,6 +625,15 @@ static int __devinit juli_init(struct snd_ice1712 *ice) return err; } + /* juli is clocked by Xilinx array */ + ice->hw_rates = &juli_rates_info; + ice->is_spdif_master = juli_is_spdif_master; + ice->get_rate = juli_get_rate; + ice->set_rate = juli_set_rate; + ice->set_mclk = juli_set_mclk; + ice->set_spdif_clock = juli_set_spdif_clock; + + ice->spdif.ops.open = juli_spdif_in_open; return 0; } @@ -220,18 +644,20 @@ static int __devinit juli_init(struct snd_ice1712 *ice) */ static unsigned char juli_eeprom[] __devinitdata = { - [ICE_EEP2_SYSCONF] = 0x20, /* clock 512, mpu401, 1xADC, 1xDACs */ + [ICE_EEP2_SYSCONF] = 0x2b, /* clock 512, mpu401, 1xADC, 1xDACs, + SPDIF in */ [ICE_EEP2_ACLINK] = 0x80, /* I2S */ [ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit, 192k */ [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */ - [ICE_EEP2_GPIO_DIR] = 0x9f, + [ICE_EEP2_GPIO_DIR] = 0x9f, /* 5, 6:inputs; 7, 4-0 outputs*/ [ICE_EEP2_GPIO_DIR1] = 0xff, [ICE_EEP2_GPIO_DIR2] = 0x7f, - [ICE_EEP2_GPIO_MASK] = 0x9f, - [ICE_EEP2_GPIO_MASK1] = 0xff, + [ICE_EEP2_GPIO_MASK] = 0x60, /* 5, 6: locked; 7, 4-0 writable */ + [ICE_EEP2_GPIO_MASK1] = 0x00, /* 0-7 writable */ [ICE_EEP2_GPIO_MASK2] = 0x7f, - [ICE_EEP2_GPIO_STATE] = 0x16, /* internal clock, multiple 1x, 48kHz */ - [ICE_EEP2_GPIO_STATE1] = 0x80, /* mute */ + [ICE_EEP2_GPIO_STATE] = GPIO_FREQ_48KHZ | GPIO_MULTI_1X | + GPIO_INTERNAL_CLOCK, /* internal clock, multiple 1x, 48kHz*/ + [ICE_EEP2_GPIO_STATE1] = 0x00, /* unmuted */ [ICE_EEP2_GPIO_STATE2] = 0x00, }; diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c index 4945c81..203cdc1 100644 --- a/sound/pci/ice1712/pontis.c +++ b/sound/pci/ice1712/pontis.c @@ -246,7 +246,7 @@ static int wm_adc_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val wm_put(ice, WM_ADC_MUX, nval); } mutex_unlock(&ice->gpio_mutex); - return 0; + return change; } /* @@ -450,7 +450,7 @@ static int cs_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu change = 1; } mutex_unlock(&ice->gpio_mutex); - return 0; + return change; } diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c index 48cf40a..25ceb67 100644 --- a/sound/pci/ice1712/prodigy192.c +++ b/sound/pci/ice1712/prodigy192.c @@ -319,12 +319,11 @@ static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol, /* * Handler for setting correct codec rate - called when rate change is detected */ -static void stac9460_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate) +static void stac9460_set_rate_val(struct snd_ice1712 *ice, unsigned int rate) { unsigned char old, new; int idx; unsigned char changed[7]; - struct snd_ice1712 *ice = ak->private_data[0]; struct prodigy192_spec *spec = ice->spec; if (rate == 0) /* no hint - S/PDIF input is master, simply return */ @@ -357,16 +356,6 @@ static void stac9460_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate) mutex_unlock(&spec->mute_mutex); } -/* using akm infrastructure for setting rate of the codec */ -static struct snd_akm4xxx akmlike_stac9460 __devinitdata = { - .type = NON_AKM, /* special value */ - .num_adcs = 6, /* not used in any way, just for completeness */ - .num_dacs = 2, - .ops = { - .set_rate_val = stac9460_set_rate_val - } -}; - static const DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0); static const DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0); @@ -642,12 +631,19 @@ static int prodigy192_ak4114_init(struct snd_ice1712 *ice) 0x41, 0x02, 0x2c, 0x00, 0x00 }; struct prodigy192_spec *spec = ice->spec; + int err; - return snd_ak4114_create(ice->card, + err = snd_ak4114_create(ice->card, prodigy192_ak4114_read, prodigy192_ak4114_write, ak4114_init_vals, ak4114_init_txcsb, ice, &spec->ak4114); + if (err < 0) + return err; + /* AK4114 in Prodigy192 cannot detect external rate correctly. + * No reason to stop capture stream due to incorrect checks */ + spec->ak4114->check_flags = AK4114_CHECK_NO_RATE; + return 0; } static void stac9460_proc_regs_read(struct snd_info_entry *entry, @@ -743,7 +739,6 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice) }; const unsigned short *p; int err = 0; - struct snd_akm4xxx *ak; struct prodigy192_spec *spec; /* prodigy 192 */ @@ -761,15 +756,7 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice) p = stac_inits_prodigy; for (; *p != (unsigned short)-1; p += 2) stac9460_put(ice, p[0], p[1]); - /* reusing the akm codecs infrastructure, - * for setting rate on stac9460 */ - ak = ice->akm = kmalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); - if (!ak) - return -ENOMEM; - ice->akm_codecs = 1; - err = snd_ice1712_akm4xxx_init(ak, &akmlike_stac9460, NULL, ice); - if (err < 0) - return err; + ice->gpio.set_pro_rate = stac9460_set_rate_val; /* MI/ODI/O add on card with AK4114 */ if (prodigy192_miodio_exists(ice)) { diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c index 301bf92..dba93d8 100644 --- a/sound/pci/ice1712/revo.c +++ b/sound/pci/ice1712/revo.c @@ -322,17 +322,23 @@ static struct snd_pt2258 ptc_revo51_volume; static void ap192_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate) { struct snd_ice1712 *ice = ak->private_data[0]; + int dfs; revo_set_rate_val(ak, rate); -#if 1 /* FIXME: do we need this procedure? */ - /* reset DFS pin of AK5385A for ADC, too */ - /* DFS0 (pin 18) -- GPIO10 pin 77 */ - snd_ice1712_save_gpio_status(ice); - snd_ice1712_gpio_write_bits(ice, 1 << 10, - rate > 48000 ? (1 << 10) : 0); - snd_ice1712_restore_gpio_status(ice); -#endif + /* reset CKS */ + snd_ice1712_gpio_write_bits(ice, 1 << 8, rate > 96000 ? 1 : 0); + /* reset DFS pins of AK5385A for ADC, too */ + if (rate > 96000) + dfs = 2; + else if (rate > 48000) + dfs = 1; + else + dfs = 0; + snd_ice1712_gpio_write_bits(ice, 3 << 9, dfs << 9); + /* reset ADC */ + snd_ice1712_gpio_write_bits(ice, 1 << 11, 0); + snd_ice1712_gpio_write_bits(ice, 1 << 11, 1); } static const struct snd_akm4xxx_dac_channel ap192_dac[] = { @@ -353,28 +359,20 @@ static struct snd_ak4xxx_private akm_ap192_priv __devinitdata = { .cif = 0, .data_mask = VT1724_REVO_CDOUT, .clk_mask = VT1724_REVO_CCLK, - .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS3, - .cs_addr = VT1724_REVO_CS3, - .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS3, + .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1, + .cs_addr = VT1724_REVO_CS1, + .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1, .add_flags = VT1724_REVO_CCLK, /* high at init */ .mask_flags = 0, }; -#if 0 -/* FIXME: ak4114 makes the sound much lower due to some confliction, - * so let's disable it right now... - */ -#define BUILD_AK4114_AP192 -#endif - -#ifdef BUILD_AK4114_AP192 /* AK4114 support on Audiophile 192 */ /* CDTO (pin 32) -- GPIO2 pin 52 * CDTI (pin 33) -- GPIO3 pin 53 (shared with AK4358) * CCLK (pin 34) -- GPIO1 pin 51 (shared with AK4358) * CSN (pin 35) -- GPIO7 pin 59 */ -#define AK4114_ADDR 0x00 +#define AK4114_ADDR 0x02 static void write_data(struct snd_ice1712 *ice, unsigned int gpio, unsigned int data, int idx) @@ -428,7 +426,7 @@ static unsigned int ap192_4wire_start(struct snd_ice1712 *ice) tmp = snd_ice1712_gpio_read(ice); tmp |= VT1724_REVO_CCLK; /* high at init */ tmp |= VT1724_REVO_CS0; - tmp &= ~VT1724_REVO_CS3; + tmp &= ~VT1724_REVO_CS1; snd_ice1712_gpio_write(ice, tmp); udelay(1); return tmp; @@ -436,7 +434,7 @@ static unsigned int ap192_4wire_start(struct snd_ice1712 *ice) static void ap192_4wire_finish(struct snd_ice1712 *ice, unsigned int tmp) { - tmp |= VT1724_REVO_CS3; + tmp |= VT1724_REVO_CS1; tmp |= VT1724_REVO_CS0; snd_ice1712_gpio_write(ice, tmp); udelay(1); @@ -485,13 +483,17 @@ static int __devinit ap192_ak4114_init(struct snd_ice1712 *ice) struct ak4114 *ak; int err; - return snd_ak4114_create(ice->card, + err = snd_ak4114_create(ice->card, ap192_ak4114_read, ap192_ak4114_write, ak4114_init_vals, ak4114_init_txcsb, ice, &ak); + /* AK4114 in Revo cannot detect external rate correctly. + * No reason to stop capture stream due to incorrect checks */ + ak->check_flags = AK4114_CHECK_NO_RATE; + + return 0; /* error ignored; it's no fatal error */ } -#endif /* BUILD_AK4114_AP192 */ static int __devinit revo_init(struct snd_ice1712 *ice) { @@ -557,6 +559,9 @@ static int __devinit revo_init(struct snd_ice1712 *ice) if (err < 0) return err; + /* unmute all codecs */ + snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE, + VT1724_REVO_MUTE); break; } @@ -588,11 +593,9 @@ static int __devinit revo_add_controls(struct snd_ice1712 *ice) err = snd_ice1712_akm4xxx_build_controls(ice); if (err < 0) return err; -#ifdef BUILD_AK4114_AP192 err = ap192_ak4114_init(ice); if (err < 0) return err; -#endif break; } return 0; diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index c52abd0..47485af 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -155,7 +155,8 @@ DEFINE_REGSET(SP, 0x60); /* SPDIF out */ #define ICH_PCM_SPDIF_69 0x80000000 /* s/pdif pcm on slots 6&9 */ #define ICH_PCM_SPDIF_1011 0xc0000000 /* s/pdif pcm on slots 10&11 */ #define ICH_PCM_20BIT 0x00400000 /* 20-bit samples (ICH4) */ -#define ICH_PCM_246_MASK 0x00300000 /* 6 channels (not all chips) */ +#define ICH_PCM_246_MASK 0x00300000 /* chan mask (not all chips) */ +#define ICH_PCM_8 0x00300000 /* 8 channels (not all chips) */ #define ICH_PCM_6 0x00200000 /* 6 channels (not all chips) */ #define ICH_PCM_4 0x00100000 /* 4 channels (not all chips) */ #define ICH_PCM_2 0x00000000 /* 2 channels (stereo) */ @@ -382,6 +383,7 @@ struct intel8x0 { unsigned multi4: 1, multi6: 1, + multi8 :1, dra: 1, smp20bit: 1; unsigned in_ac97_init: 1, @@ -997,6 +999,8 @@ static void snd_intel8x0_setup_pcm_out(struct intel8x0 *chip, cnt |= ICH_PCM_4; else if (runtime->channels == 6) cnt |= ICH_PCM_6; + else if (runtime->channels == 8) + cnt |= ICH_PCM_8; if (chip->device_type == DEVICE_NFORCE) { /* reset to 2ch once to keep the 6 channel data in alignment, * to start from Front Left always @@ -1106,6 +1110,16 @@ static struct snd_pcm_hw_constraint_list hw_constraints_channels6 = { .mask = 0, }; +static unsigned int channels8[] = { + 2, 4, 6, 8, +}; + +static struct snd_pcm_hw_constraint_list hw_constraints_channels8 = { + .count = ARRAY_SIZE(channels8), + .list = channels8, + .mask = 0, +}; + static int snd_intel8x0_pcm_open(struct snd_pcm_substream *substream, struct ichdev *ichdev) { struct intel8x0 *chip = snd_pcm_substream_chip(substream); @@ -1136,7 +1150,12 @@ static int snd_intel8x0_playback_open(struct snd_pcm_substream *substream) if (err < 0) return err; - if (chip->multi6) { + if (chip->multi8) { + runtime->hw.channels_max = 8; + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + &hw_constraints_channels8); + } else if (chip->multi6) { runtime->hw.channels_max = 6; snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels6); @@ -2203,8 +2222,11 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock, } if (pbus->pcms[0].r[0].slots & (1 << AC97_SLOT_PCM_SLEFT)) { chip->multi4 = 1; - if (pbus->pcms[0].r[0].slots & (1 << AC97_SLOT_LFE)) + if (pbus->pcms[0].r[0].slots & (1 << AC97_SLOT_LFE)) { chip->multi6 = 1; + if (chip->ac97[0]->flags & AC97_HAS_8CH) + chip->multi8 = 1; + } } if (pbus->pcms[0].r[1].rslots[0]) { chip->dra = 1; @@ -2648,7 +2670,7 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip) t = stop_time.tv_sec - start_time.tv_sec; t *= 1000000; t += stop_time.tv_usec - start_time.tv_usec; - printk(KERN_INFO "%s: measured %lu usecs\n", __FUNCTION__, t); + printk(KERN_INFO "%s: measured %lu usecs\n", __func__, t); if (t == 0) { snd_printk(KERN_ERR "?? calculation error..\n"); return; diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 04fa0a6..a753dae 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -2068,7 +2068,7 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip) { struct snd_ac97_bus *pbus; struct snd_ac97_template ac97; - struct snd_ctl_elem_id id; + struct snd_ctl_elem_id elem_id; int err; static struct snd_ac97_bus_ops ops = { .write = snd_m3_ac97_write, @@ -2088,14 +2088,14 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip) schedule_timeout_uninterruptible(msecs_to_jiffies(100)); snd_ac97_write(chip->ac97, AC97_PCM, 0); - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(id.name, "Master Playback Switch"); - chip->master_switch = snd_ctl_find_id(chip->card, &id); - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(id.name, "Master Playback Volume"); - chip->master_volume = snd_ctl_find_id(chip->card, &id); + memset(&elem_id, 0, sizeof(elem_id)); + elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(elem_id.name, "Master Playback Switch"); + chip->master_switch = snd_ctl_find_id(chip->card, &elem_id); + memset(&elem_id, 0, sizeof(elem_id)); + elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(elem_id.name, "Master Playback Volume"); + chip->master_volume = snd_ctl_find_id(chip->card, &elem_id); return 0; } @@ -2569,7 +2569,7 @@ static int m3_suspend(struct pci_dev *pci, pm_message_t state) { struct snd_card *card = pci_get_drvdata(pci); struct snd_m3 *chip = card->private_data; - int i, index; + int i, dsp_index; if (chip->suspend_mem == NULL) return 0; @@ -2583,12 +2583,12 @@ static int m3_suspend(struct pci_dev *pci, pm_message_t state) snd_m3_assp_halt(chip); /* save dsp image */ - index = 0; + dsp_index = 0; for (i = REV_B_CODE_MEMORY_BEGIN; i <= REV_B_CODE_MEMORY_END; i++) - chip->suspend_mem[index++] = + chip->suspend_mem[dsp_index++] = snd_m3_assp_read(chip, MEMTYPE_INTERNAL_CODE, i); for (i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++) - chip->suspend_mem[index++] = + chip->suspend_mem[dsp_index++] = snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA, i); pci_disable_device(pci); @@ -2601,7 +2601,7 @@ static int m3_resume(struct pci_dev *pci) { struct snd_card *card = pci_get_drvdata(pci); struct snd_m3 *chip = card->private_data; - int i, index; + int i, dsp_index; if (chip->suspend_mem == NULL) return 0; @@ -2625,13 +2625,13 @@ static int m3_resume(struct pci_dev *pci) snd_m3_ac97_reset(chip); /* restore dsp image */ - index = 0; + dsp_index = 0; for (i = REV_B_CODE_MEMORY_BEGIN; i <= REV_B_CODE_MEMORY_END; i++) snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, i, - chip->suspend_mem[index++]); + chip->suspend_mem[dsp_index++]); for (i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++) snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, i, - chip->suspend_mem[index++]); + chip->suspend_mem[dsp_index++]); /* tell the dma engine to restart itself */ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c index 666f69a..1e54a3c 100644 --- a/sound/pci/oxygen/hifier.c +++ b/sound/pci/oxygen/hifier.c @@ -160,11 +160,11 @@ static const struct oxygen_model model_hifier = { .update_dac_volume = update_ak4396_volume, .update_dac_mute = update_ak4396_mute, .model_data_size = sizeof(struct hifier_data), + .pcm_dev_cfg = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + CAPTURE_0_FROM_I2S_1, .dac_channels = 2, - .used_channels = OXYGEN_CHANNEL_A | - OXYGEN_CHANNEL_SPDIF | - OXYGEN_CHANNEL_MULTICH, - .function_flags = 0, + .function_flags = OXYGEN_FUNCTION_SPI, .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, }; @@ -181,7 +181,7 @@ static int __devinit hifier_probe(struct pci_dev *pci, ++dev; return -ENOENT; } - err = oxygen_pci_probe(pci, index[dev], id[dev], 0, &model_hifier); + err = oxygen_pci_probe(pci, index[dev], id[dev], &model_hifier); if (err >= 0) ++dev; return err; diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c index 9a9941b..b3b7771 100644 --- a/sound/pci/oxygen/oxygen.c +++ b/sound/pci/oxygen/oxygen.c @@ -39,7 +39,7 @@ #include #include "oxygen.h" #include "ak4396.h" -#include "cm9780.h" +#include "wm8785.h" MODULE_AUTHOR("Clemens Ladisch "); MODULE_DESCRIPTION("C-Media CMI8788 driver"); @@ -78,49 +78,6 @@ MODULE_DEVICE_TABLE(pci, oxygen_ids); #define GPIO_AK5385_DFS_DOUBLE 0x0001 #define GPIO_AK5385_DFS_QUAD 0x0002 -#define GPIO_LINE_MUTE CM9780_GPO0 - -#define WM8785_R0 0 -#define WM8785_R1 1 -#define WM8785_R2 2 -#define WM8785_R7 7 - -/* R0 */ -#define WM8785_MCR_MASK 0x007 -#define WM8785_MCR_SLAVE 0x000 -#define WM8785_MCR_MASTER_128 0x001 -#define WM8785_MCR_MASTER_192 0x002 -#define WM8785_MCR_MASTER_256 0x003 -#define WM8785_MCR_MASTER_384 0x004 -#define WM8785_MCR_MASTER_512 0x005 -#define WM8785_MCR_MASTER_768 0x006 -#define WM8785_OSR_MASK 0x018 -#define WM8785_OSR_SINGLE 0x000 -#define WM8785_OSR_DOUBLE 0x008 -#define WM8785_OSR_QUAD 0x010 -#define WM8785_FORMAT_MASK 0x060 -#define WM8785_FORMAT_RJUST 0x000 -#define WM8785_FORMAT_LJUST 0x020 -#define WM8785_FORMAT_I2S 0x040 -#define WM8785_FORMAT_DSP 0x060 -/* R1 */ -#define WM8785_WL_MASK 0x003 -#define WM8785_WL_16 0x000 -#define WM8785_WL_20 0x001 -#define WM8785_WL_24 0x002 -#define WM8785_WL_32 0x003 -#define WM8785_LRP 0x004 -#define WM8785_BCLKINV 0x008 -#define WM8785_LRSWAP 0x010 -#define WM8785_DEVNO_MASK 0x0e0 -/* R2 */ -#define WM8785_HPFR 0x001 -#define WM8785_HPFL 0x002 -#define WM8785_SDODIS 0x004 -#define WM8785_PWRDNR 0x008 -#define WM8785_PWRDNL 0x010 -#define WM8785_TDM_MASK 0x1c0 - struct generic_data { u8 ak4396_ctl2; }; @@ -185,23 +142,16 @@ static void wm8785_init(struct oxygen *chip) snd_component_add(chip->card, "WM8785"); } -static void cmi9780_init(struct oxygen *chip) -{ - oxygen_ac97_clear_bits(chip, 0, CM9780_GPIO_STATUS, GPIO_LINE_MUTE); -} - static void generic_init(struct oxygen *chip) { ak4396_init(chip); wm8785_init(chip); - cmi9780_init(chip); } static void meridian_init(struct oxygen *chip) { ak4396_init(chip); ak5385_init(chip); - cmi9780_init(chip); } static void generic_cleanup(struct oxygen *chip) @@ -297,27 +247,6 @@ static void set_ak5385_params(struct oxygen *chip, value, GPIO_AK5385_DFS_MASK); } -static void cmi9780_switch_hook(struct oxygen *chip, unsigned int codec, - unsigned int reg, int mute) -{ - if (codec != 0) - return; - switch (reg) { - case AC97_LINE: - oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS, - mute ? GPIO_LINE_MUTE : 0, - GPIO_LINE_MUTE); - break; - case AC97_MIC: - case AC97_CD: - case AC97_AUX: - if (!mute) - oxygen_ac97_set_bits(chip, 0, CM9780_GPIO_STATUS, - GPIO_LINE_MUTE); - break; - } -} - static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0); static int ak4396_control_filter(struct snd_kcontrol_new *template) @@ -341,15 +270,16 @@ static const struct oxygen_model model_generic = { .set_adc_params = set_wm8785_params, .update_dac_volume = update_ak4396_volume, .update_dac_mute = update_ak4396_mute, - .ac97_switch_hook = cmi9780_switch_hook, .model_data_size = sizeof(struct generic_data), + .pcm_dev_cfg = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + PLAYBACK_2_TO_AC97_1 | + CAPTURE_0_FROM_I2S_1 | + CAPTURE_1_FROM_SPDIF | + CAPTURE_2_FROM_AC97_1, .dac_channels = 8, - .used_channels = OXYGEN_CHANNEL_A | - OXYGEN_CHANNEL_C | - OXYGEN_CHANNEL_SPDIF | - OXYGEN_CHANNEL_MULTICH | - OXYGEN_CHANNEL_AC97, - .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5, + .function_flags = OXYGEN_FUNCTION_SPI | + OXYGEN_FUNCTION_ENABLE_SPI_4_5, .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, }; @@ -365,15 +295,17 @@ static const struct oxygen_model model_meridian = { .set_adc_params = set_ak5385_params, .update_dac_volume = update_ak4396_volume, .update_dac_mute = update_ak4396_mute, - .ac97_switch_hook = cmi9780_switch_hook, .model_data_size = sizeof(struct generic_data), + .pcm_dev_cfg = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + PLAYBACK_2_TO_AC97_1 | + CAPTURE_0_FROM_I2S_2 | + CAPTURE_1_FROM_SPDIF | + CAPTURE_2_FROM_AC97_1, .dac_channels = 8, - .used_channels = OXYGEN_CHANNEL_B | - OXYGEN_CHANNEL_C | - OXYGEN_CHANNEL_SPDIF | - OXYGEN_CHANNEL_MULTICH | - OXYGEN_CHANNEL_AC97, - .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5, + .misc_flags = OXYGEN_MISC_MIDI, + .function_flags = OXYGEN_FUNCTION_SPI | + OXYGEN_FUNCTION_ENABLE_SPI_4_5, .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, }; @@ -392,7 +324,7 @@ static int __devinit generic_oxygen_probe(struct pci_dev *pci, return -ENOENT; } is_meridian = pci_id->driver_data; - err = oxygen_pci_probe(pci, index[dev], id[dev], is_meridian, + err = oxygen_pci_probe(pci, index[dev], id[dev], is_meridian ? &model_meridian : &model_generic); if (err >= 0) ++dev; diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h index ad50fb8..2f25c8d 100644 --- a/sound/pci/oxygen/oxygen.h +++ b/sound/pci/oxygen/oxygen.h @@ -16,6 +16,16 @@ #define PCM_AC97 5 #define PCM_COUNT 6 +/* model-specific configuration of outputs/inputs */ +#define PLAYBACK_0_TO_I2S 0x001 +#define PLAYBACK_1_TO_SPDIF 0x004 +#define PLAYBACK_2_TO_AC97_1 0x008 +#define CAPTURE_0_FROM_I2S_1 0x010 +#define CAPTURE_0_FROM_I2S_2 0x020 +#define CAPTURE_1_FROM_SPDIF 0x080 +#define CAPTURE_2_FROM_I2S_2 0x100 +#define CAPTURE_2_FROM_AC97_1 0x200 + enum { CONTROL_SPDIF_PCM, CONTROL_SPDIF_INPUT_BITS, @@ -87,12 +97,11 @@ struct oxygen_model { struct snd_pcm_hw_params *params); void (*update_dac_volume)(struct oxygen *chip); void (*update_dac_mute)(struct oxygen *chip); - void (*ac97_switch_hook)(struct oxygen *chip, unsigned int codec, - unsigned int reg, int mute); void (*gpio_changed)(struct oxygen *chip); size_t model_data_size; + unsigned int pcm_dev_cfg; u8 dac_channels; - u8 used_channels; + u8 misc_flags; u8 function_flags; u16 dac_i2s_format; u16 adc_i2s_format; @@ -100,7 +109,7 @@ struct oxygen_model { /* oxygen_lib.c */ -int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, int midi, +int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, const struct oxygen_model *model); void oxygen_pci_remove(struct pci_dev *pci); diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index 78c2115..4f3d9e5 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -240,12 +240,12 @@ static void oxygen_init(struct oxygen *chip) chip->has_ac97_0 = (i & OXYGEN_AC97_CODEC_0) != 0; chip->has_ac97_1 = (i & OXYGEN_AC97_CODEC_1) != 0; - oxygen_set_bits8(chip, OXYGEN_FUNCTION, - OXYGEN_FUNCTION_RESET_CODEC | - chip->model->function_flags); oxygen_write8_masked(chip, OXYGEN_FUNCTION, - OXYGEN_FUNCTION_SPI, - OXYGEN_FUNCTION_2WIRE_SPI_MASK); + OXYGEN_FUNCTION_RESET_CODEC | + chip->model->function_flags, + OXYGEN_FUNCTION_RESET_CODEC | + OXYGEN_FUNCTION_2WIRE_SPI_MASK | + OXYGEN_FUNCTION_ENABLE_SPI_4_5); oxygen_write8(chip, OXYGEN_DMA_STATUS, 0); oxygen_write8(chip, OXYGEN_DMA_PAUSE, 0); oxygen_write8(chip, OXYGEN_PLAY_CHANNELS, @@ -253,11 +253,13 @@ static void oxygen_init(struct oxygen *chip) OXYGEN_DMA_A_BURST_8 | OXYGEN_DMA_MULTICH_BURST_8); oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0); - oxygen_write8_masked(chip, OXYGEN_MISC, 0, + oxygen_write8_masked(chip, OXYGEN_MISC, + chip->model->misc_flags, OXYGEN_MISC_WRITE_PCI_SUBID | OXYGEN_MISC_REC_C_FROM_SPDIF | OXYGEN_MISC_REC_B_FROM_AC97 | - OXYGEN_MISC_REC_A_FROM_MULTICH); + OXYGEN_MISC_REC_A_FROM_MULTICH | + OXYGEN_MISC_MIDI); oxygen_write8(chip, OXYGEN_REC_FORMAT, (OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_A_SHIFT) | (OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_B_SHIFT) | @@ -267,21 +269,28 @@ static void oxygen_init(struct oxygen *chip) (OXYGEN_FORMAT_16 << OXYGEN_MULTICH_FORMAT_SHIFT)); oxygen_write8(chip, OXYGEN_REC_CHANNELS, OXYGEN_REC_CHANNELS_2_2_2); oxygen_write16(chip, OXYGEN_I2S_MULTICH_FORMAT, - OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST | - OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 | - OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); - oxygen_write16(chip, OXYGEN_I2S_A_FORMAT, - OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST | - OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 | - OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); - oxygen_write16(chip, OXYGEN_I2S_B_FORMAT, - OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST | - OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 | + OXYGEN_RATE_48000 | chip->model->dac_i2s_format | + OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 | OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); + if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_1) + oxygen_write16(chip, OXYGEN_I2S_A_FORMAT, + OXYGEN_RATE_48000 | chip->model->adc_i2s_format | + OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 | + OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); + else + oxygen_write16(chip, OXYGEN_I2S_A_FORMAT, + OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK); + if (chip->model->pcm_dev_cfg & (CAPTURE_0_FROM_I2S_2 | + CAPTURE_2_FROM_I2S_2)) + oxygen_write16(chip, OXYGEN_I2S_B_FORMAT, + OXYGEN_RATE_48000 | chip->model->adc_i2s_format | + OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 | + OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); + else + oxygen_write16(chip, OXYGEN_I2S_B_FORMAT, + OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK); oxygen_write16(chip, OXYGEN_I2S_C_FORMAT, - OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST | - OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 | - OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); + OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK); oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL, OXYGEN_SPDIF_SENSE_MASK | OXYGEN_SPDIF_LOCK_MASK | @@ -351,6 +360,8 @@ static void oxygen_init(struct oxygen *chip) oxygen_write_ac97(chip, 0, AC97_REC_GAIN, 0x8000); oxygen_write_ac97(chip, 0, AC97_CENTER_LFE_MASTER, 0x8080); oxygen_write_ac97(chip, 0, AC97_SURROUND_MASTER, 0x8080); + oxygen_ac97_clear_bits(chip, 0, CM9780_GPIO_STATUS, + CM9780_GPO0); /* power down unused ADCs and DACs */ oxygen_ac97_set_bits(chip, 0, AC97_POWERDOWN, AC97_PD_PR0 | AC97_PD_PR1); @@ -400,7 +411,7 @@ static void oxygen_card_free(struct snd_card *card) } int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, - int midi, const struct oxygen_model *model) + const struct oxygen_model *model) { struct snd_card *card; struct oxygen *chip; @@ -472,9 +483,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, if (err < 0) goto err_card; - oxygen_write8_masked(chip, OXYGEN_MISC, - midi ? OXYGEN_MISC_MIDI : 0, OXYGEN_MISC_MIDI); - if (midi) { + if (model->misc_flags & OXYGEN_MISC_MIDI) { err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI, chip->addr + OXYGEN_MPU401, MPU401_INFO_INTEGRATED, 0, 0, diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c index a8e4623..9a7c880 100644 --- a/sound/pci/oxygen/oxygen_mixer.c +++ b/sound/pci/oxygen/oxygen_mixer.c @@ -446,6 +446,50 @@ static int spdif_loopback_put(struct snd_kcontrol *ctl, return changed; } +static int monitor_volume_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = 1; + info->value.integer.min = 0; + info->value.integer.max = 1; + return 0; +} + +static int monitor_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u8 bit = ctl->private_value; + int invert = ctl->private_value & (1 << 8); + + value->value.integer.value[0] = + !!invert ^ !!(oxygen_read8(chip, OXYGEN_ADC_MONITOR) & bit); + return 0; +} + +static int monitor_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u8 bit = ctl->private_value; + int invert = ctl->private_value & (1 << 8); + u8 oldreg, newreg; + int changed; + + spin_lock_irq(&chip->reg_lock); + oldreg = oxygen_read8(chip, OXYGEN_ADC_MONITOR); + if ((!!value->value.integer.value[0] ^ !!invert) != 0) + newreg = oldreg | bit; + else + newreg = oldreg & ~bit; + changed = newreg != oldreg; + if (changed) + oxygen_write8(chip, OXYGEN_ADC_MONITOR, newreg); + spin_unlock_irq(&chip->reg_lock); + return changed; +} + static int ac97_switch_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) { @@ -466,6 +510,19 @@ static int ac97_switch_get(struct snd_kcontrol *ctl, return 0; } +static void mute_ac97_ctl(struct oxygen *chip, unsigned int control) +{ + unsigned int priv_idx = chip->controls[control]->private_value & 0xff; + u16 value; + + value = oxygen_read_ac97(chip, 0, priv_idx); + if (!(value & 0x8000)) { + oxygen_write_ac97(chip, 0, priv_idx, value | 0x8000); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->controls[control]->id); + } +} + static int ac97_switch_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) { @@ -487,9 +544,22 @@ static int ac97_switch_put(struct snd_kcontrol *ctl, change = newreg != oldreg; if (change) { oxygen_write_ac97(chip, codec, index, newreg); - if (bitnr == 15 && chip->model->ac97_switch_hook) - chip->model->ac97_switch_hook(chip, codec, index, - newreg & 0x8000); + if (index == AC97_LINE) { + oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS, + newreg & 0x8000 ? + CM9780_GPO0 : 0, CM9780_GPO0); + if (!(newreg & 0x8000)) { + mute_ac97_ctl(chip, CONTROL_MIC_CAPTURE_SWITCH); + mute_ac97_ctl(chip, CONTROL_CD_CAPTURE_SWITCH); + mute_ac97_ctl(chip, CONTROL_AUX_CAPTURE_SWITCH); + } + } else if ((index == AC97_MIC || index == AC97_CD || + index == AC97_VIDEO || index == AC97_AUX) && + bitnr == 15 && !(newreg & 0x8000)) { + mute_ac97_ctl(chip, CONTROL_LINE_CAPTURE_SWITCH); + oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS, + CM9780_GPO0, CM9780_GPO0); + } } mutex_unlock(&chip->mutex); return change; @@ -608,6 +678,7 @@ static int ac97_fp_rec_volume_put(struct snd_kcontrol *ctl, .private_value = ((codec) << 24) | (index), \ } +static DECLARE_TLV_DB_SCALE(monitor_db_scale, -1000, 1000, 0); static DECLARE_TLV_DB_SCALE(ac97_db_scale, -3450, 150, 0); static DECLARE_TLV_DB_SCALE(ac97_rec_db_scale, 0, 150, 0); @@ -692,11 +763,118 @@ static const struct snd_kcontrol_new controls[] = { }, }; +static const struct { + unsigned int pcm_dev; + struct snd_kcontrol_new controls[2]; +} monitor_controls[] = { + { + .pcm_dev = CAPTURE_0_FROM_I2S_1, + .controls = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Input Monitor Switch", + .info = snd_ctl_boolean_mono_info, + .get = monitor_get, + .put = monitor_put, + .private_value = OXYGEN_ADC_MONITOR_A, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Input Monitor Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = monitor_volume_info, + .get = monitor_get, + .put = monitor_put, + .private_value = OXYGEN_ADC_MONITOR_A_HALF_VOL + | (1 << 8), + .tlv = { .p = monitor_db_scale, }, + }, + }, + }, + { + .pcm_dev = CAPTURE_0_FROM_I2S_2, + .controls = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Input Monitor Switch", + .info = snd_ctl_boolean_mono_info, + .get = monitor_get, + .put = monitor_put, + .private_value = OXYGEN_ADC_MONITOR_B, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Input Monitor Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = monitor_volume_info, + .get = monitor_get, + .put = monitor_put, + .private_value = OXYGEN_ADC_MONITOR_B_HALF_VOL + | (1 << 8), + .tlv = { .p = monitor_db_scale, }, + }, + }, + }, + { + .pcm_dev = CAPTURE_2_FROM_I2S_2, + .controls = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Input Monitor Switch", + .index = 1, + .info = snd_ctl_boolean_mono_info, + .get = monitor_get, + .put = monitor_put, + .private_value = OXYGEN_ADC_MONITOR_B, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Input Monitor Volume", + .index = 1, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = monitor_volume_info, + .get = monitor_get, + .put = monitor_put, + .private_value = OXYGEN_ADC_MONITOR_B_HALF_VOL + | (1 << 8), + .tlv = { .p = monitor_db_scale, }, + }, + }, + }, + { + .pcm_dev = CAPTURE_1_FROM_SPDIF, + .controls = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Input Monitor Switch", + .info = snd_ctl_boolean_mono_info, + .get = monitor_get, + .put = monitor_put, + .private_value = OXYGEN_ADC_MONITOR_C, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Input Monitor Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = monitor_volume_info, + .get = monitor_get, + .put = monitor_put, + .private_value = OXYGEN_ADC_MONITOR_C_HALF_VOL + | (1 << 8), + .tlv = { .p = monitor_db_scale, }, + }, + }, + }, +}; + static const struct snd_kcontrol_new ac97_controls[] = { AC97_VOLUME("Mic Capture Volume", 0, AC97_MIC), AC97_SWITCH("Mic Capture Switch", 0, AC97_MIC, 15, 1), AC97_SWITCH("Mic Boost (+20dB)", 0, AC97_MIC, 6, 0), - AC97_VOLUME("Line Capture Volume", 0, AC97_LINE), AC97_SWITCH("Line Capture Switch", 0, AC97_LINE, 15, 1), AC97_VOLUME("CD Capture Volume", 0, AC97_CD), AC97_SWITCH("CD Capture Switch", 0, AC97_CD, 15, 1), @@ -773,11 +951,20 @@ static int add_controls(struct oxygen *chip, int oxygen_mixer_init(struct oxygen *chip) { + unsigned int i; int err; err = add_controls(chip, controls, ARRAY_SIZE(controls)); if (err < 0) return err; + for (i = 0; i < ARRAY_SIZE(monitor_controls); ++i) { + if (!(chip->model->pcm_dev_cfg & monitor_controls[i].pcm_dev)) + continue; + err = add_controls(chip, monitor_controls[i].controls, + ARRAY_SIZE(monitor_controls[i].controls)); + if (err < 0) + return err; + } if (chip->has_ac97_0) { err = add_controls(chip, ac97_controls, ARRAY_SIZE(ac97_controls)); diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c index b70046a..b17c405 100644 --- a/sound/pci/oxygen/oxygen_pcm.c +++ b/sound/pci/oxygen/oxygen_pcm.c @@ -119,7 +119,7 @@ static int oxygen_open(struct snd_pcm_substream *substream, runtime->private_data = (void *)(uintptr_t)channel; if (channel == PCM_B && chip->has_ac97_1 && - (chip->model->used_channels & OXYGEN_CHANNEL_AC97)) + (chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1)) runtime->hw = oxygen_ac97_hardware; else runtime->hw = *oxygen_hardware[channel]; @@ -365,7 +365,7 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream, return err; is_ac97 = chip->has_ac97_1 && - (chip->model->used_channels & OXYGEN_CHANNEL_AC97); + (chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1); spin_lock_irq(&chip->reg_lock); oxygen_write8_masked(chip, OXYGEN_REC_FORMAT, @@ -640,34 +640,39 @@ int oxygen_pcm_init(struct oxygen *chip) int outs, ins; int err; - outs = 1; /* OXYGEN_CHANNEL_MULTICH is always used */ - ins = !!(chip->model->used_channels & (OXYGEN_CHANNEL_A | - OXYGEN_CHANNEL_B)); - err = snd_pcm_new(chip->card, "Analog", 0, outs, ins, &pcm); - if (err < 0) - return err; - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &oxygen_multich_ops); - if (chip->model->used_channels & OXYGEN_CHANNEL_A) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, - &oxygen_rec_a_ops); - else if (chip->model->used_channels & OXYGEN_CHANNEL_B) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, - &oxygen_rec_b_ops); - pcm->private_data = chip; - pcm->private_free = oxygen_pcm_free; - strcpy(pcm->name, "Analog"); - snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, - SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data(chip->pci), - 512 * 1024, 2048 * 1024); - if (ins) - snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, - SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data(chip->pci), - 128 * 1024, 256 * 1024); - - outs = !!(chip->model->used_channels & OXYGEN_CHANNEL_SPDIF); - ins = !!(chip->model->used_channels & OXYGEN_CHANNEL_C); + outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_0_TO_I2S); + ins = !!(chip->model->pcm_dev_cfg & (CAPTURE_0_FROM_I2S_1 | + CAPTURE_0_FROM_I2S_2)); + if (outs | ins) { + err = snd_pcm_new(chip->card, "Analog", 0, outs, ins, &pcm); + if (err < 0) + return err; + if (outs) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &oxygen_multich_ops); + if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_1) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &oxygen_rec_a_ops); + else if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_2) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &oxygen_rec_b_ops); + pcm->private_data = chip; + pcm->private_free = oxygen_pcm_free; + strcpy(pcm->name, "Analog"); + if (outs) + snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, + SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + 512 * 1024, 2048 * 1024); + if (ins) + snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, + SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + 128 * 1024, 256 * 1024); + } + + outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_1_TO_SPDIF); + ins = !!(chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF); if (outs | ins) { err = snd_pcm_new(chip->card, "Digital", 1, outs, ins, &pcm); if (err < 0) @@ -686,12 +691,13 @@ int oxygen_pcm_init(struct oxygen *chip) 128 * 1024, 256 * 1024); } - outs = chip->has_ac97_1 && - (chip->model->used_channels & OXYGEN_CHANNEL_AC97); - ins = outs || - (chip->model->used_channels & (OXYGEN_CHANNEL_A | - OXYGEN_CHANNEL_B)) - == (OXYGEN_CHANNEL_A | OXYGEN_CHANNEL_B); + if (chip->has_ac97_1) { + outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_2_TO_AC97_1); + ins = !!(chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1); + } else { + outs = 0; + ins = !!(chip->model->pcm_dev_cfg & CAPTURE_2_FROM_I2S_2); + } if (outs | ins) { err = snd_pcm_new(chip->card, outs ? "AC97" : "Analog2", 2, outs, ins, &pcm); diff --git a/sound/pci/oxygen/pcm1796.h b/sound/pci/oxygen/pcm1796.h new file mode 100644 index 0000000..698bf46 --- /dev/null +++ b/sound/pci/oxygen/pcm1796.h @@ -0,0 +1,58 @@ +#ifndef PCM1796_H_INCLUDED +#define PCM1796_H_INCLUDED + +/* register 16 */ +#define PCM1796_ATL_MASK 0xff +/* register 17 */ +#define PCM1796_ATR_MASK 0xff +/* register 18 */ +#define PCM1796_MUTE 0x01 +#define PCM1796_DME 0x02 +#define PCM1796_DMF_MASK 0x0c +#define PCM1796_DMF_DISABLED 0x00 +#define PCM1796_DMF_48 0x04 +#define PCM1796_DMF_441 0x08 +#define PCM1796_DMF_32 0x0c +#define PCM1796_FMT_MASK 0x70 +#define PCM1796_FMT_16_RJUST 0x00 +#define PCM1796_FMT_20_RJUST 0x10 +#define PCM1796_FMT_24_RJUST 0x20 +#define PCM1796_FMT_24_LJUST 0x30 +#define PCM1796_FMT_16_I2S 0x40 +#define PCM1796_FMT_24_I2S 0x50 +#define PCM1796_ATLD 0x80 +/* register 19 */ +#define PCM1796_INZD 0x01 +#define PCM1796_FLT_MASK 0x02 +#define PCM1796_FLT_SHARP 0x00 +#define PCM1796_FLT_SLOW 0x02 +#define PCM1796_DFMS 0x04 +#define PCM1796_OPE 0x10 +#define PCM1796_ATS_MASK 0x60 +#define PCM1796_ATS_1 0x00 +#define PCM1796_ATS_2 0x20 +#define PCM1796_ATS_4 0x40 +#define PCM1796_ATS_8 0x60 +#define PCM1796_REV 0x80 +/* register 20 */ +#define PCM1796_OS_MASK 0x03 +#define PCM1796_OS_64 0x00 +#define PCM1796_OS_32 0x01 +#define PCM1796_OS_128 0x02 +#define PCM1796_CHSL_MASK 0x04 +#define PCM1796_CHSL_LEFT 0x00 +#define PCM1796_CHSL_RIGHT 0x04 +#define PCM1796_MONO 0x08 +#define PCM1796_DFTH 0x10 +#define PCM1796_DSD 0x20 +#define PCM1796_SRST 0x40 +/* register 21 */ +#define PCM1796_PCMZ 0x01 +#define PCM1796_DZ_MASK 0x06 +/* register 22 */ +#define PCM1796_ZFGL 0x01 +#define PCM1796_ZFGR 0x02 +/* register 23 */ +#define PCM1796_ID_MASK 0x1f + +#endif diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c index d163397..2c3daf3 100644 --- a/sound/pci/oxygen/virtuoso.c +++ b/sound/pci/oxygen/virtuoso.c @@ -30,10 +30,6 @@ * GPIO 5 <- external power present (D2X only) * GPIO 7 -> ALT * GPIO 8 -> enable output to speakers - * - * CM9780: - * - * GPIO 0 -> enable AC'97 bypass (line in -> ADC) */ #include @@ -47,6 +43,7 @@ #include #include "oxygen.h" #include "cm9780.h" +#include "pcm1796.h" MODULE_AUTHOR("Clemens Ladisch "); MODULE_DESCRIPTION("Asus AV200 driver"); @@ -80,62 +77,6 @@ MODULE_DEVICE_TABLE(pci, xonar_ids); #define GPIO_ALT 0x0080 #define GPIO_OUTPUT_ENABLE 0x0100 -#define GPIO_LINE_MUTE CM9780_GPO0 - -/* register 16 */ -#define PCM1796_ATL_MASK 0xff -/* register 17 */ -#define PCM1796_ATR_MASK 0xff -/* register 18 */ -#define PCM1796_MUTE 0x01 -#define PCM1796_DME 0x02 -#define PCM1796_DMF_MASK 0x0c -#define PCM1796_DMF_DISABLED 0x00 -#define PCM1796_DMF_48 0x04 -#define PCM1796_DMF_441 0x08 -#define PCM1796_DMF_32 0x0c -#define PCM1796_FMT_MASK 0x70 -#define PCM1796_FMT_16_RJUST 0x00 -#define PCM1796_FMT_20_RJUST 0x10 -#define PCM1796_FMT_24_RJUST 0x20 -#define PCM1796_FMT_24_LJUST 0x30 -#define PCM1796_FMT_16_I2S 0x40 -#define PCM1796_FMT_24_I2S 0x50 -#define PCM1796_ATLD 0x80 -/* register 19 */ -#define PCM1796_INZD 0x01 -#define PCM1796_FLT_MASK 0x02 -#define PCM1796_FLT_SHARP 0x00 -#define PCM1796_FLT_SLOW 0x02 -#define PCM1796_DFMS 0x04 -#define PCM1796_OPE 0x10 -#define PCM1796_ATS_MASK 0x60 -#define PCM1796_ATS_1 0x00 -#define PCM1796_ATS_2 0x20 -#define PCM1796_ATS_4 0x40 -#define PCM1796_ATS_8 0x60 -#define PCM1796_REV 0x80 -/* register 20 */ -#define PCM1796_OS_MASK 0x03 -#define PCM1796_OS_64 0x00 -#define PCM1796_OS_32 0x01 -#define PCM1796_OS_128 0x02 -#define PCM1796_CHSL_MASK 0x04 -#define PCM1796_CHSL_LEFT 0x00 -#define PCM1796_CHSL_RIGHT 0x04 -#define PCM1796_MONO 0x08 -#define PCM1796_DFTH 0x10 -#define PCM1796_DSD 0x20 -#define PCM1796_SRST 0x40 -/* register 21 */ -#define PCM1796_PCMZ 0x01 -#define PCM1796_DZ_MASK 0x06 -/* register 22 */ -#define PCM1796_ZFGL 0x01 -#define PCM1796_ZFGR 0x02 -/* register 23 */ -#define PCM1796_ID_MASK 0x1f - struct xonar_data { u8 is_d2x; u8 has_power; @@ -187,7 +128,6 @@ static void xonar_init(struct oxygen *chip) & GPIO_EXT_POWER); } oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC); - oxygen_ac97_clear_bits(chip, 0, CM9780_GPIO_STATUS, GPIO_LINE_MUTE); msleep(300); oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_OUTPUT_ENABLE); oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); @@ -272,49 +212,6 @@ static void xonar_gpio_changed(struct oxygen *chip) } } -static void mute_ac97_ctl(struct oxygen *chip, unsigned int control) -{ - unsigned int index = chip->controls[control]->private_value & 0xff; - u16 value; - - value = oxygen_read_ac97(chip, 0, index); - if (!(value & 0x8000)) { - oxygen_write_ac97(chip, 0, index, value | 0x8000); - snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - &chip->controls[control]->id); - } -} - -static void xonar_ac97_switch_hook(struct oxygen *chip, unsigned int codec, - unsigned int reg, int mute) -{ - if (codec != 0) - return; - /* line-in is exclusive */ - switch (reg) { - case AC97_LINE: - oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS, - mute ? GPIO_LINE_MUTE : 0, - GPIO_LINE_MUTE); - if (!mute) { - mute_ac97_ctl(chip, CONTROL_MIC_CAPTURE_SWITCH); - mute_ac97_ctl(chip, CONTROL_CD_CAPTURE_SWITCH); - mute_ac97_ctl(chip, CONTROL_AUX_CAPTURE_SWITCH); - } - break; - case AC97_MIC: - case AC97_CD: - case AC97_VIDEO: - case AC97_AUX: - if (!mute) { - oxygen_ac97_set_bits(chip, 0, CM9780_GPIO_STATUS, - GPIO_LINE_MUTE); - mute_ac97_ctl(chip, CONTROL_LINE_CAPTURE_SWITCH); - } - break; - } -} - static int pcm1796_volume_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) { @@ -374,8 +271,6 @@ static int xonar_control_filter(struct snd_kcontrol_new *template) } else if (!strncmp(template->name, "CD Capture ", 11)) { /* CD in is actually connected to the video in pin */ template->private_value ^= AC97_CD ^ AC97_VIDEO; - } else if (!strcmp(template->name, "Line Capture Volume")) { - return 1; /* line-in bypasses the AC'97 mixer */ } return 0; } @@ -398,15 +293,16 @@ static const struct oxygen_model model_xonar = { .set_adc_params = set_cs5381_params, .update_dac_volume = update_pcm1796_volume, .update_dac_mute = update_pcm1796_mute, - .ac97_switch_hook = xonar_ac97_switch_hook, .gpio_changed = xonar_gpio_changed, .model_data_size = sizeof(struct xonar_data), + .pcm_dev_cfg = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + CAPTURE_0_FROM_I2S_2 | + CAPTURE_1_FROM_SPDIF, .dac_channels = 8, - .used_channels = OXYGEN_CHANNEL_B | - OXYGEN_CHANNEL_C | - OXYGEN_CHANNEL_SPDIF | - OXYGEN_CHANNEL_MULTICH, - .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5, + .misc_flags = OXYGEN_MISC_MIDI, + .function_flags = OXYGEN_FUNCTION_SPI | + OXYGEN_FUNCTION_ENABLE_SPI_4_5, .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, }; @@ -423,7 +319,7 @@ static int __devinit xonar_probe(struct pci_dev *pci, ++dev; return -ENOENT; } - err = oxygen_pci_probe(pci, index[dev], id[dev], 1, &model_xonar); + err = oxygen_pci_probe(pci, index[dev], id[dev], &model_xonar); if (err >= 0) ++dev; return err; diff --git a/sound/pci/oxygen/wm8785.h b/sound/pci/oxygen/wm8785.h new file mode 100644 index 0000000..8c23e31 --- /dev/null +++ b/sound/pci/oxygen/wm8785.h @@ -0,0 +1,45 @@ +#ifndef WM8785_H_INCLUDED +#define WM8785_H_INCLUDED + +#define WM8785_R0 0 +#define WM8785_R1 1 +#define WM8785_R2 2 +#define WM8785_R7 7 + +/* R0 */ +#define WM8785_MCR_MASK 0x007 +#define WM8785_MCR_SLAVE 0x000 +#define WM8785_MCR_MASTER_128 0x001 +#define WM8785_MCR_MASTER_192 0x002 +#define WM8785_MCR_MASTER_256 0x003 +#define WM8785_MCR_MASTER_384 0x004 +#define WM8785_MCR_MASTER_512 0x005 +#define WM8785_MCR_MASTER_768 0x006 +#define WM8785_OSR_MASK 0x018 +#define WM8785_OSR_SINGLE 0x000 +#define WM8785_OSR_DOUBLE 0x008 +#define WM8785_OSR_QUAD 0x010 +#define WM8785_FORMAT_MASK 0x060 +#define WM8785_FORMAT_RJUST 0x000 +#define WM8785_FORMAT_LJUST 0x020 +#define WM8785_FORMAT_I2S 0x040 +#define WM8785_FORMAT_DSP 0x060 +/* R1 */ +#define WM8785_WL_MASK 0x003 +#define WM8785_WL_16 0x000 +#define WM8785_WL_20 0x001 +#define WM8785_WL_24 0x002 +#define WM8785_WL_32 0x003 +#define WM8785_LRP 0x004 +#define WM8785_BCLKINV 0x008 +#define WM8785_LRSWAP 0x010 +#define WM8785_DEVNO_MASK 0x0e0 +/* R2 */ +#define WM8785_HPFR 0x001 +#define WM8785_HPFL 0x002 +#define WM8785_SDODIS 0x004 +#define WM8785_PWRDNR 0x008 +#define WM8785_PWRDNL 0x010 +#define WM8785_TDM_MASK 0x1c0 + +#endif diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c index 9d5bb76..7fdcdc8 100644 --- a/sound/pci/pcxhr/pcxhr.c +++ b/sound/pci/pcxhr/pcxhr.c @@ -458,7 +458,7 @@ static int pcxhr_update_r_buffer(struct pcxhr_stream *stream) snd_printdd("pcxhr_update_r_buffer(pcm%c%d) : addr(%p) bytes(%zx) subs(%d)\n", is_capture ? 'c' : 'p', - chip->chip_idx, (void*)subs->runtime->dma_addr, + chip->chip_idx, (void *)(long)subs->runtime->dma_addr, subs->runtime->dma_bytes, subs->number); pcxhr_init_rmh(&rmh, CMD_UPDATE_R_BUFFERS); @@ -626,7 +626,7 @@ static void pcxhr_trigger_tasklet(unsigned long arg) #ifdef CONFIG_SND_DEBUG_DETECT do_gettimeofday(&my_tv2); snd_printdd("***TRIGGER TASKLET*** TIME = %ld (err = %x)\n", - my_tv2.tv_usec - my_tv1.tv_usec, err); + (long)(my_tv2.tv_usec - my_tv1.tv_usec), err); #endif } @@ -846,7 +846,6 @@ static int pcxhr_open(struct snd_pcm_substream *subs) struct pcxhr_mgr *mgr = chip->mgr; struct snd_pcm_runtime *runtime = subs->runtime; struct pcxhr_stream *stream; - int is_capture; mutex_lock(&mgr->setup_mutex); @@ -856,12 +855,10 @@ static int pcxhr_open(struct snd_pcm_substream *subs) if( subs->stream == SNDRV_PCM_STREAM_PLAYBACK ) { snd_printdd("pcxhr_open playback chip%d subs%d\n", chip->chip_idx, subs->number); - is_capture = 0; stream = &chip->playback_stream[subs->number]; } else { snd_printdd("pcxhr_open capture chip%d subs%d\n", chip->chip_idx, subs->number); - is_capture = 1; if (mgr->mono_capture) runtime->hw.channels_max = 1; else diff --git a/sound/pci/pcxhr/pcxhr_core.c b/sound/pci/pcxhr/pcxhr_core.c index c4e415d..78aa81f 100644 --- a/sound/pci/pcxhr/pcxhr_core.c +++ b/sound/pci/pcxhr/pcxhr_core.c @@ -897,7 +897,7 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, int capture_m #ifdef CONFIG_SND_DEBUG_DETECT do_gettimeofday(&my_tv2); snd_printdd("***SET PIPE STATE*** TIME = %ld (err = %x)\n", - my_tv2.tv_usec - my_tv1.tv_usec, err); + (long)(my_tv2.tv_usec - my_tv1.tv_usec), err); #endif return 0; } @@ -1005,30 +1005,37 @@ void pcxhr_msg_tasklet(unsigned long arg) int nb_stream = (prmh->stat[i] >> (2*FIELD_SIZE)) & MASK_FIRST_FIELD; int pipe = prmh->stat[i] & MASK_FIRST_FIELD; int is_capture = prmh->stat[i] & 0x400000; - u32 err; + u32 err2; if (prmh->stat[i] & 0x800000) { /* if BIT_END */ snd_printdd("TASKLET : End%sPipe %d\n", is_capture ? "Record" : "Play", pipe); } i++; - err = prmh->stat[i] ? prmh->stat[i] : prmh->stat[i+1]; - if (err) - pcxhr_handle_async_err(mgr, err, PCXHR_ERR_PIPE, + err2 = prmh->stat[i] ? prmh->stat[i] : prmh->stat[i+1]; + if (err2) + pcxhr_handle_async_err(mgr, err2, + PCXHR_ERR_PIPE, pipe, is_capture); i += 2; for (j = 0; j < nb_stream; j++) { - err = prmh->stat[i] ? prmh->stat[i] : prmh->stat[i+1]; - if (err) - pcxhr_handle_async_err(mgr, err, PCXHR_ERR_STREAM, - pipe, is_capture); + err2 = prmh->stat[i] ? + prmh->stat[i] : prmh->stat[i+1]; + if (err2) + pcxhr_handle_async_err(mgr, err2, + PCXHR_ERR_STREAM, + pipe, + is_capture); i += 2; } for (j = 0; j < nb_audio; j++) { - err = prmh->stat[i] ? prmh->stat[i] : prmh->stat[i+1]; - if (err) - pcxhr_handle_async_err(mgr, err, PCXHR_ERR_AUDIO, - pipe, is_capture); + err2 = prmh->stat[i] ? + prmh->stat[i] : prmh->stat[i+1]; + if (err2) + pcxhr_handle_async_err(mgr, err2, + PCXHR_ERR_AUDIO, + pipe, + is_capture); i += 2; } } diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index 9408b1e..979f7da 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -1630,14 +1630,14 @@ static int snd_riptide_playback_open(struct snd_pcm_substream *substream) struct snd_riptide *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct pcmhw *data; - int index = substream->number; + int sub_num = substream->number; - chip->playback_substream[index] = substream; + chip->playback_substream[sub_num] = substream; runtime->hw = snd_riptide_playback; data = kzalloc(sizeof(struct pcmhw), GFP_KERNEL); - data->paths = lbus_play_paths[index]; - data->id = play_ids[index]; - data->source = play_sources[index]; + data->paths = lbus_play_paths[sub_num]; + data->id = play_ids[sub_num]; + data->source = play_sources[sub_num]; data->intdec[0] = 0xff; data->intdec[1] = 0xff; data->state = ST_STOP; @@ -1670,10 +1670,10 @@ static int snd_riptide_playback_close(struct snd_pcm_substream *substream) { struct snd_riptide *chip = snd_pcm_substream_chip(substream); struct pcmhw *data = get_pcmhwdev(substream); - int index = substream->number; + int sub_num = substream->number; substream->runtime->private_data = NULL; - chip->playback_substream[index] = NULL; + chip->playback_substream[sub_num] = NULL; kfree(data); return 0; } diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index df184aa..e7ef3a1 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -1350,7 +1350,8 @@ static int __devinit snd_rme32_create(struct rme32 * rme32) return err; rme32->port = pci_resource_start(rme32->pci, 0); - if ((rme32->iobase = ioremap_nocache(rme32->port, RME32_IO_SIZE)) == 0) { + rme32->iobase = ioremap_nocache(rme32->port, RME32_IO_SIZE); + if (!rme32->iobase) { snd_printk(KERN_ERR "unable to remap memory region 0x%lx-0x%lx\n", rme32->port, rme32->port + RME32_IO_SIZE - 1); return -ENOMEM; diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index fb0a4ee..3fdd488 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -1559,7 +1559,8 @@ snd_rme96_create(struct rme96 *rme96) return err; rme96->port = pci_resource_start(rme96->pci, 0); - if ((rme96->iobase = ioremap_nocache(rme96->port, RME96_IO_SIZE)) == 0) { + rme96->iobase = ioremap_nocache(rme96->port, RME96_IO_SIZE); + if (!rme96->iobase) { snd_printk(KERN_ERR "unable to remap memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1); return -ENOMEM; } diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 1be84f2..4d6fbb3 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -318,6 +318,10 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin"); #define HDSP_midi1IRQPending (1<<31) #define HDSP_spdifFrequencyMask (HDSP_spdifFrequency0|HDSP_spdifFrequency1|HDSP_spdifFrequency2) +#define HDSP_spdifFrequencyMask_9632 (HDSP_spdifFrequency0|\ + HDSP_spdifFrequency1|\ + HDSP_spdifFrequency2|\ + HDSP_spdifFrequency3) #define HDSP_spdifFrequency32KHz (HDSP_spdifFrequency0) #define HDSP_spdifFrequency44_1KHz (HDSP_spdifFrequency1) @@ -328,7 +332,9 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin"); #define HDSP_spdifFrequency96KHz (HDSP_spdifFrequency2|HDSP_spdifFrequency1) /* This is for H9632 cards */ -#define HDSP_spdifFrequency128KHz HDSP_spdifFrequencyMask +#define HDSP_spdifFrequency128KHz (HDSP_spdifFrequency0|\ + HDSP_spdifFrequency1|\ + HDSP_spdifFrequency2) #define HDSP_spdifFrequency176_4KHz HDSP_spdifFrequency3 #define HDSP_spdifFrequency192KHz (HDSP_spdifFrequency3|HDSP_spdifFrequency0) @@ -885,28 +891,15 @@ static int snd_hdsp_use_is_exclusive(struct hdsp *hdsp) return ret; } -static int hdsp_external_sample_rate (struct hdsp *hdsp) -{ - unsigned int status2 = hdsp_read(hdsp, HDSP_status2Register); - unsigned int rate_bits = status2 & HDSP_systemFrequencyMask; - - switch (rate_bits) { - case HDSP_systemFrequency32: return 32000; - case HDSP_systemFrequency44_1: return 44100; - case HDSP_systemFrequency48: return 48000; - case HDSP_systemFrequency64: return 64000; - case HDSP_systemFrequency88_2: return 88200; - case HDSP_systemFrequency96: return 96000; - default: - return 0; - } -} - static int hdsp_spdif_sample_rate(struct hdsp *hdsp) { unsigned int status = hdsp_read(hdsp, HDSP_statusRegister); unsigned int rate_bits = (status & HDSP_spdifFrequencyMask); + /* For the 9632, the mask is different */ + if (hdsp->io_type == H9632) + rate_bits = (status & HDSP_spdifFrequencyMask_9632); + if (status & HDSP_SPDIFErrorFlag) return 0; @@ -933,6 +926,31 @@ static int hdsp_spdif_sample_rate(struct hdsp *hdsp) return 0; } +static int hdsp_external_sample_rate(struct hdsp *hdsp) +{ + unsigned int status2 = hdsp_read(hdsp, HDSP_status2Register); + unsigned int rate_bits = status2 & HDSP_systemFrequencyMask; + + /* For the 9632 card, there seems to be no bit for indicating external + * sample rate greater than 96kHz. The card reports the corresponding + * single speed. So the best means seems to get spdif rate when + * autosync reference is spdif */ + if (hdsp->io_type == H9632 && + hdsp_autosync_ref(hdsp) == HDSP_AUTOSYNC_FROM_SPDIF) + return hdsp_spdif_sample_rate(hdsp); + + switch (rate_bits) { + case HDSP_systemFrequency32: return 32000; + case HDSP_systemFrequency44_1: return 44100; + case HDSP_systemFrequency48: return 48000; + case HDSP_systemFrequency64: return 64000; + case HDSP_systemFrequency88_2: return 88200; + case HDSP_systemFrequency96: return 96000; + default: + return 0; + } +} + static void hdsp_compute_period_size(struct hdsp *hdsp) { hdsp->period_bytes = 1 << ((hdsp_decode_latency(hdsp->control_register) + 8)); diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 9a19ae6..38c931c 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -1028,9 +1028,9 @@ static inline void snd_hdspm_midi_write_byte (struct hdspm *hdspm, int id, { /* the hardware already does the relevant bit-mask with 0xff */ if (id) - return hdspm_write(hdspm, HDSPM_midiDataOut1, val); + hdspm_write(hdspm, HDSPM_midiDataOut1, val); else - return hdspm_write(hdspm, HDSPM_midiDataOut0, val); + hdspm_write(hdspm, HDSPM_midiDataOut0, val); } static inline int snd_hdspm_midi_input_available (struct hdspm *hdspm, int id) diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index 42c1eb7..29b3056 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -2249,6 +2249,8 @@ static int snd_ymfpci_free(struct snd_ymfpci *chip) #ifdef CONFIG_PM vfree(chip->saved_regs); #endif + if (chip->irq >= 0) + free_irq(chip->irq, chip); release_and_free_resource(chip->mpu_res); release_and_free_resource(chip->fm_res); snd_ymfpci_free_gameport(chip); @@ -2257,8 +2259,6 @@ static int snd_ymfpci_free(struct snd_ymfpci *chip) if (chip->work_ptr.area) snd_dma_free_pages(&chip->work_ptr); - if (chip->irq >= 0) - free_irq(chip->irq, chip); release_and_free_resource(chip->res_reg_area); pci_write_config_word(chip->pci, 0x40, chip->old_legacy_ctrl); diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 2765852..a3b51df 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -29,6 +29,7 @@ source "sound/soc/pxa/Kconfig" source "sound/soc/s3c24xx/Kconfig" source "sound/soc/sh/Kconfig" source "sound/soc/fsl/Kconfig" +source "sound/soc/davinci/Kconfig" # Supported codecs source "sound/soc/codecs/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 4869c9a..e489dbd 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,4 +1,4 @@ snd-soc-core-objs := soc-core.o soc-dapm.o obj-$(CONFIG_SND_SOC) += snd-soc-core.o -obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ sh/ fsl/ +obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 898a7d3..3903ab7 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -18,6 +18,10 @@ config SND_SOC_WM9712 tristate depends on SND_SOC +config SND_SOC_WM9713 + tristate + depends on SND_SOC + # Cirrus Logic CS4270 Codec config SND_SOC_CS4270 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index c6e5338..4e1314c 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -3,6 +3,7 @@ snd-soc-wm8731-objs := wm8731.o snd-soc-wm8750-objs := wm8750.o snd-soc-wm8753-objs := wm8753.o snd-soc-wm9712-objs := wm9712.o +snd-soc-wm9713-objs := wm9713.o snd-soc-cs4270-objs := cs4270.o snd-soc-tlv320aic3x-objs := tlv320aic3x.o @@ -11,5 +12,6 @@ obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o +obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 889a897..e6a730b 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -660,33 +660,43 @@ struct aic3x_rate_divs { /* AIC3X codec mclk clock divider coefficients */ static const struct aic3x_rate_divs aic3x_divs[] = { /* 8k */ + {12000000, 8000, 48000, 0xa, 16, 3840}, {22579200, 8000, 48000, 0xa, 8, 7075}, {33868800, 8000, 48000, 0xa, 5, 8049}, /* 11.025k */ + {12000000, 11025, 44100, 0x6, 15, 528}, {22579200, 11025, 44100, 0x6, 8, 0}, {33868800, 11025, 44100, 0x6, 5, 3333}, /* 16k */ + {12000000, 16000, 48000, 0x4, 16, 3840}, {22579200, 16000, 48000, 0x4, 8, 7075}, {33868800, 16000, 48000, 0x4, 5, 8049}, /* 22.05k */ + {12000000, 22050, 44100, 0x2, 15, 528}, {22579200, 22050, 44100, 0x2, 8, 0}, {33868800, 22050, 44100, 0x2, 5, 3333}, /* 32k */ + {12000000, 32000, 48000, 0x1, 16, 3840}, {22579200, 32000, 48000, 0x1, 8, 7075}, {33868800, 32000, 48000, 0x1, 5, 8049}, /* 44.1k */ + {12000000, 44100, 44100, 0x0, 15, 528}, {22579200, 44100, 44100, 0x0, 8, 0}, {33868800, 44100, 44100, 0x0, 5, 3333}, /* 48k */ + {12000000, 48000, 48000, 0x0, 16, 3840}, {22579200, 48000, 48000, 0x0, 8, 7075}, {33868800, 48000, 48000, 0x0, 5, 8049}, /* 64k */ + {12000000, 64000, 96000, 0x1, 16, 3840}, {22579200, 64000, 96000, 0x1, 8, 7075}, {33868800, 64000, 96000, 0x1, 5, 8049}, /* 88.2k */ + {12000000, 88200, 88200, 0x0, 15, 528}, {22579200, 88200, 88200, 0x0, 8, 0}, {33868800, 88200, 88200, 0x0, 5, 3333}, /* 96k */ + {12000000, 96000, 96000, 0x0, 16, 3840}, {22579200, 96000, 96000, 0x0, 8, 7075}, {33868800, 96000, 96000, 0x0, 5, 8049}, }; @@ -807,6 +817,7 @@ static int aic3x_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai, struct aic3x_priv *aic3x = codec->private_data; switch (freq) { + case 12000000: case 22579200: case 33868800: aic3x->sysclk = freq; diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index ddd9c71..76a5c7b 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -198,6 +198,7 @@ static const char *wm8753_mic_sel[] = {"Mic 1", "Mic 2", "Mic 3"}; static const char *wm8753_dai_mode[] = {"DAI 0", "DAI 1", "DAI 2", "DAI 3"}; static const char *wm8753_dat_sel[] = {"Stereo", "Left ADC", "Right ADC", "Channel Swap"}; +static const char *wm8753_rout2_phase[] = {"Non Inverted", "Inverted"}; static const struct soc_enum wm8753_enum[] = { SOC_ENUM_SINGLE(WM8753_BASS, 7, 2, wm8753_base), @@ -228,6 +229,7 @@ SOC_ENUM_SINGLE(WM8753_ADC, 4, 2, wm8753_adc_filter), SOC_ENUM_SINGLE(WM8753_MICBIAS, 6, 3, wm8753_mic_sel), SOC_ENUM_SINGLE(WM8753_IOCTL, 2, 4, wm8753_dai_mode), SOC_ENUM_SINGLE(WM8753_ADC, 7, 4, wm8753_dat_sel), +SOC_ENUM_SINGLE(WM8753_OUTCTL, 2, 2, wm8753_rout2_phase), }; @@ -279,7 +281,7 @@ SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8753_LOUT2V, WM8753_ROUT2V, 7, 1, 0 SOC_SINGLE("Mono Bypass Playback Volume", WM8753_MOUTM1, 4, 7, 1), SOC_SINGLE("Mono Sidetone Playback Volume", WM8753_MOUTM2, 4, 7, 1), -SOC_SINGLE("Mono Voice Playback Volume", WM8753_MOUTM2, 4, 7, 1), +SOC_SINGLE("Mono Voice Playback Volume", WM8753_MOUTM2, 0, 7, 1), SOC_SINGLE("Mono Playback ZC Switch", WM8753_MOUTV, 7, 1, 0), SOC_ENUM("Bass Boost", wm8753_enum[0]), @@ -330,6 +332,7 @@ SOC_SINGLE("Mic1 Capture Volume", WM8753_INCTL1, 5, 3, 0), SOC_ENUM_EXT("DAI Mode", wm8753_enum[26], wm8753_get_dai, wm8753_set_dai), SOC_ENUM("ADC Data Select", wm8753_enum[27]), +SOC_ENUM("ROUT2 Phase", wm8753_enum[28]), }; /* add non dapm controls */ diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c new file mode 100644 index 0000000..e3174c4 --- /dev/null +++ b/sound/soc/codecs/wm9713.c @@ -0,0 +1,1291 @@ +/* + * wm9713.c -- ALSA Soc WM9713 codec support + * + * Copyright 2006 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Revision history + * 4th Feb 2006 Initial version. + * + * Features:- + * + * o Support for AC97 Codec, Voice DAC and Aux DAC + * o Support for DAPM + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm9713.h" + +#define WM9713_VERSION "0.15" + +struct wm9713_priv { + u32 pll_in; /* PLL input frequency */ + u32 pll_out; /* PLL output frequency */ +}; + +static unsigned int ac97_read(struct snd_soc_codec *codec, + unsigned int reg); +static int ac97_write(struct snd_soc_codec *codec, + unsigned int reg, unsigned int val); + +/* + * WM9713 register cache + * Reg 0x3c bit 15 is used by touch driver. + */ +static const u16 wm9713_reg[] = { + 0x6174, 0x8080, 0x8080, 0x8080, + 0xc880, 0xe808, 0xe808, 0x0808, + 0x00da, 0x8000, 0xd600, 0xaaa0, + 0xaaa0, 0xaaa0, 0x0000, 0x0000, + 0x0f0f, 0x0040, 0x0000, 0x7f00, + 0x0405, 0x0410, 0xbb80, 0xbb80, + 0x0000, 0xbb80, 0x0000, 0x4523, + 0x0000, 0x2000, 0x7eff, 0xffff, + 0x0000, 0x0000, 0x0080, 0x0000, + 0x0000, 0x0000, 0xfffe, 0xffff, + 0x0000, 0x0000, 0x0000, 0xfffe, + 0x4000, 0x0000, 0x0000, 0x0000, + 0xb032, 0x3e00, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0006, + 0x0001, 0x0000, 0x574d, 0x4c13, + 0x0000, 0x0000, 0x0000 +}; + +/* virtual HP mixers regs */ +#define HPL_MIXER 0x80 +#define HPR_MIXER 0x82 +#define MICB_MUX 0x82 + +static const char *wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"}; +static const char *wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"}; +static const char *wm9713_rec_src[] = + {"Mic 1", "Mic 2", "Line", "Mono In", "Headphone", "Speaker", + "Mono Out", "Zh"}; +static const char *wm9713_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"}; +static const char *wm9713_alc_select[] = {"None", "Left", "Right", "Stereo"}; +static const char *wm9713_mono_pga[] = {"Vmid", "Zh", "Mono", "Inv", + "Mono Vmid", "Inv Vmid"}; +static const char *wm9713_spk_pga[] = + {"Vmid", "Zh", "Headphone", "Speaker", "Inv", "Headphone Vmid", + "Speaker Vmid", "Inv Vmid"}; +static const char *wm9713_hp_pga[] = {"Vmid", "Zh", "Headphone", + "Headphone Vmid"}; +static const char *wm9713_out3_pga[] = {"Vmid", "Zh", "Inv 1", "Inv 1 Vmid"}; +static const char *wm9713_out4_pga[] = {"Vmid", "Zh", "Inv 2", "Inv 2 Vmid"}; +static const char *wm9713_dac_inv[] = + {"Off", "Mono", "Speaker", "Left Headphone", "Right Headphone", + "Headphone Mono", "NC", "Vmid"}; +static const char *wm9713_bass[] = {"Linear Control", "Adaptive Boost"}; +static const char *wm9713_ng_type[] = {"Constant Gain", "Mute"}; +static const char *wm9713_mic_select[] = {"Mic 1", "Mic 2 A", "Mic 2 B"}; +static const char *wm9713_micb_select[] = {"MPB", "MPA"}; + +static const struct soc_enum wm9713_enum[] = { +SOC_ENUM_SINGLE(AC97_LINE, 3, 4, wm9713_mic_mixer), /* record mic mixer 0 */ +SOC_ENUM_SINGLE(AC97_VIDEO, 14, 4, wm9713_rec_mux), /* record mux hp 1 */ +SOC_ENUM_SINGLE(AC97_VIDEO, 9, 4, wm9713_rec_mux), /* record mux mono 2 */ +SOC_ENUM_SINGLE(AC97_VIDEO, 3, 8, wm9713_rec_src), /* record mux left 3 */ +SOC_ENUM_SINGLE(AC97_VIDEO, 0, 8, wm9713_rec_src), /* record mux right 4*/ +SOC_ENUM_DOUBLE(AC97_CD, 14, 6, 2, wm9713_rec_gain), /* record step size 5 */ +SOC_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9713_alc_select), /* alc source select 6*/ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 14, 4, wm9713_mono_pga), /* mono input select 7 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 11, 8, wm9713_spk_pga), /* speaker left input select 8 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 8, 8, wm9713_spk_pga), /* speaker right input select 9 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 6, 3, wm9713_hp_pga), /* headphone left input 10 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 4, 3, wm9713_hp_pga), /* headphone right input 11 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 2, 4, wm9713_out3_pga), /* out 3 source 12 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 0, 4, wm9713_out4_pga), /* out 4 source 13 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 13, 8, wm9713_dac_inv), /* dac invert 1 14 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 10, 8, wm9713_dac_inv), /* dac invert 2 15 */ +SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, wm9713_bass), /* bass control 16 */ +SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9713_ng_type), /* noise gate type 17 */ +SOC_ENUM_SINGLE(AC97_3D_CONTROL, 12, 3, wm9713_mic_select), /* mic selection 18 */ +SOC_ENUM_SINGLE(MICB_MUX, 0, 2, wm9713_micb_select), /* mic selection 19 */ +}; + +static const struct snd_kcontrol_new wm9713_snd_ac97_controls[] = { +SOC_DOUBLE("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1), +SOC_DOUBLE("Speaker Playback Switch", AC97_MASTER, 15, 7, 1, 1), +SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1), +SOC_DOUBLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 7, 1, 1), +SOC_DOUBLE("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1), +SOC_DOUBLE("PCM Playback Volume", AC97_PHONE, 8, 0, 31, 1), +SOC_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1), +SOC_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1), + +SOC_SINGLE("Mic Boost (+20dB) Switch", AC97_LINE, 5, 1, 0), +SOC_SINGLE("Mic Headphone Mixer Volume", AC97_LINE, 0, 7, 1), + +SOC_SINGLE("Capture Switch", AC97_CD, 15, 1, 1), +SOC_ENUM("Capture Volume Steps", wm9713_enum[5]), +SOC_DOUBLE("Capture Volume", AC97_CD, 8, 0, 31, 0), +SOC_SINGLE("Capture ZC Switch", AC97_CD, 7, 1, 0), + +SOC_SINGLE("Capture to Headphone Volume", AC97_VIDEO, 11, 7, 1), +SOC_SINGLE("Capture to Mono Boost (+20dB) Switch", AC97_VIDEO, 8, 1, 0), +SOC_SINGLE("Capture ADC Boost (+20dB) Switch", AC97_VIDEO, 6, 1, 0), + +SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0), +SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0), +SOC_SINGLE("ALC Decay Time ", AC97_CODEC_CLASS_REV, 4, 15, 0), +SOC_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0), +SOC_ENUM("ALC Function", wm9713_enum[6]), +SOC_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 0), +SOC_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 0), +SOC_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0), +SOC_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0), +SOC_ENUM("ALC NG Type", wm9713_enum[17]), +SOC_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 0), + +SOC_DOUBLE("Speaker Playback ZC Switch", AC97_MASTER, 14, 6, 1, 0), +SOC_DOUBLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 14, 6, 1, 0), + +SOC_SINGLE("Out4 Playback Switch", AC97_MASTER_MONO, 15, 1, 1), +SOC_SINGLE("Out4 Playback ZC Switch", AC97_MASTER_MONO, 14, 1, 0), +SOC_SINGLE("Out4 Playback Volume", AC97_MASTER_MONO, 8, 63, 1), + +SOC_SINGLE("Out3 Playback Switch", AC97_MASTER_MONO, 7, 1, 1), +SOC_SINGLE("Out3 Playback ZC Switch", AC97_MASTER_MONO, 6, 1, 0), +SOC_SINGLE("Out3 Playback Volume", AC97_MASTER_MONO, 0, 63, 1), + +SOC_SINGLE("Mono Capture Volume", AC97_MASTER_TONE, 8, 31, 1), +SOC_SINGLE("Mono Playback Switch", AC97_MASTER_TONE, 7, 1, 1), +SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_TONE, 6, 1, 0), +SOC_SINGLE("Mono Playback Volume", AC97_MASTER_TONE, 0, 31, 1), + +SOC_SINGLE("PC Beep Playback Headphone Volume", AC97_AUX, 12, 7, 1), +SOC_SINGLE("PC Beep Playback Speaker Volume", AC97_AUX, 8, 7, 1), +SOC_SINGLE("PC Beep Playback Mono Volume", AC97_AUX, 4, 7, 1), + +SOC_SINGLE("Voice Playback Headphone Volume", AC97_PCM, 12, 7, 1), +SOC_SINGLE("Voice Playback Master Volume", AC97_PCM, 8, 7, 1), +SOC_SINGLE("Voice Playback Mono Volume", AC97_PCM, 4, 7, 1), + +SOC_SINGLE("Aux Playback Headphone Volume", AC97_REC_SEL, 12, 7, 1), +SOC_SINGLE("Aux Playback Master Volume", AC97_REC_SEL, 8, 7, 1), +SOC_SINGLE("Aux Playback Mono Volume", AC97_REC_SEL, 4, 7, 1), + +SOC_ENUM("Bass Control", wm9713_enum[16]), +SOC_SINGLE("Bass Cut-off Switch", AC97_GENERAL_PURPOSE, 12, 1, 1), +SOC_SINGLE("Tone Cut-off Switch", AC97_GENERAL_PURPOSE, 4, 1, 1), +SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_GENERAL_PURPOSE, 6, 1, 0), +SOC_SINGLE("Bass Volume", AC97_GENERAL_PURPOSE, 8, 15, 1), +SOC_SINGLE("Tone Volume", AC97_GENERAL_PURPOSE, 0, 15, 1), + +SOC_SINGLE("3D Upper Cut-off Switch", AC97_REC_GAIN_MIC, 5, 1, 0), +SOC_SINGLE("3D Lower Cut-off Switch", AC97_REC_GAIN_MIC, 4, 1, 0), +SOC_SINGLE("3D Depth", AC97_REC_GAIN_MIC, 0, 15, 1), +}; + +/* add non dapm controls */ +static int wm9713_add_controls(struct snd_soc_codec *codec) +{ + int err, i; + + for (i = 0; i < ARRAY_SIZE(wm9713_snd_ac97_controls); i++) { + err = snd_ctl_add(codec->card, + snd_soc_cnew(&wm9713_snd_ac97_controls[i], + codec, NULL)); + if (err < 0) + return err; + } + return 0; +} + +/* We have to create a fake left and right HP mixers because + * the codec only has a single control that is shared by both channels. + * This makes it impossible to determine the audio path using the current + * register map, thus we add a new (virtual) register to help determine the + * audio route within the device. + */ +static int mixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + u16 l, r, beep, tone, phone, rec, pcm, aux; + + l = ac97_read(w->codec, HPL_MIXER); + r = ac97_read(w->codec, HPR_MIXER); + beep = ac97_read(w->codec, AC97_PC_BEEP); + tone = ac97_read(w->codec, AC97_MASTER_TONE); + phone = ac97_read(w->codec, AC97_PHONE); + rec = ac97_read(w->codec, AC97_REC_SEL); + pcm = ac97_read(w->codec, AC97_PCM); + aux = ac97_read(w->codec, AC97_AUX); + + if (event & SND_SOC_DAPM_PRE_REG) + return 0; + if ((l & 0x1) || (r & 0x1)) + ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff); + else + ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000); + + if ((l & 0x2) || (r & 0x2)) + ac97_write(w->codec, AC97_MASTER_TONE, tone & 0x7fff); + else + ac97_write(w->codec, AC97_MASTER_TONE, tone | 0x8000); + + if ((l & 0x4) || (r & 0x4)) + ac97_write(w->codec, AC97_PHONE, phone & 0x7fff); + else + ac97_write(w->codec, AC97_PHONE, phone | 0x8000); + + if ((l & 0x8) || (r & 0x8)) + ac97_write(w->codec, AC97_REC_SEL, rec & 0x7fff); + else + ac97_write(w->codec, AC97_REC_SEL, rec | 0x8000); + + if ((l & 0x10) || (r & 0x10)) + ac97_write(w->codec, AC97_PCM, pcm & 0x7fff); + else + ac97_write(w->codec, AC97_PCM, pcm | 0x8000); + + if ((l & 0x20) || (r & 0x20)) + ac97_write(w->codec, AC97_AUX, aux & 0x7fff); + else + ac97_write(w->codec, AC97_AUX, aux | 0x8000); + + return 0; +} + +/* Left Headphone Mixers */ +static const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = { +SOC_DAPM_SINGLE("PC Beep Playback Switch", HPL_MIXER, 5, 1, 0), +SOC_DAPM_SINGLE("Voice Playback Switch", HPL_MIXER, 4, 1, 0), +SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 3, 1, 0), +SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 2, 1, 0), +SOC_DAPM_SINGLE("MonoIn Playback Switch", HPL_MIXER, 1, 1, 0), +SOC_DAPM_SINGLE("Bypass Playback Switch", HPL_MIXER, 0, 1, 0), +}; + +/* Right Headphone Mixers */ +static const struct snd_kcontrol_new wm9713_hpr_mixer_controls[] = { +SOC_DAPM_SINGLE("PC Beep Playback Switch", HPR_MIXER, 5, 1, 0), +SOC_DAPM_SINGLE("Voice Playback Switch", HPR_MIXER, 4, 1, 0), +SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 3, 1, 0), +SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 2, 1, 0), +SOC_DAPM_SINGLE("MonoIn Playback Switch", HPR_MIXER, 1, 1, 0), +SOC_DAPM_SINGLE("Bypass Playback Switch", HPR_MIXER, 0, 1, 0), +}; + +/* headphone capture mux */ +static const struct snd_kcontrol_new wm9713_hp_rec_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[1]); + +/* headphone mic mux */ +static const struct snd_kcontrol_new wm9713_hp_mic_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[0]); + +/* Speaker Mixer */ +static const struct snd_kcontrol_new wm9713_speaker_mixer_controls[] = { +SOC_DAPM_SINGLE("PC Beep Playback Switch", AC97_AUX, 11, 1, 1), +SOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 11, 1, 1), +SOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 11, 1, 1), +SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 14, 1, 1), +SOC_DAPM_SINGLE("MonoIn Playback Switch", AC97_MASTER_TONE, 14, 1, 1), +SOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 14, 1, 1), +}; + +/* Mono Mixer */ +static const struct snd_kcontrol_new wm9713_mono_mixer_controls[] = { +SOC_DAPM_SINGLE("PC Beep Playback Switch", AC97_AUX, 7, 1, 1), +SOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 7, 1, 1), +SOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 7, 1, 1), +SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 13, 1, 1), +SOC_DAPM_SINGLE("MonoIn Playback Switch", AC97_MASTER_TONE, 13, 1, 1), +SOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 13, 1, 1), +SOC_DAPM_SINGLE("Mic 1 Sidetone Switch", AC97_LINE, 7, 1, 1), +SOC_DAPM_SINGLE("Mic 2 Sidetone Switch", AC97_LINE, 6, 1, 1), +}; + +/* mono mic mux */ +static const struct snd_kcontrol_new wm9713_mono_mic_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[2]); + +/* mono output mux */ +static const struct snd_kcontrol_new wm9713_mono_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[7]); + +/* speaker left output mux */ +static const struct snd_kcontrol_new wm9713_hp_spkl_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[8]); + +/* speaker right output mux */ +static const struct snd_kcontrol_new wm9713_hp_spkr_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[9]); + +/* headphone left output mux */ +static const struct snd_kcontrol_new wm9713_hpl_out_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[10]); + +/* headphone right output mux */ +static const struct snd_kcontrol_new wm9713_hpr_out_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[11]); + +/* Out3 mux */ +static const struct snd_kcontrol_new wm9713_out3_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[12]); + +/* Out4 mux */ +static const struct snd_kcontrol_new wm9713_out4_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[13]); + +/* DAC inv mux 1 */ +static const struct snd_kcontrol_new wm9713_dac_inv1_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[14]); + +/* DAC inv mux 2 */ +static const struct snd_kcontrol_new wm9713_dac_inv2_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[15]); + +/* Capture source left */ +static const struct snd_kcontrol_new wm9713_rec_srcl_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[3]); + +/* Capture source right */ +static const struct snd_kcontrol_new wm9713_rec_srcr_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[4]); + +/* mic source */ +static const struct snd_kcontrol_new wm9713_mic_sel_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[18]); + +/* mic source B virtual control */ +static const struct snd_kcontrol_new wm9713_micb_sel_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[19]); + +static const struct snd_soc_dapm_widget wm9713_dapm_widgets[] = { +SND_SOC_DAPM_MUX("Capture Headphone Mux", SND_SOC_NOPM, 0, 0, + &wm9713_hp_rec_mux_controls), +SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0, + &wm9713_hp_mic_mux_controls), +SND_SOC_DAPM_MUX("Capture Mono Mux", SND_SOC_NOPM, 0, 0, + &wm9713_mono_mic_mux_controls), +SND_SOC_DAPM_MUX("Mono Out Mux", SND_SOC_NOPM, 0, 0, + &wm9713_mono_mux_controls), +SND_SOC_DAPM_MUX("Left Speaker Out Mux", SND_SOC_NOPM, 0, 0, + &wm9713_hp_spkl_mux_controls), +SND_SOC_DAPM_MUX("Right Speaker Out Mux", SND_SOC_NOPM, 0, 0, + &wm9713_hp_spkr_mux_controls), +SND_SOC_DAPM_MUX("Left Headphone Out Mux", SND_SOC_NOPM, 0, 0, + &wm9713_hpl_out_mux_controls), +SND_SOC_DAPM_MUX("Right Headphone Out Mux", SND_SOC_NOPM, 0, 0, + &wm9713_hpr_out_mux_controls), +SND_SOC_DAPM_MUX("Out 3 Mux", SND_SOC_NOPM, 0, 0, + &wm9713_out3_mux_controls), +SND_SOC_DAPM_MUX("Out 4 Mux", SND_SOC_NOPM, 0, 0, + &wm9713_out4_mux_controls), +SND_SOC_DAPM_MUX("DAC Inv Mux 1", SND_SOC_NOPM, 0, 0, + &wm9713_dac_inv1_mux_controls), +SND_SOC_DAPM_MUX("DAC Inv Mux 2", SND_SOC_NOPM, 0, 0, + &wm9713_dac_inv2_mux_controls), +SND_SOC_DAPM_MUX("Left Capture Source", SND_SOC_NOPM, 0, 0, + &wm9713_rec_srcl_mux_controls), +SND_SOC_DAPM_MUX("Right Capture Source", SND_SOC_NOPM, 0, 0, + &wm9713_rec_srcr_mux_controls), +SND_SOC_DAPM_MUX("Mic A Source", SND_SOC_NOPM, 0, 0, + &wm9713_mic_sel_mux_controls), +SND_SOC_DAPM_MUX("Mic B Source", SND_SOC_NOPM, 0, 0, + &wm9713_micb_sel_mux_controls), +SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_EXTENDED_MID, 3, 1, + &wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls), + mixer_event, SND_SOC_DAPM_POST_REG), +SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_EXTENDED_MID, 2, 1, + &wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls), + mixer_event, SND_SOC_DAPM_POST_REG), +SND_SOC_DAPM_MIXER("Mono Mixer", AC97_EXTENDED_MID, 0, 1, + &wm9713_mono_mixer_controls[0], ARRAY_SIZE(wm9713_mono_mixer_controls)), +SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_EXTENDED_MID, 1, 1, + &wm9713_speaker_mixer_controls[0], + ARRAY_SIZE(wm9713_speaker_mixer_controls)), +SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", AC97_EXTENDED_MID, 7, 1), +SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", AC97_EXTENDED_MID, 6, 1), +SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("HP Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("Line Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("Capture Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", AC97_EXTENDED_MID, 12, 1), +SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", AC97_EXTENDED_MID, 11, 1), +SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", AC97_EXTENDED_MID, 5, 1), +SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", AC97_EXTENDED_MID, 4, 1), +SND_SOC_DAPM_PGA("Left Headphone", AC97_EXTENDED_MSTATUS, 10, 1, NULL, 0), +SND_SOC_DAPM_PGA("Right Headphone", AC97_EXTENDED_MSTATUS, 9, 1, NULL, 0), +SND_SOC_DAPM_PGA("Left Speaker", AC97_EXTENDED_MSTATUS, 8, 1, NULL, 0), +SND_SOC_DAPM_PGA("Right Speaker", AC97_EXTENDED_MSTATUS, 7, 1, NULL, 0), +SND_SOC_DAPM_PGA("Out 3", AC97_EXTENDED_MSTATUS, 11, 1, NULL, 0), +SND_SOC_DAPM_PGA("Out 4", AC97_EXTENDED_MSTATUS, 12, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mono Out", AC97_EXTENDED_MSTATUS, 13, 1, NULL, 0), +SND_SOC_DAPM_PGA("Left Line In", AC97_EXTENDED_MSTATUS, 6, 1, NULL, 0), +SND_SOC_DAPM_PGA("Right Line In", AC97_EXTENDED_MSTATUS, 5, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mono In", AC97_EXTENDED_MSTATUS, 4, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mic A PGA", AC97_EXTENDED_MSTATUS, 3, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mic B PGA", AC97_EXTENDED_MSTATUS, 2, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mic A Pre Amp", AC97_EXTENDED_MSTATUS, 1, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mic B Pre Amp", AC97_EXTENDED_MSTATUS, 0, 1, NULL, 0), +SND_SOC_DAPM_MICBIAS("Mic Bias", AC97_EXTENDED_MSTATUS, 14, 1), +SND_SOC_DAPM_OUTPUT("MONO"), +SND_SOC_DAPM_OUTPUT("HPL"), +SND_SOC_DAPM_OUTPUT("HPR"), +SND_SOC_DAPM_OUTPUT("SPKL"), +SND_SOC_DAPM_OUTPUT("SPKR"), +SND_SOC_DAPM_OUTPUT("OUT3"), +SND_SOC_DAPM_OUTPUT("OUT4"), +SND_SOC_DAPM_INPUT("LINEL"), +SND_SOC_DAPM_INPUT("LINER"), +SND_SOC_DAPM_INPUT("MONOIN"), +SND_SOC_DAPM_INPUT("PCBEEP"), +SND_SOC_DAPM_INPUT("MIC1"), +SND_SOC_DAPM_INPUT("MIC2A"), +SND_SOC_DAPM_INPUT("MIC2B"), +SND_SOC_DAPM_VMID("VMID"), +}; + +static const char *audio_map[][3] = { + /* left HP mixer */ + {"Left HP Mixer", "PC Beep Playback Switch", "PCBEEP"}, + {"Left HP Mixer", "Voice Playback Switch", "Voice DAC"}, + {"Left HP Mixer", "Aux Playback Switch", "Aux DAC"}, + {"Left HP Mixer", "Bypass Playback Switch", "Left Line In"}, + {"Left HP Mixer", "PCM Playback Switch", "Left DAC"}, + {"Left HP Mixer", "MonoIn Playback Switch", "Mono In"}, + {"Left HP Mixer", NULL, "Capture Headphone Mux"}, + + /* right HP mixer */ + {"Right HP Mixer", "PC Beep Playback Switch", "PCBEEP"}, + {"Right HP Mixer", "Voice Playback Switch", "Voice DAC"}, + {"Right HP Mixer", "Aux Playback Switch", "Aux DAC"}, + {"Right HP Mixer", "Bypass Playback Switch", "Right Line In"}, + {"Right HP Mixer", "PCM Playback Switch", "Right DAC"}, + {"Right HP Mixer", "MonoIn Playback Switch", "Mono In"}, + {"Right HP Mixer", NULL, "Capture Headphone Mux"}, + + /* virtual mixer - mixes left & right channels for spk and mono */ + {"AC97 Mixer", NULL, "Left DAC"}, + {"AC97 Mixer", NULL, "Right DAC"}, + {"Line Mixer", NULL, "Right Line In"}, + {"Line Mixer", NULL, "Left Line In"}, + {"HP Mixer", NULL, "Left HP Mixer"}, + {"HP Mixer", NULL, "Right HP Mixer"}, + {"Capture Mixer", NULL, "Left Capture Source"}, + {"Capture Mixer", NULL, "Right Capture Source"}, + + /* speaker mixer */ + {"Speaker Mixer", "PC Beep Playback Switch", "PCBEEP"}, + {"Speaker Mixer", "Voice Playback Switch", "Voice DAC"}, + {"Speaker Mixer", "Aux Playback Switch", "Aux DAC"}, + {"Speaker Mixer", "Bypass Playback Switch", "Line Mixer"}, + {"Speaker Mixer", "PCM Playback Switch", "AC97 Mixer"}, + {"Speaker Mixer", "MonoIn Playback Switch", "Mono In"}, + + /* mono mixer */ + {"Mono Mixer", "PC Beep Playback Switch", "PCBEEP"}, + {"Mono Mixer", "Voice Playback Switch", "Voice DAC"}, + {"Mono Mixer", "Aux Playback Switch", "Aux DAC"}, + {"Mono Mixer", "Bypass Playback Switch", "Line Mixer"}, + {"Mono Mixer", "PCM Playback Switch", "AC97 Mixer"}, + {"Mono Mixer", "Mic 1 Sidetone Switch", "Mic A PGA"}, + {"Mono Mixer", "Mic 2 Sidetone Switch", "Mic B PGA"}, + {"Mono Mixer", NULL, "Capture Mono Mux"}, + + /* DAC inv mux 1 */ + {"DAC Inv Mux 1", "Mono", "Mono Mixer"}, + {"DAC Inv Mux 1", "Speaker", "Speaker Mixer"}, + {"DAC Inv Mux 1", "Left Headphone", "Left HP Mixer"}, + {"DAC Inv Mux 1", "Right Headphone", "Right HP Mixer"}, + {"DAC Inv Mux 1", "Headphone Mono", "HP Mixer"}, + + /* DAC inv mux 2 */ + {"DAC Inv Mux 2", "Mono", "Mono Mixer"}, + {"DAC Inv Mux 2", "Speaker", "Speaker Mixer"}, + {"DAC Inv Mux 2", "Left Headphone", "Left HP Mixer"}, + {"DAC Inv Mux 2", "Right Headphone", "Right HP Mixer"}, + {"DAC Inv Mux 2", "Headphone Mono", "HP Mixer"}, + + /* headphone left mux */ + {"Left Headphone Out Mux", "Headphone", "Left HP Mixer"}, + + /* headphone right mux */ + {"Right Headphone Out Mux", "Headphone", "Right HP Mixer"}, + + /* speaker left mux */ + {"Left Speaker Out Mux", "Headphone", "Left HP Mixer"}, + {"Left Speaker Out Mux", "Speaker", "Speaker Mixer"}, + {"Left Speaker Out Mux", "Inv", "DAC Inv Mux 1"}, + + /* speaker right mux */ + {"Right Speaker Out Mux", "Headphone", "Right HP Mixer"}, + {"Right Speaker Out Mux", "Speaker", "Speaker Mixer"}, + {"Right Speaker Out Mux", "Inv", "DAC Inv Mux 2"}, + + /* mono mux */ + {"Mono Out Mux", "Mono", "Mono Mixer"}, + {"Mono Out Mux", "Inv", "DAC Inv Mux 1"}, + + /* out 3 mux */ + {"Out 3 Mux", "Inv 1", "DAC Inv Mux 1"}, + + /* out 4 mux */ + {"Out 4 Mux", "Inv 2", "DAC Inv Mux 2"}, + + /* output pga */ + {"HPL", NULL, "Left Headphone"}, + {"Left Headphone", NULL, "Left Headphone Out Mux"}, + {"HPR", NULL, "Right Headphone"}, + {"Right Headphone", NULL, "Right Headphone Out Mux"}, + {"OUT3", NULL, "Out 3"}, + {"Out 3", NULL, "Out 3 Mux"}, + {"OUT4", NULL, "Out 4"}, + {"Out 4", NULL, "Out 4 Mux"}, + {"SPKL", NULL, "Left Speaker"}, + {"Left Speaker", NULL, "Left Speaker Out Mux"}, + {"SPKR", NULL, "Right Speaker"}, + {"Right Speaker", NULL, "Right Speaker Out Mux"}, + {"MONO", NULL, "Mono Out"}, + {"Mono Out", NULL, "Mono Out Mux"}, + + /* input pga */ + {"Left Line In", NULL, "LINEL"}, + {"Right Line In", NULL, "LINER"}, + {"Mono In", NULL, "MONOIN"}, + {"Mic A PGA", NULL, "Mic A Pre Amp"}, + {"Mic B PGA", NULL, "Mic B Pre Amp"}, + + /* left capture select */ + {"Left Capture Source", "Mic 1", "Mic A Pre Amp"}, + {"Left Capture Source", "Mic 2", "Mic B Pre Amp"}, + {"Left Capture Source", "Line", "LINEL"}, + {"Left Capture Source", "Mono In", "MONOIN"}, + {"Left Capture Source", "Headphone", "Left HP Mixer"}, + {"Left Capture Source", "Speaker", "Speaker Mixer"}, + {"Left Capture Source", "Mono Out", "Mono Mixer"}, + + /* right capture select */ + {"Right Capture Source", "Mic 1", "Mic A Pre Amp"}, + {"Right Capture Source", "Mic 2", "Mic B Pre Amp"}, + {"Right Capture Source", "Line", "LINER"}, + {"Right Capture Source", "Mono In", "MONOIN"}, + {"Right Capture Source", "Headphone", "Right HP Mixer"}, + {"Right Capture Source", "Speaker", "Speaker Mixer"}, + {"Right Capture Source", "Mono Out", "Mono Mixer"}, + + /* left ADC */ + {"Left ADC", NULL, "Left Capture Source"}, + + /* right ADC */ + {"Right ADC", NULL, "Right Capture Source"}, + + /* mic */ + {"Mic A Pre Amp", NULL, "Mic A Source"}, + {"Mic A Source", "Mic 1", "MIC1"}, + {"Mic A Source", "Mic 2 A", "MIC2A"}, + {"Mic A Source", "Mic 2 B", "Mic B Source"}, + {"Mic B Pre Amp", "MPB", "Mic B Source"}, + {"Mic B Source", NULL, "MIC2B"}, + + /* headphone capture */ + {"Capture Headphone Mux", "Stereo", "Capture Mixer"}, + {"Capture Headphone Mux", "Left", "Left Capture Source"}, + {"Capture Headphone Mux", "Right", "Right Capture Source"}, + + /* mono capture */ + {"Capture Mono Mux", "Stereo", "Capture Mixer"}, + {"Capture Mono Mux", "Left", "Left Capture Source"}, + {"Capture Mono Mux", "Right", "Right Capture Source"}, + + {NULL, NULL, NULL}, +}; + +static int wm9713_add_widgets(struct snd_soc_codec *codec) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wm9713_dapm_widgets); i++) + snd_soc_dapm_new_control(codec, &wm9713_dapm_widgets[i]); + + /* set up audio path audio_mapnects */ + for (i = 0; audio_map[i][0] != NULL; i++) + snd_soc_dapm_connect_input(codec, audio_map[i][0], + audio_map[i][1], audio_map[i][2]); + + snd_soc_dapm_new_widgets(codec); + return 0; +} + +static unsigned int ac97_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + u16 *cache = codec->reg_cache; + + if (reg == AC97_RESET || reg == AC97_GPIO_STATUS || + reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 || + reg == AC97_CD) + return soc_ac97_ops.read(codec->ac97, reg); + else { + reg = reg >> 1; + + if (reg > (ARRAY_SIZE(wm9713_reg))) + return -EIO; + + return cache[reg]; + } +} + +static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int val) +{ + u16 *cache = codec->reg_cache; + if (reg < 0x7c) + soc_ac97_ops.write(codec->ac97, reg, val); + reg = reg >> 1; + if (reg <= (ARRAY_SIZE(wm9713_reg))) + cache[reg] = val; + + return 0; +} + +/* PLL divisors */ +struct _pll_div { + u32 divsel:1; + u32 divctl:1; + u32 lf:1; + u32 n:4; + u32 k:24; +}; + +/* The size in bits of the PLL divide multiplied by 10 + * to allow rounding later */ +#define FIXED_PLL_SIZE ((1 << 22) * 10) + +static void pll_factors(struct _pll_div *pll_div, unsigned int source) +{ + u64 Kpart; + unsigned int K, Ndiv, Nmod, target; + + /* The the PLL output is always 98.304MHz. */ + target = 98304000; + + /* If the input frequency is over 14.4MHz then scale it down. */ + if (source > 14400000) { + source >>= 1; + pll_div->divsel = 1; + + if (source > 14400000) { + source >>= 1; + pll_div->divctl = 1; + } else + pll_div->divctl = 0; + + } else { + pll_div->divsel = 0; + pll_div->divctl = 0; + } + + /* Low frequency sources require an additional divide in the + * loop. + */ + if (source < 8192000) { + pll_div->lf = 1; + target >>= 2; + } else + pll_div->lf = 0; + + Ndiv = target / source; + if ((Ndiv < 5) || (Ndiv > 12)) + printk(KERN_WARNING + "WM9713 PLL N value %d out of recommended range!\n", + Ndiv); + + pll_div->n = Ndiv; + Nmod = target % source; + Kpart = FIXED_PLL_SIZE * (long long)Nmod; + + do_div(Kpart, source); + + K = Kpart & 0xFFFFFFFF; + + /* Check if we need to round */ + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + K /= 10; + + pll_div->k = K; +} + +/** + * Please note that changing the PLL input frequency may require + * resynchronisation with the AC97 controller. + */ +static int wm9713_set_pll(struct snd_soc_codec *codec, + int pll_id, unsigned int freq_in, unsigned int freq_out) +{ + struct wm9713_priv *wm9713 = codec->private_data; + u16 reg, reg2; + struct _pll_div pll_div; + + /* turn PLL off ? */ + if (freq_in == 0 || freq_out == 0) { + /* disable PLL power and select ext source */ + reg = ac97_read(codec, AC97_HANDSET_RATE); + ac97_write(codec, AC97_HANDSET_RATE, reg | 0x0080); + reg = ac97_read(codec, AC97_EXTENDED_MID); + ac97_write(codec, AC97_EXTENDED_MID, reg | 0x0200); + wm9713->pll_out = 0; + return 0; + } + + pll_factors(&pll_div, freq_in); + + if (pll_div.k == 0) { + reg = (pll_div.n << 12) | (pll_div.lf << 11) | + (pll_div.divsel << 9) | (pll_div.divctl << 8); + ac97_write(codec, AC97_LINE1_LEVEL, reg); + } else { + /* write the fractional k to the reg 0x46 pages */ + reg2 = (pll_div.n << 12) | (pll_div.lf << 11) | (1 << 10) | + (pll_div.divsel << 9) | (pll_div.divctl << 8); + + /* K [21:20] */ + reg = reg2 | (0x5 << 4) | (pll_div.k >> 20); + ac97_write(codec, AC97_LINE1_LEVEL, reg); + + /* K [19:16] */ + reg = reg2 | (0x4 << 4) | ((pll_div.k >> 16) & 0xf); + ac97_write(codec, AC97_LINE1_LEVEL, reg); + + /* K [15:12] */ + reg = reg2 | (0x3 << 4) | ((pll_div.k >> 12) & 0xf); + ac97_write(codec, AC97_LINE1_LEVEL, reg); + + /* K [11:8] */ + reg = reg2 | (0x2 << 4) | ((pll_div.k >> 8) & 0xf); + ac97_write(codec, AC97_LINE1_LEVEL, reg); + + /* K [7:4] */ + reg = reg2 | (0x1 << 4) | ((pll_div.k >> 4) & 0xf); + ac97_write(codec, AC97_LINE1_LEVEL, reg); + + reg = reg2 | (0x0 << 4) | (pll_div.k & 0xf); /* K [3:0] */ + ac97_write(codec, AC97_LINE1_LEVEL, reg); + } + + /* turn PLL on and select as source */ + reg = ac97_read(codec, AC97_EXTENDED_MID); + ac97_write(codec, AC97_EXTENDED_MID, reg & 0xfdff); + reg = ac97_read(codec, AC97_HANDSET_RATE); + ac97_write(codec, AC97_HANDSET_RATE, reg & 0xff7f); + wm9713->pll_out = freq_out; + wm9713->pll_in = freq_in; + + /* wait 10ms AC97 link frames for the link to stabilise */ + schedule_timeout_interruptible(msecs_to_jiffies(10)); + return 0; +} + +static int wm9713_set_dai_pll(struct snd_soc_codec_dai *codec_dai, + int pll_id, unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_codec *codec = codec_dai->codec; + return wm9713_set_pll(codec, pll_id, freq_in, freq_out); +} + +/* + * Tristate the PCM DAI lines, tristate can be disabled by calling + * wm9713_set_dai_fmt() + */ +static int wm9713_set_dai_tristate(struct snd_soc_codec_dai *codec_dai, + int tristate) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0x9fff; + + if (tristate) + ac97_write(codec, AC97_CENTER_LFE_MASTER, reg); + + return 0; +} + +/* + * Configure WM9713 clock dividers. + * Voice DAC needs 256 FS + */ +static int wm9713_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai, + int div_id, int div) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 reg; + + switch (div_id) { + case WM9713_PCMCLK_DIV: + reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xf0ff; + ac97_write(codec, AC97_HANDSET_RATE, reg | div); + break; + case WM9713_CLKA_MULT: + reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xfffd; + ac97_write(codec, AC97_HANDSET_RATE, reg | div); + break; + case WM9713_CLKB_MULT: + reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xfffb; + ac97_write(codec, AC97_HANDSET_RATE, reg | div); + break; + case WM9713_HIFI_DIV: + reg = ac97_read(codec, AC97_HANDSET_RATE) & 0x8fff; + ac97_write(codec, AC97_HANDSET_RATE, reg | div); + break; + case WM9713_PCMBCLK_DIV: + reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xf1ff; + ac97_write(codec, AC97_CENTER_LFE_MASTER, reg | div); + break; + case WM9713_PCMCLK_PLL_DIV: + reg = ac97_read(codec, AC97_LINE1_LEVEL) & 0xff80; + ac97_write(codec, AC97_LINE1_LEVEL, reg | 0x60 | div); + break; + case WM9713_HIFI_PLL_DIV: + reg = ac97_read(codec, AC97_LINE1_LEVEL) & 0xff80; + ac97_write(codec, AC97_LINE1_LEVEL, reg | 0x70 | div); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wm9713_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 gpio = ac97_read(codec, AC97_GPIO_CFG) & 0xffc5; + u16 reg = 0x8000; + + /* clock masters */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + reg |= 0x4000; + gpio |= 0x0010; + break; + case SND_SOC_DAIFMT_CBM_CFS: + reg |= 0x6000; + gpio |= 0x0018; + break; + case SND_SOC_DAIFMT_CBS_CFS: + reg |= 0x0200; + gpio |= 0x001a; + break; + case SND_SOC_DAIFMT_CBS_CFM: + gpio |= 0x0012; + break; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_IF: + reg |= 0x00c0; + break; + case SND_SOC_DAIFMT_IB_NF: + reg |= 0x0080; + break; + case SND_SOC_DAIFMT_NB_IF: + reg |= 0x0040; + break; + } + + /* DAI format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + reg |= 0x0002; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg |= 0x0001; + break; + case SND_SOC_DAIFMT_DSP_A: + reg |= 0x0003; + break; + case SND_SOC_DAIFMT_DSP_B: + reg |= 0x0043; + break; + } + + ac97_write(codec, AC97_GPIO_CFG, gpio); + ac97_write(codec, AC97_CENTER_LFE_MASTER, reg); + return 0; +} + +static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->codec; + u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xfff3; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + reg |= 0x0004; + break; + case SNDRV_PCM_FORMAT_S24_LE: + reg |= 0x0008; + break; + case SNDRV_PCM_FORMAT_S32_LE: + reg |= 0x000c; + break; + } + + /* enable PCM interface in master mode */ + ac97_write(codec, AC97_CENTER_LFE_MASTER, reg); + return 0; +} + +static void wm9713_voiceshutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->codec; + u16 status; + + /* Gracefully shut down the voice interface. */ + status = ac97_read(codec, AC97_EXTENDED_STATUS) | 0x1000; + ac97_write(codec, AC97_HANDSET_RATE, 0x0280); + schedule_timeout_interruptible(msecs_to_jiffies(1)); + ac97_write(codec, AC97_HANDSET_RATE, 0x0F80); + ac97_write(codec, AC97_EXTENDED_MID, status); +} + +static int ac97_hifi_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->codec; + int reg; + u16 vra; + + vra = ac97_read(codec, AC97_EXTENDED_STATUS); + ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = AC97_PCM_FRONT_DAC_RATE; + else + reg = AC97_PCM_LR_ADC_RATE; + + return ac97_write(codec, reg, runtime->rate); +} + +static int ac97_aux_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->codec; + u16 vra, xsle; + + vra = ac97_read(codec, AC97_EXTENDED_STATUS); + ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1); + xsle = ac97_read(codec, AC97_PCI_SID); + ac97_write(codec, AC97_PCI_SID, xsle | 0x8000); + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -ENODEV; + + return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate); +} + +#define WM9713_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000) + +#define WM9713_PCM_FORMATS \ + (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \ + SNDRV_PCM_FORMAT_S24_LE) + +struct snd_soc_codec_dai wm9713_dai[] = { +{ + .name = "AC97 HiFi", + .type = SND_SOC_DAI_AC97_BUS, + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM9713_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM9713_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .ops = { + .prepare = ac97_hifi_prepare,}, + .dai_ops = { + .set_clkdiv = wm9713_set_dai_clkdiv, + .set_pll = wm9713_set_dai_pll,}, + }, + { + .name = "AC97 Aux", + .playback = { + .stream_name = "Aux Playback", + .channels_min = 1, + .channels_max = 1, + .rates = WM9713_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .ops = { + .prepare = ac97_aux_prepare,}, + .dai_ops = { + .set_clkdiv = wm9713_set_dai_clkdiv, + .set_pll = wm9713_set_dai_pll,}, + }, + { + .name = "WM9713 Voice", + .playback = { + .stream_name = "Voice Playback", + .channels_min = 1, + .channels_max = 1, + .rates = WM9713_RATES, + .formats = WM9713_PCM_FORMATS,}, + .capture = { + .stream_name = "Voice Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM9713_RATES, + .formats = WM9713_PCM_FORMATS,}, + .ops = { + .hw_params = wm9713_pcm_hw_params, + .shutdown = wm9713_voiceshutdown,}, + .dai_ops = { + .set_clkdiv = wm9713_set_dai_clkdiv, + .set_pll = wm9713_set_dai_pll, + .set_fmt = wm9713_set_dai_fmt, + .set_tristate = wm9713_set_dai_tristate, + }, + }, +}; +EXPORT_SYMBOL_GPL(wm9713_dai); + +int wm9713_reset(struct snd_soc_codec *codec, int try_warm) +{ + if (try_warm && soc_ac97_ops.warm_reset) { + soc_ac97_ops.warm_reset(codec->ac97); + if (!(ac97_read(codec, 0) & 0x8000)) + return 1; + } + + soc_ac97_ops.reset(codec->ac97); + if (ac97_read(codec, 0) & 0x8000) + return -EIO; + return 0; +} +EXPORT_SYMBOL_GPL(wm9713_reset); + +static int wm9713_dapm_event(struct snd_soc_codec *codec, int event) +{ + u16 reg; + + switch (event) { + case SNDRV_CTL_POWER_D0: /* full On */ + /* enable thermal shutdown */ + reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x1bff; + ac97_write(codec, AC97_EXTENDED_MID, reg); + break; + case SNDRV_CTL_POWER_D1: /* partial On */ + case SNDRV_CTL_POWER_D2: /* partial On */ + break; + case SNDRV_CTL_POWER_D3hot: /* Off, with power */ + /* enable master bias and vmid */ + reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x3bff; + ac97_write(codec, AC97_EXTENDED_MID, reg); + ac97_write(codec, AC97_POWERDOWN, 0x0000); + break; + case SNDRV_CTL_POWER_D3cold: /* Off, without power */ + /* disable everything including AC link */ + ac97_write(codec, AC97_EXTENDED_MID, 0xffff); + ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff); + ac97_write(codec, AC97_POWERDOWN, 0xffff); + break; + } + codec->dapm_state = event; + return 0; +} + +static int wm9713_soc_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->codec; + + wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3cold); + return 0; +} + +static int wm9713_soc_resume(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->codec; + struct wm9713_priv *wm9713 = codec->private_data; + int i, ret; + u16 *cache = codec->reg_cache; + + ret = wm9713_reset(codec, 1); + if (ret < 0) { + printk(KERN_ERR "could not reset AC97 codec\n"); + return ret; + } + + wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3hot); + + /* do we need to re-start the PLL ? */ + if (wm9713->pll_out) + wm9713_set_pll(codec, 0, wm9713->pll_in, wm9713->pll_out); + + /* only synchronise the codec if warm reset failed */ + if (ret == 0) { + for (i = 2; i < ARRAY_SIZE(wm9713_reg) << 1; i += 2) { + if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID || + i == AC97_EXTENDED_MSTATUS || i > 0x66) + continue; + soc_ac97_ops.write(codec->ac97, i, cache[i>>1]); + } + } + + if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) + wm9713_dapm_event(codec, SNDRV_CTL_POWER_D0); + + return ret; +} + +static int wm9713_soc_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + int ret = 0, reg; + + printk(KERN_INFO "WM9713/WM9714 SoC Audio Codec %s\n", WM9713_VERSION); + + socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); + if (socdev->codec == NULL) + return -ENOMEM; + codec = socdev->codec; + mutex_init(&codec->mutex); + + codec->reg_cache = kmemdup(wm9713_reg, sizeof(wm9713_reg), GFP_KERNEL); + if (codec->reg_cache == NULL) { + ret = -ENOMEM; + goto cache_err; + } + codec->reg_cache_size = sizeof(wm9713_reg); + codec->reg_cache_step = 2; + + codec->private_data = kzalloc(sizeof(struct wm9713_priv), GFP_KERNEL); + if (codec->private_data == NULL) { + ret = -ENOMEM; + goto priv_err; + } + + codec->name = "WM9713"; + codec->owner = THIS_MODULE; + codec->dai = wm9713_dai; + codec->num_dai = ARRAY_SIZE(wm9713_dai); + codec->write = ac97_write; + codec->read = ac97_read; + codec->dapm_event = wm9713_dapm_event; + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0); + if (ret < 0) + goto codec_err; + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) + goto pcm_err; + + /* do a cold reset for the controller and then try + * a warm reset followed by an optional cold reset for codec */ + wm9713_reset(codec, 0); + ret = wm9713_reset(codec, 1); + if (ret < 0) { + printk(KERN_ERR "AC97 link error\n"); + goto reset_err; + } + + wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3hot); + + /* unmute the adc - move to kcontrol */ + reg = ac97_read(codec, AC97_CD) & 0x7fff; + ac97_write(codec, AC97_CD, reg); + + wm9713_add_controls(codec); + wm9713_add_widgets(codec); + ret = snd_soc_register_card(socdev); + if (ret < 0) + goto reset_err; + return 0; + +reset_err: + snd_soc_free_pcms(socdev); + +pcm_err: + snd_soc_free_ac97_codec(codec); + +codec_err: + kfree(codec->private_data); + +priv_err: + kfree(codec->reg_cache); + +cache_err: + kfree(socdev->codec); + socdev->codec = NULL; + return ret; +} + +static int wm9713_soc_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->codec; + + if (codec == NULL) + return 0; + + snd_soc_dapm_free(socdev); + snd_soc_free_pcms(socdev); + snd_soc_free_ac97_codec(codec); + kfree(codec->private_data); + kfree(codec->reg_cache); + kfree(codec->dai); + kfree(codec); + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_wm9713 = { + .probe = wm9713_soc_probe, + .remove = wm9713_soc_remove, + .suspend = wm9713_soc_suspend, + .resume = wm9713_soc_resume, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_wm9713); + +MODULE_DESCRIPTION("ASoC WM9713/WM9714 driver"); +MODULE_AUTHOR("Liam Girdwood"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm9713.h b/sound/soc/codecs/wm9713.h new file mode 100644 index 0000000..d357b6c --- /dev/null +++ b/sound/soc/codecs/wm9713.h @@ -0,0 +1,53 @@ +/* + * wm9713.h -- WM9713 Soc Audio driver + */ + +#ifndef _WM9713_H +#define _WM9713_H + +/* clock inputs */ +#define WM9713_CLKA_PIN 0 +#define WM9713_CLKB_PIN 1 + +/* clock divider ID's */ +#define WM9713_PCMCLK_DIV 0 +#define WM9713_CLKA_MULT 1 +#define WM9713_CLKB_MULT 2 +#define WM9713_HIFI_DIV 3 +#define WM9713_PCMBCLK_DIV 4 +#define WM9713_PCMCLK_PLL_DIV 5 +#define WM9713_HIFI_PLL_DIV 6 + +/* Calculate the appropriate bit mask for the external PCM clock divider */ +#define WM9713_PCMDIV(x) ((x - 1) << 8) + +/* Calculate the appropriate bit mask for the external HiFi clock divider */ +#define WM9713_HIFIDIV(x) ((x - 1) << 12) + +/* MCLK clock mulitipliers */ +#define WM9713_CLKA_X1 (0 << 1) +#define WM9713_CLKA_X2 (1 << 1) +#define WM9713_CLKB_X1 (0 << 2) +#define WM9713_CLKB_X2 (1 << 2) + +/* MCLK clock MUX */ +#define WM9713_CLK_MUX_A (0 << 0) +#define WM9713_CLK_MUX_B (1 << 0) + +/* Voice DAI BCLK divider */ +#define WM9713_PCMBCLK_DIV_1 (0 << 9) +#define WM9713_PCMBCLK_DIV_2 (1 << 9) +#define WM9713_PCMBCLK_DIV_4 (2 << 9) +#define WM9713_PCMBCLK_DIV_8 (3 << 9) +#define WM9713_PCMBCLK_DIV_16 (4 << 9) + +#define WM9713_DAI_AC97_HIFI 0 +#define WM9713_DAI_AC97_AUX 1 +#define WM9713_DAI_PCM_VOICE 2 + +extern struct snd_soc_codec_device soc_codec_dev_wm9713; +extern struct snd_soc_codec_dai wm9713_dai[3]; + +int wm9713_reset(struct snd_soc_codec *codec, int try_warm); + +#endif diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig new file mode 100644 index 0000000..20680c5 --- /dev/null +++ b/sound/soc/davinci/Kconfig @@ -0,0 +1,19 @@ +config SND_DAVINCI_SOC + tristate "SoC Audio for the TI DAVINCI chip" + depends on ARCH_DAVINCI && SND_SOC + help + Say Y or M if you want to add support for codecs attached to + the DAVINCI AC97 or I2S interface. You will also need + to select the audio interfaces to support below. + +config SND_DAVINCI_SOC_I2S + tristate + +config SND_DAVINCI_SOC_EVM + tristate "SoC Audio support for DaVinci EVM" + depends on SND_DAVINCI_SOC && MACH_DAVINCI_EVM + select SND_DAVINCI_SOC_I2S + select SND_SOC_TLV320AIC3X + help + Say Y if you want to add support for SoC audio on TI + DaVinci EVM platform. diff --git a/sound/soc/davinci/Makefile b/sound/soc/davinci/Makefile new file mode 100644 index 0000000..ca772e5 --- /dev/null +++ b/sound/soc/davinci/Makefile @@ -0,0 +1,11 @@ +# DAVINCI Platform Support +snd-soc-davinci-objs := davinci-pcm.o +snd-soc-davinci-i2s-objs := davinci-i2s.o + +obj-$(CONFIG_SND_DAVINCI_SOC) += snd-soc-davinci.o +obj-$(CONFIG_SND_DAVINCI_SOC_I2S) += snd-soc-davinci-i2s.o + +# DAVINCI Machine Support +snd-soc-evm-objs := davinci-evm.o + +obj-$(CONFIG_SND_DAVINCI_SOC_EVM) += snd-soc-evm.o diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c new file mode 100644 index 0000000..fcd1652 --- /dev/null +++ b/sound/soc/davinci/davinci-evm.c @@ -0,0 +1,208 @@ +/* + * ASoC driver for TI DAVINCI EVM platform + * + * Author: Vladimir Barinov, + * Copyright: (C) 2007 MontaVista Software, Inc., + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../codecs/tlv320aic3x.h" +#include "davinci-pcm.h" +#include "davinci-i2s.h" + +#define EVM_CODEC_CLOCK 22579200 + +static int evm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; + int ret = 0; + + /* set codec DAI configuration */ + ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + return ret; + + /* set cpu DAI configuration */ + ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM | + SND_SOC_DAIFMT_IB_NF); + if (ret < 0) + return ret; + + /* set the codec system clock */ + ret = codec_dai->dai_ops.set_sysclk(codec_dai, 0, EVM_CODEC_CLOCK, + SND_SOC_CLOCK_OUT); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops evm_ops = { + .hw_params = evm_hw_params, +}; + +/* davinci-evm machine dapm widgets */ +static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_LINE("Line In", NULL), +}; + +/* davinci-evm machine audio_mapnections to the codec pins */ +static const char *audio_map[][3] = { + /* Headphone connected to HPLOUT, HPROUT */ + {"Headphone Jack", NULL, "HPLOUT"}, + {"Headphone Jack", NULL, "HPROUT"}, + + /* Line Out connected to LLOUT, RLOUT */ + {"Line Out", NULL, "LLOUT"}, + {"Line Out", NULL, "RLOUT"}, + + /* Mic connected to (MIC3L | MIC3R) */ + {"MIC3L", NULL, "Mic Bias 2V"}, + {"MIC3R", NULL, "Mic Bias 2V"}, + {"Mic Bias 2V", NULL, "Mic Jack"}, + + /* Line In connected to (LINE1L | LINE2L), (LINE1R | LINE2R) */ + {"LINE1L", NULL, "Line In"}, + {"LINE2L", NULL, "Line In"}, + {"LINE1R", NULL, "Line In"}, + {"LINE2R", NULL, "Line In"}, + + {NULL, NULL, NULL}, +}; + +/* Logic for a aic3x as connected on a davinci-evm */ +static int evm_aic3x_init(struct snd_soc_codec *codec) +{ + int i; + + /* Add davinci-evm specific widgets */ + for (i = 0; i < ARRAY_SIZE(aic3x_dapm_widgets); i++) + snd_soc_dapm_new_control(codec, &aic3x_dapm_widgets[i]); + + /* Set up davinci-evm specific audio path audio_map */ + for (i = 0; audio_map[i][0] != NULL; i++) + snd_soc_dapm_connect_input(codec, audio_map[i][0], + audio_map[i][1], audio_map[i][2]); + + /* not connected */ + snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0); + snd_soc_dapm_set_endpoint(codec, "HPLCOM", 0); + snd_soc_dapm_set_endpoint(codec, "HPRCOM", 0); + + /* always connected */ + snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1); + snd_soc_dapm_set_endpoint(codec, "Line Out", 1); + snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1); + snd_soc_dapm_set_endpoint(codec, "Line In", 1); + + snd_soc_dapm_sync_endpoints(codec); + + return 0; +} + +/* davinci-evm digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link evm_dai = { + .name = "TLV320AIC3X", + .stream_name = "AIC3X", + .cpu_dai = &davinci_i2s_dai, + .codec_dai = &aic3x_dai, + .init = evm_aic3x_init, + .ops = &evm_ops, +}; + +/* davinci-evm audio machine driver */ +static struct snd_soc_machine snd_soc_machine_evm = { + .name = "DaVinci EVM", + .dai_link = &evm_dai, + .num_links = 1, +}; + +/* evm audio private data */ +static struct aic3x_setup_data evm_aic3x_setup = { + .i2c_address = 0x1b, +}; + +/* evm audio subsystem */ +static struct snd_soc_device evm_snd_devdata = { + .machine = &snd_soc_machine_evm, + .platform = &davinci_soc_platform, + .codec_dev = &soc_codec_dev_aic3x, + .codec_data = &evm_aic3x_setup, +}; + +static struct resource evm_snd_resources[] = { + { + .start = DAVINCI_MCBSP_BASE, + .end = DAVINCI_MCBSP_BASE + SZ_8K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct evm_snd_platform_data evm_snd_data = { + .tx_dma_ch = DM644X_DMACH_MCBSP_TX, + .rx_dma_ch = DM644X_DMACH_MCBSP_RX, +}; + +static struct platform_device *evm_snd_device; + +static int __init evm_init(void) +{ + int ret; + + evm_snd_device = platform_device_alloc("soc-audio", 0); + if (!evm_snd_device) + return -ENOMEM; + + platform_set_drvdata(evm_snd_device, &evm_snd_devdata); + evm_snd_devdata.dev = &evm_snd_device->dev; + evm_snd_device->dev.platform_data = &evm_snd_data; + + ret = platform_device_add_resources(evm_snd_device, evm_snd_resources, + ARRAY_SIZE(evm_snd_resources)); + if (ret) { + platform_device_put(evm_snd_device); + return ret; + } + + ret = platform_device_add(evm_snd_device); + if (ret) + platform_device_put(evm_snd_device); + + return ret; +} + +static void __exit evm_exit(void) +{ + platform_device_unregister(evm_snd_device); +} + +module_init(evm_init); +module_exit(evm_exit); + +MODULE_AUTHOR("Vladimir Barinov"); +MODULE_DESCRIPTION("TI DAVINCI EVM ASoC driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c new file mode 100644 index 0000000..c421774 --- /dev/null +++ b/sound/soc/davinci/davinci-i2s.c @@ -0,0 +1,407 @@ +/* + * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor + * + * Author: Vladimir Barinov, + * Copyright: (C) 2007 MontaVista Software, Inc., + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "davinci-pcm.h" + +#define DAVINCI_MCBSP_DRR_REG 0x00 +#define DAVINCI_MCBSP_DXR_REG 0x04 +#define DAVINCI_MCBSP_SPCR_REG 0x08 +#define DAVINCI_MCBSP_RCR_REG 0x0c +#define DAVINCI_MCBSP_XCR_REG 0x10 +#define DAVINCI_MCBSP_SRGR_REG 0x14 +#define DAVINCI_MCBSP_PCR_REG 0x24 + +#define DAVINCI_MCBSP_SPCR_RRST (1 << 0) +#define DAVINCI_MCBSP_SPCR_RINTM(v) ((v) << 4) +#define DAVINCI_MCBSP_SPCR_XRST (1 << 16) +#define DAVINCI_MCBSP_SPCR_XINTM(v) ((v) << 20) +#define DAVINCI_MCBSP_SPCR_GRST (1 << 22) +#define DAVINCI_MCBSP_SPCR_FRST (1 << 23) +#define DAVINCI_MCBSP_SPCR_FREE (1 << 25) + +#define DAVINCI_MCBSP_RCR_RWDLEN1(v) ((v) << 5) +#define DAVINCI_MCBSP_RCR_RFRLEN1(v) ((v) << 8) +#define DAVINCI_MCBSP_RCR_RDATDLY(v) ((v) << 16) +#define DAVINCI_MCBSP_RCR_RWDLEN2(v) ((v) << 21) + +#define DAVINCI_MCBSP_XCR_XWDLEN1(v) ((v) << 5) +#define DAVINCI_MCBSP_XCR_XFRLEN1(v) ((v) << 8) +#define DAVINCI_MCBSP_XCR_XDATDLY(v) ((v) << 16) +#define DAVINCI_MCBSP_XCR_XFIG (1 << 18) +#define DAVINCI_MCBSP_XCR_XWDLEN2(v) ((v) << 21) + +#define DAVINCI_MCBSP_SRGR_FWID(v) ((v) << 8) +#define DAVINCI_MCBSP_SRGR_FPER(v) ((v) << 16) +#define DAVINCI_MCBSP_SRGR_FSGM (1 << 28) + +#define DAVINCI_MCBSP_PCR_CLKRP (1 << 0) +#define DAVINCI_MCBSP_PCR_CLKXP (1 << 1) +#define DAVINCI_MCBSP_PCR_FSRP (1 << 2) +#define DAVINCI_MCBSP_PCR_FSXP (1 << 3) +#define DAVINCI_MCBSP_PCR_CLKRM (1 << 8) +#define DAVINCI_MCBSP_PCR_CLKXM (1 << 9) +#define DAVINCI_MCBSP_PCR_FSRM (1 << 10) +#define DAVINCI_MCBSP_PCR_FSXM (1 << 11) + +#define MOD_REG_BIT(val, mask, set) do { \ + if (set) { \ + val |= mask; \ + } else { \ + val &= ~mask; \ + } \ +} while (0) + +enum { + DAVINCI_MCBSP_WORD_8 = 0, + DAVINCI_MCBSP_WORD_12, + DAVINCI_MCBSP_WORD_16, + DAVINCI_MCBSP_WORD_20, + DAVINCI_MCBSP_WORD_24, + DAVINCI_MCBSP_WORD_32, +}; + +static struct davinci_pcm_dma_params davinci_i2s_pcm_out = { + .name = "I2S PCM Stereo out", +}; + +static struct davinci_pcm_dma_params davinci_i2s_pcm_in = { + .name = "I2S PCM Stereo in", +}; + +struct davinci_mcbsp_dev { + void __iomem *base; + struct clk *clk; + struct davinci_pcm_dma_params *dma_params[2]; +}; + +static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev, + int reg, u32 val) +{ + __raw_writel(val, dev->base + reg); +} + +static inline u32 davinci_mcbsp_read_reg(struct davinci_mcbsp_dev *dev, int reg) +{ + return __raw_readl(dev->base + reg); +} + +static void davinci_mcbsp_start(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data; + u32 w; + + /* Start the sample generator and enable transmitter/receiver */ + w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); + MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_GRST, 1); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1); + else + MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 1); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w); + + /* Start frame sync */ + w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); + MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_FRST, 1); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w); +} + +static void davinci_mcbsp_stop(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data; + u32 w; + + /* Reset transmitter/receiver and sample rate/frame sync generators */ + w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); + MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_GRST | + DAVINCI_MCBSP_SPCR_FRST, 0); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 0); + else + MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 0); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w); +} + +static int davinci_i2s_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; + struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data; + + cpu_dai->dma_data = dev->dma_params[substream->stream]; + + return 0; +} + +static int davinci_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai, + unsigned int fmt) +{ + struct davinci_mcbsp_dev *dev = cpu_dai->private_data; + u32 w; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, + DAVINCI_MCBSP_PCR_FSXM | + DAVINCI_MCBSP_PCR_FSRM | + DAVINCI_MCBSP_PCR_CLKXM | + DAVINCI_MCBSP_PCR_CLKRM); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, + DAVINCI_MCBSP_SRGR_FSGM); + break; + case SND_SOC_DAIFMT_CBM_CFM: + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, 0); + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_NF: + w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_PCR_REG); + MOD_REG_BIT(w, DAVINCI_MCBSP_PCR_CLKXP | + DAVINCI_MCBSP_PCR_CLKRP, 1); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, w); + break; + case SND_SOC_DAIFMT_NB_IF: + w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_PCR_REG); + MOD_REG_BIT(w, DAVINCI_MCBSP_PCR_FSXP | + DAVINCI_MCBSP_PCR_FSRP, 1); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, w); + break; + case SND_SOC_DAIFMT_IB_IF: + w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_PCR_REG); + MOD_REG_BIT(w, DAVINCI_MCBSP_PCR_CLKXP | + DAVINCI_MCBSP_PCR_CLKRP | + DAVINCI_MCBSP_PCR_FSXP | + DAVINCI_MCBSP_PCR_FSRP, 1); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, w); + break; + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + } + + return 0; +} + +static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct davinci_pcm_dma_params *dma_params = rtd->dai->cpu_dai->dma_data; + struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data; + struct snd_interval *i = NULL; + int mcbsp_word_length; + u32 w; + + /* general line settings */ + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, + DAVINCI_MCBSP_SPCR_RINTM(3) | + DAVINCI_MCBSP_SPCR_XINTM(3) | + DAVINCI_MCBSP_SPCR_FREE); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, + DAVINCI_MCBSP_RCR_RFRLEN1(1) | + DAVINCI_MCBSP_RCR_RDATDLY(1)); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, + DAVINCI_MCBSP_XCR_XFRLEN1(1) | + DAVINCI_MCBSP_XCR_XDATDLY(1) | + DAVINCI_MCBSP_XCR_XFIG); + + i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); + w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SRGR_REG); + MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1), 1); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, w); + + i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS); + w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SRGR_REG); + MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1), 1); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, w); + + /* Determine xfer data type */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + dma_params->data_type = 1; + mcbsp_word_length = DAVINCI_MCBSP_WORD_8; + break; + case SNDRV_PCM_FORMAT_S16_LE: + dma_params->data_type = 2; + mcbsp_word_length = DAVINCI_MCBSP_WORD_16; + break; + case SNDRV_PCM_FORMAT_S32_LE: + dma_params->data_type = 4; + mcbsp_word_length = DAVINCI_MCBSP_WORD_32; + break; + default: + printk(KERN_WARNING "davinci-i2s: unsupported PCM format"); + return -EINVAL; + } + + w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_RCR_REG); + MOD_REG_BIT(w, DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) | + DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length), 1); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, w); + + w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_XCR_REG); + MOD_REG_BIT(w, DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) | + DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length), 1); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, w); + + return 0; +} + +static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + davinci_mcbsp_start(substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + davinci_mcbsp_stop(substream); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int davinci_i2s_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_machine *machine = socdev->machine; + struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai; + struct davinci_mcbsp_dev *dev; + struct resource *mem, *ioarea; + struct evm_snd_platform_data *pdata; + int ret; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource?\n"); + return -ENODEV; + } + + ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1, + pdev->name); + if (!ioarea) { + dev_err(&pdev->dev, "McBSP region already claimed\n"); + return -EBUSY; + } + + dev = kzalloc(sizeof(struct davinci_mcbsp_dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto err_release_region; + } + + cpu_dai->private_data = dev; + + dev->clk = clk_get(&pdev->dev, "McBSPCLK"); + if (IS_ERR(dev->clk)) { + ret = -ENODEV; + goto err_free_mem; + } + clk_enable(dev->clk); + + dev->base = (void __iomem *)IO_ADDRESS(mem->start); + pdata = pdev->dev.platform_data; + + dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK] = &davinci_i2s_pcm_out; + dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]->channel = pdata->tx_dma_ch; + dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]->dma_addr = + (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DXR_REG); + + dev->dma_params[SNDRV_PCM_STREAM_CAPTURE] = &davinci_i2s_pcm_in; + dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]->channel = pdata->rx_dma_ch; + dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]->dma_addr = + (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DRR_REG); + + return 0; + +err_free_mem: + kfree(dev); +err_release_region: + release_mem_region(mem->start, (mem->end - mem->start) + 1); + + return ret; +} + +static void davinci_i2s_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_machine *machine = socdev->machine; + struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai; + struct davinci_mcbsp_dev *dev = cpu_dai->private_data; + struct resource *mem; + + clk_disable(dev->clk); + clk_put(dev->clk); + dev->clk = NULL; + + kfree(dev); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mem->start, (mem->end - mem->start) + 1); +} + +#define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000 + +struct snd_soc_cpu_dai davinci_i2s_dai = { + .name = "davinci-i2s", + .id = 0, + .type = SND_SOC_DAI_I2S, + .probe = davinci_i2s_probe, + .remove = davinci_i2s_remove, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = DAVINCI_I2S_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = DAVINCI_I2S_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .ops = { + .startup = davinci_i2s_startup, + .trigger = davinci_i2s_trigger, + .hw_params = davinci_i2s_hw_params,}, + .dai_ops = { + .set_fmt = davinci_i2s_set_dai_fmt, + }, +}; +EXPORT_SYMBOL_GPL(davinci_i2s_dai); + +MODULE_AUTHOR("Vladimir Barinov"); +MODULE_DESCRIPTION("TI DAVINCI I2S (McBSP) SoC Interface"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/davinci/davinci-i2s.h b/sound/soc/davinci/davinci-i2s.h new file mode 100644 index 0000000..9592d17 --- /dev/null +++ b/sound/soc/davinci/davinci-i2s.h @@ -0,0 +1,17 @@ +/* + * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor + * + * Author: Vladimir Barinov, + * Copyright: (C) 2007 MontaVista Software, Inc., + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _DAVINCI_I2S_H +#define _DAVINCI_I2S_H + +extern struct snd_soc_cpu_dai davinci_i2s_dai; + +#endif diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c new file mode 100644 index 0000000..6a76927 --- /dev/null +++ b/sound/soc/davinci/davinci-pcm.c @@ -0,0 +1,389 @@ +/* + * ALSA PCM interface for the TI DAVINCI processor + * + * Author: Vladimir Barinov, + * Copyright: (C) 2007 MontaVista Software, Inc., + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "davinci-pcm.h" + +#define DAVINCI_PCM_DEBUG 0 +#if DAVINCI_PCM_DEBUG +#define DPRINTK(x...) printk(KERN_DEBUG x) +#else +#define DPRINTK(x...) +#endif + +static struct snd_pcm_hardware davinci_pcm_hardware = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + .formats = (SNDRV_PCM_FMTBIT_S16_LE), + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_KNOT), + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 8 * 1024, + .periods_min = 16, + .periods_max = 255, + .fifo_size = 0, +}; + +struct davinci_runtime_data { + spinlock_t lock; + int period; /* current DMA period */ + int master_lch; /* Master DMA channel */ + int slave_lch; /* Slave DMA channel */ + struct davinci_pcm_dma_params *params; /* DMA params */ +}; + +static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream) +{ + struct davinci_runtime_data *prtd = substream->runtime->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + int lch = prtd->slave_lch; + unsigned int period_size; + unsigned int dma_offset; + dma_addr_t dma_pos; + dma_addr_t src, dst; + unsigned short src_bidx, dst_bidx; + unsigned int data_type; + unsigned int count; + + period_size = snd_pcm_lib_period_bytes(substream); + dma_offset = prtd->period * period_size; + dma_pos = runtime->dma_addr + dma_offset; + + DPRINTK("audio_set_dma_params_play channel = %d dma_ptr = %x " + "period_size=%x\n", lch, dma_pos, period_size); + + data_type = prtd->params->data_type; + count = period_size / data_type; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + src = dma_pos; + dst = prtd->params->dma_addr; + src_bidx = data_type; + dst_bidx = 0; + } else { + src = prtd->params->dma_addr; + dst = dma_pos; + src_bidx = 0; + dst_bidx = data_type; + } + + davinci_set_dma_src_params(lch, src, INCR, W8BIT); + davinci_set_dma_dest_params(lch, dst, INCR, W8BIT); + davinci_set_dma_src_index(lch, src_bidx, 0); + davinci_set_dma_dest_index(lch, dst_bidx, 0); + davinci_set_dma_transfer_params(lch, data_type, count, 1, 0, ASYNC); + + prtd->period++; + if (unlikely(prtd->period >= runtime->periods)) + prtd->period = 0; +} + +static void davinci_pcm_dma_irq(int lch, u16 ch_status, void *data) +{ + struct snd_pcm_substream *substream = data; + struct davinci_runtime_data *prtd = substream->runtime->private_data; + + DPRINTK("lch=%d, status=0x%x\n", lch, ch_status); + + if (unlikely(ch_status != DMA_COMPLETE)) + return; + + if (snd_pcm_running(substream)) { + snd_pcm_period_elapsed(substream); + + spin_lock(&prtd->lock); + davinci_pcm_enqueue_dma(substream); + spin_unlock(&prtd->lock); + } +} + +static int davinci_pcm_dma_request(struct snd_pcm_substream *substream) +{ + struct davinci_runtime_data *prtd = substream->runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct davinci_pcm_dma_params *dma_data = rtd->dai->cpu_dai->dma_data; + int tcc = TCC_ANY; + int ret; + + if (!dma_data) + return -ENODEV; + + prtd->params = dma_data; + + /* Request master DMA channel */ + ret = davinci_request_dma(prtd->params->channel, prtd->params->name, + davinci_pcm_dma_irq, substream, + &prtd->master_lch, &tcc, EVENTQ_0); + if (ret) + return ret; + + /* Request slave DMA channel */ + ret = davinci_request_dma(PARAM_ANY, "Link", + NULL, NULL, &prtd->slave_lch, &tcc, EVENTQ_0); + if (ret) { + davinci_free_dma(prtd->master_lch); + return ret; + } + + /* Link slave DMA channel in loopback */ + davinci_dma_link_lch(prtd->slave_lch, prtd->slave_lch); + + return 0; +} + +static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct davinci_runtime_data *prtd = substream->runtime->private_data; + int ret = 0; + + spin_lock(&prtd->lock); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + davinci_start_dma(prtd->master_lch); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + davinci_stop_dma(prtd->master_lch); + break; + default: + ret = -EINVAL; + break; + } + + spin_unlock(&prtd->lock); + + return ret; +} + +static int davinci_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct davinci_runtime_data *prtd = substream->runtime->private_data; + struct paramentry_descriptor temp; + + prtd->period = 0; + davinci_pcm_enqueue_dma(substream); + + /* Get slave channel dma params for master channel startup */ + davinci_get_dma_params(prtd->slave_lch, &temp); + davinci_set_dma_params(prtd->master_lch, &temp); + + return 0; +} + +static snd_pcm_uframes_t +davinci_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct davinci_runtime_data *prtd = runtime->private_data; + unsigned int offset; + dma_addr_t count; + dma_addr_t src, dst; + + spin_lock(&prtd->lock); + + davinci_dma_getposition(prtd->master_lch, &src, &dst); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + count = src - runtime->dma_addr; + else + count = dst - runtime->dma_addr;; + + spin_unlock(&prtd->lock); + + offset = bytes_to_frames(runtime, count); + if (offset >= runtime->buffer_size) + offset = 0; + + return offset; +} + +static int davinci_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct davinci_runtime_data *prtd; + int ret = 0; + + snd_soc_set_runtime_hwparams(substream, &davinci_pcm_hardware); + + prtd = kzalloc(sizeof(struct davinci_runtime_data), GFP_KERNEL); + if (prtd == NULL) + return -ENOMEM; + + spin_lock_init(&prtd->lock); + + runtime->private_data = prtd; + + ret = davinci_pcm_dma_request(substream); + if (ret) { + printk(KERN_ERR "davinci_pcm: Failed to get dma channels\n"); + kfree(prtd); + } + + return ret; +} + +static int davinci_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct davinci_runtime_data *prtd = runtime->private_data; + + davinci_dma_unlink_lch(prtd->slave_lch, prtd->slave_lch); + + davinci_free_dma(prtd->slave_lch); + davinci_free_dma(prtd->master_lch); + + kfree(prtd); + + return 0; +} + +static int davinci_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); +} + +static int davinci_pcm_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int davinci_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + return dma_mmap_writecombine(substream->pcm->card->dev, vma, + runtime->dma_area, + runtime->dma_addr, + runtime->dma_bytes); +} + +struct snd_pcm_ops davinci_pcm_ops = { + .open = davinci_pcm_open, + .close = davinci_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = davinci_pcm_hw_params, + .hw_free = davinci_pcm_hw_free, + .prepare = davinci_pcm_prepare, + .trigger = davinci_pcm_trigger, + .pointer = davinci_pcm_pointer, + .mmap = davinci_pcm_mmap, +}; + +static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + size_t size = davinci_pcm_hardware.buffer_bytes_max; + + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + buf->area = dma_alloc_writecombine(pcm->card->dev, size, + &buf->addr, GFP_KERNEL); + + DPRINTK("preallocate_dma_buffer: area=%p, addr=%p, size=%d\n", + (void *) buf->area, (void *) buf->addr, size); + + if (!buf->area) + return -ENOMEM; + + buf->bytes = size; + return 0; +} + +static void davinci_pcm_free(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + struct snd_dma_buffer *buf; + int stream; + + for (stream = 0; stream < 2; stream++) { + substream = pcm->streams[stream].substream; + if (!substream) + continue; + + buf = &substream->dma_buffer; + if (!buf->area) + continue; + + dma_free_writecombine(pcm->card->dev, buf->bytes, + buf->area, buf->addr); + buf->area = NULL; + } +} + +static u64 davinci_pcm_dmamask = 0xffffffff; + +static int davinci_pcm_new(struct snd_card *card, + struct snd_soc_codec_dai *dai, struct snd_pcm *pcm) +{ + int ret; + + if (!card->dev->dma_mask) + card->dev->dma_mask = &davinci_pcm_dmamask; + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = 0xffffffff; + + if (dai->playback.channels_min) { + ret = davinci_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret) + return ret; + } + + if (dai->capture.channels_min) { + ret = davinci_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_CAPTURE); + if (ret) + return ret; + } + + return 0; +} + +struct snd_soc_platform davinci_soc_platform = { + .name = "davinci-audio", + .pcm_ops = &davinci_pcm_ops, + .pcm_new = davinci_pcm_new, + .pcm_free = davinci_pcm_free, +}; +EXPORT_SYMBOL_GPL(davinci_soc_platform); + +MODULE_AUTHOR("Vladimir Barinov"); +MODULE_DESCRIPTION("TI DAVINCI PCM DMA module"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h new file mode 100644 index 0000000..8d6a45e --- /dev/null +++ b/sound/soc/davinci/davinci-pcm.h @@ -0,0 +1,29 @@ +/* + * ALSA PCM interface for the TI DAVINCI processor + * + * Author: Vladimir Barinov, + * Copyright: (C) 2007 MontaVista Software, Inc., + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _DAVINCI_PCM_H +#define _DAVINCI_PCM_H + +struct davinci_pcm_dma_params { + char *name; /* stream identifier */ + int channel; /* sync dma channel ID */ + dma_addr_t dma_addr; /* device physical address for DMA */ + unsigned int data_type; /* xfer data type */ +}; + +struct evm_snd_platform_data { + int tx_dma_ch; + int rx_dma_ch; +}; + +extern struct snd_soc_platform davinci_soc_platform; + +#endif diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index 652514f..78de716 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -20,7 +20,6 @@ #include #include -#include #include #include #include diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 145ad13..b2a11b0 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -15,7 +15,6 @@ #include #include -#include #include #include #include diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index 815c153..a57a964 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -81,7 +81,7 @@ static unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1); if (!((GSR | gsr_bits) & GSR_SDONE)) { printk(KERN_ERR "%s: read error (ac97_reg=%x GSR=%#lx)\n", - __FUNCTION__, reg, GSR | gsr_bits); + __func__, reg, GSR | gsr_bits); val = -1; goto out; } @@ -121,7 +121,7 @@ static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1); if (!((GSR | gsr_bits) & GSR_CDONE)) printk(KERN_ERR "%s: write error (ac97_reg=%x GSR=%#lx)\n", - __FUNCTION__, reg, GSR | gsr_bits); + __func__, reg, GSR | gsr_bits); mutex_unlock(&car_mutex); } @@ -145,7 +145,7 @@ static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97) if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n", - __FUNCTION__, gsr_bits); + __func__, gsr_bits); GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); GCR |= GCR_SDONE_IE|GCR_CDONE_IE; @@ -172,7 +172,7 @@ static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97) if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n", - __FUNCTION__, gsr_bits); + __func__, gsr_bits); GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); GCR |= GCR_SDONE_IE|GCR_CDONE_IE; diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c index 0a3c630..301002c 100644 --- a/sound/soc/s3c24xx/s3c24xx-i2s.c +++ b/sound/soc/s3c24xx/s3c24xx-i2s.c @@ -89,7 +89,7 @@ static void s3c24xx_snd_txctrl(int on) u32 iiscon; u32 iismod; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON); iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); @@ -134,7 +134,7 @@ static void s3c24xx_snd_rxctrl(int on) u32 iiscon; u32 iismod; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON); iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); @@ -182,7 +182,7 @@ static int s3c24xx_snd_lrsync(void) u32 iiscon; unsigned long timeout = jiffies + msecs_to_jiffies(5); - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); while (1) { iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); @@ -201,7 +201,7 @@ static int s3c24xx_snd_lrsync(void) */ static inline int s3c24xx_snd_is_clkmaster(void) { - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); return (readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & S3C2410_IISMOD_SLAVE) ? 0:1; } @@ -214,7 +214,7 @@ static int s3c24xx_i2s_set_fmt(struct snd_soc_cpu_dai *cpu_dai, { u32 iismod; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); DBG("hw_params r: IISMOD: %lx \n", iismod); @@ -250,7 +250,7 @@ static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; u32 iismod; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) rtd->dai->cpu_dai->dma_data = &s3c24xx_i2s_pcm_stereo_out; @@ -278,7 +278,7 @@ static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd) { int ret = 0; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -320,7 +320,7 @@ static int s3c24xx_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai, { u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); iismod &= ~S3C2440_IISMOD_MPLL; @@ -346,7 +346,7 @@ static int s3c24xx_i2s_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai, { u32 reg; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); switch (div_id) { case S3C24XX_DIV_BCLK: @@ -381,7 +381,7 @@ EXPORT_SYMBOL_GPL(s3c24xx_i2s_get_clockrate); static int s3c24xx_i2s_probe(struct platform_device *pdev) { - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); s3c24xx_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100); if (s3c24xx_i2s.regs == NULL) diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c index 29a6c82..40112e2 100644 --- a/sound/soc/s3c24xx/s3c24xx-pcm.c +++ b/sound/soc/s3c24xx/s3c24xx-pcm.c @@ -88,7 +88,7 @@ static void s3c24xx_pcm_enqueue(struct snd_pcm_substream *substream) dma_addr_t pos = prtd->dma_pos; int ret; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); while (prtd->dma_loaded < prtd->dma_limit) { unsigned long len = prtd->dma_period; @@ -98,7 +98,7 @@ static void s3c24xx_pcm_enqueue(struct snd_pcm_substream *substream) if ((pos + len) > prtd->dma_end) { len = prtd->dma_end - pos; DBG(KERN_DEBUG "%s: corrected dma len %ld\n", - __FUNCTION__, len); + __func__, len); } ret = s3c2410_dma_enqueue(prtd->params->channel, @@ -123,7 +123,7 @@ static void s3c24xx_audio_buffdone(struct s3c2410_dma_chan *channel, struct snd_pcm_substream *substream = dev_id; struct s3c24xx_runtime_data *prtd; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); if (result == S3C2410_RES_ABORT || result == S3C2410_RES_ERR) return; @@ -152,7 +152,7 @@ static int s3c24xx_pcm_hw_params(struct snd_pcm_substream *substream, unsigned long totbytes = params_buffer_bytes(params); int ret=0; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); /* return if this is a bufferless transfer e.g. * codec <--> BT codec or GSM modem -- lg FIXME */ @@ -200,7 +200,7 @@ static int s3c24xx_pcm_hw_free(struct snd_pcm_substream *substream) { struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); /* TODO - do we need to ensure DMA flushed */ snd_pcm_set_runtime_buffer(substream, NULL); @@ -218,7 +218,7 @@ static int s3c24xx_pcm_prepare(struct snd_pcm_substream *substream) struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; int ret = 0; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); /* return if this is a bufferless transfer e.g. * codec <--> BT codec or GSM modem -- lg FIXME */ @@ -263,7 +263,7 @@ static int s3c24xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; int ret = 0; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); spin_lock(&prtd->lock); @@ -301,7 +301,7 @@ static snd_pcm_uframes_t unsigned long res; dma_addr_t src, dst; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); spin_lock(&prtd->lock); s3c2410_dma_getposition(prtd->params->channel, &src, &dst); @@ -334,7 +334,7 @@ static int s3c24xx_pcm_open(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct s3c24xx_runtime_data *prtd; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); snd_soc_set_runtime_hwparams(substream, &s3c24xx_pcm_hardware); @@ -353,7 +353,7 @@ static int s3c24xx_pcm_close(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct s3c24xx_runtime_data *prtd = runtime->private_data; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); if (prtd) kfree(prtd); @@ -368,7 +368,7 @@ static int s3c24xx_pcm_mmap(struct snd_pcm_substream *substream, { struct snd_pcm_runtime *runtime = substream->runtime; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); return dma_mmap_writecombine(substream->pcm->card->dev, vma, runtime->dma_area, @@ -394,7 +394,7 @@ static int s3c24xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) struct snd_dma_buffer *buf = &substream->dma_buffer; size_t size = s3c24xx_pcm_hardware.buffer_bytes_max; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); buf->dev.type = SNDRV_DMA_TYPE_DEV; buf->dev.dev = pcm->card->dev; @@ -413,7 +413,7 @@ static void s3c24xx_pcm_free_dma_buffers(struct snd_pcm *pcm) struct snd_dma_buffer *buf; int stream; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); for (stream = 0; stream < 2; stream++) { substream = pcm->streams[stream].substream; @@ -437,7 +437,7 @@ static int s3c24xx_pcm_new(struct snd_card *card, { int ret = 0; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); if (!card->dev->dma_mask) card->dev->dma_mask = &s3c24xx_pcm_dmamask; diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index f03220d..4c1e013 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -1,4 +1,5 @@ menu "SoC Audio support for SuperH" + depends on SUPERH config SND_SOC_PCM_SH7760 tristate "SoC Audio support for Renesas SH7760" diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 620d7ea..4c64560 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1288,7 +1288,7 @@ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, mutex_unlock(&codec->mutex); dapm_power_widgets(codec, event); - dump_dapm(codec, __FUNCTION__); + dump_dapm(codec, __func__); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event); @@ -1334,10 +1334,11 @@ int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec, list_for_each_entry(w, &codec->dapm_widgets, list) { if (!strcmp(w->name, endpoint)) { w->connected = status; + return 0; } } - return 0; + return -ENODEV; } EXPORT_SYMBOL_GPL(snd_soc_dapm_set_endpoint); diff --git a/sound/spi/at73c213.c b/sound/spi/at73c213.c index 89d6e9c..09802e8 100644 --- a/sound/spi/at73c213.c +++ b/sound/spi/at73c213.c @@ -118,7 +118,7 @@ static struct snd_pcm_hardware snd_at73c213_playback_hw = { .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = 8000, /* Replaced by chip->bitrate later. */ .rate_max = 50000, /* Replaced by chip->bitrate later. */ - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .buffer_bytes_max = 64 * 1024 - 1, .period_bytes_min = 512, @@ -133,7 +133,8 @@ static struct snd_pcm_hardware snd_at73c213_playback_hw = { static int snd_at73c213_set_bitrate(struct snd_at73c213 *chip) { unsigned long ssc_rate = clk_get_rate(chip->ssc->clk); - unsigned long dac_rate_new, ssc_div, status; + unsigned long dac_rate_new, ssc_div; + int status; unsigned long ssc_div_max, ssc_div_min; int max_tries; @@ -209,7 +210,13 @@ static int snd_at73c213_pcm_open(struct snd_pcm_substream *substream) { struct snd_at73c213 *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; + int err; + /* ensure buffer_size is a multiple of period_size */ + err = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (err < 0) + return err; snd_at73c213_playback_hw.rate_min = chip->bitrate; snd_at73c213_playback_hw.rate_max = chip->bitrate; runtime->hw = snd_at73c213_playback_hw; @@ -228,6 +235,14 @@ static int snd_at73c213_pcm_close(struct snd_pcm_substream *substream) static int snd_at73c213_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { + struct snd_at73c213 *chip = snd_pcm_substream_chip(substream); + int channels = params_channels(hw_params); + int val; + + val = ssc_readl(chip->ssc->regs, TFMR); + val = SSC_BFINS(TFMR_DATNB, channels - 1, val); + ssc_writel(chip->ssc->regs, TFMR, val); + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); } @@ -249,10 +264,12 @@ static int snd_at73c213_pcm_prepare(struct snd_pcm_substream *substream) ssc_writel(chip->ssc->regs, PDC_TPR, (long)runtime->dma_addr); - ssc_writel(chip->ssc->regs, PDC_TCR, runtime->period_size * 2); + ssc_writel(chip->ssc->regs, PDC_TCR, + runtime->period_size * runtime->channels); ssc_writel(chip->ssc->regs, PDC_TNPR, (long)runtime->dma_addr + block_size); - ssc_writel(chip->ssc->regs, PDC_TNCR, runtime->period_size * 2); + ssc_writel(chip->ssc->regs, PDC_TNCR, + runtime->period_size * runtime->channels); return 0; } @@ -314,15 +331,6 @@ static struct snd_pcm_ops at73c213_playback_ops = { .pointer = snd_at73c213_pcm_pointer, }; -static void snd_at73c213_pcm_free(struct snd_pcm *pcm) -{ - struct snd_at73c213 *chip = snd_pcm_chip(pcm); - if (chip->pcm) { - snd_pcm_lib_preallocate_free_for_all(chip->pcm); - chip->pcm = NULL; - } -} - static int __devinit snd_at73c213_pcm_new(struct snd_at73c213 *chip, int device) { struct snd_pcm *pcm; @@ -334,7 +342,6 @@ static int __devinit snd_at73c213_pcm_new(struct snd_at73c213 *chip, int device) goto out; pcm->private_data = chip; - pcm->private_free = snd_at73c213_pcm_free; pcm->info_flags = SNDRV_PCM_INFO_BLOCK_TRANSFER; strcpy(pcm->name, "at73c213"); chip->pcm = pcm; @@ -375,7 +382,8 @@ static irqreturn_t snd_at73c213_interrupt(int irq, void *dev_id) ssc_writel(chip->ssc->regs, PDC_TNPR, (long)runtime->dma_addr + offset); - ssc_writel(chip->ssc->regs, PDC_TNCR, runtime->period_size * 2); + ssc_writel(chip->ssc->regs, PDC_TNCR, + runtime->period_size * runtime->channels); retval = IRQ_HANDLED; } @@ -737,7 +745,7 @@ cleanup: /* * Device functions */ -static int snd_at73c213_ssc_init(struct snd_at73c213 *chip) +static int __devinit snd_at73c213_ssc_init(struct snd_at73c213 *chip) { /* * Continuous clock output. @@ -767,7 +775,7 @@ static int snd_at73c213_ssc_init(struct snd_at73c213 *chip) return 0; } -static int snd_at73c213_chip_init(struct snd_at73c213 *chip) +static int __devinit snd_at73c213_chip_init(struct snd_at73c213 *chip) { int retval; unsigned char dac_ctrl = 0; @@ -933,7 +941,7 @@ out: return retval; } -static int snd_at73c213_probe(struct spi_device *spi) +static int __devinit snd_at73c213_probe(struct spi_device *spi) { struct snd_card *card; struct snd_at73c213 *chip; diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index f48838a..5c40c03 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -1762,8 +1762,10 @@ static int check_hw_params_convention(struct snd_usb_substream *subs) channels = kcalloc(MAX_MASK, sizeof(u32), GFP_KERNEL); rates = kcalloc(MAX_MASK, sizeof(u32), GFP_KERNEL); - if (!channels || !rates) + if (!channels || !rates) { + err = -ENOMEM; goto __out; + } list_for_each(p, &subs->fmt_list) { struct audioformat *f; @@ -1916,7 +1918,10 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre 1000 * MIN_PACKS_URB, /*(nrpacks * MAX_URBS) * 1000*/ UINT_MAX); - if (check_hw_params_convention(subs)) { + err = check_hw_params_convention(subs); + if (err < 0) + return err; + else if (err) { hwc_debug("setting extra hw constraints...\n"); if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, hw_rule_rate, subs, diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h index 938dff5..82a8d14 100644 --- a/sound/usb/usbquirks.h +++ b/sound/usb/usbquirks.h @@ -39,6 +39,30 @@ .idProduct = prod, \ .bInterfaceClass = USB_CLASS_VENDOR_SPEC +/* Creative/E-Mu devices */ +{ + USB_DEVICE(0x041e, 0x3010), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Creative Labs", + .product_name = "Sound Blaster MP3+", + .ifnum = QUIRK_NO_INTERFACE + } +}, +{ + /* E-Mu 0202 USB */ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x041e, + .idProduct = 0x3f02, + .bInterfaceClass = USB_CLASS_AUDIO, +}, +{ + /* E-Mu 0404 USB */ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x041e, + .idProduct = 0x3f04, + .bInterfaceClass = USB_CLASS_AUDIO, +}, + /* * Logitech QuickCam: bDeviceClass is vendor-specific, so generic interface * class matches do not take effect without an explicit ID match. @@ -97,19 +121,7 @@ .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL }, -/* E-Mu devices */ -{ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = 0x041e, - .idProduct = 0x3f02, - .bInterfaceClass = USB_CLASS_AUDIO, -}, -{ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = 0x041e, - .idProduct = 0x3f04, - .bInterfaceClass = USB_CLASS_AUDIO, -}, + /* * Yamaha devices */ @@ -1165,19 +1177,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, -{ - USB_DEVICE(0x582, 0x00a6), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "Juno-G", - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - } -}, { /* * This quirk is for the "Advanced" modes of the Edirol UA-25. * If the switch is not in an advanced setting, the UA-25 has @@ -1336,6 +1335,19 @@ YAMAHA_DEVICE(0x7010, "UB99"), }, /* TODO: add Edirol MD-P1 support */ { + USB_DEVICE(0x582, 0x00a6), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "Juno-G", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ /* Roland SH-201 */ USB_DEVICE(0x0582, 0x00ad), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { @@ -1719,17 +1731,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, -{ - /* Creative Sound Blaster MP3+ */ - USB_DEVICE(0x041e, 0x3010), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Creative Labs", - .product_name = "Sound Blaster MP3+", - .ifnum = QUIRK_NO_INTERFACE - } - -}, - /* Emagic devices */ { USB_DEVICE(0x086a, 0x0001),